From ccb4e0ff2c972bb21810e776847bf4420c1f89a3 Mon Sep 17 00:00:00 2001 From: Daniel Vedder <daniel.vedder@idiv.de> Date: Thu, 15 Dec 2022 22:49:22 +0100 Subject: [PATCH] Wrote @respond, @trait and @here macros for the species DSL Also wrote the documentation for @species and @here. Actually getting the code to run will be the next challenge ;-) --- src/nature/nature.jl | 91 +++++++++++++++++++++++++++++++++++ src/nature/species/skylark.jl | 10 +++- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/nature/nature.jl b/src/nature/nature.jl index b39fb82..70c13b3 100644 --- a/src/nature/nature.jl +++ b/src/nature/nature.jl @@ -24,6 +24,37 @@ end ### MACROS IMPLEMENTING THE DOMAIN-SPECIFIC LANGUAGE FOR DEFINING SPECIES +""" + @species(name, body) + +A macro used to create new species definitions for the nature model. +This is effectively a simple domain-specific language, establishing a +custom syntax to describe species' biology: + +```julia +@species name begin + initialise! = initrandompopulation() + phase = "phase1" + ... + @phase phase1 begin + ... + end +end +``` + +The definition body (enclosed in the begin/end block) has two sections. +First comes a list of species-specific parameters, which are assigned +just like normal variables. Second come one or more phase definitions, +that describe the behaviour of the species during various parts of its +life cycle (see the documentation to `@phase` for details). + +There are two parameters that all species must define: +- `initialise!` should specify a function that will be used to create + the starting population for this species. This function must take + exactly one argument (an `AgentBasedModel` object). +- `phase` should be a string specifying the name of the first phase + that individuals of this species will be assigned to on birth. +""" macro species(name, body) funcname = "speciesfunction_"*name quote @@ -40,10 +71,70 @@ macro species(name, body) end end +""" + @phase(name, body) + +This macro is designed to be used within a species definition block (i.e. within +the body of a call to `@species`). + +The idea behind this is that species show very different behaviour during different +phases of their lives. Therefore, `@phase` can be used define the behaviour for one +such phase, and the conditions under which the animal transitions to another phase. + +`@phase` works by creating a function that will be called by the model if the +animal is in the relevant phase. When it is called, it has access to the following +variables: +- `animal` a reference to the animal itself. This provides access to `animal.age`, + `animal.sex`, and `animal.traits` (a dict that gives access to all species parameters). +- `model` a reference to the model world (an object of type `AgentBasedModel`). + This allows access to `model.date` (the current simulation date) and + `model.landscape` (a two-dimensional array of pixels containing geographic + information). + +The utility macros `@respond`, `@trait`, and `@here` can be used within the body of +`@phase` as short-hand for common expressions. + +To transition an individual to another phase, simply redefine its phase variable: +`@trait(phase) = "newphase"`. +""" macro phase(name, body) :($(esc(name)) = function(animal::Animal, model::AgentBasedModel) $body end) end +""" + @respond(eventname, body) + +Define how an animal responds to a landscape event that affects its current position. +This can only be used nested within `@phase`. +""" +macro respond(eventname, body) + quote + if $(esc(eventname)) in @here(events) + $body + end + end +end + +""" + @trait(traitname) + +A utility macro to quickly access an animal's trait value. +This can only be used nested within `@phase`. +""" +macro trait(traitname) + :(animal.traits[string($(QuoteNode(name)))]) +end + +""" + @here(property) + +A utility macro to quickly access a property of the animal's current position. +This can only be used nested within `@phase`. +""" +macro here(property) + :(model.landscape[animal.pos...].$(property)) +end + ### FUNCTIONS INTEGRATING THE NATURE MODEL WITH THE REST OF PERSEPHONE diff --git a/src/nature/species/skylark.jl b/src/nature/species/skylark.jl index 9a478c8..53a0453 100644 --- a/src/nature/species/skylark.jl +++ b/src/nature/species/skylark.jl @@ -8,15 +8,21 @@ @species Skylark begin - lifeexpectancy = 365*5 + initialise! = initrandompopulation() phase = "egg" + + lifeexpectancy = 365*5 + eggharvestmortality = 0.9 @phase egg begin if !parentsalive(animal) killanimal(animal) end + + @respond harvest killanimal(animal, eggharvestmortality) + if animal.age == 14 - changephase("nestling") + @trait(phase) = "nestling" end end -- GitLab