### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe. ### ### This file is responsible for managing the crop growth modules. ### #TODO write tests for input functions """ GrowthPhase ALMaSS crop growth curves are split into five phases, triggered by seasonal dates or agricultural events. """ @enum GrowthPhase janfirst sow marchfirst harvest1 harvest2 """ CropCurveParams The values in this struct define one crop growth curve. """ struct CropCurveParams #TODO add Unitful curveID::Int highnutrients::Bool GDD::Dict{GrowthPhase,Vector{Float64}} LAItotal::Dict{GrowthPhase,Vector{Float64}} LAIgreen::Dict{GrowthPhase,Vector{Float64}} height::Dict{GrowthPhase,Vector{Float64}} end """ CropType The type struct for all crops. Currently follows the crop growth model as implemented in ALMaSS. """ struct CropType #TODO make this into an abstract type and create subtypes for different # crop submodels (#70) name::String minsowdate::Union{Missing,Date} maxsowdate::Union{Missing,Date} minharvestdate::Union{Missing,Date} maxharvestdate::Union{Missing,Date} mingrowthtemp::Union{Missing,Float64} highnutrientgrowth::Union{Missing,CropCurveParams} lownutrientgrowth::Union{Missing,CropCurveParams} #issowable::Union{Function,Bool} end """ Base.tryparse(type, str) Extend `tryparse` to allow parsing GrowthPhase values. (Needed to read in the CSV parameter file.) """ function Base.tryparse(type::Type{GrowthPhase}, str::String) str == "janfirst" ? janfirst : str == "sow" ? sow : str == "marchfirst" ? marchfirst : str == "harvest1" ? harvest1 : str == "harvest2" ? harvest2 : nothing end """ buildgrowthcurve(data) Convert a list of rows from the crop growth data into a CropCurveParams object. """ function buildgrowthcurve(data::Vector{CSV.Row}) isempty(data) && return missing GDD = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(), marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(), harvest2=>Vector{Float64}()) LAItotal = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(), marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(), harvest2=>Vector{Float64}()) LAIgreen = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(), marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(), harvest2=>Vector{Float64}()) height = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(), marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(), harvest2=>Vector{Float64}()) for e in data append!(GDD[e.growth_phase], e.GDD) append!(LAItotal[e.growth_phase], e.LAI_total) append!(LAIgreen[e.growth_phase], e.LAI_green) append!(height[e.growth_phase], e.height) end CropCurveParams(data[1].curve_id, data[1].nutrient_status=="high", GDD, LAItotal, LAIgreen, height) end """ readcropparameters(generalcropfile, cropgrowthfile) Parse a CSV file containing the required parameter values for each crop (as produced from the original ALMaSS file by `convert_almass_data.py`). """ function readcropparameters(generalcropfile::String, growthfile::String) @debug "Reading crop parameters" cropdata = CSV.File(generalcropfile, missingstring="NA", dateformat="d U", types=[String,Date,Date,Date,Date,Float64]) growthdata = CSV.File(growthfile, missingstring="NA", types=[Int,String,String,GrowthPhase,String, Float64,Float64,Float64,Float64]) croptypes = Dict{String,CropType}() for crop in cropdata cropgrowthdata = growthdata |> filter(x -> !ismissing(x.crop_name) && x.crop_name == crop.name) highnuts = buildgrowthcurve(cropgrowthdata |> filter(x -> x.nutrient_status=="high")) lownuts = buildgrowthcurve(cropgrowthdata |> filter(x -> x.nutrient_status=="low")) croptypes[crop.name] = CropType(crop.name, crop.minsowdate, crop.maxsowdate, crop.minharvestdate, crop.maxharvestdate, crop.mingrowthtemp, highnuts, lownuts) end croptypes end