Skip to content
Snippets Groups Projects
Select Git revision
  • 94fd287633de7b587808f3ea693e47e963c6ad19
  • main default protected
2 results

remGOF.Rd

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    simulation.jl 6.15 KiB
    ### Persephone - a socio-economic-ecological model of European agricultural landscapes.
    ###
    ### This file includes the core functions for initialising and running simulations.
    ###
    
    #XXX With the parameter scanning, code execution has become rather difficult to follow.
    # Can I refactor this into two clear, separate paths - one for the normal case (single
    # run) and one for parameter scanning?
    
    """
        initmodel(settings)
    
    Initialise a model object using a ready-made settings dict. This is
    a helper function for `initialise()` and `initmodelsparallel()`.
    """
    function initmodel(settings::Dict{String, Any})
        @debug "Initialising model object."
        createdatadir(settings["core.outdir"], settings["core.overwrite"])
        logger = modellogger(settings["core.loglevel"], settings["core.outdir"])
        with_logger(logger) do
            events = Vector{FarmEvent}()
            dataoutputs = Vector{DataOutput}()
            landscape = initlandscape(settings["core.landcovermap"], settings["core.farmfieldsmap"])
            space = GridSpace(size(landscape), periodic=false)
            properties = Dict{Symbol,Any}(:settings=>settings,
                                          :logger=>logger,
                                          :date=>settings["core.startdate"],
                                          :landscape=>landscape,
                                          :dataoutputs=>dataoutputs,
                                          :events=>events)
            model = AgentBasedModel(Union{Farmer,Animal,FarmPlot}, space, properties=properties,
                                    rng=StableRNG(settings["core.seed"]), warn=false)
            saveinputfiles(model)
            initfarms!(model)
            initfields!(model)
            initnature!(model)
            model
        end
    end
    
    """
        initmodelsparallel(settings)
    
    Initialise multiple model objects using ready-made settings dicts. This is
    a helper function for `initialise()`.
    """
    function initmodelsparallel(settingsdicts::Vector{Dict{String, Any}})
        #TODO parallelise model initialisation
        @debug "Beginning to initialise model objects."
        models = Vector{AgentBasedModel}()
        for settings in settingsdicts
            push!(models, initmodel(settings))
        end
        models
    end
    
    """
        paramscan(settings)
    
    Initialise a list of model objects, covering all possible parameter combinations
    given by the settings (i.e. a full-factorial experiment). This is a helper function
    for `initialise()`.
    """
    function paramscan(settings::Dict{String,Any}, scanparams::Vector{String})
        # recursively generate a set of settings dicts covering all combinations
        function generatecombinations(params::Vector{String})
            (length(params) == 0) && return [settings]
            param = pop!(params)
            combinations = Vector{Dict{String,Any}}()
            for comb in generatecombinations(params)
                for value in settings[param]
                    newcombination = deepcopy(comb)
                    newcombination[param] = value
                    if comb["core.outdir"] == settings["core.outdir"]
                        outdir = joinpath(comb["core.outdir"], "$(split(param, ".")[2])_$(value)")
                    else
                        outdir = "$(comb["core.outdir"])_$(split(param, ".")[2])_$(value)"
                    end
                    newcombination["core.outdir"] = outdir
                    push!(combinations, newcombination)
                end
            end
            combinations
        end
        generatecombinations(scanparams)
    end
    
    """
        initialise(config=PARAMFILE, seed=nothing)
    
    Initialise the model: read in parameters, create the output data directory,
    and instantiate the AgentBasedModel object(s). Optionally allows specifying the
    configuration file and overriding the `seed` parameter. 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)
        @info "Simulation run started at $(Dates.now())."
        settings = getsettings(config, seed)
        scanparams = settings["internal.scanparams"]
        delete!(settings, "internal.scanparams")
        isempty(scanparams) ? initmodel(settings) : initmodelsparallel(settings, scanparameters)
    end
    
    """
        stepsimulation!(model)
    
    Execute one update of the model.
    """
    function stepsimulation!(model::AgentBasedModel)
        with_logger(model.logger) do
            @info "Simulating day $(model.date)."
            for a in Schedulers.ByType((Farmer,FarmPlot,Animal), true)(model)
                try #The animal may have been killed
                    stepagent!(model[a], model)
                catch exc
                    # check if the KeyError comes from the `model[a]` or the function call
                    isa(exc, KeyError) && isa(exc.key, Int) ? continue : throw(exc)
                end
            end
            updateevents!(model)
            outputdata(model)
            model.date += Day(1)
            model
        end
    end
    
    """
        finalise!(model)
    
    Wrap up the simulation. Currently doesn't do anything except print some information.
    """
    function finalise!(model::AgentBasedModel)
        with_logger(model.logger) do
            @info "Simulated $(model.date-@param(core.startdate))."
            @info "Simulation run completed at $(Dates.now()),\nwrote output to $(@param(core.outdir))."
            #XXX is there anything to do here?
            model
        end
    end
    
    """
        simulate!(model)
    
    Carry out a complete simulation run using a pre-initialised model object.
    """
    function simulate!(model::AgentBasedModel)
        runtime = Dates.value(@param(core.enddate)-@param(core.startdate))+1
        step!(model, dummystep, stepsimulation!, runtime)
        finalise!(model)
    end
    
    """
        simulate(config=PARAMFILE, seed=nothing)
    
    Initialise one or more model objects and carry out a full simulation experiment,
    optionally specifying a configuration file and a seed for the RNG.
    
    This is the default way to run a Persephone simulation.
    """
    function simulate(config::String=PARAMFILE, seed::Union{Int64,Nothing}=nothing)
        models = initialise(config, seed)
        if isa(models, Vector)
            for m in models
                @info "Executing run $(m.settings["core.outdir"])"
                simulate!(m) #TODO parallelise
            end
        else
            simulate!(models)
        end
    end