-
-
Notifications
You must be signed in to change notification settings - Fork 236
[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
Changes from all commits
da60fbf
9b676bd
0f6d9ba
fe0f93a
7fb4e5a
d45a4b7
8548467
80c19dc
e477433
8546980
2a68806
bd73108
d49d3e9
513b312
b5ee07e
5f61205
15a6236
7a234f1
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 |
|---|---|---|
|
|
@@ -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" | ||
|
Contributor
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?
Member
Author
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) | ||
|
|
@@ -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_inputsis 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