Skip to content

Commit 94a5f4e

Browse files
Merge pull request #330 from TorkelE/reactions_with_multu_stoich_and_empty_sides
Allow reactions with: Multiple copies of one components and either no substrates or no products
2 parents 98e6705 + 07f6e2d commit 94a5f4e

File tree

2 files changed

+39
-24
lines changed

2 files changed

+39
-24
lines changed

src/systems/reaction/reactionsystem.jl

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,36 @@ struct Reaction{S <: Variable, T <: Number}
33
substrates::Vector{Operation}
44
products::Vector{Operation}
55
substoich::Vector{T}
6-
prodstoich::Vector{T}
6+
prodstoich::Vector{T}
77
netstoich::Vector{Pair{S,T}}
88
only_use_rate::Bool
99
end
1010

11-
function Reaction(rate, subs, prods, substoich, prodstoich;
11+
function Reaction(rate, subs, prods, substoich, prodstoich;
1212
netstoich=nothing, only_use_rate=false, kwargs...)
1313

14-
subsv = isnothing(subs) ? Vector{Operation}() : subs
15-
prodsv = isnothing(prods) ? Vector{Operation}() : prods
16-
ns = isnothing(netstoich) ? get_netstoich(subsv, prodsv, substoich, prodstoich) : netstoich
17-
Reaction(rate, subsv, prodsv, substoich, prodstoich, ns, only_use_rate)
14+
(isnothing(prods)&&isnothing(subs)) && error("A reaction requires a non-nothing substrate or product vector.")
15+
(isnothing(prodstoich)&&isnothing(substoich)) && error("Both substrate and product stochiometry inputs cannot be nothing.")
16+
if isnothing(subs)
17+
subs = Vector{Operation}()
18+
(substoich!=nothing) && error("If substrates are nothing, substrate stiocihometries have to be so too.")
19+
substoich = typeof(prodstoich)()
20+
end
21+
if isnothing(prods)
22+
prods = Vector{Operation}()
23+
(prodstoich!=nothing) && error("If products are nothing, product stiocihometries have to be so too.")
24+
prodstoich = typeof(substoich)()
25+
end
26+
ns = isnothing(netstoich) ? get_netstoich(subs, prods, substoich, prodstoich) : netstoich
27+
Reaction(rate, subs, prods, substoich, prodstoich, ns, only_use_rate)
1828
end
1929

30+
2031
# three argument constructor assumes stoichiometric coefs are one and integers
21-
function Reaction(rate, subs, prods; kwargs...)
32+
function Reaction(rate, subs, prods; kwargs...)
2233

23-
sstoich = isnothing(subs) ? Int[] : ones(Int,length(subs))
24-
pstoich = isnothing(prods) ? Int[] : ones(Int,length(prods))
34+
sstoich = isnothing(subs) ? nothing : ones(Int,length(subs))
35+
pstoich = isnothing(prods) ? nothing : ones(Int,length(prods))
2536
Reaction(rate, subs, prods, sstoich, pstoich; kwargs...)
2637
end
2738

@@ -54,18 +65,18 @@ end
5465
function ReactionSystem(eqs, iv, species, params; systems = ReactionSystem[],
5566
name = gensym(:ReactionSystem))
5667

57-
ReactionSystem(eqs, iv, convert.(Variable,species), convert.(Variable,params),
68+
ReactionSystem(eqs, iv, convert.(Variable,species), convert.(Variable,params),
5869
name, systems)
5970
end
6071

6172
# Calculate the ODE rate law
6273
function oderatelaw(rx)
63-
@unpack rate, substrates, substoich, only_use_rate = rx
74+
@unpack rate, substrates, substoich, only_use_rate = rx
6475
rl = rate
6576
if !only_use_rate
6677
coef = one(eltype(substoich))
6778
for (i,stoich) in enumerate(substoich)
68-
coef *= factorial(stoich)
79+
coef *= factorial(stoich)
6980
rl *= isone(stoich) ? substrates[i] : substrates[i]^stoich
7081
end
7182
(!isone(coef)) && (rl /= coef)
@@ -78,15 +89,15 @@ function assemble_drift(rs)
7889
eqs = [D(x(rs.iv())) ~ 0 for x in rs.states]
7990
species_to_idx = Dict((x => i for (i,x) in enumerate(rs.states)))
8091

81-
for rx in rs.eqs
92+
for rx in rs.eqs
8293
rl = oderatelaw(rx)
8394
for (spec,stoich) in rx.netstoich
8495
i = species_to_idx[spec]
8596
if iszero(eqs[i].rhs)
8697
signedrl = (stoich > zero(stoich)) ? rl : -rl
87-
rhs = isone(abs(stoich)) ? signedrl : stoich * rl
98+
rhs = isone(abs(stoich)) ? signedrl : stoich * rl
8899
else
89-
Δspec = isone(abs(stoich)) ? rl : abs(stoich) * rl
100+
Δspec = isone(abs(stoich)) ? rl : abs(stoich) * rl
90101
rhs = (stoich > zero(stoich)) ? (eqs[i].rhs + Δspec) : (eqs[i].rhs - Δspec)
91102
end
92103
eqs[i] = Equation(eqs[i].lhs, rhs)
@@ -104,7 +115,7 @@ function assemble_diffusion(rs)
104115
for (spec,stoich) in rx.netstoich
105116
i = species_to_idx[spec]
106117
signedrlsqrt = (stoich > zero(stoich)) ? rlsqrt : -rlsqrt
107-
eqs[i,j] = isone(abs(stoich)) ? signedrlsqrt : stoich * rlsqrt
118+
eqs[i,j] = isone(abs(stoich)) ? signedrlsqrt : stoich * rlsqrt
108119
end
109120
end
110121
eqs

test/reactionsystem.jl

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using ModelingToolkit, LinearAlgebra, Test
22

3-
@parameters t k[1:13]
3+
@parameters t k[1:15]
44
@variables A(t) B(t) C(t) D(t)
55
rxs = [Reaction(k[1], nothing, [A]), # 0 -> A
66
Reaction(k[2], [B], nothing), # B -> 0
77
Reaction(k[3],[A],[C]), # A -> C
8-
Reaction(k[4], [C], [A,B]), # C -> A + B
8+
Reaction(k[4], [C], [A,B]), # C -> A + B
99
Reaction(k[5], [C], [A], [1], [2]), # C -> A + A
1010
Reaction(k[6], [A,B], [C]), # A + B -> C
1111
Reaction(k[7], [B], [A], [2], [1]), # 2B -> A
@@ -14,8 +14,10 @@ rxs = [Reaction(k[1], nothing, [A]), # 0 -> A
1414
Reaction(k[10], [A], [C,D], [2], [1,1]), # 2A -> C + D
1515
Reaction(k[11], [A], [A,B], [2], [1,1]), # 2A -> A + B
1616
Reaction(k[12], [A,B,C], [C,D], [1,3,4], [2, 3]), # A+3B+4C -> 2C + 3D
17-
Reaction(k[13]*A/(2+A), [A], nothing; only_use_rate=true) # A -> 0 with custom rate
18-
]
17+
Reaction(k[13], [A,B], nothing, [3,1], nothing), # 3A+B -> 0
18+
Reaction(k[14], nothing, [A], nothing, [2]), # 0 -> 2A
19+
Reaction(k[15]*A/(2+A), [A], nothing; only_use_rate=true) # A -> 0 with custom rate
20+
]
1921
rs = ReactionSystem(rxs,t,[A,B,C,D],k)
2022
odesys = convert(ODESystem,rs)
2123
sdesys = convert(SDESystem,rs)
@@ -24,8 +26,8 @@ sdesys = convert(SDESystem,rs)
2426
function oderhs(u,k,t)
2527
A = u[1]; B = u[2]; C = u[3]; D = u[4];
2628
du = zeros(eltype(u),4)
27-
du[1] = k[1] - k[3]*A + k[4]*C + 2*k[5]*C - k[6]*A*B + k[7]*B^2/2 - k[9]*A*B - k[10]*A^2 - k[11]*A^2/2 - k[12]*A*B^3*C^4/144 - k[13]*A/(2+A)
28-
du[2] = -k[2]*B + k[4]*C - k[6]*A*B - k[7]*B^2 - k[8]*A*B - k[9]*A*B + k[11]*A^2/2 - 3*k[12]*A*B^3*C^4/144
29+
du[1] = k[1] - k[3]*A + k[4]*C + 2*k[5]*C - k[6]*A*B + k[7]*B^2/2 - k[9]*A*B - k[10]*A^2 - k[11]*A^2/2 - k[12]*A*B^3*C^4/144 - 3*k[13]*A^3*B/6 + 2*k[14] - k[15]*A/(2+A)
30+
du[2] = -k[2]*B + k[4]*C - k[6]*A*B - k[7]*B^2 - k[8]*A*B - k[9]*A*B + k[11]*A^2/2 - 3*k[12]*A*B^3*C^4/144 - k[13]*A^3*B/6
2931
du[3] = k[3]*A - k[4]*C - k[5]*C + k[6]*A*B + k[8]*A*B + k[9]*A*B + k[10]*A^2/2 - 2*k[12]*A*B^3*C^4/144
3032
du[4] = k[9]*A*B + k[10]*A^2/2 + 3*k[12]*A*B^3*C^4/144
3133
du
@@ -49,8 +51,10 @@ function sdenoise(u,k,t)
4951
-2*sqrt(k[10]*A^2/2) z sqrt(k[10]*A^2/2) sqrt(k[10]*A^2/2);
5052
-sqrt(k[11]*A^2/2) sqrt(k[11]*A^2/2) z z;
5153
-sqrt(k[12]*A*B^3*C^4/144) -3*sqrt(k[12]*A*B^3*C^4/144) -2*sqrt(k[12]*A*B^3*C^4/144) 3*sqrt(k[12]*A*B^3*C^4/144);
52-
-sqrt(k[13]*A/(2+A)) z z z]'
53-
54+
-3*sqrt(k[13]*A^3*B/6) -sqrt(k[13]*A^3*B/6) z z
55+
2*sqrt(k[14]) z z z;
56+
-sqrt(k[15]*A/(2+A)) z z z]'
57+
5458
return G
5559
end
5660

0 commit comments

Comments
 (0)