Skip to content

Commit 4780f41

Browse files
fix: use defaults for operating point in linearization
1 parent c421522 commit 4780f41

File tree

3 files changed

+95
-17
lines changed

3 files changed

+95
-17
lines changed

src/systems/abstractsystem.jl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1896,6 +1896,7 @@ function linearization_function(sys::AbstractSystem, inputs,
18961896
zero_dummy_der = false,
18971897
initialization_solver_alg = TrustRegion(),
18981898
eval_expression = false, eval_module = @__MODULE__,
1899+
warn_initialize_determined = true,
18991900
kwargs...)
19001901
inputs isa AbstractVector || (inputs = [inputs])
19011902
outputs isa AbstractVector || (outputs = [outputs])
@@ -1909,10 +1910,26 @@ function linearization_function(sys::AbstractSystem, inputs,
19091910
op = merge(defs, op)
19101911
end
19111912
sys = ssys
1913+
u0map = Dict(k => v for (k, v) in op if is_variable(ssys, k))
19121914
initsys = structural_simplify(
19131915
generate_initializesystem(
1914-
sys, guesses = guesses(sys), algebraic_only = true),
1916+
sys, u0map = u0map, guesses = guesses(sys), algebraic_only = true),
19151917
fully_determined = false)
1918+
1919+
# HACK: some unknowns may not be involved in any initialization equations, and are
1920+
# thus removed from the system during `structural_simplify`.
1921+
# This causes `getu(initsys, unknowns(sys))` to fail, so we add them back as parameters
1922+
# for now.
1923+
missing_unknowns = setdiff(unknowns(sys), all_symbols(initsys))
1924+
if !isempty(missing_unknowns)
1925+
if warn_initialize_determined
1926+
@warn "Initialization system is underdetermined. No equations for $(missing_unknowns). Initialization will default to using least squares. To suppress this warning pass warn_initialize_determined = false."
1927+
end
1928+
new_parameters = [parameters(initsys); missing_unknowns]
1929+
@set! initsys.ps = new_parameters
1930+
initsys = complete(initsys)
1931+
end
1932+
19161933
if p isa SciMLBase.NullParameters
19171934
p = Dict()
19181935
else

src/systems/nonlinear/initializesystem.jl

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,25 +70,23 @@ function generate_initializesystem(sys::ODESystem;
7070
defs = merge(defaults(sys), filtered_u0)
7171
guesses = merge(get_guesses(sys), todict(guesses), dd_guess)
7272

73-
if !algebraic_only
74-
for st in full_states
75-
if st keys(defs)
76-
def = defs[st]
73+
for st in full_states
74+
if st keys(defs)
75+
def = defs[st]
7776

78-
if def isa Equation
79-
st keys(guesses) && check_defguess &&
80-
error("Invalid setup: unknown $(st) has an initial condition equation with no guess.")
81-
push!(eqs_ics, def)
82-
push!(u0, st => guesses[st])
83-
else
84-
push!(eqs_ics, st ~ def)
85-
push!(u0, st => def)
86-
end
87-
elseif st keys(guesses)
77+
if def isa Equation
78+
st keys(guesses) && check_defguess &&
79+
error("Invalid setup: unknown $(st) has an initial condition equation with no guess.")
80+
push!(eqs_ics, def)
8881
push!(u0, st => guesses[st])
89-
elseif check_defguess
90-
error("Invalid setup: unknown $(st) has no default value or initial guess")
82+
else
83+
push!(eqs_ics, st ~ def)
84+
push!(u0, st => def)
9185
end
86+
elseif st keys(guesses)
87+
push!(u0, st => guesses[st])
88+
elseif check_defguess
89+
error("Invalid setup: unknown $(st) has no default value or initial guess")
9290
end
9391
end
9492

test/downstream/linearize.jl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,66 @@ lsys, ssys = linearize(sat, [u], [y]; op = Dict(u => 2))
195195
@test isempty(lsys.B)
196196
@test isempty(lsys.C)
197197
@test lsys.D[] == 0
198+
199+
# Test case when unknowns in system do not have equations in initialization system
200+
using ModelingToolkit, OrdinaryDiffEq, LinearAlgebra
201+
using ModelingToolkitStandardLibrary.Mechanical.Rotational
202+
using ModelingToolkitStandardLibrary.Blocks: Add, Sine, PID, SecondOrder, Step, RealOutput
203+
using ModelingToolkit: connect
204+
205+
# Parameters
206+
m1 = 1
207+
m2 = 1
208+
k = 1000 # Spring stiffness
209+
c = 10 # Damping coefficient
210+
@named inertia1 = Inertia(; J = m1)
211+
@named inertia2 = Inertia(; J = m2)
212+
@named spring = Spring(; c = k)
213+
@named damper = Damper(; d = c)
214+
@named torque = Torque()
215+
216+
function SystemModel(u = nothing; name = :model)
217+
eqs = [connect(torque.flange, inertia1.flange_a)
218+
connect(inertia1.flange_b, spring.flange_a, damper.flange_a)
219+
connect(inertia2.flange_a, spring.flange_b, damper.flange_b)]
220+
if u !== nothing
221+
push!(eqs, connect(torque.tau, u.output))
222+
return ODESystem(eqs, t;
223+
systems = [
224+
torque,
225+
inertia1,
226+
inertia2,
227+
spring,
228+
damper,
229+
u
230+
],
231+
name)
232+
end
233+
ODESystem(eqs, t; systems = [torque, inertia1, inertia2, spring, damper], name)
234+
end
235+
236+
@named r = Step(start_time = 0)
237+
model = SystemModel()
238+
@named pid = PID(k = 100, Ti = 0.5, Td = 1)
239+
@named filt = SecondOrder(d = 0.9, w = 10)
240+
@named sensor = AngleSensor()
241+
@named er = Add(k2 = -1)
242+
243+
connections = [connect(r.output, :r, filt.input)
244+
connect(filt.output, er.input1)
245+
connect(pid.ctr_output, :u, model.torque.tau)
246+
connect(model.inertia2.flange_b, sensor.flange)
247+
connect(sensor.phi, :y, er.input2)
248+
connect(er.output, :e, pid.err_input)]
249+
250+
closed_loop = ODESystem(connections, t, systems = [model, pid, filt, sensor, r, er],
251+
name = :closed_loop, defaults = [
252+
model.inertia1.phi => 0.0,
253+
model.inertia2.phi => 0.0,
254+
model.inertia1.w => 0.0,
255+
model.inertia2.w => 0.0,
256+
filt.x => 0.0,
257+
filt.xd => 0.0
258+
])
259+
260+
@test_nowarn linearize(closed_loop, :r, :y)

0 commit comments

Comments
 (0)