From b32fc6223e7b348e09d3b6f7559b9b6b2d739b80 Mon Sep 17 00:00:00 2001 From: Daniel Vedder <daniel.vedder@idiv.de> Date: Wed, 7 Aug 2024 10:52:37 +0200 Subject: [PATCH] Added mortality data output --- src/core/output.jl | 10 +++++----- src/nature/ecologicaldata.jl | 1 + src/nature/individuals.jl | 1 + src/nature/macros.jl | 21 +++++++++++++++++++++ src/nature/species/skylark.jl | 29 ++++++++++++++++++----------- 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/core/output.jl b/src/core/output.jl index 555c4be..d59eb3d 100644 --- a/src/core/output.jl +++ b/src/core/output.jl @@ -194,7 +194,7 @@ function newdataoutput!(model::SimulationModel, name::String, end end end - ndo = DataOutput(frequency, [[]], df, outputfunction, plotfunction) + ndo = DataOutput(frequency, [], df, outputfunction, plotfunction) model.dataoutputs[name] = ndo end @@ -221,7 +221,7 @@ function outputdata(model::SimulationModel, force=false) (output.frequency == "yearly" && isnextyear(model.date)) || (output.frequency == "end" && model.date == @param(core.enddate)) !isnothing(output.outputfunction) && (output.databuffer = output.outputfunction(model)) - isempty(output.databuffer) && continue + (isempty(output.databuffer) || output.databuffer == [[]]) && continue if @param(core.csvoutput) open(joinpath(@param(core.outdir), o*".csv"), "a") do f for row in output.databuffer @@ -234,17 +234,17 @@ function outputdata(model::SimulationModel, force=false) push!(output.datastore, row) end end - output.databuffer = [[]] + output.databuffer = [] end end end """ - record(model, outputname, data) + record!(model, outputname, data) Append an observation vector to the given output. """ -function record(model::SimulationModel, outputname::String, data::Vector) +function record!(model::SimulationModel, outputname::String, data::Vector) push!(model.dataoutputs[outputname].databuffer, data) end diff --git a/src/nature/ecologicaldata.jl b/src/nature/ecologicaldata.jl index c350b99..8f0b7e8 100644 --- a/src/nature/ecologicaldata.jl +++ b/src/nature/ecologicaldata.jl @@ -14,6 +14,7 @@ function initecologicaldata(model::SimulationModel) @param(nature.popoutfreq), savepopulationdata, populationtrends) newdataoutput!(model, "individuals", ["Date","ID","X","Y","Species","Sex","Age"], @param(nature.indoutfreq), saveindividualdata, visualisemap) + newdataoutput!(model, "mortality", ["Date", "Species", "Cause"], @param(nature.popoutfreq)) initskylarkdata(model) end diff --git a/src/nature/individuals.jl b/src/nature/individuals.jl index 934d219..7cb9da5 100644 --- a/src/nature/individuals.jl +++ b/src/nature/individuals.jl @@ -47,6 +47,7 @@ function kill!(animal::Animal, model::SimulationModel, # print the epitaph and remove the animal from the model postfix = isempty(cause) ? "." : " from $cause." @debug "$(animalid(animal)) has died$(postfix)" + @record("mortality", [model.date, speciesof(animal), cause]) model.animals[animal.id] = nothing return true end diff --git a/src/nature/macros.jl b/src/nature/macros.jl index 14a81d3..250d688 100644 --- a/src/nature/macros.jl +++ b/src/nature/macros.jl @@ -496,6 +496,7 @@ end @thisyear(annualdate) Construct a date object referring to the current model year from an AnnualDate. +Only use in scopes where `model` is available. """ macro thisyear(annualdate) :(thisyear($(esc(annualdate)), $(esc(:model)))) @@ -505,6 +506,7 @@ end @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. """ macro nextyear(annualdate) :(nextyear($(esc(annualdate)), $(esc(:model)))) @@ -514,7 +516,26 @@ end @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. """ macro lastyear(annualdate) :(lastyear($(esc(annualdate)), $(esc(:model)))) end + +""" + @record(outputname, data) + +Record an observation / data point. Only use in scopes where `model` is available. +""" +macro record(args...) + :(record!($(esc(:model)), $(map(esc, args)...))) +end + +""" + @destroynest(reason) + +Utility wrapper for `destroynest!()` in the Skylark model. +""" +macro destroynest(reason) + :(destroynest!($(esc(:self)), $(esc(:model)), $(esc(reason)))) +end diff --git a/src/nature/species/skylark.jl b/src/nature/species/skylark.jl index cced022..547f1cd 100644 --- a/src/nature/species/skylark.jl +++ b/src/nature/species/skylark.jl @@ -229,8 +229,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, destroynest!(self, "tillage")) - @respond(harvesting, destroynest!(self, "harvesting")) + @respond(tillage, @destroynest("tillage")) + @respond(harvesting, @destroynest("harvesting")) end """ @@ -243,24 +243,27 @@ chicks are independent or in case of brood loss. self.timer += 1 #TODO this should be habitat-dependent! if self.timer <= self.eggtime - @chance(self.eggpredationmortality) && destroynest!(self, "predation") + @chance(self.eggpredationmortality) && @destroynest("predation") elseif self.timer <= self.eggtime + self.nestlingtime - @chance(self.nestlingpredationmortality) && destroynest!(self, "predation") + @chance(self.nestlingpredationmortality) && @destroynest("predation") elseif self.timer <= self.eggtime + self.nestlingtime + self.fledglingtime - @chance(self.fledglingpredationmortality) && destroynest!(self, "predation") + @chance(self.fledglingpredationmortality) && @destroynest("predation") else # create new young, reset timer and clutch counter for o in 1:self.clutch #XXX is this the best way of doing first-year mortality? - @chance(self.firstyearmortality) ? - @debug("A skylark has died from first year mortality") : + if @chance(self.firstyearmortality) + @debug("A skylark has died from first year mortality") + @record("mortality", [model.date, "Skylark", "firstyearmortality"]) + else @reproduce(1, self.mate) + end end self.clutch = 0 end if self.clutch > 0 # tillage and harvest destroys the nest - @respond(tillage, destroynest!(self, "tillage")) - @respond(harvesting, destroynest!(self, "harvesting")) + @respond(tillage, @destroynest("tillage")) + @respond(harvesting, @destroynest("harvesting")) else # restart breeding cycle if there is time self.timer = 0 self.nest = () @@ -358,16 +361,20 @@ function allowsnesting(skylark::Skylark, model::SimulationModel, pos::Tuple{Int6 end """ - destroynest!(skylark, reason) + destroynest!(skylark, model, reason) Remove the skylark's nest and offspring due to disturbance or predation. """ -function destroynest!(self::Skylark, reason::String) +function destroynest!(self::Skylark, model::SimulationModel, reason::String) + for c in self.clutch + @record("mortality", [model.date, "Skylark", reason]) + end self.nest = () self.clutch = 0 @debug("$(animalid(self)) had her nest destroyed by $reason.") end + ## INITIALISATION """ -- GitLab