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