diff --git a/src/Persefone.jl b/src/Persefone.jl
index d6b7ccb599aa811b9c4e96aaa237e834b06e3bfd..fa7be8b2a923660bd10b204c53eb8fb6dadb2fbe 100644
--- a/src/Persefone.jl
+++ b/src/Persefone.jl
@@ -148,6 +148,9 @@ model has to define a type `CropState <: AbstractCropState`.
 """
 abstract type AbstractCropState end
 
+function makecropstate end
+setsoiltype!(cropstate::AbstractCropState, soiltype) = nothing
+
 function stepagent! end
 
 ## include all module files (note that the order matters - if file
diff --git a/src/crop/almass.jl b/src/crop/almass.jl
index a2dada4fc2d1d5e58552246a3fa6cd38ae34785e..ecd155cce1affc5686da6e5acae2860855916103 100644
--- a/src/crop/almass.jl
+++ b/src/crop/almass.jl
@@ -49,6 +49,7 @@ using Persefone:
     Management,
     cm,
     SimulationModel,
+    SoilType,
     fertiliser,
     maxtemp,
     mintemp,
@@ -62,6 +63,7 @@ import Persefone:
     cropheight,
     cropcover,
     cropyield,
+    makecropstate,
     sow!,
     harvest!,
     isharvestable,
@@ -202,6 +204,12 @@ function isharvestable(cs::CropState)
     return cs.ddegs >= last(gdd)
 end
 
+function makecropstate(croptype::CropType, model::SimulationModel, soiltype::SoilType)
+    phase = month(model.date) < 3 ? janfirst : marchfirst
+    return CropState(; croptype, phase)
+end
+
+
 # Constant in original ALMaSS code:
 #     `EL_VEG_START_LAIT` from `Landscape/Elements.cpp`, line 238-239
 const VEG_START_LAIT::Float64 = 1.08
diff --git a/src/crop/aquacrop.jl b/src/crop/aquacrop.jl
index 975dd5a3523a8e7f57f8921478bdfb1e2fc9e3e8..f95e5160be2554fe0a8aa67fe7d41c02aa45c28a 100644
--- a/src/crop/aquacrop.jl
+++ b/src/crop/aquacrop.jl
@@ -25,9 +25,11 @@ import Persefone:
     cropheight,
     cropcover,
     cropyield,
+    makecropstate,
     sow!,
     harvest!,
-    isharvestable
+    isharvestable,
+    setsoiltype!
 
 using Unitful: @u_str
 
@@ -167,6 +169,14 @@ cropheight(cs::CropState) = cs.height  # TODO: calculate from AquaCrop state inf
 cropcover(cs::CropState) = AquaCrop.canopycover(cs.cropstate)
 cropyield(cs::CropState) = AquaCrop.dryyield(cs.cropstate)  # TODO: there is also freshyield
 isharvestable(cs::CropState) = true  # TODO: implement this correctly
+makecropstate(crop_type::CropType, model::SimulationModel, soiltype::SoilType) =
+    CropState(crop_type, soiltype, model)
+function setsoiltype!(cs::CropState, soiltype::SoilType)
+    # TODO: this does not affect the actual soil type of the state of
+    # the aquacrop simulation
+    cs.soiltype = soiltype
+    return nothing
+end
 
 """
     stepagent!(cropstate, model)
diff --git a/src/crop/cropmodels.jl b/src/crop/cropmodels.jl
index 5571dbdc0dc27d5a4964bb4c85a2e30f753ac61a..f5ec6d084ca919ed5a0cbed8916475c0611afc07 100644
--- a/src/crop/cropmodels.jl
+++ b/src/crop/cropmodels.jl
@@ -32,6 +32,12 @@ end
 Initialise the farm plots in the simulation model.
 """
 function initfields!(model::SimulationModel)
+    # TODO: we take the soiltype from the first pixel in the farmplot,
+    # then later we fix the soiltypes of the farmplots and cropstates
+    # to be the most common soiltype of a farmplot.  It would be
+    # better to first collect all the data for pixels belonging
+    # together, and then create the farmplots and cropstates with the
+    # correct soiltype directly
     convertid = Dict{Int64,Int64}()
     width, height = size(model.landscape)
     for x in 1:width
@@ -46,7 +52,7 @@ function initfields!(model::SimulationModel)
                 push!(model.farmplots[objectid].pixels, (x,y))
             else
                 soiltype = model.landscape[x,y].soiltype
-                cropstate = makecropstate(model, soiltype)
+                cropstate = makecropstate(model.crops["natural grass"], model, soiltype)
                 fp = FarmPlot(length(model.farmplots) + 1, [(x, y)], -1, soiltype, cropstate)
                 push!(model.farmplots, fp)
                 model.landscape[x,y].fieldid = fp.id
@@ -58,48 +64,50 @@ function initfields!(model::SimulationModel)
     # Adjust farmplot soil type to most common soil type of its
     # pixels, and set the cropstate soil type to this as well.
     for fp in model.farmplots
-        fp.soiltype = mode(map(p -> model.landscape[p[1], p[2]].soiltype, fp.pixels))
-        set_cropstate_soiltype!(fp, model)
+        soiltype = mode(map(p -> model.landscape[p[1], p[2]].soiltype, fp.pixels))
+        setsoiltype!(fp, soiltype)
     end
 
     @info "Initialised $(length(model.farmplots)) farm plots."
 end
 
-# TODO: this function should be moved to the individual crop models,
-# and overloaded on the type of cropstate
-"""
-    set_cropstate_soiltype!(farmplot, model)
-"""
-function set_cropstate_soiltype!(farmplot::FarmPlot, model::SimulationModel)
-    if @param(crop.cropmodel) == "aquacrop"
-        farmplot.cropstate.soiltype = farmplot.soiltype
-    else
-        # do nothing for other cropmodels
-    end
-end
+# # TODO: this function should be moved to the individual crop models,
+# # and overloaded on the type of cropstate (see notes above)
+# """
+#     setsoiltype!(farmplot, soiltype, model)
+# """
+# function setsoiltype!(farmplot::FarmPlot, soiltype::SoilType, model::SimulationModel)
+#     farmplot.soiltype = soiltype
+#     if @param(crop.cropmodel) == "aquacrop"
+#         farmplot.cropstate.soiltype = soiltype
+#     else
+#         # do nothing for other cropmodels
+#     end
+# end
 
