Skip to content

Commit f472a97

Browse files
authored
Merge pull request #1104 from isaacsas/add_compse_dispatch
Add compose dispatch and constent handling of scoping to MTK
2 parents 0032275 + 45e171f commit f472a97

File tree

4 files changed

+94
-30
lines changed

4 files changed

+94
-30
lines changed

src/Catalyst.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Symbolics: BasicSymbolic
2626
using Symbolics: iscall, sorted_arguments
2727
using ModelingToolkit: Symbolic, value, get_unknowns, get_ps, get_iv, get_systems,
2828
get_eqs, get_defaults, toparam, get_var_to_name, get_observed,
29-
getvar
29+
getvar, has_iv
3030

3131
import ModelingToolkit: get_variables, namespace_expr, namespace_equation, get_variables!,
3232
modified_unknowns!, validate, namespace_variables,

src/reaction.jl

+8-12
Original file line numberDiff line numberDiff line change
@@ -351,20 +351,16 @@ MT.is_alg_equation(rx::Reaction) = false
351351
# MTK functions for extracting variables within equation type object
352352
MT.eqtype_supports_collect_vars(rx::Reaction) = true
353353
function MT.collect_vars!(unknowns, parameters, rx::Reaction, iv; depth = 0,
354-
op = MT.Differential)
354+
op = MT.Differential)
355+
355356
MT.collect_vars!(unknowns, parameters, rx.rate, iv; depth, op)
356-
for sub in rx.substrates
357-
MT.collect_vars!(unknowns, parameters, sub, iv; depth, op)
358-
end
359-
for prod in rx.products
360-
MT.collect_vars!(unknowns, parameters, prod, iv; depth, op)
361-
end
362-
for substoich in rx.substoich
363-
MT.collect_vars!(unknowns, parameters, substoich, iv; depth, op)
364-
end
365-
for prodstoich in rx.prodstoich
366-
MT.collect_vars!(unknowns, parameters, prodstoich, iv; depth, op)
357+
358+
for items in (rx.substrates, rx.products, rx.substoich, rx.prodstoich)
359+
for item in items
360+
MT.collect_vars!(unknowns, parameters, item, iv; depth, op)
361+
end
367362
end
363+
368364
if hasnoisescaling(rx)
369365
ns = getnoisescaling(rx)
370366
MT.collect_vars!(unknowns, parameters, ns, iv; depth, op)

src/reactionsystem.jl

+42-17
Original file line numberDiff line numberDiff line change
@@ -512,24 +512,8 @@ function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, us_in, ps_in;
512512
eqs = Equation[eq for eq in rxs_and_eqs if eq isa Equation]
513513

514514
# Loops through all reactions, adding encountered quantities to the unknown and parameter vectors.
515-
# Starts by looping through substrates + products only (so these are added to the vector first).
516-
# Next, the other components of reactions (e.g. rates and stoichiometries) are added.
517515
for rx in rxs
518-
for reactants in (rx.substrates, rx.products), spec in reactants
519-
MT.isparameter(spec) ? push!(ps, spec) : push!(us, spec)
520-
end
521-
end
522-
for rx in rxs
523-
# Adds all quantities encountered in the reaction's rate.
524-
findvars!(ps, us, rx.rate, ivs, vars)
525-
526-
# Extracts all quantities encountered within stoichiometries.
527-
for stoichiometry in (rx.substoich, rx.prodstoich), sym in stoichiometry
528-
(sym isa Symbolic) && findvars!(ps, us, sym, ivs, vars)
529-
end
530-
531-
# Extract all quantities encountered in relevant `Reaction` metadata.
532-
hasnoisescaling(rx) && findvars!(ps, us, getnoisescaling(rx), ivs, vars)
516+
MT.collect_vars!(us, ps, rx, iv)
533517
end
534518

535519
# Extracts any species, variables, and parameters that occur in (non-reaction) equations.
@@ -543,6 +527,11 @@ function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, us_in, ps_in;
543527
fulleqs = rxs
544528
end
545529

530+
# get variables in subsystems with scope at this level
531+
for ssys in get(kwargs, :systems, [])
532+
MT.collect_scoped_vars!(us, ps, ssys, iv)
533+
end
534+
546535
# Loops through all events, adding encountered quantities to the unknown and parameter vectors.
547536
find_event_vars!(ps, us, continuous_events, ivs, vars)
548537
find_event_vars!(ps, us, discrete_events, ivs, vars)
@@ -1397,6 +1386,42 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs))
13971386
discrete_events = MT.discrete_events(rs))
13981387
end
13991388

