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

Wrote @habitat and associated macros

Not yet functional - the necessary functions still need to be implemented.
parent dbb3c9a5
No related branches found
No related tags found
No related merge requests found
...@@ -101,16 +101,41 @@ end ...@@ -101,16 +101,41 @@ end
Return the land cover class at this position (utility wrapper). Return the land cover class at this position (utility wrapper).
""" """
function landcover(model::AgentBasedModel, pos::Tuple{Int64,Int64}) function landcover(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
model.landscape[pos...].landcover model.landscape[pos...].landcover
end end
""" """
farmplot(model, position) farmplot(model, position)
Return the farm plot at this position (utility wrapper). Return the farm plot at this position, or nothing if there is none (utility wrapper).
""" """
function farmplot(model::AgentBasedModel, pos::Tuple{Int64,Int64}) function farmplot(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
model[model.landscape[pos...].fieldid] ismissing(model.landscape[pos...].fieldid) ? nothing :
model[model.landscape[pos...].fieldid]
end
"""
distanceto(model, pos, habitattype)
Calculate the distance from the given location to the closest habitat of the specified type.
Caution: can be computationally expensive!
"""
function distanceto(pos::Tuple{Int64,Int64}, model::AgentBasedModel, habitattype::LandCover)
#XXX can I make this check for both land cover type and crop type?
# (Or even take a full habitat descriptor?)
#TODO
end
"""
distancetoedge(model, pos)
Calculate the distance from the given location to the closest neighbouring habitat.
Caution: can be computationally expensive!
"""
function distancetoedge(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
lc = landcover(model, pos)
dist = 1
#TODO
end end
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
### This file is responsible for managing the crop growth modules. ### This file is responsible for managing the crop growth modules.
### ###
"The crop types simulated by the model"
@enum CropType fallow wheat barley maize rapeseed
#XXX not sure whether it makes sense to have this as an agent type, #XXX not sure whether it makes sense to have this as an agent type,
# or perhaps better a grid property? # or perhaps better a grid property?
""" """
...@@ -11,9 +14,9 @@ ...@@ -11,9 +14,9 @@
This represents one field, i.e. a collection of pixels with the same management. This represents one field, i.e. a collection of pixels with the same management.
This is the spatial unit with which the crop growth model and the farm model work. This is the spatial unit with which the crop growth model and the farm model work.
""" """
@agent FarmPlot NoSpaceAgent begin @agent FarmPlot GridAgent{2} begin
pixels::Vector{Tuple{Int64, Int64}} pixels::Vector{Tuple{Int64, Int64}}
croptype::String croptype::CropType
height::Float64 height::Float64
biomass::Float64 biomass::Float64
#TODO #TODO
...@@ -49,7 +52,7 @@ function initfields!(model::AgentBasedModel) ...@@ -49,7 +52,7 @@ function initfields!(model::AgentBasedModel)
model.landscape[x,y].fieldid = objectid model.landscape[x,y].fieldid = objectid
push!(model[objectid].pixels, (x,y)) push!(model[objectid].pixels, (x,y))
else else
fp = add_agent!(FarmPlot, model, [(x,y)], "fallow", 0.0, 0.0) fp = add_agent!(FarmPlot, model, [(x,y)], fallow, 0.0, 0.0)
model.landscape[x,y].fieldid = fp.id model.landscape[x,y].fieldid = fp.id
convertid[rawid] = fp.id convertid[rawid] = fp.id
n += 1 n += 1
...@@ -86,5 +89,25 @@ function averagefieldsize(model::AgentBasedModel) ...@@ -86,5 +89,25 @@ function averagefieldsize(model::AgentBasedModel)
push!(sizes, size(a.pixels)[1]/conversionfactor) push!(sizes, size(a.pixels)[1]/conversionfactor)
end end
round(sum(sizes)/size(sizes)[1], digits=2) round(sum(sizes)/size(sizes)[1], digits=2)
#sizes end
"""
croptype(model, position)
Return the crop at this position, or nothing if there is no crop here (utility wrapper).
"""
function croptype(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
ismissing(model.landscape[pos...].fieldid) ? nothing :
model[model.landscape[pos...].fieldid].croptype
end
"""
cropheight(model, position)
Return the height of the crop at this position, or nothing if there is no crop here
(utility wrapper).
"""
function cropheight(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
ismissing(model.landscape[pos...].fieldid) ? nothing :
model[model.landscape[pos...].fieldid].height
end end
...@@ -25,7 +25,9 @@ function initrandompopulation(popsize::Union{Int64,Float64}, asexual::Bool=true) ...@@ -25,7 +25,9 @@ function initrandompopulation(popsize::Union{Int64,Float64}, asexual::Bool=true)
return initfunc return initfunc
end end
#TODO initpopulation #TODO initpopulation with habitat descriptor
#TODO initpopulation with dispersal from an original source
#TODO initpopulation based on known occurences in real-life
""" """
reproduce!(animal, model, n=1) reproduce!(animal, model, n=1)
...@@ -57,3 +59,16 @@ function kill!(animal::Animal, model::AgentBasedModel, probability::Float64=1.0, ...@@ -57,3 +59,16 @@ function kill!(animal::Animal, model::AgentBasedModel, probability::Float64=1.0,
end end
return false return false
end end
"""
countanimals(model, pos, speciesname)
Count the number of animals of the given species in this location.
"""
macro countanimals(model::AgentBasedModel, pos::Tuple{Int64,Int64}, speciesname::String)
n = 0
for a in nearby_ids(pos, model, 0)
typeof(model[a]) == Animal && model[a].traits.name == speciesname && (n += 1)
end
return n
end
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
### This file is responsible for managing the animal modules. ### This file is responsible for managing the animal modules.
### ###
### FUNCTIONS AND TYPES INTEGRATING THE NATURE MODEL WITH THE REST OF PERSEPHONE
## 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
...@@ -30,6 +33,31 @@ function animalid(a::Animal) ...@@ -30,6 +33,31 @@ function animalid(a::Animal)
return "$(a.traits["name"]) $(a.id)" return "$(a.traits["name"]) $(a.id)"
end end
"""
stepagent!(animal, model)
Update an animal by one day, executing it's currently active phase function.
"""
function stepagent!(animal::Animal, model::AgentBasedModel)
animal.age += 1
animal.traits[animal.traits["phase"]](animal,model)
end
"""
initnature!(model)
Initialise the model with all simulated animal populations.
"""
function initnature!(model::AgentBasedModel)
# The config file determines which species are simulated in this run
for speciesname in param("nature.targetspecies")
species = @eval $(Symbol(speciesname))($model)
species["initialise!"](species, model)
end
# Initialise the data output
initecologicaldata()
end
### MACROS IMPLEMENTING THE DOMAIN-SPECIFIC LANGUAGE FOR DEFINING SPECIES ### MACROS IMPLEMENTING THE DOMAIN-SPECIFIC LANGUAGE FOR DEFINING SPECIES
...@@ -104,7 +132,7 @@ variables: ...@@ -104,7 +132,7 @@ variables:
information). information).
Several utility macros can be used within the body of `@phase` as a short-hand for Several utility macros can be used within the body of `@phase` as a short-hand for
common expressions: `@respond`, `@trait`, `@here`, `@kill`, `@reproduce` 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"`.
...@@ -122,7 +150,6 @@ This can only be used nested within `@phase`. ...@@ -122,7 +150,6 @@ This can only be used nested within `@phase`.
""" """
macro respond(eventname, body) macro respond(eventname, body)
quote quote
#TODO test this
if $(esc(eventname)) in @here(events) if $(esc(eventname)) in @here(events)
$body $body
end end
...@@ -175,30 +202,105 @@ end ...@@ -175,30 +202,105 @@ end
#XXX add a macro @f to call a function with animal and model as first parameters? #XXX add a macro @f to call a function with animal and model as first parameters?
# e.g. @f(nearby_agents, distance) # e.g. @f(nearby_agents, distance)
# -> rather create wrapper macros
"""
@habitat
Specify habitat suitability for spatial ecological processes.
### FUNCTIONS INTEGRATING THE NATURE MODEL WITH THE REST OF PERSEPHONE This macro works by creating an anonymous function that takes in a model object
and a position, and returns `true` or `false` depending on the conditions
specified in the macro body.
Several utility macros can be used within the body of `@habitat` as a short-hand for
common expressions: `@landcover`, `@croptype`, `@cropheight`, `@distanceto`,
`@distancetoedge`, `@countanimals`. The variables `model` and `pos` can be used
for checks that don't have a macro available.
Two example uses of `@habitat` might look like this:
```julia
movementhabitat = @habitat(@landcover() in (grass agriculture soil))
nestinghabitat = @habitat((@landcover() == grass ||
(@landcover() == agriculture && @croptype() != maize &&
@cropheight() < 10)) &&
@distanceto(forest) > 20)
```
For more complex habitat suitability checks, the use of this macro can be
circumvented by directly creating an equivalent function.
""" """
stepagent!(animal, model) macro habitat(body)
quote
function($(esc(:pos)), $(esc(:model)))
if $(esc(body))
return true
else
return false
end
end
end
end
Update an animal by one day, executing it's currently active phase function.
""" """
function stepagent!(animal::Animal, model::AgentBasedModel) @landcover
animal.age += 1
animal.traits[animal.traits["phase"]](animal,model) Returns the local landcover. This is a utility wrapper that can only be used
nested within `@habitat`.
"""
macro landcover()
:(landcover($(esc(:pos)), $(esc(:model))))
end end
""" """
initnature!(model) @croptype
Initialise the model with all simulated animal populations. Return the local croptype, or nothing if there is no crop here.
This is a utility wrapper that can only be used nested within `@habitat`.
""" """
function initnature!(model::AgentBasedModel) macro croptype()
# The config file determines which species are simulated in this run :(croptype($(esc(:pos)), $(esc(:model))))
for speciesname in param("nature.targetspecies") end
species = @eval $(Symbol(speciesname))($model)
species["initialise!"](species, model) """
end @cropheight
# Initialise the data output
initecologicaldata() Return the height of the crop at this position, or 0 if there is no crop here.
This is a utility wrapper that can only be used nested within `@habitat`.
"""
macro cropheight()
:(cropheight($(esc(:pos)), $(esc(:model))))
end
"""
@distanceto(habitattype)
Calculate the distance to the closest habitat of the specified type.
This is a utility wrapper that can only be used nested within `@habitat`.
"""
macro distanceto(habitattype)
:(distanceto($(esc(:pos)), $(esc(:model)), $habitattype))
end
"""
@distancetoedge
Calculate the distance to the closest neighbouring habitat.
This is a utility wrapper that can only be used nested within `@habitat`.
"""
macro distancetoedge()
:(distancetoedge($(esc(:pos)), $(esc(:model))))
end
"""
@countanimals(speciesname)
Count the number of animals of the given species in this location.
This is a utility wrapper that can only be used nested within `@habitat`.
"""
macro countanimals(speciesname)
#XXX this also counts the enquiring agent
:(countanimals($(esc(:pos)), $(esc(:model)), $speciesname))
end end
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
### ###
##XXX At the moment, this is just a skeleton to show what I want to be able to interpret ##XXX At the moment, this is just a skeleton to show what I want to be able to interpret
## with the @species and @phase macros ## with the @species, @phase, and @habitat macros (and their helper macros)
@species Skylark begin @species Skylark begin
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
initialise! = initrandompopulation(popsize) initialise! = initrandompopulation(popsize)
phase = "egg" phase = "egg"
habitats = @habitat((@landcover() == grass ||
(@landcover() == agriculture && @croptype() != maize &&
@cropheight() < 10)) &&
@distanceto(forest) > 20)
@phase egg begin @phase egg begin
@kill(@trait(eggpredationmortality), "predation") @kill(@trait(eggpredationmortality), "predation")
......
...@@ -3,4 +3,8 @@ ...@@ -3,4 +3,8 @@
### These are the tests for the nature model (excluding individual species). ### These are the tests for the nature model (excluding individual species).
### ###
@testset "Species macros" begin
#TODO
end
#TODO #TODO
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment