Hi,
I’ve recently started a pull request in an attempt to add more generalized affect!
support to MTK (and also to support discrete_events
, which is taken on by others)
The reason I think this is needed is that in some instances, there’s a need to interface with a complex component that can’t be modeled easily by a set of differential equations. For example, a PV module could be controlled by a complex MPPT.
Anyway, I’m stuck on the right interface.
Currently, one can define an event as follows:
ODESystem(eqs, t; continuous_events = [ x ~ 0 ] => [v ~ -v])
Now, for a generalized affect!
we can do something like:
ODESystem(eqs, t; continuous_events = [ x ~ 0 ] => affect!)
where affect!
is a function. However, note that in this case the user doesn’t pass the variables/parameters of interest.
Therefore, with such an interface, MTK will have to expose all variables/parameters belonging to the ODESystem
(note that if the affect!
is defined as part of a sub-system, in principle, it could access all states and parameters of the entire system. This would break modularity, so let’s not go there).
For example, we could have:
function affect!(t, sts, pars)
sts.v = -sts.v
end
However, MTK aggressively prunes unnecessary equations and variables. It can deduce (although it currently doesn’t) what variables that appear in affect equations should be “protected” from pruning. However, for the opaque affect!
above, this doesn’t seem possible.
Another issue is that of “context”: to be useful, the affect!
function should have access to some context/state it can interact with (e.g. the MPPT
component).
The user could use closure to achieve this:
function make_affect(context)
let context = context
function (t,u,p)
update!(context, u)
p.C = new_c(context)
end
end
end
affect! = make_affect(MyContext())
However, this looks to me like an hack (and also a lot to expect from a library user).
So, an alternative interface could be for the user to pass not just the function, e.g.:
ODESystem(eqs, t; continuous_events = [ x ~ 0 ] => (affect!, context, (v = v, r = resistor.r), [C]))
This would allow MTK to pass the requested variables/parameters via kwargs
:
function affect!(t, ctx; v, r, C)
v = -C*v
update_context!(ctx, r)
end
The main drawback I see in this solution is it’s cumbersome, but it’s the best I got.
What do you think?
DD