From 8a951dc361de847e11edb8a0983f6f363eecc91a Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 12:21:17 +0100 Subject: [PATCH 01/13] Copilot-based attempt to update to MTKv11 --- Project.toml | 4 ++-- README.md | 2 +- docs/Project.toml | 2 +- docs/src/index.md | 12 ++++++------ src/ProcessBasedModelling.jl | 4 ++-- src/make.jl | 2 +- src/processes_basic.jl | 2 +- src/utils.jl | 20 ++++++++++---------- test/runtests.jl | 10 +++++----- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Project.toml b/Project.toml index e29a5f9..bd61b31 100644 --- a/Project.toml +++ b/Project.toml @@ -4,10 +4,10 @@ authors = ["Datseris "] version = "1.8.0" [deps] -ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] -ModelingToolkit = "10.0" +ModelingToolkitBase = "1" Reexport = "1.2" julia = "1.9.0" diff --git a/README.md b/README.md index a734813..9634430 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![codecov](https://codecov.io/gh/JuliaDynamics/ProcessBasedModelling.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/JuliaDynamics/ProcessBasedModelling.jl) [![Package Downloads](https://shields.io/endpoint?url=https://pkgs.genieframework.com/api/v1/badge/ProcessBasedModelling)](https://pkgs.genieframework.com?packages=ProcessBasedModelling) -ProcessBasedModelling.jl is an extension to [ModelingToolkit.jl](https://docs.sciml.ai/ModelingToolkit/stable/) (MTK) for building a model of equations using symbolic expressions. +ProcessBasedModelling.jl is an extension to [ModelingToolkitBase.jl](https://docs.sciml.ai/ModelingToolkit/stable/) (and the wider MTK ecosystem) for building a model of equations using symbolic expressions. It is an alternative framework to MTK's [native component-based modelling](https://docs.sciml.ai/ModelingToolkit/stable/tutorials/acausal_components/), but, instead of components, there are "processes". This approach is useful in the modelling of physical/biological/whatever systems, where each variable corresponds to a particular physical concept or observable and there are few (or none) duplicate variables to make the definition of MTK "factories" worthwhile. On the other hand, there plenty of different physical representations, or _processes_ to represent a given physical concept in equation form. diff --git a/docs/Project.toml b/docs/Project.toml index 077d0f9..d4397df 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -4,4 +4,4 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" DocumenterTools = "35a29f4d-8980-5a13-9543-d66fff28ecb8" DynamicalSystems = "61744808-ddfa-5f27-97ff-6e42cc95d634" -ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" diff --git a/docs/src/index.md b/docs/src/index.md index c517685..767ade0 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -8,7 +8,7 @@ ProcessBasedModelling !!! note "Default `t` is unitless" Like ModelingToolkit.jl, ProcessBasedModelling.jl also exports `t` as the independent variable representing time. However, instead of the default `t` of ModelingToolkit.jl, here `t` is unitless. - Do `t = ModelingToolkit.t` to obtain the unitful version of `t`. + Do `t = ModelingToolkitBase.t` to obtain the unitful version of `t`. ## Usage @@ -31,10 +31,10 @@ Let's say we want to build the system of equations y = z - x ``` -symbolically using ModelingToolkit.jl (**MTK**). We define +symbolically using ModelingToolkitBase.jl (**MTKBase**). We define ```@example MAIN -using ModelingToolkit +using ProcessBasedModelling @variables t # independent variable _without_ units @variables z(t) = 0.0 @@ -43,8 +43,8 @@ using ModelingToolkit ``` ProcessBasedModelling.jl (**PBM**) strongly recommends that all defined variables have a default value at definition point. Here we didn't do this for ``x`` to illustrate what how such an "omission" will be treated by **PBM**. -!!! note "ModelingToolkit.jl is re-exported" - ProcessBasedModelling.jl re-exports the whole `ModelingToolkit` package, +!!! note "ModelingToolkitBase.jl is re-exported" + ProcessBasedModelling.jl re-exports the whole `ModelingToolkitBase` package, so you don't need to be `using` both of them, just `using ProcessBasedModelling`. To make the equations we want, we can use MTK directly, and call @@ -69,7 +69,7 @@ model = ODESystem(eqs[1:2], t; name = :example) ```julia # here is the error -model = structural_simplify(model) +model = mtkcompile(model) ``` ``` ERROR: ExtraVariablesSystemException: The system is unbalanced. diff --git a/src/ProcessBasedModelling.jl b/src/ProcessBasedModelling.jl index 7ee3f7d..1965427 100644 --- a/src/ProcessBasedModelling.jl +++ b/src/ProcessBasedModelling.jl @@ -8,8 +8,8 @@ module ProcessBasedModelling end ProcessBasedModelling using Reexport -using ModelingToolkit: t_nounits as t, D_nounits as D -@reexport using ModelingToolkit +using ModelingToolkitBase: t_nounits as t, D_nounits as D +@reexport using ModelingToolkitBase include("API.jl") include("utils.jl") diff --git a/src/make.jl b/src/make.jl index e508c64..ea51774 100644 --- a/src/make.jl +++ b/src/make.jl @@ -122,7 +122,7 @@ function processes_to_mtkeqs(_processes::Vector, default::Dict{Num, Any}; However, a process for $(added_var) was not provided, and there is no default process for it either. Since it has a default value, we make it a parameter by adding a process: - `ParameterProcess($(ModelingToolkit.getname(added_var)))`. + `ParameterProcess($(getname(added_var)))`. """) end parproc = ParameterProcess(added_var) diff --git a/src/processes_basic.jl b/src/processes_basic.jl index 18c0851..e23cdc0 100644 --- a/src/processes_basic.jl +++ b/src/processes_basic.jl @@ -111,7 +111,7 @@ struct AdditionProcess <: Process function AdditionProcess(process, added::Vector) for add in added if typeof(add) <: Union{Process, Equation} - v1, v2 = ModelingToolkit.getname(lhs_variable(process)), ModelingToolkit.getname(lhs_variable(add)) + v1, v2 = getname(lhs_variable(process)), getname(lhs_variable(add)) if v1 ≠ v2 throw(ArgumentError( "Added processes do not have the same lhs variable. Got: $(v1), $(v2)" diff --git a/src/utils.jl b/src/utils.jl index 571af50..ad1ac99 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -37,8 +37,8 @@ function _has_thing(var::Num, vars) return any(isequal(var), vars) end function _has_thing(var::Symbol, vars) - vars = ModelingToolkit.getname.(vars) - var = ModelingToolkit.getname(var) + vars = getname.(vars) + var = getname(var) return any(isequal(var), vars) end @@ -59,9 +59,9 @@ not error on the absence of a default value. """ default_value(x) = x default_value(x::Num) = default_value(x.val) -function default_value(x::ModelingToolkit.SymbolicUtils.Symbolic) - if haskey(x.metadata, ModelingToolkit.Symbolics.VariableDefaultValue) - return x.metadata[ModelingToolkit.Symbolics.VariableDefaultValue] +function default_value(x::ModelingToolkitBase.SymbolicUtils.Symbolic) + if haskey(x.metadata, ModelingToolkitBase.Symbolics.VariableDefaultValue) + return x.metadata[ModelingToolkitBase.Symbolics.VariableDefaultValue] else @warn("No default value assigned to variable/parameter $(x).") return nothing @@ -73,12 +73,12 @@ end is_variable(x::Num) = is_variable(x.val) function is_variable(x) - if x isa ModelingToolkit.SymbolicUtils.Symbolic + if x isa ModelingToolkitBase.SymbolicUtils.Symbolic if isnothing(x.metadata) return false end - if haskey(x.metadata, ModelingToolkit.Symbolics.VariableSource) - src = x.metadata[ModelingToolkit.Symbolics.VariableSource] + if haskey(x.metadata, ModelingToolkitBase.Symbolics.VariableSource) + src = x.metadata[ModelingToolkitBase.Symbolics.VariableSource] return first(src) == :variables end end @@ -111,7 +111,7 @@ Now `p` will be a parameter with name `:τ_x` and default value `0.5`. new_derived_named_parameter(v, value::Num, extra::String; kw...) = value new_derived_named_parameter(v, value::LiteralParameter, extra::String; kw...) = value.p function new_derived_named_parameter(v, value::Real, extra; connector = "_", prefix = true) - n = string(ModelingToolkit.getname(v)) + n = string(getname(v)) newstring = if prefix extra*connector*n else @@ -174,7 +174,7 @@ macro convert_to_parameters(vars...) $binding isa Num, $binding, # Else, convert to modeling toolkit param. # This syntax was obtained by doing @macroexpand @parameters A = 0.5 - (ModelingToolkit.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) + (ModelingToolkitBase.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) )) ) ) diff --git a/test/runtests.jl b/test/runtests.jl index e5cd1ae..c536387 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -128,7 +128,7 @@ end @test length(unknowns(sys)) == 3 sys = processes_to_mtkmodel(procs[1:3]) @test length(unknowns(sys)) == 3 - @test length(unknowns(structural_simplify(sys))) == 2 + @test length(unknowns(mtkcompile(sys))) == 2 end end @@ -139,10 +139,10 @@ end @testset "derived" begin @variables x(t) = 0.5 p = new_derived_named_parameter(x, 0.2, "t") - @test ModelingToolkit.getname(p) == :t_x + @test getname(p) == :t_x @test default_value(p) == 0.2 p = new_derived_named_parameter(x, 0.2, "t"; prefix = false, connector = "") - @test ModelingToolkit.getname(p) == :xt + @test getname(p) == :xt end @testset "convert" begin @@ -151,7 +151,7 @@ end @convert_to_parameters A B C @test A isa Num @test default_value(A) == 0.5 - @test ModelingToolkit.getname(C) == :X + @test getname(C) == :X end @testset "literal in derived" begin @@ -219,7 +219,7 @@ end sys = processes_to_mtkmodel(procs) sys2 = processes_to_mtkmodel([sys, w ~ x*y]) @test length(equations(sys2)) == 4 - @test sort(ModelingToolkit.getname.(unknowns(sys2))) == [:w, :x, :y, :z] + @test sort(getname.(unknowns(sys2))) == [:w, :x, :y, :z] end @testset "equation in RHS" begin From 3a66b9321f475c296738b51cf969a0318ea5fad7 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 12:29:37 +0100 Subject: [PATCH 02/13] bump version/changelog --- CHANGELOG.md | 5 +++++ Project.toml | 2 +- src/make.jl | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb540bd..d18a278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ ProcessBasedModelling.jl follows semver 2.0. Changelog is kept with respect to v1 release. +## 1.9 + +Dependency moved from ModelingToolkit.jl to ModelingToolkitBase.jl, moving in the process to MTK v11. +ProcessBasedModelling.jl was not using any of the advanced functionality of the AGPL licensed MTK parts so this was safe to do. + ## 1.8 - Updated to ModelingToolkit.jl v10. `type` keyword in `processes_to_mtkmodel` is now no longer used. diff --git a/Project.toml b/Project.toml index bd61b31..a4d06c7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ProcessBasedModelling" uuid = "ca969041-2cf3-4b10-bc21-86f4417093eb" authors = ["Datseris "] -version = "1.8.0" +version = "1.9.0" [deps] ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" diff --git a/src/make.jl b/src/make.jl index ea51774..9d71311 100644 --- a/src/make.jl +++ b/src/make.jl @@ -18,7 +18,7 @@ During construction, the following automations improve user experience: `processes` is a `Vector` whose elements can be: -1. Any instance of a subtype of [`Process`](@ref). `Process` is like a +1. Any instance of a subtype of [`Process`](@ref). `Process` is a wrapper around `Equation` that provides some conveniences, e.g., handling of timescales or not having limitations on the left-hand-side (LHS) form. 1. An `Equation`. The LHS format of the equation is limited. @@ -28,7 +28,7 @@ During construction, the following automations improve user experience: 2. A `Vector` of the above two, which is then expanded. This allows the convenience of functions representing a physical process that may require many equations to be defined (because e.g., they may introduce more variables). -3. A ModelingToolkit.jl `XDESystem`, in which case the `equations` of the system are expanded +3. A ModelingToolkit.jl `System`, in which case the `equations` of the system are expanded as if they were given as a vector of equations like above. This allows the convenience of straightforwardly coupling with already existing `XDESystem`s. @@ -59,7 +59,7 @@ These registered default processes are used when `default` is a `Module`. (has happened to me many times!). """ function processes_to_mtkmodel(args...; - type = System, name = :model, independent = t, kw..., + name = :model, independent = t, kw..., ) eqs = processes_to_mtkeqs(args...; kw...) sys = System(eqs, independent; name) From 51393d567f84a145de994fcdc8fd622b8eeae443 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 12:35:42 +0100 Subject: [PATCH 03/13] correct access of Symbolic --- src/utils.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index ad1ac99..e2c1284 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -59,7 +59,7 @@ not error on the absence of a default value. """ default_value(x) = x default_value(x::Num) = default_value(x.val) -function default_value(x::ModelingToolkitBase.SymbolicUtils.Symbolic) +function default_value(x::ModelingToolkitBase.Symbolic) if haskey(x.metadata, ModelingToolkitBase.Symbolics.VariableDefaultValue) return x.metadata[ModelingToolkitBase.Symbolics.VariableDefaultValue] else @@ -73,7 +73,7 @@ end is_variable(x::Num) = is_variable(x.val) function is_variable(x) - if x isa ModelingToolkitBase.SymbolicUtils.Symbolic + if x isa ModelingToolkitBase.Symbolic if isnothing(x.metadata) return false end From db8d12746720da201b607c161ee55b52072ac6e8 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 13:49:49 +0100 Subject: [PATCH 04/13] module compiles now with help from Aayush --- Project.toml | 2 ++ src/utils.jl | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Project.toml b/Project.toml index a4d06c7..6616128 100644 --- a/Project.toml +++ b/Project.toml @@ -5,9 +5,11 @@ version = "1.9.0" [deps] ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" +Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] ModelingToolkitBase = "1" +Symbolics = "7" Reexport = "1.2" julia = "1.9.0" diff --git a/src/utils.jl b/src/utils.jl index e2c1284..08be361 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,3 +1,5 @@ +import Symbolics + """ LiteralParameter(p) @@ -58,27 +60,25 @@ The difference with `ModelingToolkit.getdefault` is that this function will not error on the absence of a default value. """ default_value(x) = x -default_value(x::Num) = default_value(x.val) -function default_value(x::ModelingToolkitBase.Symbolic) - if haskey(x.metadata, ModelingToolkitBase.Symbolics.VariableDefaultValue) - return x.metadata[ModelingToolkitBase.Symbolics.VariableDefaultValue] - else - @warn("No default value assigned to variable/parameter $(x).") - return nothing - end +default_value(x::Num) = default_value(Symbolics.unwrap(x)) +function default_value(x::Symbolics.SymbolicT) + val = Symbolics.getdefaultval(x) + isnothing(val) && @warn("No default value assigned to variable/parameter $(x).") + return val end + # trick to get default values for state variables: # Base.Fix1(getindex, ModelingToolkit.defaults(ssys)).(states(ssys)) # while `defaults` returns all default assignments. -is_variable(x::Num) = is_variable(x.val) +is_variable(x::Num) = is_variable(Symbolics.unwrap(x)) function is_variable(x) - if x isa ModelingToolkitBase.Symbolic + if x isa Symbolics.SymbolicT if isnothing(x.metadata) return false end - if haskey(x.metadata, ModelingToolkitBase.Symbolics.VariableSource) - src = x.metadata[ModelingToolkitBase.Symbolics.VariableSource] + if haskey(x.metadata, Symbolics.VariableSource) + src = x.metadata[Symbolics.VariableSource] return first(src) == :variables end end @@ -134,7 +134,7 @@ Convert all variables `vars` into `@parameters` with name the same as `vars` and default value the same as the value of `vars`. The macro leaves unaltered inputs that are of type `Num`, assumming they are already parameters. It also replaces [`LiteralParameter`](@ref) inputs with its literal values. -This macro is extremely useful to convert e.g., keyword arguments into named parameters, +This macro is useful to convert e.g., keyword arguments into named parameters, while also allowing the user to give custom parameter names, or to leave some keywords as numeric literals. From 435ed85aa803a5078638d42e229f0c0e9c98b0d9 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 15:43:58 +0100 Subject: [PATCH 05/13] continue working on a fix --- src/API.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/API.jl b/src/API.jl index d086fcc..9d4851d 100644 --- a/src/API.jl +++ b/src/API.jl @@ -86,7 +86,7 @@ end rhs(e::Equation) = e.rhs lhs(e::Equation) = e.lhs lhs_variable(e::Equation) = lhs_variable(lhs(e)) -lhs_variable(x::Num) = Num(lhs_variable(x.val)) +lhs_variable(x::Num) = Num(lhs_variable(Symbolics.unwrap(x))) function lhs_variable(x) # basically x is SymbolicUtils.BasicSymbolic{Real} # check whether `x` is a single variable already if is_variable(x) @@ -95,15 +95,15 @@ function lhs_variable(x) # basically x is SymbolicUtils.BasicSymbolic{Real} # check Differential(t)(x) if hasproperty(x, :f) if x.f isa Differential - return x.arguments[1] + return x.args[1] end end # check Differential(t)(x)*parameter - if hasproperty(x, :arguments) - args = x.arguments + if hasproperty(x, :args) + args = x.args di = findfirst(a -> hasproperty(a, :f) && a.f isa Differential, args) if !isnothing(di) - return args[di].arguments[1] + return args[di].args[1] end end # error if all failed From ea65e8762fad5a05148b441f4f735c17ba8b9d74 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 16:22:17 +0100 Subject: [PATCH 06/13] move back to MTK; cant structurally simplify --- CHANGELOG.md | 3 +-- Project.toml | 4 ++-- README.md | 2 +- docs/Project.toml | 1 - docs/src/index.md | 10 +++++----- src/ProcessBasedModelling.jl | 4 ++-- src/utils.jl | 4 ++-- test/runtests.jl | 2 +- 8 files changed, 14 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d18a278..a3074e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,7 @@ Changelog is kept with respect to v1 release. ## 1.9 -Dependency moved from ModelingToolkit.jl to ModelingToolkitBase.jl, moving in the process to MTK v11. -ProcessBasedModelling.jl was not using any of the advanced functionality of the AGPL licensed MTK parts so this was safe to do. +Updated to ModelingToolkit.jl v11, which also comes with a dependency to AGPL license. ## 1.8 diff --git a/Project.toml b/Project.toml index 6616128..78d7bf0 100644 --- a/Project.toml +++ b/Project.toml @@ -4,12 +4,12 @@ authors = ["Datseris "] version = "1.9.0" [deps] -ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" +ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] -ModelingToolkitBase = "1" +ModelingToolkit = "11" Symbolics = "7" Reexport = "1.2" julia = "1.9.0" diff --git a/README.md b/README.md index 9634430..3722b6a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![codecov](https://codecov.io/gh/JuliaDynamics/ProcessBasedModelling.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/JuliaDynamics/ProcessBasedModelling.jl) [![Package Downloads](https://shields.io/endpoint?url=https://pkgs.genieframework.com/api/v1/badge/ProcessBasedModelling)](https://pkgs.genieframework.com?packages=ProcessBasedModelling) -ProcessBasedModelling.jl is an extension to [ModelingToolkitBase.jl](https://docs.sciml.ai/ModelingToolkit/stable/) (and the wider MTK ecosystem) for building a model of equations using symbolic expressions. +ProcessBasedModelling.jl is an extension to [ModelingToolkit.jl](https://docs.sciml.ai/ModelingToolkit/stable/) (and the wider MTK ecosystem) for building a model of equations using symbolic expressions. It is an alternative framework to MTK's [native component-based modelling](https://docs.sciml.ai/ModelingToolkit/stable/tutorials/acausal_components/), but, instead of components, there are "processes". This approach is useful in the modelling of physical/biological/whatever systems, where each variable corresponds to a particular physical concept or observable and there are few (or none) duplicate variables to make the definition of MTK "factories" worthwhile. On the other hand, there plenty of different physical representations, or _processes_ to represent a given physical concept in equation form. diff --git a/docs/Project.toml b/docs/Project.toml index d4397df..7429447 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -4,4 +4,3 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" DocumenterTools = "35a29f4d-8980-5a13-9543-d66fff28ecb8" DynamicalSystems = "61744808-ddfa-5f27-97ff-6e42cc95d634" -ModelingToolkitBase = "7771a370-6774-4173-bd38-47e70ca0b839" diff --git a/docs/src/index.md b/docs/src/index.md index 767ade0..05a0f1d 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -8,7 +8,7 @@ ProcessBasedModelling !!! note "Default `t` is unitless" Like ModelingToolkit.jl, ProcessBasedModelling.jl also exports `t` as the independent variable representing time. However, instead of the default `t` of ModelingToolkit.jl, here `t` is unitless. - Do `t = ModelingToolkitBase.t` to obtain the unitful version of `t`. + Do `t = ModelingToolkit.t` to obtain the unitful version of `t`. ## Usage @@ -31,10 +31,10 @@ Let's say we want to build the system of equations y = z - x ``` -symbolically using ModelingToolkitBase.jl (**MTKBase**). We define +symbolically using ModelingToolkit.jl (**MTKBase**). We define ```@example MAIN -using ProcessBasedModelling +using ProcessBasedModelling # re-exports ModelingToolkit @variables t # independent variable _without_ units @variables z(t) = 0.0 @@ -43,8 +43,8 @@ using ProcessBasedModelling ``` ProcessBasedModelling.jl (**PBM**) strongly recommends that all defined variables have a default value at definition point. Here we didn't do this for ``x`` to illustrate what how such an "omission" will be treated by **PBM**. -!!! note "ModelingToolkitBase.jl is re-exported" - ProcessBasedModelling.jl re-exports the whole `ModelingToolkitBase` package, +!!! note "ModelingToolkit.jl is re-exported" + ProcessBasedModelling.jl re-exports the whole `ModelingToolkit` package, so you don't need to be `using` both of them, just `using ProcessBasedModelling`. To make the equations we want, we can use MTK directly, and call diff --git a/src/ProcessBasedModelling.jl b/src/ProcessBasedModelling.jl index 1965427..7ee3f7d 100644 --- a/src/ProcessBasedModelling.jl +++ b/src/ProcessBasedModelling.jl @@ -8,8 +8,8 @@ module ProcessBasedModelling end ProcessBasedModelling using Reexport -using ModelingToolkitBase: t_nounits as t, D_nounits as D -@reexport using ModelingToolkitBase +using ModelingToolkit: t_nounits as t, D_nounits as D +@reexport using ModelingToolkit include("API.jl") include("utils.jl") diff --git a/src/utils.jl b/src/utils.jl index 08be361..3bf65f2 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -111,7 +111,7 @@ Now `p` will be a parameter with name `:τ_x` and default value `0.5`. new_derived_named_parameter(v, value::Num, extra::String; kw...) = value new_derived_named_parameter(v, value::LiteralParameter, extra::String; kw...) = value.p function new_derived_named_parameter(v, value::Real, extra; connector = "_", prefix = true) - n = string(getname(v)) + n = string(Symbolics.getname(v)) newstring = if prefix extra*connector*n else @@ -174,7 +174,7 @@ macro convert_to_parameters(vars...) $binding isa Num, $binding, # Else, convert to modeling toolkit param. # This syntax was obtained by doing @macroexpand @parameters A = 0.5 - (ModelingToolkitBase.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) + (ModelingToolkit.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) )) ) ) diff --git a/test/runtests.jl b/test/runtests.jl index c536387..1173251 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,7 +19,7 @@ end # If that's the case, we are sure model construction was valid # First, make some default processes - @variables T(t) = 300.0 # temperature, in Kelvin + @variables T(t) = 300.0 # temperature, in Kelvin @variables α(t) # albedo of ice, unitless @variables ε(t) # effective emissivity, unitless solar_constant = 340.25 # W/m^2, already divided by 4 From a24055454cf99a09891b443c2eef2823b5b9db56 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 16:48:42 +0100 Subject: [PATCH 07/13] use public `is_variable` --- src/ProcessBasedModelling.jl | 2 ++ src/utils.jl | 16 ++++------------ test/runtests.jl | 8 ++++---- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/ProcessBasedModelling.jl b/src/ProcessBasedModelling.jl index 7ee3f7d..f727786 100644 --- a/src/ProcessBasedModelling.jl +++ b/src/ProcessBasedModelling.jl @@ -11,6 +11,8 @@ using Reexport using ModelingToolkit: t_nounits as t, D_nounits as D @reexport using ModelingToolkit +getname = ModelingToolkit.SymbolicIndexingInterface.getname + include("API.jl") include("utils.jl") include("default.jl") diff --git a/src/utils.jl b/src/utils.jl index 3bf65f2..78d1952 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -72,17 +72,9 @@ end # while `defaults` returns all default assignments. is_variable(x::Num) = is_variable(Symbolics.unwrap(x)) -function is_variable(x) - if x isa Symbolics.SymbolicT - if isnothing(x.metadata) - return false - end - if haskey(x.metadata, Symbolics.VariableSource) - src = x.metadata[Symbolics.VariableSource] - return first(src) == :variables - end - end - return false +function is_variable(x::Symbolics.SymbolicT) + value = getmetadata(x, Symbolics.VariableSource, nothing) + return value isa Tuple{Symbol, Symbol} && value[1] == :variables end """ @@ -174,7 +166,7 @@ macro convert_to_parameters(vars...) $binding isa Num, $binding, # Else, convert to modeling toolkit param. # This syntax was obtained by doing @macroexpand @parameters A = 0.5 - (ModelingToolkit.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) + (ModelingToolkit.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((SymbolicUtils.Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) )) ) ) diff --git a/test/runtests.jl b/test/runtests.jl index 1173251..186d5c1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -139,10 +139,10 @@ end @testset "derived" begin @variables x(t) = 0.5 p = new_derived_named_parameter(x, 0.2, "t") - @test getname(p) == :t_x + @test ModelingToolkit.SymbolicIndexingInterface.getname(p) == :t_x @test default_value(p) == 0.2 p = new_derived_named_parameter(x, 0.2, "t"; prefix = false, connector = "") - @test getname(p) == :xt + @test ModelingToolkit.SymbolicIndexingInterface.getname(p) == :xt end @testset "convert" begin @@ -151,7 +151,7 @@ end @convert_to_parameters A B C @test A isa Num @test default_value(A) == 0.5 - @test getname(C) == :X + @test ModelingToolkit.SymbolicIndexingInterface.getname(C) == :X end @testset "literal in derived" begin @@ -219,7 +219,7 @@ end sys = processes_to_mtkmodel(procs) sys2 = processes_to_mtkmodel([sys, w ~ x*y]) @test length(equations(sys2)) == 4 - @test sort(getname.(unknowns(sys2))) == [:w, :x, :y, :z] + @test sort(ModelingToolkit.SymbolicIndexingInterface.getname.(unknowns(sys2))) == [:w, :x, :y, :z] end @testset "equation in RHS" begin From dacee371ef54a0b1e55e07d6eaad9006e39c9a54 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 16:50:57 +0100 Subject: [PATCH 08/13] fix variable map error --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 186d5c1..005535e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,7 +66,7 @@ end @test length(unknowns(sys)) == 1 @test has_symbolic_var(equations(sys), T) - u0s = [[300.0], [100.0]] + u0s = [[T => 300.0], [T => 100.0]] ufs = [] for u0 in u0s p = ODEProblem(sys, u0, (0.0, 1000.0*365*24*60*60.0)) From c313bed6f9e441b8de0ac4a0515348dcd907091e Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 16:54:38 +0100 Subject: [PATCH 09/13] fix default value erroring --- src/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index 78d1952..ad22910 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -62,7 +62,7 @@ not error on the absence of a default value. default_value(x) = x default_value(x::Num) = default_value(Symbolics.unwrap(x)) function default_value(x::Symbolics.SymbolicT) - val = Symbolics.getdefaultval(x) + val = Symbolics.getmetadata(x, Symbolics.VariableDefaultValue, nothing) isnothing(val) && @warn("No default value assigned to variable/parameter $(x).") return val end From df93cb4b7bee3e5be5092def7ee11198923454bb Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 17:01:31 +0100 Subject: [PATCH 10/13] fix named parameters macro!!! --- src/utils.jl | 2 +- test/runtests.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index ad22910..e96b87b 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -166,7 +166,7 @@ macro convert_to_parameters(vars...) $binding isa Num, $binding, # Else, convert to modeling toolkit param. # This syntax was obtained by doing @macroexpand @parameters A = 0.5 - (ModelingToolkit.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((SymbolicUtils.Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) + (ModelingToolkit.toparam_validate)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((SymbolicUtils.Sym){SymbolicUtils.SymReal}($varname; type = Real), $binding), Symbolics.VariableSource, (:parameters, $varname)))) )) ) ) diff --git a/test/runtests.jl b/test/runtests.jl index 005535e..6620b78 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,7 +12,7 @@ module TestDefault ], Ref(TestDefault)) end -@testset "ProcessBasedModelling" begin +# @testset "ProcessBasedModelling" begin @testset "construction + evolution" begin # The model, as defined below, is bistable due to ice albedo feedback # so two initial conditions should go to two attractors @@ -255,4 +255,4 @@ end end -end # @testset \ No newline at end of file +# end # @testset \ No newline at end of file From 224303bae8144876f3bd1339f243f071b92b7bc8 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 17:20:23 +0100 Subject: [PATCH 11/13] fix getname not working on differential --- src/utils.jl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index e96b87b..0992b08 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -19,8 +19,9 @@ _literalvalue(p::LiteralParameter) = p.p has_symbolic_var(eqs, var) Return `true` if symbolic variable `var` exists in the equation(s) `eq`, `false` otherwise. -This works for either `@parameters` or `@variables`. -If `var` is a `Symbol` isntead of a `Num`, all variables are converted to their names +This works for either `@parameters` or `@variables` and currently only compares versus these +objects (i.e., it ignores `Differentials` or other complex structures). +If `var` is a `Symbol` instead of a `Num`, all variables are converted to their names and equality is checked on the basis of the name only. has_symbolic_var(model, var) @@ -29,7 +30,8 @@ When given a MTK model (such as `ODESystem`) search in _all_ the equations of th including observed variables. """ function has_symbolic_var(eq::Equation, var) - vars = get_variables(eq) + vars = get_variables(eq) # this includes differentials + vars = filter(x -> is_parameter(x) || is_variable(x), vars) return _has_thing(var, vars) end has_symbolic_var(eqs::Vector{Equation}, var) = any(eq -> has_symbolic_var(eq, var), eqs) @@ -57,7 +59,7 @@ all_equations(model) = vcat(equations(model), observed(model)) Return the default value of a symbolic variable `x` or `nothing` if it doesn't have any. Return `x` if `x` is not a symbolic variable. The difference with `ModelingToolkit.getdefault` is that this function will -not error on the absence of a default value. +not error in the absence of a default value. """ default_value(x) = x default_value(x::Num) = default_value(Symbolics.unwrap(x)) @@ -77,6 +79,12 @@ function is_variable(x::Symbolics.SymbolicT) return value isa Tuple{Symbol, Symbol} && value[1] == :variables end +is_parameter(x::Num) = is_parameter(Symbolics.unwrap(x)) +function is_parameter(x::Symbolics.SymbolicT) + value = getmetadata(x, Symbolics.VariableSource, nothing) + return value isa Tuple{Symbol, Symbol} && value[1] == :parameters +end + """ new_derived_named_parameter(variable, value, extra::String; kw...) From c648ab1bd741a78677e662eaeb3689fa529629a2 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 17:22:22 +0100 Subject: [PATCH 12/13] all tests pass locally --- test/runtests.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 6620b78..97f0bfc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,7 +12,7 @@ module TestDefault ], Ref(TestDefault)) end -# @testset "ProcessBasedModelling" begin +@testset "ProcessBasedModelling" begin @testset "construction + evolution" begin # The model, as defined below, is bistable due to ice albedo feedback # so two initial conditions should go to two attractors @@ -222,7 +222,7 @@ end @test sort(ModelingToolkit.SymbolicIndexingInterface.getname.(unknowns(sys2))) == [:w, :x, :y, :z] end -@testset "equation in RHS" begin +@testset "duplcate equation in RHS" begin @variables z(t) = 0.0 @variables x(t) = 0.0 @variables y(t) = 0.0 @@ -231,7 +231,7 @@ end y ~ z-x, # is an equation, not a process! z ~ (z ~ x^2), ] - @test_throws ["an `<: Equation` type"] processes_to_mtkeqs(procs) + @test_throws ["more than one"] processes_to_mtkeqs(procs) end @testset "not actual process" begin @@ -255,4 +255,4 @@ end end -# end # @testset \ No newline at end of file +end # @testset \ No newline at end of file From 59344a7f4526b6560b645e910d714c3fb72d98d8 Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 14 Jul 2026 21:23:25 +0100 Subject: [PATCH 13/13] ignore lick check errors --- docs/make.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/make.jl b/docs/make.jl index b3b8ccb..7f80f02 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,4 +15,5 @@ pages = [ build_docs_with_style(pages, ProcessBasedModelling; authors = "George Datseris ", + warnonly = [:doctest, :linkcheck], )