What do people recommend for ModelingToolkit.jl with variable equations that change depending on some input “type” specification? By that I mean that I have some sort of physical process that has a couple of different ways it can be written with in equation depending on the assumptions I impose on my model. As far as I see, there are two ways to do it:
- Decide the equation using traditional multiple dispatch and the Symbolic.jl function registration. I.e., Have something like
abstract type AbstractLCL end
struct ExactLCL <: AbstractLCL end
struct ApproximateLCL <: AbstractLCL end
struct ConstantLCL <: AbstractLCL end
zb ~ lifting_condensation_level(z, s, q, p₀, LCLtype)
function lifting_condensation_level(z, s, q, p₀, LCLtype::ConstantLCL)
# actual math here. Three functions exist for each LCLtype
end
@register_symbolic lifting_condensation_level(z, s, q, p₀, LCLtype::ExactLCL)
@register_symbolic lifting_condensation_level(z, s, q, p₀, LCLtype::ApproximateLCL) false
@register_symbolic lifting_condensation_level(z, s, q, p₀, LCLtype::ConstantLCL) false
- Use a string and and if statement and define three functions, so I would have
if LCLtype == "ExactLCL"
zb ~ function_exact(a,b,c)
elseif LCLtype == "ApproxLCL"
zb ~ function_approximate(a,b,c)
# etc.
end
- Use a type like in 1. but make the type a “functor”. So that the type itself is a function. Having
struct ExactLCL <: AbstractLCL end
struct ApproximateLCL <: AbstractLCL end
I would define
function (e::ExactLCL)(z, s, q, p0)
# code
end
and write my equation as
lifting_condensation_level = ExactLCL()
zb ~ lifting_condensation_level(z, s, q, p₀)
What’s of these 3 is a best practice and why? My concerns are also with performance. The second version has no problems with performance because the equation is processed as-is. The first or third version may have type instability problems if the ExactLCL()
type is not “captured” appropriately.