CViM / src / model / utils.jl
utils.jl
Raw
"""
    ids_by_type(T::DataType, model::ABM) → ids

Returns ids of agents per type defined as structs.
"""
function ids_by_type(T::DataType, model::ABM)
    ids = Int[]
    for id in Schedulers.randomly(model)
        if model[id] isa T
            push!(ids, id)
        end
    end
    return ids
end

"""
    SFC_checks!(model; explosive::Bool = false) → nothing

Checks for Stock-Flow Consistency. Allows for explosive dynamics through the Bool variable `explosive` which 
is set to `false` by default.
"""
function SFC_checks!(model; explosive::Bool = false)
    GDP = 
        if explosive
            sum(a.GDP for a in allagents(model) if a isa Firm)
        else
            1.0
        end
    SFC_checks_net_worth!(model, GDP)
    SFC_checks_aggregate!(model, GDP)
    SFC_checks_balance!(model, GDP)
    SFC_explosive!(model, GDP)
    return nothing
end

"""
    SFC_checks_net_worth!(model; tol::Float64 = 1e-06) → nothing

Checks that the net worth of all sectors corresponds to the nominal value of tangible assets, i.e. capital.
"""
function SFC_checks_net_worth!(model, GDP; tol::Float64 = 1e-06)
    networth = sum(a.networth for a in allagents(model) if a isa Government) - 
            sum(a.networth for a in allagents(model) if !isa(a, Government))
    if abs(networth) - abs(sum(a.inventories for a in allagents(model) if a isa Firm)) > tol * GDP
        @warn """
        Stock-flow error at $(model.step) for $(tol * GDP) tolerance level - Net Worth!
        Check whether the model explodes. To do so, modify in the `model_step` function the keyword of `SFC_checks!` as 
        `SFC_checks!(model; explosive = true)` and run the model checks again. If the problem persists, check the model or 
        initial values and sequence of events again.
        """
    end
    return nothing
end

"""
    SFC_checks_balance!(model; tol::Float64 = 1e-06) → nothing

Checks that net lending/borrowing positions of all sectors sum to zero.
"""
function SFC_checks_balance!(model, GDP; tol::Float64 = 1e-06)
    balance = sum(a.balance_current for a in allagents(model))

    if abs(balance) > tol * GDP
        println(abs(balance))
        @warn """ 
        Stock-flow error at $(model.step) for $(tol * GDP) tolerance level - Sectoral Balances!
        Check whether the model explodes. To do so, modify in the `model_step` function the keyword of `SFC_checks!` as 
        `SFC_checks!(model; explosive = true)` and run the model checks again. If the problem persists, check the model or 
        initial values and sequence of events again.
        """
    end
    return nothing
end
"""
    SFC_explosive!(model, GDP; tol::Float64 = 1e-06) → nothing

Checks that the SFC structure holds despite explosive dynamics.
"""
function SFC_explosive!(model, GDP; tol::Float64 = 1e-06)
    tot_balance = sum(a.balance_current for a in allagents(model)) + 
        sum(a.balance_capital for a in allagents(model) if a isa Firm || a isa CentralBank || a isa Bank)

    if abs(tot_balance)/GDP > tol
        println(abs(tot_balance))
        @warn """ 
        Stock-flow error at $(model.step) for $(tol * GDP) tolerance level - Explosive SFC issue!
        Check whether the model explodes. To do so, modify in the `model_step` function the keyword of `SFC_checks!` as 
        `SFC_checks!(model; explosive = true)` and run the model checks again. If the problem persists, check the model or 
        initial values and sequence of events again.
        """
    end
    return nothing
end

