SIR Standard Stratification

using StockFlow
using StockFlow.Syntax

using Catlab
using Catlab.CategoricalAlgebra
using LabelledArrays
using OrdinaryDiffEq
using Plots

using Catlab.Graphics
using Catlab.Programs
using Catlab.Theories
using Catlab.WiringDiagrams

using Catlab.Graphics.Graphviz: Html
using Catlab.Graphics.Graphviz

Functions for graphing typed Petri nets

colors_vflow = ["antiquewhite4","antiquewhite", "gold", "saddlebrown", "slateblue", "blueviolet", "olive"]
colors_s = ["deeppink","darkorchid","darkred","coral"] # red series
colors_sv = ["cornflowerblue","cyan4","cyan","chartreuse"] # green and blue series
colors_p = ["gold","gold4","darkorange1","lightgoldenrod","goldenrod"] # yellow and orange

flatten(fname::Symbol) = "$fname"

function flatten(fname::Tuple)
    names = split(replace(string(fname), "("=>"", ")"=>"", ":"=>""), ",")
    for i in 1:length(names)
        name = strip(names[i])
        if name[1:2] == "id"
            continue
        end
        return name
    end
    return "id"
end

def_stock(typed_StockFlow::ACSetTransformation, colors) =
  (p,s) -> ("s$s", Attributes(:label=>sname(p,s) isa Tuple where T ? Html(replace(string(sname(p,s)), ":"=>"", "," => "<BR/>", "("=>"", ")"=>"")) : "$(sname(p,s))",
                                     :shape=>"square",
                                     :color=>"black",
                                     :style=>"filled",
                                     :fillcolor=>colors[typed_StockFlow[:S](s)]))

def_parameter(typed_StockFlow::ACSetTransformation, colors) =
(p, pp) -> ("p$pp", Attributes(:label=>pname(p,pp) isa Tuple where T ? Html(replace(string(pname(p,pp)), ":"=>"", "," => "<BR/>", "("=>"", ")"=>"")) : "$(pname(p,pp))",
                                     :shape=>"circle",
                                     :color=>colors[typed_StockFlow[:P](pp)],
                                     :fontcolor=>colors[typed_StockFlow[:P](pp)]))

def_auxiliaryVF(typed_StockFlow::ACSetTransformation, colors)=
  (p, v) -> ("v$v", Attributes(:label=>make_v_expr(p,v) isa Tuple where T ? Html(replace(string(make_v_expr(p,v)), ":"=>"", "," => "<BR/>", "("=>"", ")"=>"")) : "$(make_v_expr(p,v))",
                                          :shape=>"plaintext",
                                          :fontcolor=>colors[typed_StockFlow[:V](v)]))


def_sumV(typed_StockFlow::ACSetTransformation, colors) =
  (p, sv) -> ("sv$sv", Attributes(:label=>svname(p,sv) isa Tuple where T ? Html(replace(string(svname(p,sv)), ":"=>"", "," => "<BR/>", "("=>"", ")"=>"")) : "$(svname(p,sv))",
                                       :shape=>"circle",
                                       :color=>"black",
                                       :fillcolor=>colors[typed_StockFlow[:SV](sv)],
                                       :style=>"filled"))


def_flow_V(typed_StockFlow::ACSetTransformation, colors)=
  (p, us, ds, v, f) -> begin
    labelfontsize = "6"
    colorType = colors[typed_StockFlow[:F](f)]
    color = "$colorType"*":invis:"*"$colorType"
    arrowhead = "none"
    splines = "ortho"
    return ([us, "v$v"],Attributes(:label=>"", :labelfontsize=>labelfontsize, :color=>color, :arrowhead=>arrowhead, :splines=>splines)),
           (["v$v", ds],Attributes(:label=>Html(flatten(fname(p,f))), :labelfontsize=>labelfontsize, :color=>color, :splines=>splines))
end

def_flow_noneV(typed_StockFlow::ACSetTransformation, colors)=
  (p, us, ds, f) -> begin
     colorType = colors[typed_StockFlow[:F](f)]
     color = "$colorType"*":invis:"*"$colorType"
     ([us, ds],Attributes(:label=>Html(flatten(fname(p,f))), :labelfontsize=>"6", :color=>color))
end

def_flow_V(colors = colors_vflow)=
  (p, us, ds, v, f) -> begin
    labelfontsize = "6"
    colorType = colors[f]
    color = "$colorType"*":invis:"*"$colorType"
    arrowhead = "none"
    splines = "ortho"
    return ([us, "v$v"],Attributes(:label=>"", :labelfontsize=>labelfontsize, :color=>color, :arrowhead=>arrowhead, :splines=>splines)),
           (["v$v", ds],Attributes(:label=>Html(flatten(fname(p,f))), :labelfontsize=>labelfontsize, :color=>color, :splines=>splines))

