# Options structure
# Author: Philipp Witte, pwitte@eos.ubc.ca
# Date: May 2017
#

export Options, subsample

# Object for velocity/slowness models
mutable struct Options
    space_order::Integer
    free_surface::Bool
    limit_m::Bool
    buffer_size::Real
    save_data_to_disk::Bool
    file_path::String
    file_name::String
    sum_padding::Bool
    optimal_checkpointing::Bool
    num_checkpoints::Union{Integer, Nothing}
    checkpoints_maxmem::Union{Real, Nothing}
    frequencies::Array
    isic::Bool
    subsampling_factor::Integer
    dft_subsampling_factor::Integer
    return_array::Bool
    dt_comp::Union{Real, Nothing}
end

"""
    Options
        space_order::Integer
        free_surface::Bool
        limit_m::Bool
        buffer_size::Real
        save_rate::Real
        save_data_to_disk::Bool
        file_path::String
        file_name::String
        sum_padding::Bool
        optimal_checkpointing::Bool
        num_checkpoints::Integer
        checkpoints_maxmem::Real
	    frequencies::Array
	    isic::Bool
        subsampling_factor::Integer
	    dft_subsampling_factor::Integer
        return_array::Bool
        dt_comp::Real

Options structure for seismic modeling.

`space_order`: finite difference space order for wave equation (default is 8, needs to be multiple of 4)

`free_surface`: set to `true` to enable a free surface boundary condition.

`limit_m`: for 3D modeling, limit modeling domain to area with receivers (saves memory)

`buffer_size`: if `limit_m=true`, define buffer area on each side of modeling domain (in meters)

`save_data_to_disk`: if `true`, saves shot records as separate SEG-Y files

`file_path`: path to directory where data is saved

`file_name`: shot records will be saved as specified file name plus its source coordinates

`sum_padding`: when removing the padding area of the gradient, sum into boundary rows/columns for true adjoints

`optimal_checkpointing`: instead of saving the forward wavefield, recompute it using optimal checkpointing

`num_checkpoints`: number of checkpoints. If not supplied, is set to log(num_timesteps).

`checkpoints_maxmem`: maximum amount of memory that can be allocated for checkpoints (MB)

`frequencies`: calculate the FWI/LS-RTM gradient in the frequency domain for a given set of frequencies

`subsampling_factor`: compute forward wavefield on a time axis that is reduced by a given factor (default is 1)

`dft_subsampling_factor`: compute on-the-fly DFTs on a time axis that is reduced by a given factor (default is 1)

`isic`: use linearized inverse scattering imaging condition

`return_array`: return data from nonlinear/linear modeling as a plain Julia array.

`dt_comp`: overwrite automatically computed computational time step with this value.

Constructor
==========

All arguments are optional keyword arguments with the following default values:

    Options(;space_order=8, free_surface=false,
            limit_m=false, buffer_size=1e3,
            save_data_to_disk=false, file_path="",
            file_name="shot", sum_padding=false,
            optimal_checkpointing=false,
            num_checkpoints=nothing, checkpoints_maxmem=nothing,
            frequencies=[], isic=false,
            subsampling_factor=1, dft_subsampling_factor=1, return_array=false,
            dt_comp=nothing)

"""
Options(;space_order=8,
		 free_surface=false,
         limit_m=false,
		 buffer_size=1e3,
		 save_data_to_disk=false,
		 file_path="",
		 file_name="shot",
         sum_padding=false,
		 optimal_checkpointing=false,
		 num_checkpoints=nothing,
		 checkpoints_maxmem=nothing,
		 frequencies=[],
		 isic=false,
		 subsampling_factor=1,
		 dft_subsampling_factor=1,
         return_array=false,
         dt_comp=nothing) =
		 Options(space_order,
		 		 free_surface,
		         limit_m,
				 buffer_size,
				 save_data_to_disk,
				 file_path,
				 file_name,
				 sum_padding,
				 optimal_checkpointing,
				 num_checkpoints,
				 checkpoints_maxmem,
				 frequencies,
				 isic,
				 subsampling_factor,
				 dft_subsampling_factor,
                 return_array,
                 dt_comp)

function subsample(options::Options, srcnum)
    if isempty(options.frequencies)
        return options
    else
        opt_out = deepcopy(options)
        floc = options.frequencies[srcnum]
        typeof(floc) <: Array && (opt_out.frequencies = floc)
        return opt_out
    end
end


module DESPOT

using Distributions
using POMDPToolbox # for belief updater
using POMDPs

import POMDPs:
        solve,
        action,
        initialize_belief,
        update,
        updater,
        rand,
        rand!

import POMDPToolbox: create_belief

include("history.jl")

abstract type DESPOTBeliefUpdate{S,A,O} end

const DESPOTReward = Float64

mutable struct DESPOTRandomNumber <: POMDPs.AbstractRNG
    number::Float64
end

mutable struct DESPOTParticle{S}
  state::S
  id::Int64
  weight::Float64
end

mutable struct DESPOTBelief{S,A,O}
    particles::Vector{DESPOTParticle{S}}
    history::History{A,O}
end

function rand!(rng::DESPOTRandomNumber, random_number::Array{Float64})
    random_number[1] = rng.number
    return nothing
end

mutable struct DESPOTDefaultRNG <: POMDPs.AbstractRNG
    seed::Array{UInt32,1}
    rand_max::Int64
    debug::Int64

    function DESPOTDefaultRNG(seed::UInt32, rand_max::Int64, debug::Int64 = 0)
        this = new()
        this.seed = Cuint[seed]
        this.rand_max = rand_max
        this.debug = debug
        return this
    end
end

function rand!(rng::DESPOTDefaultRNG, random_number::Array{Float64})
    if is_linux()
        random_number[1] = ccall((:rand_r, "libc"), Int, (Ptr{Cuint},), rng.seed) / rng.rand_max
    else #Windows, etc
        srand(rng.seed)
        random_number[1] = Base.rand()
    end
    return nothing
end

include("config.jl")
include("randomStreams.jl")
include("nodes.jl")
include("utils.jl")
include("solver.jl")

mutable struct DESPOTPolicy{S,A,O,B} <: POMDPs.Policy
    solver::DESPOTSolver{S,A,O,B}
    pomdp ::POMDPs.POMDP{S,A,O}
end

create_policy{S,A,O,B}(solver::DESPOTSolver{S,A,O,B}, pomdp::POMDPs.POMDP{S,A,O}) = DESPOTPolicy(solver, pomdp)

# Bounds interface

bounds{S,A,O,B}(bounds::B,
            pomdp::POMDP{S,A,O},
            particles::Vector{DESPOTParticle{S}},
            config::DESPOTConfig) =
    error("no bounds() method found for $(typeof(bounds)) type")

init_bounds{S,A,O,B}(bounds::B,
            pomdp::POMDPs.POMDP{S,A,O},
            config::DESPOTConfig) = nothing

# this may change
"""
    default_action(bounds, pomdp::POMDP, particles::Vector, config::DESPOTConfig)

Return an action for the case when an action was not determined by the solver. This could be, for example, the best action from the lower bound calculation.
"""
function default_action end

# FUNCTIONS

function action{S,A,O}(policy::DESPOTPolicy{S,A,O}, belief::DESPOTBelief{S})
    new_root(policy.solver, policy.pomdp, belief)
    a, n_trials = search(policy.solver, policy.pomdp) #TODO: return n_trials some other way
    return a
end

# for any kind of belief besides DESPOTBelief
function action{S,A,O}(p::DESPOTPolicy{S,A,O}, b)
    N = p.solver.config.n_particles
    pool = Array{DESPOTParticle{S}}(N)
    w = 1.0/N
    for i in 1:N
        pool[i] = DESPOTParticle{S}(rand(p.solver.rng, b), i-1, w)
    end

    db = DESPOTBelief(pool, History{A,O}())
    action(p, db)
end

function solve{S,A,O,B}(solver::DESPOTSolver{S,A,O,B}, pomdp::POMDPs.POMDP{S,A,O})
    @warn_requirements solve(solver, pomdp)
    policy = DESPOTPolicy(solver, pomdp)
    init_solver(solver, pomdp)
    return policy
end

@POMDP_require solve(solver::DESPOTSolver, pomdp::POMDP) begin
    P = typeof(pomdp)
    S = state_type(P)
    A = action_type(P)
    O = obs_type(P)
    @req actions(::P)
    @req generate_sor(::P,::S,::A,::typeof(create_rng(solver.random_streams)))
    @req reward(::P,::S,::A,::S)
    @req discount(::P)
    @req isterminal(::P,::S)
    as = actions(pomdp)
    @req iterator(::typeof(as))
end

include("visualization.jl")
include("beliefUpdate/beliefUpdateParticle.jl")

export
    ################## DESPOT TYPES ##################
    DESPOTSolver,
    DESPOTPolicy,
    DESPOTParticle,
    DESPOTBelief,
    DESPOTBeliefUpdate,
    DESPOTConfig,
    DESPOTDefaultRNG,
    DESPOTRandomNumber,
    DESPOTReward,
    ######## RANDOM STREAMS ######
    RandomStreams,
    MersenneStreamArray,
    ######## HISTORY-RELATED TYPES AND METHODS ######
    History, #TODO: need to handle history-related stuff better, place somewhere else
    add,
    remove_last,
    history_size,
    truncate,
    ############# STANDARD POMDP METHODS ############
    solve,
    action,
    start_state,
    create_policy,
    ########## UPPER AND LOWER BOUND METHODS #########
    bounds,
    init_bounds,
    sample_particles!, #TODO: need a better way of doing this, perhaps put in POMDPutils
    ########## Visualization #######
    blink,
    DESPOTVisualizer,
    ########## Belief Updater #######
    DESPOTBeliefUpdater,
    create_belief

end #module


using MuZero
using GridGames

include("game.jl")
include("muzero_config.jl")
config = getconfig(Tac)

learner = Learner(
    config;
    uuid="ad24fc40-a07b-4418-9fbc-adb0278dd4e1",
    dir="~/models/MuZeroModels.jl/tictactoe/",
)
l = learner

games = newgames(learner, 2)

is_terminal(games[1])
is_terminal(games)

movesmaxedout(learner, games[1])
movesmaxedout(learner, games)

get_state(games[1]) |> typeof
get_state.(games) |> typeof

root = Node.(0, get_state.(games))
net_output = initial_inference(net, games) |> NetworkOutput

add_exploration_noise(learner.config, root)

store_search_statistics.(games, root)

action = [1,2]
num_moves = length.(history.(game))

run_mcts(net, learner.config, root[1], action_history(games[1]))

action = select_action(
    root[1];
    num_moves=num_moves[1],
    num_steps=net.step,
    temp_fn=learner.config.visit_softmax_temperature_fn,
    preplanned=nothing
)

action = select_action(
    root;
    num_moves=num_moves,
    num_steps=net.step,
    temp_fn=learner.config.visit_softmax_temperature_fn,
    preplanned=nothing
)

apply!.(games, action)

#
# legacy
#
net = getplaynet(learner)
game = new_game(learner.config, net.step)
config = learner.config
while !is_terminal(game) && !movesmaxedout(learner, game)
    root = Node(0, get_state(game))
    net_output = initial_inference(net, game) |> SingleNetworkOutput
    expand_node!(root, legal_actions(game), net_output, Float32(0))
    add_exploration_noise(config, root)
    run_mcts(net, config, root, action_history(game))
    store_search_statistics(game, root)
    action = select_action(
        root;
        num_moves=length(history(game)),
        num_steps=net.step,
        temp_fn=config.visit_softmax_temperature_fn,
        preplanned=nothing
    )
    apply!(game, action)
end

#
# novel
#
isactive(game) = !is_terminal(game) && !movesmaxedout(l, game)
net = getplaynet(l)
games = newgames(l, 100)
config = l.config
active = view(games, :)

@time begin
    while !isempty(active)
        root = Node.(0, get_state.(active))
        net_output = initial_inference(net, active) |> NetworkOutput
        expand_node!(root, legal_actions.(active), net_output, zeros(Float32, length(root)))
        add_exploration_noise(config, root)
        run_mcts(net, config, root, action_history.(active))
        store_search_statistics.(active, root)
        action = select_action.(
            root;
            num_moves=length.(history.(active)),
            num_steps=net.step,
            temp_fn=config.visit_softmax_temperature_fn,
            preplanned=nothing
        )
        apply!.(active, action)
        active = view(active, isactive.(active))
    end
end

@time begin
    for _ in 1:100
        play_game(l)
    end
end


include("../../utils.jl")
using Cthulhu

module TortureTest

    # Scalability parameters - set here to roughly match the real world usecase
    # though that use case does more work.
    const NFIELDS = 888
    const NSET = 387
    const NREPS = 40

    using Random
    using Base.Meta

    struct Maybe{T}
        x::T
        is_missing::Bool
    end
    Base.convert(::Type{Maybe{T}}, x::Maybe{T}) where {T} = x
    Base.convert(::Type{Maybe{T}}, x::T) where {T} = Maybe{T}(x, true)
    Base.convert(::Type{Maybe{T}}, x) where {T} = Maybe{T}(convert(T, x), true)
    Base.eltype(::Type{Maybe{T}}) where {T} = T
    Maybe(x::T) where {T} = Maybe(x, false)

    @eval struct TestCase
        $([:($(Symbol("x$i"))::Maybe{$(rand(Bool) ? Float64 : Int64)}) for i = 1:NFIELDS]...)
    end

    @eval TestCase($(Expr(:parameters, [Expr(:kw, Symbol("x$i"), :(Maybe(convert(eltype(fieldtype(TestCase, $(quot(Symbol("x$i"))))), $(rand(Bool) ? Float64(i) : i))))) for i = 1:NFIELDS]...))) =
        TestCase($([Symbol("x$i") for i = 1:NFIELDS]...))

    @eval function run_test1(x)
        $([:(TestCase(;$([:($(Symbol("x$i")) = $(rand() < 0.05 ? (:x) : rand(Bool) ? Float64(i) : i)) for i = randperm(NFIELDS)[1:NSET]]...))) for _ = 1:NREPS]...)
    end

    @eval function run_test2(x)
        $([:(TestCase(;$([:($(Symbol("x$i")) = $(rand(Bool) ? Float64(i) : i)) for i = randperm(NFIELDS)[1:NSET]]...))) for _ = 1:NREPS]...)
    end

end

@gctime Cthulhu.mkinterp(TortureTest.run_test1, Tuple{Int})[end].inInference


using MCTS
include("roadnet_pursuer_generator_MDP.jl")
using ProgressMeter
#  using PyPlot
using JLD
using MicroLogging
using Base.Markdown
using DataFrames, CSV
include("road_net_visualize.jl")
include("utilities.jl")
include("self_confidence.jl")

rwd_global = []

####### NEED TO RE-VISIT HOW THESE FILES WORK, THERE ARE A LOT OF MULTIPLE IMPORTS
####### I PROBABLY NEED TO PACKAGE THIS STUFF TO AVOID ISSUES WHEN I TRY TO IMPORT @EVERYWHERE
####### THIS IS CAUSING ISSUES WHEN TRYING TO RUN IN PARALLEL, I GOT A "method too new to be called from this world context" error

function get_reward_distribution(g::MetaGraph, mdp::roadnet_with_pursuer; max_steps::Int64=25,
                        its_vals::Array{Int64}=[5],d_vals::Array{Int64}=[2],exp_vals::Array{Float64}=[5.],
                        num_repeats::Int64=10,discounted_rwd::Bool=true,
                        initial_state::roadnet_pursuer_state=roadnet_pursuer_state(1,4))
    rnet = mdp.road_net.gprops[:net_stats]
    net_diam = rnet.diam

    if discounted_rwd
        dis_str = "Discounted Rwd"
    else
        dis_str = "Rwd"
    end

    @info "calculating distribution"
    @debug "Using:" max_steps its_vals d_vals num_repeats rnet mdp.tprob mdp.discount mdp.road_net.gprops[:reward_dict] mdp.road_net.gprops[:exit_nodes]

    # TODO: investigate what the exploration constant does, i.e. does it scale with N?
    # This instantiates empty MCTS policies because MCTS runs "online"
    policy_tilde = Vector{MCTS.MCTSPlanner}(0)
    hist_tilde = Vector{HistoryRecorder}(0)
    its_axis = Vector{Int64}(0)
    d_axis = Vector{Int64}(0)

    for i in 1:length(its_vals)
        for d in d_vals
            its = its_vals[i]
            s = MCTSSolver(n_iterations=its,depth=d,exploration_constant=exp_vals[i],enable_tree_vis=true)
            push!(its_axis,its)
            push!(d_axis,d)
            push!(policy_tilde,solve(s,mdp))
            push!(hist_tilde,HistoryRecorder(max_steps=max_steps))
        end
    end
    @debug "Total number of solvers being tested:" length(policy_tilde)

    function reward_grab(mdp::roadnet_with_pursuer,policy::MCTS.MCTSPlanner,hist::HistoryRecorder,s::roadnet_pursuer_state;discounted::Bool=true,return_hist::Bool=false)
        hist = simulate(hist,mdp,policy,s)
        if return_hist
            return hist
        end

        # discounted reward takes time to solution into account
        if discounted
            return discounted_reward(hist)
        else
            return undiscounted_reward(hist)
        end
    end

    PT = repmat(policy_tilde,1,num_repeats)
    HT = repmat(hist_tilde,1,num_repeats)
    IA = repmat(its_axis,1,num_repeats)
    DA = repmat(d_axis,1,num_repeats)
    @debug "PT $(size(PT)),HT $(size(HT)),IA $(size(IA)),DA $(size(DA))"
    rewards = Array{Float64}(size(PT))
    sim_time = Array{Int64}(size(PT))
    idx = 1

    @info md"# Simulations"
    tic()

    if nprocs() > 1
        q = []
        for pol in PT
            value = Sim(mdp,pol,initial_state,max_steps=max_steps)
            push!(q,value)
        end
        sim_results = run_parallel(q) do sim,hist
            display(discounted_reward(hist))
            return [:steps=>n_steps(hist), :reward=>discounted_reward(hist)]
        end

        rewards = [float(x) for x in sim_results[:reward]] #need to convert to float, because sim_results[:reward] is a union of floats and missing values, even though there aren't any missing values...

    else
        pm = Progress(length(PT[:]),1)
        for (p,h) in zip(PT,HT)
            @info "Running Simulations" progress=idx/length(PT)
            rewards[idx] = reward_grab(mdp,p,h,initial_state,discounted=discounted_rwd)
            hist = reward_grab(mdp,p,h,initial_state,discounted=discounted_rwd,return_hist=true)
            sim_time[idx] = length(hist)
            if false
              @debug print_hist(mdp,hist)
            end
            next!(pm)
            idx += 1
        end
    end
    @info "Completed $(length(PT)) Simulations in $(toq()) seconds"

    R = reshape(rewards,length(its_axis),:)
    ST = reshape(sim_time,length(its_axis),:)

    # Save off data
    jld_fname = "data/$(num_repeats)_$(DateTime(now())).jld"
    @info "saving data to `$jld_fname`"
    JLD.save(jld_fname,"its_axis",its_axis,"d_axis",d_axis,"rewards",rewards,
         "u_vals",mean(rewards,2),"max_steps",max_steps,"R",R,"ST",ST,"IA",IA,"DA",DA)

    return vec(R) #convert to vector
end

function netprops2array(g::roadnet_with_pursuer)
    discount = g.discount
    exits = g.exit_nodes
    rwd_states = g.reward_states # not used right now
    rwd_vals = g.reward_vals # not used right now
    tprob = g.tprob

    num_exit_nodes = length(exits)

    rnet = g.road_net
    rnet_props = rnet.gprops
    exit_rwd = rnet_props[:reward_dict][:exit]
    caught_rwd = rnet_props[:reward_dict][:caught]
    sensor_rwd = rnet_props[:reward_dict][:sensor]
    rnet_stats = rnet_props[:net_stats]
    avg_degree = rnet_stats.average_degree
    deg_variance = rnet_stats.degree_variance
    diam = rnet_stats.diam
    max_degree = rnet_stats.maximum_degree
    N = rnet_stats.nv
    E = rnet_stats.ne

    netprops_ary = [discount tprob num_exit_nodes exit_rwd caught_rwd sensor_rwd avg_degree deg_variance diam max_degree N E]

    return netprops_ary

end

function make_training_data(net::Dict)
    net_dict = net[:sims]
    reps = net_dict[:repeats]
    log_to_file = net_dict[:log_to_file]
    log_fname = net_dict[:log_fname]
    log_lvl = net_dict[:log_lvl]
    sim_steps = net_dict[:steps]
    dis_rwd = net_dict[:dis_rwd]
    fname = net[:fname]

    fn = joinpath(fldr,fname)

    make_training_data(data_fname=fn,repeats=reps,sim_steps=sim_steps,dis_rwd=dis_rwd)
end

function make_training_data(;data_fname::String="nets",logtofile::Bool=false, logfname::String="logs/$(now()).log",loglvl::Symbol=:debug,repeats::Int64=25,sim_steps::Int64=-1,dis_rwd::Bool=false)

    # initialize the DataFrame
    training_data = DataFrame(graphID=Int64[],discount=Float64[],tprob=Float64[],num_exit_nodes=Float64[],exit_rwd=Float64[],
                              caught_rwd=Float64[],sensor_rwd=Float64[],avg_degree=Float64[],deg_variance=Float64[],
                              diam=Float64[],max_degree=Float64[],N=Float64[],E=Float64[],its=Float64[],
                              e_mcts=Float64[],d_mcts=Float64[],steps=Float64[],repeats=Float64[],
                              exit_distance=Float64[],pursuer_distance=Float64[],X3_1=Float64[],X3_2=Float64[],X4=Float64[],
                              mean=Float64[],median=Float64[],moment_2=Float64[],moment_3=Float64[],
                              moment_4=Float64[],moment_5=Float64[],moment_6=Float64[],moment_7=Float64[],
                              moment_8=Float64[],moment_9=Float64[],moment_10=Float64[])
    #### Logging
    if logtofile
        buffer = IOBuffer()
        logger = SimpleLogger(buffer)
        configure_logging(min_level=loglvl)
    else
        logger = global_logger()
        configure_logging(min_level=loglvl)
    end

    # get list of graphs
    problem_dict = jldopen("$data_fname.jld", "r") do file
        @info "Grabbing Pre-Generated Networks"
        read(file,"problem_dict")
    end

    global_rwd_range = Array{Float64}(2,1) # idx 1 is minimun, idx 2 is maximum

    i = 0
    for problem in problem_dict
        i += 1
        display(problem_dict)
        println()
        display(problem)
        println()
        net_num = problem[1]
        problem = problem[2]
        if :error in keys(problem)
            println(problem)
            continue
        else
            @info md"# Processing Network $i of $(length(problem_dict))"
            g = problem[:graph]
            t = problem[:trans_prob]
            d = problem[:discount]

            # do stuff
            # i'm thinking using the same network multiple times using different discount factors, and transition probabilities, this will do two things: 1) make training data better, 2) increase total amount of training data
            mdp = roadnet_with_pursuer(g,tp=t,d=d)

            its = [problem[:mcts_its]] # perhaps base this on transition probability
            d_mcts = [problem[:mcts_depth]]
            e_mcts = [problem[:mcts_e]]

            if sim_steps == -1
                # -1 signifies auto calculation
                # throw the book at it!
                steps = 20*mdp.road_net.gprops[:net_stats].diam
            else
                steps = sim_steps
            end

            start = problem[:evader_start]
            escape = problem[:exit_loc]
            pursuer = problem[:pursuer_start]
            start_state = roadnet_pursuer_state(start,pursuer)
            exit_distance = a_star(mdp.road_net,start,escape)
            pursuer_distance = a_star(mdp.road_net,start,pursuer)

            r_dist = []
            with_logger(logger) do
                r_dist = get_reward_distribution(g,mdp,its_vals=its,d_vals=d_mcts,exp_vals=e_mcts,max_steps=steps,num_repeats=repeats,discounted_rwd=dis_rwd,initial_state=start_state)
            end

            # update global reward range
            minr = minimum(r_dist)
            maxr = maximum(r_dist)
            if i==1
                global_rwd_range[1] = minr
                global_rwd_range[2] = maxr
            else
                minr < global_rwd_range[1] && (global_rwd_range[1] = minr)
                maxr > global_rwd_range[2] && (global_rwd_range[2] = maxr)
            end

            @info "calculating X3 data"
            SQ_data = X3(r_dist)
            @debug SQ_data

            @info "calculating X4 data"
            OA_data = X4(r_dist,threshold=mean(r_dist))
            @debug OA_data

            @info "calculating statistical moments"
            r_dist_moments = [mean(r_dist) median(r_dist) moment(r_dist,2) moment(r_dist,3) moment(r_dist,4) moment(r_dist,5) moment(r_dist,6) moment(r_dist,7) moment(r_dist,8) moment(r_dist,9) moment(r_dist,10)]
            @debug r_dist_moments

            @info "collecting net props"
            mdp_props_ary = netprops2array(mdp)
            @debug mdp_props_ary

            @info "collecting solver props"
            solver_props_ary = [its[1] e_mcts[1] d_mcts[1] steps repeats length(exit_distance) length(pursuer_distance)]
            @debug solver_props_ary

            @info "combining to a row"
            data_entry = [net_num mdp_props_ary solver_props_ary SQ_data OA_data r_dist_moments]
            @debug length(net_num) length(mdp_props_ary) length(solver_props_ary) length(SQ_data) length(r_dist_moments)
            @debug data_entry
            @debug length(data_entry)
            @debug training_data
            push!(training_data,data_entry)

            # store training data with individual network for future use
            training_data_dict = Dict()
            n_cntr = 1
            for n in names(training_data)
                training_data_dict[n] =  data_entry[n_cntr]
                n_cntr += 1
            end
            problem[:training_data] = training_data_dict
            problem[:training_data][:r_dist] = r_dist
        end
    end
    CSV.write("$data_fname.csv",training_data)

    # re-write original file with "r_dist" object added to each network
    jldopen("$data_fname.jld", "w") do file
        write(file, "problem_dict", problem_dict)
        write(file, "global_rwd_range", global_rwd_range)
    end

    if logtofile
        # using the "open(...) do f ... syntax printed the length of the file at the end, this ways doesn't do that
        f = open(logfname,"w")
        logtxt = strip(String(take!(buffer)));
        logtxt = replace(logtxt, r"^\e\[1m\e\[..m(.- )\e\[39m\e\[22m", s"\1")
        write(f,logtxt)
        close(f)
    end
end


_ELK_CONTROLBLOCKS() = ElkControlBlockInfo[ElkControlBlockInfo(:atoms, ElkFlagInfo[ElkFlagInfo{Int64}(:nspecies, 0, "number of species"), ElkFlagInfo{String}(:spfname, "-", "species filename for species \$i\$"), ElkFlagInfo{Int64}(:natoms, nothing, "number of atoms for species \$i\$"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:atposl, nothing, "atomic position in lattice coordinates for atom \$j\$"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:bfcmt, nothing, "muffin-tin external magnetic field in Cartesian\n coordinates for atom \$j\$")], "\nDefines the atomic species as well as their positions in the unit cell and\nthe external magnetic field applied throughout the muffin-tin. These fields\nare used to break spin symmetry and should be considered infinitesimal as\nthey do not contribute directly to the total energy. Collinear calculations\nare more efficient if the field is applied in the \$z\$-direction. One could,\nfor example, set up an anti-ferromagnetic crystal by pointing the field on\none atom in the positive \$z\$-direction and in the opposite direction on\nanother atom. If {\\tt molecule} is {\\tt .true.} then the atomic positions\nare assumed to be in Cartesian coordinates. See also {\\tt sppath},\n{\\tt bfieldc} and {\\tt molecule}.\n"), ElkControlBlockInfo(:autokpt, ElkFlagInfo[ElkFlagInfo{Bool}(:autokpt, false, "{.true.} if the \$k\$-point set is to be determined\n automatically")], "\nSee {\\tt radkpt} for details.\n"), ElkControlBlockInfo(:autolinengy, ElkFlagInfo[ElkFlagInfo{Bool}(:autolinengy, false, "{.true.} if the fixed linearisation energies are\n to be determined automatically")], "\nSee {\\tt dlefe} for details.\n"), ElkControlBlockInfo(:autoswidth, ElkFlagInfo[ElkFlagInfo{Bool}(:autoswidth, false, "{.true.} if the smearing parameter {swidth}\n should be determined automatically")], "\nCalculates the smearing width from the \$k\$-point density, \$V_{\\rm BZ}/n_k\$;\nthe valence band width, \$W\$; and an effective mass parameter, \$m^{*}\$;\naccording to\n\$\$ \\sigma=\\frac{\\sqrt{2W}}{m^{*}}\\left(\\frac{3}{4\\pi}\n \\frac{V_{\\rm BZ}}{n_k}\\right)^{1/3}. \$\$\nThe variable {\\tt mstar} then replaces {\\tt swidth} as the control parameter\nof the smearing width. A large value of \$m^{*}\$ gives a narrower smearing\nfunction. Since {\\tt swidth} is adjusted according to the fineness of the\n\${\\bf k}\$-mesh, the smearing parameter can then be eliminated. It is not\nrecommended that {\\tt autoswidth} be used in conjunction with the\nFermi-Dirac smearing function, since the electronic temperature will then be\na function of the \$k\$-point mesh. See T. Bj\\\"orkman and O. Gr\\aa n\\\"as,\n{\\it Int. J. Quant. Chem.} DOI: 10.1002/qua.22476 (2010) for details. See\nalso {\\tt stype} and {\\tt swidth}.\n"), ElkControlBlockInfo(:avec, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:avec, [10.0, 0.0, 0.0], "first lattice vector"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:avec, [0.0, 10.0, 0.0], "second lattice vector"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:avec, [0.0, 0.0, 10.0], "third lattice vector")], "\nLattice vectors of the crystal in atomic units (Bohr).\n"), ElkControlBlockInfo(:beta0, ElkFlagInfo[ElkFlagInfo{Float64}(:beta0, 5.0, "adaptive mixing parameter")], "\nThis determines how much of the potential from the previous self-consistent\nloop is mixed with the potential from the current loop. It should be made\nsmaller if the calculation is unstable. See {\\tt betamax} and also the\nroutine {\\tt mixadapt}.\n"), ElkControlBlockInfo(:betamax, ElkFlagInfo[ElkFlagInfo{Float64}(:betamax, 5.0, "maximum adaptive mixing parameter")], "\nMaximum allowed mixing parameter used in routine {\\tt mixadapt}.\n"), ElkControlBlockInfo(:bfieldc, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:bfieldc, [0.0, 0.0, 0.0], "global external magnetic field in Cartesian coordinates")], "\nThis is a constant magnetic field applied throughout the entire unit cell\nand enters the second-variational Hamiltonian as\n\$\$ \\frac{g_e}{4c}\\,\\vec{\\sigma}\\cdot{\\bf B}_{\\rm ext}, \$\$\nwhere \$g_e\$ is the electron \$g\$-factor. This field is normally used to break\nspin symmetry for spin-polarised calculations and considered to be\ninfinitesimal with no direct contribution to the total energy. In cases\nwhere the magnetic field is finite (for example when computing magnetic\nresponse) the external \${\\bf B}\$-field energy reported in {\\tt INFO.OUT}\nshould be added to the total by hand. This field is applied throughout the\nentire unit cell. To apply magnetic fields in particular muffin-tins use the\n{\\tt bfcmt} vectors in the {\\tt atoms} block. Collinear calculations are\nmore efficient if the field is applied in the \$z\$-direction.\n"), ElkControlBlockInfo(:broydpm, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{2},Float64,1,2}}(:broydpm, [4.0, 15.0], "Broyden mixing parameters \$\\alpha\$ and \$w_0\$")], "\nSee {\\tt mixtype} and {\\tt mixsdb}.\n"), ElkControlBlockInfo(Symbol("c\\_tb09"), ElkFlagInfo[ElkFlagInfo{Float64}(:c_tb09, nothing, "Tran-Blaha constant \$c\$")], "\nSets the constant \$c\$ in the Tran-Blaha '09 functional. Normally this is\ncalculated from the density, but there may be situations where this needs to\nbe adjusted by hand. See {\\it Phys. Rev. Lett.} {\\bf 102}, 226401 (2009).\n"), ElkControlBlockInfo(:chgexs, ElkFlagInfo[ElkFlagInfo{Float64}(:chgexs, 0.0, "excess electronic charge")], "\nThis controls the amount of charge in the unit cell beyond that required to\nmaintain neutrality. It can be set positive or negative depending on whether\nelectron or hole doping is required.\n"), ElkControlBlockInfo(:cmagz, ElkFlagInfo[ElkFlagInfo{Bool}(:cmagz, false, ".true. if \$z\$-axis collinear magnetism is to be enforced")], "\nThis variable can be set to .true. in cases where the magnetism is\npredominantly collinear in the \$z\$-direction, for example a ferromagnet with\nspin-orbit coupling. This will make the calculation considerably faster at\nthe slight expense of precision.\n"), ElkControlBlockInfo(:deltaem, ElkFlagInfo[ElkFlagInfo{Float64}(:deltaem, 25.0, "the size of the \${\\bf k}\$-vector displacement used when\n calculating numerical derivatives for the effective mass tensor")], "\nSee {\\tt ndspem} and {\\tt vklem}.\n"), ElkControlBlockInfo(:deltaph, ElkFlagInfo[ElkFlagInfo{Float64}(:deltaph, 1.0, "size of the atomic displacement used for calculating\n dynamical matrices")], "\nPhonon calculations are performed by constructing a supercell corresponding\nto a particular \${\\bf q}\$-vector and making a small periodic displacement of\nthe atoms. The magnitude of this displacement is given by {\\tt deltaph}.\nThis should not be made too large, as anharmonic terms could then become\nsignificant, neither should it be too small as this can introduce numerical\nerror.\n"), ElkControlBlockInfo(:deltast, ElkFlagInfo[ElkFlagInfo{Float64}(:deltast, 1.0, "size of the change in lattice vectors used for calculating\n the stress tensor")], "\nThe stress tensor is computed by changing the lattice vector matrix \$A\$ by\n\$\$ A\\rightarrow (1+\\delta t\\,e_i)A, \$\$\nwhere \$dt\$ is an infinitesimal equal in practice to {\\tt deltast} and \$e_i\$\nis the \$i^{\\rm th}\$ strain tensor. Numerical finite differences are used to\ncompute the stress tensor as the derivative of the total energy \$dE_i/dt\$.\n"), ElkControlBlockInfo(Symbol("dft+u"), ElkFlagInfo[ElkFlagInfo{Int64}(:dftu, 0, "type of DFT+\$U\$ calculation"), ElkFlagInfo{Int64}(:inpdftu, 1, "type of input for DFT+U calculation"), ElkFlagInfo{Int64}(:is, nothing, "species number"), ElkFlagInfo{Int64}(:l, nothing, "angular momentum value"), ElkFlagInfo{Float64}(:u, 0.0, "the desired \$U\$ value"), ElkFlagInfo{Float64}(:j, 0.0, "the desired \$J\$ value")], "\nThis block contains the parameters required for an DFT+\$U\$ calculation, with\nthe list of parameters for each species terminated with a blank line. The\ntype of double counting required is set with the parameter {\\tt dftu}.\nCurrently implemented are:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n 0 & No DFT+\$U\$ calculation \\\\\n 1 & Fully localised limit (FLL) \\\\\n 2 & Around mean field (AFM) \\\\\n 3 & An interpolation between FLL and AFM \\\\\n\\end{tabularx}\n\\vskip 6pt\nThe type of input parameters is set with the parameter {\\tt inpdftu}.\nThe current possibilities are:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n 1 & U and J \\\\\n 2 & Slater parameters \\\\\n 3 & Racah parameters \\\\\n 4 & Yukawa screening length \\\\\n 5 & U and determination of corresponding Yukawa screening length\n\\end{tabularx}\n\\vskip 6pt\nSee (amongst others) {\\it Phys. Rev. B} {\\bf 67}, 153106 (2003),\n{\\it Phys. Rev. B} {\\bf 52}, R5467 (1995), {\\it Phys. Rev. B} {\\bf 60},\n10763 (1999), and {\\it Phys. Rev. B} {\\bf 80}, 035121 (2009).\n"), ElkControlBlockInfo(:dlefe, ElkFlagInfo[ElkFlagInfo{Float64}(:dlefe, nothing, "difference between the fixed linearisation energy and the\n Fermi energy")], "\nWhen {\\tt autolinengy} is {\\tt .true.} then the fixed linearisation energies\nare set to the Fermi energy plus {\\tt dlefe}.\n"), ElkControlBlockInfo(:dncgga, ElkFlagInfo[ElkFlagInfo{Float64}(:dncgga, nothing, "small constant used to stabilise non-collinear GGA")], "\nThis small constant, \$d\$, is required in order to remove the infinite\ngradients obtained when using `Kubler's trick' in conjunction with GGA and\nnon-collinear magnetism. It is applied by calculating the up and down\ndensities as\n\$\$ \\rho^{\\uparrow}({\\bf r})=\\rho({\\bf r})+\\widetilde{m}({\\bf r})\n \\qquad \\rho^{\\downarrow}({\\bf r})=\\rho({\\bf r})-\\widetilde{m}({\\bf r}), \$\$\nwhere \$\\widetilde{m}({\\bf r})=\\sqrt{{\\bf m}^2({\\bf r})+d}\$,\nand should be taken as the smallest value for which the exchange-correlation\nmagnetic field \${\\bf B}_{\\rm xc}\$ is smooth.\n"), ElkControlBlockInfo(:dosmsum, ElkFlagInfo[ElkFlagInfo{Bool}(:dosmsum, false, "{.true.} if the partial DOS is to be summed over \$m\$")], "\nBy default, the partial density of states is resolved over \$(l,m)\$ quantum\nnumbers. If {\\tt dosmsum} is set to {\\tt .true.} then the partial DOS is\nsummed over \$m\$, and thus depends only on \$l\$.\n"), ElkControlBlockInfo(:dosssum, ElkFlagInfo[ElkFlagInfo{Bool}(:dosssum, false, "{.true.} if the partial DOS is to be summed over spin")], "\nBy default, the partial density of states for spin-polarised systems is spin\nresolved.\n"), ElkControlBlockInfo(:dtimes, ElkFlagInfo[ElkFlagInfo{Float64}(:dtimes, 1.0, "time step used in time evolution run")], "\nSee also {\\tt tstime}.\n"), ElkControlBlockInfo(:epsband, ElkFlagInfo[ElkFlagInfo{Float64}(:epsband, nothing, "convergence tolerance for determining band energies")], "\nAPW and local-orbital linearisation energies are determined from the band\nenergies. This is done by first searching upwards in energy until the radial\nwavefunction at the muffin-tin radius is zero. This is the energy at the top\nof the band, denoted \$E_{\\rm t}\$. A downward search is now performed from\n\$E_{\\rm t}\$ until the slope of the radial wavefunction at the muffin-tin\nradius is zero. This energy, \$E_{\\rm b}\$, is at the bottom of the band. The\nband energy is taken as \$(E_{\\rm t}+E_{\\rm b})/2\$. If either \$E_{\\rm t}\$ or\n\$E_{\\rm b}\$ is not found, then the band energy is set to the default value.\n"), ElkControlBlockInfo(:epschg, ElkFlagInfo[ElkFlagInfo{Float64}(:epschg, nothing, "maximum allowed error in the calculated total charge beyond\n which a warning message will be issued")], "\n"), ElkControlBlockInfo(:epsengy, ElkFlagInfo[ElkFlagInfo{Float64}(:epsengy, nothing, "convergence criterion for the total energy")], "\nSee {\\tt epspot}.\n"), ElkControlBlockInfo(:epsforce, ElkFlagInfo[ElkFlagInfo{Float64}(:epsforce, nothing, "convergence tolerance for the forces during a geometry\n optimisation run")], "\nIf the mean absolute value of the atomic forces is less than {\\tt epsforce}\nthen the geometry optimisation run is ended. See also {\\tt tasks} and\n{\\tt latvopt}.\n"), ElkControlBlockInfo(:epslat, ElkFlagInfo[ElkFlagInfo{Float64}(:epslat, nothing, "vectors with lengths less than this are considered zero")], "\nSets the tolerance for determining if a vector or its components are zero.\nThis is to account for any numerical error in real or reciprocal space\nvectors.\n"), ElkControlBlockInfo(:epsocc, ElkFlagInfo[ElkFlagInfo{Float64}(:epsocc, nothing, "smallest occupancy for which a state will contribute to the\n density")], "\n"), ElkControlBlockInfo(:epspot, ElkFlagInfo[ElkFlagInfo{Float64}(:epspot, nothing, "convergence criterion for the Kohn-Sham potential and field")], "\nIf the RMS change in the Kohn-Sham potential and magnetic field is smaller\nthan {\\tt epspot} and the absolute change in the total energy is less than\n{\\tt epsengy}, then the self-consistent loop is considered converged\nand exited. For geometry optimisation runs this results in the forces being\ncalculated, the atomic positions updated and the loop restarted. See also\n{\\tt epsengy} and {\\tt maxscl}.\n"), ElkControlBlockInfo(:epsstress, ElkFlagInfo[ElkFlagInfo{Float64}(:epsstress, nothing, "convergence tolerance for the stress tensor during a\n geometry optimisation run with lattice vector relaxation")], "\nSee also {\\tt epsforce} and {\\tt latvopt}.\n"), ElkControlBlockInfo(:emaxelnes, ElkFlagInfo[ElkFlagInfo{Float64}(:emaxelnes, nothing, "maximum allowed initial-state eigenvalue for ELNES\n calculations")], "\n"), ElkControlBlockInfo(:emaxrf, ElkFlagInfo[ElkFlagInfo{Float64}(:emaxrf, 1.0e7, "energy cut-off used when calculating Kohn-Sham response\n functions")], "\nA typical Kohn-Sham response function is of the form\n\\begin{align*}\n \\chi_s({\\bf r},{\\bf r}',\\omega)\n \\equiv\\frac{\\delta\\rho({\\bf r},\\omega)}{\\delta v_s({\\bf r}',\\omega)}\n =\\frac{1}{N_k}\\sum_{i{\\bf k},j{\\bf k}'}(f_{i{\\bf k}}-f_{j{\\bf k}'})\n \\frac{\\langle i{\\bf k}|\\hat{\\rho}({\\bf r})|j{\\bf k}'\\rangle\n \\langle j{\\bf k}'|\\hat{\\rho}({\\bf r}')|i{\\bf k}\\rangle}\n {w+(\\varepsilon_{i{\\bf k}}-\\varepsilon_{j{\\bf k}'})+i\\eta},\n\\end{align*}\nwhere \$\\hat{\\rho}\$ is the density operator; \$N_k\$ is the number of\n\$k\$-points; \$\\varepsilon_{i{\\bf k}}\$ and \$f_{i{\\bf k}}\$ are the eigenvalues\nand occupation numbers, respectively. The variable {\\tt emaxrf} is an energy\nwindow which limits the summation over states in the formula above so that\n\$|\\varepsilon_{i{\\bf k}}-\\varepsilon_{\\rm Fermi}|<{\\tt emaxrf}\$. Reducing\nthis can result in a faster calculation at the expense of accuracy.\n"), ElkControlBlockInfo(:fracinr, ElkFlagInfo[ElkFlagInfo{Float64}(:fracinr, 1.0, "fraction of the muffin-tin radius up to which {lmaxi}\n is used as the angular momentum cut-off")], "\nSee {\\tt lmaxi}.\n"), ElkControlBlockInfo(:fsmtype, ElkFlagInfo[ElkFlagInfo{Int64}(:fsmtype, 0, "0 for no fixed spin moment (FSM), 1 for total FSM, 2 for\n local muffin-tin FSM, and 3 for both total and local FSM")], "\nSet to 1, 2 or 3 for fixed spin moment calculations. To fix only the\ndirection and not the magnitude set to \$-1\$, \$-2\$ or \$-3\$. See also\n{\\tt momfix}, {\\tt mommtfix}, {\\tt taufsm} and {\\tt spinpol}.\n"), ElkControlBlockInfo(:ftmtype, ElkFlagInfo[ElkFlagInfo{Int64}(:ftmtype, 0, "1 to enable a fixed tensor moment (FTM) calculation,\n 0 otherwise")], "\nIf {\\tt ftmtype} is \$-1\$ then the symmetry corresponding to the tensor\nmoment is broken but no FTM calculation is performed. See {\\it Phys. Rev.\nLett.} {\\bf 103}, 107202 (2009) and also {\\tt tmomfix}.\n"), ElkControlBlockInfo(:fxclrc, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{2},Float64,1,2}}(:fxclrc, [0.0, 0.0], "parameters for the dynamical long-range contribution (LRC) to\n the TDDFT exchange-correlation kernel")], "\nThese are the parameters \$\\alpha\$ and \$\\beta\$ for the kernel proposed in\n{\\it Phys. Rev. B} {\\bf 72}, 125203 (2005), namely\n\$\$ f_{xc}({\\bf G},{\\bf G}',{\\bf q},\\omega)=-\\frac{\\alpha+\\beta\\omega^2}{q^2}\n \\delta_{{\\bf G},{\\bf G}'}\\delta_{{\\bf G},{\\bf 0}}. \$\$\n"), ElkControlBlockInfo(:fxctype, ElkFlagInfo[ElkFlagInfo{Int64}(:fxctype, nothing, "integer defining the type of exchange-correlation kernel\n \$f_{\\rm xc}\$")], "\nThe acceptable values are:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n\$-1\$ & \$f_{\\rm xc}\$ defined by {\\tt xctype} \\\\\n0,1 & RPA (\$f_{\\rm xc}=0\$) \\\\\n200 & Long-range contribution (LRC) kernel, S. Botti {\\it et al.},\n {\\it Phys. Rev. B} {\\bf 72}, 125203 (2005); see {\\tt fxclrc} \\\\\n210 & `Bootstrap' kernel, S. Sharma, J. K. Dewhurst, A. Sanna and\n E. K. U. Gross, {\\it Phys. Rev. Lett.} {\\bf 107}, 186401 (2011) \\\\\n211 & Single iteration bootstrap\n\\end{tabularx}\n"), ElkControlBlockInfo(:gmaxrf, ElkFlagInfo[ElkFlagInfo{Float64}(:gmaxrf, 30.0, "maximum length of \$|{\\bf G}|\$ for computing response\n functions")], "\n"), ElkControlBlockInfo(:gmaxvr, ElkFlagInfo[ElkFlagInfo{Float64}(:gmaxvr, 120.0, "maximum length of \$|{\\bf G}|\$ for expanding the interstitial\n density and potential")], "\nThis variable has a lower bound which is enforced by the code as follows:\n\$\$ {\\rm gmaxvr}\\rightarrow\\max\\,({\\rm gmaxvr},2\\times{\\rm gkmax}\n +{\\rm epslat}) \$\$\nSee {\\tt rgkmax}.\n"), ElkControlBlockInfo(:gwdiag, ElkFlagInfo[ElkFlagInfo{Int64}(:gwdiag, 1, "type of diagonal approximation for the \$GW\$ self-energy")], "\nCalculation of the \$GW\$ self-energy \$\\Sigma\$ can be made faster, at the\nexpense of accuracy, by taking \$\\Sigma_{ij}(i\\omega)\$ or\nthe correlation part of the interaction,\n\$W_c({\\bf G},{\\bf G}',{\\bf q},i\\omega)\$, to be diagonal. The choices are\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n0 & \$\\Sigma\$ and \$W_c\$ are full matrices \\\\\n1 & \$\\Sigma\$ is diagonal in the orbital indices \$i,j\$ \\\\\n2 & \$\\Sigma\$ as with 1 but \$W_c\$ is also diagonal in \${\\bf G},{\\bf G}'\$\n\\end{tabularx}\n"), ElkControlBlockInfo(:hdbse, ElkFlagInfo[ElkFlagInfo{Bool}(:hdbse, true, "{.true.} if the direct term is to be included in the BSE\n Hamiltonian")], "\n"), ElkControlBlockInfo(:highq, ElkFlagInfo[ElkFlagInfo{Bool}(:highq, false, "{.true.} if a high quality parameter set should be used")], "\nSetting this to {\\tt .true.} results in some default parameters being\nchanged to ensure good convergence in most situations. These changes can be\noverruled by subsequent blocks in the input file. See also {\\tt vhighq}.\n"), ElkControlBlockInfo(:hmaxvr, ElkFlagInfo[ElkFlagInfo{Float64}(:hmaxvr, 60.0, "maximum length of \${\\bf H}\$-vectors")], "\nThe \${\\bf H}\$-vectors are used for calculating X-ray and magnetic structure\nfactors. They are also used in linear response phonon calculations for\nexpanding the density and potential in plane waves. See also {\\tt gmaxvr},\n{\\tt vhmat}, {\\tt reduceh}, {\\tt wsfac} and {\\tt hkmax}.\n"), ElkControlBlockInfo(:hxbse, ElkFlagInfo[ElkFlagInfo{Bool}(:hxbse, true, "{.true.} if the exchange term is to be included in the BSE\n Hamiltonian")], "\n"), ElkControlBlockInfo(:hybrid, ElkFlagInfo[ElkFlagInfo{Bool}(:hybrid, false, "{.true} if a hybrid functional is to be used when running\n a Hartree-Fock calculation")], "\nSee also {\\tt hybridc} and {\\tt xctype}.\n"), ElkControlBlockInfo(:hybridc, ElkFlagInfo[ElkFlagInfo{Float64}(:hybridc, 10.0, "hybrid functional mixing coefficient")], "\n"), ElkControlBlockInfo(:intraband, ElkFlagInfo[ElkFlagInfo{Bool}(:intraband, false, "{.true.} if the intraband (Drude-like) contribution is\n to be added to the dieletric tensor")], "\n"), ElkControlBlockInfo(:isgkmax, ElkFlagInfo[ElkFlagInfo{Int64}(:isgkmax, nothing, "species for which the muffin-tin radius will be used for\n calculating {gkmax}")], "\nThe APW cut-off is determined from \${\\tt gkmax}={\\tt rgkmax}/R\$. The\nvariable {\\tt isgkmax} determines which muffin-tin radius is to be used for\n\$R\$. These are the options:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n-4 & Use the largest radius \\\\\n-3 & Use the smallest radius \\\\\n-2 & Use the fixed value \$R=2.0\$ \\\\\n-1 & Use the average of the muffin-tin radii \\\\\n\$n\\ge 1\$ & Use the radius of species \$n\$\n\\end{tabularx}\n"), ElkControlBlockInfo(:kstlist, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{2},Int64,1,2}}(:kstlist, [1, 1], "\$i\$th \$k\$-point and state pair")], "\nThis is a user-defined list of \$k\$-point and state index pairs which are\nthose used for plotting wavefunctions and writing \${\\bf L}\$, \${\\bf S}\$ and\n\${\\bf J}\$ expectation values. Only the first pair is used by the\naforementioned tasks. The list should be terminated by a blank line.\n"), ElkControlBlockInfo(:latvopt, ElkFlagInfo[ElkFlagInfo{Int64}(:latvopt, 0, "type of lattice vector optimisation to be performed during\n structural relaxation")], "\nUnconstrained optimisation of the lattice vectors will be performed with\n\${\\tt task}=2,3\$ when \${\\tt latvopt}=1\$. When \${\\tt latvopt}=2\$ the lattice\nvectors will be optimised while keeping the unit cell volume fixed. In\neither case, the crystal symmetry is retained during optimisation. By\ndefault (\${\\tt latvopt}=0\$) no lattice vector optimisation is performed\nduring structural relaxation. See also {\\tt tau0latv}.\n"), ElkControlBlockInfo(:lmaxapw, ElkFlagInfo[ElkFlagInfo{Int64}(:lmaxapw, 8, "angular momentum cut-off for the APW functions")], "\n"), ElkControlBlockInfo(:lmaxdos, ElkFlagInfo[ElkFlagInfo{Int64}(:lmaxdos, 3, "angular momentum cut-off for the partial DOS plot")], "\n"), ElkControlBlockInfo(:lmaxi, ElkFlagInfo[ElkFlagInfo{Int64}(:lmaxi, 2, "angular momentum cut-off for the muffin-tin density and\n potential on the inner part of the muffin-tin")], "\nClose to the nucleus, the density and potential is almost spherical and\ntherefore the spherical harmonic expansion can be truncated a low angular\nmomentum. See also {\\tt fracinr}.\n"), ElkControlBlockInfo(:lmaxo, ElkFlagInfo[ElkFlagInfo{Int64}(:lmaxo, 7, "angular momentum cut-off for the muffin-tin density and\n potential")], "\n"), ElkControlBlockInfo(:lmirep, ElkFlagInfo[ElkFlagInfo{Bool}(:lmirep, true, "{.true.} if the \$Y_{lm}\$ basis is to be transformed\n into the basis of irreducible representations of the site symmetries for\n DOS plotting")], "\nWhen lmirep is set to .true., the spherical harmonic basis is transformed\ninto one in which the site symmetries are block diagonal. Band characters\ndetermined from the density matrix expressed in this basis correspond to\nirreducible representations, and allow the partial DOS to be resolved into\nphysically relevant contributions, for example \$e_g\$ and \$t_{2g}\$.\n"), ElkControlBlockInfo(:lorbcnd, ElkFlagInfo[ElkFlagInfo{Bool}(:lorbcnd, false, "{.true.} if conduction state local-orbitals are to be\n automatically added to the basis")], "\nAdding these higher energy local-orbitals can improve calculations which\nrely on accurate unoccupied states, such as the response function. See also\n{\\tt lorbordc}.\n"), ElkControlBlockInfo(:lorbordc, ElkFlagInfo[ElkFlagInfo{Int64}(:lorbordc, 3, "the order of the conduction state local-orbitals")], "\nSee {\\tt lorbcnd}.\n"), ElkControlBlockInfo(:lradstp, ElkFlagInfo[ElkFlagInfo{Int64}(:lradstp, 4, "radial step length for determining coarse radial mesh")], "\nSome muffin-tin functions (such as the density) are calculated on a coarse\nradial mesh and then interpolated onto a fine mesh. This is done for the\nsake of efficiency. {\\tt lradstp} defines the step size in going from the\nfine to the coarse radial mesh. If it is too large, loss of precision may\noccur.\n"), ElkControlBlockInfo(:maxitoep, ElkFlagInfo[ElkFlagInfo{Int64}(:maxitoep, 200, "maximum number of iterations when solving the exact\n exchange integral equations")], "\nSee {\\tt tau0oep}.\n"), ElkControlBlockInfo(:maxscl, ElkFlagInfo[ElkFlagInfo{Int64}(:maxscl, 200, "maximum number of self-consistent loops allowed")], "\nThis determines after how many loops the self-consistent cycle will\nterminate if the convergence criterion is not met. If {\\tt maxscl} is \$1\$\nthen the density and potential file, {\\tt STATE.OUT}, will {\\bf not} be\nwritten to disk at the end of the loop. See {\\tt epspot}.\n"), ElkControlBlockInfo(:mixtype, ElkFlagInfo[ElkFlagInfo{Int64}(:mixtype, 1, "type of mixing required for the potential")], "\nCurrently implemented are:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n0 & Linear mixing \\\\\n1 & Adaptive linear mixing \\\\\n3 & Broyden mixing, {\\it J. Phys. A: Math. Gen.} {\\bf 17}, L317 (1984)\n\\end{tabularx}\n"), ElkControlBlockInfo(:mixsdb, ElkFlagInfo[ElkFlagInfo{Int64}(:mixsdb, 5, "subspace dimension for Broyden mixing")], "\nThis is the number of mixing vectors which define the subspace in which the\nHessian matrix is calculated. See {\\tt mixtype} and {\\tt broydpm}.\n"), ElkControlBlockInfo(:molecule, ElkFlagInfo[ElkFlagInfo{Bool}(:molecule, false, "{.true.} if the system is an isolated molecule")], "\nIf {\\tt molecule} is {\\tt .true.}, then the atomic positions, \${\\bf a}\$,\ngiven in the {\\tt atoms} block are assumed to be in Cartesian coordinates.\n"), ElkControlBlockInfo(:momfix, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:momfix, [0.0, 0.0, 0.0], "the desired total moment for a FSM calculation")], "\nNote that all three components must be specified (even for collinear\ncalculations). See {\\tt fsmtype}, {\\tt taufsm} and {\\tt spinpol}.\n"), ElkControlBlockInfo(:mommtfix, ElkFlagInfo[ElkFlagInfo{Int64}(:is, 0, "species number"), ElkFlagInfo{Int64}(:ia, 0, "atom number"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:mommtfix, [0.0, 0.0, 0.0], "the desired muffin-tin moment for a FSM calculation")], "\nThe local muffin-tin moments are specified for a subset of atoms, with the\nlist terminated with a blank line. Note that all three components must be\nspecified (even for collinear calculations). See {\\tt fsmtype}, {\\tt taufsm}\nand {\\tt spinpol}.\n"), ElkControlBlockInfo(:msmooth, ElkFlagInfo[ElkFlagInfo{Int64}(:msmooth, 0, "amount of smoothing to be applied to the\n exchange-correlation potentials and magnetic field")], "\nSmoothing operations can be applied to the exchange-correlation potentials\n\$v_{xc}\$, \$w_{xc}\$ and the magnetic field \${\\bf B}_{xc}\$ in order to improve\nconvergence. In the muffin-tin, this smoothing takes the form of \$m\$\nsuccessive three-point running averages applied to the radial component. In\nthe interstitial region, the potential is first Fourier transformed to\n\$G\$-space, then a low-pass filter of the form \$\\exp[-2m(G/G_{\\rm max})^8]\$\nis applied and the function is transformed back to real-space.\n"), ElkControlBlockInfo(:mstar, ElkFlagInfo[ElkFlagInfo{Float64}(:mstar, 100.0, "value of the effective mass parameter used for adaptive\n determination of {swidth}")], "\nSee {\\tt autoswidth}.\n"), ElkControlBlockInfo(:mustar, ElkFlagInfo[ElkFlagInfo{Float64}(:mustar, 15.0, "Coulomb pseudopotential, \$\\mue*\$, used in the\n McMillan-Allen-Dynes equation")], "\nThis is used when calculating the superconducting critical temperature with\nthe formula {\\it Phys. Rev. B 12, 905 (1975)}\n\$\$ T_c=\\frac{\\omega_{\\rm log}}{1.2 k_B}\\exp\\left[\\frac{-1.04(1+\\lambda)}\n {\\lambda-\\mu^*(1+0.62\\lambda)}\\right], \$\$\nwhere \$\\omega_{\\rm log}\$ is the logarithmic average frequency and \$\\lambda\$\nis the electron-phonon coupling constant.\n"), ElkControlBlockInfo(:ncbse, ElkFlagInfo[ElkFlagInfo{Int64}(:ncbse, 3, "number of conduction states to be used for BSE calculations")], "\nSee also {\\tt nvbse}.\n"), ElkControlBlockInfo(:ndspem, ElkFlagInfo[ElkFlagInfo{Int64}(:ndspem, 1, "the number of {\\bf k}-vector displacements in each direction\n around {vklem} when computing the numerical derivatives for the\n effective mass tensor")], "\nSee {\\tt deltaem} and {\\tt vklem}.\n"), ElkControlBlockInfo(:nempty, ElkFlagInfo[ElkFlagInfo{Float64}(:nempty, 40.0, "the number of empty states per atom and spin")], "\nDefines the number of eigenstates beyond that required for charge\nneutrality. When running metals it is not known {\\it a priori} how many\nstates will be below the Fermi energy for each \$k\$-point. Setting\n{\\tt nempty} greater than zero allows the additional states to act as a\nbuffer in such cases. Furthermore, magnetic calculations use the\nfirst-variational eigenstates as a basis for setting up the\nsecond-variational Hamiltonian, and thus {\\tt nempty} will determine the\nsize of this basis set. Convergence with respect to this quantity should be\nchecked.\n"), ElkControlBlockInfo(:ngridk, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Int64,1,3}}(:ngridk, [1, 1, 1], "the \$k\$-point mesh sizes")], "\nThe \${\\bf k}\$-vectors are generated using\n\$\$ {\\bf k}=(\\frac{i_1+v_1}{n_1},\\frac{i_2+v_2}{n_2},\\frac{i_3+v_3}{n_3}), \$\$\nwhere \$i_j\$ runs from 0 to \$n_j-1\$ and \$0\\le v_j<1\$ for \$j=1,2,3\$. The\nvector \${\\bf v}\$ is given by the variable {\\tt vkloff}. See also\n{\\tt reducek}.\n"), ElkControlBlockInfo(:ngridq, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Int64,1,3}}(:ngridq, [1, 1, 1], "the phonon \$q\$-point mesh sizes")], "\nSame as {\\tt ngridk}, except that this mesh is for the phonon \$q\$-points\nand other tasks. See also {\\tt reduceq}.\n"), ElkControlBlockInfo(:nosource, ElkFlagInfo[ElkFlagInfo{Bool}(:nosource, false, "when set to {.true.}, source fields are projected out\n of the exchange-correlation magnetic field")], "\nExperimental feature.\n"), ElkControlBlockInfo(:notes, ElkFlagInfo[ElkFlagInfo{String}(:notes, "-}", "the \$i\$th line of the notes")], "\nThis block allows users to add their own notes to the file {\\tt INFO.OUT}.\nThe block should be terminated with a blank line, and no line should exceed\n80 characters.\n"), ElkControlBlockInfo(:npmae, ElkFlagInfo[ElkFlagInfo{Int64}(:npmae, nothing, "number or distribution of directions for MAE calculations")], "\nAutomatic determination of the magnetic anisotropy energy (MAE) requires\nthat the total energy is determined for a set of directions of the total\nmagnetic moment. This variable controls the number or distribution of these\ndirections. The convention is:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n\$-4,-3,-2,-1\$ & Cardinal directions given by the primitive translation\n vectors \$n_1{\\bf A}_1+n_2{\\bf A}_2+n_3{\\bf A}_3\$, where\n \$1\\le n_i\\le|{\\tt npmae}|\$ \\\\\n2 & Cartesian \$x\$ and \$z\$ directions \\\\\n3 & Cartesian \$x\$, \$y\$ and \$z\$ directions \\\\\n\$4,5,\\ldots\$ & Even distribution of {\\tt npmae} directions\n\\end{tabularx}\n"), ElkControlBlockInfo(:ntemp, ElkFlagInfo[ElkFlagInfo{Int64}(:ntemp, 40, "number of temperature steps")], "\nThis is the number of temperature steps to be used in the Eliashberg gap\nand thermodynamic properties calculations.\n"), ElkControlBlockInfo(:nvbse, ElkFlagInfo[ElkFlagInfo{Int64}(:nvbse, 2, "number of valence states to be used for BSE calculations")], "\nSee also {\\tt ncbse}.\n"), ElkControlBlockInfo(:nwrite, ElkFlagInfo[ElkFlagInfo{Int64}(:nwrite, 0, "number of self-consistent loops after which {STATE.OUT}\n is to be written")], "\nNormally, the density and potentials are written to the file {\\tt STATE.OUT}\nonly after completion of the self-consistent loop. By setting {\\tt nwrite}\nto a positive integer the file will instead be written every {\\tt nwrite}\nloops.\n"), ElkControlBlockInfo(:nxoapwlo, ElkFlagInfo[ElkFlagInfo{Int64}(:nxoapwlo, 0, "extra order of radial functions to be added to the existing\n APW and local-orbital set")], "\nSetting this variable will result in the APWs and local-orbitals for all\nspecies becoming higher order with corresponding increase in derivative\nmatching at the muffin-tin surface. For example, setting {\\tt nxoapwlo}=1\nturns all APWs into LAPWs.\n"), ElkControlBlockInfo(:optcomp, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Int64,1,3}}(:optcomp, [1, 1, 1], "the components of the first- or second-order optical tensor\n to be calculated")], "\nThis selects which components of the optical tensor you would like to plot.\nOnly the first two are used for the first-order tensor. Several components\ncan be listed one after the other with a blank line terminating the list.\n"), ElkControlBlockInfo(:phwrite, ElkFlagInfo[ElkFlagInfo{Int64}(:nphwrt, 1, "number of \$q\$-points for which phonon modes are to be found"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vqlwrt, [0.0, 0.0, 0.0], "the \$i\$th \$q\$-point in lattice coordinates")], "\nThis is used in conjunction with {\\tt task}=230. The code will write the\nphonon frequencies and eigenvectors to the file {\\tt PHONON.OUT} for all the\n\$q\$-points in the list. The \$q\$-points can be anywhere in the Brillouin zone\nand do not have to lie on the mesh defined by {\\tt ngridq}. Obviously, all\nthe dynamical matrices have to be computed first using {\\tt task}=200.\n"), ElkControlBlockInfo(:plot1d, ElkFlagInfo[ElkFlagInfo{Int64}(:nvp1d, 2, "number of vertices"), ElkFlagInfo{Int64}(:npp1d, 200, "number of plotting points"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vvlp1d, [0.0, 0.0, 0.0], "lattice coordinates for vertex \$i\$")], "\nDefines the path in either real or reciprocal space along which the 1D plot\nis to be produced. The user should provide {\\tt nvp1d} vertices in lattice\ncoordinates.\n"), ElkControlBlockInfo(:plot2d, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vclp2d, [0.0, 0.0, 0.0], "zeroth corner (origin)"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vclp2d, [10.0, 0.0, 0.0], "first corner"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vclp2d, [0.0, 10.0, 0.0], "second corner"), ElkFlagInfo{SArray{Tuple{2},Int64,1,2}}(:np2d, [40, 40], "number of plotting points in both directions")], "\nDefines the corners of a parallelogram and the grid size used for producing\n2D plots.\n"), ElkControlBlockInfo(:plot3d, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vclp3d, [0.0, 0.0, 0.0], "zeroth corner (origin)"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vclp3d, [10.0, 0.0, 0.0], "first corner"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vclp3d, [0.0, 10.0, 0.0], "second corner"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vclp3d, [0.0, 0.0, 10.0], "third corner"), ElkFlagInfo{SArray{Tuple{3},Int64,1,3}}(:np3d, [20, 20, 20], "number of plotting points each direction")], "\nDefines the corners of a box and the grid size used for producing 3D plots.\n"), ElkControlBlockInfo(:primcell, ElkFlagInfo[ElkFlagInfo{Bool}(:primcell, false, "{.true.} if the primitive unit cell should be found")], "\nAllows the primitive unit cell to be determined automatically from the\nconventional cell. This is done by searching for lattice vectors among all\nthose which connect atomic sites, and using the three shortest which produce\na unit cell with non-zero volume.\n"), ElkControlBlockInfo(:pulse, ElkFlagInfo[ElkFlagInfo{Int64}(:n, nothing, "number of pulses"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:a0, nothing, "polarisation vector (including amplitude)"), ElkFlagInfo{Float64}(:w, nothing, "frequency"), ElkFlagInfo{Float64}(:phi, nothing, "phase in degrees"), ElkFlagInfo{Float64}(:rc, nothing, "chirp rate"), ElkFlagInfo{Float64}(:t0, nothing, "peak time"), ElkFlagInfo{Float64}(:d, nothing, "full-width at half-maximum")], "\nParameters used to generate a time-dependent vector potential \${\\bf A}(t)\$\nrepresenting a laser pulse. The total vector potential is the sum of\nindividual pulses and is given by the formula\n\$\$ {\\bf A}(t)=\\sum_{i=1}^n {\\bf A}_0^i\\exp\n \\left[-(t-t_0^i)^2/2\\sigma_i^2\\right]\n \\sin\\left[w_i(t-t_0^i)+\\phi_i+r_{\\rm c}^i t^2/2\\right], \$\$\nwhere \$\\sigma=d/2\\sqrt{2\\ln 2}\$. See also {\\tt ramp}.\n"), ElkControlBlockInfo(:radkpt, ElkFlagInfo[ElkFlagInfo{Float64}(:radkpt, 400.0, "radius of sphere used to determine \$k\$-point density")], "\nUsed for the automatic determination of the \$k\$-point mesh. If {\\tt autokpt}\nis set to {\\tt .true.} then the mesh sizes will be determined by\n\$n_i=R_k|{\\bf B}_i|+1\$, where \${\\bf B}_i\$ are the primitive reciprocal\nlattice vectors.\n"), ElkControlBlockInfo(:ramp, ElkFlagInfo[ElkFlagInfo{Int64}(:n, nothing, "number of ramps"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:a0, nothing, "polarisation vector (including amplitude)"), ElkFlagInfo{Float64}(:t0, nothing, "ramp start time"), ElkFlagInfo{Float64}(:c1, nothing, "linear coefficient of \${\\bf A}(t)\$"), ElkFlagInfo{Float64}(:c2, nothing, "quadratic coefficient")], "\nParameters used to generate a time-dependent vector potential \${\\bf A}(t)\$\nrepresenting a constant or linearly increasing electric field\n\${\\bf E}(t)=-\\partial{\\bf A}(t)/\\partial t\$. The vector potential is given\nby\n\$\$ {\\bf A}(t)=\\sum_{i=1}^n {\\bf A}_0^i\n \\left[c_1(t-t_0)+c_2(t-t_0)^2\\right]\\Theta(t-t_0). \$\$\n"), ElkControlBlockInfo(:readadu, ElkFlagInfo[ElkFlagInfo{Bool}(:readadu, false, "set to {.true.} if the interpolation constant for\n DFT+\$U\$ should be read from file rather than calculated")], "\nWhen {\\tt dftu}=3, the DFT+\$U\$ energy and potential are interpolated\nbetween FLL and AFM. The interpolation constant, \$\\alpha\$, is normally\ncalculated from the density matrix, but can also be read in from the file\n{\\tt ALPHADU.OUT}. This allows the user to fix \$\\alpha\$, but is also\nnecessary when calculating forces, since the contribution of the potential\nof the variation of \$\\alpha\$ with respect to the density matrix is not\ncomputed. See {\\tt dft+u}.\n"), ElkControlBlockInfo(:reducebf, ElkFlagInfo[ElkFlagInfo{Float64}(:reducebf, 10.0, "reduction factor for the external magnetic fields")], "\nAfter each self-consistent loop, the external magnetic fields are multiplied\nwith {\\tt reducebf}. This allows for a large external magnetic field at the\nstart of the self-consistent loop to break spin symmetry, while at the end\nof the loop the field will be effectively zero, i.e. infinitesimal. See\n{\\tt bfieldc} and {\\tt atoms}.\n"), ElkControlBlockInfo(:reduceh, ElkFlagInfo[ElkFlagInfo{Bool}(:reduceh, true, "set to {.true.} if the reciprocal \${\\bf H}\$-vectors\n should be reduced by the symmorphic crystal symmetries")], "\nSee {\\tt hmaxvr} and {\\tt vmat}.\n"), ElkControlBlockInfo(:reducek, ElkFlagInfo[ElkFlagInfo{Int64}(:reducek, 1, "type of reduction of the \$k\$-point set")], "\nTypes of reduction are defined by the symmetry group used:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n0 & no reduction \\\\\n1 & reduce with full crystal symmetry group (including non-symmorphic\n symmetries) \\\\\n2 & reduce with symmorphic symmetries only\n\\end{tabularx}\n\\vskip 6pt\nSee also {\\tt ngridk} and {\\tt vkloff}.\n"), ElkControlBlockInfo(:reduceq, ElkFlagInfo[ElkFlagInfo{Int64}(:reduceq, 1, "type of reduction of the \$q\$-point set")], "\nSee {\\tt reducek} and {\\tt ngridq}.\n"), ElkControlBlockInfo(:rgkmax, ElkFlagInfo[ElkFlagInfo{Float64}(:rgkmax, 70.0, "\$Re{\\rm MT}_{\\rm min}\\times\\max\\{|{\\bf G}+{\\bf k}|\\}\$")], "\nThis sets the maximum length for the \${\\bf G}+{\\bf k}\$ vectors, defined as\n{\\tt rgkmax} divided by the average muffin-tin radius. See {\\tt isgkmax}.\n"), ElkControlBlockInfo(:rotavec, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{4},Float64,1,4}}(:axang, [0.0, 0.0, 0.0, 0.0], "axis-angle representation of lattice vector rotation")], "\nThis determines the rotation matrix which is applied to the lattice vectors\nprior to any calculation. The first three components specify the axis and\nthe last component is the angle in degrees. The `right-hand rule' convention\nis followed.\n"), ElkControlBlockInfo(:scale, ElkFlagInfo[ElkFlagInfo{Float64}(:scale, 10.0, "lattice vector scaling factor")], "\nScaling factor for all three lattice vectors. Applied in conjunction with\n{\\tt scale1}, {\\tt scale2} and {\\tt scale3}.\n"), ElkControlBlockInfo(Symbol("scale1/2/3"), ElkFlagInfo[ElkFlagInfo{Float64}(Symbol("scale1/2/3"), 10.0, "separate scaling factors for each lattice vector")], "\n"), ElkControlBlockInfo(:scissor, ElkFlagInfo[ElkFlagInfo{Float64}(:scissor, 0.0, "the scissor correction")], "\nThis is the scissor shift applied to states above the Fermi energy\n{\\it Phys. Rev. B} {\\bf 43}, 4187 (1991). Affects optics calculations only.\n"), ElkControlBlockInfo(:scrpath, ElkFlagInfo[ElkFlagInfo{String}(:scrpath, "null}", "scratch space path")], "\nThis is the scratch space path where the eigenvector files {\\tt EVALFV.OUT}\nand {\\tt EVALSV.OUT} will be written. If the run directory is accessed via a\nnetwork then {\\tt scrpath} can be set to a directory on the local disk, for\nexample {\\tt /tmp/}. Note that the forward slash {\\tt /} at the end of the\npath must be included.\n"), ElkControlBlockInfo(:socscf, ElkFlagInfo[ElkFlagInfo{Float64}(:socscf, 10.0, "scaling factor for the spin-orbit coupling term in the\n Hamiltonian")], "\nThis can be used to enhance the effect of spin-orbit coupling in order to\naccurately determine the magnetic anisotropy energy (MAE).\n"), ElkControlBlockInfo(:spincore, ElkFlagInfo[ElkFlagInfo{Bool}(:spincore, false, "set to {.true.} if the core should be spin-polarised")], "\n"), ElkControlBlockInfo(:spinorb, ElkFlagInfo[ElkFlagInfo{Bool}(:spinorb, false, "set to {.true.} if a spin-orbit coupling is required")], "\nIf {\\tt spinorb} is {\\tt .true.}, then a \$\\boldsymbol\\sigma\\cdot{\\bf L}\$\nterm is added to the second-variational Hamiltonian. See {\\tt spinpol}.\n"), ElkControlBlockInfo(:spinpol, ElkFlagInfo[ElkFlagInfo{Bool}(:spinpol, false, "set to {.true.} if a spin-polarised calculation is\n required")], "\nIf {\\tt spinpol} is {\\tt .true.}, then the spin-polarised Hamiltonian is\nsolved as a second-variational step using two-component spinors in the\nKohn-Sham magnetic field. The first variational scalar wavefunctions are\nused as a basis for setting this Hamiltonian.\n"), ElkControlBlockInfo(:spinsprl, ElkFlagInfo[ElkFlagInfo{Bool}(:spinsprl, false, "set to {.true.} if a spin-spiral calculation is\nrequired")], "\nExperimental feature for the calculation of spin-spiral states. See\n{\\tt vqlss} for details.\n"), ElkControlBlockInfo(:sppath, ElkFlagInfo[ElkFlagInfo{String}(:sppath, "null}", "path where the species files can be found")], "\nNote that the forward slash {\\tt /} at the end of the path must be included.\n"), ElkControlBlockInfo(:ssdph, ElkFlagInfo[ElkFlagInfo{Bool}(:ssdph, true, "set to {.true.} if a complex de-phasing factor is to be\n used in spin-spiral calculations")], "\nIf this is {\\tt .true.} then spin-spiral wavefunctions in each muffin-tin at\nposition \${\\bf r}_{\\alpha}\$ are de-phased by the matrix\n\$\$ \\begin{pmatrix} e^{-i{\\bf q}\\cdot{\\bf r}_{\\alpha}/2} & 0 \\\\\n 0 & e^{i{\\bf q}\\cdot{\\bf r}_{\\alpha}/2} \\end{pmatrix}. \$\$\nIn simple situations, this has the advantage of producing magnon dynamical\nmatrices which are already in diagonal form. This option should be used with\ncare, and a full understanding of the spin-spiral configuration is required.\nSee {\\tt spinsprl}.\n"), ElkControlBlockInfo(:stype, ElkFlagInfo[ElkFlagInfo{Int64}(:stype, 3, "integer defining the type of smearing to be used")], "\nA smooth approximation to the Dirac delta function is needed to compute the\noccupancies of the Kohn-Sham states. The variable {\\tt swidth} determines\nthe width of the approximate delta function. Currently implemented are\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n0 & Gaussian \\\\\n1 & Methfessel-Paxton order 1, Phys. Rev. B {\\bf 40}, 3616 (1989) \\\\\n2 & Methfessel-Paxton order 2 \\\\\n3 & Fermi-Dirac\n\\end{tabularx}\n\\vskip 6pt\nSee also {\\tt autoswidth}, {\\tt swidth} and {\\tt tempk}.\n"), ElkControlBlockInfo(:swidth, ElkFlagInfo[ElkFlagInfo{Float64}(:swidth, 1.0, "width of the smooth approximation to the Dirac delta\n function")], "\nSee {\\tt stype} for details and the variable {\\tt tempk}.\n\\newpage\n"), ElkControlBlockInfo(:tasks, ElkFlagInfo[ElkFlagInfo{Int64}(:task, nothing, "the \$i\$th task")], "\nA list of tasks for the code to perform sequentially. The list should be\nterminated with a blank line. Each task has an associated integer as\nfollows:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n-1 & Write out the version number of the code. \\\\\n0 & Ground state run starting from the atomic densities. \\\\\n1 & Resumption of ground-state run using density in {\\tt STATE.OUT}. \\\\\n2 & Geometry optimisation run starting from the atomic densities, with\n atomic positions written to {\\tt GEOMETRY.OUT}. \\\\\n3 & Resumption of geometry optimisation run using density in {\\tt STATE.OUT}\n but with positions from {\\tt elk.in}. \\\\\n5 & Ground state Hartree-Fock run. \\\\\n10 & Total, partial and interstitial density of states (DOS). \\\\\n14 & Plots the smooth Dirac delta and Heaviside step functions used by the\n     code to calculate occupancies. \\\\\n15 & Output \${\\bf L}\$, \${\\bf S}\$ and \${\\bf J}\$ total expectation values. \\\\\n16 & Output \${\\bf L}\$, \${\\bf S}\$ and \${\\bf J}\$ expectation values for each\n     \$k\$-point and state in {\\tt kstlist}. \\\\\n20 & Band structure plot. \\\\\n21 & Band structure plot which includes angular momentum characters for\n every atom. \\\\\n25 & Compute the effective mass tensor at the \$k\$-point given by\n {\\tt vklem}. \\\\\n31, 32, 33 & 1/2/3D charge density plot. \\\\\n41, 42, 43 & 1/2/3D exchange-correlation and Coulomb potential plots. \\\\\n51, 52, 53 & 1/2/3D electron localisation function (ELF) plot. \\\\\n61, 62, 63 & 1/2/3D wavefunction plot:\n \$\\left|\\Psi_{i{\\bf k}}({\\bf r})\\right|^2\$. \\\\\n65 & Write the core wavefunctions to file for plotting. \\\\\n72, 73 & 2/3D plot of magnetisation vector field, \${\\bf m}({\\bf r})\$. \\\\\n82, 83 & 2/3D plot of exchange-correlation magnetic vector field,\n \${\\bf B}_{\\rm xc}({\\bf r})\$. \\\\\n91, 92, 93 & 1/2/3D plot of \$\\nabla\\cdot{\\bf B}_{\\rm xc}({\\bf r})\$. \\\\\n100 & 3D Fermi surface plot using the scalar product\n \$p({\\bf k})=\\Pi_i(\\epsilon_{i{\\bf k}}-\\epsilon_{\\rm F})\$. \\\\\n101 & 3D Fermi surface plot using separate bands (minus the Fermi\n energy). \\\\\n102 & 3D Fermi surface which can be plotted with XCrysDen. \\\\\n105 & 3D nesting function plot. \\\\\n110 & Calculation of M\\\"{o}ssbauer contact charge densities and magnetic\n fields at the nuclear sites. \\\\\n115 & Calculation of the electric field gradient (EFG) at the nuclear\n sites. \\\\\n120 & Output of the momentum matrix elements\n \$\\langle\\Psi_{i{\\bf k}}|-i\\nabla|\\Psi_{j{\\bf k}}\\rangle\$. \\\\\n121 & Linear optical dielectric response tensor calculated within the random\n phase approximation (RPA) and in the \$q\\rightarrow 0\$ limit, with no\n microscopic contributions. \\\\\n122 & Magneto optical Kerr effect (MOKE) angle. \\\\\n125 & Non-linear optical second harmonic generation. \\\\\n130 & Output matrix elements of the type\n \$\\langle\\Psi_{i{\\bf k+q}}|\\exp[i({\\bf G+q})\\cdot{\\bf r}]|\n \\Psi_{j{\\bf k}}\\rangle\$. \\\\\n135 & Output all wavefunctions expanded in the plane wave basis up to a\n cut-off defined by {\\tt rgkmax}.\n\\end{tabularx}\n\\begin{tabularx}{\\textwidth}[h]{lX}\n140 & Energy loss near edge structure (ELNES). \\\\\n142, 143 & 2/3D plot of the electric field\n \${\\bf E}({\\bf r})\\equiv\\nabla V_{\\rm C}({\\bf r})\$. \\\\\n152, 153 & 2/3D plot of\n \${\\bf m}({\\bf r})\\times{\\bf B}_{\\rm xc}({\\bf r})\$. \\\\\n162 & Scanning-tunneling microscopy (STM) image. \\\\\n180 & Generate the RPA inverse dielectric function with local contributions\n and write it to file. \\\\\n185 & Write the Bethe-Salpeter equation (BSE) Hamiltonian to file. \\\\\n186 & Diagonalise the BSE Hamiltonian and write the eigenvectors and\n      eigenvalues to file. \\\\\n187 & Output the BSE dielectric response function. \\\\\n190 & Write the atomic geometry to file for plotting with XCrySDen and\n V\\_Sim. \\\\\n195 & Calculation of X-ray density structure factors. \\\\\n196 & Calculation of magnetic structure factors. \\\\\n200 & Calculation of phonon dynamical matrices on a \$q\$-point set defined by\n {\\tt ngridq} using the supercell method. \\\\\n202 & Phonon dry run: just produce a set of empty DYN files. \\\\\n205 & Calculation of phonon dynamical matrices using density functional\n perturbation theory (DFPT). \\\\\n210 & Phonon density of states. \\\\\n220 & Phonon dispersion plot. \\\\\n230 & Phonon frequencies and eigenvectors for an arbitrary \$q\$-point. \\\\\n240 & Generate the \${\\bf q}\$-dependent phonon linewidths and electron-phonon\n coupling constants and write them to file. \\\\\n245 & Phonon linewidths plot. \\\\\n250 & Eliashberg function \$\\alpha^2F(\\omega)\$, electron-phonon coupling\n constant \$\\lambda\$, and the McMillan-Allen-Dynes critical temperature\n \$T_c\$. \\\\\n300 & Reduced density matrix functional theory (RDMFT) calculation. \\\\\n320 & Time-dependent density functional theory (TDDFT) calculation of the\n dielectric response function including microscopic contributions. \\\\\n330, 331 & TDDFT calculation of the spin-polarised response function for\n arbitrary \${\\bf q}\$-vectors. Task 331 writes the entire response function\n \$\\overleftrightarrow{\\chi}({\\bf G},{\\bf G}',q,\\omega)\$ to file. \\\\\n400 & Calculation of tensor moments and corresponding DFT+\$U\$ Hartree-Fock\n energy contributions. \\\\\n450 & Generates a laser pulse in the form of a time-dependent vector\n potential \${\\bf A}(t)\$ and writes it to AFIELDT.OUT. \\\\\n460 & Time evolution run using TDDFT under the influence of \${\\bf A}(t)\$. \\\\\n601 & Generate {\\tt seedname.win} file with the Wannier90 input\nparameters. \\\\\n602 & Generate and write all basic files required for the Wannier90. \\\\\n604 & Recompute {\\tt seedname.amn} file with \$A_{mn}\$ integrals required\nfor the Wannier90. \\\\\n605 & Generate {\\tt UNKp.s} files for plotting Wannier Functions in the\nWannier90.\n\\end{tabularx}\n"), ElkControlBlockInfo(:tau0atp, ElkFlagInfo[ElkFlagInfo{Float64}(:tau0atp, 25.0, "the step size to be used for atomic position optimisation")], "\nThe position of atom \$\\alpha\$ is updated on step \$m\$ of a geometry\noptimisation run using\n\$\$ {\\bf r}_{\\alpha}^{m+1}={\\bf r}_{\\alpha}^m+\\tau_{\\alpha}^m\n \\left({\\bf F}_{\\alpha}^m+{\\bf F}_{\\alpha}^{m-1}\\right), \$\$\nwhere \$\\tau_{\\alpha}\$ is set to {\\tt tau0atp} for \$m=0\$, and incremented by\nthe same amount if the atom is moving in the same direction between steps.\nIf the direction changes then \$\\tau_{\\alpha}\$ is reset to {\\tt tau0atp}.\n"), ElkControlBlockInfo(:tau0latv, ElkFlagInfo[ElkFlagInfo{Float64}(:tau0latv, 25.0, "the step size to be used for lattice vector optimisation")], "\nThis parameter is used for lattice vector optimisation in a procedure\nidentical to that for atomic position optimisation. See {\\tt tau0atp} and\n{\\tt latvopt}.\n"), ElkControlBlockInfo(:tauoep, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:tauoep, [10.0, 2.0, 15.0], "step length initial value and scaling factors for the OEP\n iterative solver")], "\nThe optimised effective potential is determined using an interative method.\n[Phys. Rev. Lett. 98, 196405 (2007)]. At the first iteration the step length\nis set to {\\tt tauoep(1)}. During subsequent iterations, the step length is\nscaled by {\\tt tauoep(2)} or {\\tt tauoep(3)}, when the residual is\nincreasing or decreasing, respectively. See also {\\tt maxitoep}.\n"), ElkControlBlockInfo(:taufsm, ElkFlagInfo[ElkFlagInfo{Float64}(:taufsm, 1.0, "the step size to be used when finding the effective magnetic\nfield in fixed spin moment calculations")], "\nAn effective magnetic field, \${\\bf B}_{\\rm FSM}\$, is required for fixing the\nspin moment to a given value, \${\\bf M}_{\\rm FSM}\$. This is found by adding a\nvector to the field which is proportional to the difference between the\nmoment calculated in the \$i\$th self-consistent loop and the required moment:\n\$\$ {\\bf B}_{\\rm FSM}^{i+1}={\\bf B}_{\\rm FSM}^i+\\lambda\\left({\\bf M}^i\n -{\\bf M}_{\\rm FSM}\\right), \$\$\nwhere \$\\lambda\$ is proportional to {\\tt taufsm}. See also {\\tt fsmtype},\n{\\tt momfix} and {\\tt spinpol}.\n"), ElkControlBlockInfo(:tempk, ElkFlagInfo[ElkFlagInfo{Float64}(:tempk, nothing, "temperature \$T\$ of the electronic system in kelvin")], "\nAssigning a value to this variable sets {\\tt stype} to 3 (Fermi-Dirac) and\nthe smearing width to \$k_{\\rm B}T\$.\n"), ElkControlBlockInfo(:tforce, ElkFlagInfo[ElkFlagInfo{Bool}(:tforce, false, "set to {.true.} if the force should be calculated at the\n end of the self-consistent cycle")], "\nThis variable is automatically set to {\\tt .true.} when performing geometry\noptimisation.\n"), ElkControlBlockInfo(:tmwrite, ElkFlagInfo[ElkFlagInfo{Bool}(:tmwrite, false, "set to {.true.} if the tensor moments and the\n corresponding decomposition of DFT+\$U\$ energy should be calculated\n at every loop of the self-consistent cycle")], "\nThis variable is useful to check the convergence of the tensor moments in\nDFT+\$U\$ caculations. Alternatively, with {\\tt task} equal to 400, one can\ncalculate the tensor moments and corresponding DFT+\$U\$ energy contributions\nfrom a given density matrix and set of Slater parameters at the end of the\nself-consistent cycle.\n"), ElkControlBlockInfo(:tefvit, ElkFlagInfo[ElkFlagInfo{Bool}(:tefvit, false, "set to {.true.} if the first-variational eigenvalue\n equation should be solved iteratively")], "\n"), ElkControlBlockInfo(:tefvr, ElkFlagInfo[ElkFlagInfo{Bool}(:tefvr, true, "set to {.true.} if a real symmetric eigenvalue solver\n should be used for crystals which have inversion symmetry")], "\nFor crystals with inversion symmetry, the first-variational Hamiltonian and\noverlap matrices can be made real by using appropriate transformations. In\nthis case, a real symmetric (instead of complex Hermitian) eigenvalue solver\ncan be used. This makes the calculation about three times faster.\n"), ElkControlBlockInfo(:tmomfix, ElkFlagInfo[ElkFlagInfo{Int64}(:ntmfix, 0, "number of tensor moments (TM) to be fixed"), ElkFlagInfo{Int64}(:is, nothing, "species number for entry \$i\$"), ElkFlagInfo{Int64}(:ia, nothing, "atom number"), ElkFlagInfo{Int64}(Symbol(""), nothing, "\$l\$ and \$n\$ indices of TM"), ElkFlagInfo{Int64}(Symbol(""), nothing, "indices for the 2-index or 3-index TM,\n respectively"), ElkFlagInfo{Int64}(Symbol(""), nothing, "indices for the 2-index or 3-index TM,\n respectively"), ElkFlagInfo{Complex{Float64}}(:z, nothing, "complex TM value"), ElkFlagInfo{Int64}(:p, nothing, "parity of spatial rotation"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:aspl, nothing, "Euler angles of spatial rotation"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:aspn, nothing, "Euler angles of spin rotation")], "\nThis block sets up the fixed tensor moment (FTM). There should be as many\nTM entries as {\\tt ntmfix}. See {\\it Phys. Rev. Lett.} {\\bf 103}, 107202\n(2009) for the tensor moment indexing convention. This is a highly\ncomplicated feature of the code, and should only be attempted with a full\nunderstanding of tensor moments.\n"), ElkControlBlockInfo(:tshift, ElkFlagInfo[ElkFlagInfo{Bool}(:tshift, true, "set to {.true.} if the crystal can be shifted so that the\n atom closest to the origin is exactly at the origin")], "\n"), ElkControlBlockInfo(:tstime, ElkFlagInfo[ElkFlagInfo{Float64}(:tstime, 10000.0, "total simulation time of time evolution run")], "\nSee also {\\tt dtimes}.\n"), ElkControlBlockInfo(:vhmat, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vhmat, [10.0, 0.0, 0.0], "matrix row 1"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vhmat, [0.0, 10.0, 0.0], "matrix row 2"), ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vhmat, [0.0, 0.0, 10.0], "matrix row 3")], "\nThis is the transformation matrix \$M\$ applied to every vector \$\\bf H\$ in the\nstructure factor output files {\\tt SFACRHO.OUT} and {\\tt SFACMAG.OUT}. It is\nstored in the usual row-column setting and applied directly as\n\${\\bf H}'=M{\\bf H}\$ to every vector but {\\em only} when writing the output\nfiles. See also {\\tt hmaxvr} and {\\tt reduceh}.\n"), ElkControlBlockInfo(:vhighq, ElkFlagInfo[ElkFlagInfo{Bool}(:vhighq, false, "{.true.} if a very high quality parameter set should be\n used")], "\nSetting this to {\\tt .true.} results in some default parameters being\nchanged to ensure excellent convergence in most situations. See also\n{\\tt highq}.\n"), ElkControlBlockInfo(:vklem, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vklem, [0.0, 0.0, 0.0], "the \$k\$-point in lattice coordinates at which to compute the\n effective mass tensors")], "\nSee {\\tt deltaem} and {\\tt ndspem}.\n"), ElkControlBlockInfo(:vkloff, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vkloff, [0.0, 0.0, 0.0], "the \$k\$-point offset vector in lattice coordinates")], "\nSee {\\tt ngridk}.\n"), ElkControlBlockInfo(:vqlss, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Float64,1,3}}(:vqlss, [0.0, 0.0, 0.0], "the \${\\bf q}\$-vector of the spin-spiral state in lattice\n coordinates")], "\nSpin-spirals arise from spinor states assumed to be of the form\n\$\$ \\Psi^{\\bf q}_{\\bf k}({\\bf r})=\n \\left( \\begin{array}{c}\n U^{{\\bf q}\\uparrow}_{\\bf k}({\\bf r})e^{i({\\bf k+q/2})\\cdot{\\bf r}} \\\\\n U^{{\\bf q}\\downarrow}_{\\bf k}({\\bf r})e^{i({\\bf k-q/2})\\cdot{\\bf r}} \\\\\n \\end{array} \\right). \$\$\nThese are determined using a second-variational approach, and give rise to a\nmagnetisation density of the form\n\$\$ {\\bf m}^{\\bf q}({\\bf r})=(m_x({\\bf r})\\cos({\\bf q \\cdot r}),\n m_y({\\bf r})\\sin({\\bf q \\cdot r}),m_z({\\bf r})), \$\$\nwhere \$m_x\$, \$m_y\$ and \$m_z\$ are lattice periodic. See also {\\tt spinsprl}.\n"), ElkControlBlockInfo(:wannier, ElkFlagInfo[ElkFlagInfo{Symbol}(:wann_seedname, nothing, "{seedname} of all files produced by interface"), ElkFlagInfo{SArray{Tuple{3},Int64,1,3}}(:wann_ngridk, [1, 1, 1], "k-point grid used for Wannierization"), ElkFlagInfo{Int64}(:wann_nwf, nothing, "number of Wannier functions"), ElkFlagInfo{UnitRange{Int64}}(:wann_bands, nothing, "range of bands to be used for Wannierization."), ElkFlagInfo{Array{String,1}}(:wann_projections, nothing, "list of projections")], "\nDefines necessary parameters for Wannierization. Note that the range of\nbands should be a comma separated list of indices (e.g. 1-4 or 1,2,4\nor 1-3,5-6). List of projections should be defined in one line or many\nstrings (up to 256 lines): the format is the same as the Wannier90 requires\n(for the details please look into Wannier90 user guide).\n\\medskip\n\\textit{Tips:} band indexes can be identified using {\\tt EIGVAL.OUT} file.\n"), ElkControlBlockInfo(:wannierExtra, ElkFlagInfo[ElkFlagInfo{String}(:wann_input, "-\n}", "arbitrary input for the Wannier90 {seedname.win}")], "\nDefines additional information for Wannierization. Information under this\nblock will be fully passed to the {\\tt seedname.win} file.\n"), ElkControlBlockInfo(:wmaxgw, ElkFlagInfo[ElkFlagInfo{Float64}(:wmaxgw, nothing, "maximum Matsubara frequency for \$GW\$ calculations")], "\nThis defines the cut-off of the Matsubara frequencies on the imaginary\naxis for calculating the \$GW\$ self-energy and solving the Dyson equation.\nIf this number is negative then the cut-off is taken to be\n\$|{\\tt wmaxgw}|\\times\\Delta\\epsilon\$, where \$\\Delta\\epsilon\$ is the\ndifference between the largest and smallest Kohn-Sham valence eigenvalues.\n"), ElkControlBlockInfo(:wplot, ElkFlagInfo[ElkFlagInfo{Int64}(:nwplot, 500, "number of frequency/energy points in the DOS or optics plot"), ElkFlagInfo{Int64}(:ngrkf, 100, "fine \$k\$-point grid size used for integrating functions in the\n Brillouin zone"), ElkFlagInfo{Int64}(:nswplot, 1, "level of smoothing applied to DOS/optics output"), ElkFlagInfo{SArray{Tuple{2},Float64,1,2}}(:wplot, nothing, "frequency/energy window for the DOS or optics plot")], "\nDOS and optics plots require integrals of the kind\n\$\$ g(\\omega_i)=\\frac{\\Omega}{(2\\pi)^3}\\int_{\\rm BZ} f({\\bf k})\n \\delta(\\omega_i-e({\\bf k}))d{\\bf k}. \$\$\nThese are calculated by first interpolating the functions \$e({\\bf k})\$ and\n\$f({\\bf k})\$ with the trilinear method on a much finer mesh whose size is\ndetermined by {\\tt ngrkf}. Then the \$\\omega\$-dependent histogram of the\nintegrand is accumulated over the fine mesh. If the output function is noisy\nthen either {\\tt ngrkf} should be increased or {\\tt nwplot} decreased.\nAlternatively, the output function can be artificially smoothed up to a\nlevel given by {\\tt nswplot}. This is the number of successive 3-point\naverages to be applied to the function \$g\$.\n"), ElkControlBlockInfo(:wsfac, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{2},Float64,1,2}}(:wsfac, nothing, "energy window to be used when calculating density or magnetic\n structure factors")], "\nOnly those states with eigenvalues within this window will contribute to the\ndensity or magnetisation. See also {\\tt hmaxvr} and {\\tt vhmat}.\n"), ElkControlBlockInfo(:xctype, ElkFlagInfo[ElkFlagInfo{SArray{Tuple{3},Int64,1,3}}(:xctype, [3, 0, 0], "integers defining the type of exchange-correlation functional\n to be used")], "\nNormally only the first value is used to define the functional type. The\nother value may be used for external libraries. Currently implemented are:\n\\vskip 6pt\n\\begin{tabularx}{\\textwidth}[h]{lX}\n\$-n\$ & Exact-exchange optimised effective potential (EXX-OEP) method with\n correlation energy and potential given by functional number \$n\$ \\\\\n1 & No exchange-correlation funtional (\$E_{\\rm xc}\\equiv 0\$) \\\\\n2 & LDA, Perdew-Zunger/Ceperley-Alder, {\\it Phys. Rev. B} {\\bf 23}, 5048\n (1981) \\\\\n3 & LSDA, Perdew-Wang/Ceperley-Alder, {\\it Phys. Rev. B} {\\bf 45}, 13244\n (1992) \\\\\n4 & LDA, X-alpha approximation, J. C. Slater, {\\it Phys. Rev.} {\\bf 81}, 385\n (1951) \\\\\n5 & LSDA, von Barth-Hedin, {\\it J. Phys. C} {\\bf 5}, 1629 (1972) \\\\\n20 & GGA, Perdew-Burke-Ernzerhof, {\\it Phys. Rev. Lett.} {\\bf 77}, 3865\n (1996) \\\\\n21 & GGA, Revised PBE, Zhang-Yang, {\\it Phys. Rev. Lett.} {\\bf 80}, 890\n (1998) \\\\\n22 & GGA, PBEsol, Phys. Rev. Lett. 100, 136406 (2008) \\\\\n26 & GGA, Wu-Cohen exchange (WC06) with PBE correlation, {\\it Phys. Rev. B}\n {\\bf 73}, 235116 (2006) \\\\\n30 & GGA, Armiento-Mattsson (AM05) spin-unpolarised functional,\n {\\it Phys. Rev. B} {\\bf 72}, 085108 (2005) \\\\\n100 & Libxc functionals; the second and third values of {\\tt xctype} define\n the exchange and correlation functionals in the Libxc library,\n respectively \\\\\n\\end{tabularx}\n\\section{Contributing to Elk}\nPlease bear in mind when writing code for the Elk project that it should be\nan exercise in physics and not software engineering. All code should\ntherefore be kept as simple and concise as possible, and above all it should\nbe easy for anyone to locate and follow the Fortran representation of the\noriginal mathematics. We would also appreciate the following conventions\nbeing adhered to:\n\\begin{itemize}\n\\item Strict Fortran 2003 should be used. Features which are marked as\n obsolescent in Fortran 2003 should be avoided. These include assigned\n format specifiers, labeled do-loops, computed goto statements and statement\n functions.\n\\item Modules should be used in place of common blocks for declaring\n global variables. Use the existing modules to declare new global variables.\n\\item Any code should be written in lower-case free form style, starting\n from column one. Try and keep the length of each line to fewer than 80\n characters using the \\& character for line continuation.\n\\item Every function or subroutine, no matter how small, should be in its\n own file named {\\tt routine.f90}, where {\\tt routine} is the function or\n subroutine name. It is recommended that the routines are named so as to\n make their purpose apparent from the name alone.\n\\item Use of {\\tt implicit none} is mandatory. Remember also to define the\n {\\tt intent} of any passed arguments.\n\\item Local allocatable arrays must be deallocated on exit of the routine to\n prevent memory leakage. Use of automatic arrays should be limited to arrays\n of small size.\n\\item Every function or subroutine must be documented with the Protex source\n code documentation system. This should include a short \\LaTeX\\ description\n of the algorithms and methods involved. Equations which need to be\n referenced should be labeled with {\\tt routine\\_1}, {\\tt routine\\_2}, etc.\n The authorship of each new piece of code or modification should be\n indicated in the {\\tt REVISION HISTORY} part of the header. See the Protex\n documentation for details.\n\\item Ensure as much as possible that a routine will terminate the program\n when given improper input instead of continuing with erroneous results.\n Specifically, functions should have a well-defined domain for which they\n return accurate results. Input outside that domain should result in an\n error message and termination.\n\\item Report errors prior to termination with a short description, for\n example:\n \\begin{verbatim}\n  write(*,*)\n  write(*,'(\"Error(readinput): natoms <= 0 : \",I8)') natoms(is)\n  write(*,'(\" for species \",I4)') is\n  write(*,*)\n  stop\n \\end{verbatim}\n\\item Wherever possible, real numbers outputted as ASCII data should be\n formatted with the {\\tt G18.10} specifier.\n\\item Avoid redundant or repeated code: check to see if the routine you need\n already exists, before writing a new one.\n\\item All reading in of ASCII data should be done in the subroutine\n {\\tt readinput}. For binary data, separate routines for reading and writing\n should be used (for example {\\tt writestate} and {\\tt readstate}).\n\\item Input filenames should be in lowercase and have the extension\n {\\tt .in} . All output filenames should be in uppercase with the extension\n {\\tt .OUT} .\n\\item All internal units should be atomic. Input and output units should be\n atomic by default and clearly stated otherwise. Rydbergs should not be used\n under any circumstances.\n\\end{itemize}\n\\subsection{Licensing}\n Routines which constitute the main part of the code are released under the\n GNU General Public License (GPL). Library routines are released under the\n less restrictive GNU Lesser General Public License (LGPL). Both licenses\n are contained in the file {\\tt COPYING}. Any contribution to the code must\n be licensed at the authors' discretion under either the GPL or LGPL.\n Author(s) of the code retain the copyrights. Copyright and (L)GPL\n information must be included at the beginning of every file, and no code\n will be accepted without this.\n")]


export TinyHanabiEnv

const TINY_HANABI_REWARD_TABLE = begin
    t = Array{Int,4}(undef, 3, 3, 2, 2)
    t[:, :, 1, 1] = [
        10 0 0
        4 8 4
        10 0 0
    ]
    t[:, :, 1, 2] = [
        0 0 10
        4 8 4
        0 0 10
    ]
    t[:, :, 2, 1] = [
        0 0 10
        4 8 4
        0 0 0
    ]
    t[:, :, 2, 2] = [
        10 0 0
        4 8 4
        10 0 0
    ]
    t
end

struct TinyHanabiEnv <: AbstractEnv
    reward_table::Array{Int,4}
    cards::Vector{Int}
    actions::Vector{Int}
end

"""
    TinyHanabiEnv()

See https://arxiv.org/abs/1902.00506.
"""
TinyHanabiEnv() = TinyHanabiEnv(TINY_HANABI_REWARD_TABLE, Int[], Int[])

function RLBase.reset!(env::TinyHanabiEnv)
    empty!(env.cards)
    empty!(env.actions)
end

RLBase.players(env::TinyHanabiEnv) = 1:2

RLBase.current_player(env::TinyHanabiEnv) =
    if length(env.cards) < 2
        CHANCE_PLAYER
    elseif length(env.actions) == 0
        1
    else
        2
    end

(env::TinyHanabiEnv)(action, ::ChancePlayer) = push!(env.cards, action)
(env::TinyHanabiEnv)(action, ::Int) = push!(env.actions, action)

RLBase.action_space(env::TinyHanabiEnv, ::Int) = Base.OneTo(3)
RLBase.action_space(env::TinyHanabiEnv, ::ChancePlayer) = Base.OneTo(2)

RLBase.legal_action_space(env::TinyHanabiEnv, ::ChancePlayer) = findall(!in(env.cards), 1:2)
RLBase.legal_action_space_mask(env::TinyHanabiEnv, ::ChancePlayer) =
    [x ∉ env.cards for x in 1:2]

function RLBase.prob(env::TinyHanabiEnv, ::ChancePlayer)
    if isempty(env.cards)
        [0.5, 0.5]
    elseif length(env.cards) == 1
        p = ones(2)
        p[env.cards[]] = 0.0
        p
    else
        @error "shouldn't reach here."
    end
end

RLBase.state_space(env::TinyHanabiEnv, ::InformationSet, ::ChancePlayer) =
    ((0,), (0, 1), (0, 2), (0, 1, 2), (0, 2, 1)) # (chance_player_id(0), chance_player's actions...)
RLBase.state(env::TinyHanabiEnv, ::InformationSet, ::ChancePlayer) = (0, env.cards...)

