From 1e52e2591dea158ad441d94e9b9e3252f6da0633 Mon Sep 17 00:00:00 2001 From: Marco Matthies <71844+marcom@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:50:49 +0200 Subject: [PATCH] Rework functions for FarmPlot and crop models - `stepagent!` for FarmPlot that delegates to crop state stepagent! function - `harvest!`, `sow!` functions work on a `FarmPlot`, and delegate to the respective methods acting on a `CropState` --- src/crop/almass.jl | 36 ++++++++++++++++-------------------- src/crop/farmplot.jl | 30 ++++++++++++++++++++++++++++++ src/crop/simplecrop.jl | 34 ++++++++++++++++++++++++++++++---- src/farm/farm.jl | 3 ++- test/crop_tests.jl | 2 +- 5 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/crop/almass.jl b/src/crop/almass.jl index a306348..9203323 100644 --- a/src/crop/almass.jl +++ b/src/crop/almass.jl @@ -9,7 +9,6 @@ module ALMaSS using Persefone: Management, - FarmPlot, Length, cm, SimulationModel, @@ -74,8 +73,8 @@ cropname(ct::CropType) = ct.name """ CropState -The state data for an ALMaSS vegetation point calculation as used in -FarmPlot. +The state data for an ALMaSS vegetation point calculation. Usually +part of a `FarmPlot`. """ mutable struct CropState #TODO add Unitful @@ -168,15 +167,14 @@ function readcropparameters(generalcropfile::String, growthfile::String) end """ - stepagent!(farmplot, model) + stepagent!(cropstate, model) Update a farm plot by one day. """ -function stepagent!(farmplot::FarmPlot{CropState}, model::SimulationModel) +function stepagent!(cs::CropState, model::SimulationModel) # update growing degree days # if no crop-specific base temperature is given, default to 5°C # (https://www.eea.europa.eu/publications/europes-changing-climate-hazards-1/heat-and-cold/heat-and-cold-2014-mean) - cs = farmplot.crop_state basetemp = cs.croptype.mingrowthtemp ismissing(basetemp) && (basetemp = 5.0) gdd = (maxtemp(model)+mintemp(model))/2 - basetemp @@ -185,19 +183,18 @@ function stepagent!(farmplot::FarmPlot{CropState}, model::SimulationModel) monthday(model.date) == (1,1) && (cs.phase = ALMaSS.janfirst) monthday(model.date) == (3,1) && (cs.phase = ALMaSS.marchfirst) # update crop growth - growcrop!(farmplot, model) + growcrop!(cs, model) end ## CROP MANAGEMENT AND GROWTH FUNCTIONS """ - sow!(cropname, farmplot, model) + sow!(cropstate, model, cropname) -Sow the specified crop on this farmplot. +Change the cropstate to sow the specified crop. """ -function sow!(cropname::String, farmplot::FarmPlot{CropState}, model::SimulationModel) - cs = farmplot.crop_state +function sow!(cs::CropState, model::SimulationModel, cropname::String) createevent!(model, farmplot.pixels, sowing) cs.croptype = model.crops[cropname] cs.phase = ALMaSS.sow @@ -205,12 +202,11 @@ function sow!(cropname::String, farmplot::FarmPlot{CropState}, model::Simulation end """ - harvest!(farmplot, model) + harvest!(cropstate, model) -Harvest the crop on this farmplot. +Harvest the crop of this cropstate. """ -function harvest!(farmplot::FarmPlot{CropState}, model::SimulationModel) - cs = farmplot.crop_state +function harvest!(cs::CropState, model::SimulationModel) createevent!(model, farmplot.pixels, harvesting) cs.phase in [ALMaSS.harvest1, ALMaSS.harvest2] ? cs.phase = ALMaSS.harvest2 : @@ -224,13 +220,13 @@ end #TODO till!() """ - growcrop!(farmplot, model) + growcrop!(cropstate, model) -Apply the relevant crop growth model to update the plants on this farm plot. -Currently only supports the ALMaSS crop growth model by Topping et al. +Apply the relevant crop growth model to update the plants crop state +on this farm plot. Implements the ALMaSS crop growth model by Topping +et al. """ -function growcrop!(farmplot::FarmPlot{CropState}, model::SimulationModel) - cs = farmplot.crop_state +function growcrop!(cs::CropState, model::SimulationModel) fertiliser in cs.events ? curve = cs.croptype.lownutrientgrowth : curve = cs.croptype.highnutrientgrowth diff --git a/src/crop/farmplot.jl b/src/crop/farmplot.jl index 018ec54..b1c479a 100644 --- a/src/crop/farmplot.jl +++ b/src/crop/farmplot.jl @@ -15,6 +15,36 @@ cropheight(f::FarmPlot{T}) where {T} = cropheight(f.crop_state) cropcover(f::FarmPlot{T}) where {T} = cropcover(f.crop_state) cropyield(f::FarmPlot{T}) where {T} = cropyield(f.crop_state) +""" + stepagent!(farmplot, model) + +Update a farm plot by one day. +""" +function stepagent!(farmplot::FarmPlot{T}, model::SimulationModel) where T + stepagent!(farmplot.crop_state, model) +end + +""" + sow!(farmplot, model, cropname) + +Sow the specified crop on the farmplot. +""" +function sow!(farmplot::FarmPlot, model::SimulationModel, cropname::String) + createevent!(model, farmplot.pixels, sowing) + sow!(farmplot.crop_state, model, cropname) + #XXX test if the crop is sowable? +end + +""" + harvest!(farmplot, model) + +Harvest the crop of this farmplot. +""" +function harvest!(farmplot::FarmPlot{T}, model::SimulationModel) where T + createevent!(model, farmplot.pixels, harvesting) + harvest!(farmplot.crop_state, model) # TODO: multiply with area to return units of `g` +end + ## UTILITY FUNCTIONS """ diff --git a/src/crop/simplecrop.jl b/src/crop/simplecrop.jl index dead41f..6bfe858 100644 --- a/src/crop/simplecrop.jl +++ b/src/crop/simplecrop.jl @@ -3,7 +3,7 @@ module SimpleCrop using Persefone: FarmPlot, Length, - m, + cm, SimulationModel import Persefone: @@ -35,10 +35,36 @@ cropyield(cs::CropState) = 0.0 # TODO: units? """ stepagent!(farmplot, model) -Update a farm plot by one day. +Update a crop state by one day. """ -function stepagent!(farmplot::FarmPlot{CropState}, model::SimulationModel) - # TODO: do something simple +function stepagent!(cs::CropState, model::SimulationModel) + # height undergoes random diffusion, bounded by 0 + delta_height = (2 * rand() - 1) * cm + cs.height = max(0.0cm, cs.height + delta_height) +end + +""" + sow!(cropstate, model, cropname) + +Change the cropstate to sow the specified crop. +""" +function sow!(cs::CropState, model::SimulationModel, cropname::String) + cs.croptype = model.crops[cropname] + cs.height = 0cm +end + +""" + harvest!(cropstate, model) + +Harvest the crop of this cropstate. +""" +function harvest!(cs::CropState, model::SimulationModel) + # TODO: set cs.croptype ? + # TODO: this 1.0g/cm/m^2 height_to_yield factor should be a param + # for every crop type + yield = cs.height * 1.0g/cm/m^2 + cs.height = 0cm + return yield end end # module SimpleCrop diff --git a/src/farm/farm.jl b/src/farm/farm.jl index e9e70c3..6d7b158 100644 --- a/src/farm/farm.jl +++ b/src/farm/farm.jl @@ -12,7 +12,8 @@ mutable struct Farmer <: ModelAgent #XXX make this into an abstract type and create subtypes for different # farm submodels? (#69) const id::Int64 - fields::Vector{ALMaSS.FarmPlot} + # TODO: hardcoded ALMaSS crop model + fields::Vector{FarmPlot{ALMaSS.CropState}} croprotation::Vector{ALMaSS.CropType} #TODO add AES end diff --git a/test/crop_tests.jl b/test/crop_tests.jl index 1cdbe4f..9146925 100644 --- a/test/crop_tests.jl +++ b/test/crop_tests.jl @@ -34,7 +34,7 @@ end @testset "Submodule SimpleCrop" begin ct = Ps.SimpleCrop.CropType("olive tree") - fp = FarmPlot(0, [(0,0)], Ps.SimpleCrop.CropState(ct, 0.0m)) + fp = FarmPlot(0, [(0,0)], Ps.SimpleCrop.CropState(ct, 0.0cm)) @test fp isa FarmPlot @test fp isa FarmPlot{Ps.SimpleCrop.CropState} @test croptype(fp) isa Ps.SimpleCrop.CropType -- GitLab