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

Fixed errors in basic skylark

Still need to write tests to make sure it works properly, but at least
it runs without errors now and seems to do what it's supposed to.
parent 79f4da42
No related branches found
No related tags found
No related merge requests found
...@@ -59,6 +59,7 @@ Initialise a model object using a ready-made settings dict. This is ...@@ -59,6 +59,7 @@ Initialise a model object using a ready-made settings dict. This is
a helper function for `initialise()`. a helper function for `initialise()`.
""" """
function initmodel(settings::Dict{String, Any}) function initmodel(settings::Dict{String, Any})
#TODO catch exceptions and print them to the log file
#TODO remove Agents.jl-related code, reimplement this more cleanly (#72) #TODO remove Agents.jl-related code, reimplement this more cleanly (#72)
@debug "Initialising model object." @debug "Initialising model object."
createdatadir(settings["core.outdir"], settings["core.overwrite"]) createdatadir(settings["core.outdir"], settings["core.overwrite"])
...@@ -127,6 +128,7 @@ end ...@@ -127,6 +128,7 @@ end
Execute one update of the model. Execute one update of the model.
""" """
function stepsimulation!(model::AgentBasedModel) function stepsimulation!(model::AgentBasedModel)
#TODO catch exceptions and print them to the log file
with_logger(model.logger) do with_logger(model.logger) do
@info "Simulating day $(model.date)." @info "Simulating day $(model.date)."
#TODO remove Agents.jl-related code, reimplement this more cleanly (#72) #TODO remove Agents.jl-related code, reimplement this more cleanly (#72)
...@@ -135,6 +137,8 @@ function stepsimulation!(model::AgentBasedModel) ...@@ -135,6 +137,8 @@ function stepsimulation!(model::AgentBasedModel)
stepagent!(model[a], model) stepagent!(model[a], model)
catch exc catch exc
# check if the KeyError comes from the `model[a]` or the function call # check if the KeyError comes from the `model[a]` or the function call
#FIXME this also silences KeyErrors caused in the species code (e.g.
# by accessing dead mates) - will be fixed once I reorganise the code
isa(exc, KeyError) && isa(exc.key, Int) ? continue : throw(exc) isa(exc, KeyError) && isa(exc.key, Int) ? continue : throw(exc)
end end
end end
......
...@@ -113,7 +113,8 @@ macro phase(name, body) ...@@ -113,7 +113,8 @@ macro phase(name, body)
#TODO the docstrings give a lot of warnings in the log - can I fix that? #TODO the docstrings give a lot of warnings in the log - can I fix that?
quote quote
Core.@__doc__ function $(esc(name))($(esc(:animal))::Animal, $(esc(:model))::AgentBasedModel) Core.@__doc__ function $(esc(name))($(esc(:animal))::Animal, $(esc(:model))::AgentBasedModel)
$(esc(:pos)) = $(esc(:animal)).pos #TODO add `self` as a synonym for `animal`
$(esc(:pos)) = $(esc(:animal)).pos #XXX does this make sense?
#$(esc(:date)) = $(esc(:model)).date #XXX does this make sense? #$(esc(:date)) = $(esc(:model)).date #XXX does this make sense?
$(esc(body)) $(esc(body))
end end
...@@ -128,8 +129,7 @@ A utility macro to quickly access an animal's trait value. ...@@ -128,8 +129,7 @@ A utility macro to quickly access an animal's trait value.
This can only be used nested within [`@phase`](@ref). This can only be used nested within [`@phase`](@ref).
""" """
macro trait(traitname) macro trait(traitname)
#TODO provide a version that can access another animal's traits #FIXME actually, we can get rid of this altogether if we add a Python-style `self`
#XXX replace with an @v macro? (shorter, and not all variables are "traits")
#XXX This would error if called in the first part of a species definition block #XXX This would error if called in the first part of a species definition block
# (i.e. outside of a @phase block). Although this is specified in the documentation, # (i.e. outside of a @phase block). Although this is specified in the documentation,
# it is unexpected and liable to be overlooked. Can we add a third clause to # it is unexpected and liable to be overlooked. Can we add a third clause to
...@@ -145,7 +145,7 @@ This can only be used in a context where the `model` object is available ...@@ -145,7 +145,7 @@ This can only be used in a context where the `model` object is available
(e.g. nested within [`@phase`](@ref)). (e.g. nested within [`@phase`](@ref)).
""" """
macro animal(id) macro animal(id)
:($(esc(:model))[$(id)]) :($(esc(:model))[$(esc(id))])
end end
""" """
...@@ -156,7 +156,7 @@ This can only be used in a context where the `model` object is available ...@@ -156,7 +156,7 @@ This can only be used in a context where the `model` object is available
(e.g. nested within [`@phase`](@ref)). (e.g. nested within [`@phase`](@ref)).
""" """
macro isalive(id) macro isalive(id)
:(isalive($(id), $(esc(:model)))) :(isalive($(esc(id)), $(esc(:model))))
end end
""" """
......
...@@ -73,7 +73,7 @@ Update an animal by one day, executing it's currently active phase function. ...@@ -73,7 +73,7 @@ Update an animal by one day, executing it's currently active phase function.
""" """
function stepagent!(animal::Animal, model::AgentBasedModel) function stepagent!(animal::Animal, model::AgentBasedModel)
animal.age += 1 animal.age += 1
animal.traits[animal.phase](animal,model) #FIXME -> note to self: why? animal.traits[animal.phase](animal,model) #FIXME -> note to self: why, what's wrong?
end end
""" """
...@@ -106,5 +106,3 @@ function updatenature!(model::AgentBasedModel) ...@@ -106,5 +106,3 @@ function updatenature!(model::AgentBasedModel)
end end
#XXX what else needs to go here? #XXX what else needs to go here?
end end
#TODO test migration
...@@ -39,7 +39,7 @@ This can be used to create the `initialise!` variable in a species definition bl ...@@ -39,7 +39,7 @@ This can be used to create the `initialise!` variable in a species definition bl
function initpopulation(habitatdescriptor::Function; phase::Union{String,Nothing}=nothing, function initpopulation(habitatdescriptor::Function; phase::Union{String,Nothing}=nothing,
popsize::Int64=-1, popdensity::Int64=-1, pairs::Bool=false, popsize::Int64=-1, popdensity::Int64=-1, pairs::Bool=false,
asexual::Bool=false, initfunction::Function=(a,m)->nothing) asexual::Bool=false, initfunction::Function=(a,m)->nothing)
#TODO add a constructor function for the individual? #TODO add a constructor function/macro for the individual
function(species::Dict{String,Any}, model::AgentBasedModel) function(species::Dict{String,Any}, model::AgentBasedModel)
n = 0 n = 0
lastn = 0 lastn = 0
...@@ -107,7 +107,7 @@ function reproduce!(animal::Animal, model::AgentBasedModel, mate::Int64, n::Int6 ...@@ -107,7 +107,7 @@ function reproduce!(animal::Animal, model::AgentBasedModel, mate::Int64, n::Int6
for i in 1:n for i in 1:n
sex = (animal.sex == hermaphrodite) ? hermaphrodite : @rand([male, female]) sex = (animal.sex == hermaphrodite) ? hermaphrodite : @rand([male, female])
# We need to generate a fresh species dict here # We need to generate a fresh species dict here
species = @eval $(Symbol(animal.traits["name"]))($model) species = @eval $(Symbol(animal.name))($model)
a = add_agent!(animal.pos, Animal, model, species, (animal.id, mate), sex, 0) a = add_agent!(animal.pos, Animal, model, species, (animal.id, mate), sex, 0)
push!(offspring, a.id) push!(offspring, a.id)
end end
......
...@@ -55,7 +55,8 @@ At the moment, this implementation is still in development. ...@@ -55,7 +55,8 @@ At the moment, this implementation is still in development.
# @distanceto(forest) > 5 && # at least 50m from forest edges # @distanceto(forest) > 5 && # at least 50m from forest edges
# @distanceto(builtup) > 5) # and from anthropogenic structures # @distanceto(builtup) > 5) # and from anthropogenic structures
@initialise(habitats, popdensity=300, pairs=true, initfunction=initskylark) @initialise(habitats, phase="mating", popdensity=300, pairs=true,
initfunction=initskylark)
""" """
As an egg, simply check for mortality and hatching. As an egg, simply check for mortality and hatching.
...@@ -101,7 +102,7 @@ At the moment, this implementation is still in development. ...@@ -101,7 +102,7 @@ At the moment, this implementation is still in development.
@phase nonbreeding begin @phase nonbreeding begin
# flocking behaviour - follow a random neighbour or move randomly # flocking behaviour - follow a random neighbour or move randomly
#TODO add feeding and mortality, respect habitat when moving #TODO add feeding and mortality, respect habitat when moving
neighbours = map(a->a.id, @neighbours(10)) neighbours = map(a->a.id, @neighbours(10)) #FIXME
#isempty(neighbours) ? @randomwalk(5) : @follow(@rand(neighbours), 2) #isempty(neighbours) ? @randomwalk(5) : @follow(@rand(neighbours), 2)
if isempty(neighbours) if isempty(neighbours)
@randomwalk(5) @randomwalk(5)
...@@ -133,17 +134,17 @@ At the moment, this implementation is still in development. ...@@ -133,17 +134,17 @@ At the moment, this implementation is still in development.
return return
end end
m, d = monthday(model.date) m, d = monthday(model.date)
nest = ((m == @trait(nestingbegin)[1] && d > @trait(nestingbegin)[2] nest = ((m == @trait(nestingbegin)[1] && d >= @trait(nestingbegin)[2]
&& @chance(0.05)) || (m > @trait(nestingbegin)[1])) && @chance(0.05)) || (m > @trait(nestingbegin)[1]))
nest && @setphase(nestbuilding) nest && @setphase(nestbuilding)
return return
end end
# look for a mate among the neighbouring birds, or move randomly # look for a mate among the neighbouring birds, or move randomly
for n in @neighbours(50) for n in @neighbours(50)
if n.sex != @trait(sex) && n.mate == -1 if n.sex != @trait(sex) && n.phase == "mating" && n.mate == -1
@trait(mate) = n.id @trait(mate) = n.id
n.mate = @trait(id) n.mate = @trait(id)
@debug "$(animalid(@trait(id))) and $(animalid(n.id)) have mated." @debug "$(animalid(animal)) and $(animalid(n)) have mated."
return return
end end
end end
...@@ -154,26 +155,35 @@ At the moment, this implementation is still in development. ...@@ -154,26 +155,35 @@ At the moment, this implementation is still in development.
Females select a location and build a nest. Males do nothing. (Sound familiar?) Females select a location and build a nest. Males do nothing. (Sound familiar?)
""" """
@phase nestbuilding begin @phase nestbuilding begin
if !@isalive(@trait(mate))
@setphase(nonbreeding)
return
end
if @trait(sex) == female if @trait(sex) == female
if isempty(@trait(nest)) if isempty(@trait(nest))
# try to find a nest in the neighbourhood, or move on # try to find a nest in the neighbourhood, or move on
nest = @randompixel(10, @trait(habitats)) nestlocation = @randompixel(10, @trait(habitats))
if isnothing(nest) if isnothing(nestlocation)
nest = ()
@randomwalk(20) @randomwalk(20)
else else
# if we've found a location, start the clock on the building time # if we've found a location, start the clock on the building time
# (building time doubles for the first nest of the year) # (building time doubles for the first nest of the year)
@trait(nest) = nestlocation
@trait(nestcompletion) = @rand(nestbuildingtime) @trait(nestcompletion) = @rand(nestbuildingtime)
month(model.date) == 4 && (@trait(nestcompletion) *= 2) month(model.date) == 4 && (@trait(nestcompletion) *= 2)
@debug "$(animalid(animal)) is building a nest."
end end
else else
# wait while nest is being built, then lay eggs and go to next phase # wait while nest is being built, then lay eggs and go to next phase
if @trait(nestcompletion) > 0 if @trait(nestcompletion) > 0
@trait(nestcompletion) -= 1 @trait(nestcompletion) -= 1
else else
#XXX more accurately, a female lays one egg per day, not all at once
@trait(clutch) = @reproduce(@trait(mate), @rand(eggsperclutch)) @trait(clutch) = @reproduce(@trait(mate), @rand(eggsperclutch))
@animal(@trait(mate)).clutch = @trait(clutch) @animal(@trait(mate)).clutch = @trait(clutch)
for c in @trait(clutch) #FIXME find a cleaner solution for this
initskylark(@animal(c), model)
end
@setphase(breeding) @setphase(breeding)
end end
end end
...@@ -224,7 +234,7 @@ function initskylark(animal::Animal, model::AgentBasedModel) ...@@ -224,7 +234,7 @@ function initskylark(animal::Animal, model::AgentBasedModel)
@migrate(returndate) @migrate(returndate)
end end
# set individual life-history parameters that are defined as ranges for the species # set individual life-history parameters that are defined as ranges for the species
@trait(nestlingtime) = @rand(@trait(nestlingtime)) @trait(nestlingtime) = @rand(@trait(nestlingtime)) #FIXME no effect?
@trait(fledglingtime) = @rand(@trait(fledglingtime)) @trait(fledglingtime) = @rand(@trait(fledglingtime))
#TODO other stuff? #TODO other stuff?
end end
......
...@@ -184,8 +184,8 @@ habitatdescriptor (create this using [`@habitat`](@ref)). ...@@ -184,8 +184,8 @@ habitatdescriptor (create this using [`@habitat`](@ref)).
""" """
function randompixel(pos::Tuple{Int64,Int64}, model::AgentBasedModel, range::Int64=1, function randompixel(pos::Tuple{Int64,Int64}, model::AgentBasedModel, range::Int64=1,
habitatdescriptor::Function=(pos,model)->nothing) habitatdescriptor::Function=(pos,model)->nothing)
for x in @shuffle!((pos[1]-range):(pos[1]+range)) for x in @shuffle!(collect((pos[1]-range):(pos[1]+range)))
for y in @shuffle!((pos[2]-range):(pos[2]+range)) for y in @shuffle!(collect((pos[2]-range):(pos[2]+range)))
!inbounds((x,y), model) && continue !inbounds((x,y), model) && continue
habitatdescriptor((x,y), model) && return (x,y) habitatdescriptor((x,y), model) && return (x,y)
end end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment