From 6a8fff3f7780fa7a9df186668fda9cf98b0bf4bb Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 10 Aug 2023 13:22:12 -0700 Subject: [PATCH 01/32] Fix error in error reporting code. --- src/systems/connectors.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/connectors.jl b/src/systems/connectors.jl index d77f4c4fb9..5249e621f2 100644 --- a/src/systems/connectors.jl +++ b/src/systems/connectors.jl @@ -192,7 +192,7 @@ function connection2set!(connectionsets, namespace, ss, isouter) if domain_ss === nothing domain_ss = s else - names = join(string(map(name, ss)), ",") + names = join(map(string ∘ nameof, ss), ",") error("connect($names) contains multiple source domain connectors. There can only be one!") end else From e5257d6cab372339fb07333bd1dd3230038f5124 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 22 Aug 2023 12:34:33 -0400 Subject: [PATCH 02/32] Consistently initialize before computing the linearization Co-authored-by: Fredrik Bagge Carlson --- Project.toml | 3 ++- src/systems/abstractsystem.jl | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1eb70f40d5..34ed6c5a19 100644 --- a/Project.toml +++ b/Project.toml @@ -31,6 +31,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" @@ -78,6 +79,7 @@ Latexify = "0.11, 0.12, 0.13, 0.14, 0.15, 0.16" MLStyle = "0.4.17" MacroTools = "0.5" NaNMath = "0.3, 1" +OrdinaryDiffEq = "6" RecursiveArrayTools = "2.3" Reexport = "0.2, 1" RuntimeGeneratedFunctions = "0.5.9" @@ -107,7 +109,6 @@ NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" ReferenceTests = "324d217c-45ce-50fc-942e-d289b448e8cf" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index a8ebdd50db..7478a0aeec 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -1,3 +1,4 @@ +using OrdinaryDiffEq const SYSTEM_COUNT = Threads.Atomic{UInt}(0) get_component_type(x::AbstractSystem) = get_gui_metadata(x).type @@ -1294,6 +1295,9 @@ function linearization_function(sys::AbstractSystem, inputs, if u !== nothing # Handle systems without states length(sts) == length(u) || error("Number of state variables ($(length(sts))) does not match the number of input states ($(length(u)))") + prob = ODEProblem(fun, u, (t, t + 1), p) + integ = init(prob, Rodas5P()) + u = integ.u uf = SciMLBase.UJacobianWrapper(fun, t, p) fg_xz = ForwardDiff.jacobian(uf, u) h_xz = ForwardDiff.jacobian(let p = p, t = t From f371df30949ec3f4627faa765c6761e7fbb311d6 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 22 Aug 2023 12:40:51 -0400 Subject: [PATCH 03/32] Fix project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 34ed6c5a19..af373f7a38 100644 --- a/Project.toml +++ b/Project.toml @@ -121,4 +121,4 @@ Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["AmplNLWriter", "BenchmarkTools", "ControlSystemsMTK", "NonlinearSolve", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "OrdinaryDiffEq", "Random", "ReferenceTests", "SafeTestsets", "StableRNGs", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials", "StochasticDelayDiffEq"] +test = ["AmplNLWriter", "BenchmarkTools", "ControlSystemsMTK", "NonlinearSolve", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "Random", "ReferenceTests", "SafeTestsets", "StableRNGs", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials", "StochasticDelayDiffEq"] From 9aa0e0fd77fede8a846aceeace9341716d52dff0 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 22 Aug 2023 16:26:26 -0400 Subject: [PATCH 04/32] Update tests --- test/linearize.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/linearize.jl b/test/linearize.jl index 244f2506da..e72f63f8d2 100644 --- a/test/linearize.jl +++ b/test/linearize.jl @@ -187,7 +187,7 @@ if VERSION >= v"1.8" using ModelingToolkitStandardLibrary using ModelingToolkitStandardLibrary.Blocks using ModelingToolkitStandardLibrary.Mechanical.MultiBody2D - using ModelingToolkitStandardLibrary.Mechanical.Translational + using ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition using ControlSystemsMTK using ControlSystemsMTK.ControlSystemsBase: sminreal, minreal, poles @@ -197,9 +197,9 @@ if VERSION >= v"1.8" D = Differential(t) @named link1 = Link(; m = 0.2, l = 10, I = 1, g = -9.807) - @named cart = Translational.Mass(; m = 1, s = 0) + @named cart = TranslationalPosition.Mass(; m = 1, s = 0) @named fixed = Fixed() - @named force = Force() + @named force = Force(use_support = false) eqs = [connect(link1.TX1, cart.flange) connect(cart.flange, force.flange) @@ -207,12 +207,10 @@ if VERSION >= v"1.8" @named model = ODESystem(eqs, t, [], []; systems = [link1, cart, force, fixed]) def = ModelingToolkit.defaults(model) - for s in states(model) - def[s] = 0 - end - def[link1.x1] = 10 - def[link1.fy1] = -def[link1.g] * def[link1.m] + def[cart.s] = 10 + def[cart.v] = 0 def[link1.A] = -pi / 2 + def[link1.dA] = 0 lin_outputs = [cart.s, cart.v, link1.A, link1.dA] lin_inputs = [force.f.u] @@ -235,11 +233,12 @@ if VERSION >= v"1.8" dummyder = setdiff(states(sysss), states(model)) def = merge(def, Dict(x => 0.0 for x in dummyder)) + def[link1.fy1] = -def[link1.g] * def[link1.m] @test substitute(lsyss.A, def) ≈ lsys.A # We cannot pivot symbolically, so the part where a linear solve is required # is not reliable. - @test substitute(lsyss.B, def)[1:6, :] ≈ lsys.B[1:6, :] + @test substitute(lsyss.B, def)[1:6, 1] ≈ lsys.B[1:6, 1] @test substitute(lsyss.C, def) ≈ lsys.C @test substitute(lsyss.D, def) ≈ lsys.D end From e07cbaff7f10e268b0b6994888d970b1d26bf203 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Wed, 23 Aug 2023 08:22:15 +0200 Subject: [PATCH 05/32] make initialization optional in linearize --- src/systems/abstractsystem.jl | 19 +++++++++++++------ src/systems/diffeqs/abstractodesystem.jl | 1 - 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 7478a0aeec..22f391ff3a 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -1250,7 +1250,7 @@ function io_preprocessing(sys::AbstractSystem, inputs, end """ - lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, kwargs...) + lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, initialize = true, kwargs...) Return a function that linearizes the system `sys`. The function [`linearize`](@ref) provides a higher-level and easier to use interface. @@ -1274,12 +1274,14 @@ The `simplified_sys` has undergone [`structural_simplify`](@ref) and had any occ - `inputs`: A vector of variables that indicate the inputs of the linearized input-output model. - `outputs`: A vector of variables that indicate the outputs of the linearized input-output model. - `simplify`: Apply simplification in tearing. + - `initialize`: If true, a check is performed to ensure that the operating point is consistent (satisfies algebraic equations). If the op is not consistent, initialization is performed. - `kwargs`: Are passed on to `find_solvables!` See also [`linearize`](@ref) which provides a higher-level interface. """ function linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, + initialize = true, kwargs...) sys, diff_idxs, alge_idxs, input_idxs = io_preprocessing(sys, inputs, outputs; simplify, kwargs...) @@ -1295,9 +1297,14 @@ function linearization_function(sys::AbstractSystem, inputs, if u !== nothing # Handle systems without states length(sts) == length(u) || error("Number of state variables ($(length(sts))) does not match the number of input states ($(length(u)))") - prob = ODEProblem(fun, u, (t, t + 1), p) - integ = init(prob, Rodas5P()) - u = integ.u + if initialize && !isempty(alge_idxs) # This is expensive and can be omitted if the user knows that the system is already initialized + residual = fun(u, p, t) + if norm(residual[alge_idxs]) > √(eps(eltype(residual))) + prob = ODEProblem(fun, u, (t, t + 1), p) + integ = init(prob, Rodas5P()) + u = integ.u + end + end uf = SciMLBase.UJacobianWrapper(fun, t, p) fg_xz = ForwardDiff.jacobian(uf, u) h_xz = ForwardDiff.jacobian(let p = p, t = t @@ -1401,7 +1408,7 @@ function linearize_symbolic(sys::AbstractSystem, inputs, if !allow_input_derivatives der_inds = findall(vec(any(!iszero, Bs, dims = 1))) @show typeof(der_inds) - error("Input derivatives appeared in expressions (-g_z\\g_u != 0), the following inputs appeared differentiated: $(ModelingToolkit.inputs(sys)[der_inds]). Call `linear_statespace` with keyword argument `allow_input_derivatives = true` to allow this and have the returned `B` matrix be of double width ($(2nu)), where the last $nu inputs are the derivatives of the first $nu inputs.") + error("Input derivatives appeared in expressions (-g_z\\g_u != 0), the following inputs appeared differentiated: $(ModelingToolkit.inputs(sys)[der_inds]). Call `linearize_symbolic` with keyword argument `allow_input_derivatives = true` to allow this and have the returned `B` matrix be of double width ($(2nu)), where the last $nu inputs are the derivatives of the first $nu inputs.") end B = [B [zeros(nx, nu); Bs]] D = [D zeros(ny, nu)] @@ -1580,7 +1587,7 @@ function linearize(sys, lin_fun; t = 0.0, op = Dict(), allow_input_derivatives = if !iszero(Bs) if !allow_input_derivatives der_inds = findall(vec(any(!=(0), Bs, dims = 1))) - error("Input derivatives appeared in expressions (-g_z\\g_u != 0), the following inputs appeared differentiated: $(inputs(sys)[der_inds]). Call `linear_statespace` with keyword argument `allow_input_derivatives = true` to allow this and have the returned `B` matrix be of double width ($(2nu)), where the last $nu inputs are the derivatives of the first $nu inputs.") + error("Input derivatives appeared in expressions (-g_z\\g_u != 0), the following inputs appeared differentiated: $(inputs(sys)[der_inds]). Call `linearize` with keyword argument `allow_input_derivatives = true` to allow this and have the returned `B` matrix be of double width ($(2nu)), where the last $nu inputs are the derivatives of the first $nu inputs.") end B = [B [zeros(nx, nu); Bs]] D = [D zeros(ny, nu)] diff --git a/src/systems/diffeqs/abstractodesystem.jl b/src/systems/diffeqs/abstractodesystem.jl index 30b237c807..ae04b67ce5 100644 --- a/src/systems/diffeqs/abstractodesystem.jl +++ b/src/systems/diffeqs/abstractodesystem.jl @@ -687,7 +687,6 @@ function get_u0_p(sys, use_union = false, tofloat = !use_union, symbolic_u0 = false) - eqs = equations(sys) dvs = states(sys) ps = parameters(sys) From 78b9f1a5ede00920dbb9ff55ed761d2a2ae9b899 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Wed, 23 Aug 2023 08:22:47 +0200 Subject: [PATCH 06/32] improve linearization docstring --- src/systems/abstractsystem.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 22f391ff3a..83a28a259c 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -1453,8 +1453,8 @@ function markio!(state, orig_inputs, inputs, outputs; check = true) end """ - (; A, B, C, D), simplified_sys = linearize(sys, inputs, outputs; t=0.0, op = Dict(), allow_input_derivatives = false, kwargs...) - (; A, B, C, D) = linearize(simplified_sys, lin_fun; t=0.0, op = Dict(), allow_input_derivatives = false) + (; A, B, C, D), simplified_sys = linearize(sys, inputs, outputs; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false, kwargs...) + (; A, B, C, D) = linearize(simplified_sys, lin_fun; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false) Return a NamedTuple with the matrices of a linear statespace representation on the form @@ -1474,6 +1474,8 @@ the default values of `sys` are used. If `allow_input_derivatives = false`, an error will be thrown if input derivatives (``u̇``) appear as inputs in the linearized equations. If input derivatives are allowed, the returned `B` matrix will be of double width, corresponding to the input `[u; u̇]`. +`zero_dummy_der` can be set to automatically set the operating point to zero for all dummy derivatives. + See also [`linearization_function`](@ref) which provides a lower-level interface, [`linearize_symbolic`](@ref) and [`ModelingToolkit.reorder_states`](@ref). See extended help for an example. From 12c83d44d4a5aeead605626f70632d3795ae2553 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 23 Aug 2023 10:31:32 -0400 Subject: [PATCH 07/32] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index af373f7a38..5a2edb6515 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkit" uuid = "961ee093-0014-501f-94e3-6117800e7a78" authors = ["Yingbo Ma ", "Chris Rackauckas and contributors"] -version = "8.64.0" +version = "8.65.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From bed9504e6e2828bd640cfe41b8dca900e76ec3a2 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Sat, 29 Jul 2023 17:28:20 +0200 Subject: [PATCH 08/32] fix #2215 --- src/systems/abstractsystem.jl | 7 ++++++- test/linearize.jl | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 83a28a259c..7bb8ef9158 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -1424,7 +1424,12 @@ function markio!(state, orig_inputs, inputs, outputs; check = true) outputset = Dict{Any, Bool}(o => false for o in outputs) for (i, v) in enumerate(fullvars) if v in keys(inputset) - v = setio(v, true, false) + if v in keys(outputset) + v = setio(v, true, true) + outputset[v] = true + else + v = setio(v, true, false) + end inputset[v] = true fullvars[i] = v elseif v in keys(outputset) diff --git a/test/linearize.jl b/test/linearize.jl index e72f63f8d2..8561d26819 100644 --- a/test/linearize.jl +++ b/test/linearize.jl @@ -19,6 +19,13 @@ lsys, ssys = linearize(sys, [r], [y]) @test lsys.C[] == 1 @test lsys.D[] == 0 +lsys, ssys = linearize(sys, [r], [r]) + +@test lsys.A[] == -2 +@test lsys.B[] == 1 +@test lsys.C[] == 0 +@test lsys.D[] == 1 + ## ``` From a52649e4e8b62d32adfadc440be5d0cff380cb9e Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 28 Aug 2023 13:12:45 +0530 Subject: [PATCH 09/32] fix: evaluate the metadata expr - Ex: units are now stored as a Unitlike term instead of an expression. --- src/systems/model_parsing.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 74d236d38d..cea177e13e 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -198,7 +198,13 @@ function set_var_metadata(a, ms) end function get_var(mod::Module, b) - b isa Symbol ? getproperty(mod, b) : b + if b isa Symbol + getproperty(mod, b) + elseif b isa Expr + Core.eval(mod, b) + else + b + end end function mtkmodel_macro(mod, name, expr) From c76c9aed6ecce51557cc060d9c50de2381ac5254 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 28 Aug 2023 13:14:12 +0530 Subject: [PATCH 10/32] test: metadata of parameters defined within MTKModel macro --- test/model_parsing.jl | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 17be63c955..a1d7295346 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -1,14 +1,16 @@ using ModelingToolkit, Test using ModelingToolkit: get_gui_metadata, VariableDescription, getdefault using URIs: URI +using Distributions +using Unitful ENV["MTK_ICONS_DIR"] = "$(@__DIR__)/icons" @connector RealInput begin - u(t), [input = true] + u(t), [input = true, unit = u"V"] end @connector RealOutput begin - u(t), [output = true] + u(t), [output = true, unit = u"V"] end @mtkmodel Constant begin @components begin @@ -22,12 +24,12 @@ end end end -@variables t +@variables t [unit = u"s"] D = Differential(t) @connector Pin begin - v(t) # Potential at the pin [V] - i(t), [connect = Flow] # Current flowing into the pin [A] + v(t), [unit = u"V"] # Potential at the pin [V] + i(t), [connect = Flow, unit = u"A"] # Current flowing into the pin [A] @icon "pin.png" end @@ -41,8 +43,8 @@ end n = Pin() end @variables begin - v(t) - i(t) + v(t), [unit = u"V"] + i(t), [unit = u"A"] end @icon "oneport.png" @equations begin @@ -70,7 +72,7 @@ resistor_log = "$(@__DIR__)/logo/resistor.svg" @mtkmodel Resistor begin @extend v, i = oneport = OnePort() @parameters begin - R + R, [unit = u"Ω"] end @icon begin """ @@ -95,7 +97,7 @@ end @mtkmodel Capacitor begin @parameters begin - C + C, [unit = u"F"] end @extend v, i = oneport = OnePort(; v = 0.0) @icon "https://upload.wikimedia.org/wikipedia/commons/7/78/Capacitor_symbol.svg" @@ -218,8 +220,8 @@ getdefault(a.b.j) == 30 getdefault(a.b.k) == 40 metadata = Dict(:description => "Variable to test metadata in the Model.structure", - :input => true, :bounds => :((-1, 1)), :connection_type => :Flow, :integer => true, - :binary => false, :tunable => false, :disturbance => true, :dist => :(Normal(1, 1))) + :input => true, :bounds => (-1, 1), :connection_type => :Flow, :integer => true, + :binary => false, :tunable => false, :disturbance => true, :dist => Normal(1, 1)) @connector MockMeta begin m(t), From 5570a786efaf27e3cfd3e17721eaf8918f3a10ec Mon Sep 17 00:00:00 2001 From: spaette Date: Mon, 28 Aug 2023 16:13:16 -0500 Subject: [PATCH 11/32] typos --- CONTRIBUTING.md | 2 +- docs/src/basics/FAQ.md | 2 +- src/ModelingToolkit.jl | 2 +- src/bipartite_graph.jl | 6 +++--- src/inputoutput.jl | 8 ++++---- src/structural_transformation/bareiss.jl | 6 +++--- src/structural_transformation/partial_state_selection.jl | 2 +- src/structural_transformation/symbolics_tearing.jl | 4 ++-- src/systems/alias_elimination.jl | 4 ++-- src/systems/diffeqs/modelingtoolkitize.jl | 2 +- src/systems/diffeqs/odesystem.jl | 2 +- src/systems/diffeqs/sdesystem.jl | 4 ++-- src/systems/jumps/jumpsystem.jl | 4 ++-- src/systems/model_parsing.jl | 2 +- src/systems/sparsematrixclil.jl | 2 +- src/variables.jl | 2 +- test/clock.jl | 2 +- test/lowering_solving.jl | 2 +- test/runtests.jl | 4 ++-- test/structural_transformation/bareiss.jl | 4 ++-- 20 files changed, 33 insertions(+), 33 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 57a2171b50..9e2640e751 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,3 @@ - This repository follows the [SciMLStyle](https://github.com/SciML/SciMLStyle) and the SciML [ColPrac](https://github.com/SciML/ColPrac). - - Please run `using JuliaFormatter, ModelingToolkit; format(joinpath(dirname(pathof(ModelingToolkit)), ".."))` before commiting. + - Please run `using JuliaFormatter, ModelingToolkit; format(joinpath(dirname(pathof(ModelingToolkit)), ".."))` before committing. - Add tests for any new features. diff --git a/docs/src/basics/FAQ.md b/docs/src/basics/FAQ.md index 75c9bb4cca..5aeedfb75e 100644 --- a/docs/src/basics/FAQ.md +++ b/docs/src/basics/FAQ.md @@ -120,7 +120,7 @@ We can solve this problem by using the `missing_variable_defaults()` function prob = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0,1)) ``` -This function provides 0 for the default values, which is a safe assumption for dummy derivatives of most models. However, the 2nd arument allows for a different default value or values to be used if needed. +This function provides 0 for the default values, which is a safe assumption for dummy derivatives of most models. However, the 2nd argument allows for a different default value or values to be used if needed. ``` julia> ModelingToolkit.missing_variable_defaults(sys, [1,2,3]) diff --git a/src/ModelingToolkit.jl b/src/ModelingToolkit.jl index 07f03e5c8e..2157c4093d 100644 --- a/src/ModelingToolkit.jl +++ b/src/ModelingToolkit.jl @@ -106,7 +106,7 @@ abstract type AbstractOptimizationSystem <: AbstractTimeIndependentSystem end function independent_variable end -# this has to be included early to deal with depency issues +# this has to be included early to deal with dependency issues include("structural_transformation/bareiss.jl") function complete end function var_derivative! end diff --git a/src/bipartite_graph.jl b/src/bipartite_graph.jl index 128460c955..1168d1ad24 100644 --- a/src/bipartite_graph.jl +++ b/src/bipartite_graph.jl @@ -618,7 +618,7 @@ Essentially the graph adapter performs two largely orthogonal functions 1. It pairs an undirected bipartite graph with a matching of the destination vertex. This matching is used to induce an orientation on the otherwise undirected graph: - Matched edges pass from destination to source [source to desination], all other edges + Matched edges pass from destination to source [source to destination], all other edges pass in the opposite direction. 2. It exposes the graph view obtained by contracting the destination [source] vertices @@ -632,7 +632,7 @@ graph is acyclic. # Hypergraph interpretation Consider the bipartite graph `B` as the incidence graph of some hypergraph `H`. -Note that a maching `M` on `B` in the above sense is equivalent to determining +Note that a matching `M` on `B` in the above sense is equivalent to determining an (1,n)-orientation on the hypergraph (i.e. each directed hyperedge has exactly one head, but any arbitrary number of tails). In this setting, this is simply the graph formed by expanding each directed hyperedge into `n` ordinary edges @@ -685,7 +685,7 @@ function Base.iterate(c::CMONeighbors{false}, (l, state...)) r === nothing && return nothing # If this is a matched edge, skip it, it's reversed in the induced # directed graph. Otherwise, if there is no matching for this destination - # edge, also skip it, since it got delted in the contraction. + # edge, also skip it, since it got deleted in the contraction. vsrc = c.g.matching[r[1]] if vsrc === c.v || !isa(vsrc, Int) state = (r[2],) diff --git a/src/inputoutput.jl b/src/inputoutput.jl index 99b8bdca85..fe3dfb29fb 100644 --- a/src/inputoutput.jl +++ b/src/inputoutput.jl @@ -112,13 +112,13 @@ end same_or_inner_namespace(u, var) Determine whether `var` is in the same namespace as `u`, or a namespace internal to the namespace of `u`. -Example: `sys.u ~ sys.inner.u` will bind `sys.inner.u`, but `sys.u` remains an unbound, external signal. The namepsaced signal `sys.inner.u` lives in a namspace internal to `sys`. +Example: `sys.u ~ sys.inner.u` will bind `sys.inner.u`, but `sys.u` remains an unbound, external signal. The namespaced signal `sys.inner.u` lives in a namespace internal to `sys`. """ function same_or_inner_namespace(u, var) nu = get_namespace(u) nv = get_namespace(var) nu == nv || # namespaces are the same - startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namepsace to nu + startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namespace to nu occursin('₊', string(getname(var))) && !occursin('₊', string(getname(u))) # or u is top level but var is internal end @@ -127,7 +127,7 @@ function inner_namespace(u, var) nu = get_namespace(u) nv = get_namespace(var) nu == nv && return false - startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namepsace to nu + startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namespace to nu occursin('₊', string(getname(var))) && !occursin('₊', string(getname(u))) # or u is top level but var is internal end @@ -177,7 +177,7 @@ f_ip : (xout,x,u,p,t) -> nothing The return values also include the remaining states and parameters, in the order they appear as arguments to `f`. -If `disturbance_inputs` is an array of variables, the generated dynamics function will preserve any state and dynamics associated with distrubance inputs, but the distrubance inputs themselves will not be included as inputs to the generated function. The use case for this is to generate dynamics for state observers that estimate the influence of unmeasured disturbances, and thus require state variables for the disturbance model, but without disturbance inputs since the disturbances are not available for measurement. +If `disturbance_inputs` is an array of variables, the generated dynamics function will preserve any state and dynamics associated with disturbance inputs, but the disturbance inputs themselves will not be included as inputs to the generated function. The use case for this is to generate dynamics for state observers that estimate the influence of unmeasured disturbances, and thus require state variables for the disturbance model, but without disturbance inputs since the disturbances are not available for measurement. See [`add_input_disturbance`](@ref) for a higher-level interface to this functionality. # Example diff --git a/src/structural_transformation/bareiss.jl b/src/structural_transformation/bareiss.jl index 8ba8da2ffd..3a65f74218 100644 --- a/src/structural_transformation/bareiss.jl +++ b/src/structural_transformation/bareiss.jl @@ -34,7 +34,7 @@ else function Base.swapcols!(A::AbstractSparseMatrixCSC, i, j) i == j && return - # For simplicitly, let i denote the smaller of the two columns + # For simplicity, let i denote the smaller of the two columns j < i && @swap(i, j) colptr = getcolptr(A) @@ -72,7 +72,7 @@ else return nothing end function swaprows!(A::AbstractSparseMatrixCSC, i, j) - # For simplicitly, let i denote the smaller of the two rows + # For simplicity, let i denote the smaller of the two rows j < i && @swap(i, j) rows = rowvals(A) @@ -184,7 +184,7 @@ const bareiss_virtcolswap = ((M, i, j) -> nothing, swaprows!, Perform Bareiss's fraction-free row-reduction algorithm on the matrix `M`. Optionally, a specific pivoting method may be specified. -swap_strategy is an optional argument that determines how the swapping of rows and coulmns is performed. +swap_strategy is an optional argument that determines how the swapping of rows and columns is performed. bareiss_colswap (the default) swaps the columns and rows normally. bareiss_virtcolswap pretends to swap the columns which can be faster for sparse matrices. """ diff --git a/src/structural_transformation/partial_state_selection.jl b/src/structural_transformation/partial_state_selection.jl index 9338cd585b..f2fec0269f 100644 --- a/src/structural_transformation/partial_state_selection.jl +++ b/src/structural_transformation/partial_state_selection.jl @@ -209,7 +209,7 @@ function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, ja J = nothing else _J = jac(eqs, vars) - # only accecpt small intergers to avoid overflow + # only accept small integers to avoid overflow is_all_small_int = all(_J) do x′ x = unwrap(x′) x isa Number || return false diff --git a/src/structural_transformation/symbolics_tearing.jl b/src/structural_transformation/symbolics_tearing.jl index 63b9e3ab14..5b0fa727fc 100644 --- a/src/structural_transformation/symbolics_tearing.jl +++ b/src/structural_transformation/symbolics_tearing.jl @@ -103,7 +103,7 @@ function full_equations(sys::AbstractSystem; simplify = false) if rhs isa Symbolic return 0 ~ rhs else # a number - error("tearing failled because the system is singular") + error("tearing failed because the system is singular") end end eq @@ -194,7 +194,7 @@ function to_mass_matrix_form(neweqs, ieq, graph, fullvars, isdervar::F, return (new_lhs ~ new_rhs), invview(var_to_diff)[dervar] else # a number if abs(rhs) > 100eps(float(rhs)) - @warn "The equation $eq is not consistent. It simplifed to 0 == $rhs." + @warn "The equation $eq is not consistent. It simplified to 0 == $rhs." end return nothing end diff --git a/src/systems/alias_elimination.jl b/src/systems/alias_elimination.jl index 44364406fd..0437cb72e6 100644 --- a/src/systems/alias_elimination.jl +++ b/src/systems/alias_elimination.jl @@ -200,7 +200,7 @@ count_nonzeros(a::CLILVector) = nnz(a) # Linear variables are highest order differentiated variables that only appear # in linear equations with only linear variables. Also, if a variable's any -# derivaitves is nonlinear, then all of them are not linear variables. +# derivatives is nonlinear, then all of them are not linear variables. function find_linear_variables(graph, linear_equations, var_to_diff, irreducibles) stack = Int[] linear_variables = falses(length(var_to_diff)) @@ -273,7 +273,7 @@ function aag_bareiss!(structure, mm_orig::SparseMatrixCLIL{T, Ti}) where {T, Ti} # linear algebraic equations can be set to 0. # # For all the other variables, we can update the original system with - # Bareiss'ed coefficients as Gaussian elimination is nullspace perserving + # Bareiss'ed coefficients as Gaussian elimination is nullspace preserving # and we are only working on linear homogeneous subsystem. is_algebraic = let var_to_diff = var_to_diff diff --git a/src/systems/diffeqs/modelingtoolkitize.jl b/src/systems/diffeqs/modelingtoolkitize.jl index 4aeccab8bc..c29ef022c6 100644 --- a/src/systems/diffeqs/modelingtoolkitize.jl +++ b/src/systems/diffeqs/modelingtoolkitize.jl @@ -37,7 +37,7 @@ function modelingtoolkitize(prob::DiffEqBase.ODEProblem; kwargs...) elseif v in var_set D(v) else - error("Non-permuation mass matrix is not supported.") + error("Non-permutation mass matrix is not supported.") end end end diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index ab66628a54..e368df4f1b 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -492,7 +492,7 @@ of [cumulative_var1 => x + y, cumulative_var2 => x^2] ``` Then, cumulative variables `cumulative_var1` and `cumulative_var2` that computes -the comulative `x + y` and `x^2` would be added to `sys`. +the cumulative `x + y` and `x^2` would be added to `sys`. """ function add_accumulations(sys::ODESystem, vars::Vector{<:Pair}) eqs = get_eqs(sys) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index b1f9b945b2..31ac69e58a 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -227,7 +227,7 @@ end """ $(TYPEDSIGNATURES) -Choose correction_factor=-1//2 (1//2) to converte Ito -> Stratonovich (Stratonovich->Ito). +Choose correction_factor=-1//2 (1//2) to convert Ito -> Stratonovich (Stratonovich->Ito). """ function stochastic_integral_transform(sys::SDESystem, correction_factor) name = nameof(sys) @@ -329,7 +329,7 @@ simmod = solve(ensemble_probmod,EM(),dt=dt,trajectories=numtraj) function Girsanov_transform(sys::SDESystem, u; θ0 = 1.0) name = nameof(sys) - # register new varible θ corresponding to 1D correction process θ(t) + # register new variable θ corresponding to 1D correction process θ(t) t = get_iv(sys) D = Differential(t) @variables θ(t), weight(t) diff --git a/src/systems/jumps/jumpsystem.jl b/src/systems/jumps/jumpsystem.jl index 71cfd97c50..910e4fc57b 100644 --- a/src/systems/jumps/jumpsystem.jl +++ b/src/systems/jumps/jumpsystem.jl @@ -1,6 +1,6 @@ const JumpType = Union{VariableRateJump, ConstantRateJump, MassActionJump} -# modifies the expression representating an affect function to +# modifies the expression representing an affect function to # call reset_aggregated_jumps!(integrator). # assumes iip function _reset_aggregator!(expr, integrator) @@ -391,7 +391,7 @@ function JumpProcesses.JumpProblem(js::JumpSystem, prob, aggregator; callback = eqs = equations(js) invttype = prob.tspan[1] === nothing ? Float64 : typeof(1 / prob.tspan[2]) - # handling parameter substition and empty param vecs + # handling parameter substitution and empty param vecs p = (prob.p isa DiffEqBase.NullParameters || prob.p === nothing) ? Num[] : prob.p majpmapper = JumpSysMajParamMapper(js, p; jseqs = eqs, rateconsttype = invttype) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index cea177e13e..1fd1bdc25c 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -301,7 +301,7 @@ end function component_args!(a, b, expr, kwargs) # Whenever `b` is a function call, skip the first arg aka the function name. - # Whenver it is a kwargs list, include it. + # Whenever it is a kwargs list, include it. start = b.head == :call ? 2 : 1 for i in start:lastindex(b.args) arg = b.args[i] diff --git a/src/systems/sparsematrixclil.jl b/src/systems/sparsematrixclil.jl index d54c3df3f8..a44b0d9a8c 100644 --- a/src/systems/sparsematrixclil.jl +++ b/src/systems/sparsematrixclil.jl @@ -170,7 +170,7 @@ function bareiss_update_virtual_colswap_mtk!(zero!, M::SparseMatrixCLIL, k, swap pivot_equal = pivot_equal_optimization && abs(pivot) == abs(last_pivot) for ei in (k + 1):size(M, 1) - # elimate `v` + # eliminate `v` coeff = 0 ivars = eadj[ei] vj = findfirst(isequal(vpivot), ivars) diff --git a/src/variables.jl b/src/variables.jl index 4d11193462..f1f7edb1df 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -33,7 +33,7 @@ setoutput(x, v) = setmetadata(x, VariableOutput, v) setio(x, i, o) = setoutput(setinput(x, i), o) isinput(x) = isvarkind(VariableInput, x) isoutput(x) = isvarkind(VariableOutput, x) -# Before the solvability check, we already have handled IO varibales, so +# Before the solvability check, we already have handled IO variables, so # irreducibility is independent from IO. isirreducible(x) = isvarkind(VariableIrreducible, x) state_priority(x) = convert(Float64, getmetadata(x, VariableStatePriority, 0.0))::Float64 diff --git a/test/clock.jl b/test/clock.jl index 7301f132a8..42074b65ff 100644 --- a/test/clock.jl +++ b/test/clock.jl @@ -54,7 +54,7 @@ By inference: =# #= - D(x) ~ Shift(x, 0, dt) + 1 # this should never meet with continous variables + D(x) ~ Shift(x, 0, dt) + 1 # this should never meet with continuous variables => (Shift(x, 0, dt) - Shift(x, -1, dt))/dt ~ Shift(x, 0, dt) + 1 => Shift(x, 0, dt) - Shift(x, -1, dt) ~ Shift(x, 0, dt) * dt + dt => Shift(x, 0, dt) - Shift(x, 0, dt) * dt ~ Shift(x, -1, dt) + dt diff --git a/test/lowering_solving.jl b/test/lowering_solving.jl index 2d544dfaf0..712a2a3595 100644 --- a/test/lowering_solving.jl +++ b/test/lowering_solving.jl @@ -17,7 +17,7 @@ eqs2 = [0 ~ x * y - k, D(z) ~ x * y - β * z] @named sys2 = ODESystem(eqs2, t, [x, y, z, k], parameters(sys′)) sys2 = ode_order_lowering(sys2) -# test equation/varible ordering +# test equation/variable ordering ModelingToolkit.calculate_massmatrix(sys2) == Diagonal([1, 1, 1, 1, 0]) u0 = [D(x) => 2.0, diff --git a/test/runtests.jl b/test/runtests.jl index b3c017006d..79321c977f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -31,10 +31,10 @@ using SafeTestsets, Test @safetestset "StructuralTransformations" include("structural_transformation/runtests.jl") @safetestset "State Selection Test" include("state_selection.jl") @safetestset "Symbolic Event Test" include("symbolic_events.jl") -@safetestset "Stream Connnect Test" include("stream_connectors.jl") +@safetestset "Stream Connect Test" include("stream_connectors.jl") @safetestset "Lowering Integration Test" include("lowering_solving.jl") @safetestset "Test Big System Usage" include("bigsystem.jl") -@safetestset "Depdendency Graph Test" include("dep_graphs.jl") +@safetestset "Dependency Graph Test" include("dep_graphs.jl") @safetestset "Function Registration Test" include("function_registration.jl") @safetestset "Precompiled Modules Test" include("precompile_test.jl") @testset "Distributed Test" begin diff --git a/test/structural_transformation/bareiss.jl b/test/structural_transformation/bareiss.jl index 97d42d2394..5d0a10c30c 100644 --- a/test/structural_transformation/bareiss.jl +++ b/test/structural_transformation/bareiss.jl @@ -18,10 +18,10 @@ end @testset "bareiss tests" begin # copy gives a dense matrix @testset "bareiss tests: $T" for T in (copy, sparse) - # matrix determinent pairs + # matrix determinant pairs for (M, d) in ((BigInt[9 1 8 0; 0 0 8 7; 7 6 8 3; 2 9 7 7], -1), (BigInt[1 big(2)^65+1; 3 4], 4 - 3 * (big(2)^65 + 1))) - # test that the determinent was correctly computed + # test that the determinant was correctly computed @test det_bareiss!(T(M)) == d end end From 81166d52b5506ad9170b611c01e07c7ffda2bb90 Mon Sep 17 00:00:00 2001 From: Sathvik Bhagavan Date: Tue, 29 Aug 2023 07:26:14 +0000 Subject: [PATCH 12/32] docs: fix the dependency graphs example in the docstrings --- src/systems/dependency_graphs.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/systems/dependency_graphs.jl b/src/systems/dependency_graphs.jl index abfc8d7343..7a69d2bc18 100644 --- a/src/systems/dependency_graphs.jl +++ b/src/systems/dependency_graphs.jl @@ -22,8 +22,8 @@ rate₁ = β * S * I rate₂ = γ * I + t affect₁ = [S ~ S - 1, I ~ I + 1] affect₂ = [I ~ I - 1, R ~ R + 1] -j₁ = ConstantRateJump(rate₁, affect₁) -j₂ = VariableRateJump(rate₂, affect₂) +j₁ = ModelingToolkit.ConstantRateJump(rate₁, affect₁) +j₂ = ModelingToolkit.VariableRateJump(rate₂, affect₂) # create a JumpSystem using these jumps @named jumpsys = JumpSystem([j₁, j₂], t, [S, I, R], [β, γ]) @@ -66,8 +66,8 @@ Example: Continuing the example started in [`equation_dependencies`](@ref) ```julia -digr = asgraph(equation_dependencies(odesys), - Dict(s => i for (i, s) in enumerate(states(odesys)))) +digr = asgraph(equation_dependencies(jumpsys), + Dict(s => i for (i, s) in enumerate(states(jumpsys)))) ``` """ function asgraph(eqdeps, vtois) @@ -109,7 +109,7 @@ Example: Continuing the example started in [`equation_dependencies`](@ref) ```julia -digr = asgraph(odesys) +digr = asgraph(jumpsys) ``` """ function asgraph(sys::AbstractSystem; variables = states(sys), @@ -136,7 +136,7 @@ Example: Continuing the example of [`equation_dependencies`](@ref) ```julia -variable_dependencies(odesys) +variable_dependencies(jumpsys) ``` """ function variable_dependencies(sys::AbstractSystem; variables = states(sys), @@ -188,7 +188,7 @@ Example: Continuing the example in [`asgraph`](@ref) ```julia -dg = asdigraph(digr) +dg = asdigraph(digr, jumpsys) ``` """ function asdigraph(g::BipartiteGraph, sys::AbstractSystem; variables = states(sys), @@ -230,7 +230,7 @@ Example: Continuing the example of `equation_dependencies` ```julia -eqeqdep = eqeq_dependencies(asgraph(odesys), variable_dependencies(odesys)) +eqeqdep = eqeq_dependencies(asgraph(jumpsys), variable_dependencies(jumpsys)) ``` """ function eqeq_dependencies(eqdeps::BipartiteGraph{T}, @@ -269,7 +269,7 @@ Example: Continuing the example of `equation_dependencies` ```julia -varvardep = varvar_dependencies(asgraph(odesys), variable_dependencies(odesys)) +varvardep = varvar_dependencies(asgraph(jumpsys), variable_dependencies(jumpsys)) ``` """ function varvar_dependencies(eqdeps::BipartiteGraph{T}, From 90fb06318b63a1c6667f0dcfbb1e66b8de956161 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:39:24 +0530 Subject: [PATCH 13/32] feat: `@connector` now supports `@parameters`, `@equations`, `@extend`, `@variables`, `@components` and a `begin...end` block along with variables specified without a begin block --- src/systems/model_parsing.jl | 118 ++++++++++++----------------------- 1 file changed, 41 insertions(+), 77 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 1fd1bdc25c..dc9ff68b83 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -6,58 +6,64 @@ end (m::Model)(args...; kw...) = m.f(args...; kw...) for f in (:connector, :mtkmodel) + isconnector = f == :connector ? true : false @eval begin macro $f(name::Symbol, body) - esc($(Symbol(f, :_macro))(__module__, name, body)) + esc($(:_model_macro)(__module__, name, body, $isconnector)) end end end -@inline is_kwarg(::Symbol) = false -@inline is_kwarg(e::Expr) = (e.head == :parameters) - -function connector_macro(mod, name, body) - if !Meta.isexpr(body, :block) - err = """ - connector body must be a block! It should be in the form of - ``` - @connector Pin begin - v(t) = 1 - (i(t) = 1), [connect = Flow] - end - ``` - """ - error(err) - end - vs = [] - kwargs = [] - icon = Ref{Union{String, URI}}() +function _model_macro(mod, name, expr, isconnector) + exprs = Expr(:block) dict = Dict{Symbol, Any}() dict[:kwargs] = Dict{Symbol, Any}() - expr = Expr(:block) - for arg in body.args + comps = Symbol[] + ext = Ref{Any}(nothing) + eqs = Expr[] + icon = Ref{Union{String, URI}}() + vs = [] + ps = [] + kwargs = [] + + for arg in expr.args arg isa LineNumberNode && continue - if arg.head == :macrocall && arg.args[1] == Symbol("@icon") - parse_icon!(icon, dict, dict, arg.args[end]) - continue + if arg.head == :macrocall + parse_model!(exprs.args, comps, ext, eqs, icon, vs, ps, + dict, mod, arg, kwargs) + elseif arg.head == :block + push!(exprs.args, arg) + elseif isconnector + # Connectors can have variables listed without `@variables` prefix or + # begin block. + parse_variable_arg!(exprs, vs, dict, mod, arg, :variables, kwargs) + else + error("$arg is not valid syntax. Expected a macro call.") end - parse_variable_arg!(expr, vs, dict, mod, arg, :variables, kwargs) end + iv = get(dict, :independent_variable, nothing) if iv === nothing - error("$name doesn't have a independent variable") + iv = dict[:independent_variable] = variable(:t) end - gui_metadata = isassigned(icon) ? GUIMetadata(GlobalRef(mod, name), icon[]) : + + gui_metadata = isassigned(icon) > 0 ? GUIMetadata(GlobalRef(mod, name), icon[]) : GUIMetadata(GlobalRef(mod, name)) - quote - $name = $Model((; name, $(kwargs...)) -> begin - $expr - var"#___sys___" = $ODESystem($(Equation[]), $iv, [$(vs...)], $([]); - name, gui_metadata = $gui_metadata) - $Setfield.@set!(var"#___sys___".connector_type=$connector_type(var"#___sys___")) - end, $dict, true) + sys = :($ODESystem($Equation[$(eqs...)], $iv, [$(vs...)], [$(ps...)]; + name, systems = [$(comps...)], gui_metadata = $gui_metadata)) + + if ext[] === nothing + push!(exprs.args, :(var"#___sys___" = $sys)) + else + push!(exprs.args, :(var"#___sys___" = $extend($sys, $(ext[])))) end + + isconnector && push!(exprs.args, + :($Setfield.@set!(var"#___sys___".connector_type=$connector_type(var"#___sys___")))) + + f = :($(Symbol(:__, name, :__))(; name, $(kwargs...)) = $exprs) + :($name = $Model($f, $dict, $isconnector)) end function parse_variable_def!(dict, mod, arg, varclass, kwargs, def = nothing) @@ -207,48 +213,6 @@ function get_var(mod::Module, b) end end -function mtkmodel_macro(mod, name, expr) - exprs = Expr(:block) - dict = Dict{Symbol, Any}() - dict[:kwargs] = Dict{Symbol, Any}() - comps = Symbol[] - ext = Ref{Any}(nothing) - eqs = Expr[] - icon = Ref{Union{String, URI}}() - vs = [] - ps = [] - kwargs = [] - - for arg in expr.args - arg isa LineNumberNode && continue - if arg.head == :macrocall - parse_model!(exprs.args, comps, ext, eqs, icon, vs, ps, - dict, mod, arg, kwargs) - elseif arg.head == :block - push!(exprs.args, arg) - else - error("$arg is not valid syntax. Expected a macro call.") - end - end - iv = get(dict, :independent_variable, nothing) - if iv === nothing - iv = dict[:independent_variable] = variable(:t) - end - - gui_metadata = isassigned(icon) > 0 ? GUIMetadata(GlobalRef(mod, name), icon[]) : - GUIMetadata(GlobalRef(mod, name)) - - sys = :($ODESystem($Equation[$(eqs...)], $iv, [$(vs...)], [$(ps...)]; - systems = [$(comps...)], name, gui_metadata = $gui_metadata)) #, defaults = $defaults)) - if ext[] === nothing - push!(exprs.args, sys) - else - push!(exprs.args, :($extend($sys, $(ext[])))) - end - - :($name = $Model((; name, $(kwargs...)) -> $exprs, $dict, false)) -end - function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, dict, mod, arg, kwargs) mname = arg.args[1] From 820684b12864675ef80b710b0e64b79e0a01ea9b Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:39:37 +0530 Subject: [PATCH 14/32] test: connector with parameters, equations, icon, components, equations --- test/model_parsing.jl | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index a1d7295346..21c69878e3 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -1,5 +1,5 @@ using ModelingToolkit, Test -using ModelingToolkit: get_gui_metadata, VariableDescription, getdefault +using ModelingToolkit: get_gui_metadata, VariableDescription, getdefault, RegularConnector using URIs: URI using Distributions using Unitful @@ -233,3 +233,41 @@ end for (k, v) in metadata @test MockMeta.structure[:variables][:m][k] == v end + +@testset "Connector with parameters, equations..." begin + @connector A begin + @extend (e,) = extended_e = E() + @icon "pin.png" + @parameters begin + p + end + @variables begin + v(t) + end + @components begin + cc = C() + end + @equations begin + e ~ 0 + end + end + + @connector C begin + c(t) + end + + @connector E begin + e(t) + end + + @named aa = A() + @test aa.connector_type == RegularConnector() + + @test A.isconnector == true + + @test A.structure[:parameters] == Dict(:p => Dict()) + @test A.structure[:extend] == [[:e], :extended_e, :E] + @test A.structure[:equations] == ["e ~ 0"] + @test A.structure[:kwargs] == Dict(:p => nothing, :v => nothing) + @test A.structure[:components] == [[:cc, :C]] +end From 40073b68eb612b142e6695a1550cda181d8cccb1 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Thu, 31 Aug 2023 12:13:37 -0400 Subject: [PATCH 15/32] Fix hierarchical modeling with DDEs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/SciML/ModelingToolkit.jl/issues/2232 The issue before was that the namespace handling was not recursing into variables, meaning that when namespace was applied, states and parameters used in the delay definition were not namespaced. This was the output before: ```julia using ModelingToolkit, DifferentialEquations @variables t D = Differential(t) @parameters x(..) a function oscillator(;name, k=1.0, τ=0.01) @parameters k=k τ=τ @variables x(..)=0.1 y(t)=0.1 jcn(t)=0.0 delx(t) eqs = [D(x(t)) ~ y, D(y) ~ -k*x(t-τ)+jcn, delx ~ x(t-τ)] return System(eqs; name=name) end @named osc1 = oscillator(k=1.0, τ=0.01) @named osc2 = oscillator(k=2.0, τ=0.04) eqs = [osc1.jcn ~ osc2.delx, osc2.jcn ~ osc1.delx] @named coupledOsc = compose(System(eqs, t; name=:connected), [osc1, osc2]) julia> equations(coupledOsc) 8-element Vector{Equation}: osc1₊jcn(t) ~ osc2₊delx(t) osc2₊jcn(t) ~ osc1₊delx(t) Differential(t)(osc1₊x(t)) ~ osc1₊y(t) Differential(t)(osc1₊y(t)) ~ osc1₊jcn(t) - osc1₊k*osc1₊x(t - τ) osc1₊delx(t) ~ osc1₊x(t - τ) Differential(t)(osc2₊x(t)) ~ osc2₊y(t) Differential(t)(osc2₊y(t)) ~ osc2₊jcn(t) - osc2₊k*osc2₊x(t - τ) osc2₊delx(t) ~ osc2₊x(t - τ) ``` You can see the issue is that it's using `τ` instead of a namespaced `tau`. With this PR: ```julia julia> equations(coupledOsc) 8-element Vector{Equation}: osc1₊jcn(t) ~ osc2₊delx(t) osc2₊jcn(t) ~ osc1₊delx(t) Differential(t)(osc1₊x(t)) ~ osc1₊y(t) Differential(t)(osc1₊y(t)) ~ osc1₊jcn(t) - osc1₊k*osc1₊x(t - osc1₊τ) osc1₊delx(t) ~ osc1₊x(t - osc1₊τ) Differential(t)(osc2₊x(t)) ~ osc2₊y(t) Differential(t)(osc2₊y(t)) ~ osc2₊jcn(t) - osc2₊k*osc2₊x(t - osc2₊τ) osc2₊delx(t) ~ osc2₊x(t - osc2₊τ) ``` However, something is going on with structural_simplify here: ```julia simpsys = structural_simplify(coupledOsc) equations(simpsys) 6-element Vector{Equation}: Differential(t)(osc1₊x(t)) ~ osc1₊y(t) Differential(t)(osc1₊y(t)) ~ osc1₊jcn(t) - osc1₊k*osc1₊x(t - osc1₊τ) Differential(t)(osc2₊x(t)) ~ osc2₊y(t) Differential(t)(osc2₊y(t)) ~ osc2₊jcn(t) - osc2₊k*osc2₊x(t - osc2₊τ) 0 ~ osc1₊x(t - osc1₊τ) - osc1₊delx(t) 0 ~ osc2₊x(t - osc2₊τ) - osc2₊delx(t) ``` This errors: ```julia julia> prob = DDEProblem(structural_simplify(coupledOsc), [0.1,0.1,0.1,0.1], (0, 50), constant_lags=[osc1.τ, osc2.τ]) ERROR: ArgumentError: Equations (6), states (4), and initial conditions (4) are of different lengths. To allow a different number of equations than states use kwarg check_length=false. Stacktrace: [1] check_eqs_u0(eqs::Vector{Equation}, dvs::Vector{Any}, u0::Vector{Float64}; check_length::Bool, kwargs::Base.Pairs{Symbol, Any, Tuple{Symbol, Symbol, Symbol}, NamedTuple{(:t, :has_difference, :constant_lags), Tuple{Int64, Bool, Vector{Num}}}}) @ ModelingToolkit C:\Users\accou\.julia\packages\ModelingToolkit\ua9Sp\src\systems\abstractsystem.jl:1714 [2] process_DEProblem(constructor::Type, sys::ODESystem, u0map::Vector{Float64}, parammap::SciMLBase.NullParameters; implicit_dae::Bool, du0map::Nothing, version::Nothing, tgrad::Bool, jac::Bool, checkbounds::Bool, sparse::Bool, simplify::Bool, linenumbers::Bool, parallel::Symbolics.SerialForm, eval_expression::Bool, use_union::Bool, tofloat::Bool, symbolic_u0::Bool, kwargs::Base.Pairs{Symbol, Any, NTuple{4, Symbol}, NamedTuple{(:t, :has_difference, :check_length, :constant_lags), Tuple{Int64, Bool, Bool, Vector{Num}}}}) @ ModelingToolkit C:\Users\accou\.julia\packages\ModelingToolkit\ua9Sp\src\systems\diffeqs\abstractodesystem.jl:736 [3] (DDEProblem{true})(sys::ODESystem, u0map::Vector{Float64}, tspan::Tuple{Int64, Int64}, parammap::SciMLBase.NullParameters; callback::Nothing, check_length::Bool, kwargs::Base.Pairs{Symbol, Vector{Num}, Tuple{Symbol}, NamedTuple{(:constant_lags,), Tuple{Vector{Num}}}}) @ ModelingToolkit C:\Users\accou\.julia\packages\ModelingToolkit\ua9Sp\src\systems\diffeqs\abstractodesystem.jl:930 [4] DDEProblem @ C:\Users\accou\.julia\packages\ModelingToolkit\ua9Sp\src\systems\diffeqs\abstractodesystem.jl:923 [inlined] [5] DDEProblem(::ODESystem, ::Vector{Float64}, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, Vector{Num}, Tuple{Symbol}, NamedTuple{(:constant_lags,), Tuple{Vector{Num}}}}) @ ModelingToolkit C:\Users\accou\.julia\packages\ModelingToolkit\ua9Sp\src\systems\diffeqs\abstractodesystem.jl:921 [6] top-level scope @ REPL[2]:1 ``` The reason is because the last two equations are not deleted. ```julia julia> states(simpsys) 4-element Vector{Any}: osc1₊x(t) osc1₊y(t) osc2₊x(t) osc2₊y(t) ``` ```julia julia> states(simpsys) 4-element Vector{Any}: osc1₊x(t) osc1₊y(t) osc2₊x(t) osc2₊y(t) julia> observed(simpsys) 4-element Vector{Equation}: osc2₊delx(t) ~ -0.0 osc1₊delx(t) ~ -0.0 osc1₊jcn(t) ~ osc2₊delx(t) osc2₊jcn(t) ~ osc1₊delx(t) ``` These last two equations ``` 0 ~ osc1₊x(t - osc1₊τ) - osc1₊delx(t) 0 ~ osc2₊x(t - osc2₊τ) - osc2₊delx(t) ``` should be observed equations in terms of the constant value (via delay): ``` osc1₊delx(t) ~ osc1₊x(t - osc1₊τ) osc2₊delx(t) ~ osc2₊x(t - osc2₊τ) ``` and removed from the equations, but instead they are still in there so it things there's 6 equations and errors. Is some value matching thing missing the deletion of these equations? --- src/systems/abstractsystem.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 83a28a259c..67535c4aa7 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -425,7 +425,7 @@ function GlobalScope(sym::Union{Num, Symbolic}) end end -renamespace(sys, eq::Equation) = namespace_equation(eq, sys) +renamespace(sys, eq::Equation) = @show namespace_equation(eq, sys) renamespace(names::AbstractVector, x) = foldr(renamespace, names, init = x) function renamespace(sys, x) @@ -434,7 +434,7 @@ function renamespace(sys, x) if x isa Symbolic T = typeof(x) if istree(x) && operation(x) isa Operator - return similarterm(x, operation(x), + return similarterm(x, renamespace.(sys,operation(x)), Any[renamespace(sys, only(arguments(x)))])::T end let scope = getmetadata(x, SymScope, LocalScope()) @@ -495,18 +495,18 @@ function namespace_expr(O, sys, n = nameof(sys)) O = unwrap(O) if any(isequal(O), ivs) return O - elseif isvariable(O) - renamespace(n, O) elseif istree(O) T = typeof(O) - if symtype(operation(O)) <: FnType - renamespace(n, O)::T + renamed = let sys = sys, n = n, T = T + map(a -> namespace_expr(a, sys, n)::Any, arguments(O)) + end + if isvariable(O) + similarterm(O, renamespace(n, operation(O)), renamed)::T else - renamed = let sys = sys, n = n, T = T - map(a -> namespace_expr(a, sys, n)::Any, arguments(O)) - end similarterm(O, operation(O), renamed)::T end + elseif isvariable(O) + renamespace(n, O) elseif O isa Array let sys = sys, n = n map(o -> namespace_expr(o, sys, n), O) From a5c9440eb47702d7d653ed8392bb952dd7fc0d90 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Thu, 31 Aug 2023 12:15:33 -0400 Subject: [PATCH 16/32] some clean up --- src/systems/abstractsystem.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 67535c4aa7..c6702afa95 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -425,7 +425,7 @@ function GlobalScope(sym::Union{Num, Symbolic}) end end -renamespace(sys, eq::Equation) = @show namespace_equation(eq, sys) +renamespace(sys, eq::Equation) = namespace_equation(eq, sys) renamespace(names::AbstractVector, x) = foldr(renamespace, names, init = x) function renamespace(sys, x) @@ -434,7 +434,7 @@ function renamespace(sys, x) if x isa Symbolic T = typeof(x) if istree(x) && operation(x) isa Operator - return similarterm(x, renamespace.(sys,operation(x)), + return similarterm(x, operation(x), Any[renamespace(sys, only(arguments(x)))])::T end let scope = getmetadata(x, SymScope, LocalScope()) From e24bd29785d962ee2993a219f7a9ca62b2d5947b Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Thu, 31 Aug 2023 17:43:31 -0400 Subject: [PATCH 17/32] Remember to attach metadata back --- src/systems/abstractsystem.jl | 5 +++-- test/dde.jl | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index c6702afa95..90a4b45bb9 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -501,9 +501,10 @@ function namespace_expr(O, sys, n = nameof(sys)) map(a -> namespace_expr(a, sys, n)::Any, arguments(O)) end if isvariable(O) - similarterm(O, renamespace(n, operation(O)), renamed)::T + similarterm(O, renamespace(n, operation(O)), renamed, + metadata = metadata(O))::T else - similarterm(O, operation(O), renamed)::T + similarterm(O, operation(O), renamed, metadata = metadata(O))::T end elseif isvariable(O) renamespace(n, O) diff --git a/test/dde.jl b/test/dde.jl index cd111c21af..94903daec6 100644 --- a/test/dde.jl +++ b/test/dde.jl @@ -82,3 +82,27 @@ sys = structural_simplify(sys) @test isequal(ModelingToolkit.get_noiseeqs(sys), [α * x(t) + γ;;]) prob_mtk = SDDEProblem(sys, [x(t) => 1.0 + t], tspan; constant_lags = (τ,)); @test_nowarn sol_mtk = solve(prob_mtk, RKMil()) + + +@variables t +D = Differential(t) +@parameters x(..) a + +function oscillator(;name, k=1.0, τ=0.01) + @parameters k=k τ=τ + @variables x(..)=0.1 y(t)=0.1 jcn(t)=0.0 delx(t) + eqs = [D(x(t)) ~ y, + D(y) ~ -k*x(t-τ)+jcn, + delx ~ x(t-τ)] + return System(eqs; name=name) +end + +@named osc1 = oscillator(k=1.0, τ=0.01) +@named osc2 = oscillator(k=2.0, τ=0.04) +eqs = [osc1.jcn ~ osc2.delx, + osc2.jcn ~ osc1.delx] +@named coupledOsc = System(eqs, t) +@named coupledOsc = compose(coupledOsc, [osc1, osc2]) +sys = structural_simplify(coupledOsc) +@test length(equations(sys)) == 4 +@test length(states(sys)) == 4 From e7df8d4157ca2f2fb40b20db3803b3b128cbddd2 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Thu, 31 Aug 2023 19:22:45 -0400 Subject: [PATCH 18/32] fix scoping --- src/systems/abstractsystem.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 90a4b45bb9..9699e6c37d 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -501,8 +501,11 @@ function namespace_expr(O, sys, n = nameof(sys)) map(a -> namespace_expr(a, sys, n)::Any, arguments(O)) end if isvariable(O) - similarterm(O, renamespace(n, operation(O)), renamed, - metadata = metadata(O))::T + # Use renamespace so the scope is correct, and make sure to use the + # metadata from the rescoped variable + rescoped = renamespace(n, O) + similarterm(O, operation(rescoped), renamed, + metadata = metadata(rescoped))::T else similarterm(O, operation(O), renamed, metadata = metadata(O))::T end From 672e42c33680ba29229e3958937721c8255d42de Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:29:33 +0530 Subject: [PATCH 19/32] refactor: pass the symbolic vals for kwargs of sub components instead of depending on the numerical value of it - this change allows user to set a parameter with initial value and pass that as default value of kwargs of sub components. --- src/systems/model_parsing.jl | 19 +++++++++++++------ test/model_parsing.jl | 31 +++++++++++++++++++------------ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index dc9ff68b83..c75a9b8d2d 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -236,7 +236,8 @@ end function parse_components!(exprs, cs, dict, body, kwargs) expr = Expr(:block) - push!(exprs, expr) + varexpr = Expr(:block) + push!(exprs, varexpr) comps = Vector{Symbol}[] for arg in body.args arg isa LineNumberNode && continue @@ -247,15 +248,17 @@ function parse_components!(exprs, cs, dict, body, kwargs) arg = deepcopy(arg) b = deepcopy(arg.args[2]) - component_args!(a, b, expr, kwargs) + component_args!(a, b, dict, expr, varexpr, kwargs) - push!(b.args, Expr(:kw, :name, Meta.quot(a))) arg.args[2] = b push!(expr.args, arg) end _ => error("`@components` only takes assignment expressions. Got $arg") end end + + push!(exprs, :(@named $expr)) + dict[:components] = comps end @@ -263,7 +266,7 @@ function _rename(compname, varname) compname = Symbol(compname, :__, varname) end -function component_args!(a, b, expr, kwargs) +function component_args!(a, b, dict, expr, varexpr, kwargs) # Whenever `b` is a function call, skip the first arg aka the function name. # Whenever it is a kwargs list, include it. start = b.head == :call ? 2 : 1 @@ -274,15 +277,19 @@ function component_args!(a, b, expr, kwargs) x::Symbol || Expr(:kw, x) => begin _v = _rename(a, x) b.args[i] = Expr(:kw, x, _v) + push!(varexpr.args, :((@isdefined $x) && ($_v = $x))) push!(kwargs, Expr(:kw, _v, nothing)) + dict[:kwargs][_v] = nothing end Expr(:parameters, x...) => begin - component_args!(a, arg, expr, kwargs) + component_args!(a, arg, dict, expr, varexpr, kwargs) end Expr(:kw, x, y) => begin _v = _rename(a, x) b.args[i] = Expr(:kw, x, _v) - push!(kwargs, Expr(:kw, _v, y)) + push!(varexpr.args, :($_v = $_v === nothing ? $y : $_v)) + push!(kwargs, Expr(:kw, _v, nothing)) + dict[:kwargs][_v] = nothing end _ => error("Could not parse $arg of component $a") end diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 21c69878e3..3af4a45046 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -1,5 +1,6 @@ using ModelingToolkit, Test -using ModelingToolkit: get_gui_metadata, VariableDescription, getdefault, RegularConnector +using ModelingToolkit: get_gui_metadata, + VariableDescription, getdefault, RegularConnector, get_ps using URIs: URI using Distributions using Unitful @@ -120,9 +121,13 @@ end end @mtkmodel RC begin + @parameters begin + R_val = 10 + C_val = 5 + end @components begin - resistor = Resistor(; R) - capacitor = Capacitor(; C = 10) + resistor = Resistor(; R = R_val) + capacitor = Capacitor(; C = C_val) source = Voltage() constant = Constant(; k = 1) ground = Ground() @@ -135,9 +140,10 @@ end end end -@named rc = RC(; resistor.R = 20) -@test getdefault(rc.resistor.R) == 20 -@test getdefault(rc.capacitor.C) == 10 +@named rc = RC(; R_val = 20) +params = ModelingToolkit.get_ps(rc) +@test isequal(getdefault(rc.resistor.R), params[1]) +@test isequal(getdefault(rc.capacitor.C), params[2]) @test getdefault(rc.capacitor.v) == 0.0 @test getdefault(rc.constant.k) == 1 @@ -210,14 +216,15 @@ end end @named a = A(p = 10) -getdefault(a.b.i) == 10 -getdefault(a.b.j) == 0.1 -getdefault(a.b.k) == 1 +params = get_ps(a) +@test isequal(getdefault(a.b.i), params[1]) +@test isequal(getdefault(a.b.j), 1 / params[1]) +@test getdefault(a.b.k) == 1 @named a = A(p = 10, b.i = 20, b.j = 30, b.k = 40) -getdefault(a.b.i) == 20 -getdefault(a.b.j) == 30 -getdefault(a.b.k) == 40 +@test getdefault(a.b.i) == 20 +@test getdefault(a.b.j) == 30 +@test getdefault(a.b.k) == 40 metadata = Dict(:description => "Variable to test metadata in the Model.structure", :input => true, :bounds => (-1, 1), :connection_type => :Flow, :integer => true, From 0076490a3583e7bae1e40b679407b117f848249b Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:36:44 +0530 Subject: [PATCH 20/32] refactor: set symbolic defaults to the kwargs of base sys + treat kwargs of base sys as components kwargs --- src/systems/model_parsing.jl | 47 ++++++++++++++++++++++++++++++++++-- test/model_parsing.jl | 3 ++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index c75a9b8d2d..bd514e2440 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -296,8 +296,49 @@ function component_args!(a, b, dict, expr, varexpr, kwargs) end end +function extend_args!(a, b, dict, expr, kwargs, varexpr, has_param = false) + # Whenever `b` is a function call, skip the first arg aka the function name. + # Whenver it is a kwargs list, include it. + start = b.head == :call ? 2 : 1 + for i in start:lastindex(b.args) + arg = b.args[i] + arg isa LineNumberNode && continue + MLStyle.@match arg begin + x::Symbol => begin + if b.head != :parameters + if has_param + popat!(b.args, i) + push!(b.args[2].args, x) + else + b.args[i] = Expr(:parameters, x) + end + end + push!(kwargs, Expr(:kw, x, nothing)) + dict[:kwargs][x] = nothing + end + Expr(:kw, x) => begin + push!(kwargs, Expr(:kw, x, nothing)) + dict[:kwargs][x] = nothing + end + Expr(:kw, x, y) => begin + b.args[i] = Expr(:kw, x, x) + push!(varexpr.args, :($x = $x === nothing ? $y : $x)) + push!(kwargs, Expr(:kw, x, nothing)) + dict[:kwargs][x] = nothing + end + Expr(:parameters, x...) => begin + has_param = true + extend_args!(a, arg, dict, expr, kwargs, varexpr, has_param) + end + _ => error("Could not parse $arg of component $a") + end + end +end + function parse_extend!(exprs, ext, dict, body, kwargs) expr = Expr(:block) + varexpr = Expr(:block) + push!(exprs, varexpr) push!(exprs, expr) body = deepcopy(body) MLStyle.@match body begin @@ -309,13 +350,15 @@ function parse_extend!(exprs, ext, dict, body, kwargs) error("`@extend` destructuring only takes an tuple as LHS. Got $body") end a, b = b.args - component_args!(a, b, expr, kwargs) + extend_args!(a, b, dict, expr, kwargs, varexpr) vars, a, b end ext[] = a push!(b.args, Expr(:kw, :name, Meta.quot(a))) - dict[:extend] = [Symbol.(vars.args), a, b.args[1]] push!(expr.args, :($a = $b)) + + dict[:extend] = [Symbol.(vars.args), a, b.args[1]] + if vars !== nothing push!(expr.args, :(@unpack $vars = $a)) end diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 3af4a45046..8c267f1e42 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -107,7 +107,7 @@ end end end -@named capacitor = Capacitor(C = 10, oneport.v = 10.0) +@named capacitor = Capacitor(C = 10, v = 10.0) @test getdefault(capacitor.v) == 10.0 @mtkmodel Voltage begin @@ -132,6 +132,7 @@ end constant = Constant(; k = 1) ground = Ground() end + @equations begin connect(constant.output, source.V) connect(source.p, resistor.p) From 1f07daa6601b0a07675b280cf4d0a7533d2476bb Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:03:37 +0530 Subject: [PATCH 21/32] feat: adds `@structural_parameters` to `@mtkmodel` - This will allow to provide arguments that aren't parameters/variables like int, function... - Adds tests for the same --- src/systems/model_parsing.jl | 30 ++++++++++++++++++++++++------ test/model_parsing.jl | 28 +++++++++++++++++++--------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index bd514e2440..6c73dfa743 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -22,15 +22,13 @@ function _model_macro(mod, name, expr, isconnector) ext = Ref{Any}(nothing) eqs = Expr[] icon = Ref{Union{String, URI}}() - vs = [] - ps = [] - kwargs = [] + kwargs, ps, sps, vs, = [], [], [], [] for arg in expr.args arg isa LineNumberNode && continue if arg.head == :macrocall parse_model!(exprs.args, comps, ext, eqs, icon, vs, ps, - dict, mod, arg, kwargs) + sps, dict, mod, arg, kwargs) elseif arg.head == :block push!(exprs.args, arg) elseif isconnector @@ -213,8 +211,8 @@ function get_var(mod::Module, b) end end -function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, dict, - mod, arg, kwargs) +function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, sps, + dict, mod, arg, kwargs) mname = arg.args[1] body = arg.args[end] if mname == Symbol("@components") @@ -225,6 +223,8 @@ function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, dict, parse_variables!(exprs, vs, dict, mod, body, :variables, kwargs) elseif mname == Symbol("@parameters") parse_variables!(exprs, ps, dict, mod, body, :parameters, kwargs) + elseif mname == Symbol("@structural_parameters") + parse_structural_parameters!(exprs, sps, dict, mod, body, kwargs) elseif mname == Symbol("@equations") parse_equations!(exprs, eqs, dict, body) elseif mname == Symbol("@icon") @@ -234,6 +234,24 @@ function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, dict, end end +function parse_structural_parameters!(exprs, sps, dict, mod, body, kwargs) + Base.remove_linenums!(body) + for arg in body.args + MLStyle.@match arg begin + Expr(:(=), a, b) => begin + push!(sps, a) + push!(kwargs, Expr(:kw, a, b)) + dict[:kwargs][a] = b + end + a => begin + push!(sps, a) + push!(kwargs, a) + dict[:kwargs][a] = nothing + end + end + end +end + function parse_components!(exprs, cs, dict, body, kwargs) expr = Expr(:block) varexpr = Expr(:block) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 8c267f1e42..2ef9e36de0 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -121,15 +121,16 @@ end end @mtkmodel RC begin - @parameters begin + @structural_parameters begin R_val = 10 - C_val = 5 + C_val = 10 + k_val = 10 end @components begin resistor = Resistor(; R = R_val) capacitor = Capacitor(; C = C_val) source = Voltage() - constant = Constant(; k = 1) + constant = Constant(; k = k_val) ground = Ground() end @@ -141,12 +142,17 @@ end end end -@named rc = RC(; R_val = 20) -params = ModelingToolkit.get_ps(rc) -@test isequal(getdefault(rc.resistor.R), params[1]) -@test isequal(getdefault(rc.capacitor.C), params[2]) +C_val = 20 +R_val = 20 +res__R = 100 +@named rc = RC(; C_val, R_val, resistor.R = res__R) +# Test that `resistor.R` overrides `R_val` in the argument. +@test getdefault(rc.resistor.R) == res__R != R_val +# Test that `C_val` passed via argument is set as default of C. +@test getdefault(rc.capacitor.C) == C_val +# Test that `k`'s default value is unchanged. +@test getdefault(rc.constant.k) == RC.structure[:kwargs][:k_val] @test getdefault(rc.capacitor.v) == 0.0 -@test getdefault(rc.constant.k) == 1 @test get_gui_metadata(rc.resistor).layout == Resistor.structure[:icon] == read(joinpath(ENV["MTK_ICONS_DIR"], "resistor.svg"), String) @@ -177,10 +183,14 @@ params = ModelingToolkit.get_ps(rc) j(t) = jval, [description = "j(t)"] k = kval, [description = "k"] end + @structural_parameters begin + l = 1 + func + end end kval = 5 -@named model = MockModel(; kval, cval = 1) +@named model = MockModel(; kval, cval = 1, func = identity) @test hasmetadata(model.e, VariableDescription) @test hasmetadata(model.f, VariableDescription) From a9fa2442f57c45ee9d1bc39c4ee32abe365a90f7 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:16:41 +0530 Subject: [PATCH 22/32] test: rearranges model-parsing tests with `@testsets` --- test/model_parsing.jl | 404 +++++++++++++++++++++--------------------- 1 file changed, 206 insertions(+), 198 deletions(-) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 2ef9e36de0..3f380df100 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -7,76 +7,77 @@ using Unitful ENV["MTK_ICONS_DIR"] = "$(@__DIR__)/icons" -@connector RealInput begin - u(t), [input = true, unit = u"V"] -end -@connector RealOutput begin - u(t), [output = true, unit = u"V"] -end -@mtkmodel Constant begin - @components begin - output = RealOutput() +@testset "Comprehensive Test of Parsing Models (with an RC Circuit)" begin + @connector RealInput begin + u(t), [input = true, unit = u"V"] end - @parameters begin - k, [description = "Constant output value of block"] + @connector RealOutput begin + u(t), [output = true, unit = u"V"] end - @equations begin - output.u ~ k + @mtkmodel Constant begin + @components begin + output = RealOutput() + end + @parameters begin + k, [description = "Constant output value of block"] + end + @equations begin + output.u ~ k + end end -end -@variables t [unit = u"s"] -D = Differential(t) + @variables t [unit = u"s"] + D = Differential(t) -@connector Pin begin - v(t), [unit = u"V"] # Potential at the pin [V] - i(t), [connect = Flow, unit = u"A"] # Current flowing into the pin [A] - @icon "pin.png" -end + @connector Pin begin + v(t), [unit = u"V"] # Potential at the pin [V] + i(t), [connect = Flow, unit = u"A"] # Current flowing into the pin [A] + @icon "pin.png" + end -@named p = Pin(; v = π) -@test getdefault(p.v) == π -@test Pin.isconnector == true + @named p = Pin(; v = π) + @test getdefault(p.v) == π + @test Pin.isconnector == true -@mtkmodel OnePort begin - @components begin - p = Pin() - n = Pin() - end - @variables begin - v(t), [unit = u"V"] - i(t), [unit = u"A"] - end - @icon "oneport.png" - @equations begin - v ~ p.v - n.v - 0 ~ p.i + n.i - i ~ p.i + @mtkmodel OnePort begin + @components begin + p = Pin() + n = Pin() + end + @variables begin + v(t), [unit = u"V"] + i(t), [unit = u"A"] + end + @icon "oneport.png" + @equations begin + v ~ p.v - n.v + 0 ~ p.i + n.i + i ~ p.i + end end -end -@test OnePort.isconnector == false + @test OnePort.isconnector == false -@mtkmodel Ground begin - @components begin - g = Pin() - end - @icon begin - read(abspath(ENV["MTK_ICONS_DIR"], "ground.svg"), String) - end - @equations begin - g.v ~ 0 + @mtkmodel Ground begin + @components begin + g = Pin() + end + @icon begin + read(abspath(ENV["MTK_ICONS_DIR"], "ground.svg"), String) + end + @equations begin + g.v ~ 0 + end end -end -resistor_log = "$(@__DIR__)/logo/resistor.svg" -@mtkmodel Resistor begin - @extend v, i = oneport = OnePort() - @parameters begin - R, [unit = u"Ω"] - end - @icon begin - """ + resistor_log = "$(@__DIR__)/logo/resistor.svg" + @mtkmodel Resistor begin + @extend v, i = oneport = OnePort() + @parameters begin + R, [unit = u"Ω"] + end + @icon begin + """ """ + end + @equations begin + v ~ i * R + end end - @equations begin - v ~ i * R - end -end -@mtkmodel Capacitor begin - @parameters begin - C, [unit = u"F"] - end - @extend v, i = oneport = OnePort(; v = 0.0) - @icon "https://upload.wikimedia.org/wikipedia/commons/7/78/Capacitor_symbol.svg" - @equations begin - D(v) ~ i / C + @mtkmodel Capacitor begin + @parameters begin + C, [unit = u"F"] + end + @extend v, i = oneport = OnePort(; v = 0.0) + @icon "https://upload.wikimedia.org/wikipedia/commons/7/78/Capacitor_symbol.svg" + @equations begin + D(v) ~ i / C + end end -end -@named capacitor = Capacitor(C = 10, v = 10.0) -@test getdefault(capacitor.v) == 10.0 + @named capacitor = Capacitor(C = 10, v = 10.0) + @test getdefault(capacitor.v) == 10.0 -@mtkmodel Voltage begin - @extend v, i = oneport = OnePort() - @components begin - V = RealInput() - end - @equations begin - v ~ V.u + @mtkmodel Voltage begin + @extend v, i = oneport = OnePort() + @components begin + V = RealInput() + end + @equations begin + v ~ V.u + end end -end -@mtkmodel RC begin - @structural_parameters begin - R_val = 10 - C_val = 10 - k_val = 10 - end - @components begin - resistor = Resistor(; R = R_val) - capacitor = Capacitor(; C = C_val) - source = Voltage() - constant = Constant(; k = k_val) - ground = Ground() - end + @mtkmodel RC begin + @structural_parameters begin + R_val = 10 + C_val = 10 + k_val = 10 + end + @components begin + resistor = Resistor(; R = R_val) + capacitor = Capacitor(; C = C_val) + source = Voltage() + constant = Constant(; k = k_val) + ground = Ground() + end - @equations begin - connect(constant.output, source.V) - connect(source.p, resistor.p) - connect(resistor.n, capacitor.p) - connect(capacitor.n, source.n, ground.g) + @equations begin + connect(constant.output, source.V) + connect(source.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, source.n, ground.g) + end end + + C_val = 20 + R_val = 20 + res__R = 100 + @named rc = RC(; C_val, R_val, resistor.R = res__R) + # Test that `resistor.R` overrides `R_val` in the argument. + @test getdefault(rc.resistor.R) == res__R != R_val + # Test that `C_val` passed via argument is set as default of C. + @test getdefault(rc.capacitor.C) == C_val + # Test that `k`'s default value is unchanged. + @test getdefault(rc.constant.k) == RC.structure[:kwargs][:k_val] + @test getdefault(rc.capacitor.v) == 0.0 + + @test get_gui_metadata(rc.resistor).layout == Resistor.structure[:icon] == + read(joinpath(ENV["MTK_ICONS_DIR"], "resistor.svg"), String) + @test get_gui_metadata(rc.ground).layout == + read(abspath(ENV["MTK_ICONS_DIR"], "ground.svg"), String) + @test get_gui_metadata(rc.capacitor).layout == + URI("https://upload.wikimedia.org/wikipedia/commons/7/78/Capacitor_symbol.svg") + @test OnePort.structure[:icon] == + URI("file:///" * abspath(ENV["MTK_ICONS_DIR"], "oneport.png")) + @test ModelingToolkit.get_gui_metadata(rc.resistor.p).layout == Pin.structure[:icon] == + URI("file:///" * abspath(ENV["MTK_ICONS_DIR"], "pin.png")) + + @test length(equations(structural_simplify(rc))) == 1 end -C_val = 20 -R_val = 20 -res__R = 100 -@named rc = RC(; C_val, R_val, resistor.R = res__R) -# Test that `resistor.R` overrides `R_val` in the argument. -@test getdefault(rc.resistor.R) == res__R != R_val -# Test that `C_val` passed via argument is set as default of C. -@test getdefault(rc.capacitor.C) == C_val -# Test that `k`'s default value is unchanged. -@test getdefault(rc.constant.k) == RC.structure[:kwargs][:k_val] -@test getdefault(rc.capacitor.v) == 0.0 - -@test get_gui_metadata(rc.resistor).layout == Resistor.structure[:icon] == - read(joinpath(ENV["MTK_ICONS_DIR"], "resistor.svg"), String) -@test get_gui_metadata(rc.ground).layout == - read(abspath(ENV["MTK_ICONS_DIR"], "ground.svg"), String) -@test get_gui_metadata(rc.capacitor).layout == - URI("https://upload.wikimedia.org/wikipedia/commons/7/78/Capacitor_symbol.svg") -@test OnePort.structure[:icon] == - URI("file:///" * abspath(ENV["MTK_ICONS_DIR"], "oneport.png")) -@test ModelingToolkit.get_gui_metadata(rc.resistor.p).layout == Pin.structure[:icon] == - URI("file:///" * abspath(ENV["MTK_ICONS_DIR"], "pin.png")) - -@test length(equations(structural_simplify(rc))) == 1 - -@mtkmodel MockModel begin - @parameters begin - a - b(t) - cval - jval - kval - c(t) = cval + jval - d = 2 - e, [description = "e"] - f = 3, [description = "f"] - h(t), [description = "h(t)"] - i(t) = 4, [description = "i(t)"] - j(t) = jval, [description = "j(t)"] - k = kval, [description = "k"] - end - @structural_parameters begin - l = 1 - func +@testset "Parameters and Structural parameters in various modes" begin + @mtkmodel MockModel begin + @parameters begin + a + b(t) + cval + jval + kval + c(t) = cval + jval + d = 2 + e, [description = "e"] + f = 3, [description = "f"] + h(t), [description = "h(t)"] + i(t) = 4, [description = "i(t)"] + j(t) = jval, [description = "j(t)"] + k = kval, [description = "k"] + end + @structural_parameters begin + l = 1 + func + end end + + kval = 5 + @named model = MockModel(; kval, cval = 1, func = identity) + + @test hasmetadata(model.e, VariableDescription) + @test hasmetadata(model.f, VariableDescription) + @test hasmetadata(model.h, VariableDescription) + @test hasmetadata(model.i, VariableDescription) + @test hasmetadata(model.j, VariableDescription) + @test hasmetadata(model.k, VariableDescription) + + model = complete(model) + @test getdefault(model.cval) == 1 + @test isequal(getdefault(model.c), model.cval + model.jval) + @test getdefault(model.d) == 2 + @test_throws KeyError getdefault(model.e) + @test getdefault(model.f) == 3 + @test getdefault(model.i) == 4 + @test isequal(getdefault(model.j), model.jval) + @test isequal(getdefault(model.k), model.kval) end -kval = 5 -@named model = MockModel(; kval, cval = 1, func = identity) - -@test hasmetadata(model.e, VariableDescription) -@test hasmetadata(model.f, VariableDescription) -@test hasmetadata(model.h, VariableDescription) -@test hasmetadata(model.i, VariableDescription) -@test hasmetadata(model.j, VariableDescription) -@test hasmetadata(model.k, VariableDescription) - -model = complete(model) -@test getdefault(model.cval) == 1 -@test isequal(getdefault(model.c), model.cval + model.jval) -@test getdefault(model.d) == 2 -@test_throws KeyError getdefault(model.e) -@test getdefault(model.f) == 3 -@test getdefault(model.i) == 4 -@test isequal(getdefault(model.j), model.jval) -@test isequal(getdefault(model.k), model.kval) - -@mtkmodel A begin - @parameters begin - p - end - @components begin - b = B(i = p, j = 1 / p, k = 1) +@testset "Defaults of subcomponents MTKModel" begin + @mtkmodel A begin + @parameters begin + p + end + @components begin + b = B(i = p, j = 1 / p, k = 1) + end end -end -@mtkmodel B begin - @parameters begin - i - j - k + @mtkmodel B begin + @parameters begin + i + j + k + end end -end -@named a = A(p = 10) -params = get_ps(a) -@test isequal(getdefault(a.b.i), params[1]) -@test isequal(getdefault(a.b.j), 1 / params[1]) -@test getdefault(a.b.k) == 1 - -@named a = A(p = 10, b.i = 20, b.j = 30, b.k = 40) -@test getdefault(a.b.i) == 20 -@test getdefault(a.b.j) == 30 -@test getdefault(a.b.k) == 40 - -metadata = Dict(:description => "Variable to test metadata in the Model.structure", - :input => true, :bounds => (-1, 1), :connection_type => :Flow, :integer => true, - :binary => false, :tunable => false, :disturbance => true, :dist => Normal(1, 1)) - -@connector MockMeta begin - m(t), - [description = "Variable to test metadata in the Model.structure", - input = true, bounds = (-1, 1), connect = Flow, integer = true, - binary = false, tunable = false, disturbance = true, dist = Normal(1, 1)] + @named a = A(p = 10) + params = get_ps(a) + @test isequal(getdefault(a.b.i), params[1]) + @test isequal(getdefault(a.b.j), 1 / params[1]) + @test getdefault(a.b.k) == 1 + + @named a = A(p = 10, b.i = 20, b.j = 30, b.k = 40) + @test getdefault(a.b.i) == 20 + @test getdefault(a.b.j) == 30 + @test getdefault(a.b.k) == 40 end -for (k, v) in metadata - @test MockMeta.structure[:variables][:m][k] == v +@testset "Metadata in variables" begin + metadata = Dict(:description => "Variable to test metadata in the Model.structure", + :input => true, :bounds => (-1, 1), :connection_type => :Flow, :integer => true, + :binary => false, :tunable => false, :disturbance => true, :dist => Normal(1, 1)) + + @connector MockMeta begin + m(t), + [description = "Variable to test metadata in the Model.structure", + input = true, bounds = (-1, 1), connect = Flow, integer = true, + binary = false, tunable = false, disturbance = true, dist = Normal(1, 1)] + end + + for (k, v) in metadata + @test MockMeta.structure[:variables][:m][k] == v + end end @testset "Connector with parameters, equations..." begin From 6dacc1e4651ce8033636bab559765a1a230a70a7 Mon Sep 17 00:00:00 2001 From: Chris Rackauckas Date: Fri, 1 Sep 2023 08:23:39 -0400 Subject: [PATCH 23/32] `@test_broken` time dependent vars in OptimizationSystem --- test/optimizationsystem.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/optimizationsystem.jl b/test/optimizationsystem.jl index 1e5c1e9cce..a75154fda9 100644 --- a/test/optimizationsystem.jl +++ b/test/optimizationsystem.jl @@ -207,7 +207,8 @@ end prob = OptimizationProblem(combinedsys, u0, p, grad = true, hess = true, cons_j = true, cons_h = true) @test prob.f.sys === combinedsys - sol = solve(prob, Ipopt.Optimizer(); print_level = 0) + @test_broken SciMLBase.successful_retcode(solve(prob, Ipopt.Optimizer(); print_level = 0)) + #= @test sol.minimum < -1e5 prob = OptimizationProblem(sys2, [x => 0.0, y => 0.0], [a => 1.0, b => 100.0], @@ -217,6 +218,7 @@ end @test sol.minimum < 1.0 sol = solve(prob, Ipopt.Optimizer(); print_level = 0) @test sol.minimum < 1.0 + =# end @testset "metadata" begin From 65f007df34a2e0c97ef5eb4821b3b52415ec0ac6 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Fri, 1 Sep 2023 12:07:13 -0400 Subject: [PATCH 24/32] Fix `systems` keyword argument handling in `System` --- src/systems/abstractsystem.jl | 2 +- src/systems/systems.jl | 2 +- test/dde.jl | 28 ++++++++++++++++------------ test/optimizationsystem.jl | 26 ++++++++++++++------------ 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 9699e6c37d..639ee60afc 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -505,7 +505,7 @@ function namespace_expr(O, sys, n = nameof(sys)) # metadata from the rescoped variable rescoped = renamespace(n, O) similarterm(O, operation(rescoped), renamed, - metadata = metadata(rescoped))::T + metadata = metadata(rescoped))::T else similarterm(O, operation(O), renamed, metadata = metadata(O))::T end diff --git a/src/systems/systems.jl b/src/systems/systems.jl index 8c54dcd361..7aaaabb917 100644 --- a/src/systems/systems.jl +++ b/src/systems/systems.jl @@ -1,6 +1,6 @@ function System(eqs::AbstractVector{<:Equation}, iv = nothing, args...; name = nothing, kw...) - ODESystem(eqs, iv, args...; name, checks = false) + ODESystem(eqs, iv, args...; name, kw..., checks = false) end """ diff --git a/test/dde.jl b/test/dde.jl index 94903daec6..479a1a59a7 100644 --- a/test/dde.jl +++ b/test/dde.jl @@ -83,26 +83,30 @@ sys = structural_simplify(sys) prob_mtk = SDDEProblem(sys, [x(t) => 1.0 + t], tspan; constant_lags = (τ,)); @test_nowarn sol_mtk = solve(prob_mtk, RKMil()) - @variables t D = Differential(t) @parameters x(..) a -function oscillator(;name, k=1.0, τ=0.01) +function oscillator(; name, k = 1.0, τ = 0.01) @parameters k=k τ=τ @variables x(..)=0.1 y(t)=0.1 jcn(t)=0.0 delx(t) eqs = [D(x(t)) ~ y, - D(y) ~ -k*x(t-τ)+jcn, - delx ~ x(t-τ)] - return System(eqs; name=name) + D(y) ~ -k * x(t - τ) + jcn, + delx ~ x(t - τ)] + return System(eqs; name = name) end -@named osc1 = oscillator(k=1.0, τ=0.01) -@named osc2 = oscillator(k=2.0, τ=0.04) +systems = @named begin + osc1 = oscillator(k = 1.0, τ = 0.01) + osc2 = oscillator(k = 2.0, τ = 0.04) +end eqs = [osc1.jcn ~ osc2.delx, - osc2.jcn ~ osc1.delx] + osc2.jcn ~ osc1.delx] @named coupledOsc = System(eqs, t) -@named coupledOsc = compose(coupledOsc, [osc1, osc2]) -sys = structural_simplify(coupledOsc) -@test length(equations(sys)) == 4 -@test length(states(sys)) == 4 +@named coupledOsc = compose(coupledOsc, systems) +@named coupledOsc2 = System(eqs, t; systems) +for coupledOsc in [coupledOsc, coupledOsc2] + local sys = structural_simplify(coupledOsc) + @test length(equations(sys)) == 4 + @test length(states(sys)) == 4 +end diff --git a/test/optimizationsystem.jl b/test/optimizationsystem.jl index a75154fda9..d190003339 100644 --- a/test/optimizationsystem.jl +++ b/test/optimizationsystem.jl @@ -207,18 +207,20 @@ end prob = OptimizationProblem(combinedsys, u0, p, grad = true, hess = true, cons_j = true, cons_h = true) @test prob.f.sys === combinedsys - @test_broken SciMLBase.successful_retcode(solve(prob, Ipopt.Optimizer(); print_level = 0)) - #= - @test sol.minimum < -1e5 - - prob = OptimizationProblem(sys2, [x => 0.0, y => 0.0], [a => 1.0, b => 100.0], - grad = true, hess = true, cons_j = true, cons_h = true) - @test prob.f.sys === sys2 - sol = solve(prob, IPNewton()) - @test sol.minimum < 1.0 - sol = solve(prob, Ipopt.Optimizer(); print_level = 0) - @test sol.minimum < 1.0 - =# + @test_broken SciMLBase.successful_retcode(solve(prob, + Ipopt.Optimizer(); + print_level = 0)) + #= + @test sol.minimum < -1e5 + + prob = OptimizationProblem(sys2, [x => 0.0, y => 0.0], [a => 1.0, b => 100.0], + grad = true, hess = true, cons_j = true, cons_h = true) + @test prob.f.sys === sys2 + sol = solve(prob, IPNewton()) + @test sol.minimum < 1.0 + sol = solve(prob, Ipopt.Optimizer(); print_level = 0) + @test sol.minimum < 1.0 + =# end @testset "metadata" begin From 2267dd1642176d83c1f45f5754a5a99dc8feca86 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Fri, 1 Sep 2023 13:40:38 -0400 Subject: [PATCH 25/32] Fix namespacing of mixed equations and reactions --- src/systems/abstractsystem.jl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 639ee60afc..075e11290c 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -472,15 +472,18 @@ function namespace_defaults(sys) for (k, v) in pairs(defs)) end -function namespace_equations(sys::AbstractSystem) +function namespace_equations(sys::AbstractSystem, ivs = nothing) eqs = equations(sys) isempty(eqs) && return Equation[] - map(eq -> namespace_equation(eq, sys), eqs) + map(eq -> namespace_equation(eq, sys; ivs), eqs) end -function namespace_equation(eq::Equation, sys, n = nameof(sys)) - _lhs = namespace_expr(eq.lhs, sys, n) - _rhs = namespace_expr(eq.rhs, sys, n) +function namespace_equation(eq::Equation, + sys, + n = nameof(sys); + ivs = independent_variables(sys)) + _lhs = namespace_expr(eq.lhs, sys, n; ivs) + _rhs = namespace_expr(eq.rhs, sys, n; ivs) _lhs ~ _rhs end @@ -490,15 +493,14 @@ function namespace_assignment(eq::Assignment, sys) Assignment(_lhs, _rhs) end -function namespace_expr(O, sys, n = nameof(sys)) - ivs = independent_variables(sys) +function namespace_expr(O, sys, n = nameof(sys); ivs = independent_variables(sys)) O = unwrap(O) if any(isequal(O), ivs) return O elseif istree(O) T = typeof(O) renamed = let sys = sys, n = n, T = T - map(a -> namespace_expr(a, sys, n)::Any, arguments(O)) + map(a -> namespace_expr(a, sys, n; ivs)::Any, arguments(O)) end if isvariable(O) # Use renamespace so the scope is correct, and make sure to use the @@ -513,7 +515,7 @@ function namespace_expr(O, sys, n = nameof(sys)) renamespace(n, O) elseif O isa Array let sys = sys, n = n - map(o -> namespace_expr(o, sys, n), O) + map(o -> namespace_expr(o, sys, n; ivs), O) end else O From 506a26e39d5b1cffa4b56fe99b8e4231c8516a87 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Fri, 1 Sep 2023 14:04:16 -0400 Subject: [PATCH 26/32] Fix default --- src/systems/abstractsystem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 075e11290c..2d38589eaf 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -472,7 +472,7 @@ function namespace_defaults(sys) for (k, v) in pairs(defs)) end -function namespace_equations(sys::AbstractSystem, ivs = nothing) +function namespace_equations(sys::AbstractSystem, ivs = independent_variables(sys)) eqs = equations(sys) isempty(eqs) && return Equation[] map(eq -> namespace_equation(eq, sys; ivs), eqs) From 05211143d5c479c9c12917419005852c5b03caac Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Fri, 1 Sep 2023 14:14:31 -0400 Subject: [PATCH 27/32] Remove dead code --- src/systems/abstractsystem.jl | 14 ----------- src/systems/jumps/jumpsystem.jl | 1 - .../optimization/optimizationsystem.jl | 6 ++--- src/utils.jl | 24 ------------------- 4 files changed, 3 insertions(+), 42 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 2d38589eaf..99ae83fa2b 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -663,20 +663,6 @@ function SymbolicIndexingInterface.is_param_sym(sys::AbstractSystem, sym) !isnothing(SymbolicIndexingInterface.param_sym_to_index(sys, sym)) end -struct AbstractSysToExpr - sys::AbstractSystem - states::Vector -end -AbstractSysToExpr(sys) = AbstractSysToExpr(sys, states(sys)) -function (f::AbstractSysToExpr)(O) - !istree(O) && return toexpr(O) - any(isequal(O), f.states) && return nameof(operation(O)) # variables - if issym(operation(O)) - return build_expr(:call, Any[nameof(operation(O)); f.(arguments(O))]) - end - return build_expr(:call, Any[operation(O); f.(arguments(O))]) -end - ### ### System utils ### diff --git a/src/systems/jumps/jumpsystem.jl b/src/systems/jumps/jumpsystem.jl index 910e4fc57b..8e87d145c2 100644 --- a/src/systems/jumps/jumpsystem.jl +++ b/src/systems/jumps/jumpsystem.jl @@ -181,7 +181,6 @@ function generate_rate_function(js::JumpSystem, rate) end rf = build_function(rate, states(js), parameters(js), get_iv(js), - conv = states_to_sym(states(js)), expression = Val{true}) end diff --git a/src/systems/optimization/optimizationsystem.jl b/src/systems/optimization/optimizationsystem.jl index 811597ec77..2d935aa030 100644 --- a/src/systems/optimization/optimizationsystem.jl +++ b/src/systems/optimization/optimizationsystem.jl @@ -124,7 +124,7 @@ function generate_gradient(sys::OptimizationSystem, vs = states(sys), ps = param grad = calculate_gradient(sys) pre = get_preprocess_constants(grad) return build_function(grad, vs, ps; postprocess_fbody = pre, - conv = AbstractSysToExpr(sys), kwargs...) + kwargs...) end function calculate_hessian(sys::OptimizationSystem) @@ -140,14 +140,14 @@ function generate_hessian(sys::OptimizationSystem, vs = states(sys), ps = parame end pre = get_preprocess_constants(hess) return build_function(hess, vs, ps; postprocess_fbody = pre, - conv = AbstractSysToExpr(sys), kwargs...) + kwargs...) end function generate_function(sys::OptimizationSystem, vs = states(sys), ps = parameters(sys); kwargs...) eqs = subs_constants(objective(sys)) return build_function(eqs, vs, ps; - conv = AbstractSysToExpr(sys), kwargs...) + kwargs...) end function namespace_objective(sys::AbstractSystem) diff --git a/src/utils.jl b/src/utils.jl index 4dc2a636df..29ba9b19ab 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -50,30 +50,6 @@ end @deprecate substitute_expr!(expr, s) substitute(expr, s) -function states_to_sym(states::Set) - function _states_to_sym(O) - if O isa Equation - Expr(:(=), _states_to_sym(O.lhs), _states_to_sym(O.rhs)) - elseif istree(O) - op = operation(O) - args = arguments(O) - if issym(op) - O in states && return tosymbol(O) - # dependent variables - return build_expr(:call, Any[nameof(op); _states_to_sym.(args)]) - else - canonical, O = canonicalexpr(O) - return canonical ? O : build_expr(:call, Any[op; _states_to_sym.(args)]) - end - elseif O isa Num - return _states_to_sym(value(O)) - else - return toexpr(O) - end - end -end -states_to_sym(states) = states_to_sym(Set(states)) - function todict(d) eltype(d) <: Pair || throw(ArgumentError("The variable-value mapping must be a Dict.")) d isa Dict ? d : Dict(d) From 7d0d846c3acf9337c24fa8fb2d99d82698563b92 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Fri, 1 Sep 2023 17:18:51 -0400 Subject: [PATCH 28/32] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5a2edb6515..4c48acca5f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkit" uuid = "961ee093-0014-501f-94e3-6117800e7a78" authors = ["Yingbo Ma ", "Chris Rackauckas and contributors"] -version = "8.65.0" +version = "8.66.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 847af0d77d6259a5c30fce409cd210732c80d950 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 4 Sep 2023 11:25:22 -0400 Subject: [PATCH 29/32] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4c48acca5f..1ad042c18f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkit" uuid = "961ee093-0014-501f-94e3-6117800e7a78" authors = ["Yingbo Ma ", "Chris Rackauckas and contributors"] -version = "8.66.0" +version = "8.67.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From a8acb6a9bed58fd775384c84e1863e4cb228bac0 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Thu, 7 Sep 2023 17:32:14 +0200 Subject: [PATCH 30/32] add default arguments to dummy derivatives --- src/structural_transformation/partial_state_selection.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structural_transformation/partial_state_selection.jl b/src/structural_transformation/partial_state_selection.jl index f2fec0269f..bae8816b50 100644 --- a/src/structural_transformation/partial_state_selection.jl +++ b/src/structural_transformation/partial_state_selection.jl @@ -175,8 +175,8 @@ function dummy_derivative_graph!(state::TransformationState, jac = nothing; dummy_derivative_graph!(state.structure, var_eq_matching, jac, state_priority, log) end -function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, jac, - state_priority, ::Val{log} = Val(false)) where {log} +function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, jac = nothing, + state_priority = nothing, ::Val{log} = Val(false)) where {log} @unpack eq_to_diff, var_to_diff, graph = structure diff_to_eq = invview(eq_to_diff) diff_to_var = invview(var_to_diff) From ef64d7853e48e5cca9a7eb437ffc989eb50662da Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Thu, 7 Sep 2023 13:14:28 -0400 Subject: [PATCH 31/32] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1ad042c18f..2dbca378c8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkit" uuid = "961ee093-0014-501f-94e3-6117800e7a78" authors = ["Yingbo Ma ", "Chris Rackauckas and contributors"] -version = "8.67.0" +version = "8.68.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 45a179ef38576a80716781fa77eaaffa72742a10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:39:05 +0000 Subject: [PATCH 32/32] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/Documentation.yml | 2 +- .github/workflows/Downstream.yml | 4 ++-- .github/workflows/FormatCheck.yml | 2 +- .github/workflows/Invalidations.yml | 4 ++-- .github/workflows/ci.yml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index e87960a661..8a688af7aa 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -11,7 +11,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@latest with: version: '1' diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index b8720a6d73..519532d82b 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -31,14 +31,14 @@ jobs: - {user: SciML, repo: MethodOfLines.jl, group: DAE} - {user: ai4energy, repo: Ai4EComponentLib.jl, group: Downstream} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.julia-version }} arch: x64 - uses: julia-actions/julia-buildpkg@latest - name: Clone Downstream - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ matrix.package.user }}/${{ matrix.package.repo }} path: downstream diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index b4ab23fd41..55af13cc40 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -21,7 +21,7 @@ jobs: with: version: ${{ matrix.julia-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install JuliaFormatter and format # This will use the latest version by default but you can set the version like so: # diff --git a/.github/workflows/Invalidations.yml b/.github/workflows/Invalidations.yml index 4d0004e831..28b9ce2fad 100644 --- a/.github/workflows/Invalidations.yml +++ b/.github/workflows/Invalidations.yml @@ -19,12 +19,12 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: '1' - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-invalidations@v1 id: invs_pr - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.repository.default_branch }} - uses: julia-actions/julia-buildpkg@v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 262e88f4b0..65ff03ee1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - '1' - '1.6' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }}