From 11803d92b52a0f869ac1d2e6cde2164bc405f506 Mon Sep 17 00:00:00 2001 From: Daniel Vedder <daniel.vedder@idiv.de> Date: Fri, 6 Jan 2023 11:58:38 +0100 Subject: [PATCH] Wrote the `initpopulation()` function Still needs to be tested. --- src/core/output.jl | 2 +- src/core/simulation.jl | 10 +++--- src/crop/crops.jl | 2 +- src/nature/nature.jl | 7 ++-- src/nature/populations.jl | 63 ++++++++++++++++++++++++++++++++--- src/nature/species/skylark.jl | 2 +- 6 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/core/output.jl b/src/core/output.jl index 830303f..db3ce30 100644 --- a/src/core/output.jl +++ b/src/core/output.jl @@ -42,7 +42,7 @@ function setupdatadir() simulationlogger = TeeLogger(ConsoleLogger(logfile, loglevel), ConsoleLogger(stdout, loglevel)) global_logger(simulationlogger) - @info "Setting up output directory $(param("core.outdir"))" + @debug "Setting up output directory $(param("core.outdir"))" # Export a copy of the current parameter settings to the output folder. # This can be used to replicate this exact run in future, and also # records the current time and git commit. diff --git a/src/core/simulation.jl b/src/core/simulation.jl index b94b8d6..f8c94f9 100644 --- a/src/core/simulation.jl +++ b/src/core/simulation.jl @@ -10,7 +10,8 @@ Initialise the model: read in parameters, create the output data directory, and instantiate the AgentBasedModel object. """ function initialise(config::String=PARAMFILE) - #XXX add a seed parameter? + @info "Simulation run started at $(Dates.now())." + #TODO add a seed parameter - requires mutable parameters # do some housekeeping initsettings(config) Random.seed!(param("core.seed")) @@ -29,7 +30,6 @@ function initialise(config::String=PARAMFILE) initfarms!(model) initfields!(model) initnature!(model) - @info "Simulation initialised at $(Dates.now())." model end @@ -52,11 +52,11 @@ end """ finalise(model) -Wrap up the simulation. Output all remaining data and exit. +Wrap up the simulation. Currently doesn't do anything except print some information. """ function finalise(model::AgentBasedModel) - @info "Simulated $(model.date-param("core.startdate"))." - @info "Simulation completed at $(Dates.now()),\nwrote output to $(param("core.outdir"))." + @info "Simulated $(model.date-param("core.startdate")) days." + @info "Simulation run completed at $(Dates.now()),\nwrote output to $(param("core.outdir"))." #XXX is there anything to do here? #genocide!(model) end diff --git a/src/crop/crops.jl b/src/crop/crops.jl index c96c7c0..c84c75d 100644 --- a/src/crop/crops.jl +++ b/src/crop/crops.jl @@ -59,7 +59,7 @@ function initfields!(model::AgentBasedModel) end end end - @debug "Initialised $n farm plots." + @info "Initialised $n farm plots." end #XXX only needed during development, can be deleted again? diff --git a/src/nature/nature.jl b/src/nature/nature.jl index bae1428..186c476 100644 --- a/src/nature/nature.jl +++ b/src/nature/nature.jl @@ -304,12 +304,11 @@ macro distancetoedge() end """ - @countanimals(speciesname) + @countanimals(speciesname, radius=0) Count the number of animals of the given species in this location. This is a utility wrapper that can only be used nested within `@phase` or `@habitat`. """ -macro countanimals(speciesname) - #XXX this also counts the enquiring agent - :(countanimals($(esc(:pos)), $(esc(:model)), $speciesname)) +macro countanimals(speciesname, radius=0) + :(countanimals($(esc(:pos)), $(esc(:model)), $speciesname, $radius)) end diff --git a/src/nature/populations.jl b/src/nature/populations.jl index 28b9528..bd6577f 100644 --- a/src/nature/populations.jl +++ b/src/nature/populations.jl @@ -4,6 +4,63 @@ ### reproduction, and mortality. ### +""" + initpopulation(habitatdescriptor; popsize=-1, pairs=false, asexual=false) + +Creates a function that initialises individuals at random locations across the landscape. +This can be used to create the `initialise!` variable in a species definition block. + +- `habitatdescriptor` is a function that determines whether a given location is suitable + or not (create this using `@habitat`). + +- `popsize` determines the number of individuals that will be created. If this is zero or + negative, one individual will be created in every suitable location in the landscape. + If `popsize` is greater than the number of suitable locations, multiple individuals + will be created in one place. (Maximum population density can be set in the habitat + descriptor using the `@countanimals` macro.) + +- If `pairs` is true, a male and a female individual will be created in each selected + location, otherwise, only one individual will be created at a time. + +- If `asexual` is true, all created individuals are assigned the sex `hermaphrodite`, + otherwise, they are randomly assigned male of female. (If `pairs` is true, `asexual` + is ignored.) +""" +function initpopulation(habitatdescriptor::Function; + popsize::Int64=-1, pairs::Bool=false, asexual=false) + function(species::Dict{String,Any}, model::AgentBasedModel) + n = 0 + lastn = 0 + specname = species["name"] + width, height = size(model.landscape) + while n == 0 || n < popsize + for x in shuffle!(Vector(1:width)) + for y in shuffle!(Vector(1:height)) + if habitatdescriptor((x,y), model) + if pairs + add_agent!(Animal, (x,y), model, species, female, 0) + add_agent!(Animal, (x,y), model, species, male, 0) + n += 2 + else + sex = asexual ? hermaphrodite : rand([male, female]) + add_agent!(Animal, (x,y), model, species, sex, 0) + n += 1 + end + end + (n >= popsize) && break + end + (n >= popsize) && break + end + if lastn == n # prevent an infinite loop - we don't have a Cray... + @warn "There are not enough suitable locations for $(specname) in the landscape." + break + end + lastn = n + end + @info "Initialised $(n) $(specname)s." + end +end + """ initrandompopulation(popsize, asexual=true) @@ -12,7 +69,7 @@ A simplified version of `initpopulation()`. Creates a function that initialises than 1, it is interpreted as a population density (i.e. 1 animal per `popsize` pixels). """ function initrandompopulation(popsize::Union{Int64,Float64}, asexual::Bool=true) - initfunc = function(species::Dict{String,Any}, model::AgentBasedModel) + function(species::Dict{String,Any}, model::AgentBasedModel) if popsize < 1.0 x, y = size(model.landscape) popsize = Int(round(x*y*popsize)) @@ -21,12 +78,10 @@ function initrandompopulation(popsize::Union{Int64,Float64}, asexual::Bool=true) sex = asexual ? hermaphrodite : rand([male, female]) add_agent!(Animal, model, species, sex, 0) end - @debug "Initialised $(popsize) $(species["name"])s." + @info "Initialised $(popsize) $(species["name"])s." end - return initfunc end -#TODO initpopulation with habitat descriptor #XXX initpopulation with dispersal from an original source? #XXX initpopulation based on known occurences in real-life? diff --git a/src/nature/species/skylark.jl b/src/nature/species/skylark.jl index d29ab65..0f877bd 100644 --- a/src/nature/species/skylark.jl +++ b/src/nature/species/skylark.jl @@ -35,7 +35,7 @@ At the moment, this implementation is still in development. @respond harvest @kill(@trait(eggharvestmortality), "harvest") - if animal.age == 14 + if @trait(age) == 14 @trait(phase) = "nestling" end end -- GitLab