From aea0e548410641aba16eaebe3c7fd15f09a726c1 Mon Sep 17 00:00:00 2001 From: Daniel Vedder <daniel.vedder@idiv.de> Date: Tue, 10 Sep 2024 10:02:20 +0200 Subject: [PATCH] Allow setting any parameters by arguments to simulate()/initialise() --- src/core/input.jl | 18 ++++++++++-------- src/core/simulation.jl | 19 +++++++++---------- test/simulation_tests.jl | 3 ++- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/core/input.jl b/src/core/input.jl index 803c9ab..e479b84 100644 --- a/src/core/input.jl +++ b/src/core/input.jl @@ -41,12 +41,12 @@ macro param(domainparam) end """ - getsettings(configfile, seed=nothing) + getsettings(configfile, userparams=Dict()) Combines all configuration options to produce a single settings dict. -Precedence: commandline parameters - user config file - default values +Precedence: function arguments - commandline parameters - user config file - default values """ -function getsettings(configfile::String, seed::Union{Int64,Nothing}=nothing) +function getsettings(configfile::String, userparams::Dict{String,Any}=Dict()) # read in and merge configurations from the commandline, the default config file # and a config file supplied by the user (via the --configfile option) defaults::Dict{String, Any} = TOML.parsefile(PARAMFILE) |> flattenTOML @@ -61,7 +61,9 @@ function getsettings(configfile::String, seed::Union{Int64,Nothing}=nothing) end settings = deepcopy(defaults) for param in keys(defaults) - if split(param, ".")[2] in keys(commandline) + if param in keys(userparams) + settings[param] = userparams[param] + elseif split(param, ".")[2] in keys(commandline) settings[param] = commandline[split(param, ".")[2]] elseif !isnothing(configs) && param in keys(configs) settings[param] = configs[param] @@ -73,7 +75,6 @@ function getsettings(configfile::String, seed::Union{Int64,Nothing}=nothing) end end end - !isnothing(seed) && (settings["core.seed"] = seed) settings["internal.scanparams"] = scanparams preprocessparameters(settings, defaults["core.outdir"]) end @@ -85,6 +86,7 @@ Take the raw input parameters and process them where necessary (e.g. convert typ perform checks). This is a helper function for [`getsettings`](@ref). """ function preprocessparameters(settings::Dict{String,Any}, defaultoutdir::String) + #TODO replace errors with exceptions # miscellaneous processing (settings["core.seed"] == 0) && (settings["core.seed"] = abs(rand(RandomDevice(), Int32))) settings["world.mapresolution"] = settings["world.mapresolution"] * 1m @@ -107,15 +109,15 @@ function preprocessparameters(settings::Dict{String,Any}, defaultoutdir::String) settings["crop.cropdirectory"] = abspath(pkgdir(@__MODULE__), settings["crop.cropdirectory"]) @debug "Using package directory to load crop data: $(settings["crop.cropdirectory"])". else - Base.error("Couldn't find map directory $(settings["crop.cropdirectory"]).") + Base.error("Couldn't find crop directory $(settings["crop.cropdirectory"]).") end end # sanity checks if settings["core.startdate"] > settings["core.enddate"] - Base.error("Enddate is earlier than startdate.") #TODO replace with exception + Base.error("Enddate is earlier than startdate.") end if !(settings["crop.cropmodel"] in AVAILABLE_CROPMODELS) - error("crop.cropmodel = \"$(settings["crop.cropmodel"])\", but has to be one of: $AVAILABLE_CROPMODELS") + Base.error("crop.cropmodel = \"$(settings["crop.cropmodel"])\", but has to be one of: $AVAILABLE_CROPMODELS") end #FIXME enable parallelisation # if !isempty(settings["internal.scanparams"]) && (nprocs() < 2) diff --git a/src/core/simulation.jl b/src/core/simulation.jl index 4d227a0..b19f402 100644 --- a/src/core/simulation.jl +++ b/src/core/simulation.jl @@ -47,15 +47,15 @@ function stepagent!(agent::ModelAgent, model::SimulationModel) end """ - simulate(config=PARAMFILE, seed=nothing) + simulate(configfile=PARAMFILE, params=Dict()) Initialise one or more model objects and carry out a full simulation experiment, -optionally specifying a configuration file and a seed for the RNG. +optionally specifying a configuration file and/or specific parameters. This is the default way to run a Persefone simulation. """ -function simulate(config::String=PARAMFILE, seed::Union{Int64,Nothing}=nothing) - models = initialise(config, seed) +function simulate(;configfile::String=PARAMFILE, params::Dict{String,Any}=Dict{String,Any}()) + models = initialise(configfile=configfile, params=params) isa(models, Vector) ? map(simulate!, models) : #TODO parallelise simulate!(models) @@ -68,25 +68,24 @@ Carry out a complete simulation run using a pre-initialised model object. """ function simulate!(model::SimulationModel) @info "Simulation run started at $(Dates.now())." - runtime = Dates.value(@param(core.enddate)-@param(core.startdate))+1 - for d in 1:runtime + while model.date <= @param(core.enddate) stepsimulation!(model) end finalise!(model) end """ - initialise(config=PARAMFILE, seed=nothing) + initialise(configfile=PARAMFILE, params=Dict()) Initialise the model: read in parameters, create the output data directory, and instantiate the SimulationModel object(s). Optionally allows specifying the -configuration file and overriding the `seed` parameter. This returns a single +configuration file and overriding specific parameters. This returns a single model object, unless the config file contains multiple values for one or more parameters, in which case it creates a full-factorial simulation experiment and returns a vector of model objects. """ -function initialise(config::String=PARAMFILE, seed::Union{Int64,Nothing}=nothing) - settings = getsettings(config, seed) +function initialise(;configfile::String=PARAMFILE, params::Dict{String,Any}=Dict{String,Any}()) + settings = getsettings(configfile, params) scanparams = settings["internal.scanparams"] delete!(settings, "internal.scanparams") isempty(scanparams) ? diff --git a/test/simulation_tests.jl b/test/simulation_tests.jl index fce2f2b..b8bb76e 100644 --- a/test/simulation_tests.jl +++ b/test/simulation_tests.jl @@ -4,8 +4,9 @@ ### @testset "Model initialisation" begin - model = initialise(TESTPARAMETERS) + model = initialise(TESTPARAMETERS, params=Dict("core.visualise"=>false)) @test typeof(model.settings) == Dict{String, Any} + @test @param(core.visualise) == false @test model.date == Date(2022,2,1) @test typeof(model.landscape) == Matrix{Pixel} @test typeof(model.dataoutputs) == Dict{String,DataOutput} -- GitLab