- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 233
          [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" | ||
| 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) | ||
|  | @@ -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