1389+
"""
1390+
ModelingToolkit.compose(sys::ReactionSystem, systems::AbstractArray; name = nameof(sys))
1391+
1392+
Compose the indicated [`ReactionSystem`](@ref) with one or more `AbstractSystem`s.
1393+
1394+
Notes:
1395+
- The `AbstractSystem` being added in must be an `ODESystem`, `NonlinearSystem`,
1396+
or `ReactionSystem` currently.
1397+
- Returns a new `ReactionSystem` and does not modify `rs`.
1398+
- By default, the new `ReactionSystem` will have the same name as `sys`.
1399+
"""
1400+
function ModelingToolkit.compose(sys::ReactionSystem, systems::AbstractArray; name = nameof(sys))
1401+
nsys = length(systems)
1402+
nsys == 0 && return sys
1403+
@set! sys.name = name
1404+
@set! sys.systems = [get_systems(sys); systems]
1405+
newunknowns = OrderedSet{BasicSymbolic{Real}}()
1406+
newparams = OrderedSet()
1407+
iv = has_iv(sys) ? get_iv(sys) : nothing
1408+
for ssys in systems
1409+
MT.collect_scoped_vars!(newunknowns, newparams, ssys, iv)
1410+
end
1411+
1412+
if !isempty(newunknowns)
1413+
@set! sys.unknowns = union(get_unknowns(sys), newunknowns)
1414+
sort!(get_unknowns(sys), by = !isspecies)
1415+
@set! sys.species = filter(isspecies, get_unknowns(sys))
1416+
end
1417+
1418+
if !isempty(newparams)
1419+
@set! sys.ps = union(get_ps(sys), newparams)
1420+
end
1421+
1422+
return sys
1423+
end
1424+
14001425
"""
14011426
ModelingToolkit.extend(sys::AbstractSystem, rs::ReactionSystem; name::Symbol=nameof(sys))
14021427

test/compositional_modelling/component_based_model_creation.jl

+43
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,46 @@ let
501501
defs[rn2.X] == 30.0
502502
defs[rn2.Z] == 40.0
503503
end
504+
505+
# test scoping in compose
506+
# code adapted from ModelingToolkit.jl tests
507+
let
508+
t = default_t()
509+
D = default_time_deriv()
510+
@species x1(t) x2(t)
511+
@variables x3(t) x4(t) x5(t)
512+
x2 = ParentScope(x2)
513+
x3 = ParentScope(ParentScope(x3))
514+
x4 = DelayParentScope(x4, 2)
515+
x5 = GlobalScope(x5)
516+
@parameters p1 p2 p3 p4 p5
517+
p2 = ParentScope(p2)
518+
p3 = ParentScope(ParentScope(p3))
519+
p4 = DelayParentScope(p4, 2)
520+
p5 = GlobalScope(p5)
521+
rxs = [Reaction(p1, nothing, [x1]), Reaction(p2, [x2], nothing),
522+
D(x3) ~ p3, D(x4) ~ p4, D(x5) ~ p5]
523+
@named sys1 = ReactionSystem(rxs, t)
524+
@test isequal(x1, only(unknowns(sys1)))
525+
@test isequal(x1, only(species(sys1)))
526+
@test isequal(p1, only(parameters(sys1)))
527+
@named sys2 = ReactionSystem([], t; systems = [sys1])
528+
@test length(unknowns(sys2)) == 2
529+
@test any(isequal(x2), unknowns(sys2))
530+
@test any(isequal(x2), species(sys2))
531+
@test length(parameters(sys2)) == 2
532+
@test any(isequal(p2), parameters(sys2))
533+
@named sys3 = ReactionSystem(Equation[], t)
534+
sys3 = sys3 sys2
535+
@test length(unknowns(sys3)) == 4
536+
@test any(isequal(x3), unknowns(sys3))
537+
@test any(isequal(x4), unknowns(sys3))
538+
@test length(species(sys3)) == 2
539+
@test length(parameters(sys3)) == 4
540+
@test any(isequal(p3), parameters(sys3))
541+
@test any(isequal(p4), parameters(sys3))
542+
sys4 = complete(sys3)
543+
@test length(unknowns(sys3)) == 4
544+
@test length(parameters(sys4)) == 5
545+
@test any(isequal(p5), parameters(sys4))
546+
end

0 commit comments

Comments
 (0)