diff --git a/src/nature/nature.jl b/src/nature/nature.jl index 4fca51f3ded4ee9dd79429a8dfea10d54dfe5739..92f3c6f199f9a74d496051e3278f708d9bb74c40 100644 --- a/src/nature/nature.jl +++ b/src/nature/nature.jl @@ -7,6 +7,7 @@ ## An enum used to assign a sex to each animal @enum Sex hermaphrodite male female +#XXX hermaphrodite probably is not needed and its implementation is iffy """ Animal @@ -24,13 +25,15 @@ i.e. the trait `phase` can be accessed and modified with `animal.phase`.) #XXX add `phase` and `species` (rather than `name`) directly to the struct? traits::Dict{String,Any} energy::Union{EnergyBudget,Nothing} # DEB is optional + parents::Tuple{Int64,Int64} #XXX two parents for hermaphrodites? sex::Sex age::Int end # DEB is optional Animal(id::Int64, pos::Tuple{Int64,Int64}, traits::Dict{String,Any}, - sex::Sex, age::Int) = Animal(id, pos, traits, nothing, sex, age) + parents::Tuple{Int64,Int64}, sex::Sex, age::Int) = + Animal(id, pos, traits, nothing, parents, sex, age) # Overloading the `getproperty` and `setproperty!` methods for `Animal` allows # us to write `animal.property` instead of `animal.traits["property"]`. diff --git a/src/nature/populations.jl b/src/nature/populations.jl index c0029a1c4562fa4116d0357137402f6a1381d7e1..e00d22f4e113bd0514a0a2cba72f9cce6a9d55f2 100644 --- a/src/nature/populations.jl +++ b/src/nature/populations.jl @@ -20,8 +20,10 @@ This can be used to create the `initialise!` variable in a species definition bl - `popsize` determines the number of individuals that will be created. If this is zero or negative, one individual will be created in every suitable location in the landscape. If `popsize` is greater than the number of suitable locations, multiple individuals - will be created in one place. (Maximum population density can be set in the habitat - descriptor using the [`@countanimals`](@ref) macro.) + will be created in one place. + +- `popdensity`: if this is greater than zero, the chance of creating an individual (or + pair of individuals) at a suitable location is 1/popdensity. - If `pairs` is true, a male and a female individual will be created in each selected location, otherwise, only one individual will be created at a time. @@ -35,8 +37,8 @@ This can be used to create the `initialise!` variable in a species definition bl individual has been created and placed. """ function initpopulation(habitatdescriptor::Function; phase::Union{String,Nothing}=nothing, - popsize::Int64=-1, pairs::Bool=false, asexual::Bool=false, - initfunction::Function=(a,m)->nothing) + popsize::Int64=-1, popdensity::Int64=-1, pairs::Bool=false, + asexual::Bool=false, initfunction::Function=(a,m)->nothing) #TODO add a constructor function for the individual? function(species::Dict{String,Any}, model::AgentBasedModel) n = 0 @@ -47,18 +49,20 @@ function initpopulation(habitatdescriptor::Function; phase::Union{String,Nothing while n == 0 || n < popsize for x in @shuffle!(Vector(1:width)) for y in @shuffle!(Vector(1:height)) - if habitatdescriptor((x,y), model) + if habitatdescriptor((x,y), model) && + (popdensity <= 0 || @chance(1/popdensity)) #XXX what if pd==0? if pairs - a1 = add_agent!((x,y), Animal, model, - deepcopy(species), female, 0) - a2 = add_agent!((x,y), Animal, model, - deepcopy(species), male, 0) + a1 = add_agent!((x,y), Animal, model, deepcopy(species), + (-1,-1), female, 0) + a2 = add_agent!((x,y), Animal, model, deepcopy(species), + (-1, -1), male, 0) initfunction(a1, model) initfunction(a2, model) n += 2 else sex = asexual ? hermaphrodite : @rand([male, female]) - a = add_agent!((x,y),Animal,model,deepcopy(species),sex,0) + a = add_agent!((x,y), Animal, model, deepcopy(species), + (-1, -1), sex,0) initfunction(a, model) n += 1 end @@ -92,16 +96,18 @@ end #XXX initpopulation based on known occurences in real-life? """ - reproduce!(animal, model, n=1) + reproduce!(animal, model, mate, n=1) Produce one or more offspring for the given animal at its current location. +The `mate` argument gives the ID of the reproductive partner. """ -function reproduce!(animal::Animal, model::AgentBasedModel, n::Int64=1) +function reproduce!(animal::Animal, model::AgentBasedModel, mate::Int64, n::Int64=1) + (animal.sex == male) && @warn "Male $(animalid(animal)) is reproducing." for i in 1:n sex = (animal.sex == hermaphrodite) ? hermaphrodite : @rand([male, female]) # We need to generate a fresh species dict here species = @eval $(Symbol(animal.traits["name"]))($model) - add_agent!(animal.pos, Animal, model, species, sex, 0) + add_agent!(animal.pos, Animal, model, species, (animal.id, mate), sex, 0) end @debug "$(animalid(animal)) has reproduced." end diff --git a/src/nature/species/wolpertinger.jl b/src/nature/species/wolpertinger.jl index 482076348d87664e92bbc9f565fa205b014e5d5a..4f5c14ccaca850ced253110f5e9e9dea18d0fbb4 100644 --- a/src/nature/species/wolpertinger.jl +++ b/src/nature/species/wolpertinger.jl @@ -31,7 +31,7 @@ of a deer. if @rand() < @trait(fecundity) && @countanimals(species="Wolpertinger") < @trait(crowding) - @reproduce + @reproduce(-1) end @kill @trait(mortality) diff --git a/src/nature/species/wyvern.jl b/src/nature/species/wyvern.jl index eb1d9889fbe0abe7de221734bbf4d2b785301da7..7becb7120252485d245b03b9d449bcba0276fcba 100644 --- a/src/nature/species/wyvern.jl +++ b/src/nature/species/wyvern.jl @@ -61,7 +61,7 @@ legs, but that doesn't make it any less dangerous... end # reproduce every once in a blue moon @label reproduce - (@rand() < @trait(fecundity)) && @reproduce + (@rand() < @trait(fecundity)) && @reproduce(-1) # hibernate from November to March if monthday(model.date) == (11,1) @trait(phase) = "winter" diff --git a/src/parameters.toml b/src/parameters.toml index 45a4519b968dd80730b1c485af187459b19d9624..b9539d390e8065de3bcdc6a1af40204da3239e67 100644 --- a/src/parameters.toml +++ b/src/parameters.toml @@ -21,9 +21,9 @@ startdate = 2022-01-01 enddate = 2022-12-31 [world] -landcovermap = "data/regions/jena/landcover.tif" # location of the landcover map -farmfieldsmap = "data/regions/jena/fields.tif" # location of the field geometry map -weatherfile = "data/regions/jena/weather.csv" # location of the weather data file +landcovermap = "data/regions/jena-small/landcover.tif" # location of the landcover map +farmfieldsmap = "data/regions/jena-small/fields.tif" # location of the field geometry map +weatherfile = "data/regions/jena-small/weather.csv" # location of the weather data file [farm] farmmodel = "FieldManager" # which version of the farm model to use (not yet implemented) diff --git a/src/world/landscape.jl b/src/world/landscape.jl index 4d8e28376da5e6d98eed1293dbba40920e3083e8..628781cfc84e3e3de1514536fd5c0e8fccc460b5 100644 --- a/src/world/landscape.jl +++ b/src/world/landscape.jl @@ -3,9 +3,9 @@ ### This file manages the landscape maps that underlie the model. ### +## IMPORTANT: do not change the order of this enum, or initlandscape() will break! "The land cover classes encoded in the Mundialis Sentinel data." @enum LandCover nodata forest grass water builtup soil agriculture -## Do not change the order of this enum, or initlandscape() will break! "The types of landscape event that can be simulated" @enum EventType tillage sowing fertiliser pesticide harvesting @@ -160,6 +160,7 @@ Calculate the distance from the given location to the closest habitat of the spe Caution: can be computationally expensive! """ function distanceto(pos::Tuple{Int64,Int64}, model::AgentBasedModel, habitattype::LandCover) + #XXX allow testing for multiple habitat types? # can't use @habitat here because nature.jl is loaded later than this file distanceto(pos, model, function(p,m) landcover(p,m) == habitattype end) end diff --git a/test/nature_tests.jl b/test/nature_tests.jl index dddbb64ada3a88773f37efcdafbfab46e518a577..ef945ffdb6e455da0f25c5943af5279b129b57ea 100644 --- a/test/nature_tests.jl +++ b/test/nature_tests.jl @@ -83,7 +83,7 @@ end @debug "Animal: $animal" if @trait(sex) == Persefone.female && @countanimals() < 3 && @trait(age) >= @trait(ageofmaturity) && @landcover() == Persefone.water - @reproduce() + @reproduce(-1) end end @phase drought begin