Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ecologicaldata.jl 5.38 KiB
### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
###
### This file includes the functions for collecting ecological output data, which
### are then passed on to the `core/output.jl`.
###

"""
    initecologicaldata()

Create output files for each data group collected by the nature model.
"""
function initecologicaldata(model::SimulationModel)
    newdataoutput!(model, "populations", ["Date", "Species", "Abundance"],
                   @param(nature.popoutfreq), savepopulationdata, populationtrends)
    newdataoutput!(model, "individuals", ["Date","ID","X","Y","Species","Sex","Age"],
                   @param(nature.indoutfreq), saveindividualdata, visualisemap)
    initskylarkdata(model)
end

"""
    savepopulationdata(model)

Return a data table (to be printed to `populations.csv`), giving the current
date and population size for each animal species. May be called never, daily,
monthly, yearly, or at the end of a simulation, depending on the parameter
`nature.popoutfreq`.
"""
function savepopulationdata(model::SimulationModel)
    pops = Dict{String,Int}(s=>0 for s = @param(nature.targetspecies))
    for a in model.animals
        isnothing(a) && continue
        pops[speciesof(a)] += 1
    end
    for m in model.migrants
        pops[speciesof(m.first)] += 1
    end
    data = []
    for p in keys(pops)
        push!(data, [model.date, p, pops[p]])
    end
    data
end

"""
    saveindividualdata(model)

Return a data table (to be printed to `individuals.csv`), listing all
properties of all animal individuals in the model. May be called never, daily,
monthly, yearly, or at the end of a simulation, depending on the parameter
`nature.indoutfreq`. WARNING: Produces very big files!
"""
function saveindividualdata(model::SimulationModel)
    data = []
    for a in model.animals
        isnothing(a) && continue
        push!(data, [model.date,a.id,a.pos[1],a.pos[2],speciesof(a),a.sex,a.age])
    end
    for m in model.migrants
        push!(data, [model.date, m[1].id, -1, -1, speciesof(m[1]), m[1].sex, m[1].age])
    end
    data
end


## SKYLARK DATA OUTPUT

function initskylarkdata(model::SimulationModel)
    newdataoutput!(model, "skylark_abundance",
                   ["Date", "TotalAbundance", "Mating", "Breeding",
                    "Nonbreeding", "Juvenile", "Migrants"],
                   "daily", skylarkabundance, skylarkpopulation)
    # newdataoutput!(model, "skylark_territories", ["Date", "ID", "X", "Y"],
    #                skylarkterritories, "monthly") #TODO add plotting function
    newdataoutput!(model, "skylark_nests", ["Date", "ID", "X", "Y", "Landcover", "Crop"],
                   "monthly", skylarknests) #TODO add plotting function
    # newdataoutput!(model, "skylark_mortality", ["Date", "N", "Cause"],
    #                skylarkmortality, "daily") #TODO add plotting function
end

"""
    skylarkabundance(model)

Save skylark abundance data, including total abundance and demographic data
(abundances of breeding/non-breeding/juvenile/migrated individuals).
"""
function skylarkabundance(model::SimulationModel)
    pops = Dict{String,Int}("TotalAbundance" => 0, "Mating" => 0, "Breeding" => 0,
                            "Nonbreeding" => 0, "Juvenile" => 0, "Migrants" => 0)
    for a in model.animals
        (isnothing(a) || speciesof(a) != "Skylark") && continue
        pops["TotalAbundance"] += 1
        if a.phase == nonbreeding
            pops["Nonbreeding"] += 1
        elseif a.phase in (territorysearch, matesearch) || (a.phase == occupation && a.mate == -1)
            pops["Mating"] += 1
        else
            pops["Breeding"] += 1
        end
        if a.sex == female && a.clutch > 0
            pops["TotalAbundance"] += a.clutch
            pops["Juvenile"] += a.clutch
        end
    end
    for m in model.migrants
        if speciesof(m.first) == "Skylark"
            pops["TotalAbundance"] += 1
            pops["Migrants"] += 1
        end
    end
    return [[model.date, pops["TotalAbundance"], pops["Mating"], pops["Breeding"],
             pops["Nonbreeding"], pops["Juvenile"], pops["Migrants"]]]
end

"""
    skylarkterritories(model)

Return a list of all coordinates occupied by a skylark territory, and the ID of the individual
holding the territory. WARNING: produces very big files.
"""
function skylarkterritories(model::SimulationModel)
    #TODO is there a way of outputting this that takes less space? For example as a landscape-size
    # matrix where each position is the ID of the individual occupying this pixel?
    data = []
    for a in model.animals
        (isnothing(a) || a.sex != male || speciesof(a) != "Skylark" || a.phase != occupation) && continue
        for pos in a.territory
            push!(data, [model.date, a.id, pos[1], pos[2]])
        end
    end
    data
end

"""
    skylarknests(model)

Return a list of coordinates of active skylark nests, and the habitat type they occupy.
"""
function skylarknests(model::SimulationModel)
    data = []
    for a in model.animals
        (isnothing(a) || a.sex != female || speciesof(a) != "Skylark" || isempty(a.nest)) && continue
        push!(data, [model.date, a.id, a.nest[1], a.nest[2],
                     landcover(a.nest, model), cropname(a.nest, model)])
    end
    data 
end

"""
    skylarkmortality(model)


"""
function skylarkmortality(model::SimulationModel)
    #TODO
    #XXX may require an additional feature in output.jl to account for a different output type
end