Skip to content

Commit 0e11e2e

Browse files
authored
Merge pull request #83 from JuliaControl/fbc/default-linearize-kwargs
Fix input-derivative pairing; default to inline linear-SCC reassembly
2 parents 8638748 + fa5e62e commit 0e11e2e

2 files changed

Lines changed: 40 additions & 11 deletions

File tree

src/ControlSystemsMTK.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ export build_quadratic_cost_matrix
3535

3636
export get_named_sensitivity, get_named_comp_sensitivity, get_named_looptransfer
3737

38+
"""
39+
Default keyword arguments forwarded to ModelingToolkit's linearization (and through it to
40+
`mtkcompile`) by every linearization-based function in this package (`named_ss`,
41+
`get_named_sensitivity` and friends, `batch_ss`, `trajectory_ss`,
42+
`build_quadratic_cost_matrix`). `inline_linear_sccs` makes tearing solve linear
43+
strongly-connected components inline (symbolically for size ≤ `analytical_linear_scc_limit`)
44+
rather than through numerical linear-solve calls embedded in the generated code. This is
45+
required for correct linearization of models with large linear SCCs (e.g. multibody
46+
mechanisms). Keyword arguments passed by the caller take precedence, so pass
47+
`reassemble_alg = ModelingToolkit.StructuralTransformations.DefaultReassembleAlgorithm()` to
48+
restore the ModelingToolkit default.
49+
"""
50+
const DEFAULT_LINEARIZE_KWARGS = (;
51+
reassemble_alg = ModelingToolkit.StructuralTransformations.DefaultReassembleAlgorithm(;
52+
inline_linear_sccs = true, analytical_linear_scc_limit = 1),
53+
)
54+
3855
include("ode_system.jl")
3956
# include("symbolic_optimization.jl")
4057

src/ode_system.jl

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,16 @@ function RobustAndOptimalControl.named_ss(
110110
out
111111
end
112112
end
113-
matrices, ssys, xpt = ModelingToolkit.linearize(sys, inputs, outputs; kwargs...)
113+
matrices, ssys, xpt = ModelingToolkit.linearize(sys, inputs, outputs; DEFAULT_LINEARIZE_KWARGS..., kwargs...)
114114
unames = symstr.(inputs)
115115
if nu > 0 && size(matrices.B, 2) == 2nu
116116
# This indicates that input derivatives are present
117-
duinds = findall(any(!iszero, eachcol(matrices.B[:, nu+1:end]))) .+ nu
118-
u2du = (1:nu) .=> duinds # This maps inputs to their derivatives
117+
# Derivative column nu + i is the derivative of input i (see the MTK docs for
118+
# `linearize` with `allow_input_derivatives = true`), so the pairing is positional.
119+
# All-zero derivative columns contribute nothing and are included for simplicity;
120+
# a pairing derived from the set of nonzero columns mispairs inputs when only a
121+
# subset of the derivative columns is nonzero.
122+
u2du = [i => i + nu for i in 1:nu] # This maps inputs to their derivatives
119123
lsys = causal_simplification(matrices, u2du; descriptor, simple_infeigs, big, balance)
120124
else
121125
lsys = ss(matrices...)
@@ -154,8 +158,12 @@ function RobustAndOptimalControl.named_ss(
154158
unames = symstr.(inputs)
155159
if nu > 0 && size(matrices.B, 2) == 2nu
156160
# This indicates that input derivatives are present
157-
duinds = findall(any(!iszero, eachcol(matrices.B[:, nu+1:end]))) .+ nu
158-
u2du = (1:nu) .=> duinds # This maps inputs to their derivatives
161+
# Derivative column nu + i is the derivative of input i (see the MTK docs for
162+
# `linearize` with `allow_input_derivatives = true`), so the pairing is positional.
163+
# All-zero derivative columns contribute nothing and are included for simplicity;
164+
# a pairing derived from the set of nonzero columns mispairs inputs when only a
165+
# subset of the derivative columns is nonzero.
166+
u2du = [i => i + nu for i in 1:nu] # This maps inputs to their derivatives
159167
lsys = causal_simplification(matrices, u2du; descriptor, simple_infeigs, big, balance, verbose=false)
160168
else
161169
lsys = ss(matrices...)
@@ -301,14 +309,18 @@ function named_sensitivity_function(
301309
end
302310
end
303311
nu = length(inputs)
304-
matrices, ssys = fun(sys, inputs, args...; kwargs...)
312+
matrices, ssys = fun(sys, inputs, args...; DEFAULT_LINEARIZE_KWARGS..., kwargs...)
305313
symstr(x) = Symbol(x isa AnalysisPoint ? x.name : string(x))
306314
unames = symstr.(inputs)
307315
fm(x) = convert(Matrix{Float64}, x)
308316
if nu > 0 && size(matrices.B, 2) == 2nu
309317
# This indicates that input derivatives are present
310-
duinds = findall(any(!iszero, eachcol(matrices.B[:, nu+1:end]))) .+ nu
311-
u2du = (1:nu) .=> duinds # This maps inputs to their derivatives
318+
# Derivative column nu + i is the derivative of input i (see the MTK docs for
319+
# `linearize` with `allow_input_derivatives = true`), so the pairing is positional.
320+
# All-zero derivative columns contribute nothing and are included for simplicity;
321+
# a pairing derived from the set of nonzero columns mispairs inputs when only a
322+
# subset of the derivative columns is nonzero.
323+
u2du = [i => i + nu for i in 1:nu] # This maps inputs to their derivatives
312324
lsys = causal_simplification(matrices, u2du; descriptor, simple_infeigs, big, balance)
313325
else
314326
lsys = ss(matrices...)
@@ -384,7 +396,7 @@ The second problem above, the ordering of the states, can be worked around using
384396
- `costs`: A vector of pairs.
385397
"""
386398
function build_quadratic_cost_matrix(sys::System, inputs::AbstractVector, costs::AbstractVector{<:Pair}; kwargs...)
387-
matrices, ssys, extras = ModelingToolkit.linearize(sys, inputs, first.(costs); kwargs...)
399+
matrices, ssys, extras = ModelingToolkit.linearize(sys, inputs, first.(costs); DEFAULT_LINEARIZE_KWARGS..., kwargs...)
388400
x = ModelingToolkit.unknowns(ssys)
389401
y = ModelingToolkit.outputs(ssys)
390402
nx = length(x)
@@ -406,7 +418,7 @@ end
406418
function batch_linearize(sys, inputs, outputs, ops::AbstractVector{<:AbstractDict}; t = 0.0,
407419
allow_input_derivatives = false,
408420
kwargs...)
409-
lin_fun, ssys = linearization_function(sys, inputs, outputs; op=ops[1], kwargs...)
421+
lin_fun, ssys = linearization_function(sys, inputs, outputs; op=ops[1], DEFAULT_LINEARIZE_KWARGS..., kwargs...)
410422
lins_ops = map(ops) do op
411423
linearize(ssys, lin_fun; op, t, allow_input_derivatives)
412424
end
@@ -552,7 +564,7 @@ function trajectory_ss(sys, inputs, outputs, sol; t = _max_100(sol.t), allow_inp
552564
ops = reduce(vcat, opsv)
553565
t = repeat(t, inner = length(ops) ÷ length(t))
554566
end
555-
lin_fun, ssys = linearization_function(sys, inputs, outputs; op=ops[1], kwargs...)#, initialization_abstol=1e-1, initialization_reltol=1e-1, kwargs...) # initializealg=ModelingToolkit.SciMLBase.NoInit()
567+
lin_fun, ssys = linearization_function(sys, inputs, outputs; op=ops[1], DEFAULT_LINEARIZE_KWARGS..., kwargs...)#, initialization_abstol=1e-1, initialization_reltol=1e-1, kwargs...) # initializealg=ModelingToolkit.SciMLBase.NoInit()
556568
# Main.lin_fun = lin_fun
557569
# Main.op1 = ops[1]
558570
# Main.ops = ops

0 commit comments

Comments
 (0)