diff --git a/src/crop/crops.jl b/src/crop/crops.jl index 036cb1f5d20e2cef5f814c5c4864a6320ff84fd4..7f51beba10857a2f14ebe1563018d6f87fee3aff 100644 --- a/src/crop/crops.jl +++ b/src/crop/crops.jl @@ -3,85 +3,117 @@ ### This file is responsible for managing the crop growth modules. ### -@enum GrowthPhase janfirst sow marchfirst harvest1 harvest2 vegphase +#TODO write tests for input functions +#TODO write actual growth function """ - CropType + GrowthPhase -The type struct for all crops. Currently follows the crop -growth model as implemented in ALMaSS. +ALMaSS crop growth curves are split into five phases, triggered by +seasonal dates or agricultural events. """ -@kwdef struct CropType - name::String - sowdate::Date - harvestdate::Date - mingrowthtemp::Float64 - inflectionpoints::Dict{GrowthPhase,Vector{Float64}} - slopes::Dict{GrowthPhase,Vector{Float64}} - #issowable::Union{Function,Bool}=true +@enum GrowthPhase janfirst sow marchfirst harvest1 harvest2 + +""" + CropCurveParams + +The values in this struct define one crop growth curve. +""" +struct CropCurveParams + 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 -let croptypes = Dict{String,CropType}() - global function registercrop(ct::CropType) - croptypes[ct.name] = ct - end +""" + CropType - global function getcrop(name::String) - croptypes[name] - end +The type struct for all crops. Currently follows the crop growth model as +implemented in ALMaSS. +""" +struct CropType + 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 """ - @dday(datestring) + Base.tryparse(type, str) -A utility macro to make it quicker to create dates in the -format "21 August". +Extend `tryparse` to allow parsing GrowthPhase values. +(Needed to read in the CSV parameter file.) """ -macro dday(date::String) - :(Date($(date), dateformat"d U")) +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 -let croplist = Dict{String,CropType}() - - global function newcrop(;kwargs...) - crop = CropType(;kwargs...) - croplist[crop.name] = crop - end +""" + buildgrowthcurve(data) - global function cropdefinition(name) - croplist[name] +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 -# -# XXX It would be nicer to be able to define crops with a macro -# (like @species), but since macros are so tricky to get right, -# I'll leave that for later. -# -# """ -# @crop -# -# A macro to create and register a new crop type. Pass a list -# of keyword arguments to instantiate each field of the -# [`CropType`](@ref) struct. -# """ -# macro crop(;kwargs...) # macros don't take kwargs -# quote -# registercrop(CropType($(esc(kwargs)))) -# end -# end -# -# @crop "winter wheat" begin -# minsowdate = @dday("1 November") -# maxsowdate = @dday("31 November") -# growthparameter = 1.0 -# issowable = true -# end - -newcrop(name="winter wheat", - sowdate = @dday("1 November"), - harvestdate = @dday("1 July"), - mingrowthtemp = 5.0, - growthparameter = 1.0, - issowable = true) +""" + 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, cropgrowthfile::String) + @debug "Reading crop parameters" + cropdata = CSV.File(generalcropfile, missingstring="NA", dateformat="d U", + types=[String,Date,Date,Date,Date,Float64]) + growthdata = CSV.File(cropgrowthfile, missingstring="NA", + types=[Int,String,String,GrowthPhase,String, + Float64,Float64,Float64,Float64]) + croptypes = Vector{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")) + append!(croptypes, [CropType(crop.name, crop.minsowdate, crop.maxsowdate, + crop.minharvestdate, crop.maxharvestdate, + crop.mingrowthtemp, highnuts, lownuts)]) + end + croptypes +end diff --git a/src/parameters.toml b/src/parameters.toml index 172575d3d5ba6161e10d102e3be868af6ba438a4..6b26cac2ca94642e9cb07506732aa367e4ebb4f9 100644 --- a/src/parameters.toml +++ b/src/parameters.toml @@ -30,8 +30,10 @@ farmmodel = "FieldManager" # which version of the farm model to use (not yet imp targetspecies = ["Wolpertinger", "Wyvern"] # list of target species to simulate popoutfreq = "daily" # output frequency population-level data, daily/monthly/yearly/end/never indoutfreq = "end" # output frequency individual-level data, daily/monthly/yearly/end/never -insectmodel = ["season", "habitat", "pesticides"] # which factors affect insect growth ("weather" is not yet implemented) +insectmodel = ["season", "habitat", "pesticides", "weather"] # factors affecting insect growth [crop] -cropmodel = "linear" # crop growth model to use, "linear" or "aquacrop" (not yet implemented) +cropmodel = "almass" # crop growth model to use, "almass" or "aquacrop" +cropfile = "data/crop_data_general.csv" # file with general crop parameters +cropgrowthfile = "data/almass_crop_growth_curves.csv" # file with crop growth parameters