|
1 |
| -# Model Validation and Units |
| 1 | +# [Model Validation and Units](@id units) |
2 | 2 |
|
3 |
| -ModelingToolkit.jl provides extensive functionality for model validation |
4 |
| -and unit checking. This is done by providing metadata to the variable |
5 |
| -types and then running the validation functions which identify malformed |
6 |
| -systems and non-physical equations. |
| 3 | +ModelingToolkit.jl provides extensive functionality for model validation and unit checking. This is done by providing metadata to the variable types and then running the validation functions which identify malformed systems and non-physical equations. This approach provides high performance and compatibility with numerical solvers. |
7 | 4 |
|
8 |
| -## Consistency Checking |
| 5 | +## Assigning Units |
9 | 6 |
|
10 |
| -```@docs |
11 |
| -check_consistency |
| 7 | +Units may assigned with the following syntax. |
| 8 | + |
| 9 | +```julia |
| 10 | +using ModelingToolkit, Unitful |
| 11 | +@variables t [unit = u"s"] x(t) [unit = u"m"] g(t) w(t) [unit = "Hz"] |
| 12 | +#Or, |
| 13 | +@variables(t, [unit = u"s"], x(t), [unit = u"m"], g(t), w(t), [unit = "Hz"]) |
| 14 | +#Or, |
| 15 | +@variables(begin |
| 16 | +t, [unit = u"s"], |
| 17 | +x(t), [unit = u"m"], |
| 18 | +g(t), |
| 19 | +w(t), [unit = "Hz"] |
| 20 | +end) |
12 | 21 | ```
|
13 | 22 |
|
14 |
| -## Unit and Type Validation |
| 23 | +Do not use `quantities` such as `1u"s"` or `1/u"s"` or `u"1/s"` as these will result in errors; instead use `u"s"` or `u"s^1"`. |
| 24 | + |
| 25 | +## Unit Validation & Inspection |
| 26 | + |
| 27 | +Unit validation of equations happens automatically when creating a system. However, for debugging purposes one may wish to validate the equations directly using `validate`. |
15 | 28 |
|
16 | 29 | ```@docs
|
17 | 30 | ModelingToolkit.validate
|
18 | 31 | ```
|
| 32 | + |
| 33 | +Inside, `validate` uses `get_unit`, which may be directly applied to any term. Note that `validate` will not throw an error in the event of incompatible units, but `get_unit` will. If you would rather receive a warning instead of an error, use `safe_get_unit` which will yield `nothing` in the event of an error. Unit agreement is tested with `ModelingToolkit.equivalent(u1,u2)`. |
| 34 | + |
| 35 | + |
| 36 | +```@docs |
| 37 | +ModelingToolkit.get_unit |
| 38 | +``` |
| 39 | + |
| 40 | +Example usage below. Note that `ModelingToolkit` does not force unit conversions to preferred units in the event of nonstandard combinations -- it merely checks that the equations are consistent. |
| 41 | + |
| 42 | +```julia |
| 43 | +using ModelingToolkit, Unitful |
| 44 | +@parameters τ [unit = u"ms"] |
| 45 | +@variables t [unit = u"ms"] E(t) [unit = u"kJ"] P(t) [unit = u"MW"] |
| 46 | +D = Differential(t) |
| 47 | +eqs = eqs = [D(E) ~ P - E/τ, |
| 48 | + 0 ~ P ] |
| 49 | +ModelingToolkit.validate(eqs) #Returns true |
| 50 | +ModelingToolkit.validate(eqs[1]) #Returns true |
| 51 | +ModelingToolkit.get_unit(eqs[1].rhs) #Returns u"kJ ms^-1" |
| 52 | +``` |
| 53 | + |
| 54 | +An example of an inconsistent system: at present, `ModelingToolkit` requires that the units of all terms in an equation or sum to be equal-valued (`ModelingToolkit.equivalent(u1,u2)`), rather that simply dimensionally consistent. In the future, the validation stage may be upgraded to support the insertion of conversion factors into the equations. |
| 55 | + |
| 56 | +```julia |
| 57 | +using ModelingToolkit, Unitful |
| 58 | +@parameters τ [unit = u"ms"] |
| 59 | +@variables t [unit = u"ms"] E(t) [unit = u"J"] P(t) [unit = u"MW"] |
| 60 | +D = Differential(t) |
| 61 | +eqs = eqs = [D(E) ~ P - E/τ, |
| 62 | + 0 ~ P ] |
| 63 | +ModelingToolkit.validate(eqs) #Returns false while displaying a warning message |
| 64 | +``` |
| 65 | + |
| 66 | +## `Unitful` Literals & User-Defined Functions |
| 67 | + |
| 68 | +In order for a function to work correctly during both validation & execution, the function must be unit-agnostic. That is, no unitful literals may be used. Any unitful quantity must either be a `parameter` or `variable`. For example, these equations will not validate successfully. |
| 69 | + |
| 70 | +```julia |
| 71 | +using ModelingToolkit, Unitful |
| 72 | +@variables t [unit = u"ms"] E(t) [unit = u"J"] P(t) [unit = u"MW"] |
| 73 | +D = Differential(t) |
| 74 | +eqs = [D(E) ~ P - E/1u"ms" ] |
| 75 | +ModelingToolkit.validate(eqs) #Returns false while displaying a warning message |
| 76 | + |
| 77 | +myfunc(E) = E/1u"ms" |
| 78 | +eqs = [D(E) ~ P - myfunc(E) ] |
| 79 | +ModelingToolkit.validate(eqs) #Returns false while displaying a warning message |
| 80 | +``` |
| 81 | + |
| 82 | +Instead, they should be parameterized: |
| 83 | + |
| 84 | +```julia |
| 85 | +using ModelingToolkit, Unitful |
| 86 | +@parameters τ [unit = u"ms"] |
| 87 | +@variables t [unit = u"ms"] E(t) [unit = u"kJ"] P(t) [unit = u"MW"] |
| 88 | +D = Differential(t) |
| 89 | +eqs = [D(E) ~ P - E/τ] |
| 90 | +ModelingToolkit.validate(eqs) #Returns true |
| 91 | + |
| 92 | +myfunc(E,τ) = E/τ |
| 93 | +eqs = [D(E) ~ P - myfunc(E,τ)] |
| 94 | +ModelingToolkit.validate(eqs) #Returns true |
| 95 | +``` |
| 96 | + |
| 97 | +It is recommended *not* to circumvent unit validation by specializing user-defined functions on `Unitful` arguments vs. `Numbers`. This both fails to take advantage of `validate` for ensuring correctness, and may cause in errors in the |
| 98 | +future when `ModelingToolkit` is extended to support eliminating `Unitful` literals from functions. |
| 99 | + |
| 100 | +## Other Restrictions |
| 101 | + |
| 102 | +`Unitful` provides non-scalar units such as `dBm`, `°C`, etc. At this time, `ModelingToolkit` only supports scalar quantities. Additionally, angular degrees (`°`) are not supported because trigonometric functions will treat plain numerical values as radians, which would lead systems validated using degrees to behave erroneously when being solved. |
| 103 | + |
| 104 | +## Troubleshooting & Gotchas |
| 105 | + |
| 106 | +If a system fails to validate due to unit issues, at least one warning message will appear, including a line number as well as the unit types and expressions that were in conflict. Some system constructors re-order equations before the unit checking can be done, in which case the equation numbers may be inaccurate. The printed expression that the problem resides in is always correctly shown. |
| 107 | + |
| 108 | +Symbolic exponents for unitful variables *are* supported (ex: `P^γ` in thermodynamics). However, this means that `ModelingToolkit` cannot reduce such expressions to `Unitful.Unitlike` subtypes at validation time because the exponent value is not available. In this case `ModelingToolkit.get_unit` is type-unstable, yielding a symbolic result, which can still be checked for symbolic equality with `ModelingToolkit.equivalent`. |
| 109 | + |
| 110 | +## Parameter & Initial Condition Values |
| 111 | + |
| 112 | +Parameter and initial condition values are supplied to problem constructors as plain numbers, with the understanding that they have been converted to the appropriate units. This is done for simplicity of interfacing with optimization solvers. Some helper function for dealing with value maps: |
| 113 | + |
| 114 | +```julia |
| 115 | +remove_units(p::Dict) = Dict(k => Unitful.ustrip(ModelingToolkit.get_unit(k),v) for (k,v) in p) |
| 116 | +add_units(p::Dict) = Dict(k => v*ModelingToolkit.get_unit(k) for (k,v) in p) |
| 117 | +``` |
| 118 | + |
| 119 | +Recommended usage: |
| 120 | + |
| 121 | +```julia |
| 122 | +pars = @parameters τ [unit = u"ms"] |
| 123 | +p = Dict(τ => 1u"ms") |
| 124 | +ODEProblem(sys,remove_units(u0),tspan,remove_units(p)) |
| 125 | +``` |
0 commit comments