diff --git a/src/Persephone.jl b/src/Persephone.jl index 2be55d1ddfa16c853918617b0655a7e402114493..ac48d521460ae39aec5df0a4cbac4e8f89d9ff0d 100644 --- a/src/Persephone.jl +++ b/src/Persephone.jl @@ -25,21 +25,23 @@ using ## define exported functions and variables export simulate, - initsim, - stepsim, - finalisesim + initialise, + stepsimulation!, + finalise ## The file that stores all default parameters -const paramfile = "src/parameters.toml" +const PARAMFILE = "src/parameters.toml" ## (DO NOT CHANGE THIS VALUE! Instead, specify simulation-specific configuration files ## by using the "--configfile" commandline argument, or when invoking simulate().) -## include all module files +## include all module files - note that the order matters +## (if file b references something from file a, it must be included later) include("core/input.jl") include("core/output.jl") +include("farm/farm.jl") include("crop/crops.jl") include("nature/nature.jl") -include("farm/farm.jl") +include("nature/wolpertinger.jl") include("core/simulation.jl") end diff --git a/src/core/input.jl b/src/core/input.jl index ee4fc31d60f1ebccd23b7cee2c2236e2f2236aff..193fae5bbed929330fc6f088f37a15e50d646303 100644 --- a/src/core/input.jl +++ b/src/core/input.jl @@ -128,5 +128,7 @@ end Read in a TIFF map file and return it as an array. """ function readtiffmapfile(filename) + tiff = GeoArrays.read(filename) + space = GridSpace(size(tiff)[1:2], periodic=false) #TODO this requires GeoArrays end diff --git a/src/core/simulation.jl b/src/core/simulation.jl index 0e647b34beb89ec4a5f2e2dde9726a01db4bc182..8b0fc7261fd36bef4e2d0c4b44b28046c69a39d2 100644 --- a/src/core/simulation.jl +++ b/src/core/simulation.jl @@ -3,29 +3,63 @@ ### This file includes the core functions for initialising and running simulations. ### -function initsim(config::String) +""" + initialise(config) + +Initialise the model: read in parameters, create the output data directory, +and instantiate the AgentBasedModel object. +""" +function initialise(config::String=PARAMFILE) initsettings(config) - Random.seed!(param("core.seed")) setupdatadir() - #TODO create world + landcover = GeoArrays.read(param("core.mapfile")) + space = GridSpace(size(landcover)[1:2], periodic=false) + properties = Dict{Symbol,Any}(:day=>0, + :landcover=>landcover) + model = AgentBasedModel(Union{Farmer,Animal,CropPlot}, space, properties=properties, + rng=Random.Xoshiro(param("core.seed"))) + initfarms!(model) + initfields!(model) + initnature!(model) @info "Simulation initialised." + model end -function stepsim() - @info "Simulating another day." + +""" + stepsimulation!(model) + +Execute one update of the model. +""" +function stepsimulation!(model::AgentBasedModel) + model.day += 1 + @info "Simulating day $(model.day)." + for a in Schedulers.ByType((Farmer,Animal,CropPlot), true)(model) + stepagent!(getindex(model, a), model) + end #TODO end -function finalisesim() + +""" + finalise(model) + +Wrap up the simulation. Output all remaining data and exit. +""" +function finalise(model::AgentBasedModel) @info "Simulation ran. Nothing happened. But it will!" #TODO + genocide!(model) end -function simulate(config::String=paramfile) - initsim(config) - for day in 1:param("core.runtime") - stepsim() - end - finalisesim() - #TODO + +""" + simulate(config) + +Carry out a complete simulation run. +""" +function simulate(config::String=PARAMFILE) + model = initialise(config) + step!(model, dummystep, stepsimulation!, param("core.runtime")) + finalise(model) end diff --git a/src/crop/crops.jl b/src/crop/crops.jl index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0db809b6159f46e7f0a40145a8be362344fef84e 100644 --- a/src/crop/crops.jl +++ b/src/crop/crops.jl @@ -0,0 +1,29 @@ +### Persephone - a socio-economic-ecological model of European agricultural landscapes. +### +### This file is responsible for managing the crop growth modules. +### + +#XXX not sure whether it makes sense to have this as an agent type, +# or perhaps better a grid property? + +@agent CropPlot GridAgent{2} begin + #TODO +end + +""" + stepagent!(cropplot, model) + +Update a crop plot by one day. +""" +function stepagent!(cropplot::CropPlot, model::AgentBasedModel) + #TODO +end + +""" + initfields!(model) + +Initialise the model with its crop plots. +""" +function initfields!(model::AgentBasedModel) + #TODO +end diff --git a/src/farm/farm.jl b/src/farm/farm.jl index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e982cfbf574f064db11f23619be2a8328e22e36b 100644 --- a/src/farm/farm.jl +++ b/src/farm/farm.jl @@ -0,0 +1,26 @@ +### Persephone - a socio-economic-ecological model of European agricultural landscapes. +### +### This file is responsible for managing the farm module(s). +### + +@agent Farmer GridAgent{2} begin + #TODO +end + +""" + stepagent!(farmer, model) + +Update a farmer by one day. +""" +function stepagent!(farmer::Farmer, model::AgentBasedModel) + #TODO +end + +""" + initfarms!(model) + +Initialise the model with a set of farm agents. +""" +function initfarms!(model::AgentBasedModel) + #TODO +end diff --git a/src/nature/nature.jl b/src/nature/nature.jl index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e2b7cf102b3e4f75d7457773f32e46ca0fe0d224 100644 --- a/src/nature/nature.jl +++ b/src/nature/nature.jl @@ -0,0 +1,78 @@ +### Persephone - a socio-economic-ecological model of European agricultural landscapes. +### +### This file is responsible for managing the animal modules. +### + +@enum Sex hermaphrodite male female + +""" + Species + +Species are differentiated by their name, and the functions used to +initialise their population and update each individual. +""" +struct Species + name::String + initpop::Function # takes one argument: model + update::Function # takes two arguments: animal, model +end + +""" + Animal + +This is the generic agent type for all animals. Species are differentiated +by the `species` struct passed by them during initialisation. +""" + +@agent Animal GridAgent{2} begin + species::Species + sex::Sex + age::Int32 + energy::Int32 +end + +let specieslist = Dict{String, Species}() + """ + registerspecies(species) + + This function has to be called by every species definition file + in order for the species to be used. + """ + global function registerspecies(s::Species) + specieslist[s.name] = s + end + + """ + getspecies(name) + + Returns the definition struct for the named species. + """ + global function getspecies(name::String) + specieslist[name] + end +end + +""" + stepagent!(animal, model) + +Update an animal by one day. +""" +function stepagent!(animal::Animal, model::AgentBasedModel) + animal.age += 1 + animal.species.update(animal,model) + if animal.energy <= 0 + kill_agent!(animal, model) + end +end + +""" + initnature!(model) + +Initialise the model with all simulated animal populations. +""" +function initnature!(model::AgentBasedModel) + for s in param("nature.targetspecies") + getspecies(s).initpop(model) + end +end + diff --git a/src/nature/wolpertinger.jl b/src/nature/wolpertinger.jl new file mode 100644 index 0000000000000000000000000000000000000000..8875ec55bd2466a4a90f359878e995537dadd335 --- /dev/null +++ b/src/nature/wolpertinger.jl @@ -0,0 +1,34 @@ +### Persephone - a socio-economic-ecological model of European agricultural landscapes. +### +### This file holds the code for the Wolpertinger (https://en.wikipedia.org/wiki/Wolpertinger). +### NOT FOR ACTUAL USE! This is of course only a test species ;-) +### Although I dare say the Wolpertinger is rather endangered... +### + +""" + initwolpertinger!(model) + +Initialise a population of Wolpertingers in random locations around the landscape. +""" +function initwolpertinger!(model::AgentBasedModel) + species = getspecies("Wolpertinger") + worldsize = size(model.landcover)[1:2] + popsize = round(worldsize[1]*worldsize[2]/1000) + for i in 1:popsize + add_agent!(Animal, model, species, hermaphrodite, 0, 100) + end + @debug "Hid $(popsize) Wolpertingers for gullible tourists to find." +end + +""" + updatewolpertinger(animal, model) + +Wolpertingers are rather stupid creatures, all they do is move around randomly +and occasionally reproduce by spontaneous parthogenesis... +""" +function updatewolpertinger!(w::Animal, model::AgentBasedModel) + #TODO + @debug "The Wolpertinger is a mysterious animal..." w.id +end + +registerspecies(Species("Wolpertinger", initwolpertinger!, updatewolpertinger!)) diff --git a/src/parameters.toml b/src/parameters.toml index 6a1fc4988450619b8a369c3326bb540f51cd6198..52e2521740afbcdfb8fc53d77839b9c07e1d087b 100644 --- a/src/parameters.toml +++ b/src/parameters.toml @@ -18,7 +18,7 @@ seed = 0 # seed value for the RNG (0 -> random value) [nature] -targetspecies = [] # list of target species to simulate +targetspecies = ["Wolpertinger"] # list of target species to simulate [crop] cropmodel = "linear" # crop growth model to use, "linear" or "aquacrop" (not yet implemented)