Nature submodel
nature.jl
This file is responsible for managing the animal modules.
Persefone.Animal
— TypeAnimal
This is the generic agent type for all animals. Individual species are created using the @species
macro. In addition to user-defined, species-specific fields, all species contain the following fields:
id
An integer unique identifier for this individual.sex
male, female, or hermaphrodite.parents
The IDs of the individual's parents.pos
An (x, y) coordinate tuple.age
The age of the individual in days.phase
The update function to be called during the individual's current life phase.energy
A DEBparameters struct for calculating energy budgets.offspring
A vector containing the IDs of an individual's children.territory
A vector of coordinates that comprise the individual's territory.
Persefone.animalid
— Methodanimalid(animal)
A small utility function to return a string with the species name and ID of an animal.
Persefone.create!
— Methodcreate!(animal, model)
The create!
function is called for every individual at birth or at model initialisation. Species must use @create
to define a species-specific method. This is the fall- back method, in case none is implemented for a species.
Persefone.initnature!
— Methodinitnature!(model)
Initialise the model with all simulated animal populations.
Persefone.killallanimals!
— Methodkillallanimals!(model)
Remove all animal individuals from the simulation.
Persefone.speciesof
— Methodspeciesof(animal)
Return the species name of this animal as a string.
Persefone.speciestype
— Methodspeciestype(name)
Return the Type of this species.
Persefone.stepagent!
— Methodstepagent!(animal, model)
Update an animal by one day, executing it's currently active phase function.
Persefone.updatenature!
— Methodupdatenature!(model)
Run processes that affect all animals.
macros.jl
This file contains all the macros that can be used in the species DSL.
Persefone.@animal
— Macro@animal(id)
Return the animal object associated with this ID number. This can only be used in a context where the model
object is available (e.g. nested within @phase
).
Persefone.@countanimals
— Macro@countanimals(radius=0, species="")
Count the number of animals at or near this location, optionally filtering by species. This can only be used nested within @phase
or @habitat
.
Persefone.@create
— Macro@create(species, body)
Define a special phase function (create!
()) that will be called when an individual of this species is created, at the initialisation of the simulation or at birth.
As for @phase
, the body of this macro has access to the variables self
(the individual being created) and model
(the simulation world), and can thus use all macros available in @phase
.
Persefone.@cropcover
— Macro@cropcover
Return the percentage ground cover of the crop at this position, or nothing if there is no crop here. This is a utility wrapper that can only be used nested within @phase
or @habitat
.
Persefone.@cropheight
— Macro@cropheight
Return the height of the crop at this position, or nothing if there is no crop here. This is a utility wrapper that can only be used nested within @phase
or @habitat
.
Persefone.@cropname
— Macro@cropname
Return the name of the local croptype, or an empty string if there is no crop here. This is a utility wrapper that can only be used nested within @phase
or @habitat
.
Persefone.@destroynest
— Macro@destroynest(reason)
Utility wrapper for destroynest!()
in the Skylark model.
Persefone.@directionto
— Macro@directionto
Calculate the direction to an animal or the closest habitat of the specified type or descriptor. This is a utility wrapper that can only be used nested within @phase
or @habitat
.
Persefone.@distanceto
— Macro@distanceto
Calculate the distance to an animal or the closest habitat of the specified type or descriptor. This is a utility wrapper that can only be used nested within @phase
or @habitat
.
Persefone.@distancetoedge
— Macro@distancetoedge
Calculate the distance to the closest neighbouring habitat. This is a utility wrapper that can only be used nested within @phase
or @habitat
.
Persefone.@follow
— Macro@follow(leader, distance)
Move to a location within the given distance of the leading animal. This is a utility wrapper that can only be used nested within @phase
.
Persefone.@habitat
— Macro@habitat
Specify habitat suitability for spatial ecological processes.
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
, @cropname
, @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:
movementhabitat = @habitat(@landcover() in (grass agriculture soil))
nestinghabitat = @habitat((@landcover() == grass ||
(@landcover() == agriculture && @cropname() != "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.
Persefone.@here
— Macro@here()
Return the landscape pixel of this animal's current location. This can only be used nested within @phase
.
Persefone.@isalive
— Macro@isalive(id)
Test whether the animal with the given ID is still alive. This can only be used in a context where the model
object is available (e.g. nested within @phase
).
Persefone.@isoccupied
— Macro@isoccupied(position)
Test whether this position is already occupied by an animal of this species. This can only be used nested within @phase
.
Persefone.@kill
— Macro@kill
Kill this animal (and immediately abort its current update if it dies). This is a thin wrapper around kill!
, and passes on any arguments. This can only be used nested within @phase
.
Persefone.@killother
— Macro@killother
Kill another animal. This is a thin wrapper around kill!
, and passes on any arguments. This can only be used nested within @phase
.
Persefone.@landcover
— Macro@landcover
Returns the local landcover. This is a utility wrapper that can only be used nested within @phase
or @habitat
.
Persefone.@lastyear
— Macro@lastyear(annualdate)
Construct a date object referring to the last year in the model from an AnnualDate. Only use in scopes where model
is available.
Persefone.@migrate
— Macro@migrate(arrival)
Remove this animal from the map and add it to the migrant species pool. It will be returned to its current location at the specified arrival
date. This can only be used nested within @phase
.
Persefone.@move
— Macro@move(position)
Move the current individual to a new position. This is a utility wrapper that can only be used nested within @phase
.
Persefone.@nearby_animals
— Macro@nearby_animals(radius=0, species="")
Return an iterator over all animals in the given radius around the current position. This can only be used nested within @phase
or @habitat
.
Persefone.@neighbours
— Macro@neighbours(radius=0, conspecifics=true)
Return an iterator over all (by default conspecific) animals in the given radius around this animal, excluding itself. This can only be used nested within @phase
.
Persefone.@nextyear
— Macro@nextyear(annualdate)
Construct a date object referring to the next year in the model from an AnnualDate. Only use in scopes where model
is available.
Persefone.@occupy
— Macro@occupy(position)
Add the given position to this animal's territory. Use @vacate
to remove positions from the territory again. This can only be used nested within @phase
.
Persefone.@phase
— Macro@phase(name, body)
Use this macro to describe a species' behaviour during a given phase of its life. The idea behind this is that species show very different behaviour at different times 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:
self
a reference to the animal itself. This provides access to all the variables defined in the@species
definition, as well as all standardAnimal
variables (e.g.self.age
,self.sex
,self.offspring
).pos
gives the animal's current position as a coordinate tuple.model
a reference to the model world (an object of typeSimulationModel
). This allows access, amongst others, tomodel.date
(the current simulation date) andmodel.landscape
(a two-dimensional array of pixels containing geographic information).
Many macros are available to make the code within the body of @phase
more succinct. Some of the most important of these are: @setphase
, @respond
, @kill
, @reproduce
, @neighbours
, @migrate
, @move
, @occupy
, @rand
.
Persefone.@populate
— Macro@populate(species, params)
Set the parameters that are used to initialise this species' population. For parameter options, see PopInitParams
.
@populate <species> begin
<parameter> = <value>
...
end
Persefone.@randomdirection
— Macro@randomdirection(range=1)
Return a random direction tuple that can be passed to @walk
. This is a utility wrapper that can only be used nested within @phase
.
Persefone.@randompixel
— Macro@randompixel(range, habitatdescriptor)
Find a random pixel within a given range
of the animal's location that matches the habitatdescriptor (create this using @habitat
). This is a utility wrapper that can only be used nested within @phase
.
Persefone.@reproduce
— Macro@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
.
Persefone.@respond
— Macro@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
.
Persefone.@setphase
— Macro@setphase(newphase)
Switch this animal over to a different phase. This can only be used nested within @phase
.
Persefone.@species
— Macro@species(name, body)
A macro used to add new species types to the nature model. Use this to define species-specific variables and parameters.
The macro works by creating a keyword-defined mutable struct that contains the standard fields described for the Animal
type, as well as any new fields that the user adds:
@species <name> begin
<var1> = <value>
<var2> = <value>
...
end
To complete the species definition, the @phase
, @create
, and @populate
macros also need to be used.
Persefone.@thisyear
— Macro@thisyear(annualdate)
Construct a date object referring to the current model year from an AnnualDate. Only use in scopes where model
is available.
Persefone.@vacate
— Macro@vacate(position)
Remove the given position from this animal's territory. This can only be used nested within @phase
.
Persefone.@vacate
— Macro@vacate()
Remove this animal's complete territory. This can only be used nested within @phase
.
Persefone.@walk
— Macro@walk(direction, speed)
Walk the animal in a given direction, which is specified by a tuple of coordinates relative to the animal's current position (i.e. (2, -3)
increments the X coordinate by 2 and decrements the Y coordinate by 3.) This is a utility wrapper that can only be used nested within @phase
.
individuals.jl
This file contains life-history and other ecological functions that apply to all animal individuals, such reproduction, death, and movement.
Persefone.followanimal!
— Functionfollowanimal!(follower, leader, model, distance=0)
Move the follower animal to a location near the leading animal.
Persefone.kill!
— Functionkill!(animal, model, probability=1.0, cause="")
Kill this animal, optionally with a given percentage probability. Returns true if the animal dies, false if not.
Persefone.migrate!
— Methodmigrate!(animal, model, arrival)
Remove this animal from the map and add it to the migrant species pool. It will be returned to its current location at the specified arrival
date.
Persefone.move!
— Methodmove!(animal, model, position)
Move the animal to the given position, making sure that this is in-bounds. If the position is out of bounds, the animal stops at the map edge.
Persefone.occupy!
— Methodoccupy!(animal, model, position)
Add the given location to the animal's territory. Returns true
if successful (i.e. if the location was not already occupied by a conspecific), false
if not.
Persefone.reproduce!
— Functionreproduce!(animal, model, mate, n=1)
Produce one or more offspring for the given animal at its current location. The mate
argument gives the ID of the reproductive partner.
Persefone.vacate!
— Methodvacate!(animal, model, position)
Remove this position from the animal's territory.
Persefone.vacate!
— Methodvacate!(animal, model)
Remove the animal's complete territory.
Persefone.walk!
— Functionwalk!(animal, model, direction, distance=-1)
Let the animal move in the given direction, where the direction is defined by an (x, y) tuple to specify the shift in coordinates. If maxdist >= 0, move no further than the specified distance.
Persefone.walk!
— Functionwalk!(animal, model, direction, distance=1pixel)
Let the animal move a given number of steps in the given direction ("north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "random").
populations.jl
This file contains functions that apply to all animal populations, such as for initialisation, or querying for neighbours.
Persefone.PopInitParams
— TypePopInitParams
A set of parameters used by initpopulation!
to initialise the population of a species at the start of a simulation. Define these parameters for each species using @populate
.
initphase
determines which life phase individuals will be assigned to at model initialisation (required).birthphase
determines which life phase individuals will be assigned to at birth (required).habitat
is a function that determines whether a given location is suitable or not (create this using@habitat
). By default, every cell will be occupied.popsize
determines the number of individuals that will be created, dispersed over the suitable locations in the landscape. If this is zero or negative, one individual will be created in every suitable location. If it is greater than the number of suitable locations, multiple individuals will be created per location. Alternately, useindarea
.indarea
: if this is greater than zero, it determines the habitat area allocated to each individual or pair. To be precise, the chance of creating an individual (or pair of individuals) at a suitable location is 1/indarea. Use this as an alternative topopsize
.If
pairs
is true, a male and a female individual will be created in each selected location, otherwise, only one individual will be created at a time. (default: false)If
asexual
is true, all created individuals are assigned the sexhermaphrodite
, otherwise, they are randomly assignedmale
orfemale
. Ifpairs
is true,asexual
is ignored. (default: false)
Persefone.countanimals
— Methodcountanimals(pos, model; radius=0, species="")
Return the number of animals in the given radius around this position, optionally filtering by species.
Persefone.directionto
— Methoddirectionto(pos, model, animal)
Calculate the direction from the given position to the animal.
Persefone.distanceto
— Methoddistanceto(pos, model, animal)
Calculate the distance from the given position to the animal.
Persefone.initindividuals!
— Methodinitindividuals!(species, pos, popinitparams, model)
Initialise one or two individuals (depending on the pairs
parameter) in the given location. Returns the number of created individuals. (Internal helper function for initpopulation!()
.)
Persefone.initpopulation!
— Methodinitpopulation!(speciesname, model)
Initialise the population of the given species, based on the parameters stored in PopInitParams
. Define these using @populate
.
Persefone.initpopulation!
— Methodinitpopulation!(speciestype, popinitparams, model)
Initialise the population of the given species, based on the given initialisation parameters. This is an internal function called by initpopulation!()
, and was split off from it to allow better testing.
Persefone.isalive
— Methodisalive(id, model)
Test whether the animal with the given ID is still alive.
Persefone.isoccupied
— Methodisoccupied(model, position, species)
Test whether this location is part of the territory of an animal of the given species.
Persefone.nearby_animals
— Methodnearby_animals(pos, model; radius= 0, species="")
Return a list of animals in the given radius around this position, optionally filtering by species.
Persefone.nearby_ids
— Methodnearby_ids(pos, model, radius)
Return a list of IDs of the animals within a given radius of the position.
Persefone.neighbours
— Functionneighbours(animal, model, radius=0, conspecifics=true)
Return a list of animals in the given radius around this animal, excluding itself. By default, only return conspecific animals.
Persefone.populationparameters
— Methodpopulationparameters(type)
A function that returns a PopInitParams
object for the given species type. Parametric methods for each species are defined with @populate
. This is the catch-all method, which throws an error if no species-specific function is defined.
Persefone.territorysize
— Functionterritorysize(animal, model, stripunits=false)
Calculate the size of this animal's territory in the given unit. If stripunits
is true, return the size as a plain number.
ecologicaldata.jl
This file contains a set of life-history related utility functions needed by species.
Persefone.initecologicaldata
— Methodinitecologicaldata()
Create output files for each data group collected by the nature model.
Persefone.saveindividualdata
— Methodsaveindividualdata(model)
Return a data table (to be printed to individuals.csv
), listing all properties of all animal individuals in the model. May be called never, daily, monthly, yearly, or at the end of a simulation, depending on the parameter nature.indoutfreq
. WARNING: Produces very big files!
Persefone.savepopulationdata
— Methodsavepopulationdata(model)
Return a data table (to be printed to populations.csv
), giving the current date and population size for each animal species. May be called never, daily, monthly, yearly, or at the end of a simulation, depending on the parameter nature.popoutfreq
.
Persefone.skylarkabundance
— Methodskylarkabundance(model)
Save skylark abundance data, including total abundance and demographic data (abundances of breeding/non-breeding/juvenile/migrated individuals).
Persefone.skylarkterritories
— Methodskylarkterritories(model)
Return a list of all coordinates occupied by a skylark territory, and the ID of the individual holding the territory. WARNING: produces very big files.