Skip to content

Commit 8c730af

Browse files
authored
Merge pull request #2236 from SciML/mc/lin
Consistently initialize before computing the linearization
2 parents a69570a + 78b9f1a commit 8c730af

File tree

4 files changed

+29
-17
lines changed

4 files changed

+29
-17
lines changed

Project.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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"]

src/systems/abstractsystem.jl

Lines changed: 18 additions & 5 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
@@ -1249,7 +1250,7 @@ function io_preprocessing(sys::AbstractSystem, inputs,
12491250
end
12501251

12511252
"""
1252-
lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, kwargs...)
1253+
lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, initialize = true, kwargs...)
12531254
12541255
Return a function that linearizes the system `sys`. The function [`linearize`](@ref) provides a higher-level and easier to use interface.
12551256
@@ -1273,12 +1274,14 @@ The `simplified_sys` has undergone [`structural_simplify`](@ref) and had any occ
12731274
- `inputs`: A vector of variables that indicate the inputs of the linearized input-output model.
12741275
- `outputs`: A vector of variables that indicate the outputs of the linearized input-output model.
12751276
- `simplify`: Apply simplification in tearing.
1277+
- `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.
12761278
- `kwargs`: Are passed on to `find_solvables!`
12771279
12781280
See also [`linearize`](@ref) which provides a higher-level interface.
12791281
"""
12801282
function linearization_function(sys::AbstractSystem, inputs,
12811283
outputs; simplify = false,
1284+
initialize = true,
12821285
kwargs...)
12831286
sys, diff_idxs, alge_idxs, input_idxs = io_preprocessing(sys, inputs, outputs; simplify,
12841287
kwargs...)
@@ -1294,6 +1297,14 @@ function linearization_function(sys::AbstractSystem, inputs,
12941297
if u !== nothing # Handle systems without states
12951298
length(sts) == length(u) ||
12961299
error("Number of state variables ($(length(sts))) does not match the number of input states ($(length(u)))")
1300+
if initialize && !isempty(alge_idxs) # This is expensive and can be omitted if the user knows that the system is already initialized
1301+
residual = fun(u, p, t)
1302+
if norm(residual[alge_idxs]) > (eps(eltype(residual)))
1303+
prob = ODEProblem(fun, u, (t, t + 1), p)
1304+
integ = init(prob, Rodas5P())
1305+
u = integ.u
1306+
end
1307+
end
12971308
uf = SciMLBase.UJacobianWrapper(fun, t, p)
12981309
fg_xz = ForwardDiff.jacobian(uf, u)
12991310
h_xz = ForwardDiff.jacobian(let p = p, t = t
@@ -1397,7 +1408,7 @@ function linearize_symbolic(sys::AbstractSystem, inputs,
13971408
if !allow_input_derivatives
13981409
der_inds = findall(vec(any(!iszero, Bs, dims = 1)))
13991410
@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.")
1411+
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.")
14011412
end
14021413
B = [B [zeros(nx, nu); Bs]]
14031414
D = [D zeros(ny, nu)]
@@ -1442,8 +1453,8 @@ function markio!(state, orig_inputs, inputs, outputs; check = true)
14421453
end
14431454

14441455
"""
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)
1456+
(; A, B, C, D), simplified_sys = linearize(sys, inputs, outputs; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false, kwargs...)
1457+
(; A, B, C, D) = linearize(simplified_sys, lin_fun; t=0.0, op = Dict(), allow_input_derivatives = false, zero_dummy_der=false)
14471458
14481459
Return a NamedTuple with the matrices of a linear statespace representation
14491460
on the form
@@ -1463,6 +1474,8 @@ the default values of `sys` are used.
14631474
14641475
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̇]`.
14651476
1477+
`zero_dummy_der` can be set to automatically set the operating point to zero for all dummy derivatives.
1478+
14661479
See also [`linearization_function`](@ref) which provides a lower-level interface, [`linearize_symbolic`](@ref) and [`ModelingToolkit.reorder_states`](@ref).
14671480
14681481
See extended help for an example.
@@ -1576,7 +1589,7 @@ function linearize(sys, lin_fun; t = 0.0, op = Dict(), allow_input_derivatives =
15761589
if !iszero(Bs)
15771590
if !allow_input_derivatives
15781591
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.")
1592+
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.")
15801593
end
15811594
B = [B [zeros(nx, nu); Bs]]
15821595
D = [D zeros(ny, nu)]

src/systems/diffeqs/abstractodesystem.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,6 @@ function get_u0_p(sys,
687687
use_union = false,
688688
tofloat = !use_union,
689689
symbolic_u0 = false)
690-
eqs = equations(sys)
691690
dvs = states(sys)
692691
ps = parameters(sys)
693692

test/linearize.jl

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ if VERSION >= v"1.8"
187187
using ModelingToolkitStandardLibrary
188188
using ModelingToolkitStandardLibrary.Blocks
189189
using ModelingToolkitStandardLibrary.Mechanical.MultiBody2D
190-
using ModelingToolkitStandardLibrary.Mechanical.Translational
190+
using ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition
191191

192192
using ControlSystemsMTK
193193
using ControlSystemsMTK.ControlSystemsBase: sminreal, minreal, poles
@@ -197,22 +197,20 @@ if VERSION >= v"1.8"
197197
D = Differential(t)
198198

199199
@named link1 = Link(; m = 0.2, l = 10, I = 1, g = -9.807)
200-
@named cart = Translational.Mass(; m = 1, s = 0)
200+
@named cart = TranslationalPosition.Mass(; m = 1, s = 0)
201201
@named fixed = Fixed()
202-
@named force = Force()
202+
@named force = Force(use_support = false)
203203

204204
eqs = [connect(link1.TX1, cart.flange)
205205
connect(cart.flange, force.flange)
206206
connect(link1.TY1, fixed.flange)]
207207

208208
@named model = ODESystem(eqs, t, [], []; systems = [link1, cart, force, fixed])
209209
def = ModelingToolkit.defaults(model)
210-
for s in states(model)
211-
def[s] = 0
212-
end
213-
def[link1.x1] = 10
214-
def[link1.fy1] = -def[link1.g] * def[link1.m]
210+
def[cart.s] = 10
211+
def[cart.v] = 0
215212
def[link1.A] = -pi / 2
213+
def[link1.dA] = 0
216214
lin_outputs = [cart.s, cart.v, link1.A, link1.dA]
217215
lin_inputs = [force.f.u]
218216

@@ -235,11 +233,12 @@ if VERSION >= v"1.8"
235233

236234
dummyder = setdiff(states(sysss), states(model))
237235
def = merge(def, Dict(x => 0.0 for x in dummyder))
236+
def[link1.fy1] = -def[link1.g] * def[link1.m]
238237

239238
@test substitute(lsyss.A, def) lsys.A
240239
# We cannot pivot symbolically, so the part where a linear solve is required
241240
# is not reliable.
242-
@test substitute(lsyss.B, def)[1:6, :] lsys.B[1:6, :]
241+
@test substitute(lsyss.B, def)[1:6, 1] lsys.B[1:6, 1]
243242
@test substitute(lsyss.C, def) lsys.C
244243
@test substitute(lsyss.D, def) lsys.D
245244
end

0 commit comments

Comments
 (0)