Skip to content
Snippets Groups Projects
Commit e43c5ebd authored by xo30xoqa's avatar xo30xoqa
Browse files

First version of farm model (seems to work)

parent eb8515d8
No related branches found
No related tags found
No related merge requests found
......@@ -8,6 +8,7 @@
module ALMaSS
using Persefone:
AnnualDate,
Management,
Length,
cm,
......@@ -60,16 +61,17 @@ The type struct for all crops. Currently follows the crop growth model as
implemented in ALMaSS.
"""
struct CropType
#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
group::String
minsowdate::Union{Missing,Date}
maxsowdate::Union{Missing,Date}
minharvestdate::Union{Missing,Date}
maxharvestdate::Union{Missing,Date}
minsowdate::Union{Missing,AnnualDate}
maxsowdate::Union{Missing,AnnualDate}
minharvestdate::Union{Missing,AnnualDate}
maxharvestdate::Union{Missing,AnnualDate}
mingrowthtemp::Union{Missing,Float64}
highnutrientgrowth::Union{Missing,CropCurveParams}
lownutrientgrowth::Union{Missing,CropCurveParams}
#issowable::Union{Function,Bool}
end
cropname(ct::CropType) = ct.name
......@@ -106,7 +108,7 @@ isharvestable(cs::CropState) = cs.mature
Extend `tryparse` to allow parsing GrowthPhase values.
(Needed to read in the CSV parameter file.)
"""
function Base.tryparse(type::Type{GrowthPhase}, str::String)
function Base.tryparse(::Type{GrowthPhase}, str::String)
str == "janfirst" ? janfirst :
str == "sow" ? sow :
str == "marchfirst" ? marchfirst :
......@@ -152,8 +154,8 @@ Parse a CSV file containing the required parameter values for each crop
"""
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,String])
cropdata = CSV.File(generalcropfile, missingstring="NA",
types=[String,AnnualDate,AnnualDate,AnnualDate,AnnualDate,Float64,String])
growthdata = CSV.File(growthfile, missingstring="NA",
types=[Int,String,String,GrowthPhase,String,
Float64,Float64,Float64,Float64])
......
......@@ -37,9 +37,9 @@ end
Sow the specified crop on the farmplot.
"""
function sow!(farmplot::FarmPlot, model::SimulationModel, cropname::String)
#XXX test if the crop is sowable?
createevent!(model, farmplot.pixels, sowing)
sow!(farmplot.cropstate, model, cropname)
@debug "Farmer $(farmplot.farmer) sowed $(cropname) on farmplot $(farmplot.id)."
end
"""
......@@ -50,6 +50,7 @@ Harvest the crop of this farmplot.
function harvest!(farmplot::FarmPlot{T}, model::SimulationModel) where T
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)."
end
"""
......
......@@ -4,35 +4,52 @@
###
##TODO what data do we need to gather from the farm submodel?
## - area covered by each crop over time
## - average height of each crop over time
## - total income per year
#XXX Initially, we're only working with a single simple crop rotation.
# Later on, we need to figure out how to integrate several.
const CROPROTATION = ["winter rape", "winter wheat", "maize", "winter barley"]
#FIXME Currently, this is specific to the ALMaSS model. We need to figure out how to generalise it.
"""
Farmer
This is the agent type for the farm ABM. (Not yet implemented.)
This is the agent type for the farm ABM.
"""
mutable struct Farmer <: ModelAgent
#XXX make this into an abstract type and create subtypes for different farm submodels? (#69)
const id::Int64
fields::Vector{Int64} # IDs of the farmplots this farmer owns
croprotation::Vector{String} #TODO figure this out
totalincome::Float64
# farmplots owned by this farmer and their associated crop rotations and next sowing date
fields::Vector{Int64}
croprotations::Dict{Int64, Vector{String}}
sowdates::Dict{Int64, AnnualDate}
totalincome::Float64 # accumulated income XXX split up by year?
end
"""
stepagent!(farmer, model)
Update a farmer by one day.
Update a farmer by one day. Cycle through all fields and see what management is needed.
"""
function stepagent!(farmer::Farmer, model::SimulationModel)
for f in farmer.fields
field = model.farmplots[f]
ctype = croptype(field)
if ctype.group != "semi-natural" && isharvestable(field)
harvest!(field, model)
@harvest()
#XXX later: calculate income based on yield and annual price
(ctype.group != "grass") && @sow("no growth")
elseif cropname(field) == "no growth"
#TODO if a field has been harvested, check if the next crop can be sown
if ctype.group != "grass"
@sow("no growth")
cycle!(farmer.croprotations[f]) # advance the crop rotation
nextcrop = model.crops[farmer.croprotations[f][1]]
farmer.sowdates[f] = @rand(nextcrop.minsowdate:nextcrop.maxsowdate)
end
elseif cropname(field) == "no growth" && model.date == farmer.sowdates[f]
# if a field has been harvested, check if the next crop can be sown
@sow(farmer.croprotations[f][1])
end
end
end
......@@ -44,16 +61,22 @@ Initialise the model with a set of farm agents.
"""
function initfarms!(model::SimulationModel)
#XXX initially, we only have one farmer controlling all fields in the region
farmer = Farmer(1, collect(1:length(model.farmplots)), [], 0)
farmer = Farmer(1, collect(1:length(model.farmplots)), Dict(), Dict(), 0)
model.farmers = [farmer]
setasides = findsetasides(farmer, model)
for field in model.farmplots
field.farmer = farmer.id
if isgrassland(field, model)
@sow("permanent grassland (seeded)")
@sow("permanent grassland (grazed)")
farmer.croprotations[field.id] = ["permanent grassland (grazed)"]
elseif field.id in setasides
@sow("permanent set-aside")
farmer.croprotations[field.id] = ["permanent set-aside"]
else
@sow("no growth")
@sow("no growth") # assign each arable field a crop rotation, cycled randomly
farmer.croprotations[field.id] = cycle!(deepcopy(CROPROTATION), @rand(0:3))
nextcrop = model.crops[farmer.croprotations[field.id][1]]
farmer.sowdates[field.id] = @rand(nextcrop.minsowdate:nextcrop.maxsowdate)
end
end
end
......@@ -70,7 +93,7 @@ function findsetasides(farmer::Farmer, model::SimulationModel)
model.farmplots[farmer.fields]))
setasidearea = 0
setasides = []
for f in farmer.fields #XXX should be sorted smallest-largest for highest efficiency
for f in keys(farmer.fields) #XXX should be sorted smallest-largest for highest efficiency
field = model.farmplots[f]
isgrassland(field, model) && continue
push!(setasides, f)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment