From 18435687c95f88c76bd8065e4a65dc53fb685ef4 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Tue, 25 Mar 2025 23:06:56 +0530 Subject: [PATCH 1/7] feat: allow toggling namespacing independently of `complete` --- src/ModelingToolkit.jl | 2 +- src/systems/abstractsystem.jl | 38 ++++++++++++++++++- src/systems/analysis_points.jl | 8 +++- src/systems/diffeqs/odesystem.jl | 14 +++++-- src/systems/diffeqs/sdesystem.jl | 19 ++++++---- .../discrete_system/discrete_system.jl | 12 ++++-- .../implicit_discrete_system.jl | 12 ++++-- src/systems/jumps/jumpsystem.jl | 11 ++++-- src/systems/nonlinear/nonlinearsystem.jl | 11 ++++-- .../optimization/constraints_system.jl | 13 +++++-- .../optimization/optimizationsystem.jl | 15 +++++--- src/utils.jl | 14 +++++++ 12 files changed, 132 insertions(+), 37 deletions(-) diff --git a/src/ModelingToolkit.jl b/src/ModelingToolkit.jl index 1fdb946a93..b53d3ec098 100644 --- a/src/ModelingToolkit.jl +++ b/src/ModelingToolkit.jl @@ -328,7 +328,7 @@ export alg_equations, diff_equations, has_alg_equations, has_diff_equations export get_alg_eqs, get_diff_eqs, has_alg_eqs, has_diff_eqs export @variables, @parameters, @independent_variables, @constants, @brownian -export @named, @nonamespace, @namespace, extend, compose, complete +export @named, @nonamespace, @namespace, extend, compose, complete, toggle_namespacing export debug_system #export ContinuousClock, Discrete, sampletime, input_timedomain, output_timedomain diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 50440bcaae..89ff0e510b 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -590,7 +590,24 @@ function SymbolicIndexingInterface.all_symbols(sys::AbstractSystem) return syms end +""" + $(TYPEDSIGNATURES) + +Check whether a system is marked as `complete`. +""" iscomplete(sys::AbstractSystem) = isdefined(sys, :complete) && getfield(sys, :complete) +""" + $(TYPEDSIGNATURES) + +Check whether a system performs namespacing. +""" +function does_namespacing(sys::AbstractSystem) + if isdefined(sys, :namespacing) + getfield(sys, :namespacing) + else + iscomplete(sys) + end +end """ $(TYPEDSIGNATURES) @@ -798,9 +815,25 @@ function complete( if isdefined(sys, :initializesystem) && get_initializesystem(sys) !== nothing @set! sys.initializesystem = complete(get_initializesystem(sys); split) end + sys = toggle_namespacing(sys, false; safe = true) isdefined(sys, :complete) ? (@set! sys.complete = true) : sys end +""" + $(TYPEDSIGNATURES) + +Return a new `sys` with namespacing enabled or disabled, depending on `value`. The +keyword argument `safe` denotes whether systems that do not support such a toggle +should error or be ignored. +""" +function toggle_namespacing(sys::AbstractSystem, value::Bool; safe = false) + if !isdefined(sys, :namespacing) + safe && return sys + throw(ArgumentError("The system must define the `namespacing` flag to toggle namespacing")) + end + @set sys.namespacing = value +end + """ $(TYPEDSIGNATURES) @@ -985,13 +1018,14 @@ function Base.propertynames(sys::AbstractSystem; private = false) end end -function Base.getproperty(sys::AbstractSystem, name::Symbol; namespace = !iscomplete(sys)) +function Base.getproperty( + sys::AbstractSystem, name::Symbol; namespace = does_namespacing(sys)) if has_parent(sys) && (parent = get_parent(sys); parent !== nothing) return getproperty(parent, name; namespace) end wrap(getvar(sys, name; namespace = namespace)) end -function getvar(sys::AbstractSystem, name::Symbol; namespace = !iscomplete(sys)) +function getvar(sys::AbstractSystem, name::Symbol; namespace = does_namespacing(sys)) systems = get_systems(sys) if isdefined(sys, name) Base.depwarn( diff --git a/src/systems/analysis_points.jl b/src/systems/analysis_points.jl index eb43bc40ef..104c26cbfe 100644 --- a/src/systems/analysis_points.jl +++ b/src/systems/analysis_points.jl @@ -768,7 +768,13 @@ already an `AnalysisPoint` or `Symbol`) is simply wrapped in an array. `Symbol` `AnalysisPoint`s are namespaced with `sys`. """ canonicalize_ap(sys::AbstractSystem, ap::Symbol) = [AnalysisPoint(renamespace(sys, ap))] -canonicalize_ap(sys::AbstractSystem, ap::AnalysisPoint) = [ap] +function canonicalize_ap(sys::AbstractSystem, ap::AnalysisPoint) + if does_namespacing(sys) + return [ap] + else + return [renamespace(sys, ap)] + end +end canonicalize_ap(sys::AbstractSystem, ap) = [ap] function canonicalize_ap(sys::AbstractSystem, aps::Vector) mapreduce(Base.Fix1(canonicalize_ap, sys), vcat, aps; init = []) diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index ac08cbb5a7..bd0a58e10a 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -169,7 +169,11 @@ struct ODESystem <: AbstractODESystem """ substitutions::Any """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -207,8 +211,8 @@ struct ODESystem <: AbstractODESystem connector_type, preface, cevents, devents, parameter_dependencies, assertions = Dict{BasicSymbolic, String}(), metadata = nothing, gui_metadata = nothing, is_dde = false, - tstops = [], tearing_state = nothing, - substitutions = nothing, complete = false, index_cache = nothing, + tstops = [], tearing_state = nothing, substitutions = nothing, + namespacing = true, complete = false, index_cache = nothing, discrete_subsystems = nothing, solved_unknowns = nothing, split_idxs = nothing, ignored_connections = nothing, parent = nothing; checks::Union{Bool, Int} = true) @@ -218,6 +222,7 @@ struct ODESystem <: AbstractODESystem check_parameters(ps, iv) check_equations(deqs, iv) check_equations(equations(cevents), iv) + check_subsystems(systems) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) @@ -228,7 +233,8 @@ struct ODESystem <: AbstractODESystem ctrl_jac, Wfact, Wfact_t, name, description, systems, defaults, guesses, torn_matching, initializesystem, initialization_eqs, schedule, connector_type, preface, cevents, devents, parameter_dependencies, assertions, metadata, - gui_metadata, is_dde, tstops, tearing_state, substitutions, complete, index_cache, + gui_metadata, is_dde, tstops, tearing_state, substitutions, namespacing, + complete, index_cache, discrete_subsystems, solved_unknowns, split_idxs, ignored_connections, parent) end end diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index d8ac52dd1a..f51529a559 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -139,7 +139,11 @@ struct SDESystem <: AbstractODESystem """ gui_metadata::Union{Nothing, GUIMetadata} """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -166,7 +170,7 @@ struct SDESystem <: AbstractODESystem guesses, initializesystem, initialization_eqs, connector_type, cevents, devents, parameter_dependencies, assertions = Dict{ BasicSymbolic, Nothing}, - metadata = nothing, gui_metadata = nothing, + metadata = nothing, gui_metadata = nothing, namespacing = true, complete = false, index_cache = nothing, parent = nothing, is_scalar_noise = false, is_dde = false, isscheduled = false; @@ -184,6 +188,7 @@ struct SDESystem <: AbstractODESystem if is_scalar_noise && neqs isa AbstractMatrix throw(ArgumentError("Noise equations ill-formed. Received a matrix of noise equations of size $(size(neqs)), but `is_scalar_noise` was set to `true`. Scalar noise is only compatible with an `AbstractVector` of noise equations.")) end + check_subsystems(systems) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) @@ -192,8 +197,8 @@ struct SDESystem <: AbstractODESystem new(tag, deqs, neqs, iv, dvs, ps, tspan, var_to_name, ctrls, observed, tgrad, jac, ctrl_jac, Wfact, Wfact_t, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, connector_type, cevents, - devents, parameter_dependencies, assertions, metadata, gui_metadata, complete, - index_cache, parent, is_scalar_noise, is_dde, isscheduled) + devents, parameter_dependencies, assertions, metadata, gui_metadata, namespacing, + complete, index_cache, parent, is_scalar_noise, is_dde, isscheduled) end end @@ -218,7 +223,6 @@ function SDESystem(deqs::AbstractVector{<:Equation}, neqs::AbstractArray, iv, dv assertions = Dict{BasicSymbolic, String}(), metadata = nothing, gui_metadata = nothing, - complete = false, index_cache = nothing, parent = nothing, is_scalar_noise = false, @@ -274,7 +278,7 @@ function SDESystem(deqs::AbstractVector{<:Equation}, neqs::AbstractArray, iv, dv ctrl_jac, Wfact, Wfact_t, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, connector_type, cont_callbacks, disc_callbacks, parameter_dependencies, assertions, metadata, gui_metadata, - complete, index_cache, parent, is_scalar_noise, is_dde; checks = checks) + true, false, index_cache, parent, is_scalar_noise, is_dde; checks = checks) end function SDESystem(sys::ODESystem, neqs; kwargs...) @@ -321,9 +325,8 @@ function SDESystem(eqs::Vector{Equation}, noiseeqs::AbstractArray, iv; kwargs... throw(ArgumentError("Variable $dv in noise equations is not an unknown of the system.")) end algevars = setdiff(allunknowns, diffvars) - return SDESystem(eqs, noiseeqs, iv, Iterators.flatten((diffvars, algevars)), - [ps; collect(noiseps)]; kwargs...) + [collect(ps); collect(noiseps)]; kwargs...) end function SDESystem(eq::Equation, noiseeqs::AbstractArray, args...; kwargs...) diff --git a/src/systems/discrete_system/discrete_system.jl b/src/systems/discrete_system/discrete_system.jl index f51ba638a4..40f01769ee 100644 --- a/src/systems/discrete_system/discrete_system.jl +++ b/src/systems/discrete_system/discrete_system.jl @@ -97,7 +97,11 @@ struct DiscreteSystem <: AbstractDiscreteSystem """ substitutions::Any """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -114,7 +118,7 @@ struct DiscreteSystem <: AbstractDiscreteSystem observed, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, preface, connector_type, parameter_dependencies = Equation[], metadata = nothing, gui_metadata = nothing, - tearing_state = nothing, substitutions = nothing, + tearing_state = nothing, substitutions = nothing, namespacing = true, complete = false, index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) @@ -122,6 +126,7 @@ struct DiscreteSystem <: AbstractDiscreteSystem check_independent_variables([iv]) check_variables(dvs, iv) check_parameters(ps, iv) + check_subsystems(systems) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) @@ -130,7 +135,8 @@ struct DiscreteSystem <: AbstractDiscreteSystem new(tag, discreteEqs, iv, dvs, ps, tspan, var_to_name, observed, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, preface, connector_type, parameter_dependencies, metadata, gui_metadata, - tearing_state, substitutions, complete, index_cache, parent, isscheduled) + tearing_state, substitutions, namespacing, complete, index_cache, parent, + isscheduled) end end diff --git a/src/systems/discrete_system/implicit_discrete_system.jl b/src/systems/discrete_system/implicit_discrete_system.jl index 514576e927..ebae78384a 100644 --- a/src/systems/discrete_system/implicit_discrete_system.jl +++ b/src/systems/discrete_system/implicit_discrete_system.jl @@ -96,7 +96,11 @@ struct ImplicitDiscreteSystem <: AbstractDiscreteSystem """ substitutions::Any """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -113,7 +117,7 @@ struct ImplicitDiscreteSystem <: AbstractDiscreteSystem observed, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, preface, connector_type, parameter_dependencies = Equation[], metadata = nothing, gui_metadata = nothing, - tearing_state = nothing, substitutions = nothing, + tearing_state = nothing, substitutions = nothing, namespacing = true, complete = false, index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) @@ -121,6 +125,7 @@ struct ImplicitDiscreteSystem <: AbstractDiscreteSystem check_independent_variables([iv]) check_variables(dvs, iv) check_parameters(ps, iv) + check_subsystems(systems) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) @@ -129,7 +134,8 @@ struct ImplicitDiscreteSystem <: AbstractDiscreteSystem new(tag, discreteEqs, iv, dvs, ps, tspan, var_to_name, observed, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, preface, connector_type, parameter_dependencies, metadata, gui_metadata, - tearing_state, substitutions, complete, index_cache, parent, isscheduled) + tearing_state, substitutions, namespacing, complete, index_cache, parent, + isscheduled) end end diff --git a/src/systems/jumps/jumpsystem.jl b/src/systems/jumps/jumpsystem.jl index 7aa7c6909b..9da32a4305 100644 --- a/src/systems/jumps/jumpsystem.jl +++ b/src/systems/jumps/jumpsystem.jl @@ -116,7 +116,11 @@ struct JumpSystem{U <: ArrayPartition} <: AbstractTimeDependentSystem """ gui_metadata::Union{Nothing, GUIMetadata} """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -130,12 +134,13 @@ struct JumpSystem{U <: ArrayPartition} <: AbstractTimeDependentSystem systems, defaults, guesses, initializesystem, initialization_eqs, connector_type, cevents, devents, parameter_dependencies, metadata = nothing, gui_metadata = nothing, - complete = false, index_cache = nothing, isscheduled = false; + namespacing = true, complete = false, index_cache = nothing, isscheduled = false; checks::Union{Bool, Int} = true) where {U <: ArrayPartition} if checks == true || (checks & CheckComponents) > 0 check_independent_variables([iv]) check_variables(unknowns, iv) check_parameters(ps, iv) + check_subsystems(systems) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps, iv) @@ -145,7 +150,7 @@ struct JumpSystem{U <: ArrayPartition} <: AbstractTimeDependentSystem observed, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, connector_type, cevents, devents, parameter_dependencies, metadata, - gui_metadata, complete, index_cache, isscheduled) + gui_metadata, namespacing, complete, index_cache, isscheduled) end end function JumpSystem(tag, ap, iv, states, ps, var_to_name, args...; kwargs...) diff --git a/src/systems/nonlinear/nonlinearsystem.jl b/src/systems/nonlinear/nonlinearsystem.jl index ea3730fdb6..77215be5eb 100644 --- a/src/systems/nonlinear/nonlinearsystem.jl +++ b/src/systems/nonlinear/nonlinearsystem.jl @@ -95,7 +95,11 @@ struct NonlinearSystem <: AbstractTimeIndependentSystem """ substitutions::Any """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -112,17 +116,18 @@ struct NonlinearSystem <: AbstractTimeIndependentSystem tag, eqs, unknowns, ps, var_to_name, observed, jac, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, connector_type, parameter_dependencies = Equation[], metadata = nothing, gui_metadata = nothing, - tearing_state = nothing, substitutions = nothing, + tearing_state = nothing, substitutions = nothing, namespacing = true, complete = false, index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps) check_units(u, eqs) + check_subsystems(systems) end new(tag, eqs, unknowns, ps, var_to_name, observed, jac, name, description, systems, defaults, guesses, initializesystem, initialization_eqs, connector_type, parameter_dependencies, metadata, gui_metadata, tearing_state, - substitutions, complete, index_cache, parent, isscheduled) + substitutions, namespacing, complete, index_cache, parent, isscheduled) end end diff --git a/src/systems/optimization/constraints_system.jl b/src/systems/optimization/constraints_system.jl index 8a71c58043..0f69e6d0b9 100644 --- a/src/systems/optimization/constraints_system.jl +++ b/src/systems/optimization/constraints_system.jl @@ -74,7 +74,11 @@ struct ConstraintsSystem <: AbstractTimeIndependentSystem """ substitutions::Any """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -86,17 +90,18 @@ struct ConstraintsSystem <: AbstractTimeIndependentSystem name, description, systems, defaults, connector_type, metadata = nothing, - tearing_state = nothing, substitutions = nothing, + tearing_state = nothing, substitutions = nothing, namespacing = true, complete = false, index_cache = nothing; checks::Union{Bool, Int} = true) if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps) check_units(u, constraints) + check_subsystems(systems) end new(tag, constraints, unknowns, ps, var_to_name, observed, jac, name, description, systems, - defaults, - connector_type, metadata, tearing_state, substitutions, complete, index_cache) + defaults, connector_type, metadata, tearing_state, substitutions, + namespacing, complete, index_cache) end end diff --git a/src/systems/optimization/optimizationsystem.jl b/src/systems/optimization/optimizationsystem.jl index bd0f0aa679..e55f4b7871 100644 --- a/src/systems/optimization/optimizationsystem.jl +++ b/src/systems/optimization/optimizationsystem.jl @@ -55,7 +55,11 @@ struct OptimizationSystem <: AbstractOptimizationSystem """ gui_metadata::Union{Nothing, GUIMetadata} """ - If a model `sys` is complete, then `sys.x` no longer performs namespacing. + If false, then `sys.x` no longer performs namespacing. + """ + namespacing::Bool + """ + If true, denotes the model will not be modified any further. """ complete::Bool """ @@ -70,18 +74,19 @@ struct OptimizationSystem <: AbstractOptimizationSystem function OptimizationSystem(tag, op, unknowns, ps, var_to_name, observed, constraints, name, description, systems, defaults, metadata = nothing, - gui_metadata = nothing, complete = false, index_cache = nothing, parent = nothing, - isscheduled = false; + gui_metadata = nothing, namespacing = true, complete = false, + index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps) unwrap(op) isa Symbolic && check_units(u, op) check_units(u, observed) check_units(u, constraints) + check_subsystems(systems) end new(tag, op, unknowns, ps, var_to_name, observed, - constraints, name, description, systems, defaults, metadata, gui_metadata, complete, - index_cache, parent, isscheduled) + constraints, name, description, systems, defaults, metadata, gui_metadata, + namespacing, complete, index_cache, parent, isscheduled) end end diff --git a/src/utils.jl b/src/utils.jl index 55f3d2cec5..726a2ce25f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -202,6 +202,20 @@ function check_equations(eqs, iv) throw(ArgumentError("Differential w.r.t. variable ($single_iv) other than the independent variable ($iv) are not allowed.")) end end + +""" + $(TYPEDSIGNATURES) + +Assert that the subsystems have the appropriate namespacing behavior. +""" +function check_subsystems(systems) + idxs = findall(!does_namespacing, systems) + if !isempty(idxs) + names = join(" " .* string.(nameof.(systems[idxs])), "\n") + throw(ArgumentError("All subsystems have namespacing enabled. The following subsystems do not perform namespacing:\n$(names)")) + end +end + """ Get all the independent variables with respect to which differentials are taken. """ From f9bbdc5c0d2e85dc838914f3f7dc9c27334a8a32 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Tue, 25 Mar 2025 23:07:03 +0530 Subject: [PATCH 2/7] docs: document `toggle_namespacing` --- docs/src/basics/AbstractSystem.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/src/basics/AbstractSystem.md b/docs/src/basics/AbstractSystem.md index 3aa43e6d25..d1707f822f 100644 --- a/docs/src/basics/AbstractSystem.md +++ b/docs/src/basics/AbstractSystem.md @@ -148,3 +148,15 @@ The `AbstractSystem` types allow for specifying default values, for example into the value maps, where for any repeats the value maps override the default. In addition, defaults of a higher level in the system override the defaults of a lower level in the system. + +## Namespacing + +By default, unsimplified systems will namespace variables accessed via `getproperty`. +Systems created via `@mtkbuild`, or ones passed through `structural_simplify` or +`complete` will not perform this namespacing. However, all of these processes modify +the system in a variety of ways. To toggle namespacing without transforming any other +property of the system, use `toggle_namespacing`. + +```@docs +toggle_namespacing +``` From e0ae7a9131ef5f32ffae362a5fbc8e027648a620 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Tue, 25 Mar 2025 23:10:46 +0530 Subject: [PATCH 3/7] test: test new namespacing functionality --- test/analysis_points.jl | 9 +- test/namespacing.jl | 186 ++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 3 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 test/namespacing.jl diff --git a/test/analysis_points.jl b/test/analysis_points.jl index 9e8e3fb455..e36afc02f7 100644 --- a/test/analysis_points.jl +++ b/test/analysis_points.jl @@ -47,8 +47,11 @@ end @named sys = ODESystem(Equation[], t; systems = [sys_ap]) ap3 = @test_nowarn sys.hej.plant_input @test nameof(ap3) == Symbol(join(["sys", "hej", "plant_input"], NAMESPACE_SEPARATOR)) - sys = complete(sys) - ap4 = sys.hej.plant_input + csys = complete(sys) + ap4 = csys.hej.plant_input + @test nameof(ap4) == Symbol(join(["hej", "plant_input"], NAMESPACE_SEPARATOR)) + nsys = toggle_namespacing(sys, false) + ap5 = nsys.hej.plant_input @test nameof(ap4) == Symbol(join(["hej", "plant_input"], NAMESPACE_SEPARATOR)) end @@ -61,6 +64,7 @@ ap = AnalysisPoint(:plant_input) eqs = [connect(P.output, C.input), connect(C.output, ap, P.input)] sys = ODESystem(eqs, t, systems = [P, C], name = :hej) @named nested_sys = ODESystem(Equation[], t; systems = [sys]) +nonamespace_sys = toggle_namespacing(nested_sys, false) @testset "simplifies and solves" begin ssys = structural_simplify(sys) @@ -73,6 +77,7 @@ end test_cases = [ ("inner", sys, sys.plant_input), ("nested", nested_sys, nested_sys.hej.plant_input), + ("nonamespace", nonamespace_sys, nonamespace_sys.hej.plant_input), ("inner - Symbol", sys, :plant_input), ("nested - Symbol", nested_sys, nameof(sys.plant_input)) ] diff --git a/test/namespacing.jl b/test/namespacing.jl new file mode 100644 index 0000000000..b6305a1776 --- /dev/null +++ b/test/namespacing.jl @@ -0,0 +1,186 @@ +using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D, iscomplete, does_namespacing + +@testset "ODESystem" begin + @variables x(t) + @parameters p + sys = ODESystem(D(x) ~ p * x, t; name = :inner) + @test !iscomplete(sys) + @test does_namespacing(sys) + + csys = complete(sys) + @test iscomplete(csys) + @test !does_namespacing(csys) + + nsys = toggle_namespacing(sys, false) + @test !iscomplete(nsys) + @test !does_namespacing(nsys) + + @test isequal(x, csys.x) + @test isequal(x, nsys.x) + @test !isequal(x, sys.x) + @test isequal(p, csys.p) + @test isequal(p, nsys.p) + @test !isequal(p, sys.p) + + @test_throws ["namespacing", "inner"] ODESystem( + Equation[], t; systems = [nsys], name = :a) +end + +@testset "SDESystem" begin + @variables x(t) + @parameters p + sys = SDESystem([D(x) ~ p * x], [x], t, [x], [p]; name = :inner) + @test !iscomplete(sys) + @test does_namespacing(sys) + + csys = complete(sys) + @test iscomplete(csys) + @test !does_namespacing(csys) + + nsys = toggle_namespacing(sys, false) + @test !iscomplete(nsys) + @test !does_namespacing(nsys) + + @test isequal(x, csys.x) + @test isequal(x, nsys.x) + @test !isequal(x, sys.x) + @test isequal(p, csys.p) + @test isequal(p, nsys.p) + @test !isequal(p, sys.p) + + @test_throws ["namespacing", "inner"] SDESystem( + Equation[], [], t; systems = [nsys], name = :a) +end + +@testset "DiscreteSystem" begin + @variables x(t) + @parameters p + k = ShiftIndex(t) + sys = DiscreteSystem([x(k) ~ p * x(k - 1)], t; name = :inner) + @test !iscomplete(sys) + @test does_namespacing(sys) + + csys = complete(sys) + @test iscomplete(csys) + @test !does_namespacing(csys) + + nsys = toggle_namespacing(sys, false) + @test !iscomplete(nsys) + @test !does_namespacing(nsys) + + @test isequal(x, csys.x) + @test isequal(x, nsys.x) + @test !isequal(x, sys.x) + @test isequal(p, csys.p) + @test isequal(p, nsys.p) + @test !isequal(p, sys.p) + + @test_throws ["namespacing", "inner"] DiscreteSystem( + Equation[], t; systems = [nsys], name = :a) +end + +@testset "ImplicitDiscreteSystem" begin + @variables x(t) + @parameters p + k = ShiftIndex(t) + sys = ImplicitDiscreteSystem([x(k) ~ p + x(k - 1) * x(k)], t; name = :inner) + @test !iscomplete(sys) + @test does_namespacing(sys) + + csys = complete(sys) + @test iscomplete(csys) + @test !does_namespacing(csys) + + nsys = toggle_namespacing(sys, false) + @test !iscomplete(nsys) + @test !does_namespacing(nsys) + + @test isequal(x, csys.x) + @test isequal(x, nsys.x) + @test !isequal(x, sys.x) + @test isequal(p, csys.p) + @test isequal(p, nsys.p) + @test !isequal(p, sys.p) + + @test_throws ["namespacing", "inner"] ImplicitDiscreteSystem( + Equation[], t; systems = [nsys], name = :a) +end + +@testset "NonlinearSystem" begin + @variables x + @parameters p + sys = NonlinearSystem([x ~ p * x^2 + 1]; name = :inner) + @test !iscomplete(sys) + @test does_namespacing(sys) + + csys = complete(sys) + @test iscomplete(csys) + @test !does_namespacing(csys) + + nsys = toggle_namespacing(sys, false) + @test !iscomplete(nsys) + @test !does_namespacing(nsys) + + @test isequal(x, csys.x) + @test isequal(x, nsys.x) + @test !isequal(x, sys.x) + @test isequal(p, csys.p) + @test isequal(p, nsys.p) + @test !isequal(p, sys.p) + + @test_throws ["namespacing", "inner"] NonlinearSystem( + Equation[]; systems = [nsys], name = :a) +end + +@testset "OptimizationSystem" begin + @variables x + @parameters p + sys = OptimizationSystem(p * x^2 + 1; name = :inner) + @test !iscomplete(sys) + @test does_namespacing(sys) + + csys = complete(sys) + @test iscomplete(csys) + @test !does_namespacing(csys) + + nsys = toggle_namespacing(sys, false) + @test !iscomplete(nsys) + @test !does_namespacing(nsys) + + @test isequal(x, csys.x) + @test isequal(x, nsys.x) + @test !isequal(x, sys.x) + @test isequal(p, csys.p) + @test isequal(p, nsys.p) + @test !isequal(p, sys.p) + + @test_throws ["namespacing", "inner"] OptimizationSystem( + []; systems = [nsys], name = :a) +end + +@testset "ConstraintsSystem" begin + @variables x + @parameters p + sys = ConstraintsSystem([x^2 + p ~ 0], [x], [p]; name = :inner) + @test !iscomplete(sys) + @test does_namespacing(sys) + + csys = complete(sys) + @test iscomplete(csys) + @test !does_namespacing(csys) + + nsys = toggle_namespacing(sys, false) + @test !iscomplete(nsys) + @test !does_namespacing(nsys) + + @test isequal(x, csys.x) + @test isequal(x, nsys.x) + @test !isequal(x, sys.x) + @test isequal(p, csys.p) + @test isequal(p, nsys.p) + @test !isequal(p, sys.p) + + @test_throws ["namespacing", "inner"] ConstraintsSystem( + [], [], []; systems = [nsys], name = :a) +end diff --git a/test/runtests.jl b/test/runtests.jl index a03b0f1977..301134219e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -97,6 +97,7 @@ end @safetestset "Analysis Points Test" include("analysis_points.jl") @safetestset "Causal Variables Connection Test" include("causal_variables_connection.jl") @safetestset "Debugging Test" include("debugging.jl") + @safetestset "Namespacing test" include("namespacing.jl") end end From d62da8fc2c1e8900024a69b8d11100235e0b85e2 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Wed, 26 Mar 2025 11:56:33 +0530 Subject: [PATCH 4/7] refactor: improve error message for subsystem namespacing --- src/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index 726a2ce25f..2a0009b644 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -212,7 +212,7 @@ function check_subsystems(systems) idxs = findall(!does_namespacing, systems) if !isempty(idxs) names = join(" " .* string.(nameof.(systems[idxs])), "\n") - throw(ArgumentError("All subsystems have namespacing enabled. The following subsystems do not perform namespacing:\n$(names)")) + throw(ArgumentError("All subsystems must have namespacing enabled. The following subsystems do not perform namespacing:\n$(names)")) end end From 1e455e7257fe01dff8d074e88f07833975f03e7d Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Wed, 26 Mar 2025 11:56:43 +0530 Subject: [PATCH 5/7] test: fix usage of completed system as subsystem --- test/parameter_dependencies.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parameter_dependencies.jl b/test/parameter_dependencies.jl index ad2131f458..31881e1ca8 100644 --- a/test/parameter_dependencies.jl +++ b/test/parameter_dependencies.jl @@ -121,7 +121,7 @@ end @parameters p1=1.0 p2=2.0 @variables x(t) = 0 - @mtkbuild sys1 = ODESystem( + @named sys1 = ODESystem( [D(x) ~ p1 * t + p2], t ) From be6156164b26692d09deab757a8ac0cc1438a5ad Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Fri, 28 Mar 2025 14:31:34 +0530 Subject: [PATCH 6/7] fix: fix `does_namespacing` fallback to `iscomplete` --- 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 89ff0e510b..fb692b4028 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -605,7 +605,7 @@ function does_namespacing(sys::AbstractSystem) if isdefined(sys, :namespacing) getfield(sys, :namespacing) else - iscomplete(sys) + !iscomplete(sys) end end From b54b56f5c9655aac6c7e5dfe8698b91906e44a7b Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Fri, 28 Mar 2025 16:25:31 +0530 Subject: [PATCH 7/7] test: fix rootfinding tests --- test/symbolic_events.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/symbolic_events.jl b/test/symbolic_events.jl index 3fb66081a2..804432408b 100644 --- a/test/symbolic_events.jl +++ b/test/symbolic_events.jl @@ -422,7 +422,7 @@ cond.rf_ip(out, [0, 1], p0, t0) # this should return 0 cond.rf_ip(out, [0, 2], p0, t0) @test out[2] ā‰ˆ 1 # signature is u,p,t -sol = solve(prob, Tsit5()) +sol = solve(prob, Tsit5(); abstol = 1e-14, reltol = 1e-14) @test minimum(t -> abs(t - 1), sol.t) < 1e-10 # test that the solver stepped at the first root @test minimum(t -> abs(t - 2), sol.t) < 1e-10 # test that the solver stepped at the second root @@ -430,7 +430,7 @@ sol = solve(prob, Tsit5()) sys = complete(sys) prob = ODEProblem(sys, Pair[], (0.0, 3.0)) @test get_callback(prob) isa ModelingToolkit.DiffEqCallbacks.VectorContinuousCallback -sol = solve(prob, Tsit5()) +sol = solve(prob, Tsit5(); abstol = 1e-14, reltol = 1e-14) @test minimum(t -> abs(t - 1), sol.t) < 1e-10 # test that the solver stepped at the first root @test minimum(t -> abs(t - 2), sol.t) < 1e-10 # test that the solver stepped at the second root @@ -540,8 +540,8 @@ ev = [sin(20pi * t) ~ 0.0] => [vmeasured ~ v] sys = structural_simplify(sys) prob = ODEProblem(sys, zeros(2), (0.0, 5.1)) sol = solve(prob, Tsit5()) -@test all(minimum((0:0.1:5) .- sol.t', dims = 2) .< 0.0001) # test that the solver stepped every 0.1s as dictated by event -@test sol([0.25])[vmeasured][] == sol([0.23])[vmeasured][] # test the hold property +@test all(minimum((0:0.05:5) .- sol.t', dims = 2) .< 0.0001) # test that the solver stepped every 0.05s as dictated by event +@test sol([0.25 - eps()])[vmeasured][] == sol([0.23])[vmeasured][] # test the hold property ## https://github.com/SciML/ModelingToolkit.jl/issues/1528 Dā‚œ = D