Skip to content

Commit 983aaac

Browse files
committed
Merge branch 'master' into myb/ps
2 parents 510833c + 80e1b95 commit 983aaac

31 files changed

+522
-395
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
- This repository follows the [SciMLStyle](https://github.com/SciML/SciMLStyle) and the SciML [ColPrac](https://github.com/SciML/ColPrac).
2-
- Please run `using JuliaFormatter, ModelingToolkit; format(joinpath(dirname(pathof(ModelingToolkit)), ".."))` before commiting.
2+
- Please run `using JuliaFormatter, ModelingToolkit; format(joinpath(dirname(pathof(ModelingToolkit)), ".."))` before committing.
33
- Add tests for any new features.

Project.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelingToolkit"
22
uuid = "961ee093-0014-501f-94e3-6117800e7a78"
33
authors = ["Yingbo Ma <[email protected]>", "Chris Rackauckas <[email protected]> and contributors"]
4-
version = "8.64.0"
4+
version = "8.67.0"
55

66
[deps]
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
@@ -31,6 +31,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
3131
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
3232
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
3333
NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
34+
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
3435
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
3536
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
3637
RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47"
@@ -78,6 +79,7 @@ Latexify = "0.11, 0.12, 0.13, 0.14, 0.15, 0.16"
7879
MLStyle = "0.4.17"
7980
MacroTools = "0.5"
8081
NaNMath = "0.3, 1"
82+
OrdinaryDiffEq = "6"
8183
RecursiveArrayTools = "2.3"
8284
Reexport = "0.2, 1"
8385
RuntimeGeneratedFunctions = "0.5.9"
@@ -107,7 +109,6 @@ NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
107109
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
108110
OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1"
109111
OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e"
110-
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
111112
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
112113
ReferenceTests = "324d217c-45ce-50fc-942e-d289b448e8cf"
113114
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
@@ -120,4 +121,4 @@ Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4"
120121
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
121122

122123
[targets]
123-
test = ["AmplNLWriter", "BenchmarkTools", "ControlSystemsMTK", "NonlinearSolve", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "OrdinaryDiffEq", "Random", "ReferenceTests", "SafeTestsets", "StableRNGs", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials", "StochasticDelayDiffEq"]
124+
test = ["AmplNLWriter", "BenchmarkTools", "ControlSystemsMTK", "NonlinearSolve", "ForwardDiff", "Ipopt", "Ipopt_jll", "ModelingToolkitStandardLibrary", "Optimization", "OptimizationOptimJL", "OptimizationMOI", "Random", "ReferenceTests", "SafeTestsets", "StableRNGs", "Statistics", "SteadyStateDiffEq", "Test", "StochasticDiffEq", "Sundials", "StochasticDelayDiffEq"]

docs/src/basics/FAQ.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ We can solve this problem by using the `missing_variable_defaults()` function
120120
prob = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0,1))
121121
```
122122

123-
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.
123+
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.
124124

125125
```
126126
julia> ModelingToolkit.missing_variable_defaults(sys, [1,2,3])

src/ModelingToolkit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ abstract type AbstractOptimizationSystem <: AbstractTimeIndependentSystem end
106106

107107
function independent_variable end
108108

109-
# this has to be included early to deal with depency issues
109+
# this has to be included early to deal with dependency issues
110110
include("structural_transformation/bareiss.jl")
111111
function complete end
112112
function var_derivative! end

src/bipartite_graph.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ Essentially the graph adapter performs two largely orthogonal functions
618618
1. It pairs an undirected bipartite graph with a matching of the destination vertex.
619619
620620
This matching is used to induce an orientation on the otherwise undirected graph:
621-
Matched edges pass from destination to source [source to desination], all other edges
621+
Matched edges pass from destination to source [source to destination], all other edges
622622
pass in the opposite direction.
623623
624624
2. It exposes the graph view obtained by contracting the destination [source] vertices
@@ -632,7 +632,7 @@ graph is acyclic.
632632
# Hypergraph interpretation
633633
634634
Consider the bipartite graph `B` as the incidence graph of some hypergraph `H`.
635-
Note that a maching `M` on `B` in the above sense is equivalent to determining
635+
Note that a matching `M` on `B` in the above sense is equivalent to determining
636636
an (1,n)-orientation on the hypergraph (i.e. each directed hyperedge has exactly
637637
one head, but any arbitrary number of tails). In this setting, this is simply
638638
the graph formed by expanding each directed hyperedge into `n` ordinary edges
@@ -685,7 +685,7 @@ function Base.iterate(c::CMONeighbors{false}, (l, state...))
685685
r === nothing && return nothing
686686
# If this is a matched edge, skip it, it's reversed in the induced
687687
# directed graph. Otherwise, if there is no matching for this destination
688-
# edge, also skip it, since it got delted in the contraction.
688+
# edge, also skip it, since it got deleted in the contraction.
689689
vsrc = c.g.matching[r[1]]
690690
if vsrc === c.v || !isa(vsrc, Int)
691691
state = (r[2],)

src/inputoutput.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ end
112112
same_or_inner_namespace(u, var)
113113
114114
Determine whether `var` is in the same namespace as `u`, or a namespace internal to the namespace of `u`.
115-
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`.
115+
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`.
116116
"""
117117
function same_or_inner_namespace(u, var)
118118
nu = get_namespace(u)
119119
nv = get_namespace(var)
120120
nu == nv || # namespaces are the same
121-
startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namepsace to nu
121+
startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namespace to nu
122122
occursin('', string(getname(var))) &&
123123
!occursin('', string(getname(u))) # or u is top level but var is internal
124124
end
@@ -127,7 +127,7 @@ function inner_namespace(u, var)
127127
nu = get_namespace(u)
128128
nv = get_namespace(var)
129129
nu == nv && return false
130-
startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namepsace to nu
130+
startswith(nv, nu) || # or nv starts with nu, i.e., nv is an inner namespace to nu
131131
occursin('', string(getname(var))) &&
132132
!occursin('', string(getname(u))) # or u is top level but var is internal
133133
end
@@ -177,7 +177,7 @@ f_ip : (xout,x,u,p,t) -> nothing
177177
178178
The return values also include the remaining states and parameters, in the order they appear as arguments to `f`.
179179
180-
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.
180+
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.
181181
See [`add_input_disturbance`](@ref) for a higher-level interface to this functionality.
182182
183183
# Example

src/structural_transformation/bareiss.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ else
3434
function Base.swapcols!(A::AbstractSparseMatrixCSC, i, j)
3535
i == j && return
3636

37-
# For simplicitly, let i denote the smaller of the two columns
37+
# For simplicity, let i denote the smaller of the two columns
3838
j < i && @swap(i, j)
3939

4040
colptr = getcolptr(A)
@@ -72,7 +72,7 @@ else
7272
return nothing
7373
end
7474
function swaprows!(A::AbstractSparseMatrixCSC, i, j)
75-
# For simplicitly, let i denote the smaller of the two rows
75+
# For simplicity, let i denote the smaller of the two rows
7676
j < i && @swap(i, j)
7777

7878
rows = rowvals(A)
@@ -184,7 +184,7 @@ const bareiss_virtcolswap = ((M, i, j) -> nothing, swaprows!,
184184
Perform Bareiss's fraction-free row-reduction algorithm on the matrix `M`.
185185
Optionally, a specific pivoting method may be specified.
186186
187-
swap_strategy is an optional argument that determines how the swapping of rows and coulmns is performed.
187+
swap_strategy is an optional argument that determines how the swapping of rows and columns is performed.
188188
bareiss_colswap (the default) swaps the columns and rows normally.
189189
bareiss_virtcolswap pretends to swap the columns which can be faster for sparse matrices.
190190
"""

src/structural_transformation/partial_state_selection.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, ja
209209
J = nothing
210210
else
211211
_J = jac(eqs, vars)
212-
# only accecpt small intergers to avoid overflow
212+
# only accept small integers to avoid overflow
213213
is_all_small_int = all(_J) do x′
214214
x = unwrap(x′)
215215
x isa Number || return false

src/structural_transformation/symbolics_tearing.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ function full_equations(sys::AbstractSystem; simplify = false)
103103
if rhs isa Symbolic
104104
return 0 ~ rhs
105105
else # a number
106-
error("tearing failled because the system is singular")
106+
error("tearing failed because the system is singular")
107107
end
108108
end
109109
eq
@@ -194,7 +194,7 @@ function to_mass_matrix_form(neweqs, ieq, graph, fullvars, isdervar::F,
194194
return (new_lhs ~ new_rhs), invview(var_to_diff)[dervar]
195195
else # a number
196196
if abs(rhs) > 100eps(float(rhs))
197-
@warn "The equation $eq is not consistent. It simplifed to 0 == $rhs."
197+
@warn "The equation $eq is not consistent. It simplified to 0 == $rhs."
198198
end
199199
return nothing
200200
end

src/systems/abstractsystem.jl

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using OrdinaryDiffEq
12
const SYSTEM_COUNT = Threads.Atomic{UInt}(0)
23

34
get_component_type(x::AbstractSystem) = get_gui_metadata(x).type
@@ -471,15 +472,18 @@ function namespace_defaults(sys)
471472
for (k, v) in pairs(defs))
472473
end
473474