end

def_flow_noneV(colors = colors_vflow)=
  (p, us, ds, f) -> begin
     colorType = colors[f]
     color = "$colorType"*":invis:"*"$colorType"
     ([us, ds],Attributes(:label=>Html(flatten(fname(p,f))), :labelfontsize=>"6", :color=>color))
end

GraphF_typed(typed_StockFlow::ACSetTransformation, colors_vflow = colors_vflow, colors_s = colors_s, colors_p = colors_p, colors_sv = colors_sv; schema::String="C", type::String="SFVL", rd::String="LR") = GraphF(dom(typed_StockFlow),
    make_stock = def_stock(typed_StockFlow, colors_s), make_auxiliaryV=def_auxiliaryVF(typed_StockFlow, colors_vflow), make_sumV=def_sumV(typed_StockFlow, colors_sv),
    make_flow_V=def_flow_V(typed_StockFlow, colors_vflow), make_flow_noneV=def_flow_noneV(typed_StockFlow, colors_vflow),make_parameter=def_parameter(typed_StockFlow, colors_p),schema=schema, type=type, rd=rd
)
GraphF_typed (generic function with 5 methods)

using StockFlow: defstock, defparameter, defauxiliaryVF, defsumV, defflowV

Functions for graphing typed Petri nets

colorsvflow = ["antiquewhite4","antiquewhite", "gold", "saddlebrown", "slateblue", "blueviolet", "olive"] colorss = ["deeppink","darkorchid","darkred","coral"] # red series colorssv = ["cornflowerblue","cyan4","cyan","chartreuse"] # green and blue series colorsp = ["gold","gold4","darkorange1","lightgoldenrod","goldenrod"] # yellow and orange

flatten(fname::Symbol) = "fname"

function flatten(fname::Tuple) names = split(replace(string(fname), "("=>"", ")"=>"", ":"=>""), ",") for i in 1:length(names) name = strip(names[i]) if name[1:2] == "id" continue end return name end return "id" end

defflownoneV(colors = colors_vflow)= (p, us, ds, f) -> begin colorType = colors[f] color = "$colorType"*":invis:"*"$colorType" ([us, ds],Attributes(:label=>Html(flatten(fname(p,f))), :labelfontsize=>"6", :color=>color)) end

GraphFtyped(typedStockFlow::ACSetTransformation, colorsvflow = colorsvflow, colorss = colorss, colorsp = colorsp, colorssv = colorssv; schema::String="C", type::String="SFVL", rd::String="LR") = GraphF(dom(typedStockFlow), makestock = defstock(typedStockFlow, colorss), makeauxiliaryV=defauxiliaryVF(typedStockFlow, colorsvflow), makesumV=defsumV(typedStockFlow, colorssv), makeflowV=defflowV(typedStockFlow, colorsvflow), makeflownoneV=defflownoneV(typedStockFlow, colorsvflow),makeparameter=defparameter(typedStockFlow, colors_p),schema=schema, type=type, rd=rd )

s_type = @stock_and_flow begin
    :stocks
    pop

    :parameters
    c
    β
    rFstOrder
    rAge

    :dynamic_variables
    v_prevalence = pop / N
    v_meanInfectiousContactsPerS = c * v_prevalence
    v_perSIncidenceRate = β * v_meanInfectiousContactsPerS
    v_inf = pop * v_perSIncidenceRate
    v_fstOrder = pop * rFstOrder
    v_aging = pop * rAge

    :flows
    pop => f_inf(v_inf) => pop
    pop => f_fstOrder(v_fstOrder) => pop
    pop => f_aging(v_aging) => pop


    :sums
    N = [pop]


end


GraphF(s_type)

GraphF_typed(id(s_type))
Example block output

eliminate the attribute of name to enable pass the natural check only eliminate the name, the other two attributes should be okay

s_type = map(s_type, Name=name->nothing, Op=op->nothing, Position=pos->nothing);

s, = parts(s_type, :S)
N, = parts(s_type, :SV)
lsn, = parts(s_type, :LS)
f_inf, f_fstorder, f_aging = parts(s_type, :F)
i_inf, i_fstorder, i_aging = parts(s_type, :I) # note, different order from previous in both inflow and outflow
o_inf, o_fstorder, o_aging = parts(s_type, :O)
v_IN, v_cIN, v_betacIN, v_inf, v_fstOrder, v_aging = parts(s_type, :V)
lv_IN1, lv_inf1, lv_fstOrder1, lv_aging1 = parts(s_type, :LV)
lsv_IN2, = parts(s_type, :LSV)
p_c, p_beta, p_rfstOrder, p_rAge = parts(s_type, :P)
lvv_cIN2, lvv_betacIN2, lvv_inf2 = parts(s_type, :LVV)
lpv_cIN1, lpv_betacIN1, lpv_fstOrder2, lpv_aging2 = parts(s_type, :LPV)

sir = @stock_and_flow begin
    :stocks
    S
    I
    R

    :parameters
    c
    β
    rRec
    rAge

    :dynamic_variables
    v_prevalence = I / N
    v_meanInfectiousContactsPerS = c * v_prevalence
    v_perSIncidenceRate = β * v_meanInfectiousContactsPerS
    v_newInfections = S * v_perSIncidenceRate
    v_newRecovery = I * rRec
    v_idS = S * rAge
    v_idI = I * rAge
    v_idR = R * rAge

    :flows
    S => f_idS(v_idS) => S
    S => f_inf(v_newInfections) => I
    I => f_idI(v_idI) => I
    I => f_rec(v_newRecovery) => R
    R => f_idR(v_idR) => R

    :sums
    N = [S, I, R]


end

GraphF(sir)

typed_aggregate_model=ACSetTransformation(sir, s_type,
  S = [s,s,s],
  SV = [N],
  LS = [lsn,lsn,lsn],
  F = [f_aging, f_inf, f_aging, f_fstorder, f_aging],
  I = [i_aging, i_inf, i_aging, i_fstorder, i_aging], #i_inf, i_fstorder, i_aging
  O = [o_aging, o_inf, o_aging, o_fstorder, o_aging],
  V = [v_IN, v_cIN, v_betacIN, v_inf, v_fstOrder, v_aging, v_aging, v_aging],
  LV = [lv_IN1, lv_inf1, lv_fstOrder1, lv_aging1, lv_aging1, lv_aging1],
  LSV = [lsv_IN2],
  P = [p_c, p_beta, p_rfstOrder, p_rAge],
  LVV = [lvv_cIN2, lvv_betacIN2, lvv_inf2],
  LPV = [lpv_cIN1, lpv_betacIN1, lpv_fstOrder2, lpv_aging2, lpv_aging2, lpv_aging2],
  Name = name -> nothing, Op=op->nothing, Position=pos->nothing
);
@assert is_natural(typed_aggregate_model)

GraphF_typed(typed_aggregate_model)

age2 = @stock_and_flow begin
    :stocks
    Child
    Adult

    :parameters
    c_C
    β
    r
    rAge
    c_A

    :dynamic_variables
    v_INC = Child / NC
    v_cINC = c_C * v_INC
    v_cβINC = β * v_cINC

    v_infC = Child * v_cβINC
    v_fstC = Child * r
    v_agingC = Child * rAge


    v_INA = Adult / NA
    v_cINA = c_A * v_INA
    v_cβINA = β * v_cINA

    v_infA = Adult * v_cβINA
    v_fstA = Adult * r

    :flows
    Child => f_infC(v_infC) => Child
    Child => f_frsC(v_fstC) => Child
    Child => f_aging(v_agingC) => Adult
    Adult => f_infA(v_infA) => Adult
    Adult => f_frsA(v_fstA) => Adult


    :sums
    NC = [Child]
    NA = [Adult]
end

typed_age_model=ACSetTransformation(age2, s_type,
  S = [s,s],
  SV = [N,N],
  LS = [lsn,lsn],
  F = [f_inf, f_fstorder, f_aging, f_inf, f_fstorder],
  I = [i_inf, i_fstorder, i_aging, i_inf, i_fstorder],
  O = [o_inf, o_fstorder, o_aging, o_inf, o_fstorder],
  V = [v_IN, v_cIN, v_betacIN, v_inf, v_fstOrder, v_aging, v_IN, v_cIN, v_betacIN, v_inf, v_fstOrder],
  LV = [lv_IN1, lv_inf1, lv_fstOrder1, lv_aging1, lv_IN1, lv_inf1, lv_fstOrder1],
  LSV = [lsv_IN2, lsv_IN2],
  P = [p_c, p_beta, p_rfstOrder, p_rAge, p_c],
  LVV = [lvv_cIN2, lvv_betacIN2, lvv_inf2, lvv_cIN2, lvv_betacIN2, lvv_inf2],
  LPV = [lpv_cIN1, lpv_betacIN1, lpv_fstOrder2, lpv_aging2, lpv_cIN1, lpv_betacIN1, lpv_fstOrder2],
  Name = name -> nothing, Op=op->nothing, Position=pos->nothing
);
@assert is_natural(typed_age_model)

