Skip to content
Snippets Groups Projects
Commit 71a01dca authored by xo30xoqa's avatar xo30xoqa
Browse files

Wrote functions and macros for intialisation, reproduction & death

parent ccb4e0ff
No related branches found
No related tags found
No related merge requests found
### Persephone - a socio-economic-ecological model of European agricultural landscapes. ### Persephone - a socio-economic-ecological model of European agricultural landscapes.
### ###
### This file contains a set of utility functions needed by species. ### This file contains a set of life-history related utility functions needed by species.
### ###
#TODO initrandompopulation """
initrandompopulation(popsize, asexual=true)
#TODO killanimal! A simplified version of `initpopulation()`. Creates a function that initialises
`popsize` individuals, spread at random across the landscape. If `popsize` is less
than 1, it is interpreted as a population density (i.e. 1 animal per `popsize` pixels).
"""
function initrandompopulation(popsize::Float64, asexual::Bool=true)
initfunc = function(species::Dict{String,Any}, model::AgentBasedModel)
if popsize < 1.0
x, y = size(model.landscape)
popsize = Int(round(x*y*popsize))
end
for i in 1:popsize
sex = asexual ? hermaphrodite : rand([male, female])
add_agent!(Animal, model, species, sex, 0)
end
@debug "Initialised $(popsize) $(species["name"])s."
end
return initfunc
end
#TODO reproduce! #TODO initpopulation
#TODO parentsalive? """
reproduce!(animal, model, n=1)
Produce one or more offspring for the given animal at its current location.
"""
function reproduce!(animal::Animal, model::AgentBasedModel, n::Int64=1)
# XXX at the moment we don't have intra-specific variation, so currently we
# don't need sexual recombination here
for i in 1:n
sex = animal.sex == hermaphrodite ? hermaphrodite : rand([male, female])
add_agent!(animal.pos, Animal, model, animal.traits, sex, 0)
end
@debug "$(animal.traits["name"]) $(animal.id) has reproduced."
end
"""
kill(animal, model, probability=1.0, cause="")
Kill this animal, optionally with a given percentage probability.
Returns true if the animal dies, false if not.
"""
function kill!(animal::Animal, model::AgentBasedModel, probability::Float64=1.0, cause::String="")
if rand() < probability
postfix = isempty(cause) ? "." : " from $cause."
@debug "$(animal.traits["name"]) $(animal.id) has died$(postfix)"
kill_agent!(animal, model)
return true
end
return false
end
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
## An enum used to assign a sex to each animal ## An enum used to assign a sex to each animal
@enum Sex hermaphrodite male female @enum Sex hermaphrodite male female
## This will be prepended to the names of the functions created by @species
const SPECIESPREFIX = "speciesfunction_"
""" """
Animal Animal
...@@ -19,6 +22,7 @@ by trait dictionaries passed by them during initialisation. ...@@ -19,6 +22,7 @@ by trait dictionaries passed by them during initialisation.
traits::Dict{String,Any} traits::Dict{String,Any}
sex::Sex sex::Sex
age::Int32 age::Int32
#XXX keep track of parents and/or offspring?
end end
...@@ -33,7 +37,7 @@ custom syntax to describe species' biology: ...@@ -33,7 +37,7 @@ custom syntax to describe species' biology:
```julia ```julia
@species name begin @species name begin
initialise! = initrandompopulation() initialise! = initpopulation()
phase = "phase1" phase = "phase1"
... ...
@phase phase1 begin @phase phase1 begin
...@@ -51,14 +55,18 @@ life cycle (see the documentation to `@phase` for details). ...@@ -51,14 +55,18 @@ life cycle (see the documentation to `@phase` for details).
There are two parameters that all species must define: There are two parameters that all species must define:
- `initialise!` should specify a function that will be used to create - `initialise!` should specify a function that will be used to create
the starting population for this species. This function must take the starting population for this species. This function must take
exactly one argument (an `AgentBasedModel` object). two arguments, a species dict and an `AgentBasedModel` object.
The easiest way to create this function is by using `initpopultion()`.
- `phase` should be a string specifying the name of the first phase - `phase` should be a string specifying the name of the first phase
that individuals of this species will be assigned to on birth. that individuals of this species will be assigned to on birth.
Access to the rest of the model is given by the `model` variable (an object
of type `AgentBasedModel`).
""" """
macro species(name, body) macro species(name, body)
funcname = "speciesfunction_"*name funcname = Symbol(SPECIESPREFIX*string(name))
quote quote
Core.@__doc__ function $(esc(funcname))() Core.@__doc__ function $(esc(funcname))(model::AgentBasedModel)
$(esc(:name)) = string($(QuoteNode(name))) $(esc(:name)) = string($(QuoteNode(name)))
$(esc(body)) $(esc(body))
vardict = Base.@locals vardict = Base.@locals
...@@ -91,13 +99,14 @@ variables: ...@@ -91,13 +99,14 @@ variables:
`model.landscape` (a two-dimensional array of pixels containing geographic `model.landscape` (a two-dimensional array of pixels containing geographic
information). information).
The utility macros `@respond`, `@trait`, and `@here` can be used within the body of Several utility macros can be used within the body of `@phase` as a short-hand for
`@phase` as short-hand for common expressions. common expressions: `@respond`, `@trait`, `@here`, `@kill`, `@reproduce`
To transition an individual to another phase, simply redefine its phase variable: To transition an individual to another phase, simply redefine its phase variable:
`@trait(phase) = "newphase"`. `@trait(phase) = "newphase"`.
""" """
macro phase(name, body) macro phase(name, body)
#XXX Using an undeclared variable in the body doesn't throw an error?
:($(esc(name)) = function(animal::Animal, model::AgentBasedModel) $body end) :($(esc(name)) = function(animal::Animal, model::AgentBasedModel) $body end)
end end
...@@ -122,7 +131,11 @@ A utility macro to quickly access an animal's trait value. ...@@ -122,7 +131,11 @@ A utility macro to quickly access an animal's trait value.
This can only be used nested within `@phase`. This can only be used nested within `@phase`.
""" """
macro trait(traitname) macro trait(traitname)
:(animal.traits[string($(QuoteNode(name)))]) if traitname in fieldnames(Animal)
:(animal.$(traitname))
else
:(animal.traits[string($(QuoteNode(traitname)))])
end
end end
""" """
...@@ -135,6 +148,26 @@ macro here(property) ...@@ -135,6 +148,26 @@ macro here(property)
:(model.landscape[animal.pos...].$(property)) :(model.landscape[animal.pos...].$(property))
end end
"""
@kill
Kill this animal. This is a thin wrapper around `kill!()`, and passes on any arguments.
This can only be used nested within `@phase`.
"""
macro kill(args...)
:(kill!(animal, model, $(args...)))
end
"""
@reproduce
Let this animal reproduce. This is a thin wrapper around `reproduce!()`, and passes on
any arguments. This can only be used nested within `@phase`.
"""
macro reproduce(args...)
:(reproduce!(animal, model, $(args...)))
end
### FUNCTIONS INTEGRATING THE NATURE MODEL WITH THE REST OF PERSEPHONE ### FUNCTIONS INTEGRATING THE NATURE MODEL WITH THE REST OF PERSEPHONE
...@@ -156,9 +189,9 @@ Initialise the model with all simulated animal populations. ...@@ -156,9 +189,9 @@ Initialise the model with all simulated animal populations.
function initnature!(model::AgentBasedModel) function initnature!(model::AgentBasedModel)
# The config file determines which species are simulated in this run # The config file determines which species are simulated in this run
for s in param("nature.targetspecies") for s in param("nature.targetspecies")
funcname = Symbol("speciesfunction_"*s) funcname = Symbol(SPECIESPREFIX*s)
species = @eval $funcname() species = @eval $funcname($model)
species["initialise!"](model) species["initialise!"](species, model)
end end
# Initialise the data output # Initialise the data output
initecologicaldata() initecologicaldata()
......
...@@ -8,18 +8,21 @@ ...@@ -8,18 +8,21 @@
@species Skylark begin @species Skylark begin
initialise! = initrandompopulation() popdensity = 1/10000
phase = "egg"
lifeexpectancy = 365*5 lifeexpectancy = 365*5
eggharvestmortality = 0.9 eggharvestmortality = 0.9
eggpredationmortality = 0.1
initialise! = initrandompopulation(popsize)
phase = "egg"
@phase egg begin @phase egg begin
if !parentsalive(animal) @kill(@trait(eggpredationmortality), "predation")
killanimal(animal)
end
@respond harvest killanimal(animal, eggharvestmortality) #TODO dies if parents die
@respond harvest @kill(@trait(eggharvestmortality), "harvest")
if animal.age == 14 if animal.age == 14
@trait(phase) = "nestling" @trait(phase) = "nestling"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment