Skip to content

bump MTK to version 10 #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ KernelAbstractions = "0.9.18"
LinearAlgebra = "1"
MacroTools = "0.5.15"
Mixers = "0.1.2"
ModelingToolkit = "9.67"
ModelingToolkit = "10"
NNlib = "0.9.13"
NonlinearSolve = "4"
OrderedCollections = "1.8.0"
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/gas_network.jl
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ As a workaround we had to explicitly define `LinearInterpolations` as unitless,

To build the network, we first need to define the components. This is a two-step process:

- first create the symbolic `ODESystem` using ModelingToolkit
- first create the symbolic `System` using ModelingToolkit
- secondly build a NetworkDynamics component model ([`VertexModel`](@ref)/[`EdgeModel`](@ref)) based on the symbolic system.

In the first step we can use the keyword arguments to pass "default" values for our parameters and states.
Expand Down
82 changes: 45 additions & 37 deletions docs/localmake.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,61 @@ use the updated `*.md` and source files. This way the Julia session keeps alive
individual builds are much faster.
=#

#=
PORT = isempty(ARGS) ? 8000 : parse(Int, ARGS[1])
@assert 8000 ≤ PORT ≤ 9000 "PORT has to be in range 8000..9000!"

print("Do you want to update docs environment? [y/n] (default: n) ")
answer = readline()
UPDATE_ENV = if !isempty(answer) && answer[1] == 'y'
true
else
false
end

print("Do you want to build docs continously on file change? This will enable the `draft=true` flag, and the examples will not run. [y/n] (default: n)")
answer = readline()
SERVEDOCS_DRAFT = if !isempty(answer) && answer[1] == 'y'
true
else
false
end


using Pkg
Pkg.activate(@__DIR__)
Pkg.instantiate()
print("Do you want to update docs environment? [y/n] ")
answer = readline()
if !isempty(answer) && answer[1] == 'y'

if UPDATE_ENV
Pkg.update()
end

using Revise
using LiveServer
using REPL.TerminalMenus

port = isempty(ARGS) ? 8000 : parse(Int, ARGS[1])
@assert 8000 ≤ port ≤ 9000 "port has to be in range 8000..9000!"

@info "Start server..."
@async serve(;dir=joinpath(@__DIR__, "build"), port)

menu = RadioMenu(["Run again!", "Quit!"])
while true
revise()
@info "Start building docs..."
try
include("make.jl")
catch e
@info "make.jl error" e
end

println("\nDocs are served at http://localhost:$port")
using NetworkDynamics
cd(pkgdir(NetworkDynamics))

if request("What now?", menu) != 1
break
end
end
=#

if SERVEDOCS_DRAFT
servedocs(
literate_dir = joinpath("docs", "examples"),
skip_dir = joinpath("docs", "src", "generated")
)
else
@info "Start server..."
@async serve(;dir=joinpath(@__DIR__, "build"), PORT)

using Pkg
Pkg.activate(@__DIR__)
using NetworkDynamics
using LiveServer
while true
revise()
@info "Start building docs..."
try
include("make.jl")
catch e
@info "make.jl error" e
end

cd(pkgdir(NetworkDynamics))
println("\nDocs are served at http://localhost:$port")

servedocs(
literate_dir = joinpath("docs", "examples"),
skip_dir = joinpath("docs", "src", "generated")
)
println("Press [Enter] to rerun the make process or [Ctrl+C] to exit.")
readline()
end
end
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ kwargs = (;
"Directed and Weighted Graphs" => "generated/directed_and_weighted_graphs.md",
]
],
draft=false,
draft = isdefined(Main, :SERVEDOCS_DRAFT) ? Main.SERVEDOCS_PREVIEW : false,
format = Documenter.HTML(ansicolor = true),
warnonly=[:missing_docs],
)
Expand Down
6 changes: 3 additions & 3 deletions docs/src/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ EdgeModel()

## Component Models with MTK
```@docs
VertexModel(::ModelingToolkit.ODESystem, ::Any, ::Any)
EdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any, ::Any)
EdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any)
VertexModel(::ModelingToolkit.System, ::Any, ::Any)
EdgeModel(::ModelingToolkit.System, ::Any, ::Any, ::Any, ::Any)
EdgeModel(::ModelingToolkit.System, ::Any, ::Any, ::Any)
```

### Output Function Helpers/Wrappers
Expand Down
2 changes: 1 addition & 1 deletion docs/src/metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Special cases for symbol metadata are:
For those, there are special functions `has_*`, `get_*`, `set_*!` and `delete_*!`. See [Per Symbol Metadata API](@ref).


These are closely aligned with the [metadata use in ModelingToolkit](https://docs.sciml.ai/ModelingToolkit/stable/basics/Variable_metadata/). They are automatically copied from the `ODESystem` if you use MTK models to create NetworkDynamics models.
These are closely aligned with the [metadata use in ModelingToolkit](https://docs.sciml.ai/ModelingToolkit/stable/basics/Variable_metadata/). They are automatically copied from the `System` if you use MTK models to create NetworkDynamics models.

## Metadata Utils
Accessing metadata (especially defaults) of states and parameters is a very
Expand Down
8 changes: 4 additions & 4 deletions docs/src/mtk_integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ which are then connected on network scale using NetworkDynamics.

The main entry point for this interop are the constructors
```julia
VertexModel(::ODESystem, inputs, outputs)
EdgeModel(::ODESystem, srcin, dstin, [srscout], dstout)
VertexModel(::System, inputs, outputs)
EdgeModel(::System, srcin, dstin, [srscout], dstout)
```
whose docstrings can be found in the [Component Models with MTK](@ref) section in the API.

These constructors will:
- transform the states marked as input to parameters and `structural_simplify`ing the system,
- transform the states marked as input to parameters and `mtkcompile`ing the system,
- generate the `f` and `g` functions,
- generate code for observables,
- port all supported [Metadata](@ref) from MTK symbols to component symbols and
Expand Down Expand Up @@ -76,7 +76,7 @@ An ideal voltage source is just a model which pins its output voltage to a fixed
The source ejects whatever current is necessary. We introduce another variable `i(t)`
to "capture" this current. This variable will be removed during structural simplify, but will
be available for plotting through the [Observables](@ref) mechanism.
The `VertexModel` can be generated from an `ODESystem` by providing names of the input and output states:
The `VertexModel` can be generated from an `System` by providing names of the input and output states:

```@example mtk
@mtkmodel VoltageSource begin
Expand Down
34 changes: 17 additions & 17 deletions ext/NetworkDynamicsMTKExt.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module NetworkDynamicsMTKExt

using ModelingToolkit: Symbolic, iscall, operation, arguments, build_function
using ModelingToolkit: ModelingToolkit, Equation, ODESystem, Differential
using ModelingToolkit: equations, full_equations, get_variables, structural_simplify, getname, unwrap
using ModelingToolkit: ModelingToolkit, Equation, System, Differential
using ModelingToolkit: equations, full_equations, get_variables, mtkcompile, getname, unwrap
using ModelingToolkit: parameters, unknowns, independent_variables, observed, defaults
using Symbolics: Symbolics, fixpoint_sub, substitute
using RecursiveArrayTools: RecursiveArrayTools
Expand All @@ -18,22 +18,22 @@ import NetworkDynamics: VertexModel, EdgeModel, AnnotatedSym
include("MTKExt_utils.jl")

"""
VertexModel(sys::ODESystem, inputs, outputs;
VertexModel(sys::System, inputs, outputs;
verbose=false, name=getname(sys), extin=nothing, ff_to_constraint=true, kwargs...)

Create a `VertexModel` object from a given `ODESystem` created with ModelingToolkit.
Create a `VertexModel` object from a given `System` created with ModelingToolkit.
You need to provide 2 lists of symbolic names (`Symbol` or `Vector{Symbols}`):
- `inputs`: names of variables in you equation representing the aggregated edge states
- `outputs`: names of variables in you equation representing the node output

Additional kw arguments:
- `name`: Set name of the component model. Will be lifted from the ODESystem name.
- `name`: Set name of the component model. Will be lifted from the System name.
- `extin=nothing`: Provide external inputs as pairs, i.e. `extin=[:extvar => VIndex(1, :a)]`
will bound the variable `extvar(t)` in the equations to the state `a` of the first vertex.
- `ff_to_constraint=true`: Controlls, whether output transformations `g` which depend on inputs should be
transformed into constraints. Defaults to true since ND.jl does not handle vertices with FF yet.
"""
function VertexModel(sys::ODESystem, inputs, outputs; verbose=false, name=getname(sys),
function VertexModel(sys::System, inputs, outputs; verbose=false, name=getname(sys),
ff_to_constraint=true, extin=nothing, kwargs...)
warn_events(sys)
inputs = inputs isa AbstractVector ? inputs : [inputs]
Expand Down Expand Up @@ -87,9 +87,9 @@ function VertexModel(sys::ODESystem, inputs, outputs; verbose=false, name=getnam
end

"""
EdgeModel(sys::ODESystem, srcin, dstin, AntiSymmetric(dstout); kwargs...)
EdgeModel(sys::System, srcin, dstin, AntiSymmetric(dstout); kwargs...)

Create a `EdgeModel` object from a given `ODESystem` created with ModelingToolkit for **single sided models**.
Create a `EdgeModel` object from a given `System` created with ModelingToolkit for **single sided models**.

Here you only need to provide one list of output symbols: `dstout`.
To make it clear how to handle the single-sided output definiton, you musst wrap
Expand All @@ -100,27 +100,27 @@ the symbol vector in

Additional `kwargs` are the same as for the double-sided EdgeModel MTK constructor.
"""
EdgeModel(sys::ODESystem, srcin, dstin, dstout; kwargs...) = EdgeModel(sys, srcin, dstin, nothing, dstout; kwargs...)
EdgeModel(sys::System, srcin, dstin, dstout; kwargs...) = EdgeModel(sys, srcin, dstin, nothing, dstout; kwargs...)

"""
EdgeModel(sys::ODESystem, srcin, dstin, srcout, dstout;
EdgeModel(sys::System, srcin, dstin, srcout, dstout;
verbose=false, name=getname(sys), extin=nothing, ff_to_constraint=false, kwargs...)

Create a `EdgeModel` object from a given `ODESystem` created with ModelingToolkit.
Create a `EdgeModel` object from a given `System` created with ModelingToolkit.
You need to provide 4 lists of symbolic names (`Symbol` or `Vector{Symbols}`):
- `srcin`: names of variables in you equation representing the node state at the source
- `dstin`: names of variables in you equation representing the node state at the destination
- `srcout`: names of variables in you equation representing the output at the source
- `dstout`: names of variables in you equation representing the output at the destination

Additional kw arguments:
- `name`: Set name of the component model. Will be lifted from the ODESystem name.
- `name`: Set name of the component model. Will be lifted from the System name.
- `extin=nothing`: Provide external inputs as pairs, i.e. `extin=[:extvar => VIndex(1, :a)]`
will bound the variable `extvar(t)` in the equations to the state `a` of the first vertex.
- `ff_to_constraint=false`: Controlls, whether output transformations `g` which depend on inputs should be
transformed into constraints.
"""
function EdgeModel(sys::ODESystem, srcin, dstin, srcout, dstout; verbose=false, name=getname(sys),
function EdgeModel(sys::System, srcin, dstin, srcout, dstout; verbose=false, name=getname(sys),
ff_to_constraint=false, extin=nothing, kwargs...)
warn_events(sys)
srcin = srcin isa AbstractVector ? srcin : [srcin]
Expand Down Expand Up @@ -300,16 +300,16 @@ function generate_io_function(_sys, inputss::Tuple, outputss::Tuple;
verbose && @warn "The specified outputs $implicit_outputs do not appear in the equations of the system!"
end
verbose && @info "Simplifying system with inputs $_openinputs and outputs $_definedoutputs"
structural_simplify(_sys, (_openinputs, _definedoutputs); simplify=false)[1]
mtkcompile(_sys; inputs=_openinputs, outputs=_definedoutputs, simplify=false)
end

allparams = parameters(sys) # contains inputs!
@argcheck allinputs ⊆ Set(allparams) ∪ missing_inputs
params = setdiff(allparams, Set(allinputs))

# extract the main equations and observed equations
eqs::Vector{Equation} = ModelingToolkit.subs_constants(equations(sys))
obseqs_sorted::Vector{Equation} = ModelingToolkit.subs_constants(observed(sys))
eqs::Vector{Equation} = equations(sys)
obseqs_sorted::Vector{Equation} = observed(sys)
fix_metadata!(eqs, sys);
fix_metadata!(obseqs_sorted, sys);

Expand Down Expand Up @@ -398,7 +398,7 @@ function generate_io_function(_sys, inputss::Tuple, outputss::Tuple;
end
verbose && @info "Transformed algebraic eqs" eqs

# create massmatrix, we don't use the method provided by ODESystem because of reordering
# create massmatrix, we don't use the method provided by System because of reordering
mm = generate_massmatrix(eqs)
verbose && @info "Generated mass matrix" mm
mm
Expand Down
Loading