"""
    SFC_checks_aggregate!(model, GDP; tol::Float64 = 1e-06) → nothing

Checks the aggregation of variables between aggregate sectors and disaggregated ones.
"""
function SFC_checks_aggregate!(model, GDP; tol::Float64 = 1e-06)
    # check for aggregation errors
    Df = sum(a.deposits for a in allagents(model) if a isa Bank && a.type == :business) - 
         sum(a.deposits for a in allagents(model) if a isa Firm)
    Dh = sum(a.deposits for a in allagents(model) if a isa Bank && a.type == :commercial) - 
            sum(a.deposits for a in allagents(model) if a isa Household) 
    iDf = sum(a.deposits_interests for a in allagents(model) if a isa Firm) - 
        sum(a.deposits_interests for a in allagents(model) if a isa Bank && a.type == :business)
    iDh = sum(a.deposits_interests for a in allagents(model) if a isa Household) - 
        sum(a.deposits_interests for a in allagents(model) if a isa Bank && a.type == :commercial)
    Lf = sum(a.loans for a in allagents(model) if a isa Bank && a.type == :business) - sum(a.loans for a in allagents(model) if a isa Firm)
    iLf = sum(a.loans_interests for a in allagents(model) if a isa Firm) - 
        sum(a.loans_interests for a in allagents(model) if a isa Bank && a.type == :business)
    Lh = sum(a.loans for a in allagents(model) if a isa Bank && a.type == :commercial) - sum(a.loans for a in allagents(model) if a isa Household)
    iLh = sum(a.loans_interests for a in allagents(model) if a isa Household) - 
        sum(a.loans_interests for a in allagents(model) if a isa Bank && a.type == :commercial)
    B = sum(a.bills for a in allagents(model) if a isa Government) - sum(a.bills for a in allagents(model) if a isa CentralBank || a isa Bank)
    iB = model.ib * sum(a.bills_prev for a in allagents(model) if a isa CentralBank) + sum(a.bills_interests for a in allagents(model) if a isa Bank) - 
        model.ib * sum(a.bills_prev for a in allagents(model) if a isa Government)
    C = sum(a.consumption for a in allagents(model) if a isa Household) - sum(a.consumption for a in allagents(model) if a isa Firm)
    W = sum(a.wages for a in allagents(model) if a isa Household) - sum(a.wages for a in allagents(model) if a isa Firm)
    H = sum(a.hpm for a in allagents(model) if a isa CentralBank) - sum(a.hpm for a in allagents(model) if a isa Bank)
    iH = sum(a.hpm_interests for a in allagents(model) if a isa Bank) - model.icb * sum(a.hpm_prev for a in allagents(model) if a isa CentralBank)
    A = sum(a.advances for a in allagents(model) if a isa CentralBank) - sum(a.advances for a in allagents(model) if a isa Bank)
    iA = sum(a.advances_interests for a in allagents(model) if a isa Bank) - model.icb * sum(a.advances_prev for a in allagents(model) if a isa CentralBank)
    Rl = sum(a.lending_facility for a in allagents(model) if a isa CentralBank) - sum(a.lending_facility for a in allagents(model) if a isa Bank)
    iRl = model.icbl * sum(a.lending_facility_prev for a in allagents(model) if a isa CentralBank) - 
        sum(a.lending_facility_interests for a in allagents(model) if a isa Bank)
    Rd = sum(a.deposit_facility for a in allagents(model) if a isa CentralBank) - sum(a.deposit_facility for a in allagents(model) if a isa Bank)
    iRd = model.icbd * sum(a.deposit_facility_prev for a in allagents(model) if a isa CentralBank) - 
        sum(a.deposit_facility_interests for a in allagents(model) if a isa Bank)
    IB = if !isempty([id for id in allids(model) if model[id] isa Bank && model[id].status == :lender]) && 
                !isempty([id for id in allids(model) if model[id] isa Bank && model[id].status == :borrower])
            sum(a.ON_interbank + a.Term_interbank for a in allagents(model) if a isa Bank && a.status == :lender) - 
                sum(a.ON_interbank + a.Term_interbank for a in allagents(model) if a isa Bank && a.status == :borrower)
        else
            0.0
        end

    if abs(Df) > tol * GDP || abs(Dh) > tol * GDP || abs(Lf) > tol * GDP || abs(Lh) > tol * GDP || abs(B) > tol * GDP || abs(iB) > tol * GDP ||  abs(C) > tol * GDP ||  
        abs(W) > tol * GDP || abs(H) > tol * GDP || abs(IB) > tol * GDP || abs(iLf) > tol * GDP || abs(Rl) > tol * GDP || abs(iRl) > tol * GDP || abs(Rd) > tol * GDP ||
        abs(iRd) > tol * GDP || abs(iLh) > tol * GDP || abs(iDf) > tol * GDP ||  abs(iDh) > tol * GDP || abs(iH) > tol * GDP || abs(A) > tol * GDP || abs(iA) > tol * GDP
        @warn """ 
            Stock-flow error at $(model.step) for $(tol * GDP) tolerance level - Aggregation!
            Check whether the model explodes. To do so, modify in the `model_step` function the keyword of `SFC_checks!` as 
            `SFC_checks!(model; explosive = true)` and run the model checks again. If the problem persists, check the model or 
            initial values and sequence of events again.
            """
    end
    return nothing
end