diff --git a/src/nature/nature.jl b/src/nature/nature.jl index b39fb82fb653574fc1742623a044ed8d2d45875c..70c13b3f77827918fe2ba521b432760645c3d695 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 9a478c8d769fe54b97b0da0e1cadcf79b7d93d2d..53a045382a40e1529bc0de5d3cd4362d08b74d49 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