Skip to content
Snippets Groups Projects
Select Git revision
  • a1e8f923bf0c52812939864288b1f8cddfc5221c
  • master default protected
  • development
  • precompile-statements
  • precompile-tools
  • tmp-faster-loading
  • skylark
  • testsuite
  • code-review
  • v0.7.0
  • v0.6.1
  • v0.6.0
  • v0.5.5
  • v0.5.4
  • v0.5.3
  • v0.5.2
  • v0.2
  • v0.3.0
  • v0.4.1
  • v0.5
20 results

skylark.jl

Blame
  • Daniel Vedder's avatar
    xo30xoqa authored
    Also updated CONTRIBUTORS.md, added a "none" option for
    `core.logoutput`, and added type annotations to the Skylark struct
    a1e8f923
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    skylark.jl 9.84 KiB
    ### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
    ###
    ### This file holds the code for the Eurasian Skylark (Alauda arvensis).
    ###
    
    skylarkhabitat = @habitat((@landcover() == grass ||
                               # settle on grass or arable land (but not maize)
                               (@landcover() == agriculture && @cropname() != "maize")) &&
                              @distancetoedge() >= 5) # at least 50m from other habitats
    #XXX this ought to check for distance to forest and builtup,
    # but that's very expensive (see below)
    # @distanceto(forest) > 5 && # at least 50m from forest edges
    # @distanceto(builtup) > 5) # and from anthropogenic structures
    
    """
        Skylark
    
    *Alauda arvensis* is a common and charismatic species of agricultural landscapes.
    At the moment, this implementation is still in development.
    
    **Sources:**
        - Bauer, H.-G., Bezzel, E., & Fiedler, W. (Eds.). (2012).
          Das Kompendium der Vögel Mitteleuropas: Ein umfassendes
          Handbuch zu Biologie, Gefährdung und Schutz (Einbändige
          Sonderausg. der 2., vollständig überarb. und erw. Aufl. 2005).
          AULA-Verlag
        - Glutz von Blotzheim, Urs N. (Ed.). (1985). Handbuch der
          Vögel Mitteleuropas. Bd. 10. Passeriformes (Teil 1)
          1. Alaudidae - Hirundidae. AULA-Verlag, Wiesbaden.
          ISBN 3-89104-019-9
    """
    @species Skylark begin
        #XXX use Unitful.jl
        eggtime::Int64 = 11 # 11 days from laying to hatching
        eggpredationmortality::Float64 = 0.03 # per-day egg mortality from predation
        nestharvestmortality::Float64 = 0.9 # egg/nestling mortality after a harvest event (XXX guess)
    
        nestlingtime::Union{Int64,UnitRange{Int64}} = 7:11 # 7-11 days from hatching to leaving nest
        nestlingpredationmortality::Float64 = 0.03 # per-day nestling mortality from predation
    
        fledglingtime::Union{Int64,UnitRange{Int64}} = 25:30 # 25-30 days from hatching to independence
        fledglingharvestmortality::Float64 = 0.5 # fledgling mortality after harvest
        fledglingpredationmortality::Float64 = 0.01 # per-day fledgling mortality from predation
        firstyearmortality::Float64 = 0.38 # total mortality in the first year after independence
        
        migrationdates::Tuple = () # is defined by each individual in @create(Skylark)
        migrationmortality::Float64 = 0.33 # chance of dying during the winter
    
        mate::Int64 = -1 # the agent ID of the mate (-1 if none)
        nest::Tuple = () # coordinates of current nest
        nestingbegin::Tuple{Int64,Int64} = (April, 10) # begin nesting in the middle of April
        nestbuildingtime::Union{Int64,UnitRange{Int64}} = 4:5 # 4-5 days needed to build a nest (doubled for first nest)
        nestcompletion::Int64 = 0 # days left until the nest is built
        eggsperclutch::Union{Int64,UnitRange{Int64}} = 2:5 # 2-5 eggs laid per clutch
        clutch::Vector{Int64} = Vector{Int64}() # IDs of offspring in current clutch
        breedingdelay::Int64 = 18 # wait 18 days after hatching to start a new brood
        nestingend::Int64 = July # last month of nesting
        
        habitats::Function = skylarkhabitat
    end
    
    """
    As an egg, simply check for mortality and hatching.
    """
    @phase Skylark egg begin
        @kill(self.eggpredationmortality, "predation")
        @respond(harvesting, @kill(self.nestharvestmortality, "harvest"))
        if self.age == self.eggtime
            @setphase(nestling)
        end
    end
    
    """
    As a nestling, simply check for mortality and fledging.
    """
    @phase Skylark nestling begin
        #TODO add feeding & growth
        @kill(self.nestlingpredationmortality, "predation")
        @respond(harvesting, @kill(self.nestharvestmortality, "harvest"))
        if self.age == self.nestlingtime+self.eggtime
            @setphase(fledgling)
        end
    end
    
    """
    As a fledgling, move around a little, but mainly wait for maturity and
    check mortality.
    """
    @phase Skylark fledgling begin
        #TODO add feeding & growth
        @kill(self.fledglingpredationmortality, "predation")
        @walk("random") #TODO add movement following the parents
        if self.age == self.fledglingtime+self.eggtime
            @kill(self.firstyearmortality, "first year mortality") #XXX mechanistic?
            @setphase(nonbreeding)
        end
    end
    
    """
    As a non-breeding adult, move around with other individuals and check for migration.
    """
    @phase Skylark nonbreeding begin
        # flocking behaviour - follow a random neighbour or move randomly
        #TODO add feeding and mortality, respect habitat when moving
        neighbours = @neighbours(10) #XXX magic number
        isempty(neighbours) ?
            @walk("random", 5) :
            @follow(@rand(neighbours), 2)
        # check if the bird migrates
        leave, arrive = self.migrationdates
        m, d = monthday(model.date)
        migrate = (((m < arrive[1]) || (m == arrive[1] && d < arrive[2])) ||
                   ((m > leave[1]) || (m == leave[1] && d >= leave[2])))
        if migrate #FIXME not all migrate?
            @kill(self.migrationmortality, "migration")
            returndate = Date(year(model.date)+1, arrive[1], arrive[2])
            @setphase(mating)
            @migrate(returndate)
        end
    end
    
    """
    Move around until a mate is found.
    """
    @phase Skylark mating begin
        # if we've found a mate, wait for nesting begin and then go to the next phase
        if self.mate != -1
            if !@isalive(self.mate)
                self.mate = -1
                return
            end
            m, d = monthday(model.date)
            nest = ((m == self.nestingbegin[1] && d >= self.nestingbegin[2]
                     && @chance(0.05)) || (m > self.nestingbegin[1])) #XXX why the chance?
            nest && @setphase(nestbuilding)
            return
        end
        # look for a mate among the neighbouring birds, or move randomly
        for n in @neighbours(50) #XXX magic number
            if n.sex != self.sex && n.phase == mating && n.mate == -1
                self.mate = n.id
                n.mate = self.id
                @debug "$(animalid(self)) and $(animalid(n)) have mated."
                return
            end
        end
        #@debug("$(animalid(self)) didn't find a mate.")
        @walk("random", 10) #XXX magic number
    end
    
    """
    Females select a location and build a nest. Males do nothing. (Sound familiar?)
    """
    @phase Skylark nestbuilding begin
        if !@isalive(self.mate)
            self.mate = -1
            @setphase(nonbreeding)
            return
        end
        if self.sex == female
            if isempty(self.nest)
                # try to find a nest in the neighbourhood, or move on
                nestlocation = @randompixel(10, self.habitats) #XXX magic number
                if isnothing(nestlocation)
                    @walk("random", 20) #XXX magic number
                else
                    # if we've found a location, start the clock on the building time
                    # (building time doubles for the first nest of the year)
                    self.nest = nestlocation
                    self.nestcompletion = @rand(self.nestbuildingtime)
                    month(model.date) == self.nestingbegin[1] && (self.nestcompletion *= 2)
                    @debug "$(animalid(self)) is building a nest."
                end
            else
                # wait while nest is being built, then lay eggs and go to next phase
                if self.nestcompletion > 0
                    self.nestcompletion -= 1
                else
                    #XXX more accurately, a female lays one egg per day, not all at once
                    @reproduce(@rand(self.eggsperclutch),self.mate)
                    @setphase(breeding)
                end
            end
        else
            # males stay near the female
            mate = @animal(self.mate)
            @follow(mate, 5)
            mate.phase == breeding && @setphase(breeding)
        end
    end
    
    """
    Do lots of foraging (not yet implemented).
    """
    @phase Skylark breeding begin
        #TODO forage (move random)
        for offspring in self.clutch
            # check if offspring are still alive and juvenile, else remove from clutch
            if !@isalive(offspring) || @animal(offspring).phase == nonbreeding
                filter!(x -> x != offspring, self.clutch)
            end
        end
        # if all young have fledged, move to nonbreeding (if it's July) or breed again
        if isempty(self.clutch)
            self.nest = ()
            month(model.date) >= self.nestingend ?
                @setphase(nonbreeding) :
                @setphase(nestbuilding)
        end
    end
    
    """
        migrationdates(skylark, model)
    
    Select the dates on which this skylark will leave for / return from its migration,
    based on observed migration patterns.
    """
    function migrationdates(skylark::Animal, model::SimulationModel)
        #TODO this ought to be temperature-dependent and dynamic
        minleave = skylark.sex == female ? (September, 15) : (October, 1)
        minarrive = skylark.sex == male ? (February, 15) : (March, 1)
        deltaleave = @rand(0:45) #XXX ought to be normally distributed
        deltaarrive = @rand(0:15) #XXX ought to be normally distributed
        leave = monthday(Date(2001, minleave[1], minleave[2]) + Day(deltaleave))
        arrive = monthday(Date(2001, minarrive[1], minarrive[2]) + Day(deltaarrive))
        (leave, arrive)
    end
    
    """
    Initialise a skylark individual. Selects migration dates and checks if the bird
    should currently be on migration. Also sets other individual-specific variables.
    """
    @create Skylark begin
        @debug "Added $(animalid(self)) at $(self.pos)"
        # calculate migration dates for this individual
        self.migrationdates = migrationdates(self, model)
        leave, arrive = self.migrationdates
        m, d = monthday(model.date)
        migrate = (((m < arrive[1]) || (m == arrive[1] && d < arrive[2])) ||
                   ((m > leave[1]) || (m == leave[1] && d >= leave[2])))
        if migrate
            returndate = Date(year(model.date), arrive[1], arrive[2])
            model.date != @param(core.startdate) && (returndate += Year(1))
            @migrate(returndate)
        end
        # set individual life-history parameters that are defined as ranges for the species
        self.nestlingtime = @rand(self.nestlingtime)
        self.fledglingtime = @rand(self.fledglingtime)
        #TODO other stuff?
    end
    
    @populate Skylark begin
        habitat = skylarkhabitat
        initphase = mating
        birthphase = egg
        popdensity = 300 #XXX use Unitful.jl
        pairs = true
    end