function RLBase.state_space(env::TinyHanabiEnv, ::InformationSet, p::Int)
    Tuple(
        (p, c..., a...) for p in 1:2 for c in ((), 1, 2) for
        a in ((), 1:3..., ((i, j) for i in 1:3 for j in 1:3)...)
    )
end

function RLBase.state(env::TinyHanabiEnv, ::InformationSet, p::Int)
    card = length(env.cards) >= p ? env.cards[p] : ()
    (p, card..., env.actions...)
end

RLBase.is_terminated(env::TinyHanabiEnv) = length(env.actions) == 2
RLBase.reward(env::TinyHanabiEnv, player) =
    is_terminated(env) ? env.reward_table[env.actions..., env.cards...] : 0

(env::TinyHanabiEnv)(action::Int, ::ChancePlayer) = push!(env.cards, action)
(env::TinyHanabiEnv)(action::Int, ::Int) = push!(env.actions, action)

RLBase.NumAgentStyle(::TinyHanabiEnv) = MultiAgent(2)
RLBase.DynamicStyle(::TinyHanabiEnv) = SEQUENTIAL
RLBase.ActionStyle(::TinyHanabiEnv) = MINIMAL_ACTION_SET
RLBase.InformationStyle(::TinyHanabiEnv) = IMPERFECT_INFORMATION
RLBase.StateStyle(::TinyHanabiEnv) = InformationSet{Tuple{Vararg{Int}}}()
RLBase.RewardStyle(::TinyHanabiEnv) = TERMINAL_REWARD
RLBase.UtilityStyle(::TinyHanabiEnv) = IDENTICAL_UTILITY
RLBase.ChanceStyle(::TinyHanabiEnv) = EXPLICIT_STOCHASTIC