|  | 
|  | 1 | +# Callable parameters and interpolating data | 
|  | 2 | + | 
|  | 3 | +ModelingToolkit.jl allows creating parameters that represent functions to be called. This | 
|  | 4 | +is especially useful for including interpolants and/or lookup tables inside ODEs. In this | 
|  | 5 | +tutorial we will create an `ODESystem` which employs callable parameters to interpolate data | 
|  | 6 | +inside an ODE and go over the various syntax options and their implications. | 
|  | 7 | + | 
|  | 8 | +## Callable parameter syntax | 
|  | 9 | + | 
|  | 10 | +The syntax for callable parameters declared via `@parameters` must be one of the following | 
|  | 11 | + | 
|  | 12 | + 1. `(fn::FType)(..)` | 
|  | 13 | + 2. `fn(::argType1, ::argType2, ...)` | 
|  | 14 | + | 
|  | 15 | +In the first case, the parameter is callable with any number/combination of arguments, and | 
|  | 16 | +has a type of `FType` (the callable must be a subtype of `FType`). In the second case, | 
|  | 17 | +the parameter is callable with as many arguments as declared, and all must match the | 
|  | 18 | +declared types. | 
|  | 19 | + | 
|  | 20 | +By default, the return type of the callable symbolic is inferred to be `Real`. To change | 
|  | 21 | +this, a `::retType` annotation can be added at the end. | 
|  | 22 | + | 
|  | 23 | +To declare a function that returns an array of values, the same array syntax can be used | 
|  | 24 | +as for normal variables: | 
|  | 25 | + | 
|  | 26 | +```julia | 
|  | 27 | +@parameters (foo::FType)(..)[1:3]::retType | 
|  | 28 | +@parameters foo(::argType1, ::argType2)[1:3]::retType | 
|  | 29 | +``` | 
|  | 30 | + | 
|  | 31 | +`retType` here is the `eltype` of the returned array. | 
|  | 32 | + | 
|  | 33 | +## Storage of callable parameters | 
|  | 34 | + | 
|  | 35 | +Callable parameters declared with the `::FType` syntax will be stored in a `Vector{FType}`. | 
|  | 36 | +Thus, if `FType` is non-concrete, the buffer will also be non-concrete. This is sometimes | 
|  | 37 | +necessary to allow the value of the callable to be switched out for a different type without | 
|  | 38 | +rebuilding the model. Typically this syntax is preferable when `FType` is concrete or | 
|  | 39 | +a small union. | 
|  | 40 | + | 
|  | 41 | +Callable parameters declared with the `::argType1, ...` syntax will be stored in a | 
|  | 42 | +`Vector{FunctionWrappers.FunctionWrapper{retType, Tuple{argType1, ...}}}`. This suffers | 
|  | 43 | +the small overhead of a `FunctionWrapper` and restricts the signature of the callable, | 
|  | 44 | +symbolic, but allows storing the parameter in a type-stable manner and swapping it out. | 
|  | 45 | +This is preferable when the values that the callable can take do not share a common | 
|  | 46 | +subtype. For example, when a callable can represent the activation of a neural network | 
|  | 47 | +and can be `tanh`, `sigmoid`, etc. which have a common ancestor of `Function`. | 
|  | 48 | + | 
|  | 49 | +If both `::FType` and `::argType`s are specified, `::FType` takes priority. For example, | 
|  | 50 | + | 
|  | 51 | +```julia | 
|  | 52 | +@parameters (p::LinearInterpolation)(::Real) | 
|  | 53 | +``` | 
|  | 54 | + | 
|  | 55 | +`p` will be stored in a `Vector{LinearInterpolation}`. If `::LinearInterpolation` was not | 
|  | 56 | +specified, it would be stored in a `Vector{FunctionWrapper{Real, Tuple{Real}}}`. | 
|  | 57 | + | 
|  | 58 | +## Example using interpolations | 
|  | 59 | + | 
|  | 60 | +```@example callable | 
|  | 61 | +using ModelingToolkit | 
|  | 62 | +using OrdinaryDiffEq | 
|  | 63 | +using DataInterpolations | 
|  | 64 | +using ModelingToolkit: t_nounits as t, D_nounits as D | 
|  | 65 | +
 | 
|  | 66 | +ts = collect(0.0:0.1:10.0) | 
|  | 67 | +spline = LinearInterpolation(ts .^ 2, ts) | 
|  | 68 | +Tspline = typeof(spline) | 
|  | 69 | +@variables x(t) | 
|  | 70 | +@parameters (interp::Tspline)(..) | 
|  | 71 | +
 | 
|  | 72 | +@mtkbuild sys = ODESystem(D(x) ~ interp(t), t) | 
|  | 73 | +``` | 
|  | 74 | + | 
|  | 75 | +The derivative of `x` is obtained via an interpolation from DataInterpolations.jl. Note | 
|  | 76 | +the parameter syntax. The `(..)` marks the parameter as callable. `(interp::Tspline)` | 
|  | 77 | +indicates that the parameter is of type `Tspline`. | 
|  | 78 | + | 
|  | 79 | +```@example callable | 
|  | 80 | +prob = ODEProblem(sys, [x => 0.0], (0.0, 1.0), [interp => spline]) | 
|  | 81 | +solve(prob) | 
|  | 82 | +``` | 
|  | 83 | + | 
|  | 84 | +Note that the the following will not work: | 
|  | 85 | + | 
|  | 86 | +```julia | 
|  | 87 | +ODEProblem( | 
|  | 88 | +    sys; [x => 0.0], (0.0, 1.0), [interp => LinearInterpolation(0.0:0.1:1.0, 0.0:0.1:1.0)]) | 
|  | 89 | +``` | 
|  | 90 | + | 
|  | 91 | +Since the type of the spline doesn't match. | 
0 commit comments