# Issue with scoping rules of parameter (and variable) declaration in components (Catalyst vs ModelingToolkit)

**URL:** <https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232>\
**Category:** Modelling & Simulations\
**Tags:** modelingtoolkit, catalyst\
**Created:** [June 26, 2025, 12:35pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232 "2025-06-26T12:35:29Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Cedric\_Lhoussaine](https://avatars.discourse-cdn.com/v4/letter/c/a183cd/32.png) [@Cedric\_Lhoussaine](https://discourse.julialang.org/u/Cedric_Lhoussaine)\
**Post date:** [June 26, 2025, 12:35pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/1 "2025-06-26T12:35:30Z")

</div>

Working with Catalyst v15.0.8 and ModelingToolkit v9.80.5,  
I found a strange and misleading behavior w.r.t. parameter declarations in reaction components: suppose I declare a parameter (`k`) in two different components (`n1` and `n2`) with two different default values (say `1.0` in `n1` and `2.0` in `n2`), and in both components I also declare a variable `x` with `k` as initial condition. I would thus expect that the initial condition of `n1.x` is `1.0` and `n2.x` is `2.0`. This is not the case, both variable have the same initial value! More concretely:

```julia
n1 = @network_component n1 begin
    @parameters k = 1.0
    @variables x(t) = k
end

n2 = @network_component n2 begin
    @parameters k = 2.0
    @variables x(t) = k
end

@named sys1 = System([D(n2.x) ~ 0, D(n1.x) ~ 0], t)
prob1 = ODEProblem(complete(sys1))

```

I get:

```julia
ODEProblem with uType Vector{Float64} and tType Nothing. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (nothing, nothing)
u0: 2-element Vector{Float64}:
 1.0
 1.0

```

So both `n1.x` and `n2.x` have `1.0` as initial condition. If I swap both equations:

```julia
@named sys2 = System([D(n1.x) ~ 0, D(n2.x) ~ 0], t)
prob2 = ODEProblem(complete(sys2))

```

I get:

```julia
ODEProblem with uType Vector{Float64} and tType Nothing. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (nothing, nothing)
u0: 2-element Vector{Float64}:
 2.0
 2.0

```

both `n1.x` and `n2.x` have now `1.0` as initial condition.

If I translate this code in `ModelingToolkit` as follows:

```julia
@mtkmodel Decl1 begin
    @parameters begin
        k = 1.0
    end
    @variables begin
        x(t) = k
    end
end

@mtkmodel Decl2 begin
    @parameters begin
        k = 2.0
    end
    @variables begin
        x(t) = k
    end
end

@mtkmodel Eq begin
    @components begin
        n1 = Decl1()
        n2 = Decl2()
    end
    @equations begin
        D(n2.x) ~ 0
        D(n1.x) ~ 0
    end
end

@mtkbuild sys3 = Eq()
prob3 = ODEProblem(sys3)

```

then I get the expected initial conditions:

```julia
ODEProblem with uType Vector{Float64} and tType Nothing. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (nothing, nothing)
u0: 2-element Vector{Float64}:
 1.0
 2.0

```

Is it a bug or a feature of `@reaction_component` ?

---

<div class="post-metadata">

**Author:** ![isaacsas](https://avatars.discourse-cdn.com/v4/letter/i/f6c823/32.png) [@isaacsas](https://discourse.julialang.org/u/isaacsas)\
**Post date:** [June 27, 2025, 12:13pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/2 "2025-06-27T12:13:31Z")

</div>

I wouldn’t recommend mixing Catalyst `ReactionSystem`s and `ModelingToolkit.System` as you are doing. These were not designed to work together and have not been tested to work together in any way. What is it you actually want to do? If you make your model purely in Catalyst is there some problem you are encountering?

---

<div class="post-metadata">

**Author:** ![isaacsas](https://avatars.discourse-cdn.com/v4/letter/i/f6c823/32.png) [@isaacsas](https://discourse.julialang.org/u/isaacsas)\
**Post date:** [June 27, 2025, 12:32pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/3 "2025-06-27T12:32:59Z")

</div>

Note you can make a model that includes both network components in Catalyst, and then everything seems fine:

```julia
@named rn = ReactionSystem([], default_t(); systems = [n1, n2])
oprob = ODEProblem(complete(rn), [], (0.0, 10.0))

```

which gives

```julia
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (0.0, 10.0)
u0: 2-element Vector{Float64}:
 1.0
 2.0

```

---

<div class="post-metadata">

**Author:** ![Cedric\_Lhoussaine](https://avatars.discourse-cdn.com/v4/letter/c/a183cd/32.png) [@Cedric\_Lhoussaine](https://discourse.julialang.org/u/Cedric_Lhoussaine)\
**Post date:** [June 27, 2025, 3:44pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/4 "2025-06-27T15:44:16Z")

</div>

Thanks a lot for your answer!  
Following your suggestion, if add a reaction to the reaction system, the problem arises:

```julia
n1 = @network_component n1 begin
    @parameters k = 1.0
    @species x(t) = k
end

n2 = @network_component n2 begin
    @parameters k = 2.0
    @species x(t) = k
end

@named rn = ReactionSystem([Reaction(1.0,[n1.x],[n2.x])], default_t(); systems = [n1, n2])

```

which gives

```julia
Model rn:
Subsystems (2): see hierarchy(rn)
  n1
  n2
Unknowns (2): see unknowns(rn)
  n1₊x(t) [defaults to k]
  n2₊x(t) [defaults to k]
Parameters (3): see parameters(rn)
  k [defaults to 2.0]
  n1₊k [defaults to 1.0]
  n2₊k [defaults to 2.0]

```

I don’t know why their are 3 parameters here… And

```julia
oprob = ODEProblem(complete(rn), [], (0.0, 10.0))

```

gives:

```julia
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (0.0, 10.0)
u0: 2-element Vector{Float64}:
 2.0
 2.0

```

Maybe I did something wrong. If not, the problem may come from MTK because I could actually also reproduce the same thing:

```julia
@mtkmodel Decl1 begin
    @parameters begin
        k = 1.0
    end
    @variables begin
        x(t) = k
    end
end

@mtkmodel Decl2 begin
    @parameters begin
        k = 2.0
    end
    @variables begin
        x(t) = k
    end
end

@named m1 = Decl1()
@named m2 = Decl2()
@named sys1 = ODESystem([D(m2.x) ~ 0, D(m1.x) ~ 0], t)
prob1 = ODEProblem(complete(sys1))

```

gives:

```julia
ODEProblem with uType Vector{Float64} and tType Nothing. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (nothing, nothing)
u0: 2-element Vector{Float64}:
 1.0
 1.0

```

---

<div class="post-metadata">

**Author:** ![isaacsas](https://avatars.discourse-cdn.com/v4/letter/i/f6c823/32.png) [@isaacsas](https://discourse.julialang.org/u/isaacsas)\
**Post date:** [June 27, 2025, 7:04pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/5 "2025-06-27T19:04:42Z")

</div>

Catalyst uses the same code to discover the unknowns and parameters as MTK (or at least it did prior to recent MTK changes). If you update your MTK example to

```julia
@named sys1 = ODESystem([D(m2.x) ~ 0, D(m1.x) ~ 0], t; systems = [m1, m2])

```

(which I think you need to do to tell it the subsystems), then you will again see that it adds an extra parameter

```julia
julia> @named sys1 = ODESystem([D(m2.x) ~ 0, D(m1.x) ~ 0], t; systems = [m1, m2])
Model sys1:
Subsystems (2): see hierarchy(sys1)
  m1
  m2
Equations (2):
  2 standard: see equations(sys1)
Unknowns (2): see unknowns(sys1)
  m2₊x(t) [defaults to k]
  m1₊x(t) [defaults to k]
Parameters (3): see parameters(sys1)
  k [defaults to 1.0]
  m1₊k [defaults to 1.0]
  m2₊k [defaults to 2.0]

```

I’d suggest you open an issue on the ModelingToolkit Github with your MTK example from above, but using

```julia
@named sys1 = ODESystem([D(m2.x) ~ 0, D(m1.x) ~ 0], t; systems = [m1, m2])

```

---

<div class="post-metadata">

**Author:** ![isaacsas](https://avatars.discourse-cdn.com/v4/letter/i/f6c823/32.png) [@isaacsas](https://discourse.julialang.org/u/isaacsas)\
**Post date:** [June 27, 2025, 7:14pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/6 "2025-06-27T19:14:33Z")

</div>

As a temporary workaround to the bug you can use the four argument constructor like:

```julia
@named rn = ReactionSystem([Reaction(1.0, [n1.x], [n2.x])], default_t(), [], []; systems=[n1, n2])

```

giving

```julia
Model rn:
Subsystems (2): see hierarchy(rn)
  n1
  n2
Unknowns (2): see unknowns(rn)
  n1₊x(t) [defaults to n1₊k]
  n2₊x(t) [defaults to n2₊k]
Parameters (2): see parameters(rn)
  n1₊k [defaults to 1.0]
  n2₊k [defaults to 2.0]

```

Then

```julia
julia> oprob = ODEProblem(complete(rn), [], (0.0, 10.0))
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (0.0, 10.0)
u0: 2-element Vector{Float64}:
 1.0
 2.0

```

---

<div class="post-metadata">

**Author:** ![isaacsas](https://avatars.discourse-cdn.com/v4/letter/i/f6c823/32.png) [@isaacsas](https://discourse.julialang.org/u/isaacsas)\
**Post date:** [June 27, 2025, 7:20pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/7 "2025-06-27T19:20:52Z")

</div>

I’ve opened an issue on MTK [here](https://github.com/SciML/ModelingToolkit.jl/issues/3778).

---

<div class="post-metadata">

**Author:** ![Cedric\_Lhoussaine](https://avatars.discourse-cdn.com/v4/letter/c/a183cd/32.png) [@Cedric\_Lhoussaine](https://discourse.julialang.org/u/Cedric_Lhoussaine)\
**Post date:** [July 1, 2025, 12:59pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/8 "2025-07-01T12:59:14Z")

</div>

Thanks again! In the constructor

```julia
@named rn = ReactionSystem([Reaction(1.0, [n1.x], [n2.x])], default_t(), [], []; systems=[n1, n2])

```

according to the source code of Catalyst the two extra parameters (`[]` and `[]`) stand for the lists of unknowns et parameters. For my understanding, how does it come that this solves the issue ?  
Also, do you know any workaround that would work in “pure” MTK ?

---

<div class="post-metadata">

**Author:** ![isaacsas](https://avatars.discourse-cdn.com/v4/letter/i/f6c823/32.png) [@isaacsas](https://discourse.julialang.org/u/isaacsas)\
**Post date:** [July 2, 2025, 3:19pm UTC](https://discourse.julialang.org/t/issue-with-scoping-rules-of-parameter-and-variable-declaration-in-components-catalyst-vs-modelingtoolkit/130232/9 "2025-07-02T15:19:18Z")

</div>

If you use the four argument constructor, you disable automatic discovery of unknowns and parameters from the reactions/equations. Instead Catalyst (and I presume MTK) assume that you will tell it via the two arrays which parameters and unknowns should be owned by that system. Hence in this case, when passing `[]`, the top level system assumes that it has no unknowns/parameters that it owns.
