Skip to content

Commit d7a18c4

Browse files
authored
Merge pull request #851 from SciML/full_system_balancing
Full system balancing
2 parents 03cf377 + 93a26be commit d7a18c4

File tree

4 files changed

+150
-5
lines changed

4 files changed

+150
-5
lines changed

docs/src/model_creation/chemistry_related_functionality.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,23 @@ balanced_reaction = balance_reaction(unbalanced_reaction)[1]
136136
Reactions declared as a part of a `ReactionSystem` (e.g. using the DSL) can be retrieved for balancing using the [`reactions`](@ref) function. Please note that balancing these will not mutate the `ReactionSystem`, but a new reaction system will need to be created using the balanced reactions.
137137

138138
!!! note
139-
Reaction balancing is currently not supported for reactions involving compounds of compounds.
139+
Reaction balancing is currently not supported for reactions involving compounds of compounds.
140+
141+
### Balancing full systems
142+
It is possible to balance all the reactions of a reaction system simultaneously using the `balance_system` function. Here, the output is a new system, where all reactions are balanced. E.g. We can use it to balance this system of methane formation/combustion:
143+
```@example chem2
144+
rs = @reaction_network begin
145+
@species C(t) O(t) H(t)
146+
@compounds begin
147+
H2(t) = 2H
148+
CH4(t) = C + 4H
149+
O2(t) = 2O
150+
CO2(t) = C + 2O
151+
H2O(t) = 2H + O
152+
end
153+
1.0, C + H2 --> CH4
154+
2.0, CH4 + O2 --> CO2 + H2O
155+
end
156+
rs_balanced = balance_system(rs)
157+
```
158+
Except for the modified reaction stoichiometries, the new system is identical to the previous one.

src/Catalyst.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ export Graph, savegraph, complexgraph
147147
include("chemistry_functionality.jl")
148148
export @compound, @compounds
149149
export iscompound, components, coefficients, component_coefficients
150-
export balance_reaction
150+
export balance_reaction, balance_system
151151

152152
### Extensions ###
153153

src/chemistry_functionality.jl

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ function balance_reaction(reaction::Reaction)
259259
end
260260

261261
isempty(balancedrxs) && (@warn "Unable to balance reaction.")
262-
(length(balancedrxs) > 1) && (@warn "Infinite balanced reactions from ($reaction) are possible, returning a basis for them. Note that we do not check if they preserve the set of substrates and products from the original reaction.")
262+
(length(balancedrxs) > 1) && (@warn "The space of possible balanced versions of the reaction ($reaction) is greater than one-dimension. This prevents the selection of a single appropriate balanced reaction. Instead, a basis for balanced reactions is returned. Note that we do not check if they preserve the set of substrates and products from the original reaction.")
263263
return balancedrxs
264264
end
265265

@@ -346,3 +346,45 @@ function create_matrix(reaction::Catalyst.Reaction)
346346

347347
return A
348348
end
349+
350+
"""
351+
balance_system(rs::ReactionSystem)
352+
353+
From a system, creates a new system where each reaction is a balanced version of the corresponding
354+
reaction of the original system. For more information, consider the `balance_reaction` function
355+
(which is internally applied to each system reaction).
356+
357+
Arguments
358+
- `rs`: The reaction system that should be balanced.
359+
360+
Notes:
361+
- If any reaction in the system cannot be balanced, throws an error.
362+
- If any reaction in the system have an infinite number of potential reactions, throws an error.
363+
Here, it would be possible to generate a valid reaction, however, no such routine is currently
364+
implemented in `balance_system`.
365+
- `balance_system` will not modify reactions of subsystems to the input system. It is recommended
366+
not to apply `balance_system` to non-flattened systems.
367+
"""
368+
function balance_system(rs::ReactionSystem)
369+
@set! rs.eqs = CatalystEqType[get_balanced_reaction(eq) for eq in get_eqs(rs)]
370+
@set! rs.rxs = [get_balanced_reaction(rx) for rx in get_rxs(rs)]
371+
return rs
372+
end
373+
374+
# Selects a balanced version of an input reaction. Handles potential problems when there are no,
375+
# or several, balanced alternatives.
376+
function get_balanced_reaction(rx::Reaction)
377+
brxs = balance_reaction(rx)
378+
379+
# In case there are no, or multiple, solutions to the balancing problem.
380+
if isempty(brxs)
381+
error("Could not balance reaction `$rx`, unable to create a balanced `ReactionSystem`.")
382+
end
383+
if length(brxs) > 1
384+
error("The space of possible balanced versions of the reaction ($reaction) is greater than one-dimension. This prevents the selection of a single appropriate balanced reaction. No method to, in this case, automatically generate a valid reaction is currently implemented in `balance_system`.")
385+
end
386+
387+
return only(brxs)
388+
end
389+
# For non-`Reaction` equations, returns the original equation.
390+
get_balanced_reaction(eq::Equation) = eq

