Skip to content
Snippets Groups Projects
Commit 14f5fa19 authored by Marco Matthies's avatar Marco Matthies
Browse files

Prepare for allowing multiple crop models in one simulation

- each crop model must implement types `CropType <: AbstractCropType`
  and `CropState <: AbstractCropState`

- `AgricultureModel` and `FarmPlot` are not parametrised structs
  anymore to allow for different crop models active in one simulation
parent 71d9e132
No related branches found
No related tags found
No related merge requests found
...@@ -132,6 +132,22 @@ The supertype of all agents in the model (animal species, farmer types, farmplot ...@@ -132,6 +132,22 @@ The supertype of all agents in the model (animal species, farmer types, farmplot
""" """
abstract type ModelAgent end abstract type ModelAgent end
"""
AbstractCropType
The abstract supertype of all crop types in the model. Each crop
model has to define a type `CropType <: AbstractCropType`.
"""
abstract type AbstractCropType end
"""
AbstractCropState
The abstract supertype of all crop states in the model. Each crop
model has to define a type `CropState <: AbstractCropState`.
"""
abstract type AbstractCropState end
function stepagent! end function stepagent! end
## include all module files (note that the order matters - if file ## include all module files (note that the order matters - if file
......
...@@ -12,7 +12,7 @@ This is the heart of the model - a struct that holds all data and state ...@@ -12,7 +12,7 @@ This is the heart of the model - a struct that holds all data and state
for one simulation run. It is created by [`initialise`](@ref) and passed for one simulation run. It is created by [`initialise`](@ref) and passed
as input to most model functions. as input to most model functions.
""" """
@kwdef mutable struct AgricultureModel{Tcroptype, Tcropstate} <: SimulationModel @kwdef mutable struct AgricultureModel <: SimulationModel
settings = Dict{String, Any}() settings = Dict{String, Any}()
rng::AbstractRNG rng::AbstractRNG
logger::AbstractLogger logger::AbstractLogger
...@@ -20,9 +20,9 @@ as input to most model functions. ...@@ -20,9 +20,9 @@ as input to most model functions.
date::Date date::Date
landscape = Matrix{Pixel}() landscape = Matrix{Pixel}()
weather::Weather weather::Weather
crops = Dict{String, Tcroptype}() crops = Dict{String, AbstractCropType}()
farmers = Vector{Farmer}() farmers = Vector{Farmer}()
farmplots = Vector{FarmPlot{Tcropstate}}() farmplots = Vector{FarmPlot}()
animals = Vector{Union{Animal, Nothing}}() animals = Vector{Union{Animal, Nothing}}()
migrants = Vector{Pair{Animal, AnnualDate}}() migrants = Vector{Pair{Animal, AnnualDate}}()
events = Vector{FarmEvent}() events = Vector{FarmEvent}()
...@@ -140,9 +140,9 @@ function initmodel(settings::Dict{String, Any}) ...@@ -140,9 +140,9 @@ function initmodel(settings::Dict{String, Any})
settings["world.weatherfile"]), settings["world.weatherfile"]),
settings["core.startdate"], settings["core.startdate"],
settings["core.enddate"]) settings["core.enddate"])
crops, Tcroptype, Tcropstate = initcropmodel(settings["crop.cropmodel"], crops = initcropmodel(settings["crop.cropmodel"],
settings["crop.cropdirectory"]) settings["crop.cropdirectory"])
model = AgricultureModel{Tcroptype, Tcropstate}(; model = AgricultureModel(;
settings, settings,
rng = StableRNG(settings["core.seed"]), rng = StableRNG(settings["core.seed"]),
logger, logger,
......
...@@ -43,6 +43,8 @@ const GROWTHFILE = "crop_growth_curves.csv" ...@@ -43,6 +43,8 @@ const GROWTHFILE = "crop_growth_curves.csv"
using Persefone: using Persefone:
@rand, @rand,
@u_str, @u_str,
AbstractCropState,
AbstractCropType,
AnnualDate, AnnualDate,
Management, Management,
cm, cm,
...@@ -116,7 +118,7 @@ end ...@@ -116,7 +118,7 @@ end
The type struct for all crops. Currently follows the crop growth model as The type struct for all crops. Currently follows the crop growth model as
implemented in ALMaSS. implemented in ALMaSS.
""" """
struct CropType struct CropType <: AbstractCropType
#FIXME this needs thinking about. The sowing and harvest dates belong in the farm model, #FIXME this needs thinking about. The sowing and harvest dates belong in the farm model,
# not here. Also, we need to harmonise crops across the crop growth models. # not here. Also, we need to harmonise crops across the crop growth models.
name::String name::String
...@@ -155,7 +157,7 @@ cropname(ct::CropType) = ct.name ...@@ -155,7 +157,7 @@ cropname(ct::CropType) = ct.name
The state data for an ALMaSS vegetation point calculation. Usually The state data for an ALMaSS vegetation point calculation. Usually
part of a `FarmPlot`. part of a `FarmPlot`.
""" """
@kwdef mutable struct CropState @kwdef mutable struct CropState <: AbstractCropState
# Class in original ALMaSS code: # Class in original ALMaSS code:
# `VegElement` from `Landscape/Elements.h`, line 601 # `VegElement` from `Landscape/Elements.h`, line 601
croptype::CropType croptype::CropType
......
...@@ -10,6 +10,8 @@ using Dates: Date ...@@ -10,6 +10,8 @@ using Dates: Date
using DataFrames: DataFrame using DataFrames: DataFrame
using Persefone: using Persefone:
AbstractCropState,
AbstractCropType,
AnnualDate, AnnualDate,
cm, cm,
daynumber, daynumber,
...@@ -68,7 +70,7 @@ const AQUACROP_CROPNAMES = [ ...@@ -68,7 +70,7 @@ const AQUACROP_CROPNAMES = [
"wheatGDD", "wheatGDD",
] ]
@kwdef struct CropType @kwdef struct CropType <: AbstractCropType
name::String name::String
aquacrop_name::String aquacrop_name::String
group::String group::String
...@@ -101,7 +103,7 @@ function readcropparameters(cropdirectory::String) ...@@ -101,7 +103,7 @@ function readcropparameters(cropdirectory::String)
return croptypes return croptypes
end end
mutable struct CropState mutable struct CropState <: AbstractCropState
croptype::CropType croptype::CropType
height::Tlength # TODO: remove height field, supply from cropstate height::Tlength # TODO: remove height field, supply from cropstate
soiltype::SoilType soiltype::SoilType
......
...@@ -13,23 +13,17 @@ simulation, as well as the types `Tcroptype` and `Tcropstate`. ...@@ -13,23 +13,17 @@ simulation, as well as the types `Tcroptype` and `Tcropstate`.
""" """
function initcropmodel(cropmodel::AbstractString, cropdirectory::AbstractString) function initcropmodel(cropmodel::AbstractString, cropdirectory::AbstractString)
if cropmodel == "almass" if cropmodel == "almass"
Tcroptype = ALMaSS.CropType
Tcropstate = ALMaSS.CropState
crops = ALMaSS.readcropparameters(cropdirectory) crops = ALMaSS.readcropparameters(cropdirectory)
elseif cropmodel == "simple" elseif cropmodel == "simple"
Tcroptype = SimpleCrop.CropType
Tcropstate = SimpleCrop.CropState
crops_almass = ALMaSS.readcropparameters(cropdirectory) crops_almass = ALMaSS.readcropparameters(cropdirectory)
crops = Dict(name => SimpleCrop.CropType(ct.name, ct.group, ct.minsowdate, ct.maxsowdate) crops = Dict(name => SimpleCrop.CropType(ct.name, ct.group, ct.minsowdate, ct.maxsowdate)
for (name, ct) in crops_almass) for (name, ct) in crops_almass)
elseif cropmodel == "aquacrop" elseif cropmodel == "aquacrop"
Tcroptype = AquaCropWrapper.CropType
Tcropstate = AquaCropWrapper.CropState
crops = AquaCropWrapper.readcropparameters(cropdirectory) crops = AquaCropWrapper.readcropparameters(cropdirectory)
else else
error("initcropmodel: no implementation for crop model '$cropmodel'") error("initcropmodel: no implementation for crop model '$cropmodel'")
end end
return crops, Tcroptype, Tcropstate return crops
end end
""" """
......
...@@ -8,27 +8,27 @@ ...@@ -8,27 +8,27 @@
A struct representing a single field, on which a crop can be grown. A struct representing a single field, on which a crop can be grown.
""" """
mutable struct FarmPlot{T} <: ModelAgent mutable struct FarmPlot <: ModelAgent
const id::Int64 const id::Int64
pixels::Vector{Tuple{Int64, Int64}} pixels::Vector{Tuple{Int64, Int64}}
farmer::Int64 farmer::Int64
soiltype::SoilType soiltype::SoilType
cropstate::T cropstate::AbstractCropState
end end
croptype(f::FarmPlot{T}) where {T} = croptype(f.cropstate) croptype(f::FarmPlot) = croptype(f.cropstate)
cropname(f::FarmPlot{T}) where {T} = cropname(croptype(f)) cropname(f::FarmPlot) = cropname(croptype(f))
cropheight(f::FarmPlot{T}) where {T} = cropheight(f.cropstate) cropheight(f::FarmPlot) = cropheight(f.cropstate)
cropcover(f::FarmPlot{T}) where {T} = cropcover(f.cropstate) cropcover(f::FarmPlot) = cropcover(f.cropstate)
cropyield(f::FarmPlot{T}) where {T} = cropyield(f.cropstate) cropyield(f::FarmPlot) = cropyield(f.cropstate)
isharvestable(f::FarmPlot{T}) where {T} = isharvestable(f.cropstate) isharvestable(f::FarmPlot) = isharvestable(f.cropstate)
""" """
stepagent!(farmplot, model) stepagent!(farmplot, model)
Update a farm plot by one day. Update a farm plot by one day.
""" """
function stepagent!(farmplot::FarmPlot{T}, model::SimulationModel) where T function stepagent!(farmplot::FarmPlot, model::SimulationModel)
stepagent!(farmplot.cropstate, model) stepagent!(farmplot.cropstate, model)
end end
...@@ -48,7 +48,7 @@ end ...@@ -48,7 +48,7 @@ end
Harvest the crop of this farmplot. Harvest the crop of this farmplot.
""" """
function harvest!(farmplot::FarmPlot{T}, model::SimulationModel) where T function harvest!(farmplot::FarmPlot, model::SimulationModel)
createevent!(model, farmplot.pixels, harvesting) createevent!(model, farmplot.pixels, harvesting)
harvest!(farmplot.cropstate, model) # TODO: multiply with area to return units of `g` harvest!(farmplot.cropstate, model) # TODO: multiply with area to return units of `g`
@debug "Farmer $(farmplot.farmer) harvested $(cropname(farmplot)) from farmplot $(farmplot.id)." @debug "Farmer $(farmplot.farmer) harvested $(cropname(farmplot)) from farmplot $(farmplot.id)."
......
module SimpleCrop module SimpleCrop
using Persefone: using Persefone:
AbstractCropState,
AbstractCropType,
AnnualDate, AnnualDate,
FarmPlot, FarmPlot,
Length, Length,
...@@ -21,7 +23,7 @@ import Persefone: ...@@ -21,7 +23,7 @@ import Persefone:
using Unitful: @u_str using Unitful: @u_str
# TODO: alternatively just use ALMaSS.CropType ? # TODO: alternatively just use ALMaSS.CropType ?
struct CropType struct CropType <: AbstractCropType
name::String name::String
group::String group::String
minsowdate::Union{Missing,AnnualDate} minsowdate::Union{Missing,AnnualDate}
...@@ -30,7 +32,7 @@ end ...@@ -30,7 +32,7 @@ end
cropname(ct::CropType) = ct.name cropname(ct::CropType) = ct.name
mutable struct CropState mutable struct CropState <: AbstractCropState
croptype::CropType croptype::CropType
height::Length{Float64} height::Length{Float64}
end end
......
...@@ -33,7 +33,7 @@ end ...@@ -33,7 +33,7 @@ end
force_growth = false force_growth = false
fp = FarmPlot(id, pixels, farmer, Ps.nosoildata, Ps.ALMaSS.CropState(croptype=ct, phase=Ps.ALMaSS.janfirst)) fp = FarmPlot(id, pixels, farmer, Ps.nosoildata, Ps.ALMaSS.CropState(croptype=ct, phase=Ps.ALMaSS.janfirst))
@test fp isa FarmPlot @test fp isa FarmPlot
@test fp isa FarmPlot{Ps.ALMaSS.CropState} @test fp.cropstate isa Ps.ALMaSS.CropState
@test croptype(fp) isa Ps.ALMaSS.CropType @test croptype(fp) isa Ps.ALMaSS.CropType
@test cropname(fp) isa String @test cropname(fp) isa String
@test cropheight(fp) isa Length{Float64} @test cropheight(fp) isa Length{Float64}
...@@ -48,7 +48,7 @@ end ...@@ -48,7 +48,7 @@ end
farmer = 0 farmer = 0
fp = FarmPlot(id, pixels, farmer, Ps.nosoildata, Ps.SimpleCrop.CropState(ct, 0.0cm)) fp = FarmPlot(id, pixels, farmer, Ps.nosoildata, Ps.SimpleCrop.CropState(ct, 0.0cm))
@test fp isa FarmPlot @test fp isa FarmPlot
@test fp isa FarmPlot{Ps.SimpleCrop.CropState} @test fp.cropstate isa Ps.SimpleCrop.CropState
@test croptype(fp) isa Ps.SimpleCrop.CropType @test croptype(fp) isa Ps.SimpleCrop.CropType
@test cropname(fp) isa String @test cropname(fp) isa String
@test cropheight(fp) isa Length{Float64} @test cropheight(fp) isa Length{Float64}
...@@ -67,7 +67,7 @@ end ...@@ -67,7 +67,7 @@ end
soiltype = Ps.sand soiltype = Ps.sand
fp = FarmPlot(id, pixels, farmer, soiltype, PsAC.CropState(ct, soiltype, model)) fp = FarmPlot(id, pixels, farmer, soiltype, PsAC.CropState(ct, soiltype, model))
@test fp isa FarmPlot @test fp isa FarmPlot
@test fp isa FarmPlot{PsAC.CropState} @test fp.cropstate isa PsAC.CropState
@test croptype(fp) isa PsAC.CropType @test croptype(fp) isa PsAC.CropType
@test cropname(fp) isa String @test cropname(fp) isa String
@test cropheight(fp) isa Length{Float64} @test cropheight(fp) isa Length{Float64}
......
...@@ -46,7 +46,7 @@ function inittestmodel(smallmap=true) ...@@ -46,7 +46,7 @@ function inittestmodel(smallmap=true)
TESTSETTINGS["core.enddate"]) TESTSETTINGS["core.enddate"])
# TODO: support other crop models besides ALMaSS # TODO: support other crop models besides ALMaSS
crops = Ps.ALMaSS.readcropparameters(TESTSETTINGS["crop.cropdirectory"]) crops = Ps.ALMaSS.readcropparameters(TESTSETTINGS["crop.cropdirectory"])
model = AgricultureModel{Ps.ALMaSS.CropType,Ps.ALMaSS.CropState}(; model = AgricultureModel(;
settings = copy(TESTSETTINGS), settings = copy(TESTSETTINGS),
rng = StableRNG(TESTSETTINGS["core.seed"]), rng = StableRNG(TESTSETTINGS["core.seed"]),
logger = global_logger(), logger = global_logger(),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment