Skip to content

Commit 9a17f73

Browse files
committed
add ODE ratelaw expressions
1 parent 27d6498 commit 9a17f73

File tree

4 files changed

+57
-29
lines changed

4 files changed

+57
-29
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
1313
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1414
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
1515
NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
16+
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
1617
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
1718
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1819
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"

src/ModelingToolkit.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ using DiffEqBase, Distributed
44
using StaticArrays, LinearAlgebra, SparseArrays
55
using Latexify, Unitful
66
using MacroTools
7+
using Parameters: @unpack
78

89
using Base.Threads
910
import MacroTools: splitdef, combinedef, postwalk, striplines
Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,97 @@
1-
struct Reaction
1+
struct Reaction{T <: Number}
22
rate
3-
reactants::Vector{Operation}
3+
substrates::Vector{Operation}
44
products::Vector{Operation}
5+
substoich::Vector{T}
6+
prodstoich::Vector{T}
57
end
68

79
struct ReactionSystem <: AbstractSystem
810
eqs::Vector{Reaction}
911
iv::Variable
10-
states::Vector{Variable}
11-
ps::Vector{Variable}
12+
species::Vector{Variable}
13+
params::Vector{Variable}
1214
name::Symbol
1315
systems::Vector{ReactionSystem}
1416
end
1517

16-
function ReactionSystem(eqs,iv,dvs,ps;
18+
function ReactionSystem(eqs, iv, species, params;
1719
systems = ReactionSystem[],
18-
name=gensym(:ReactionSystem))
19-
ReactionSystem(eqs,iv,convert.(Variable,dvs),convert.(Variable,ps),name,systems)
20+
name = gensym(:ReactionSystem))
21+
22+
ReactionSystem(eqs, iv, convert.(Variable,species),
23+
convert.(Variable,params), name, systems)
2024
end
2125

22-
# TODO: Make it do the combinatorics stuff
23-
reaction_expr(reactants) = *(reactants...)
26+
# Calculate the ODE rate law
27+
function oderatelaw(rx)
28+
@unpack rate, substrates, substoich = rx
29+
rl = rate
30+
coef = one(eltype(substoich))
31+
for (i,stoich) in enumerate(substoich)
32+
coef *= factorial(stoich)
33+
rl *= (stoich != one(stoich)) ? substrates[i]^stoich : substrates[i]
34+
end
35+
(coef != one(coef)) && (rl /= coef)
36+
37+
rl
38+
end
2439

2540
function essemble_drift(rs)
26-
D = Differential(rs.iv())
27-
eqs = [D(x(rs.iv())) ~ 0 for x in rs.states]
41+
D = Differential(rs.iv())
42+
eqs = [D(x(rs.iv())) ~ 0 for x in rs.species]
43+
species_to_idx = Dict(enumerate(rs.species))
2844

29-
for rx in rs.eqs
30-
for reactant in rx.reactants
31-
i = findfirst(x->x == reactant.op,rs.states)
32-
eqs[i] = Equation(eqs[i].lhs,eqs[i].rhs - rx.rate * reaction_expr(rx.reactants))
45+
# note, this should really use the net stoichiometry to avoid
46+
# adding and substracting the same term for A + X -> B + X
47+
for rx in rs.eqs
48+
rl = oderatelaw(rx.rate, rx.substrates, rx.substoich)
49+
stoich = rx.substoich
50+
for (sidx,substrate) in enumerate(rx.substrates)
51+
i = species_to_idx[substrate.op]
52+
eqs[i] = Equation(eqs[i].lhs, eqs[i].rhs - stoich[sidx]*rl)
3353
end
3454

35-
for product in rx.products
36-
i = findfirst(x->x == product.op,rs.states)
37-
eqs[i] = Equation(eqs[i].lhs,eqs[i].rhs + rx.rate * reaction_expr(rx.reactants))
55+
stoich = rx.prodstoich
56+
for (pidx,product) in enumerate(rx.products)
57+
i = species_to_idx[product.op]
58+
eqs[i] = Equation(eqs[i].lhs,eqs[i].rhs + stoich[pidx]*rl)
3859
end
3960
end
61+
4062
eqs
4163
end
4264

4365
function essemble_diffusion(rs)
44-
eqs = Expression[Constant(0) for x in rs.states, y in rs.eqs]
66+
eqs = Expression[Constant(0) for x in rs.species, y in rs.eqs]
67+
species_to_idx = Dict(enumerate(rs.species))
4568

4669
for (j,rx) in enumerate(rs.eqs)
47-
for reactant in rx.reactants
48-
i = findfirst(x->x == reactant.op,rs.states)
49-
eqs[i,j] = -sqrt(rx.rate) * reaction_expr(rx.reactants)
70+
rlsqrt = sqrt(oderatelaw(rx.rate, rx.substrates, rx.substoich))
71+
stoich = rx.substoich
72+
for (sidx,substrate) in enumerate(rx.substrates)
73+
i = species_to_idx[substrate.op]
74+
eqs[i,j] = -stoich[sidx] * rlsqrt
5075
end
5176

52-
for product in rx.products
53-
i = findfirst(x->x == product.op,rs.states)
54-
eqs[i,j] = sqrt(rx.rate) * reaction_expr(rx.reactants)
77+
for (pidx,product) in enumerate(rx.products)
78+
i = species_to_idx[product.op]
79+
Δp = stoich[pidx] * rlsqrt
80+
eqs[i,j] = (eqs[i,j]==Constant(0)) ? Δp : (eqs[i,j] + Δp)
5581
end
5682
end
5783
eqs
5884
end
5985

6086
function Base.convert(::Type{<:ODESystem},rs::ReactionSystem)
6187
eqs = essemble_drift(rs)
62-
ODESystem(eqs,rs.iv,rs.states,rs.ps,name=rs.name,
88+
ODESystem(eqs,rs.iv,rs.species,rs.params,name=rs.name,
6389
systems=convert.(ODESystem,rs.systems))
6490
end
6591

6692
function Base.convert(::Type{<:SDESystem},rs::ReactionSystem)
6793
eqs = essemble_drift(rs)
6894
noiseeqs = essemble_diffusion(rs)
69-
SDESystem(eqs,noiseeqs,rs.iv,rs.states,rs.ps,
95+
SDESystem(eqs,noiseeqs,rs.iv,rs.species,rs.params,
7096
name=rs.name,systems=convert.(SDESystem,rs.systems))
7197
end

test/reactionsystem.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ rxs = [Reaction(a,[x],[y]),
66
Reaction(b,[y],[x]),
77
Reaction(c,[x,y],[x])]
88
rs = ReactionSystem(rxs,t,[x,y],[a,b])
9-
sys = convert(ODESystem,rs)
10-
sys = convert(SDESystem,rs)
9+
odesys = convert(ODESystem,rs)
10+
sdesys = convert(SDESystem,rs)

0 commit comments

Comments
 (0)