# How to tell ModelingToolkit.jl which variables are parameters

**URL:** <https://discourse.julialang.org/t/how-to-tell-modelingtoolkit-jl-which-variables-are-parameters/104501>\
**Category:** Modelling & Simulations\
**Tags:** question\
**Created:** [October 2, 2023, 2:48pm UTC](https://discourse.julialang.org/t/how-to-tell-modelingtoolkit-jl-which-variables-are-parameters/104501 "2023-10-02T14:48:21Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Lilith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lilith/32/27492_2.png) [@Lilith](https://discourse.julialang.org/u/Lilith)\
**Post date:** [October 2, 2023, 2:48pm UTC](https://discourse.julialang.org/t/how-to-tell-modelingtoolkit-jl-which-variables-are-parameters/104501/1 "2023-10-02T14:48:21Z")

</div>

I have a `Symbolics.Equation` that I got from an external source. Now I want to use that equation in ModelingToolkit.jl, but I don’t know how to tell ModelingToolkit.jl which variables are parameters.

For example, here I get my equation from python, and SymPyPythonCall’s [conversion from sympy variables to Symbolics.jl variables](https://github.com/jverzani/SymPyPythonCall.jl/blob/6a237d1355d7f6ee3e92652ad900ab74b6930e5e/ext/SymPyPythonCallSymbolicsExt.jl#L16-L22) does not support provenance.

```julia
julia> using Symbolics, SymbolicUtils, PythonCall, SymPyPythonCall, ModelingToolkit

julia> pyexec("import sympy", Main)

julia> eq = pyconvert(Equation, pyeval("sympy.core.sympify('Eq(-x*k, diff(diff(f(x), x), x))')", Main))
Differential(x)(Differential(x)(f(x))) ~ -k*x

julia> ODESystem(eq, name="MyODESystem")
ERROR: ArgumentError: Variable k is not a function of independent variable x.
Stacktrace:
 [1] check_variables(dvs::Vector{SymbolicUtils.BasicSymbolic{Real}}, iv::SymbolicUtils.BasicSymbolic{Real})
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/utils.jl:132
 [2] ODESystem(tag::UInt64, deqs::Vector{Equation}, iv::SymbolicUtils.BasicSymbolic{Real}, dvs::Vector{SymbolicUtils.BasicSymbolic{Real}}, ps::Vector{Any}, tspan::Nothing, var_to_name::Dict{Any, Any}, ctrls::Vector{Any}, observed::Vector{Equation}, tgrad::Base.RefValue{Vector{Num}}, jac::Base.RefValue{Any}, ctrl_jac::Base.RefValue{Any}, Wfact::Base.RefValue{Matrix{Num}}, Wfact_t::Base.RefValue{Matrix{Num}}, name::String, systems::Vector{ODESystem}, defaults::Dict{Any, Any}, torn_matching::Nothing, connector_type::Nothing, preface::Nothing, cevents::Vector{ModelingToolkit.SymbolicContinuousCallback}, devents::Vector{ModelingToolkit.SymbolicDiscreteCallback}, metadata::Nothing, gui_metadata::Nothing, tearing_state::Nothing, substitutions::Nothing, complete::Bool, discrete_subsystems::Nothing, unknown_states::Nothing; checks::Bool)
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/systems/diffeqs/odesystem.jl:152
 [3] ODESystem(deqs::Vector{Equation}, iv::SymbolicUtils.BasicSymbolic{Real}, dvs::Vector{Any}, ps::OrderedCollections.OrderedSet{Any}; controls::Vector{Num}, observed::Vector{Equation}, systems::Vector{ODESystem}, tspan::Nothing, name::String, default_u0::Dict{Any, Any}, default_p::Dict{Any, Any}, defaults::Dict{Any, Any}, connector_type::Nothing, preface::Nothing, continuous_events::Nothing, discrete_events::Nothing, checks::Bool, metadata::Nothing, gui_metadata::Nothing)
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/systems/diffeqs/odesystem.jl:218
 [4] ODESystem(eqs::Vector{Equation}, iv::Nothing; kwargs::Base.Pairs{Symbol, String, Tuple{Symbol}, NamedTuple{(:name,), Tuple{String}}})
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/systems/diffeqs/odesystem.jl:268
 [5] ODESystem(::Equation; kwargs::Base.Pairs{Symbol, String, Tuple{Symbol}, NamedTuple{(:name,), Tuple{String}}})
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/systems/diffeqs/odesystem.jl:303
 [6] top-level scope
   @ REPL[91]:1

julia> fx, k, x = get_variables(eq)
3-element Vector{Any}:
 f(x)
 k
 x

julia> ODESystem(eq,x,[fx],[k], name="MyODESystem")
ERROR: ArgumentError: k is not a parameter.
Stacktrace:
 [1] check_parameters(ps::Vector{SymbolicUtils.BasicSymbolic{Real}}, iv::SymbolicUtils.BasicSymbolic{Real})
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/utils.jl:108
 [2] ODESystem(tag::UInt64, deqs::Vector{Equation}, iv::SymbolicUtils.BasicSymbolic{Real}, dvs::Vector{SymbolicUtils.BasicSymbolic{Real}}, ps::Vector{SymbolicUtils.BasicSymbolic{Real}}, tspan::Nothing, var_to_name::Dict{Any, Any}, ctrls::Vector{Any}, observed::Vector{Equation}, tgrad::Base.RefValue{Vector{Num}}, jac::Base.RefValue{Any}, ctrl_jac::Base.RefValue{Any}, Wfact::Base.RefValue{Matrix{Num}}, Wfact_t::Base.RefValue{Matrix{Num}}, name::String, systems::Vector{ODESystem}, defaults::Dict{Any, Any}, torn_matching::Nothing, connector_type::Nothing, preface::Nothing, cevents::Vector{ModelingToolkit.SymbolicContinuousCallback}, devents::Vector{ModelingToolkit.SymbolicDiscreteCallback}, metadata::Nothing, gui_metadata::Nothing, tearing_state::Nothing, substitutions::Nothing, complete::Bool, discrete_subsystems::Nothing, unknown_states::Nothing; checks::Bool)
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/systems/diffeqs/odesystem.jl:153
 [3] ODESystem(deqs::Vector{Equation}, iv::SymbolicUtils.BasicSymbolic{Real}, dvs::Vector{SymbolicUtils.BasicSymbolic{Real}}, ps::Vector{SymbolicUtils.BasicSymbolic{Real}}; controls::Vector{Num}, observed::Vector{Equation}, systems::Vector{ODESystem}, tspan::Nothing, name::String, default_u0::Dict{Any, Any}, default_p::Dict{Any, Any}, defaults::Dict{Any, Any}, connector_type::Nothing, preface::Nothing, continuous_events::Nothing, discrete_events::Nothing, checks::Bool, metadata::Nothing, gui_metadata::Nothing)
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/systems/diffeqs/odesystem.jl:218
 [4] ODESystem(::Equation, ::SymbolicUtils.BasicSymbolic{Real}, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, String, Tuple{Symbol}, NamedTuple{(:name,), Tuple{String}}})
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/brJDK/src/systems/diffeqs/odesystem.jl:303
 [5] top-level scope
   @ REPL[93]:1

```

How should I tell ModelingToolkit.jl which variables are parameters and which are variables?

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [October 2, 2023, 3:08pm UTC](https://discourse.julialang.org/t/how-to-tell-modelingtoolkit-jl-which-variables-are-parameters/104501/2 "2023-10-02T15:08:22Z")

</div>

I am developing a package/codebase and I needed exactly this information. As far as I can tell, it doens’t exist in the public API. I\ve coded up the following function:

```julia

is_variable(x::Num) = is_variable(x.val)
function is_variable(x)
    if x isa ModelingToolkit.SymbolicUtils.Symbolic
        if haskey(x.metadata, ModelingToolkit.Symbolics.VariableSource)
            src = x.metadata[ModelingToolkit.Symbolics.VariableSource]
            return first(src) == :variables
        end
    end
    return false
end

```

This will return `false` for “variables” defined as `@parameters p = 0.2 c = 0.1` etc.

So you can copy this and change the `:variables` to `:parameters`. I think it will work.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [October 2, 2023, 3:11pm UTC](https://discourse.julialang.org/t/how-to-tell-modelingtoolkit-jl-which-variables-are-parameters/104501/3 "2023-10-02T15:11:01Z")

</div>

Edit: ah, I misunderstood what you need. You need the other-way around. Not to examine if something is a parameter, but to enforce that something is… I know you can create symbolically parameters, so maybe just use their symbol and re-create them using `@parameters` ?

like

```julia
"""
    make_new_named_parameter(variable, value::Num, extra::String, suffix = true)

If `value isa Num`, return `value`. Otherwise, create a new MTK `@parameter`
whose name is created from `variable` by adding the `extra` string.
If `suffix = true` the extra is added at the end after a `_`. Otherwise
it is added at the start, then a `_` and then the variable name.
"""
make_new_named_parameter(v, value::Num, extra, suffix = true) = value
function make_new_named_parameter(v, value::Real, extra, suffix = true)
    # create new string:
    n = string(ModelingToolkit.getname(v))
    newstring = if suffix
        n*"_"*extra
    else
        extra*"_"*n
    end
    varsymbol = Symbol(newstring)
    dummy = (@parameters $(varsymbol) = value)
    return first(dummy)
end

```

(another function I’ve coded up for the aforementioned project, you don’t need the suffix stsuff)

---

<div class="post-metadata">

**Author:** ![Lilith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lilith/32/27492_2.png) [@Lilith](https://discourse.julialang.org/u/Lilith)\
**Post date:** [October 2, 2023, 4:10pm UTC](https://discourse.julialang.org/t/how-to-tell-modelingtoolkit-jl-which-variables-are-parameters/104501/4 "2023-10-02T16:10:28Z")

</div>

I figured out a way to do this without using internals:

```julia
function mark_parameters(eq::Equation, symbols)
    parameters = Dict(sy => only(@parameters($sy)) for sy in symbols)
    substitutions = Dict((var => parameters[Symbol(var)] for var in get_variables(eq) if Symbol(var) in keys(parameters)))
    substitute(eq, substitutions)
end

```

It’s still not as nice as it could theoretically be, but it’s not too bad. I’m still open to hearing alternative approaches.

---

<div class="post-metadata">

**Author:** ![Lilith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lilith/32/27492_2.png) [@Lilith](https://discourse.julialang.org/u/Lilith)\
**Post date:** [October 2, 2023, 4:11pm UTC](https://discourse.julialang.org/t/how-to-tell-modelingtoolkit-jl-which-variables-are-parameters/104501/5 "2023-10-02T16:11:07Z")

</div>

I’m not sure what the code snippets you have shared are for. Would you show how I could use them to pass my equation to ODESystem?
