From 4ad42b9213658ecf30bc8a3e63a8f1ca2659b23a Mon Sep 17 00:00:00 2001 From: Daniel Vedder <daniel.vedder@idiv.de> Date: Thu, 15 Dec 2022 22:46:57 +0100 Subject: [PATCH] Big rearrange in the nature model to accommodate the @species syntax Not yet functional! --- src/Persephone.jl | 9 ++- src/core/landscape.jl | 1 + src/nature/ecologicaldata.jl | 12 +++ src/nature/lifehistory.jl | 12 +++ src/nature/nature.jl | 93 +++++++----------------- src/nature/species.jl | 35 --------- src/nature/{ => species}/wolpertinger.jl | 2 + src/nature/{ => species}/wyvern.jl | 2 + 8 files changed, 63 insertions(+), 103 deletions(-) create mode 100644 src/nature/lifehistory.jl delete mode 100644 src/nature/species.jl rename src/nature/{ => species}/wolpertinger.jl (98%) rename src/nature/{ => species}/wyvern.jl (98%) diff --git a/src/Persephone.jl b/src/Persephone.jl index a8becf9..96bfece 100644 --- a/src/Persephone.jl +++ b/src/Persephone.jl @@ -16,7 +16,7 @@ using Agents, ArgParse, Dates, - GeoArrays, + GeoArrays, #XXX this is a big dependency - can we get rid of it? Logging, LoggingExtras, Random, @@ -24,6 +24,7 @@ using ## define exported functions and variables export + param, simulate, initialise, stepsimulation!, @@ -42,9 +43,11 @@ include("core/landscape.jl") include("farm/farm.jl") include("crop/crops.jl") include("nature/nature.jl") +include("nature/lifehistory.jl") include("nature/ecologicaldata.jl") -include("nature/wolpertinger.jl") -include("nature/wyvern.jl") +#include("nature/species/skylark.jl") +#include("nature/species/wolpertinger.jl") +#include("nature/species/wyvern.jl") include("core/simulation.jl") end diff --git a/src/core/landscape.jl b/src/core/landscape.jl index f8f8fd8..573c951 100644 --- a/src/core/landscape.jl +++ b/src/core/landscape.jl @@ -17,6 +17,7 @@ in a single object. The model landscape consists of a matrix of pixels. struct Pixel landcover::LandCover fieldid::Union{Missing, UInt32} + events::Vector{Symbol} #TODO implement the rest of the events system #FIXME actually this is stupid - I don't want a field ID, I want a field object end diff --git a/src/nature/ecologicaldata.jl b/src/nature/ecologicaldata.jl index f01ead1..c387eb2 100644 --- a/src/nature/ecologicaldata.jl +++ b/src/nature/ecologicaldata.jl @@ -6,6 +6,18 @@ const POPFILE = "populations.csv" const INDFILE = "individuals.csv" +""" + initecologicaldata() + +Create output files for each data group collected by the nature model. +""" +function initecologicaldata() + newdataoutput(POPFILE, "Date;Species;Abundance", savepopulationdata, + param("nature.popoutfreq")) + newdataoutput(INDFILE, "Date;ID;X;Y;Species;Sex;Age;Energy", + saveindividualdata, param("nature.indoutfreq")) +end + """ savepopulationdata(model) diff --git a/src/nature/lifehistory.jl b/src/nature/lifehistory.jl new file mode 100644 index 0000000..a8e70c0 --- /dev/null +++ b/src/nature/lifehistory.jl @@ -0,0 +1,12 @@ +### Persephone - a socio-economic-ecological model of European agricultural landscapes. +### +### This file contains a set of utility functions needed by species. +### + +#TODO initrandompopulation + +#TODO killanimal! + +#TODO reproduce! + +#TODO parentsalive? diff --git a/src/nature/nature.jl b/src/nature/nature.jl index 6610e62..b39fb82 100644 --- a/src/nature/nature.jl +++ b/src/nature/nature.jl @@ -6,78 +6,46 @@ ## An enum used to assign a sex to each animal @enum Sex hermaphrodite male female -""" - Species - -Species are differentiated by their name, and the functions used to -initialise their population and update each individual. -""" -struct Species - name::String - initpop!::Function # takes one argument: model - update!::Function # takes two arguments: animal, model - traits::Dict{String,Any} # a collection of species-specific traits -end -#TODO convert species into dicts - """ Animal This is the generic agent type for all animals. Species are differentiated -by the `species` struct passed by them during initialisation. +by trait dictionaries passed by them during initialisation. """ - +#XXX fix the @agent docstring issue (https://github.com/JuliaDynamics/Agents.jl/issues/715) @agent Animal GridAgent{2} begin - species::Species + #XXX is it (performance-)wise to use a dict for the traits? + # Doesn't this rather obviate the point of having an agent struct? + traits::Dict{String,Any} sex::Sex age::Int32 - energy::Int32 end -# This dict stores the definitions for all species that can be simulated. -# (The definitions are created in separate files and registered here.) -##TODO this needs to be rewritten to deal with the new @species macro -let specieslist = Dict{String, Species}() - """ - registerspecies(species) - - This function has to be called by every species definition file - in order for the species to be used. - """ - global function registerspecies(s::Species) - specieslist[s.name] = s - end - - """ - getspecies(name) - Returns the definition struct for the named species. - """ - global function getspecies(name::String) - specieslist[name] +### MACROS IMPLEMENTING THE DOMAIN-SPECIFIC LANGUAGE FOR DEFINING SPECIES + +macro species(name, body) + funcname = "speciesfunction_"*name + quote + Core.@__doc__ function $(esc(funcname))() + $(esc(:name)) = string($(QuoteNode(name))) + $(esc(body)) + vardict = Base.@locals + speciesdict = Dict{String,Any}() + for k in keys(vardict) + speciesdict[string(k)] = vardict[k] + end + return speciesdict + end end end -""" - newspecies(properties...) - -A utility function to create and register a new species. -All function arguments are passed on to Species(). -""" -function newspecies(properties...) - registerspecies(Species(properties...)) +macro phase(name, body) + :($(esc(name)) = function(animal::Animal, model::AgentBasedModel) $body end) end -""" - trait(animal, trait) -A utility function to return a trait value for this animal's species. -Returns nothing if the trait is not defined. -""" -function trait(animal::Animal, trait::String) - !(trait in keys(animal.species.traits)) && (return nothing) - return animal.species.traits[trait] -end +### FUNCTIONS INTEGRATING THE NATURE MODEL WITH THE REST OF PERSEPHONE """ stepagent!(animal, model) @@ -86,11 +54,7 @@ Update an animal by one day. """ function stepagent!(animal::Animal, model::AgentBasedModel) animal.age += 1 - animal.species.update!(animal,model) - if animal.energy <= 0 - @debug "$(animal.species.name) $(animal.id) has died." - kill_agent!(animal, model) - end + animal.traits[animal.traits["phase"]](animal,model) end """ @@ -101,11 +65,10 @@ Initialise the model with all simulated animal populations. function initnature!(model::AgentBasedModel) # The config file determines which species are simulated in this run for s in param("nature.targetspecies") - getspecies(s).initpop!(model) + funcname = Symbol("speciesfunction_"*s) + species = @eval $funcname() + species["initialise!"](model) end # Initialise the data output - newdataoutput(POPFILE, "Date;Species;Abundance", savepopulationdata, - param("nature.popoutfreq")) - newdataoutput(INDFILE, "Date;ID;X;Y;Species;Sex;Age;Energy", - saveindividualdata, param("nature.indoutfreq")) + initecologicaldata() end diff --git a/src/nature/species.jl b/src/nature/species.jl deleted file mode 100644 index 9cef463..0000000 --- a/src/nature/species.jl +++ /dev/null @@ -1,35 +0,0 @@ -### Persephone - a socio-economic-ecological model of European agricultural landscapes. -### -### This file implements the macros needed for the species DSL. -### - -#XXX configure species traits via a separate TOML file? - -##TODO replace the current registerspecies() -function registerspecies(speciesdict) - println(speciesdict) - processeddict = Dict{String,Any}() - for k in keys(speciesdict) - processeddict[string(k)] = speciesdict[k] - end - processeddict["testphase"]() -end - -##TODO docstring -macro species(name, body) - speciesfun = gensym() - quote - Core.@__doc__ $speciesfun = function() - $(esc(:name)) = string($(QuoteNode(name))) - $(esc(body)) - return Base.@locals - end - registerspecies($speciesfun()) - end -end - -##TODO docstring -macro phase(name, body) - :($(esc(name)) = function(animal::Animal, model::AgentBasedModel) $body end) - #:($(esc(name)) = function() $body end) #for testing -end diff --git a/src/nature/wolpertinger.jl b/src/nature/species/wolpertinger.jl similarity index 98% rename from src/nature/wolpertinger.jl rename to src/nature/species/wolpertinger.jl index f22abc6..33a73e3 100644 --- a/src/nature/wolpertinger.jl +++ b/src/nature/species/wolpertinger.jl @@ -5,6 +5,8 @@ ### Although I dare say the Wolpertinger is probably rather endangered... ### +##TODO convert to @species syntax + """ initwolpertinger!(model) diff --git a/src/nature/wyvern.jl b/src/nature/species/wyvern.jl similarity index 98% rename from src/nature/wyvern.jl rename to src/nature/species/wyvern.jl index 13a364d..2c96df9 100644 --- a/src/nature/wyvern.jl +++ b/src/nature/species/wyvern.jl @@ -5,6 +5,8 @@ ### Thankfully, wyverns are not a species we have to manage for... ### +##TODO convert to @species syntax + """ initwyvern!(model) -- GitLab