-"""
-    makecropstate(model, soiltype)
-
-An internal utility function to initialise one instance of the configured crop growth model.
-"""
-function makecropstate(model::SimulationModel, soiltype::SoilType)
-    if @param(crop.cropmodel) == "almass"
-        phase = (month(model.date) < 3 ? ALMaSS.janfirst : ALMaSS.marchfirst)
-        cs = ALMaSS.CropState(
-            croptype = model.crops["natural grass"],
-            phase = phase,
-        )
-    elseif @param(crop.cropmodel) == "simple"
-        cs = SimpleCrop.CropState(
-            model.crops["natural grass"],
-            0.0m
-        )
-    elseif @param(crop.cropmodel) == "aquacrop"
-        croptype = model.crops["natural grass"]
-        cs = AquaCropWrapper.CropState(croptype, soiltype, model)
-    else
-        Base.error("Unhandled crop model '$(@param(crop.cropmodel))' in makecropstate().")
-    end
-    return cs
-end
+# # TODO: move this function to individual crop model files
+# """
+#     makecropstate(model, soiltype)
+#
+# An internal utility function to initialise one instance of the configured crop growth model.
+# """
+# function makecropstate(model::SimulationModel, soiltype::SoilType)
+#     if @param(crop.cropmodel) == "almass"
+#         phase = (month(model.date) < 3 ? ALMaSS.janfirst : ALMaSS.marchfirst)
+#         cs = ALMaSS.CropState(
+#             croptype = model.crops["natural grass"],
+#             phase = phase,
+#         )
+#     elseif @param(crop.cropmodel) == "simple"
+#         cs = SimpleCrop.CropState(
+#             model.crops["natural grass"],
+#             0.0m
+#         )
+#     elseif @param(crop.cropmodel) == "aquacrop"
+#         croptype = model.crops["natural grass"]
+#         cs = AquaCropWrapper.CropState(croptype, soiltype, model)
+#     else
+#         Base.error("Unhandled crop model '$(@param(crop.cropmodel))' in makecropstate().")
+#     end
+#     return cs
+# end
diff --git a/src/crop/farmplot.jl b/src/crop/farmplot.jl
index 810bbdfcee53a14fd5ac2186165d7c3604937dc7..40a3917d8d55699d1785d0624adbd849c445d2ce 100644
--- a/src/crop/farmplot.jl
+++ b/src/crop/farmplot.jl
@@ -23,6 +23,12 @@ cropcover(f::FarmPlot) = cropcover(f.cropstate)
 cropyield(f::FarmPlot) = cropyield(f.cropstate)
 isharvestable(f::FarmPlot) = isharvestable(f.cropstate)
 
+function setsoiltype!(f::FarmPlot, soiltype::SoilType)
+    f.soiltype = soiltype
+    setsoiltype!(f.cropstate, soiltype)
+    return nothing
+end
+
 """
     stepagent!(farmplot, model)
 
diff --git a/src/crop/simplecrop.jl b/src/crop/simplecrop.jl
index 32dbad83d13db43fa8af1ac64e8d330f6540cea1..7f74ca47c7981b0504747d591073ccc333cf75c6 100644
--- a/src/crop/simplecrop.jl
+++ b/src/crop/simplecrop.jl
@@ -4,9 +4,9 @@ using Persefone:
     AbstractCropState,
     AbstractCropType,
     AnnualDate,
-    FarmPlot,
     Length,
     cm,
+    SoilType,
     SimulationModel
 
 import Persefone:
@@ -16,6 +16,7 @@ import Persefone:
     cropheight,
     cropcover,
     cropyield,
+    makecropstate,
     sow!,
     harvest!,
     isharvestable
@@ -43,6 +44,9 @@ cropheight(cs::CropState) = cs.height
 cropcover(cs::CropState) = 0.0
 cropyield(cs::CropState) = 0.0  # TODO: units?
 isharvestable(cs::CropState) = true
+makecropstate(crop_type::CropType, model::SimulationModel, soiltype::SoilType) =
+    CropState(crop_type, 0.0cm)
+
 
 """
     stepagent!(cropstate, model)