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
"""
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
## 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
for one simulation run. It is created by [`initialise`](@ref) and passed
as input to most model functions.
"""
@kwdef mutable struct AgricultureModel{Tcroptype, Tcropstate} <: SimulationModel
@kwdef mutable struct AgricultureModel <: SimulationModel
settings = Dict{String, Any}()
rng::AbstractRNG
logger::AbstractLogger
......@@ -20,9 +20,9 @@ as input to most model functions.
date::Date
landscape = Matrix{Pixel}()
weather::Weather
crops = Dict{String, Tcroptype}()
crops = Dict{String, AbstractCropType}()
farmers = Vector{Farmer}()
farmplots = Vector{FarmPlot{Tcropstate}}()
farmplots = Vector{FarmPlot}()
animals = Vector{Union{Animal, Nothing}}()
migrants = Vector{Pair{Animal, AnnualDate}}()
events = Vector{FarmEvent}()
......@@ -140,9 +140,9 @@ function initmodel(settings::Dict{String, Any})
settings["world.weatherfile"]),
settings["core.startdate"],
settings["core.enddate"])
crops, Tcroptype, Tcropstate = initcropmodel(settings["crop.cropmodel"],
settings["crop.cropdirectory"])
model = AgricultureModel{Tcroptype, Tcropstate}(;
crops = initcropmodel(settings["crop.cropmodel"],
settings["crop.cropdirectory"])
model = AgricultureModel(;
settings,
rng = StableRNG(settings["core.seed"]),
logger,
......
......@@ -43,6 +43,8 @@ const GROWTHFILE = "crop_growth_curves.csv"
using Persefone:
@rand,
@u_str,
AbstractCropState,
AbstractCropType,
AnnualDate,
Management,
cm,
......@@ -116,7 +118,7 @@ end
The type struct for all crops. Currently follows the crop growth model as
implemented in ALMaSS.
"""
struct CropType
struct CropType <: AbstractCropType
#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.
name::String
......@@ -155,7 +157,7 @@ cropname(ct::CropType) = ct.name
The state data for an ALMaSS vegetation point calculation. Usually
part of a `FarmPlot`.
"""
@kwdef mutable struct CropState
@kwdef mutable struct CropState <: AbstractCropState
# Class in original ALMaSS code:
# `VegElement` from `Landscape/Elements.h`, line 601
croptype::CropType
......
......@@ -10,6 +10,8 @@ using Dates: Date
using DataFrames: DataFrame
using Persefone:
AbstractCropState,
AbstractCropType,
AnnualDate,
cm,
daynumber,
......@@ -68,7 +70,7 @@ const AQUACROP_CROPNAMES = [
"wheatGDD",
]
@kwdef struct CropType
@kwdef struct CropType <: AbstractCropType
name::String
aquacrop_name::String
group::String
......@@ -101,7 +103,7 @@ function readcropparameters(cropdirectory::String)
return croptypes
end
mutable struct CropState
mutable struct CropState <: AbstractCropState
croptype::CropType
height::Tlength # TODO: remove height field, supply from cropstate
soiltype::SoilType
......
......@@ -13,23 +13,17 @@ simulation, as well as the types `Tcroptype` and `Tcropstate`.
"""
function initcropmodel(cropmodel::AbstractString, cropdirectory::AbstractString)
if cropmodel == "almass"
Tcroptype = ALMaSS.CropType
Tcropstate = ALMaSS.CropState
crops = ALMaSS.readcropparameters(cropdirectory)
elseif cropmodel == "simple"
Tcroptype = SimpleCrop.CropType
Tcropstate = SimpleCrop.CropState
crops_almass = ALMaSS.readcropparameters(cropdirectory)
crops = Dict(name => SimpleCrop.CropType(ct.name, ct.group, ct.minsowdate, ct.maxsowdate)
for (name, ct) in crops_almass)
elseif cropmodel == "aquacrop"
Tcroptype = AquaCropWrapper.CropType
Tcropstate = AquaCropWrapper.CropState
crops = AquaCropWrapper.readcropparameters(cropdirectory)
else
error("initcropmodel: no implementation for crop model '$cropmodel'")
end
return crops, Tcroptype, Tcropstate
return crops
end
"""
......
......@@ -8,27 +8,27 @@
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
pixels::Vector{Tuple{Int64, Int64}}
farmer::Int64
soiltype::SoilType
cropstate::T
cropstate::AbstractCropState
end
croptype(f::FarmPlot{T}) where {T} = croptype(f.cropstate)
cropname(f::FarmPlot{T}) where {T} = cropname(croptype(f))
cropheight(f::FarmPlot{T}) where {T} = cropheight(f.cropstate)
cropcover(f::FarmPlot{T}) where {T} = cropcover(f.cropstate)
cropyield(f::FarmPlot{T}) where {T} = cropyield(f.cropstate)
isharvestable(f::FarmPlot{T}) where {T} = isharvestable(f.cropstate)
croptype(f::FarmPlot) = croptype(f.cropstate)
cropname(f::FarmPlot) = cropname(croptype(f))
cropheight(f::FarmPlot) = cropheight(f.cropstate)
cropcover(f::FarmPlot) = cropcover(f.cropstate)
cropyield(f::FarmPlot) = cropyield(f.cropstate)
isharvestable(f::FarmPlot) = isharvestable(f.cropstate)
"""
stepagent!(farmplot, model)
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)
end
......@@ -48,7 +48,7 @@ end
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)
harvest!(farmplot.cropstate, model) # TODO: multiply with area to return units of `g`
@debug "Farmer $(farmplot.farmer) harvested $(cropname(farmplot)) from farmplot $(farmplot.id)."
......
module SimpleCrop
using Persefone:
AbstractCropState,
AbstractCropType,
AnnualDate,
FarmPlot,
Length,
......@@ -21,7 +23,7 @@ import Persefone:
using Unitful: @u_str
# TODO: alternatively just use ALMaSS.CropType ?
struct CropType
struct CropType <: AbstractCropType
name::String
group::String
minsowdate::Union{Missing,AnnualDate}
......@@ -30,7 +32,7 @@ end
cropname(ct::CropType) = ct.name
mutable struct CropState
mutable struct CropState <: AbstractCropState
croptype::CropType
height::Length{Float64}
end
......
......@@ -33,7 +33,7 @@ end
force_growth = false
fp = FarmPlot(id, pixels, farmer, Ps.nosoildata, Ps.ALMaSS.CropState(croptype=ct, phase=Ps.ALMaSS.janfirst))
@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 cropname(fp) isa String
@test cropheight(fp) isa Length{Float64}
......@@ -48,7 +48,7 @@ end
farmer = 0
fp = FarmPlot(id, pixels, farmer, Ps.nosoildata, Ps.SimpleCrop.CropState(ct, 0.0cm))
@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 cropname(fp) isa String
@test cropheight(fp) isa Length{Float64}
......@@ -67,7 +67,7 @@ end
soiltype = Ps.sand
fp = FarmPlot(id, pixels, farmer, soiltype, PsAC.CropState(ct, soiltype, model))
@test fp isa FarmPlot
@test fp isa FarmPlot{PsAC.CropState}
@test fp.cropstate isa PsAC.CropState
@test croptype(fp) isa PsAC.CropType
@test cropname(fp) isa String
@test cropheight(fp) isa Length{Float64}
......
......@@ -46,7 +46,7 @@ function inittestmodel(smallmap=true)
TESTSETTINGS["core.enddate"])
# TODO: support other crop models besides ALMaSS
crops = Ps.ALMaSS.readcropparameters(TESTSETTINGS["crop.cropdirectory"])
model = AgricultureModel{Ps.ALMaSS.CropType,Ps.ALMaSS.CropState}(;
model = AgricultureModel(;
settings = copy(TESTSETTINGS),
rng = StableRNG(TESTSETTINGS["core.seed"]),
logger = global_logger(),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment