diff --git a/docs/src/index.md b/docs/src/index.md index 82abb6a..44fe849 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -204,4 +204,5 @@ default_value has_variable new_derived_named_parameter @convert_to_parameters +LiteralParameter ``` diff --git a/src/ProcessBasedModelling.jl b/src/ProcessBasedModelling.jl index 009b153..2d54661 100644 --- a/src/ProcessBasedModelling.jl +++ b/src/ProcessBasedModelling.jl @@ -26,7 +26,7 @@ export Process, ParameterProcess, TimeDerivative, ExpRelaxation export processes_to_mtkmodel export new_derived_named_parameter export has_variable, default_value -export @convert_to_parameters +export @convert_to_parameters, LiteralParameter export lhs_variable, rhs, lhs end diff --git a/src/utils.jl b/src/utils.jl index 76676eb..7fb1357 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,3 +1,15 @@ +""" + LiteralParameter(p) + +A wrapper around a value `p` to indicate to +[`new_derived_named_parameter`](@ref) or [`@convert_to_parameters`](@ref) +to _not_ convert the given parameter `p` into a named `@parameters` instance, +but rather keep it as a numeric literal in the generated equations. +""" +struct LiteralParameter{P} + p::P +end + """ has_variable(eq, var) @@ -47,7 +59,9 @@ end """ new_derived_named_parameter(variable, value, extra::String, suffix = true) -If `value isa Num`, return `value`. Otherwise, create a new MTK `@parameter` +If `value isa Num` return `value`. +If `value isa `[`LiteralParameter`](@ref), replace it with its literal value. +Otherwise, create a new MTK `@parameter` whose name is created from `variable` by adding the `extra` string. If `suffix = true` the extra is added at the end after a `_`. Otherwise it is added at the start, then a `_` and then the variable name. @@ -60,7 +74,8 @@ p = new_derived_named_parameter(x, 0.5, "τ") ``` Now `p` will be a parameter with name `:τ_x` and default value `0.5`. """ -new_derived_named_parameter(v, value::Num, extra, suffix = true) = value +new_derived_named_parameter(v, value::Num, args...) = value +new_derived_named_parameter(v, value::LiteralParameter, args...) = value.p function new_derived_named_parameter(v, value::Real, extra, suffix = true) n = string(ModelingToolkit.getname(v)) newstring = if suffix @@ -84,6 +99,7 @@ end Convert all variables `vars` into `@parameters` with name the same as `vars` and default value the same as the value of `vars`. The macro leaves unaltered inputs that are of type `Num`, assumming they are already parameters. +It also replaces [`LiteralParameter`](@ref) inputs with its literal values. This macro is extremely useful to convert e.g., keyword arguments into named parameters, while also allowing the user to give custom parameter names. @@ -117,12 +133,13 @@ macro convert_to_parameters(vars...) varname = QuoteNode(var) push!(expr.args, :($binding = ifelse( + $binding isa LiteralParameter, $(binding).p, ifelse( # don't do anyting if this is already a Num $binding isa Num, $binding, # Else, convert to modeling toolkit param. # This syntax was obtained by doing @macroexpand @parameters A = 0.5 (ModelingToolkit.toparam)((Symbolics.wrap)((SymbolicUtils.setmetadata)((Symbolics.setdefaultval)((Sym){Real}($varname), $binding), Symbolics.VariableSource, (:parameters, $varname)))) - ) + )) ) ) end diff --git a/test/runtests.jl b/test/runtests.jl index 70b996f..52305af 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -146,4 +146,12 @@ end @test ModelingToolkit.getname(C) == :X # Test an untested clause: @test default_value(0.5) == 0.5 + + p = LiteralParameter(0.5) + p = new_derived_named_parameter(x, p, "t") + @test p == 0.5 + + p = LiteralParameter(0.5) + @convert_to_parameters p + @test p == 0.5 end