test/miscellaneous_tests/reaction_balancing.jl

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ let
375375

376376
rx = Reaction(1.0,[CO,CO2,H2],[CH4,H2O])
377377
brxs = balance_reaction(rx)
378-
@test_logs (:warn, r"Infinite balanced reactions*") match_mode=:any balance_reaction(rx)
378+
@test_logs (:warn, r"The space of possible balanced *") match_mode=:any balance_reaction(rx)
379379
@test length(brxs) == 2
380380
end
381381

@@ -425,5 +425,89 @@ let
425425
@test isequal([rn.CO2, rn.H2O], brxs.substrates)
426426
@test isequal([rn.C6H12O6, rn.O2], brxs.products)
427427
@test isequal([6, 6], brxs.substoich)
428-
@test isequal([1, 6], brxs.prodstoich)
428+
@test isequal([1, 6], brxs.prodstoich)
429+
end
430+
431+
432+
### Reaction System Balancing ###
433+
434+
# Simple example with multiple reactions.
435+
let
436+
# Creates an unbalanced `ReactionSystem`.
437+
rs = @reaction_network begin
438+
@species C(t) O(t) H(t)
439+
@compounds begin
440+
H2(t) ~ 2H
441+
CH4(t) ~ C + 4H
442+
O2(t) ~ 2O
443+
CO2(t) ~ C + 2O
444+
H2O(t) ~ 2H + O
445+
end
446+
1.0, C + H2 --> CH4
447+
2.0, CH4 + O2 --> CO2 + H2O
448+
end
449+
rs_balanced = balance_system(rs)
450+
451+
# Checks that the system is correctly balanced (while not taking order of reactions or of
452+
# substrates/products for granted).
453+
rx1 = filter(rx -> isequal(rx.rate, 1.0), reactions(rs_balanced))[1]
454+
rx2 = filter(rx -> isequal(rx.rate, 2.0), reactions(rs_balanced))[1]
455+
@test issetequal(rx1.substoich, [1, 2])
456+
@test issetequal(rx1.prodstoich, [1])
457+
@test issetequal(rx2.substoich, [1, 2])
458+
@test issetequal(rx2.prodstoich, [1, 2])
459+
end
460+
461+
# Checks for system with non-reaction equations.
462+
let
463+
# Creates an unbalanced `ReactionSystem`.
464+
rs = @reaction_network begin
465+
@species O(t) H(t)
466+
@compounds begin
467+
H2(t) ~ 2H
468+
O2(t) ~ 2O
469+
H2O(t) ~ 2H + O
470+
end
471+
@equations begin
472+
D(V) ~ 1 - V
473+
end
474+
1.0, H2 + O2 --> H2O
475+
end
476+
rs_balanced = balance_system(rs)
477+
478+
# Checks that the system is correctly balanced (while not taking order of reactions or of
479+
# substrates/products for granted).
480+
@test issetequal(reactions(rs_balanced)[1].substoich, [2, 1])
481+
@test issetequal(reactions(rs_balanced)[1].prodstoich, [2])
482+
end
483+
484+
# Checks that systems problematic reactions yield errors.
485+
let
486+
# Check system with reaction with an infinite number of balanced versions.
487+
rs1 = @reaction_network begin
488+
@species C(t) H(t) O(t)
489+
@compounds begin
490+
CO ~ C + O
491+
CO2 ~ C + 2O
492+
H2 ~ 2H
493+
CH4 ~ C + 4H
494+
H2O ~ 2H + O
495+
end
496+
k, CO + CO2 + H2 --> CH4 + H20
497+
end
498+
@test_throws Exception balance_system(rs1)
499+
500+
# Check system with reaction without any balanced versions.
501+
rs2 = @reaction_network begin
502+
@species Fe(t) S(t) O(t) H(t) N(t)
503+
@compounds begin
504+
FeS2 ~ Fe + 2S
505+
HNO3 ~ H + N + 3O
506+
Fe2S3O12 ~ 2Fe + 3S + 12O
507+
NO ~ N + O
508+
H2SO4 ~ 2H + S + 4O
509+
end
510+
k, FeS2 + HNO3 --> Fe2S3O12 + NO + H2SO4
511+
end
512+
@test_throws Exception balance_system(rs2)
429513
end

0 commit comments

Comments
 (0)