diff --git a/docs/pages.jl b/docs/pages.jl index d772c32471..2af487adf8 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -12,7 +12,8 @@ pages = [ "tutorials/parameter_identifiability.md", "tutorials/bifurcation_diagram_computation.md", "tutorials/SampledData.md", - "tutorials/domain_connections.md"], + "tutorials/domain_connections.md", + "tutorials/callable_params.md"], "Examples" => Any[ "Basic Examples" => Any["examples/higher_order.md", "examples/spring_mass.md", diff --git a/docs/src/tutorials/callable_params.md b/docs/src/tutorials/callable_params.md new file mode 100644 index 0000000000..74e7ea87fa --- /dev/null +++ b/docs/src/tutorials/callable_params.md @@ -0,0 +1,91 @@ +# Callable parameters and interpolating data + +ModelingToolkit.jl allows creating parameters that represent functions to be called. This +is especially useful for including interpolants and/or lookup tables inside ODEs. In this +tutorial we will create an `ODESystem` which employs callable parameters to interpolate data +inside an ODE and go over the various syntax options and their implications. + +## Callable parameter syntax + +The syntax for callable parameters declared via `@parameters` must be one of the following + + 1. `(fn::FType)(..)` + 2. `fn(::argType1, ::argType2, ...)` + +In the first case, the parameter is callable with any number/combination of arguments, and +has a type of `FType` (the callable must be a subtype of `FType`). In the second case, +the parameter is callable with as many arguments as declared, and all must match the +declared types. + +By default, the return type of the callable symbolic is inferred to be `Real`. To change +this, a `::retType` annotation can be added at the end. + +To declare a function that returns an array of values, the same array syntax can be used +as for normal variables: + +```julia +@parameters (foo::FType)(..)[1:3]::retType +@parameters foo(::argType1, ::argType2)[1:3]::retType +``` + +`retType` here is the `eltype` of the returned array. + +## Storage of callable parameters + +Callable parameters declared with the `::FType` syntax will be stored in a `Vector{FType}`. +Thus, if `FType` is non-concrete, the buffer will also be non-concrete. This is sometimes +necessary to allow the value of the callable to be switched out for a different type without +rebuilding the model. Typically this syntax is preferable when `FType` is concrete or +a small union. + +Callable parameters declared with the `::argType1, ...` syntax will be stored in a +`Vector{FunctionWrappers.FunctionWrapper{retType, Tuple{argType1, ...}}}`. This suffers +the small overhead of a `FunctionWrapper` and restricts the signature of the callable, +symbolic, but allows storing the parameter in a type-stable manner and swapping it out. +This is preferable when the values that the callable can take do not share a common +subtype. For example, when a callable can represent the activation of a neural network +and can be `tanh`, `sigmoid`, etc. which have a common ancestor of `Function`. + +If both `::FType` and `::argType`s are specified, `::FType` takes priority. For example, + +```julia +@parameters (p::LinearInterpolation)(::Real) +``` + +`p` will be stored in a `Vector{LinearInterpolation}`. If `::LinearInterpolation` was not +specified, it would be stored in a `Vector{FunctionWrapper{Real, Tuple{Real}}}`. + +## Example using interpolations + +```@example callable +using ModelingToolkit +using OrdinaryDiffEq +using DataInterpolations +using ModelingToolkit: t_nounits as t, D_nounits as D + +ts = collect(0.0:0.1:10.0) +spline = LinearInterpolation(ts .^ 2, ts) +Tspline = typeof(spline) +@variables x(t) +@parameters (interp::Tspline)(..) + +@mtkbuild sys = ODESystem(D(x) ~ interp(t), t) +``` + +The derivative of `x` is obtained via an interpolation from DataInterpolations.jl. Note +the parameter syntax. The `(..)` marks the parameter as callable. `(interp::Tspline)` +indicates that the parameter is of type `Tspline`. + +```@example callable +prob = ODEProblem(sys, [x => 0.0], (0.0, 1.0), [interp => spline]) +solve(prob) +``` + +Note that the the following will not work: + +```julia +ODEProblem( + sys; [x => 0.0], (0.0, 1.0), [interp => LinearInterpolation(0.0:0.1:1.0, 0.0:0.1:1.0)]) +``` + +Since the type of the spline doesn't match.