474-
function namespace_equations(sys::AbstractSystem)
475+
function namespace_equations(sys::AbstractSystem, ivs = independent_variables(sys))
475476
eqs = equations(sys)
476477
isempty(eqs) && return Equation[]
477-
map(eq -> namespace_equation(eq, sys), eqs)
478+
map(eq -> namespace_equation(eq, sys; ivs), eqs)
478479
end
479480

480-
function namespace_equation(eq::Equation, sys, n = nameof(sys))
481-
_lhs = namespace_expr(eq.lhs, sys, n)
482-
_rhs = namespace_expr(eq.rhs, sys, n)
481+
function namespace_equation(eq::Equation,
482+
sys,
483+
n = nameof(sys);
484+
ivs = independent_variables(sys))
485+
_lhs = namespace_expr(eq.lhs, sys, n; ivs)
486+
_rhs = namespace_expr(eq.rhs, sys, n; ivs)
483487
_lhs ~ _rhs
484488
end
485489

@@ -489,26 +493,29 @@ function namespace_assignment(eq::Assignment, sys)
489493
Assignment(_lhs, _rhs)
490494
end
491495

492-
function namespace_expr(O, sys, n = nameof(sys))
493-
ivs = independent_variables(sys)
496+
function namespace_expr(O, sys, n = nameof(sys); ivs = independent_variables(sys))
494497
O = unwrap(O)
495498
if any(isequal(O), ivs)
496499
return O
497-
elseif isvariable(O)
498-
renamespace(n, O)
499500
elseif istree(O)
500501
T = typeof(O)
501-
if symtype(operation(O)) <: FnType
502-
renamespace(n, O)::T
502+
renamed = let sys = sys, n = n, T = T
503+
map(a -> namespace_expr(a, sys, n; ivs)::Any, arguments(O))
504+
end
505+
if isvariable(O)
506+
# Use renamespace so the scope is correct, and make sure to use the
507+
# metadata from the rescoped variable
508+
rescoped = renamespace(n, O)
509+
similarterm(O, operation(rescoped), renamed,
510+
metadata = metadata(rescoped))::T
503511
else
504-
renamed = let sys = sys, n = n, T = T
505-
map(a -> namespace_expr(a, sys, n)::Any, arguments(O))
506-
end
507-
similarterm(O, operation(O), renamed)::T
512+
similarterm(O, operation(O), renamed, metadata = metadata(O))::T
508513
end
514+
elseif isvariable(O)
515+
renamespace(n, O)
509516
elseif O isa Array
510517
let sys = sys, n = n
511-
map(o -> namespace_expr(o, sys, n), O)
518+
map(o -> namespace_expr(o, sys, n; ivs), O)
512519
end
513520
else
514521
O
@@ -656,20 +663,6 @@ function SymbolicIndexingInterface.is_param_sym(sys::AbstractSystem, sym)
656663
!isnothing(SymbolicIndexingInterface.param_sym_to_index(sys, sym))
657664
end
658665

659-
struct AbstractSysToExpr
660-
sys::AbstractSystem
661-
states::Vector
662-
end
663-
AbstractSysToExpr(sys) = AbstractSysToExpr(sys, states(sys))
664-
function (f::AbstractSysToExpr)(O)
665-
!istree(O) && return toexpr(O)
666-
any(isequal(O), f.states) && return nameof(operation(O)) # variables
667-
if issym(operation(O))
668-
return build_expr(:call, Any[nameof(operation(O)); f.(arguments(O))])
669-
end
670-
return build_expr(:call, Any[operation(O); f.(arguments(O))])
671-
end
672-
673666
###
674667
### System utils
675668
###
@@ -1249,7 +1242,7 @@ function io_preprocessing(sys::AbstractSystem, inputs,
12491242
end
12501243

12511244
"""
1252-
lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, kwargs...)
1245+
lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, initialize = true, kwargs...)
12531246
12541247
Return a function that linearizes the system `sys`. The function [`linearize`](@ref) provides a higher-level and easier to use interface.
12551248
@@ -1273,12 +1266,14 @@ The `simplified_sys` has undergone [`structural_simplify`](@ref) and had any occ
12731266
- `inputs`: A vector of variables that indicate the inputs of the linearized input-output model.
12741267
- `outputs`: A vector of variables that indicate the outputs of the linearized input-output model.
12751268
- `simplify`: Apply simplification in tearing.
1269+
- `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.
12761270
- `kwargs`: Are passed on to `find_solvables!`
12771271
12781272
See also [`linearize`](@ref) which provides a higher-level interface.
12791273
"""
12801274
function linearization_function(sys::AbstractSystem, inputs,
12811275
outputs; simplify = false,
1276+
initialize = true,
12821277
kwargs...)
12831278
sys, diff_idxs, alge_idxs, input_idxs = io_preprocessing(sys, inputs, outputs; simplify,
12841279
kwargs...)
@@ -1294,6 +1289,14 @@ function linearization_function(sys::AbstractSystem, inputs,
12941289
if u !== nothing # Handle systems without states
12951290
length(sts) == length(u) ||
12961291
error("Number of state variables ($(length(sts))) does not match the number of input states ($(length(u)))")
1292+
if initialize && !isempty(alge_idxs) # This is expensive and can be omitted if the user knows that the system is already initialized
1293+
residual = fun(u, p, t)
1294+
if norm(residual[alge_idxs]) > (eps(eltype(residual)))
1295+
prob = ODEProblem(fun, u, (t, t + 1), p)
1296+
integ = init(prob, Rodas5P())
1297+
u = integ.u
1298+
end
1299+
end
12971300
uf = SciMLBase.UJacobianWrapper(fun, t, p)
12981301
fg_xz = ForwardDiff.jacobian(uf, u)
12991302
h_xz = ForwardDiff.jacobian(let p = p, t = t
@@ -1397,7 +1400,7 @@ function linearize_symbolic(sys::AbstractSystem, inputs,
13971400
if !allow_input_derivatives
13981401
der_inds = findall(vec(any(!iszero, Bs, dims = 1)))
13991402
@show typeof(der_inds)
1400-
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.")
1403+
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.")
14011404
end
14021405
B = [B [zeros(nx, nu); Bs]]
14031406
D = [D zeros(ny, nu)]
@@ -1413,7 +1416,12 @@ function markio!(state, orig_inputs, inputs, outputs; check = true)
14131416
outputset = Dict{Any, Bool}(o => false for o in outputs)
14141417
for (i, v) in enumerate(fullvars)
14151418
if v in keys(inputset)
1416-
v = setio(v, true, false)
1419+
if v in keys(outputset)
1420+
v = setio(v, true, true)
1421+
outputset[v] = true
1422+
else
1423+
v = setio(v, true, false)
1424+
end
14171425
inputset[v] = true
14181426
fullvars[i] = v
14191427
elseif v in keys(outputset)
@@ -1442,8 +1450,8 @@ function markio!(state, orig_inputs, inputs, outputs; check = true)
14421450
end
14431451

14441452
"""
1445-
(; A, B, C, D), simplified_sys = linearize(sys, inputs, outputs; t=0.0, op = Dict(), allow_input_derivatives = false, kwargs...)
1446-
(; A, B, C, D) = linearize(simplified_sys, lin_fun; t=0.0, op = Dict(), allow_input_derivatives = false)
1453+
(; A, B, C, D), simplified_sys = linearize(sys, inputs, outputs; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false, kwargs...)
1454+
(; A, B, C, D) = linearize(simplified_sys, lin_fun; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false)
14471455
14481456
Return a NamedTuple with the matrices of a linear statespace representation
14491457
on the form
@@ -1463,6 +1471,8 @@ the default values of `sys` are used.
14631471
14641472
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̇]`.
14651473
1474+
`zero_dummy_der` can be set to automatically set the operating point to zero for all dummy derivatives.
1475+
14661476
See also [`linearization_function`](@ref) which provides a lower-level interface, [`linearize_symbolic`](@ref) and [`ModelingToolkit.reorder_states`](@ref).
14671477
14681478
See extended help for an example.
@@ -1576,7 +1586,7 @@ function linearize(sys, lin_fun; t = 0.0, op = Dict(), allow_input_derivatives =
15761586
if !iszero(Bs)
15771587
if !allow_input_derivatives
15781588
der_inds = findall(vec(any(!=(0), Bs, dims = 1)))
1579-
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.")
1589+
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.")
15801590
end
15811591
B = [B [zeros(nx, nu); Bs]]
15821592
D = [D zeros(ny, nu)]

0 commit comments

Comments
 (0)