Select Git revision
run_video.bat
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
farm.jl 4.47 KiB
### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
###
### This file is responsible for managing the farm module(s).
###
## GENERIC TYPES AND FUNCTIONS
"This is the agent type for the farm ABM."
abstract type Farmer <: ModelAgent end
stepagent!(f::Farmer, model::SimulationModel) =
@error "Farmer type $(typeof(f)) has no stepagent!() method."
"""
initfarms!(model)
Initialise the model with a set of farm agents, depending on the configured farm model.
"""
function initfarms!(model::SimulationModel)
if @param(farm.farmmodel) == "BasicFarmer"
initbasicfarms!(model)
else
Base.error("Farm model $(@param(farm.farmmodel)) doesn't exist.")
end
end
## BASIC FARM MODEL
#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.
"""
BasicFarmer
The BasicFarmer type simply applies a set crop rotation to his fields and keeps track of income.
"""
mutable struct BasicFarmer <: Farmer
const id::Int64
# 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. Cycle through all fields and see what management is needed.
"""
function stepagent!(farmer::BasicFarmer, model::SimulationModel)
for f in farmer.fields
field = model.farmplots[f]
ctype = croptype(field)
if ctype.group != "semi-natural" && isharvestable(field)
@harvest()
#XXX later: calculate income based on yield and annual price
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
"""
initbasicfarms!(model)
Initialise the basic farm model. All fields are controlled by a single farmer actor
and are assigned as grassland, set-aside, or arable land with a crop rotation.
"""
function initbasicfarms!(model::SimulationModel)
farmer = BasicFarmer(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 (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") # 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
initfarmdata(model)
end
"""
findsetasides(farmer, model)
Return a vector of field IDs that this farmer should keep fallow to satisfy the configured
set-aside rules.
"""
function findsetasides(farmer::BasicFarmer, model::SimulationModel)
@param(farm.setaside) == 0 && return []
croparea = @areaof(sum(f -> isgrassland(f, model) ? 0 : length(f.pixels),
model.farmplots[farmer.fields]))
setasidearea = 0m²
setasides = []
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)
setasidearea += @areaof(length(field.pixels))
if setasidearea >= croparea*@param(farm.setaside)
@debug "Farmer $(farmer.id) has set aside $(setasidearea |> ha)."
return setasides
end
end
end