diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index e87960a661..8a688af7aa 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -11,7 +11,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@latest with: version: '1' diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index b8720a6d73..519532d82b 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -31,14 +31,14 @@ jobs: - {user: SciML, repo: MethodOfLines.jl, group: DAE} - {user: ai4energy, repo: Ai4EComponentLib.jl, group: Downstream} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.julia-version }} arch: x64 - uses: julia-actions/julia-buildpkg@latest - name: Clone Downstream - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ matrix.package.user }}/${{ matrix.package.repo }} path: downstream diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index b4ab23fd41..55af13cc40 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -21,7 +21,7 @@ jobs: with: version: ${{ matrix.julia-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install JuliaFormatter and format # This will use the latest version by default but you can set the version like so: # diff --git a/.github/workflows/Invalidations.yml b/.github/workflows/Invalidations.yml index 4d0004e831..28b9ce2fad 100644 --- a/.github/workflows/Invalidations.yml +++ b/.github/workflows/Invalidations.yml @@ -19,12 +19,12 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: '1' - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-invalidations@v1 id: invs_pr - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.repository.default_branch }} - uses: julia-actions/julia-buildpkg@v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 262e88f4b0..65ff03ee1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - '1' - '1.6' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} diff --git a/Project.toml b/Project.toml index 1ad042c18f..5d2b81ae99 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkit" uuid = "961ee093-0014-501f-94e3-6117800e7a78" authors = ["Yingbo Ma ", "Chris Rackauckas and contributors"] -version = "8.67.0" +version = "8.68.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -32,6 +32,7 @@ MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" @@ -80,6 +81,7 @@ MLStyle = "0.4.17" MacroTools = "0.5" NaNMath = "0.3, 1" OrdinaryDiffEq = "6" +PrecompileTools = "1" RecursiveArrayTools = "2.3" Reexport = "0.2, 1" RuntimeGeneratedFunctions = "0.5.9" diff --git a/docs/src/basics/MTKModel_Connector.md b/docs/src/basics/MTKModel_Connector.md new file mode 100644 index 0000000000..7fdcbf4db9 --- /dev/null +++ b/docs/src/basics/MTKModel_Connector.md @@ -0,0 +1,156 @@ +## Defining components with `@mtkmodel` + +`@mtkmodel` is a convenience macro to define ModelingToolkit components. It returns `ModelingToolkit.Model`, which includes a constructor that returns an ODESystem, a `structure` dictionary with metadata and flag `isconnector` which is set to `false`. + +### What can an MTK-Model definition have? + +`@mtkmodel` definition contains begin blocks of + + - `@components`: for listing sub-components of the system + - `@equations`: for the list of equations + - `@extend`: for extending a base system and unpacking its states + - `@parameters`: for specifying the symbolic parameters + - `@structural_parameters`: for specifying non-symbolic parameters + - `@variables`: for specifing the states + +Let's explore these in more detail with the following example: + +```@example mtkmodel-example +using ModelingToolkit + +@mtkmodel ModelA begin + @parameters begin + k1 + k2 + end +end + +@mtkmodel ModelB begin + @parameters begin + p1 = 1.0, [description = "Parameter of ModelB"] + p2 = 1.0, [description = "Parameter of ModelB"] + end +end + +@mtkmodel ModelC begin + @structural_parameters begin + f = sin + end + begin + v_var = 1.0 + end + @variables begin + v(t) = v_var + end + @extend p1, p2 = model_b = ModelB(; p1) + @components begin + model_a = ModelA(; k1) + end + @equations begin + model_a.k1 ~ f(v) + end +end +``` + +#### `@parameters` and `@variables` begin block + + - Parameters and variables are declared with respective begin blocks. + - Variables must be functions of an independent variable. + - Optionally, default values and metadata can be specified for these parameters and variables. See `ModelB` in the above example. + - Along with creating parameters and variables, keyword arguments of same name with default value `nothing` are created. + - Whenever a parameter or variable has default value, for example `v(t) = 0.0`, a symbolic variable named `v` with default value 0.0 and a keyword argument `v`, with default value `nothing` are created.
This way, users can optionally pass new value of `v` while creating a component. + +```julia +julia > @named model_c = ModelC(; v = 2.0); + +julia > ModelingToolkit.getdefault(model_c.v) +2.0 +``` + +#### `@structural_parameters` begin block + + - This block is for non symbolic input arguements. These are for inputs that usually are not meant to be part of components; but influence how they are defined. One can list inputs like boolean flags, functions etc... here. + - Whenever default values are specified, unlike parameters/variables, they are reflected in the keyword argument list. + +#### `@extend` block + +To extend a partial system, + + - List the variables to unpack. If there is a single variable, explicitly specify it as a tuple. + - Give a name to the base system + - List the kwargs of the base system that should be listed as kwargs of the main component. + - Note that in above example, `p1` is promoted as an argument of `ModelC`. Users can set the value of `p1` as + +```julia +julia> @named model_c = ModelC(; p1 = 2.0) + +``` + +However, as `p2` isn't listed in the model definition, its default can't be modified by users. + +#### `@components` begin block + + - Declare the subcomponents within `@components` begin block. + - The arguments in these subcomponents are promoted as keyword arguments as `subcomponent_name__argname` with `nothing` as default value. + - Whenever components are created with `@named` macro, these can be accessed with `.` operator as `subcomponent_name.argname` + - In the above example, `k1` of `model_a` can be set in following ways: + +```julia +julia> @named model_c1 = ModelC(; model_a.k1 = 1); +# or as + +julia> model_c2 = ModelC(; name = :model_c, model_a__k1 = 1); + +``` + +And as `k2` isn't listed in the sub-component definition of `ModelC`, its default value can't be modified by users. + +#### `@equations` begin block + + - List all the equations here + +#### A begin block + + - Any other Julia operations can be included with dedicated begin blocks. + +## Defining connectors with `@connector` + +`@connector` returns `ModelingToolkit.Model`. It includes a constructor that returns a connector ODESystem, a `structure` dictionary with metadata and flag `isconnector` which is set to `true`. + +A simple connector can be defined with syntax similar to following example: + +```julia +using ModelingToolkit + +@connector Pin begin + v(t) = 0.0, [description = "Voltage"] + i(t), [connect = Flow] +end +``` + + - Variables (as function of independent variable) are listed out in the definition. These variables can optionally have default values and metadata like `descrption`, `connect` and so on. + +`@connector`s accepts begin blocks of `@components`, `@equations`, `@extend`, `@parameters`, `@structural_parameters`, `@variables`. These keywords mean the same as described above for `@mtkmodel`. + +!!! note + + For more examples of usage, checkout [ModelingToolkitStandardLibrary.jl](https://github.com/SciML/ModelingToolkitStandardLibrary.jl/) + +* * * + +### What's a `structure` dictionary? + +For components defined with `@mtkmodel` or `@connector`, a dictionary with metadata is created. It lists `:components` (sub-component list), `:extend` (the extended states and base system), `:parameters`, `:variables`, ``:kwargs`` (list of keyword arguments), `:independent_variable`, `:equations`. + +For example, the structure of `ModelC` is: + +```julia +julia> ModelC.structure +Dict{Symbol, Any} with 6 entries: + :components => [[:model_a, :ModelA]] + :variables => Dict{Symbol, Dict{Symbol, Any}}(:v=>Dict(:default=>:v_var)) + :kwargs => Dict{Symbol, Any}(:f=>:sin, :v=>:v_var, :p1=>nothing, :model_a__k1=>nothing) + :independent_variable => t + :extend => Any[[:p1, :p2], :model_b, :ModelB] + :equations => ["model_a.k1 ~ f(v)"] +``` diff --git a/src/ModelingToolkit.jl b/src/ModelingToolkit.jl index 2157c4093d..89dee632f4 100644 --- a/src/ModelingToolkit.jl +++ b/src/ModelingToolkit.jl @@ -2,75 +2,79 @@ $(DocStringExtensions.README) """ module ModelingToolkit -using DocStringExtensions -using Compat -using AbstractTrees -using DiffEqBase, SciMLBase, ForwardDiff, Reexport -using SciMLBase: StandardODEProblem, StandardNonlinearProblem, handle_varmap -using Distributed -using StaticArrays, LinearAlgebra, SparseArrays, LabelledArrays -using InteractiveUtils -using Latexify, Unitful, ArrayInterface -using MacroTools -@reexport using UnPack -using Setfield, ConstructionBase -using JumpProcesses -using DataStructures -using SpecialFunctions, NaNMath -using RuntimeGeneratedFunctions -using RuntimeGeneratedFunctions: drop_expr -using Base.Threads -using DiffEqCallbacks -using Graphs -import MacroTools: splitdef, combinedef, postwalk, striplines -import Libdl -using DocStringExtensions -using Base: RefValue -using Combinatorics -import IfElse -import Distributions -import FunctionWrappersWrappers -using URIs: URI - +using PrecompileTools, Reexport +@recompile_invalidations begin + using DocStringExtensions + using Compat + using AbstractTrees + using DiffEqBase, SciMLBase, ForwardDiff + using SciMLBase: StandardODEProblem, StandardNonlinearProblem, handle_varmap + using Distributed + using StaticArrays, LinearAlgebra, SparseArrays, LabelledArrays + using InteractiveUtils + using Latexify, Unitful, ArrayInterface + using MacroTools + using Setfield, ConstructionBase + using JumpProcesses + using DataStructures + using SpecialFunctions, NaNMath + using RuntimeGeneratedFunctions + using RuntimeGeneratedFunctions: drop_expr + using Base.Threads + using DiffEqCallbacks + using Graphs + import MacroTools: splitdef, combinedef, postwalk, striplines + import Libdl + using DocStringExtensions + using Base: RefValue + using Combinatorics + import IfElse + import Distributions + import FunctionWrappersWrappers + using URIs: URI + + using RecursiveArrayTools + + import SymbolicIndexingInterface + import SymbolicIndexingInterface: independent_variables, states, parameters + export independent_variables, states, parameters + import SymbolicUtils + import SymbolicUtils: istree, arguments, operation, similarterm, promote_symtype, + Symbolic, isadd, ismul, ispow, issym, FnType, + @rule, Rewriters, substitute, metadata, BasicSymbolic, + Sym, Term + using SymbolicUtils.Code + import SymbolicUtils.Code: toexpr + import SymbolicUtils.Rewriters: Chain, Postwalk, Prewalk, Fixpoint + import JuliaFormatter + + using MLStyle + + using Reexport + using Symbolics: degree + @reexport using Symbolics + using Symbolics: _parse_vars, value, @derivatives, get_variables, + exprs_occur_in, solve_for, build_expr, unwrap, wrap, + VariableSource, getname, variable, Connection, connect, + NAMESPACE_SEPARATOR + import Symbolics: rename, get_variables!, _solve, hessian_sparsity, + jacobian_sparsity, isaffine, islinear, _iszero, _isone, + tosymbol, lower_varname, diff2term, var_from_nested_derivative, + BuildTargets, JuliaTarget, StanTarget, CTarget, MATLABTarget, + ParallelForm, SerialForm, MultithreadedForm, build_function, + rhss, lhss, prettify_expr, gradient, + jacobian, hessian, derivative, sparsejacobian, sparsehessian, + substituter, scalarize, getparent + + import DiffEqBase: @add_kwonly + + import Graphs: SimpleDiGraph, add_edge!, incidence_matrix + + @reexport using UnPack +end RuntimeGeneratedFunctions.init(@__MODULE__) -using RecursiveArrayTools - -import SymbolicIndexingInterface -import SymbolicIndexingInterface: independent_variables, states, parameters -export independent_variables, states, parameters -import SymbolicUtils -import SymbolicUtils: istree, arguments, operation, similarterm, promote_symtype, - Symbolic, isadd, ismul, ispow, issym, FnType, - @rule, Rewriters, substitute, metadata, BasicSymbolic, - Sym, Term -using SymbolicUtils.Code -import SymbolicUtils.Code: toexpr -import SymbolicUtils.Rewriters: Chain, Postwalk, Prewalk, Fixpoint -import JuliaFormatter - -using MLStyle - -using Reexport -using Symbolics: degree -@reexport using Symbolics export @derivatives -using Symbolics: _parse_vars, value, @derivatives, get_variables, - exprs_occur_in, solve_for, build_expr, unwrap, wrap, - VariableSource, getname, variable, Connection, connect, - NAMESPACE_SEPARATOR -import Symbolics: rename, get_variables!, _solve, hessian_sparsity, - jacobian_sparsity, isaffine, islinear, _iszero, _isone, - tosymbol, lower_varname, diff2term, var_from_nested_derivative, - BuildTargets, JuliaTarget, StanTarget, CTarget, MATLABTarget, - ParallelForm, SerialForm, MultithreadedForm, build_function, - rhss, lhss, prettify_expr, gradient, - jacobian, hessian, derivative, sparsejacobian, sparsehessian, - substituter, scalarize, getparent - -import DiffEqBase: @add_kwonly - -import Graphs: SimpleDiGraph, add_edge!, incidence_matrix for fun in [:toexpr] @eval begin diff --git a/src/structural_transformation/partial_state_selection.jl b/src/structural_transformation/partial_state_selection.jl index f2fec0269f..bae8816b50 100644 --- a/src/structural_transformation/partial_state_selection.jl +++ b/src/structural_transformation/partial_state_selection.jl @@ -175,8 +175,8 @@ function dummy_derivative_graph!(state::TransformationState, jac = nothing; dummy_derivative_graph!(state.structure, var_eq_matching, jac, state_priority, log) end -function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, jac, - state_priority, ::Val{log} = Val(false)) where {log} +function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, jac = nothing, + state_priority = nothing, ::Val{log} = Val(false)) where {log} @unpack eq_to_diff, var_to_diff, graph = structure diff_to_eq = invview(eq_to_diff) diff_to_var = invview(var_to_diff) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 6c73dfa743..6a95a6e9e8 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -1,6 +1,25 @@ +""" +$(TYPEDEF) + +ModelingToolkit component or connector with metadata + +# Fields +$(FIELDS) +""" struct Model{F, S} + """The constructor that returns ODESystem.""" f::F + """ + The dictionary with metadata like keyword arguements (:kwargs), base + system this Model extends (:extend), sub-components of the Model (:components), + variables (:variables), parameters (:parameters), structural parameters + (:structural_parameters) and equations (:equations). + """ structure::S + """ + This flag is `true` when the Model is a connector and is `false` when it is + a component + """ isconnector::Bool end (m::Model)(args...; kw...) = m.f(args...; kw...)