|
| 1 | +# ModelingToolkit v11 Release Notes |
| 2 | + |
| 3 | +## Symbolics@7 and SymbolicUtils@4 compatibility |
| 4 | + |
| 5 | +SymbolicUtils version 4 involved a major overhaul of the core symbolic infrastructure, which |
| 6 | +propagated to Symbolics as Symbolics version 7. ModelingToolkit has now updated to these versions. |
| 7 | +This includes significant type-stability improvements, enabling precompilation of large parts |
| 8 | +of the symbolic infrastructure and faster TTFX. It is highly recommended to read the |
| 9 | +[Release Notes for SymbolicUtils@4](https://github.com/JuliaSymbolics/SymbolicUtils.jl/releases/tag/v4.0.0) |
| 10 | +and the [doc page](https://docs.sciml.ai/SymbolicUtils/dev/manual/variants/) describing the new |
| 11 | +variant structure before these release notes. |
| 12 | + |
| 13 | +As part of these changes, ModelingToolkit has changed how some data is represented to allow |
| 14 | +precompilation. Notably, `variable => value` mappings (such as guesses) are stored as an |
| 15 | +`AbstractDict{SymbolicT, SymbolicT}`. Here, `SymbolicT` is a type that comes from Symbolics.jl, |
| 16 | +and is the type for all unwrapped symbolic values. This means that any non-symbolic values |
| 17 | +are stored as `SymbolicUtils.Const` variants. Mutation such as `guesses(sys)[x] = 1.0` is still |
| 18 | +possible, and values are automatically converted. However, obtaining the value back requires |
| 19 | +usage of `SymbolicUtils.unwrap_const` or `Symbolics.value`. |
| 20 | + |
| 21 | +## Semantic separation of discretes |
| 22 | + |
| 23 | +ModelingToolkit has long overloaded the meaning of `@parameters` to the point that it means |
| 24 | +"anything that isn't `@variables`." This isn't a very intuitive or clear definition. This is |
| 25 | +now improved with the introduction of `@discretes`. Any quantities that vary on a different |
| 26 | +time-scale than those in `@variables` are now `@discretes`. `@parameters` can only be used to |
| 27 | +create "time-independent parameters". For clarity, the following continues to work: |
| 28 | + |
| 29 | +```julia |
| 30 | +@parameters p q[1:3] f(::Real, ::Real) |
| 31 | +``` |
| 32 | + |
| 33 | +However, this is now disallowed: |
| 34 | + |
| 35 | +```julia |
| 36 | +@parameters value(t) |
| 37 | +``` |
| 38 | + |
| 39 | +Instead, it must be declared as: |
| 40 | + |
| 41 | +```julia |
| 42 | +@discretes value(t) |
| 43 | +``` |
| 44 | + |
| 45 | +And can be passed along with the `@variables`. Essentially, for time-varying systems |
| 46 | +the constructor syntax is |
| 47 | + |
| 48 | +```julia |
| 49 | +System(equations, independent_variable, time_varying_variables, constant_values, [brownians]) |
| 50 | +``` |
| 51 | + |
| 52 | +In the subsequent release notes and in documentation, "variables" refers to either `@variables` |
| 53 | +or `@discretes` unless explicitly mentioned otherwise. |
| 54 | + |
| 55 | +An important note is that while this is a difference in declaration, the semantics are defined |
| 56 | +by their usage in the system. More concretely, a variable declared via `@discretes` is only |
| 57 | +actually considered discrete if it is part of the variables updated in a callback in the system. |
| 58 | +Otherwise, it is treated identically to a variable declared via `@variables`. |
| 59 | + |
| 60 | +## Changes to `defaults` and initialization semantics |
| 61 | + |
| 62 | +The concept of `defaults` is a relic of earlier ModelingToolkit versions, from when initialization |
| 63 | +did not exist and they served as convenient initial conditions. The package has evolved greatly since then |
| 64 | +and `defaults` have taken on many different meanings in different contexts. This makes their usage |
| 65 | +complicated and unintuitive. |
| 66 | + |
| 67 | +`defaults` have now been removed. They are replaced by two new concepts, with simple and well-defined |
| 68 | +semantics. Firstly, `initial_conditions` is a variable-value mapping aimed solely at being a convenient |
| 69 | +way to provide initial conditions to `SciMLProblem`s constructed from the system. Specifying them is |
| 70 | +identical to providing initial values to the `ODEProblem` constructor. Secondly, `bindings` is an |
| 71 | +immutable variable-value mapping representing strong constraints between variables/parameters. |
| 72 | +A binding for a variable is a function of other variables/parameters that is enforced during initialization. |
| 73 | +A binding for a parameter is a function of other parameters that exclusively defines the value of that |
| 74 | +parameter. Bound variables or parameters cannot be given initial conditions, either through the |
| 75 | +`initial_conditions` keyword or by passing them to the problem constructor. In effect, bindings |
| 76 | +serve to mark specific variables as aliases of others during initialization, and parameters as aliases |
| 77 | +of other parameters. This supersedes the previous concept of parameter bindings, and explicit parameter |
| 78 | +equations passed along with the equations of the model. Since bound parameters are computed as functions |
| 79 | +of other parameters, they are treated akin to observed variables. They are not stored in the parameter |
| 80 | +object, and instead are computed on the fly as required. |
| 81 | + |
| 82 | +Sometimes, it is useful to enforce a relation between parameters while allowing them to be given initial |
| 83 | +values. For example, one might relate the radius `r` and area `A` of a pipe as `A ~ pi * r * r`. Users of |
| 84 | +the model should be able to provide a value for either `r` or `A`, and the other should be calculated |
| 85 | +automatically. This is done by providing the relation `A ~ pi * r * r` to the `initialization_eqs` |
| 86 | +keyword of the model and binding both `A` and `r` to `missing`. Similar to v10, the equation represents |
| 87 | +a constraint to be enforced. The bindings act similar to the `missing` defaults in v9 and v10, indicating |
| 88 | +that the parameters are to be solved for. They are part of bindings since a parameter to be solved for |
| 89 | +cannot be an alias for a different value. As such, the choice of parameters that can be solved for is |
| 90 | +an immutable property of the system. Note that making a parameter solvable no longer requires specifying a |
| 91 | +guess. If a guess is required to solve the initialization, ModelingToolkit will error with an informative |
| 92 | +message during problem construction. Note that since parameters can only be bound to other parameters, |
| 93 | +a parameter `x0` can be bound to the initial value of a variable `x` using the binding `x0 = Initial(x)`. |
| 94 | + |
| 95 | +The formulation of the initialization system can now be summarized succinctly. The system solves for: |
| 96 | + |
| 97 | +- Unknowns of the system. |
| 98 | +- Observables (observed variables) of the system. |
| 99 | +- All unknowns for which derivatives are known (differential variables, and ones for which derivative |
| 100 | + information is available due to the index reduction process). |
| 101 | +- Discrete variables (created via `@discretes`). |
| 102 | +- Parameters with a binding of `missing`. |
| 103 | + |
| 104 | +It is composed of: |
| 105 | + |
| 106 | +- Algebraic equations. |
| 107 | +- Observed equations. |
| 108 | +- Initialization equations. |
| 109 | +- The `initial_conditions` of the system. |
| 110 | +- Initial conditions passed to the problem constructor. These override values in `initial_conditions` |
| 111 | + for the same variable. |
| 112 | + |
| 113 | +Additionally, `Initial` parameters exist for the following variables: |
| 114 | + |
| 115 | +- Unknowns |
| 116 | +- Observables |
| 117 | +- First derivatives of all unknowns and observables |
| 118 | +- Discrete variables |
| 119 | +- Parameters with a binding of `missing` |
| 120 | + |
| 121 | +"Defaults" specified via variable metadata are now translated into either `initial_conditions` or |
| 122 | +`bindings` depending on the value. If the value is a constant, it is part of `initial_conditions`. |
| 123 | +If it is an expression involving other variables/parameters, it is part of `bindings`. For example, |
| 124 | +the following are `initial_conditions`: |
| 125 | + |
| 126 | +```julia |
| 127 | +@variables x(t) = 1 y(t)[1:3] = zeros(3) |
| 128 | +@parameters f(::Real) = sin |
| 129 | +``` |
| 130 | + |
| 131 | +The following are bindings: |
| 132 | + |
| 133 | +```julia |
| 134 | +@variables z(t) = x w(t)[1:2] = [1.5, z] |
| 135 | +@parameters p[1:3] = f(3) |
| 136 | +``` |
| 137 | + |
| 138 | +Notably, arrays are considered atomic. This means that if even one element of an array default is |
| 139 | +symbolic, the entire array variable is considered bound. Partial bindings can be constructed by |
| 140 | +destructuring the array: |
| 141 | + |
| 142 | +```julia |
| 143 | +@parameters par[1:3] = [par1, par2, par3] |
| 144 | +``` |
| 145 | + |
| 146 | +Where `par1`, `par2` and `par3` can independently have initial conditions or bindings. In a |
| 147 | +similar vein, `guesses`, `initial_conditions` and `bindings` are all stored in special |
| 148 | +`AbstractDict` types that disallow scalarized keys. For example, `par[1]` cannot be a key |
| 149 | +of these dictionaries. `par` is allowed as a key. Initial values can still be given to the |
| 150 | +problem constructor in scalarized form. |
| 151 | + |
| 152 | +As mentioned previously, bindings cannot be mutated. To change the bindings of a system, |
| 153 | +the following pattern can be employed: |
| 154 | + |
| 155 | +```julia |
| 156 | +binds = bindings(sys) |
| 157 | +# the `ReadOnlyDict` wrapper uses `Base.parent` to get the underlying mutable container |
| 158 | +new_binds = parent(copy(binds)) |
| 159 | + |
| 160 | +# mutate `new_binds`... |
| 161 | + |
| 162 | +using Setfield: @set! |
| 163 | + |
| 164 | +@set! sys.bindings = new_binds |
| 165 | +sys = complete(sys) # Important! |
| 166 | +``` |
| 167 | + |
| 168 | +Mutation of bindings without copying them is undefined behavior and can lead to unpredictable bugs. |
| 169 | + |
| 170 | +## Array variables as inputs |
| 171 | + |
| 172 | +Previously, ModelingToolkit allowed part of an array variable to be an input. For example, the following |
| 173 | +used to be valid: |
| 174 | + |
| 175 | +```julia |
| 176 | +@variables u(t)[1:2] [input = true] |
| 177 | +@named sys = # Some system involving `u` |
| 178 | + |
| 179 | +sys = mtkcompile(sys; inputs = [u[1]]) |
| 180 | +``` |
| 181 | + |
| 182 | +This is now disallowed. `mtkcompile` will throw an informative error if part of an array is passed as an |
| 183 | +input. |
| 184 | + |
| 185 | +## Deprecation of `@mtkmodel` |
| 186 | + |
| 187 | +The `@mtkmodel` originated as a convenient DSL for creating models. However, it has not received the same |
| 188 | +level of support as other features due to the complexity of the parsing. It is also a major source of bugs, |
| 189 | +and is thus a tripping hazard for new and old users alike. The macro is now deprecated. It is moved to a new |
| 190 | +package, SciCompDSL.jl. Enough updates have been made to allow it to create systems in v11, but it will not |
| 191 | +receive more maintenance from the core developers. It is, however, still open to community contribution. For |
| 192 | +more details, please refer to the discussion in [this Discourse thread](https://discourse.julialang.org/t/using-mtk-when-i-import-modelingtoolkit/133681/12). |
| 193 | + |
| 194 | +## Splitting into `ModelingToolkitBase` and relicensing of parts of ModelingToolkit |
| 195 | + |
| 196 | +The advanced structural simplification algorithms in ModelingToolkit, such as index reduction, structural |
| 197 | +singularity removal and tearing, are now moved to the [StateSelection.jl](https://github.com/JuliaComputing/StateSelection.jl/) |
| 198 | +and ModelingToolkitTearing.jl (`lib/ModelingToolkitTearing` in the same repo) packages. These packages are |
| 199 | +AGPL licensed. ModelingToolkitBase.jl contains the `System` representation, callbacks, all of the code-generation |
| 200 | +targets (problem constructors), and initialization infrastructure. Items that depend on structural simplification |
| 201 | +and/or require highly specialized code generation (`SCCNonlinearProblem` prominent among them) have been moved |
| 202 | +to ModelingToolkit.jl, which depends on the aforementioned AGPL packages. ModelingToolkitBase still contains |
| 203 | +a simple version of `mtkcompile` suitable for most use cases. It does not perform index reduction and requires |
| 204 | +that all differential equations are explicit in the derivative. However, it does have a simpler tearing algorithm |
| 205 | +that is capable of identifying observed equations in many scenarios. In fact, it is also able to reduce many |
| 206 | +systems created using modular components and the `connect` infrastructure. Contributions to improve this |
| 207 | +are also welcome. |
| 208 | + |
| 209 | +For more information on the split and surrounding changes, please follow the discussion in |
| 210 | +[this Discourse thread](https://discourse.julialang.org/t/modelingtoolkit-v11-library-split-and-licensing-community-feedback-requested/134396). |
| 211 | + |
1 | 212 | # ModelingToolkit v10 Release Notes |
2 | 213 |
|
3 | 214 | ## Callbacks |
|
0 commit comments