-
-
Notifications
You must be signed in to change notification settings - Fork 217
[v10] refactor: change inputs/outputs handling in structural_simplify
#3573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v10
Are you sure you want to change the base?
Changes from all commits
93fee98
29d63da
efaa601
9244358
040db99
1a5b8e0
b89fa93
3350354
bc1f78f
082079b
9c8184c
3217e96
d31651c
353348f
364916c
00fe526
e385785
7406742
b54d8cc
1dbd817
821ce22
df6d7ad
a87ef5a
9308f05
1aa2fcb
58194fd
e2f28c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ on: | |
push: | ||
branches: | ||
- master | ||
- v10 | ||
tags: '*' | ||
pull_request: | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ on: | |
push: | ||
branches: | ||
- 'master' | ||
- v10 | ||
tags: '*' | ||
pull_request: | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ on: | |
branches: | ||
- master | ||
- 'release-' | ||
- v10 | ||
paths-ignore: | ||
- 'docs/**' | ||
push: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,7 +163,7 @@ has_var(ex, x) = x ∈ Set(get_variables(ex)) | |
(f_oop, f_ip), x_sym, p_sym, io_sys = generate_control_function( | ||
sys::AbstractODESystem, | ||
inputs = unbound_inputs(sys), | ||
disturbance_inputs = nothing; | ||
disturbance_inputs = disturbances(sys); | ||
implicit_dae = false, | ||
simplify = false, | ||
) | ||
|
@@ -179,9 +179,6 @@ The return values also include the chosen state-realization (the remaining unkno | |
|
||
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 (by default) 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 unknown variables for the disturbance model, but without disturbance inputs since the disturbances are not available for measurement. To add an input argument corresponding to the disturbance inputs, either include the disturbance inputs among the control inputs, or set `disturbance_argument=true`, in which case an additional input argument `w` is added to the generated function `(x,u,p,t,w)->rhs`. | ||
|
||
!!! note "Un-simplified system" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This warning is still warranted? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
This function expects `sys` to be un-simplified, i.e., `structural_simplify` or `@mtkbuild` should not be called on the system before passing it into this function. `generate_control_function` calls a special version of `structural_simplify` internally. | ||
|
||
# Example | ||
|
||
``` | ||
|
@@ -200,16 +197,18 @@ function generate_control_function(sys::AbstractODESystem, inputs = unbound_inpu | |
simplify = false, | ||
eval_expression = false, | ||
eval_module = @__MODULE__, | ||
check_simplified = true, | ||
kwargs...) | ||
# Remove this when the ControlFunction gets merged. | ||
if check_simplified && !iscomplete(sys) | ||
error("A completed `ODESystem` is required. Call `complete` or `structural_simplify` on the system before creating the control function.") | ||
end | ||
isempty(inputs) && @warn("No unbound inputs were found in system.") | ||
|
||
if disturbance_inputs !== nothing | ||
# add to inputs for the purposes of io processing | ||
inputs = [inputs; disturbance_inputs] | ||
end | ||
|
||
sys, _ = io_preprocessing(sys, inputs, []; simplify, kwargs...) | ||
|
||
dvs = unknowns(sys) | ||
ps = parameters(sys; initial_parameters = true) | ||
ps = setdiff(ps, inputs) | ||
|
@@ -218,7 +217,7 @@ function generate_control_function(sys::AbstractODESystem, inputs = unbound_inpu | |
inputs = setdiff(inputs, disturbance_inputs) | ||
# ps = [ps; disturbance_inputs] | ||
end | ||
inputs = map(x -> time_varying_as_func(value(x), sys), inputs) | ||
inputs = map(value, inputs) | ||
disturbance_inputs = unwrap.(disturbance_inputs) | ||
|
||
eqs = [eq for eq in full_equations(sys)] | ||
|
@@ -257,8 +256,11 @@ function generate_control_function(sys::AbstractODESystem, inputs = unbound_inpu | |
(; f, dvs, ps, io_sys = sys) | ||
end | ||
|
||
function inputs_to_parameters!(state::TransformationState, io) | ||
check_bound = io === nothing | ||
""" | ||
Turn input variables into parameters of the system. | ||
""" | ||
function inputs_to_parameters!(state::TransformationState, inputsyms) | ||
check_bound = inputsyms === nothing | ||
@unpack structure, fullvars, sys = state | ||
@unpack var_to_diff, graph, solvable_graph = structure | ||
@assert solvable_graph === nothing | ||
|
@@ -287,7 +289,7 @@ function inputs_to_parameters!(state::TransformationState, io) | |
push!(new_fullvars, v) | ||
end | ||
end | ||
ninputs == 0 && return (state, 1:0) | ||
ninputs == 0 && return state | ||
|
||
nvars = ndsts(graph) - ninputs | ||
new_graph = BipartiteGraph(nsrcs(graph), nvars, Val(false)) | ||
|
@@ -316,24 +318,11 @@ function inputs_to_parameters!(state::TransformationState, io) | |
@set! sys.unknowns = setdiff(unknowns(sys), keys(input_to_parameters)) | ||
ps = parameters(sys) | ||
|
||
if io !== nothing | ||
inputs, = io | ||
# Change order of new parameters to correspond to user-provided order in argument `inputs` | ||
d = Dict{Any, Int}() | ||
for (i, inp) in enumerate(new_parameters) | ||
d[inp] = i | ||
end | ||
permutation = [d[i] for i in inputs] | ||
new_parameters = new_parameters[permutation] | ||
end | ||
|
||
@set! sys.ps = [ps; new_parameters] | ||
|
||
@set! state.sys = sys | ||
@set! state.fullvars = new_fullvars | ||
@set! state.structure = structure | ||
base_params = length(ps) | ||
return state, (base_params + 1):(base_params + length(new_parameters)) # (1:length(new_parameters)) .+ base_params | ||
return state | ||
end | ||
|
||
""" | ||
|
@@ -359,7 +348,7 @@ function get_disturbance_system(dist::DisturbanceModel{<:ODESystem}) | |
end | ||
|
||
""" | ||
(f_oop, f_ip), augmented_sys, dvs, p = add_input_disturbance(sys, dist::DisturbanceModel, inputs = nothing) | ||
(f_oop, f_ip), augmented_sys, dvs, p = add_input_disturbance(sys, dist::DisturbanceModel, inputs = Any[]) | ||
|
||
Add a model of an unmeasured disturbance to `sys`. The disturbance model is an instance of [`DisturbanceModel`](@ref). | ||
|
||
|
@@ -408,13 +397,13 @@ model_outputs = [model.inertia1.w, model.inertia2.w, model.inertia1.phi, model.i | |
|
||
`f_oop` will have an extra state corresponding to the integrator in the disturbance model. This state will not be affected by any input, but will affect the dynamics from where it enters, in this case it will affect additively from `model.torque.tau.u`. | ||
""" | ||
function add_input_disturbance(sys, dist::DisturbanceModel, inputs = nothing; kwargs...) | ||
function add_input_disturbance(sys, dist::DisturbanceModel, inputs = Any[]; kwargs...) | ||
t = get_iv(sys) | ||
@variables d(t)=0 [disturbance = true] | ||
@variables u(t)=0 [input = true] # New system input | ||
dsys = get_disturbance_system(dist) | ||
|
||
if inputs === nothing | ||
if isempty(inputs) | ||
all_inputs = [u] | ||
else | ||
i = findfirst(isequal(dist.input), inputs) | ||
|
@@ -429,8 +418,9 @@ function add_input_disturbance(sys, dist::DisturbanceModel, inputs = nothing; kw | |
dist.input ~ u + dsys.output.u[1]] | ||
augmented_sys = ODESystem(eqs, t, systems = [dsys], name = gensym(:outer)) | ||
augmented_sys = extend(augmented_sys, sys) | ||
ssys = structural_simplify(augmented_sys, inputs = all_inputs, disturbance_inputs = [d]) | ||
|
||
(f_oop, f_ip), dvs, p, io_sys = generate_control_function(augmented_sys, all_inputs, | ||
(f_oop, f_ip), dvs, p, io_sys = generate_control_function(ssys, all_inputs, | ||
[d]; kwargs...) | ||
(f_oop, f_ip), augmented_sys, dvs, p, io_sys | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't there a discussion somewhere that
unbound_inputs
is buggy? Should we have a better alternative? CC @baggepinnenThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, the implementation of this function is not great, it's also very slow. It does the correct thing modulo bugs though. If the model has unbound inputs those have to be included in one of the input arguments for the generated function to work