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

Finished implementing the Skylark :D

Closes #9. Still needs testing & validation.
parent 5d3cb400
No related branches found
No related tags found
No related merge requests found
......@@ -192,23 +192,23 @@ end
"""
cropheight(model, position)
Return the height of the crop at this position, or nothing if there is no crop here
Return the height of the crop at this position, or 0 if there is no crop here
(utility wrapper).
"""
function cropheight(pos::Tuple{Int64,Int64}, model::SimulationModel)
ismissing(model.landscape[pos...].fieldid) ? 0cm : #FIXME should not return 0
model.farmplots[model.landscape[pos...].fieldid].height
ismissing(model.landscape[pos...].fieldid) ? 0cm : #FIXME can I return something better than 0?
model.farmplots[model.landscape[pos...].fieldid].height*1cm #FIXME units
end
"""
cropcover(model, position)
Return the percentage ground cover of the crop at this position, or nothing if there is no crop
Return the percentage ground cover of the crop at this position, or 0 if there is no crop
here (utility wrapper).
"""
function cropcover(pos::Tuple{Int64,Int64}, model::SimulationModel)
#FIXME LAItotal != ground cover?
ismissing(model.landscape[pos...].fieldid) ? 0 : #FIXME should not return 0
ismissing(model.landscape[pos...].fieldid) ? 0 : #FIXME can I return something better than 0?
model.farmplots[model.landscape[pos...].fieldid].LAItotal
end
......@@ -35,13 +35,12 @@ At the moment, this implementation is still in development.
const visionrange::Length = 200m #XXX arbitrary
const eggtime::Int64 = 11 # days from laying to hatching
const nestlingtime::UnitRange{Int64} = 7:11 # days from hatching to leaving nest
const fledglingtime::UnitRange{Int64} = 25:30 # days from hatching to independence
const nestlingtime::Int64 = 9 # days from hatching to leaving nest
const fledglingtime::Int64 = 21 # days from leaving the nest to independence
#XXX predation mortality should be habitat-dependent
const eggpredationmortality::Float64 = 0.03 # per-day egg mortality from predation
const nestharvestmortality::Float64 = 1.0 # egg/nestling mortality after a harvest event
const nestlingpredationmortality::Float64 = 0.03 # per-day nestling mortality from predation
const fledglingharvestmortality::Float64 = 0.5 # fledgling mortality after harvest
const fledglingpredationmortality::Float64 = 0.01 # per-day fledgling mortality from predation
const firstyearmortality::Float64 = 0.38 # total mortality in the first year after independence
const migrationmortality::Float64 = 0.33 # chance of dying during the winter
......@@ -57,17 +56,14 @@ At the moment, this implementation is still in development.
const nestingbegin::Tuple{Int64,Int64} = (April, 10) # begin nesting in the middle of April
const nestbuildingtime::UnitRange{Int64} = 4:5 # 4-5 days needed to build a nest (doubled for first nest)
const eggsperclutch::UnitRange{Int64} = 2:5 # eggs laid per clutch
const breedingdelay::Int64 = 18 # days after hatching before starting a new brood #XXX ??
const nestingend::Int64 = July # last month of nesting
# individual variables
#FIXME check what needs to be rewritten
timer::Int64 = 0 # a count-down timer that can be used for different purposes
timer::Int64 = 0 # a counter that can be used for different purposes
migrationdates::Tuple = () # is defined by each individual in @create(Skylark)
mate::Int64 = -1 # the agent ID of the mate (-1 if none)
nest::Tuple = () # coordinates of current nest
nestcompletion::Int64 = 0 # days left until the nest is built
clutch::Int64 = 0 # number of offspring in current clutch
clutch::Int64 = 0 # number and life stage of offspring in current clutch
end
......@@ -203,8 +199,8 @@ Females that have found a partner build a nest and lay eggs in a suitable locati
self.timer -= 1
end
# tillage and harvest destroys the nest
@respond(tillage, self.nest = ())
@respond(harvesting, self.nest = ())
@respond(tillage, destroynest!(self, "tillage"))
@respond(harvesting, destroynest!(self, "harvesting"))
end
"""
......@@ -212,12 +208,32 @@ Females that have laid eggs take care of their chicks, restarting the nesting pr
chicks are independent or in case of brood loss.
"""
@phase Skylark breeding begin
#TODO wait for eggs to hatch & chicks to mature, checking for mortality
# restart breeding cycle if there is time
if self.clutch == 0 && month(model.date) <= self.nestingend
@setphase(nesting) #TODO breeding delay?
elseif month(model.date) > self.nestingend
@setphase(nonbreeding)
#XXX Schachtelbruten - sometimes skylarks start a new nest before the previous young are gone
# wait for eggs to hatch & chicks to mature, checking for predation and disturbance mortality
self.timer += 1
#XXX this should be habitat-dependent!
if self.timer <= self.eggtime
@chance(self.eggpredationmortality) && destroynest!(self, "predation")
elseif self.timer <= self.eggtime + self.nestlingtime
@chance(self.nestlingpredationmortality) && destroynest!(self, "predation")
elseif self.timer <= self.eggtime + self.nestlingtime + self.fledglingtime
@chance(self.fledglingpredationmortality) && destroynest!(self, "predation")
else
# create new young, reset timer and clutch counter
@reproduce(self.clutch, self.mate)
self.clutch = 0
end
if self.clutch > 0
# tillage and harvest destroys the nest
@respond(tillage, destroynest!(self, "tillage"))
@respond(harvesting, destroynest!(self, "harvesting"))
else # restart breeding cycle if there is time
self.timer = 0
if month(model.date) <= self.nestingend
@setphase(nesting)
elseif month(model.date) > self.nestingend
@setphase(nonbreeding)
end
end
end
......@@ -236,8 +252,8 @@ function migrationdates(skylark::Skylark, model::SimulationModel)
minarrive = skylark.sex == male ? (February, 15) : (March, 1)
deltaleave = @rand(0:45) #XXX ought to be normally distributed
deltaarrive = @rand(0:15) #XXX ought to be normally distributed
leave = Date(year(model.date), minleave[1], minleave[2]) + Day(deltaleave))
arrive = Date(year(model.date)+1, minarrive[1], minarrive[2]) + Day(deltaarrive))
leave = Date(year(model.date), minleave[1], minleave[2]) + Day(deltaleave)
arrive = Date(year(model.date)+1, minarrive[1], minarrive[2]) + Day(deltaarrive)
(leave, arrive)
end
......@@ -292,7 +308,9 @@ end
foragequality(skylark, model, pos)
Calculate the relative quality of the habitat at this position for foraging.
(Approximated from Püttmanns et al., 2021; Jeromin, 2002; Jenny, 1990b.)
This assumes that open habitat is best (quality = 1.0), and steadily decreases as vegetation
height and/or cover increase. (Linear regressions based on Püttmanns et al., 2021; Jeromin, 2002;
Jenny, 1990b.)
"""
function foragequality(skylark::Skylark, model::SimulationModel, pos::Tuple{Int64,Int64})
#TODO this is a key function that needs to be validated thoroughly
......@@ -301,18 +319,9 @@ function foragequality(skylark::Skylark, model::SimulationModel, pos::Tuple{Int6
(@distanceto(builtup) < skylark.mindistancetoedge)
return 0.0
end
quality = 1.0
f = farmplot(pos, model)
# Assume that grass and soil have a habitat quality of 1.0. For fields, calculate quality
# as the sum of cover quality and height quality (each modelled using a linear regression).
if !isnothing(f)
groundcoverfactor = x -> bounds((-1/skylark.maxforagecover)*x + 1.0, max=1.0)
plantheightfactor = x -> bounds((-1/skylark.maxforageheight)*(x |> cm) + 1.0, max=1.0)
#FIXME need percentage cover in FarmPlot, not LAI
#FIXME height is currently dimensionless in FarmPlot, hence the conversion to cm
quality = bounds(groundcoverfactor(f.LAItotal) + plantheightfactor(f.height*1cm), max=1.0)
end
return quality
groundcoverfactor = x -> bounds((-1/skylark.maxforagecover)*x + 1.0, max=1.0)
plantheightfactor = x -> bounds((-1/skylark.maxforageheight)*(x |> cm) + 1.0, max=1.0)
return bounds(groundcoverfactor(@cropcover()) + plantheightfactor(@cropheight()), max=1.0)
end
"""
......@@ -324,12 +333,24 @@ function allowsnesting(skylark::Skylark, model::SimulationModel, pos::Tuple{Int6
#TODO is this condition correct? -> needs validation!
(@landcover() == grass ||
(@landcover() == agriculture &&
(skylark.nestingheight[1] <= @cropheight() <= skylark.nestingheight[2]) &&
(skylark.nestingheight[1] <= (@cropheight()) <= skylark.nestingheight[2]) &&
(skylark.nestingcover[1] <= @cropcover() <= skylark.nestingcover[2]))) #&&
#FIXME if we add the distance requirement, females don't find a nesting spot?
#(@distanceto(forest) < skylark.mindistancetoedge) &&
#(@distanceto(builtup) < skylark.mindistancetoedge)
end
"""
destroynest!(skylark, reason)
Remove the skylark's nest and offspring due to disturbance or predation.
"""
function destroynest!(self::Skylark, reason::String)
self.nest = ()
self.clutch = 0
@debug("$(animalid(self)) had her nest destroyed by $reason.")
end
## INITIALISATION
"""
......@@ -345,7 +366,7 @@ should currently be on migration. Also sets other individual-specific variables.
@setphase(territorysearch) :
@setphase(matesearch)
@migrate(arrive)
self.migrationdates = self.migrationdates .+ Year(1))
self.migrationdates = self.migrationdates .+ Year(1)
end
end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment