Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compound fixe #1154

Merged
merged 3 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/chemistry_functionality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,31 +101,29 @@ function make_compound(expr)
species_expr = expr.args[2]
species_name, ivs, _, _ = find_varinfo_in_declaration(expr.args[2])

# If no ivs were given, inserts `(..)` (e.g. turning `CO` to `CO(..)`).
isempty(ivs) && (species_expr = insert_independent_variable(species_expr, :(..)))

# Expression which when evaluated gives a vector with all the ivs of the components.
ivs_get_expr = :(unique(reduce(
vcat, [sorted_arguments(ModelingToolkit.unwrap(comp))
for comp in $components])))
# If no ivs were given, inserts an expression which evaluates to the union of the ivs
# for the species the compound depends on.
ivs_get_expr = :(unique(reduce( vcat, (sorted_arguments(ModelingToolkit.unwrap(comp))
for comp in $components))))
if isempty(ivs)
species_expr = Catalyst.insert_independent_variable(species_expr, :($ivs_get_expr...))
end

# Creates the found expressions that will create the compound species.
# The `Expr(:escape, :(...))` is required so that the expressions are evaluated in
# the scope the users use the macro in (to e.g. detect already exiting species).
# Creates something like (where `compound_ivs` and `component_ivs` evaluates to all the compound's and components' ivs):
# `@species CO2(..)`
# `isempty([])` && length(component_ivs) && error("When ...)
# `CO2 = CO2(component_ivs..)`
# `@species CO2(iv)`
# `isempty([])` && (length(component_ivs) > 1) && error("When ...)
# `issetequal(compound_ivs, component_ivs) || error("The ...)`
# `CO2 = ModelingToolkit.setmetadata(CO2, Catalyst.CompoundSpecies, true)`
# `CO2 = ModelingToolkit.setmetadata(CO2, Catalyst.CompoundSpecies, [C, O])`
# `CO2 = ModelingToolkit.setmetadata(CO2, Catalyst.CompoundSpecies, [1, 2])`
# `CO2 = ModelingToolkit.wrap(CO2)`
species_declaration_expr = Expr(:escape, :(@species $species_expr))
multiple_ivs_error_check_expr = Expr(:escape,
:($(isempty(ivs)) && (length($ivs_get_expr) > 1) &&
error($COMPOUND_CREATION_ERROR_DEPENDENT_VAR_REQUIRED)))
iv_designation_expr = Expr(:escape,
:($(isempty(ivs)) && ($species_name = $(species_name)($(ivs_get_expr)...))))
iv_check_expr = Expr(:escape,
:(issetequal(arguments(ModelingToolkit.unwrap($species_name)), $ivs_get_expr) ||
error("The independent variable(S) provided to the compound ($(arguments(ModelingToolkit.unwrap($species_name)))), and those of its components ($($ivs_get_expr)))), are not identical.")))
Expand All @@ -138,16 +136,18 @@ function make_compound(expr)
coefficients_designation_expr = Expr(:escape,
:($species_name = ModelingToolkit.setmetadata(
$species_name, Catalyst.CompoundCoefficients, $coefficients)))
compound_wrap_expr = Expr(:escape,
:($species_name = ModelingToolkit.wrap($species_name)))

# Returns the rephrased expression.
return quote
$species_declaration_expr
$multiple_ivs_error_check_expr
$iv_designation_expr
$iv_check_expr
$compound_designation_expr
$components_designation_expr
$coefficients_designation_expr
$compound_wrap_expr
end
end

Expand Down
70 changes: 44 additions & 26 deletions test/miscellaneous_tests/compound_macro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ using Catalyst, Test
# Sets the default `t` to use.
t = default_t()

### Test Macro Basic Functionality ###
### Test Macro Basic Functionality ###

# Miscellaneous basic usage.
let
@species C(t) H(t) O(t)
@species C(t) H(t) O(t)
@parameters p1 p2

# Basic cases that should pass:
Expand Down Expand Up @@ -50,7 +50,7 @@ let

# Declares stuff in the DSL.
rn = @reaction_network begin
@species N(t) H(t)
@species N(t) H(t)
@parameters p1 p2
@compounds begin
NH3_1 ~ N + 3H
Expand Down Expand Up @@ -80,7 +80,7 @@ let

@test isequal([C, H, O], components(C6H12O2))
@test isequal([6, 12, 2], coefficients(C6H12O2))
@test isequal([C => 6, H => 12, O => 2], Catalyst.component_coefficients(C6H12O2))
@test isequal([C => 6, H => 12, O => 2], Catalyst.component_coefficients(C6H12O2))
@test all(!iscompound(i) for i in components(C6H12O2))
end

Expand All @@ -95,10 +95,28 @@ let

@test isequal([O], components(O2))
@test isequal([2], coefficients(O2))
@test isequal([O => 2], Catalyst.component_coefficients(O2))
@test isequal([O => 2], Catalyst.component_coefficients(O2))
@test all(!iscompound(i) for i in components(O2))
end

# Tests https://github.com/SciML/Catalyst.jl/issues/1151.
# Checks that compounds are `Num` (and not BasicSymbolics).
# Check that ModelingToolkit.get_variables! works on compounds.
let
@species C(t) H(t) O(t)
@compounds begin
O₂ ~ 2O
CH₄ ~ C + 4H
end

@test O₂ isa Symbolics.Num
@test CH₄ isa Symbolics.Num
vars = []
ModelingToolkit.get_variables!(vars, O₂)
ModelingToolkit.get_variables!(vars, CH₄)
@test issetequal(vars, [O₂, CH₄])
end

### Independent Variables ###

# Test using different independent variable combinations.
Expand All @@ -112,14 +130,14 @@ let
@test_throws Exception @eval @compound (H2O = 2.0) ~ 2H + O
@test_throws Exception @eval @compound PH4(x) ~ P + 4H
@test_throws Exception @eval @compound SO2(t,y) ~ S + 2O

# Creates compounds.
@compound CO2 ~ C + 2O
@compound (NH4, [output=true]) ~ N + 4H
@compound (H2O(t,x) = 2.0) ~ 2H + O
@compound PH4(t,x) ~ P + 4H
@compound SO2(t,x,y) ~ S + 2O

# Checks they have the correct independent variables.
@test issetequal(Symbolics.sorted_arguments(ModelingToolkit.unwrap(CO2)), [t])
@test issetequal(Symbolics.sorted_arguments(ModelingToolkit.unwrap(NH4)), [x])
Expand Down Expand Up @@ -200,7 +218,7 @@ let
end

# Case 5.
let
let
@species A(t)
B = A
@compound A2 ~ 2A
Expand All @@ -217,7 +235,7 @@ end
### Test @compounds Macro ###

# Basic @compounds syntax.
let
let
@species C(t) H(t) O(t)
@compound OH ~ 1O + 1H
@compound C3H5OH3 ~ 3C + 5H + 3OH
Expand All @@ -239,7 +257,7 @@ let
end

# Interpolation in @compounds.
let
let
@species s1(t) s2(t) s3(t)
s2_alt = s2
s3_alt = s3
Expand All @@ -262,8 +280,8 @@ end
# Checks that compounds cannot be created from non-existing species.
let
@species C(t) H(t)
@test_throws Exception @compound C6H12O2 ~ 6C + 12H + 2O
@test_throws Exception @compound O2 ~ 2O
@test_throws Exception @compound C6H12O2 ~ 6C + 12H + 2O
@test_throws Exception @compound O2 ~ 2O
end

# Checks that nested components works as expected.
Expand Down Expand Up @@ -292,25 +310,25 @@ end
# Checks with a single compound.
# Checks using @unpack.
# Check where compounds and components does not occur in reactions.
let
let
rn = @reaction_network begin
@species C(t) O(t)
@compounds begin
CO2 ~ C + 2O
end
end
@unpack C, O, CO2 = rn

@test length(species(rn)) == 3
@test iscompound(CO2)
@test isequal([C, O], components(CO2))
@test isequal([1, 2], coefficients(CO2))
@test isequal([C => 1, O => 2], component_coefficients(CO2))
@test isequal([C => 1, O => 2], component_coefficients(CO2))
end

# Test using multiple compounds.
# Test using rn. notation to fetch species.
let
let
rn = @reaction_network begin
@species C(t) O(t) H(t)
@compounds begin
Expand All @@ -322,20 +340,20 @@ let
k, CH4 + O2 --> CO2 + H2O
end
species(rn)

@test length(species(rn)) == 7
@test isequal([rn.C, rn.H], components(rn.CH4))
@test isequal([1, 4], coefficients(rn.CH4))
@test isequal([rn.C => 1, rn.H => 4], component_coefficients(rn.CH4))
@test isequal([rn.C => 1, rn.H => 4], component_coefficients(rn.CH4))
@test isequal([rn.O], components(rn.O2))
@test isequal([2], coefficients(rn.O2))
@test isequal([rn.O => 2], component_coefficients(rn.O2))
@test isequal([rn.O => 2], component_coefficients(rn.O2))
@test isequal([rn.C, rn.O], components(rn.CO2))
@test isequal([1, 2], coefficients(rn.CO2))
@test isequal([rn.C => 1, rn.O => 2], component_coefficients(rn.CO2))
@test isequal([rn.C => 1, rn.O => 2], component_coefficients(rn.CO2))
@test isequal([rn.H, rn.O], components(rn.H2O))
@test isequal([2, 1], coefficients(rn.H2O))
@test isequal([rn.H => 2, rn.O => 1], component_coefficients(rn.H2O))
@test isequal([rn.H => 2, rn.O => 1], component_coefficients(rn.H2O))
end

# Tests using compounds of compounds.
Expand All @@ -351,13 +369,13 @@ let
end
species(rn)
@unpack S, O, SO2, S2O4 = rn

@test length(species(rn)) == 4

@test isequal([S, O], components(SO2))
@test isequal([1, 2], coefficients(SO2))
@test isequal([S => 1, O => 2], component_coefficients(SO2))
@test isequal([S => 1, O => 2], component_coefficients(SO2))
@test isequal([SO2], components(S2O4))
@test isequal([2], coefficients(S2O4))
@test isequal([SO2 => 2], component_coefficients(S2O4))
end
@test isequal([SO2 => 2], component_coefficients(S2O4))
end
Loading