158
158
159
159
has_var (ex, x) = x ∈ Set (get_variables (ex))
160
160
161
+ # Build control function
162
+
163
+ """
164
+ (f_oop, f_ip), dvs, p = generate_control_function(sys::AbstractODESystem, dvs = states(sys), ps = parameters(sys); implicit_dae = false, ddvs = if implicit_dae
165
+
166
+ For a system `sys` that has unbound inputs (as determined by [`unbound_inputs`](@ref)), generate a function with additional input argument `in`
167
+ ```
168
+ f_oop : (u,in,p,t) -> rhs
169
+ f_ip : (uout,u,in,p,t) -> nothing
170
+ ```
171
+ The return values also include the remaining states and parameters, in the order they appear as arguments to `f`.
172
+
173
+ # Example
174
+ ```
175
+ using ModelingToolkit: generate_control_function, varmap_to_vars, defaults
176
+ f, dvs, ps = generate_control_function(sys, expression=Val{false}, simplify=true)
177
+ p = varmap_to_vars(defaults(sys), ps)
178
+ x = varmap_to_vars(defaults(sys), dvs)
179
+ t = 0
180
+ f[1](x, inputs, p, t)
181
+ ```
182
+ """
183
+ function generate_control_function (
184
+ sys:: AbstractODESystem ;
185
+ implicit_dae= false ,
186
+ has_difference= false ,
187
+ simplify= true ,
188
+ kwargs...
189
+ )
190
+
191
+ ctrls = unbound_inputs (sys)
192
+ if isempty (ctrls)
193
+ error (" No unbound inputs were found in system." )
194
+ end
195
+
196
+ # One can either connect unbound inputs to new parameters and allow structural_simplify, but then the unbound inputs appear as states :( .
197
+ # One can also just remove them from the states and parameters for the purposes of code generation, but then structural_simplify fails :(
198
+ # To have the best of both worlds, all unbound inputs must be converted to `@parameters` in which case structural_simplify handles them correctly :)
199
+ sys = toparam (sys, ctrls)
200
+
201
+ if simplify
202
+ sys = structural_simplify (sys)
203
+ end
204
+
205
+ dvs = states (sys)
206
+ ps = parameters (sys)
207
+
208
+ dvs = setdiff (dvs, ctrls)
209
+ ps = setdiff (ps, ctrls)
210
+ inputs = map (x-> time_varying_as_func (value (x), sys), ctrls)
211
+
212
+ eqs = [eq for eq in equations (sys) if ! isdifferenceeq (eq)]
213
+ foreach (check_derivative_variables, eqs)
214
+ # substitute x(t) by just x
215
+ rhss = implicit_dae ? [_iszero (eq. lhs) ? eq. rhs : eq. rhs - eq. lhs for eq in eqs] :
216
+ [eq. rhs for eq in eqs]
217
+
218
+
219
+ # TODO : add an optional check on the ordering of observed equations
220
+ u = map (x-> time_varying_as_func (value (x), sys), dvs)
221
+ p = map (x-> time_varying_as_func (value (x), sys), ps)
222
+ t = get_iv (sys)
223
+
224
+ # pre = has_difference ? (ex -> ex) : get_postprocess_fbody(sys)
225
+
226
+ args = (u, inputs, p, t)
227
+ if implicit_dae
228
+ ddvs = map (Differential (get_iv (sys)), dvs)
229
+ args = (ddvs, args... )
230
+ end
231
+ pre, sol_states = get_substitutions_and_solved_states (sys)
232
+ f = build_function (rhss, args... ; postprocess_fbody= pre, states= sol_states, kwargs... )
233
+ f, dvs, ps
234
+ end
235
+
236
+ """
237
+ toparam(sys, ctrls::AbstractVector)
238
+
239
+ Transform all instances of `@varibales` in `ctrls` appearing as states and in equations of `sys` with similarly named `@parameters`. This allows [`structural_simplify`](@ref)(sys) in the presence unbound inputs.
240
+ """
241
+ function toparam (sys, ctrls:: AbstractVector )
242
+ eqs = equations (sys)
243
+ subs = Dict (ctrls .=> toparam .(ctrls))
244
+ eqs = map (eqs) do eq
245
+ substitute (eq. lhs, subs) ~ substitute (eq. rhs, subs)
246
+ end
247
+ ODESystem (eqs, name= sys. name)
248
+ end
0 commit comments