GraphF_typed(typed_age_model)

aged_sir = pullback(typed_aggregate_model, typed_age_model) |> apex |> rebuildStratifiedModelByFlattenSymbols;

GraphF(aged_sir)

LS = @stock_and_flow begin
    :stocks
    SChild
    IChild
    SAdult
    IAdult

    :parameters
    cc_C
    cc_A

    :dynamic_variables
    v_prevalencev_INC = IChild / NNC
    v_prevalencev_INA = IAdult / NNA
    v_meanInfectiousContactsPerSv_cINC = cc_C * v_prevalencev_INC
    v_meanInfectiousContactsPerSv_cINA = cc_A * v_prevalencev_INA


    :sums
    NNC = [SChild, IChild]
    NNA = [SAdult, IAdult]


end

GraphF(LS)

IS = @stock_and_flow begin
    :stocks
    SChild
    IChild
    SAdult
    IAdult

    :parameters
    cc_C
    cc_A

    :dynamic_variables
    v_prevalencev_INC = IChild / NNC
    v_prevalencev_INA = IAdult / NNA
    v_meanInfectiousContactsPerSv_cINC = *(cc_C)
    v_meanInfectiousContactsPerSv_cINA = *(cc_A)

    :sums
    NNC = [SChild, IChild]
    NNA = [SAdult, IAdult]


end

GraphF(IS)

RS = @stock_and_flow begin
    :stocks
    SChild
    IChild
    SAdult
    IAdult

    :parameters
    fcc
    fca
    fac
    faa
    cc_C
    cc_A

    :dynamic_variables
    v_prevalencev_INC = IChild / NNC
    v_prevalencev_INA = IAdult / NNA
    v_CCContacts = fcc * v_prevalencev_INC
    v_CAContacts = fca * v_prevalencev_INA

    v_ACContacts = fac * v_prevalencev_INC
    v_AAContacts = faa * v_prevalencev_INA

    v_prevalencev_INC_post = v_CCContacts + v_CAContacts
    v_prevalencev_INA_post = v_ACContacts + v_AAContacts
    v_meanInfectiousContactsPerSv_cINC = cc_C * v_prevalencev_INC_post
    v_meanInfectiousContactsPerSv_cINA = cc_A * v_prevalencev_INA_post

    :sums
    NNC = [SChild, IChild]
    NNA = [SAdult, IAdult]


end


GraphF(RS)

using AlgebraicRewriting
using AlgebraicRewriting: rewrite
const hom = Catlab.CategoricalAlgebra.homomorphism

rule_S = Rule(hom(IS,LS), hom(IS,RS))
aged_sir_rewritten = rewrite(rule_S, aged_sir)
GraphF(aged_sir_rewritten)
Example block output

define values of constant parameters

p_stratified_sir = LVector(
    fcc=0.8, fca=0.2, fac=0.2, faa=0.8, cc_C=0.45, cc_A=0.55,
    ββ=0.8, rRecr=1.0/14.0, rAgerAge=1.0/(15.0*365.0) #which means the child age group is from 0 to 15 years old
)
9-element LabelledArrays.LArray{Float64, 1, Vector{Float64}, (:fcc, :fca, :fac, :faa, :cc_C, :cc_A, :ββ, :rRecr, :rAgerAge)}:
      :fcc => 0.8
      :fca => 0.2
      :fac => 0.2
      :faa => 0.8
     :cc_C => 0.45
     :cc_A => 0.55
       :ββ => 0.8
    :rRecr => 0.07142857142857142
 :rAgerAge => 0.00018264840182648402

define initial values for stocks

u0_stratified_sir = LVector(
    SChild=990.0, IChild=10.0, RChild=0.0,
    SAdult=4900.0, IAdult=10.0, RAdult=0.0
);

prob_stratified_sir = ODEProblem(vectorfield(aged_sir_rewritten),u0_stratified_sir,(0.0,100.0),p_stratified_sir);
sol_stratified_sir = solve(prob_stratified_sir,Tsit5(),abstol=1e-8);
plot(sol_stratified_sir)
Example block output

to have the figures plotted fix to the wider of the cells

HTML("""
<style>
.output_svg div{
  width: 100% !important;
  height: 100% !important;
}
</style>
""")