From ba4a460d7dff7855ce1cae61d8aa74eb201eac16 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Wed, 13 Aug 2025 16:34:28 +0530 Subject: [PATCH 1/6] feat: add `SCCNonlinearProblem` target for nlstep --- src/problems/odeproblem.jl | 5 +++-- src/systems/solver_nlprob.jl | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/problems/odeproblem.jl b/src/problems/odeproblem.jl index da7fd10e31..822c30ce49 100644 --- a/src/problems/odeproblem.jl +++ b/src/problems/odeproblem.jl @@ -3,7 +3,8 @@ t = nothing, eval_expression = false, eval_module = @__MODULE__, sparse = false, steady_state = false, checkbounds = false, sparsity = false, analytic = nothing, simplify = false, cse = true, initialization_data = nothing, expression = Val{false}, - check_compatibility = true, nlstep = false, nlstep_compile = true, kwargs...) where {iip, spec} + check_compatibility = true, nlstep = false, nlstep_compile = true, nlstep_scc = false, + kwargs...) where {iip, spec} check_complete(sys, ODEFunction) check_compatibility && check_compatible_system(ODEFunction, sys) @@ -42,7 +43,7 @@ _M = concrete_massmatrix(M; sparse, u0) if nlstep - ode_nlstep = generate_ODENLStepData(sys, u0, p, M, nlstep_compile) + ode_nlstep = generate_ODENLStepData(sys, u0, p, M, nlstep_compile, nlstep_scc) else ode_nlstep = nothing end diff --git a/src/systems/solver_nlprob.jl b/src/systems/solver_nlprob.jl index fa7e3c3885..badfe21efb 100644 --- a/src/systems/solver_nlprob.jl +++ b/src/systems/solver_nlprob.jl @@ -1,4 +1,5 @@ -function generate_ODENLStepData(sys::System, u0, p, mm = calculate_massmatrix(sys), nlstep_compile::Bool = true) +function generate_ODENLStepData(sys::System, u0, p, mm = calculate_massmatrix(sys), + nlstep_compile::Bool = true, nlstep_scc::Bool = false) nlsys, outer_tmp, inner_tmp = inner_nlsystem(sys, mm, nlstep_compile) state = ProblemState(; u = u0, p) op = Dict() @@ -12,7 +13,11 @@ function generate_ODENLStepData(sys::System, u0, p, mm = calculate_massmatrix(sy haskey(op, v) && continue op[v] = getsym(sys, v)(state) end - nlprob = NonlinearProblem(nlsys, op; build_initializeprob = false) + nlprob = if nlstep_scc + SCCNonlinearProblem(nlsys, op; build_initializeprob = false) + else + NonlinearProblem(nlsys, op; build_initializeprob = false) + end subsetidxs = [findfirst(isequal(y), unknowns(sys)) for y in unknowns(nlsys)] set_gamma_c = setsym(nlsys, (ODE_GAMMA..., ODE_C)) From 382f17f4cebdec241c3f0846aeb38dbbfe5b6d89 Mon Sep 17 00:00:00 2001 From: Bumblebee00 Date: Thu, 14 Aug 2025 15:05:34 +0200 Subject: [PATCH 2/6] Updated tests for the new behaviour of SymbolicUtils simplify function --- test/changeofvariables.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/changeofvariables.jl b/test/changeofvariables.jl index 1dccfaec28..08f69cb16e 100644 --- a/test/changeofvariables.jl +++ b/test/changeofvariables.jl @@ -135,7 +135,7 @@ new_sys = change_of_variables(sys, t, forward_subs, backward_subs) @test noise_eqs(new_sys)[1, 1] === value(σ) @test noise_eqs(new_sys)[1, 2] === value(0) @test noise_eqs(new_sys)[2, 1] === value(0) -@test noise_eqs(new_sys)[2, 2] === value(substitute(2*α*y, backward_subs[2])) +@test noise_eqs(new_sys)[2, 2] === value(simplify(substitute(2*α*y, backward_subs[2]))) @test noise_eqs(new_sys)[3, 1] === value(σ) @test noise_eqs(new_sys)[3, 2] === value(α) From 2b5562893880f6d2ca11a4e8898bf7a6a0d39ea5 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Sun, 17 Aug 2025 13:07:08 +0530 Subject: [PATCH 3/6] fix: fix callback with non-`Real` parameter and non-empty unknowns --- src/systems/callbacks.jl | 2 +- test/symbolic_events.jl | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/systems/callbacks.jl b/src/systems/callbacks.jl index a97ab2d233..f344f33d39 100644 --- a/src/systems/callbacks.jl +++ b/src/systems/callbacks.jl @@ -855,7 +855,7 @@ end function default_operating_point(affsys::AffectSystem) sys = system(affsys) - op = Dict(unknowns(sys) .=> 0.0) + op = AnyDict(unknowns(sys) .=> 0.0) for p in parameters(sys) T = symtype(p) if T <: Number diff --git a/test/symbolic_events.jl b/test/symbolic_events.jl index cf90ccd283..5f37e524e1 100644 --- a/test/symbolic_events.jl +++ b/test/symbolic_events.jl @@ -1421,3 +1421,22 @@ end @named sys = System([D(y) ~ 2x + 1, x^2 ~ 2y^3], t; discrete_events = [dev]) sys = @test_nowarn mtkcompile(sys) end + +@testset "Non-`Real` symtype parameters in callback with unknown" begin + @mtkmodel MWE begin + @variables begin + x(t) = 1.0 + end + @parameters begin + p(t)::Int = 1 + end + @equations begin + D(x) ~ p * x + end + @discrete_events begin + 1.0 => [x ~ p * Pre(x) * sin(x)] + end + end + @mtkcompile sys = MWE() + @test_nowarn ODEProblem(sys, [], (0.0, 1.0)) +end From ff36edb474b10df37f0c2c5031d1761a6ebe6c42 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Tue, 19 Aug 2025 00:22:58 +0530 Subject: [PATCH 4/6] feat: ensure causal connectors generate causally ordered equations --- src/systems/connectors.jl | 46 +++++++++++++++++++++++++++++++-------- test/components.jl | 22 +++++++++++++++++++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/systems/connectors.jl b/src/systems/connectors.jl index 4e0c2adb14..c0ddf5baee 100644 --- a/src/systems/connectors.jl +++ b/src/systems/connectors.jl @@ -759,6 +759,7 @@ function generate_connection_equations_and_stream_connections( var = variable_from_vertex(sys, cvert)::BasicSymbolic vtype = cvert.type if vtype <: Union{InputVar, OutputVar} + length(cset) > 1 || continue inner_output = nothing outer_input = nothing for cvert in cset @@ -780,11 +781,11 @@ function generate_connection_equations_and_stream_connections( inner_output = cvert end end - root, rest = Iterators.peel(cset) - root_var = variable_from_vertex(sys, root) - for cvert in rest - var = variable_from_vertex(sys, cvert) - push!(eqs, root_var ~ var) + root_vert = something(inner_output, outer_input) + root_var = variable_from_vertex(sys, root_vert) + for cvert in cset + isequal(cvert, root_vert) && continue + push!(eqs, variable_from_vertex(sys, cvert) ~ root_var) end elseif vtype === Stream push!(stream_connections, cset) @@ -807,10 +808,37 @@ function generate_connection_equations_and_stream_connections( push!(eqs, 0 ~ rhs) end else # Equality - base = variable_from_vertex(sys, cset[1]) - for i in 2:length(cset) - v = variable_from_vertex(sys, cset[i]) - push!(eqs, base ~ v) + vars = map(Base.Fix1(variable_from_vertex, sys), cset) + outer_input = inner_output = nothing + all_io = true + # attempt to interpret the equality as a causal connectionset if + # possible + for (cvert, vert) in zip(cset, vars) + is_i = isinput(vert) + is_o = isoutput(vert) + all_io &= is_i || is_o + all_io || break + if cvert.isouter && is_i && outer_input === nothing + outer_input = cvert + elseif !cvert.isouter && is_o && inner_output === nothing + inner_output = cvert + end + end + # this doesn't necessarily mean this is a well-structured causal connection, + # but it is sufficient and we're generating equalities anyway. + if all_io && xor(outer_input !== nothing, inner_output !== nothing) + root_vert = something(inner_output, outer_input) + root_var = variable_from_vertex(sys, root_vert) + for (cvert, var) in zip(cset, vars) + isequal(cvert, root_vert) && continue + push!(eqs, var ~ root_var) + end + else + base = variable_from_vertex(sys, cset[1]) + for i in 2:length(cset) + v = vars[i] + push!(eqs, base ~ v) + end end end end diff --git a/test/components.jl b/test/components.jl index 8e5747c750..a66725ca35 100644 --- a/test/components.jl +++ b/test/components.jl @@ -335,3 +335,25 @@ end sys = complete(outer) @test getmetadata(sys, Int, nothing) == "test" end + +@testset "Causal connections generate causal equations" begin + # test interpretation of `Equality` cset as causal connection + @named input = RealInput() + @named comp1 = System(Equation[], t; systems = [input]) + @named output = RealOutput() + @named comp2 = System(Equation[], t; systems = [output]) + @named sys = System([connect(comp2.output, comp1.input)], t; systems = [comp1, comp2]) + eq = only(equations(expand_connections(sys))) + # as opposed to `output.u ~ input.u` + @test isequal(eq, comp1.input.u ~ comp2.output.u) + + # test causal ordering of true causal cset + @named input = RealInput() + @named comp1 = System(Equation[], t; systems = [input]) + @named output = RealOutput() + @named comp2 = System(Equation[], t; systems = [output]) + @named sys = System([connect(comp2.output.u, comp1.input.u)], t; systems = [comp1, comp2]) + eq = only(equations(expand_connections(sys))) + # as opposed to `output.u ~ input.u` + @test isequal(eq, comp1.input.u ~ comp2.output.u) +end From 843b396efc040f647e5180422368e8c3332eb41d Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Tue, 19 Aug 2025 12:11:53 +0530 Subject: [PATCH 5/6] test: update tests to new connection expansion --- test/causal_variables_connection.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/causal_variables_connection.jl b/test/causal_variables_connection.jl index eb922879e1..3124ac2f3b 100644 --- a/test/causal_variables_connection.jl +++ b/test/causal_variables_connection.jl @@ -36,13 +36,13 @@ end connect(C.output.u, P.input.u)] sys1 = System(eqs, t, systems = [P, C], name = :hej) sys = expand_connections(sys1) - @test any(isequal(P.output.u ~ C.input.u), equations(sys)) - @test any(isequal(C.output.u ~ P.input.u), equations(sys)) + @test any(isequal(C.input.u ~ P.output.u), equations(sys)) + @test any(isequal(P.input.u ~ C.output.u), equations(sys)) @named sysouter = System(Equation[], t; systems = [sys1]) sys = expand_connections(sysouter) - @test any(isequal(sys1.P.output.u ~ sys1.C.input.u), equations(sys)) - @test any(isequal(sys1.C.output.u ~ sys1.P.input.u), equations(sys)) + @test any(isequal(sys1.C.input.u ~ sys1.P.output.u), equations(sys)) + @test any(isequal(sys1.P.input.u ~ sys1.C.output.u), equations(sys)) end @testset "With Analysis Points" begin @@ -117,7 +117,7 @@ end @named sys = Outer() ss = toggle_namespacing(sys, false) eqs = equations(expand_connections(sys)) - @test issetequal(eqs, [ss.u ~ ss.inner.x + @test issetequal(eqs, [ss.inner.x ~ ss.u ss.inner.y ~ ss.inner.x - ss.inner.y ~ ss.v]) + ss.v ~ ss.inner.y]) end From 726912270df3c7dc8628c03c20e91c6c8ead0ca2 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Tue, 19 Aug 2025 14:03:06 +0530 Subject: [PATCH 6/6] build: bump minor version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 92f4376dca..f76149fa43 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 = "10.19.0" +version = "10.20.0" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"