Skip to content

Commit 2e6a489

Browse files
feat: [AI] allow passing an operating point to linearize using a solution
1 parent fcab2ba commit 2e6a489

3 files changed

Lines changed: 66 additions & 1 deletion

File tree

lib/ModelingToolkitBase/test/analysis_points.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,24 @@ if @isdefined(ModelingToolkit)
204204
@test matrices.D[] == 0
205205
end
206206

207+
@testset "LinearizationOpPoint" begin
208+
# sys here is the two-analysis-point system (plant_input + plant_output)
209+
# Simulate to obtain a solution, then verify that linearizing at t=0 via
210+
# LinearizationOpPoint gives the same result as the default operating point.
211+
ssys_solve = mtkcompile(sys)
212+
prob = ODEProblem(ssys_solve, [P.x => 0.0], (0.0, 1.0))
213+
sol = solve(prob, Rodas5())
214+
matrices_ref, _ = linearize(sys, sys.plant_input, sys.plant_output)
215+
matrices_op, _ = linearize(
216+
sys, sys.plant_input, sys.plant_output;
217+
op = ModelingToolkit.LinearizationOpPoint(sol, 0.0)
218+
)
219+
@test matrices_op.A matrices_ref.A
220+
@test matrices_op.B matrices_ref.B
221+
@test matrices_op.C matrices_ref.C
222+
@test matrices_op.D matrices_ref.D
223+
end
224+
207225
@testset "Complicated model" begin
208226
# Parameters
209227
m1 = 1

src/ModelingToolkit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ end
158158
export SemilinearODEFunction, SemilinearODEProblem
159159
export alias_elimination
160160
export linearize, linearization_function,
161-
LinearizationProblem, linearization_ap_transform
161+
LinearizationProblem, LinearizationOpPoint, linearization_ap_transform
162162
export solve
163163
export map_variables_to_equations, substitute_component
164164

src/linearization.jl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
1+
"""
2+
$(TYPEDEF)
3+
4+
Wraps an `ODESolution` and a time point `t`. When passed as `op` to [`linearize`](@ref),
5+
an operating point is constructed from the values of differential state variables and
6+
parameters of `sol` evaluated at time `t`. Algebraic variables are not set and will be
7+
determined by the initialization algorithm.
8+
9+
# Fields
10+
11+
$(TYPEDFIELDS)
12+
"""
13+
struct LinearizationOpPoint{S <: SciMLBase.AbstractODESolution, T}
14+
"""
15+
The solution to extract operating point values from.
16+
"""
17+
sol::S
18+
"""
19+
The time point at which to evaluate the solution.
20+
"""
21+
t::T
22+
end
23+
24+
function _build_op_from_solution(op::LinearizationOpPoint)
25+
sol_sys = MTKBase.indp_to_system(op.sol)
26+
eqs = equations(sol_sys)
27+
sts = unknowns(sol_sys)
28+
u = op.sol(op.t)
29+
result = Dict{SymbolicT, SymbolicT}()
30+
for (i, eq) in enumerate(eqs)
31+
isdiffeq(eq) || continue
32+
result[sts[i]] = u[i]
33+
end
34+
for p in parameters(sol_sys)
35+
result[p] = getp(op.sol, p)(op.sol)
36+
end
37+
return result
38+
end
39+
140
"""
241
lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, initialize = true, initialization_solver_alg = nothing, kwargs...)
342
@@ -770,6 +809,10 @@ function linearize(
770809
op = Dict(), allow_input_derivatives = false,
771810
p = DiffEqBase.NullParameters()
772811
)
812+
if op isa LinearizationOpPoint
813+
t = op.t
814+
op = _build_op_from_solution(op)
815+
end
773816
prob = LinearizationProblem(lin_fun, t)
774817
op = as_atomic_dict_with_defaults(Dict{SymbolicT, SymbolicT}(op), COMMON_NOTHING)
775818
evaluate_varmap!(op, keys(op))
@@ -797,6 +840,10 @@ function linearize(
797840
zero_dummy_der = false,
798841
kwargs...
799842
)
843+
if op isa LinearizationOpPoint
844+
t = op.t
845+
op = _build_op_from_solution(op)
846+
end
800847
lin_fun,
801848
ssys = linearization_function(
802849
sys,

0 commit comments

Comments
 (0)