diff --git a/previews/PR187/.documenter-siteinfo.json b/previews/PR187/.documenter-siteinfo.json index be8a87fb..0b3609f2 100644 --- a/previews/PR187/.documenter-siteinfo.json +++ b/previews/PR187/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-27T14:29:49","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-28T18:31:51","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/previews/PR187/API/index.html b/previews/PR187/API/index.html index 20f38294..041adc2f 100644 --- a/previews/PR187/API/index.html +++ b/previews/PR187/API/index.html @@ -1,40 +1,40 @@ -API · NetworkDynamics

API

The following functions are designed for public use.

Network Construction API

NetworkDynamics.NetworkType
Network([g,] vertexf, edgef; kwarg...)

Construct a Network object from a graph g and edge and component models vertexf and edgef.

Arguments:

  • g::AbstractGraph: The graph on which the network is defined. Optional, can be ommittet if all component models have a defined graphelement. See vidx and src/dst keywors for VertexModel and EdgeModel constructors respectively.

  • vertexm: A single VertexModel or a vector of VertexModel objects. The order of the vertex models must mirror the order of the vertices(g) iterator.

  • edgem: A single EdgeModel or a vector of EdgeModel objects. The order of the edge models must mirror the order of the edges(g) iterator.

Optional keyword arguments:

  • execution=SequentialExecution{true}(): Execution model of the network. E.g. SequentialExecution, KAExecution, PolyesterExecution or ThreadedExecution.
  • aggregator=execution isa SequentialExecution ? SequentialAggregator(+) : PolyesterAggregator(+): Aggregation function applied to the edge models. E.g. SequentialAggregator, PolyesterAggregator, ThreadedAggregator, SparseAggregator.
  • check_graphelement=true: Check if the graphelement metadata is consistent with the graph.
  • dealias=false Check if the components alias eachother and create copies if necessary. This is necessary if the same component model is referenced in multiple places in the Network but you want to dynamicially asign metadata, such as initialization information to specific instances.
  • verbose=false: Show additional information during construction.
source
Network(nw::Network; g, vertexm, edgem, kwargs...)

Rebuild the Network with same graph and vertex/edge models but possibly different kwargs.

source
NetworkDynamics.dimMethod
dim(nw::Network)

Returns the number of dynamic states in the network, corresponts to the length of the flat state vector.

source
NetworkDynamics.pdimMethod
pdim(nw::Network)

Returns the number of parameters in the network, corresponts to the length of the flat parameter vector.

source

Component Models

NetworkDynamics.VertexModelMethod
VertexModel(; kwargs...)

Build a VertexModel according to the keyword arguments.

Main Arguments:

  • f=nothing: Dynamic function of the component. Can be nothing if dim is 0.
  • g: Output function of the component. Usefull helpers: StateMask
  • sym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.
  • outsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. Can be infered automaticially if g isa StateMask.
  • psym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.
  • mass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).
  • name=dim>0 ? :VertexM : :StaticVertexM: Name of the component.

Optional Arguments:

  • insym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially.
  • vidx: Index of the vertex in the graph, enables graphless constructor.
  • ff: FeedForwardType of component. Will be typically infered from g automaticially.
  • obssym/obsf: Define additional "observable" states.
  • symmetadata/metadata: Provide prefilled metadata dictionaries.

All Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].

source
NetworkDynamics.EdgeModelMethod
EdgeModel(; kwargs...)

Build a EdgeModel according to the keyword arguments.

Main Arguments:

  • f=nothing: Dynamic function of the component. Can be nothing if dim is 0.
  • g: Output function of the component. Usefull helpers: AntiSymmetric, Symmetric, Fiducial, Directed and StateMask.
  • sym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.
  • outsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. In general, outsym for edges isa named tuple (; src, dst). However, depending on the g function, it might be enough to provide a single vector or even nothing (e.g. AntiSymmetric(StateMask(1:2))). See Building EdgeModels for examples.
  • psym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.
  • mass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).
  • name=dim>0 ? :EdgeM : :StaticEdgeM: Name of the component.

Optional Arguments:

  • insym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially. For edges, insym is a named tuple (; src, dst). If give as vector tuple is created automaticially.
  • src/dst: Index or name of the vertices at src and dst end. Enables graphless constructor.
  • ff: FeedForwardType of component. Will be typically infered from g automaticially.
  • obssym/obsf: Define additional "observable" states.
  • symmetadata/metadata: Provide prefilled metadata dictionaries.

All Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].

source

Component Models with MTK

NetworkDynamics.VertexModelMethod
VertexModel(sys::ODESystem, inputs, outputs; ff_to_constraint=true, kwargs...)

Create a VertexModel object from a given ODESystem 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

ff_to_constraint controlls, whether output transformations g which depend on inputs should be

source
NetworkDynamics.EdgeModelMethod
EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout; ff_to_constraint=false, kwargs...)

Create a EdgeModel object from a given ODESystem 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

ff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.

source
NetworkDynamics.EdgeModelMethod
EdgeModel(sys::ODESystem, srcin, dstin, AntiSymmetric(dstout); ff_to_constraint=false, kwargs...)

Create a EdgeModel object from a given ODESystem created with ModelingToolkit.

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 the symbol vector in

  • AntiSymmetric(dstout),
  • Symmetric(dstout), or
  • Directed(dstout).

ff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.

source

Output Function Helpers/Wrappers

NetworkDynamics.StateMaskType
StateMask(i::AbstractArray)
-StateMaks(i::Number)

A StateMask is a predefined output function. It can be used to define the output of a component model by picking from the internal state.

I.e. g=StateMask(2:3) in a vertex function will output the internal states 2 and 3. In many contexts, StateMasks can be constructed implicitly by just providing the indices, e.g. g=1:2.

For EdgeModel this needs to be combined with a Directed, Symmetric, AntiSymmetric or Fiducial coupling, e.g. g=Fiducial(1:2, 3:4) forwards states 1:2 to dst and states 3:4 to src.

source

Accessors for Component Properties

NetworkDynamics.outdimFunction
outdim(c::VertexModel)::Int
-outdim(c::EdgeModel)::@NamedTuple(src::Int, dst::Int)

Retrieve the output dimension of the component

source
NetworkDynamics.outsymFunction

outsym(c::VertexModel)::Vector{Symbol} outsym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}

Retrieve the output symbols of the component.

source
NetworkDynamics.insymFunction
insym(c::VertexModel)::Vector{Symbol}
-insym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}

Musst be called after hasinsym/hasindim returned true. Gives the insym vector(s). For vertex model just a single vector, for edges it returns a named tuple (; src, dst) with two symbol vectors.

source

FeedForwardType-Traits

Symbolic Indexing API

Network Parameter Object

NetworkDynamics.NWParameterType
NWParameter(nw_or_nw_wraper, pflat)

Indexable wrapper for flat parameter array pflat. Needs Network or wrapper of Network, e.g. ODEProblem.

p = NWParameter(nw)
+API · NetworkDynamics

API

The following functions are designed for public use.

Network Construction API

NetworkDynamics.NetworkType
Network([g,] vertexf, edgef; kwarg...)

Construct a Network object from a graph g and edge and component models vertexf and edgef.

Arguments:

  • g::AbstractGraph: The graph on which the network is defined. Optional, can be ommittet if all component models have a defined graphelement. See vidx and src/dst keywors for VertexModel and EdgeModel constructors respectively.

  • vertexm: A single VertexModel or a vector of VertexModel objects. The order of the vertex models must mirror the order of the vertices(g) iterator.

  • edgem: A single EdgeModel or a vector of EdgeModel objects. The order of the edge models must mirror the order of the edges(g) iterator.

Optional keyword arguments:

  • execution=SequentialExecution{true}(): Execution model of the network. E.g. SequentialExecution, KAExecution, PolyesterExecution or ThreadedExecution.
  • aggregator=execution isa SequentialExecution ? SequentialAggregator(+) : PolyesterAggregator(+): Aggregation function applied to the edge models. E.g. SequentialAggregator, PolyesterAggregator, ThreadedAggregator, SparseAggregator.
  • check_graphelement=true: Check if the graphelement metadata is consistent with the graph.
  • dealias=false Check if the components alias eachother and create copies if necessary. This is necessary if the same component model is referenced in multiple places in the Network but you want to dynamicially asign metadata, such as initialization information to specific instances.
  • verbose=false: Show additional information during construction.
source
Network(nw::Network; g, vertexm, edgem, kwargs...)

Rebuild the Network with same graph and vertex/edge models but possibly different kwargs.

source
NetworkDynamics.dimMethod
dim(nw::Network)

Returns the number of dynamic states in the network, corresponts to the length of the flat state vector.

source
NetworkDynamics.pdimMethod
pdim(nw::Network)

Returns the number of parameters in the network, corresponts to the length of the flat parameter vector.

source

Component Models

NetworkDynamics.VertexModelMethod
VertexModel(; kwargs...)

Build a VertexModel according to the keyword arguments.

Main Arguments:

  • f=nothing: Dynamic function of the component. Can be nothing if dim is 0.
  • g: Output function of the component. Usefull helpers: StateMask
  • sym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.
  • outsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. Can be infered automaticially if g isa StateMask.
  • psym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.
  • mass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).
  • name=dim>0 ? :VertexM : :StaticVertexM: Name of the component.

Optional Arguments:

  • insym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially.
  • vidx: Index of the vertex in the graph, enables graphless constructor.
  • ff: FeedForwardType of component. Will be typically infered from g automaticially.
  • obssym/obsf: Define additional "observable" states.
  • symmetadata/metadata: Provide prefilled metadata dictionaries.
  • extin=nothing: Define "external" inputs for the model with Network indices, i.e. extin=[VIndex(7,:x), ..]. Those inputs will be provided as another input vector f(x, in, extin, p, t) and g(y, x, in, extin, p, t).

All Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].

source
NetworkDynamics.EdgeModelMethod
EdgeModel(; kwargs...)

Build a EdgeModel according to the keyword arguments.

Main Arguments:

  • f=nothing: Dynamic function of the component. Can be nothing if dim is 0.
  • g: Output function of the component. Usefull helpers: AntiSymmetric, Symmetric, Fiducial, Directed and StateMask.
  • sym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.
  • outsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. In general, outsym for edges isa named tuple (; src, dst). However, depending on the g function, it might be enough to provide a single vector or even nothing (e.g. AntiSymmetric(StateMask(1:2))). See Building EdgeModels for examples.
  • psym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.
  • mass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).
  • name=dim>0 ? :EdgeM : :StaticEdgeM: Name of the component.

Optional Arguments:

  • insym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially. For edges, insym is a named tuple (; src, dst). If give as vector tuple is created automaticially.
  • src/dst: Index or name of the vertices at src and dst end. Enables graphless constructor.
  • ff: FeedForwardType of component. Will be typically infered from g automaticially.
  • obssym/obsf: Define additional "observable" states.
  • symmetadata/metadata: Provide prefilled metadata dictionaries.
  • extin=nothing: Define "external" inputs for the model with Network indices, i.e. extin=[VIndex(7,:x), ..]. Those inputs will be provided as another input vector f(x, insrc, indst, extin, p, t) and g(ysrc, ydst, x, insrc, indst, extin, p, t).

All Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].

source

Component Models with MTK

NetworkDynamics.VertexModelMethod
VertexModel(sys::ODESystem, inputs, outputs; ff_to_constraint=true, kwargs...)

Create a VertexModel object from a given ODESystem 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

ff_to_constraint controlls, whether output transformations g which depend on inputs should be

source
NetworkDynamics.EdgeModelMethod
EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout; ff_to_constraint=false, kwargs...)

Create a EdgeModel object from a given ODESystem 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

ff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.

source
NetworkDynamics.EdgeModelMethod
EdgeModel(sys::ODESystem, srcin, dstin, AntiSymmetric(dstout); ff_to_constraint=false, kwargs...)

Create a EdgeModel object from a given ODESystem created with ModelingToolkit.

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 the symbol vector in

  • AntiSymmetric(dstout),
  • Symmetric(dstout), or
  • Directed(dstout).

ff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.

source

Output Function Helpers/Wrappers

NetworkDynamics.StateMaskType
StateMask(i::AbstractArray)
+StateMaks(i::Number)

A StateMask is a predefined output function. It can be used to define the output of a component model by picking from the internal state.

I.e. g=StateMask(2:3) in a vertex function will output the internal states 2 and 3. In many contexts, StateMasks can be constructed implicitly by just providing the indices, e.g. g=1:2.

For EdgeModel this needs to be combined with a Directed, Symmetric, AntiSymmetric or Fiducial coupling, e.g. g=Fiducial(1:2, 3:4) forwards states 1:2 to dst and states 3:4 to src.

source

Accessors for Component Properties

NetworkDynamics.outdimFunction
outdim(c::VertexModel)::Int
+outdim(c::EdgeModel)::@NamedTuple(src::Int, dst::Int)

Retrieve the output dimension of the component

source
NetworkDynamics.outsymFunction

outsym(c::VertexModel)::Vector{Symbol} outsym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}

Retrieve the output symbols of the component.

source
NetworkDynamics.insymFunction
insym(c::VertexModel)::Vector{Symbol}
+insym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}

Musst be called after hasinsym/hasindim returned true. Gives the insym vector(s). For vertex model just a single vector, for edges it returns a named tuple (; src, dst) with two symbol vectors.

source

FeedForwardType-Traits

Symbolic Indexing API

Network Parameter Object

NetworkDynamics.NWParameterType
NWParameter(nw_or_nw_wraper, pflat)

Indexable wrapper for flat parameter array pflat. Needs Network or wrapper of Network, e.g. ODEProblem.

p = NWParameter(nw)
 p.v[idx, :sym] # get parameter :sym of vertex idx
 p.e[idx, :sym] # get parameter :sym of edge idx
-p[s::Union{VPIndex, EPIndex}] # get parameter for specific index

Get flat array representation using pflat(p).

source
NetworkDynamics.NWParameterMethod
NWParameter(nw_or_nw_wraper;
-            ptype=Vector{Float64}, pfill=filltype(ptype), default=true)

Creates "empty" NWParameter object for the Network/Wrapper nw with flat type ptype. The array will be prefilled with pfill (defaults to NaN).

If default=true the default parameter values attached to the network components will be loaded.

source

Network State Object

NetworkDynamics.NWStateType
NWState(nw_or_nw_wrapper, uflat, [pflat], [t])

Indexable wrapper for flat state & parameter array. Needs Network or wrapper of Network, e.g. ODEProblem.

s = NWState(nw)
+p[s::Union{VPIndex, EPIndex}] # get parameter for specific index

Get flat array representation using pflat(p).

source
NetworkDynamics.NWParameterMethod
NWParameter(nw_or_nw_wraper;
+            ptype=Vector{Float64}, pfill=filltype(ptype), default=true)

Creates "empty" NWParameter object for the Network/Wrapper nw with flat type ptype. The array will be prefilled with pfill (defaults to NaN).

If default=true the default parameter values attached to the network components will be loaded.

source

Network State Object

NetworkDynamics.NWStateType
NWState(nw_or_nw_wrapper, uflat, [pflat], [t])

Indexable wrapper for flat state & parameter array. Needs Network or wrapper of Network, e.g. ODEProblem.

s = NWState(nw)
 s.v[idx, :sym] # get state :sym of vertex idx
 s.e[idx, :sym] # get state :sym of edge idx
 s.p.v[idx, :sym] # get parameter :sym of vertex idx
 s.p.e[idx, :sym] # get parameter :sym of edge idx
-s[s::Union{VIndex, EIndex, EPIndex, VPIndex}] # get parameter for specific index

Get flat array representation using uflat(s) and pflat(s).

source
NetworkDynamics.NWStateMethod
NWState(nw_or_nw_wrapper;
+s[s::Union{VIndex, EIndex, EPIndex, VPIndex}] # get parameter for specific index

Get flat array representation using uflat(s) and pflat(s).

source
NetworkDynamics.NWStateMethod
NWState(nw_or_nw_wrapper;
         utype=Vector{Float64}, ufill=filltype(utype),
-        ptype=Vector{Float64}, pfill=filltype(ptype), default=true)

Creates "empty" NWState object for the Network/Wrapper nw with flat types utype & ptype. The arrays will be prefilled with ufill and pfill respectively (defaults to NaN).

If default=true the default state & parameter values attached to the network components will be loaded.

source
NetworkDynamics.NWStateMethod
NWState(p::NWState; utype=typeof(uflat(s)), ptype=typeof(pflat(s)))

Create NWState based on other state object, just convert types.

source
NetworkDynamics.NWStateMethod
NWState(p::NWParameter; utype=Vector{Float64}, ufill=filltype(utype), default=true)

Create NWState based on existing NWParameter object.

source
NetworkDynamics.pflatFunction
pflat(p::NWParameter)
-pflat(s::NWState)

Retrieve the wrapped flat array representation of the parameters.

source

Symbolic Indices

NetworkDynamics.VIndexType
VIndex{C,S} <: SymbolicStateIndex{C,S}
+        ptype=Vector{Float64}, pfill=filltype(ptype), default=true)

Creates "empty" NWState object for the Network/Wrapper nw with flat types utype & ptype. The arrays will be prefilled with ufill and pfill respectively (defaults to NaN).

If default=true the default state & parameter values attached to the network components will be loaded.

source
NetworkDynamics.NWStateMethod
NWState(p::NWState; utype=typeof(uflat(s)), ptype=typeof(pflat(s)))

Create NWState based on other state object, just convert types.

source
NetworkDynamics.NWStateMethod
NWState(p::NWParameter; utype=Vector{Float64}, ufill=filltype(utype), default=true)

Create NWState based on existing NWParameter object.

source
NetworkDynamics.pflatFunction
pflat(p::NWParameter)
+pflat(s::NWState)

Retrieve the wrapped flat array representation of the parameters.

source

Symbolic Indices

NetworkDynamics.VIndexType
VIndex{C,S} <: SymbolicStateIndex{C,S}
 idx = VIndex(comp, sub)

A symbolic index for a vertex state variable.

  • comp: the component index, either int or a collection of ints
  • sub: the subindex, either int, symbol or a collection of those.
VIndex(1, :P)      # vertex 1, variable :P
 VIndex(1:5, 1)     # first state of vertices 1 to 5
-VIndex(7, (:x,:y)) # states :x and :y of vertex 7

Can be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWState, NWParameter or ODESolution.

See also: EIndex, VPIndex, EPIndex

source
NetworkDynamics.EIndexType
EIndex{C,S} <: SymbolicStateIndex{C,S}
 idx = EIndex(comp, sub)

A symbolic index for an edge state variable.

  • comp: the component index, either int or a collection of ints
  • sub: the subindex, either int, symbol or a collection of those.
EIndex(1, :P)      # edge 1, variable :P
 EIndex(1:5, 1)     # first state of edges 1 to 5
-EIndex(7, (:x,:y)) # states :x and :y of edge 7

Can be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWState, NWParameter or ODESolution.

See also: VIndex, VPIndex, EPIndex

source
NetworkDynamics.VPIndexType
VPIndex{C,S} <: SymbolicStateIndex{C,S}
-idx = VPIndex(comp, sub)

A symbolic index into the parameter a vertex:

  • comp: the component index, either int or a collection of ints
  • sub: the subindex, either int, symbol or a collection of those.

Can be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.

See also: EPIndex, VIndex, EIndex

source
NetworkDynamics.EPIndexType
EPIndex{C,S} <: SymbolicStateIndex{C,S}
-idx = VEIndex(comp, sub)

A symbolic index into the parameter a vertex:

  • comp: the component index, either int or a collection of ints
  • sub: the subindex, either int, symbol or a collection of those.

Can be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.

See also: VPIndex, VIndex, EIndex

source

Index generators

NetworkDynamics.vidxsFunction
vidxs([inpr], components=:, variables=:) :: Vector{VIndex}

Generate vector of symbolic indexes for vertices.

  • inpr: Only needed for name matching or : access. Can be Network, sol, prob, ...
  • components: Number/Vector, :, Symbol (name matches), String/Regex (name contains)
  • variables: Symbol/Number/Vector, :, String/Regex (all sym containing)

Examples:

vidxs(nw)                 # all vertex state indices
+EIndex(7, (:x,:y)) # states :x and :y of edge 7

Can be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWState, NWParameter or ODESolution.

See also: VIndex, VPIndex, EPIndex

source
NetworkDynamics.VPIndexType
VPIndex{C,S} <: SymbolicStateIndex{C,S}
+idx = VPIndex(comp, sub)

A symbolic index into the parameter a vertex:

  • comp: the component index, either int or a collection of ints
  • sub: the subindex, either int, symbol or a collection of those.

Can be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.

See also: EPIndex, VIndex, EIndex

source
NetworkDynamics.EPIndexType
EPIndex{C,S} <: SymbolicStateIndex{C,S}
+idx = VEIndex(comp, sub)

A symbolic index into the parameter a vertex:

  • comp: the component index, either int or a collection of ints
  • sub: the subindex, either int, symbol or a collection of those.

Can be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.

See also: VPIndex, VIndex, EIndex

source

Index generators

NetworkDynamics.vidxsFunction
vidxs([inpr], components=:, variables=:) :: Vector{VIndex}

Generate vector of symbolic indexes for vertices.

  • inpr: Only needed for name matching or : access. Can be Network, sol, prob, ...
  • components: Number/Vector, :, Symbol (name matches), String/Regex (name contains)
  • variables: Symbol/Number/Vector, :, String/Regex (all sym containing)

Examples:

vidxs(nw)                 # all vertex state indices
 vidxs(1:2, :u)            # [VIndex(1, :u), VIndex(2, :u)]
 vidxs(nw, :, [:u, :v])    # [VIndex(i, :u), VIndex(i, :v) for i in 1:nv(nw)]
-vidxs(nw, "ODEVertex", :) # all symbols of all vertices with name containing "ODEVertex"
source
NetworkDynamics.eidxsFunction
vidxs([inpr], components=:, variables=:) :: Vector{EIndex}

Generate vector of symbolic indexes for edges.

  • inpr: Only needed for name matching or : access. Can be Network, sol, prob, ...
  • components: Number/Vector, :, Symbol (name matches), String/Regex (name contains)
  • variables: Symbol/Number/Vector, :, String/Regex (all sym containing)

Examples:

eidxs(nw)                # all edge state indices
+vidxs(nw, "ODEVertex", :) # all symbols of all vertices with name containing "ODEVertex"
source
NetworkDynamics.eidxsFunction
vidxs([inpr], components=:, variables=:) :: Vector{EIndex}

Generate vector of symbolic indexes for edges.

  • inpr: Only needed for name matching or : access. Can be Network, sol, prob, ...
  • components: Number/Vector, :, Symbol (name matches), String/Regex (name contains)
  • variables: Symbol/Number/Vector, :, String/Regex (all sym containing)

Examples:

eidxs(nw)                # all edge state indices
 eidxs(1:2, :u)           # [EIndex(1, :u), EIndex(2, :u)]
 eidxs(nw, :, [:u, :v])   # [EIndex(i, :u), EIndex(i, :v) for i in 1:ne(nw)]
-eidxs(nw, "FlowEdge", :) # all symbols of all edges with name containing "FlowEdge"
source
NetworkDynamics.vpidxsFunction
vpidxs([inpr], components=:, variables=:) :: Vector{VPIndex}

Generate vector of symbolic indexes for parameters. See vidxs for more information.

source
NetworkDynamics.epidxsFunction
epidxs([inpr], components=:, variables=:) :: Vector{EPIndex}

Generate vector of symbolic indexes for parameters. See eidxs for more information.

source

Metadata API

Component Metadata API

NetworkDynamics.get_graphelementFunction
get_graphelement(c::EdgeModel)::@NamedTuple{src::T, dst::T}
-get_graphelement(c::VertexModel)::Int

Retrieves the graphelement metadata for the component model. For edges this returns a named tupe (;src, dst) where both are either integers (vertex index) or symbols (vertex name).

source
NetworkDynamics.set_graphelement!Function
set_graphelement!(c::EdgeModel, src, dst)
-set_graphelement!(c::VertexModel, vidx)

Sets the graphelement metadata for the edge model. For edges this takes two arguments src and dst which are either integer (vertex index) or symbol (vertex name). For vertices it takes a single integer vidx.

source

Per-Symbol Metadata API

NetworkDynamics.set_metadata!Method
set_metadata!(c::ComponentModel, sym::Symbol, key::Symbol, value)
-set_metadata!(c::ComponentModel, sym::Symbol, pair)

Sets the metadata key for symbol sym to value.

source

Initialization

NetworkDynamics.find_fixpointFunction
find_fixpoint(nw::Network, [x0::NWState=NWState(nw)], [p::NWParameter=x0.p]; kwargs...)
+eidxs(nw, "FlowEdge", :) # all symbols of all edges with name containing "FlowEdge"
source
NetworkDynamics.vpidxsFunction
vpidxs([inpr], components=:, variables=:) :: Vector{VPIndex}

Generate vector of symbolic indexes for parameters. See vidxs for more information.

source
NetworkDynamics.epidxsFunction
epidxs([inpr], components=:, variables=:) :: Vector{EPIndex}

Generate vector of symbolic indexes for parameters. See eidxs for more information.

source

Metadata API

Component Metadata API

NetworkDynamics.get_graphelementFunction
get_graphelement(c::EdgeModel)::@NamedTuple{src::T, dst::T}
+get_graphelement(c::VertexModel)::Int

Retrieves the graphelement metadata for the component model. For edges this returns a named tupe (;src, dst) where both are either integers (vertex index) or symbols (vertex name).

source
NetworkDynamics.set_graphelement!Function
set_graphelement!(c::EdgeModel, src, dst)
+set_graphelement!(c::VertexModel, vidx)

Sets the graphelement metadata for the edge model. For edges this takes two arguments src and dst which are either integer (vertex index) or symbol (vertex name). For vertices it takes a single integer vidx.

source

Per-Symbol Metadata API

NetworkDynamics.set_metadata!Method
set_metadata!(c::ComponentModel, sym::Symbol, key::Symbol, value)
+set_metadata!(c::ComponentModel, sym::Symbol, pair)

Sets the metadata key for symbol sym to value.

source

Initialization

NetworkDynamics.find_fixpointFunction
find_fixpoint(nw::Network, [x0::NWState=NWState(nw)], [p::NWParameter=x0.p]; kwargs...)
 find_fixpoint(nw::Network, x0::AbstractVector, p::AbstractVector; kwargs...)
-find_fixpoint(nw::Network, x0::AbstractVector; kwargs...)

Convenience wrapper around SteadyStateProblem from SciML-ecosystem. Constructs and solves the steady state problem, returns found value wrapped as NWState.

source
NetworkDynamics.initialize_component!Function
initialize_component!(cf::ComponentModel; verbose=true, kwargs...)

Initialize a ComponentModel by solving the corresponding NonlinearLeastSquaresProblem. During initialization, everyting which has a default value (see Metadata) is considered "fixed". All other variables are considered "free" and are solved for. The initial guess for each variable depends on the guess value in the Metadata.

The result is stored in the ComponentModel itself. The values of the free variables are stored in the metadata field init.

The kwargs are passed to the nonlinear solver.

source
NetworkDynamics.init_residualFunction
init_residual(cf::T; t=NaN, recalc=false)

Calculates the residual |du| for the given component model for the values provided via default and init Metadata.

If recalc=false just return the residual determined in the actual initialization process.

See also initialize_component!.

source

Execution Types

NetworkDynamics.ExecutionStyleType
abstract type ExecutionStyle{buffered::Bool} end

Abstract type for execution style. The coreloop dispatches based on the Execution style stored in the network object.

  • buffered=true means that the edge input es explicitly gathered, i.e. the vertex outputs in the output buffer will be copied into a dedicated input buffer for the edges.
  • buffered=false means, that the edge inputs are not explicitly gathered, but the corloop will perform a redirected lookup into the output buffer.
source

Aggregators

NetworkDynamics.AggregatorType
abstract type Aggregator end

Abstract sypertype for aggregators. Aggregators operate on the output buffer of all components and fill the aggregation buffer with the aggregatated edge values per vertex.

All aggregators have the constructor

Aggegator(aggfun)

for example

SequentialAggreator(+)
source

Utils

NetworkDynamics.save_parameters!Function
save_parameters!(integrator::SciMLBase.DEIntegrator)

Save the current parameter values in the integrator. Call this function inside callbacks if the parameter values have changed. This will store a timeseries of said parameters in the solution object, thus alowing us to recosntruct observables which depend on time-dependet variables.

source
NetworkDynamics.ff_to_constraintFunction
ff_to_constraint(v::VertexModel)

Takes VertexModel v with feed forward and turns all algebraic output states into internal states by defining algebraic constraints contraints 0 = out - g(...). The new output function is just a StateMask into the extended internal state vector.

Returns the transformed VertexModel.

source
Base.copyMethod
copy(c::NetworkDynamics.ComponentModel)

Shallow copy of the component model. Creates a deepcopy of metadata and symmetadata but references the same objects everywhere else.

source
+find_fixpoint(nw::Network, x0::AbstractVector; kwargs...)

Convenience wrapper around SteadyStateProblem from SciML-ecosystem. Constructs and solves the steady state problem, returns found value wrapped as NWState.

source
NetworkDynamics.initialize_component!Function
initialize_component!(cf::ComponentModel; verbose=true, kwargs...)

Initialize a ComponentModel by solving the corresponding NonlinearLeastSquaresProblem. During initialization, everyting which has a default value (see Metadata) is considered "fixed". All other variables are considered "free" and are solved for. The initial guess for each variable depends on the guess value in the Metadata.

The result is stored in the ComponentModel itself. The values of the free variables are stored in the metadata field init.

The kwargs are passed to the nonlinear solver.

source
NetworkDynamics.init_residualFunction
init_residual(cf::T; t=NaN, recalc=false)

Calculates the residual |du| for the given component model for the values provided via default and init Metadata.

If recalc=false just return the residual determined in the actual initialization process.

See also initialize_component!.

source

Execution Types

NetworkDynamics.ExecutionStyleType
abstract type ExecutionStyle{buffered::Bool} end

Abstract type for execution style. The coreloop dispatches based on the Execution style stored in the network object.

  • buffered=true means that the edge input es explicitly gathered, i.e. the vertex outputs in the output buffer will be copied into a dedicated input buffer for the edges.
  • buffered=false means, that the edge inputs are not explicitly gathered, but the corloop will perform a redirected lookup into the output buffer.
source

Aggregators

NetworkDynamics.AggregatorType
abstract type Aggregator end

Abstract sypertype for aggregators. Aggregators operate on the output buffer of all components and fill the aggregation buffer with the aggregatated edge values per vertex.

All aggregators have the constructor

Aggegator(aggfun)

for example

SequentialAggreator(+)
source

Utils

NetworkDynamics.save_parameters!Function
save_parameters!(integrator::SciMLBase.DEIntegrator)

Save the current parameter values in the integrator. Call this function inside callbacks if the parameter values have changed. This will store a timeseries of said parameters in the solution object, thus alowing us to recosntruct observables which depend on time-dependet variables.

source
NetworkDynamics.ff_to_constraintFunction
ff_to_constraint(v::VertexModel)

Takes VertexModel v with feed forward and turns all algebraic output states into internal states by defining algebraic constraints contraints 0 = out - g(...). The new output function is just a StateMask into the extended internal state vector.

Returns the transformed VertexModel.

source
Base.copyMethod
copy(c::NetworkDynamics.ComponentModel)

Shallow copy of the component model. Creates a deepcopy of metadata and symmetadata but references the same objects everywhere else.

source
diff --git a/previews/PR187/external_inputs/072a6b35.png b/previews/PR187/external_inputs/072a6b35.png new file mode 100644 index 00000000..b0da8bbf Binary files /dev/null and b/previews/PR187/external_inputs/072a6b35.png differ diff --git a/previews/PR187/external_inputs/c563b0fb.png b/previews/PR187/external_inputs/c563b0fb.png new file mode 100644 index 00000000..5c6f3c24 Binary files /dev/null and b/previews/PR187/external_inputs/c563b0fb.png differ diff --git a/previews/PR187/external_inputs/index.html b/previews/PR187/external_inputs/index.html new file mode 100644 index 00000000..24470850 --- /dev/null +++ b/previews/PR187/external_inputs/index.html @@ -0,0 +1,148 @@ + +External Inputs · NetworkDynamics

External Inputs

External inputs for components are way to pass signals between components outside of the network structure. The most common usecase for that are control systems: make your vertex i depend on some state of vertex j.

If used, this will essentially wides the number of received inputs of a component function. I.e. the baseline mathematical models for vertex models

\[\begin{aligned} +M^{\mathrm v}\,\frac{\mathrm{d}}{\mathrm{d}t}x^{\mathrm v} &= f^{\mathrm v}(u^{\mathrm v}, i^{\mathrm v}, i^{\mathrm{ext}}, p^{\mathrm v}, t)\\ +y^{\mathrm v} &= g^{\mathrm v}(u^{\mathrm v}, i^{\mathrm v}, i^{\mathrm{ext}}, p^{\mathrm v}, t) +\end{aligned}\]

fᵥ(dxᵥ, xᵥ, e_aggr, ext, pᵥ, t)
+gᵥ(yᵥ, xᵥ, [e_aggr, ext,] pᵥ, t)

and edge models

\[\begin{aligned} +M^{\mathrm e}\,\frac{\mathrm{d}}{\mathrm{d}t}x^{\mathrm e} &= f^{\mathrm e}(u^{\mathrm e}, y^{\mathrm v}_{\mathrm{src}}, y^{\mathrm v}_{\mathrm{dst}}, i^{\mathrm{ext}}, p^{\mathrm e}, t)\\ +y^{\mathrm e}_{\mathrm{dst}} &= g_\mathrm{dst}^{\mathrm e}(u^{\mathrm e}, y^{\mathrm v}_{\mathrm{src}}, y^{\mathrm v}_{\mathrm{dst}}, i^{\mathrm{ext}}, p^{\mathrm e}, t)\\ +y^{\mathrm e}_{\mathrm{src}} &= g_\mathrm{src}^{\mathrm e}(u^{\mathrm e}, y^{\mathrm v}_{\mathrm{src}}, y^{\mathrm v}_{\mathrm{dst}}, i^{\mathrm{ext}}, p^{\mathrm e}, t) +\end{aligned}\]

fₑ(dxₑ, xₑ, v_src, v_dst, ext, pₑ, t)
+gₑ(y_src, y_dst, xᵥ, [v_src, v_dst, ext,] pₑ, t)

change. You may still oomit the input section from g according to the different Feed Forward Behaviors. However you either have to use all inputs (including ext) or none.

Usage

Vertex and Edge models with external inputs can be created using the extin keyword of the EdgeModel and VertexModel constructors.

You need to pass a vector of SymbolicIndices (VIndex and EIndex), e.g.

VertexModel(... , extin=[VIndex(12,:x), VIndex(9, :x)], ...)

means your vertex receives a 2 dimensional external input vector with the states x of vertices 12 and 9. See below for hands on example.

Limitations

There are some limitations in place. You can only reference states (i.e. things that appear in xᵥ or xₑ of some component model) or outputs of non-feed-forward components, i.e. states yᵥ or yₑ of some component model which does not have feed forward behavior in their g function.

Example

As an example system, we'll consider two capacitors with a resistor between them. Vertex 1 v1 has a controllable current source. Using a PI controller for the current source, it tries to keep the voltage at the second vertex stable under the disturbance of some time periodic current sind at v2.


+                  v1    Resistor   v2
+PI controlled   ─→─o─←────MMM────→─o─→─ time dependent 
+current source     ┴               ┴    current sink
+                   ┬               ┬
+                   ⏚               ⏚

The example will be implemented 2 times: in plain NetworkDynamcics and using MTK.

Plain NetworkDynamics

First we define the resistor:

using NetworkDynamics
+using OrdinaryDiffEqTsit5
+using CairoMakie
+
+function resistor_g(idst, vsrc, vdst, (R,), t)
+    idst[1] = (vsrc[1] - vdst[1])/R
+end
+resistor = EdgeModel(g=AntiSymmetric(resistor_g),
+                     outsym=:i, insym=:v, psym=:R=>0.1,
+                     src=:source, dst=:load, name=:resistor)
EdgeModel :resistor PureFeedForward() @ Edge source=>load
+ ├─ 1/1 inputs:  src=[src₊v] dst=[dst₊v]
+ ├─   0 states:  []  
+ ├─ 1/1 outputs: src=[₋i] dst=[i]
+ └─   1 param:   [R=0.1]

Then we define the "load" vertex with sinusoidial load profile:

function f_load(dv, v, iin, (C,), t)
+    dv[1] = 1/C*(iin[1] - (1 + 0.1*sin(t)))
+end
+load = VertexModel(f=f_load, g=1,
+                   sym=:V=>0.5, insym=:i, psym=:C=>1,
+                   vidx=2, name=:load)
VertexModel :load PureStateMap() @ Vertex 2
+ ├─ 1 input:  [i]
+ ├─ 1 state:  [V=0.5]
+ ├─ 1 output: [V=0.5]
+ └─ 1 param:  [C=1]

Lastly, we define the "source" vertex

function f_source(dv, v, iin, extin, (C,Vref,Ki,Kp), t)
+    Δ = Vref - extin[1]      # tracking error
+    dv[2] = Δ                # integrator state of PI
+    i_inj = Kp*Δ + Ki*v[2]   # controller output
+    dv[1] = 1/C*(iin[1] + i_inj)
+end
+source = VertexModel(f=f_source, g=1,
+                     sym=[:V=>0.5,:ξ=>0], insym=:i,
+                     psym=[:C=>1, :Vref=>1, :Ki=>0.5, :Kp=>10],
+                     extin=[VIndex(:load, :V)],
+                     vidx=1, name=:source)
VertexModel :source PureStateMap() @ Vertex 1
+ ├─ 1 input:  [i]
+ ├─ 2 states: [V=0.5, ξ=0]
+ ├─ 1 output: [V=0.5]
+ ├─ 4 params: [C=1, Vref=1, Ki=0.5, Kp=10]
+ └─ 1 ext in: [VIndex(:load, :V)]

Then we can create the network and simulate:

nw = Network([source, load], [resistor])
+u0 = NWState(nw) # everything has default values
+prob = ODEProblem(nw, uflat(u0), (0,100), pflat(u0))
+sol = solve(prob, Tsit5())
+
+fig, ax, p = lines(sol, idxs=VIndex(:load, :V), label="Voltage @ load");
+lines!(ax, sol, idxs=VPIndex(:source, :Vref), label="Reference", color=Cycled(2));
+axislegend(ax; position=:rb);
Example block output

MTK Models

First we define the resistor:

using NetworkDynamics
+using OrdinaryDiffEqTsit5
+using ModelingToolkit
+using ModelingToolkit: t_nounits as t, D_nounits as Dt
+using CairoMakie
+
+@mtkmodel Resistor begin
+    @variables begin
+        i(t), [description="Current at dst end"]
+        V_src(t), [description="Voltage at src end"]
+        V_dst(t), [description="Voltage at dst end"]
+    end
+    @parameters begin
+        R=0.1, [description="Resistance"]
+    end
+    @equations begin
+        i ~ (V_src - V_dst)/R
+    end
+end
+@named resistor = Resistor()
+resistor_edge = EdgeModel(resistor, [:V_src], [:V_dst], AntiSymmetric([:i]); src=:load, dst=:source)
EdgeModel :resistor PureFeedForward() @ Edge load=>source
+ ├─ 1/1 inputs:  src=[V_src] dst=[V_dst]
+ ├─   0 states:  []  
+ ├─ 1/1 outputs: src=[₋i] dst=[i]
+ └─   1 param:   [R=0.1]

Then we define the "load" vertex with sinusoidial load profile:

@mtkmodel Load begin
+    @variables begin
+        V(t)=0.5, [description="Voltage"]
+        i_load(t), [description="Load current"]
+        i_grid(t), [description="Current from grid"]
+    end
+    @parameters begin
+        C=1, [description="Capacitance"]
+    end
+    @equations begin
+        i_load ~ 1 + 0.1*sin(t)
+        Dt(V) ~ 1/C*(i_grid - i_load)
+    end
+end
+@named load = Load()
+load_vertex = VertexModel(load, [:i_grid], [:V]; vidx=2)
VertexModel :load PureStateMap() @ Vertex 2
+ ├─ 1 input:  [i_grid]
+ ├─ 1 state:  [V=0.5]
+ ├─ 1 output: [V=0.5]
+ └─ 1 param:  [C=1]

Lastly, we define the "source" vertex

@mtkmodel Source begin
+    @variables begin
+        V(t)=0.5, [description="Voltage"]
+        ξ(t)=0, [description="Integrator state"]
+        i_grid(t), [description="Current from grid"]
+        i_source(t), [description="Current from source"]
+        Δ(t), [description="Tracking Error"]
+        V_load(t), [description="Voltage at load"]
+    end
+    @parameters begin
+        C=1, [description="Capacitance"]
+        Vref=1, [description="Reference voltage"]
+        Ki=0.5, [description="Integral gain"]
+        Kp=10, [description="Proportional gain"]
+    end
+    @equations begin
+        Δ ~ Vref - V_load
+        Dt(ξ) ~ Δ
+        i_source ~ Kp*Δ + Ki*ξ
+        Dt(V) ~ 1/C*(i_grid + i_source)
+    end
+end
+@named source = Source()
+source_vertex = VertexModel(source, [:i_grid], [:V];
+                            extin=[:V_load => VIndex(:load, :V)], vidx=1)
VertexModel :source PureStateMap() @ Vertex 1
+ ├─ 1 input:  [i_grid]
+ ├─ 2 states: [ξ=0, V=0.5]
+ ├─ 1 output: [V=0.5]
+ ├─ 4 params: [C=1, Ki=0.5, Vref=1, Kp=10]
+ └─ 1 ext in: [VIndex(:load, :V)]

Then we can create the network and simulate:

nw = Network([source_vertex, load_vertex], [resistor_edge])
+u0 = NWState(nw) # everything has default values
+prob = ODEProblem(nw, uflat(u0), (0,100), pflat(u0))
+sol = solve(prob, Tsit5())
+
+let
+    fig = Figure();
+    ax1 = Axis(fig[1,1]);
+    lines!(ax1, sol, idxs=VIndex(:load, :V), label="Voltage @ load");
+    lines!(ax1, sol, idxs=VPIndex(:source, :Vref), label="Reference", color=Cycled(2));
+    axislegend(ax1; position=:rb);
+    ax2 = Axis(fig[2,1]);
+    lines!(ax2, sol, idxs=VIndex(:load, :i_load), label="load current");
+    lines!(ax2, sol, idxs=VIndex(:source, :i_source), label="source current", color=Cycled(2));
+    axislegend(ax2);
+    fig
+end
Example block output

Using MTK for modeling, we can also inspect the currents i_load and i_source the MTK interface preserves the Observables.

diff --git a/previews/PR187/generated/cascading_failure/1378455c.svg b/previews/PR187/generated/cascading_failure/23f45b50.svg similarity index 95% rename from previews/PR187/generated/cascading_failure/1378455c.svg rename to previews/PR187/generated/cascading_failure/23f45b50.svg index 5353b3ae..b79e4b58 100644 --- a/previews/PR187/generated/cascading_failure/1378455c.svg +++ b/previews/PR187/generated/cascading_failure/23f45b50.svg @@ -1,62 +1,62 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/cascading_failure/index.html b/previews/PR187/generated/cascading_failure/index.html index d853c915..bcbd40bd 100644 --- a/previews/PR187/generated/cascading_failure/index.html +++ b/previews/PR187/generated/cascading_failure/index.html @@ -1,5 +1,5 @@ -Cascading Failure · NetworkDynamics

Cascading Failure

This script reimplements the minimal example of a dynamic cascading failure described in Schäfer et al. (2018) [1]. In is an example how to use callback functions to change network parameters. In this case to disable certain lines. This script can be dowloaded as a normal Julia script here.

[1] Schäfer, B., Witthaut, D., Timme, M., & Latora, V. (2018). Dynamically induced cascading failures in power grids. Nature communications, 9(1), 1-13. https://www.nature.com/articles/s41467-018-04287-5

The system is modeled using swing equation and active power edges. The nodes are characterized by the voltage angle δ, the active power on each line is symmetric and a function of the difference between source and destination angle δ_src - δ_dst.

using NetworkDynamics
+Cascading Failure · NetworkDynamics

Cascading Failure

This script reimplements the minimal example of a dynamic cascading failure described in Schäfer et al. (2018) [1]. In is an example how to use callback functions to change network parameters. In this case to disable certain lines. This script can be dowloaded as a normal Julia script here.

[1] Schäfer, B., Witthaut, D., Timme, M., & Latora, V. (2018). Dynamically induced cascading failures in power grids. Nature communications, 9(1), 1-13. https://www.nature.com/articles/s41467-018-04287-5

The system is modeled using swing equation and active power edges. The nodes are characterized by the voltage angle δ, the active power on each line is symmetric and a function of the difference between source and destination angle δ_src - δ_dst.

using NetworkDynamics
 using Graphs
 using OrdinaryDiffEqTsit5
 using DiffEqCallbacks
@@ -70,4 +70,4 @@
 Line 4 tripped at t=2.502523192233235
 Line 1 tripped at t=3.1947647115093654
 Line 3 tripped at t=3.3380530127462587
-Line 2 tripped at t=3.4042696241577888

Through the magic of symbolic indexing we can plot the power flows on all lines:

plot(sol; idxs=eidxs(sol,:,:P))
Example block output

This page was generated using Literate.jl.

+Line 2 tripped at t=3.4042696241577888

Through the magic of symbolic indexing we can plot the power flows on all lines:

plot(sol; idxs=eidxs(sol,:,:P))
Example block output

This page was generated using Literate.jl.

diff --git a/previews/PR187/generated/directed_and_weighted_graphs/5cb0671a.svg b/previews/PR187/generated/directed_and_weighted_graphs/2b7ec18c.svg similarity index 99% rename from previews/PR187/generated/directed_and_weighted_graphs/5cb0671a.svg rename to previews/PR187/generated/directed_and_weighted_graphs/2b7ec18c.svg index 84e64822..b7abfe9a 100644 --- a/previews/PR187/generated/directed_and_weighted_graphs/5cb0671a.svg +++ b/previews/PR187/generated/directed_and_weighted_graphs/2b7ec18c.svg @@ -1,132 +1,132 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/directed_and_weighted_graphs/index.html b/previews/PR187/generated/directed_and_weighted_graphs/index.html index 9f9320be..90c61680 100644 --- a/previews/PR187/generated/directed_and_weighted_graphs/index.html +++ b/previews/PR187/generated/directed_and_weighted_graphs/index.html @@ -1,5 +1,5 @@ -Directed and Weighted Graphs · NetworkDynamics

Neurodynamic model of synchronization in the human brain

This example can be dowloaded as a normal Julia script here.

Topics covered in this tutorial include:

  • constructing a directed, weighted graph from data
  • some useful macros
  • parameter handling
  • stiff equations

The FitzHugh-Nagumo model

Dynamics of spiking neurons have been described in a simplified manner by the FitzHugh-Nagumo model.

\[\begin{aligned} +Directed and Weighted Graphs · NetworkDynamics

Neurodynamic model of synchronization in the human brain

This example can be dowloaded as a normal Julia script here.

Topics covered in this tutorial include:

  • constructing a directed, weighted graph from data
  • some useful macros
  • parameter handling
  • stiff equations

The FitzHugh-Nagumo model

Dynamics of spiking neurons have been described in a simplified manner by the FitzHugh-Nagumo model.

\[\begin{aligned} \varepsilon \dot u & = u - \frac{u^3}{3} - v \\ \dot v & = u + a \end{aligned}\]

Here $u$ is a fast, excitatory variable corresponding to the membrane potential and $v$ is a slower, inhibitory varibale. $\varepsilon$ is a parameter separating these time-scales, and $a$ is a control parameter.

In simplified models of the brain, such relaxation oscillators may be used to model individual neurons, clusters of neurons or even larger areas in the brain. The FitzHugh-Nagumo model has been widely used for studying synchronization in neuronal activity, which in turn has been connected to physiological phenomena such as epileptic seizures.

Coupling relaxation oscillators

While different coupling schemes for FitzHugh-Nagumo oscillators have been proposed, in this tutorial we focus on coupling of the excitatory variables via electrical gap junctions, as described by the following system of equations.

\[\begin{aligned} @@ -45,4 +45,4 @@ Edge-Aggregation using SequentialAggregator(+)

Since this system is a directed one with thus directed edges, the keyword argument coupling is used to set the coupling of the edges to Directed().

Parameter handling

Some of the parameters have been declared with default values. Those default values will be used when creating the NWParameter object. We can use getindex on the parameter objects to set the missing weight values.

p = NWParameter(fhn_network!)
 p.e[1:ne(g_directed), :weight] = edge_weights

The initial conditions could be created similarly to the parameters as an indexable NWState obejct. Since we chose a random initial condition we initialize the flat array directly:

x0 = randn(StableRNG(42), dim(fhn_network!)) * 5

Solving the system

Now we are ready to create an ODEProblem. Since for some choices of parameters the FitzHugh-Nagumo model is stiff (i.e. numerically unstable), we use a solver with automated stiffness detection. Such a solver switches to a more stable solver only when the solution enters a region of phase space where the problem is numerically unstable. In this case we use Tsit5 and switch to TRBDF2 when necessary. AutoTsit5 is the switching version of the Tsit5 algorithm.

Not that we call pflat on the NWParameter object to get the flat array of parameters.

tspan = (0.0, 200.0)
 prob  = ODEProblem(fhn_network!, x0, tspan, pflat(p))
-sol = solve(prob, AutoTsit5(TRBDF2()));

Plotting

The plot of the excitatory variables shows that they synchronize for this choice of parameters.

plot(sol; idxs=vidxs(fhn_network!, :, :u), legend=false, ylim=(-5, 5), fmt=:png)
Example block output

This page was generated using Literate.jl.

+sol = solve(prob, AutoTsit5(TRBDF2()));

Plotting

The plot of the excitatory variables shows that they synchronize for this choice of parameters.

plot(sol; idxs=vidxs(fhn_network!, :, :u), legend=false, ylim=(-5, 5), fmt=:png)
Example block output

This page was generated using Literate.jl.

diff --git a/previews/PR187/generated/gas_network/index.html b/previews/PR187/generated/gas_network/index.html index df11359e..f29c864a 100644 --- a/previews/PR187/generated/gas_network/index.html +++ b/previews/PR187/generated/gas_network/index.html @@ -1,5 +1,5 @@ -Gas Network · NetworkDynamics

Dynamic Flow in simple Gas Network

This Example is based on the paper

Albertus J. Malan, Lukas Rausche, Felix Strehle, Sören Hohmann, Port-Hamiltonian Modelling for Analysis and Control of Gas Networks, IFAC-PapersOnLine, Volume 56, Issue 2, 2023, https://doi.org/10.1016/j.ifacol.2023.10.193.

and tries replicate a simple simulation of flow in a 3-node gas network.

This example can be dowloaded as a normal Julia script here.

We start by importing the necessary packages:

using NetworkDynamics
+Gas Network · NetworkDynamics

Dynamic Flow in simple Gas Network

This Example is based on the paper

Albertus J. Malan, Lukas Rausche, Felix Strehle, Sören Hohmann, Port-Hamiltonian Modelling for Analysis and Control of Gas Networks, IFAC-PapersOnLine, Volume 56, Issue 2, 2023, https://doi.org/10.1016/j.ifacol.2023.10.193.

and tries replicate a simple simulation of flow in a 3-node gas network.

This example can be dowloaded as a normal Julia script here.

We start by importing the necessary packages:

using NetworkDynamics
 using ModelingToolkit
 using DynamicQuantities
 using ModelingToolkit: D as Dt, t as t
@@ -221,4 +221,4 @@
     end
     axislegend(ax, position=:rb)
     _fig
-end
Example block output

This page was generated using Literate.jl.

+end
Example block output

This page was generated using Literate.jl.

diff --git a/previews/PR187/generated/getting_started_with_network_dynamics/2a845b2e.svg b/previews/PR187/generated/getting_started_with_network_dynamics/2a845b2e.svg new file mode 100644 index 00000000..4b6dceeb --- /dev/null +++ b/previews/PR187/generated/getting_started_with_network_dynamics/2a845b2e.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/getting_started_with_network_dynamics/2ed0631e.svg b/previews/PR187/generated/getting_started_with_network_dynamics/2ed0631e.svg new file mode 100644 index 00000000..8b4cd40a --- /dev/null +++ b/previews/PR187/generated/getting_started_with_network_dynamics/2ed0631e.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/getting_started_with_network_dynamics/7dbdac8e.svg b/previews/PR187/generated/getting_started_with_network_dynamics/7dbdac8e.svg deleted file mode 100644 index 574286b2..00000000 --- a/previews/PR187/generated/getting_started_with_network_dynamics/7dbdac8e.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/previews/PR187/generated/getting_started_with_network_dynamics/9722a0c8.svg b/previews/PR187/generated/getting_started_with_network_dynamics/9722a0c8.svg deleted file mode 100644 index 737fc02b..00000000 --- a/previews/PR187/generated/getting_started_with_network_dynamics/9722a0c8.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/previews/PR187/generated/getting_started_with_network_dynamics/9a1b9e8c.svg b/previews/PR187/generated/getting_started_with_network_dynamics/9a1b9e8c.svg deleted file mode 100644 index 917ccad8..00000000 --- a/previews/PR187/generated/getting_started_with_network_dynamics/9a1b9e8c.svg +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/previews/PR187/generated/getting_started_with_network_dynamics/a7f82e43.svg b/previews/PR187/generated/getting_started_with_network_dynamics/a7f82e43.svg new file mode 100644 index 00000000..8b9c834e --- /dev/null +++ b/previews/PR187/generated/getting_started_with_network_dynamics/a7f82e43.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/getting_started_with_network_dynamics/index.html b/previews/PR187/generated/getting_started_with_network_dynamics/index.html index 01f75214..5aa58c90 100644 --- a/previews/PR187/generated/getting_started_with_network_dynamics/index.html +++ b/previews/PR187/generated/getting_started_with_network_dynamics/index.html @@ -1,5 +1,5 @@ -Getting Started · NetworkDynamics

Network Diffusion

This introductory example explains the use of the basic types and constructors in NetworkDynamics.jl by modeling a simple diffusion on an undirected network.

This example can be dowloaded as a normal Julia script here.

Theoretical background

Diffusion processes are relevant for phenomena as diverse as heat conduction, electrical currents, and random walks. Generally speaking they describe the tendency of systems to evolve towards a state of equally distributed heat, charge or concentration. In such system the local temperature (or concentration) changes according to its difference with its neighborhood, i.e. the temperature gradient.

Let $g$ be a graph with $N$ nodes and adjacency matrix $A$. Let $v = (v_1, \dots, v_n)$ be a vector of (abstract) temperatures or concentrations at each node $i = 1, \dots, N$. Then the rate of change of state $v_i$ is described by its difference with its neighbors and we obtain the following ordinary differential equation

\[\dot v_i = \sum_{j=1}^N A_{ji} (v_j - v_i).\]

The sum on the right hand side plays the role of a (discrete) gradient. If the temperature at node $i$ is higher than at its neighboring node $j$ it will decrease along that edge.

Modeling diffusion in NetworkDynamics.jl

We begin by loading the necessary packages.

using Graphs
+Getting Started · NetworkDynamics

Network Diffusion

This introductory example explains the use of the basic types and constructors in NetworkDynamics.jl by modeling a simple diffusion on an undirected network.

This example can be dowloaded as a normal Julia script here.

Theoretical background

Diffusion processes are relevant for phenomena as diverse as heat conduction, electrical currents, and random walks. Generally speaking they describe the tendency of systems to evolve towards a state of equally distributed heat, charge or concentration. In such system the local temperature (or concentration) changes according to its difference with its neighborhood, i.e. the temperature gradient.

Let $g$ be a graph with $N$ nodes and adjacency matrix $A$. Let $v = (v_1, \dots, v_n)$ be a vector of (abstract) temperatures or concentrations at each node $i = 1, \dots, N$. Then the rate of change of state $v_i$ is described by its difference with its neighbors and we obtain the following ordinary differential equation

\[\dot v_i = \sum_{j=1}^N A_{ji} (v_j - v_i).\]

The sum on the right hand side plays the role of a (discrete) gradient. If the temperature at node $i$ is higher than at its neighboring node $j$ it will decrease along that edge.

Modeling diffusion in NetworkDynamics.jl

We begin by loading the necessary packages.

using Graphs
 using NetworkDynamics
 using OrdinaryDiffEqTsit5
 using StableRNGs
@@ -29,7 +29,7 @@
 Edge-Aggregation using SequentialAggregator(+)

The constructor Network combines the component model with the topological information contained in the graph g and returns an Network compatible with the solvers of DifferentialEquations.jl.

rng = StableRNG(1)
 x0 = randn(rng, N) # random initial conditions
 ode_prob = ODEProblem(nd, x0, (0.0, 2.0))
-sol = solve(ode_prob, Tsit5());

We are solving the diffusion problem on the time interval $[0, 2]$ with the Tsit5() algorithm, which is recommended by the authors of DifferentialEquations.jl for most non-stiff problems.

plot(sol; idxs=vidxs(nd, :, :), fmt=:png)
Example block output

The plotting is straightforward. The idxs keyword allows us to pass a list of indices. Indices can be also "symbolic" indices which specify components and their symbols directly. For example idxs = VIndex(1, :v) acesses state :v of vertex 1. See Symbolic Indexing for more details.

In oder to collect multiple indices we can use the helper function vidxs and eidxs, which help to collect all symbolic indices matching a certain criteria.

Two Dimensional Extension

To illustrate a very simple multi-dimensional case, in the following we simulate two independent diffusions on an identical graph. The first uses the symbol x and is started with initial conditions drawn from the standard normal distribution $N(0,1)$, the second uses the symbol ϕ with squared standard normal inital conditions.

The symbols have to be passed with the keyword sym to VertexModel.

N = 10 # number of nodes
+sol = solve(ode_prob, Tsit5());

We are solving the diffusion problem on the time interval $[0, 2]$ with the Tsit5() algorithm, which is recommended by the authors of DifferentialEquations.jl for most non-stiff problems.

plot(sol; idxs=vidxs(nd, :, :), fmt=:png)
Example block output

The plotting is straightforward. The idxs keyword allows us to pass a list of indices. Indices can be also "symbolic" indices which specify components and their symbols directly. For example idxs = VIndex(1, :v) acesses state :v of vertex 1. See Symbolic Indexing for more details.

In oder to collect multiple indices we can use the helper function vidxs and eidxs, which help to collect all symbolic indices matching a certain criteria.

Two Dimensional Extension

To illustrate a very simple multi-dimensional case, in the following we simulate two independent diffusions on an identical graph. The first uses the symbol x and is started with initial conditions drawn from the standard normal distribution $N(0,1)$, the second uses the symbol ϕ with squared standard normal inital conditions.

The symbols have to be passed with the keyword sym to VertexModel.

N = 10 # number of nodes
 k = 4  # average degree
 g = barabasi_albert(N, k) # a little more exciting than a bare random graph
 
@@ -40,5 +40,4 @@
 
 x0_2 = vec(transpose([randn(rng, N) .^ 2 randn(rng, N)])) # x ~ N(0,1)^2; ϕ ~ N(0,1)
 ode_prob_2 = ODEProblem(nd_2, x0_2, (0.0, 3.0))
-sol_2 = solve(ode_prob_2, Tsit5());
┌ Warning: Encountered MethodError. All arguments are AbstractArrays, make sure to allways index into them: MethodError(Main.diffusionvertex_f!, (AccessTracker([0.08508015693341575, 0.7348492123524052]), AccessTracker([0.3080209018531822, 0.27594788720624586]), AccessTracker([0.5313227021336236, 0.7848358810223603]), AccessTracker(Float64[]), NaN, AccessTracker(Float64[])), 0x0000000000006a39)
-@ NetworkDynamics ~/work/NetworkDynamics.jl/NetworkDynamics.jl/src/doctor.jl:109

Try plotting the variables ϕ_i yourself. [To write ϕ type \phi and press TAB]

plot(sol_2; idxs=vidxs(nd_2, :, :x), fmt=:png)
Example block output

Using the eidxs helper function we can also plot the flow variables

plot(sol_2; idxs=eidxs(nd_2, :, :flow_x), fmt=:png)
Example block output

Appendix: The network Laplacian $L$

The diffusion equation on a network can be rewritten as

\[\dot v_i = \sum_{j=1}^N A_{ji} v_j - d_i v_i = e_i^T A v - d_i v_i\]

where $d_i$ is the degree of node $i$ and $e_i^T$ is the $i$-th standard basis vector. Introducing the diagonal matrix $D$ that has the degree of node $i$ in its $i$-th row and the Laplacian matrix $L = D - A$ we arrive at

\[\dot v = e_i^T(A - D) v\]

and finally

\[\dot v = - L v\]

This is a linear system of ODEs and its solution is a matrix exponential. To study the asymptotic behaviour of the system it suffices to analyze the eigenspectrum of $L$. For this reason $L$ is an important construction in network science.


This page was generated using Literate.jl.

+sol_2 = solve(ode_prob_2, Tsit5());

Try plotting the variables ϕ_i yourself. [To write ϕ type \phi and press TAB]

plot(sol_2; idxs=vidxs(nd_2, :, :x), fmt=:png)
Example block output

Using the eidxs helper function we can also plot the flow variables

plot(sol_2; idxs=eidxs(nd_2, :, :flow_x), fmt=:png)
Example block output

Appendix: The network Laplacian $L$

The diffusion equation on a network can be rewritten as

\[\dot v_i = \sum_{j=1}^N A_{ji} v_j - d_i v_i = e_i^T A v - d_i v_i\]

where $d_i$ is the degree of node $i$ and $e_i^T$ is the $i$-th standard basis vector. Introducing the diagonal matrix $D$ that has the degree of node $i$ in its $i$-th row and the Laplacian matrix $L = D - A$ we arrive at

\[\dot v = e_i^T(A - D) v\]

and finally

\[\dot v = - L v\]

This is a linear system of ODEs and its solution is a matrix exponential. To study the asymptotic behaviour of the system it suffices to analyze the eigenspectrum of $L$. For this reason $L$ is an important construction in network science.


This page was generated using Literate.jl.

diff --git a/previews/PR187/generated/heterogeneous_system/15b1e6ae.svg b/previews/PR187/generated/heterogeneous_system/1072b158.svg similarity index 96% rename from previews/PR187/generated/heterogeneous_system/15b1e6ae.svg rename to previews/PR187/generated/heterogeneous_system/1072b158.svg index af9fabbf..3757bc28 100644 --- a/previews/PR187/generated/heterogeneous_system/15b1e6ae.svg +++ b/previews/PR187/generated/heterogeneous_system/1072b158.svg @@ -1,66 +1,66 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/heterogeneous_system/34114752.svg b/previews/PR187/generated/heterogeneous_system/8d2a76b5.svg similarity index 95% rename from previews/PR187/generated/heterogeneous_system/34114752.svg rename to previews/PR187/generated/heterogeneous_system/8d2a76b5.svg index 0da7b926..3548cadb 100644 --- a/previews/PR187/generated/heterogeneous_system/34114752.svg +++ b/previews/PR187/generated/heterogeneous_system/8d2a76b5.svg @@ -1,62 +1,62 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/heterogeneous_system/88e9f89d.svg b/previews/PR187/generated/heterogeneous_system/f085c15d.svg similarity index 95% rename from previews/PR187/generated/heterogeneous_system/88e9f89d.svg rename to previews/PR187/generated/heterogeneous_system/f085c15d.svg index 95140e3b..7bac5e00 100644 --- a/previews/PR187/generated/heterogeneous_system/88e9f89d.svg +++ b/previews/PR187/generated/heterogeneous_system/f085c15d.svg @@ -1,62 +1,62 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/generated/heterogeneous_system/index.html b/previews/PR187/generated/heterogeneous_system/index.html index d6d3e684..8ff0c799 100644 --- a/previews/PR187/generated/heterogeneous_system/index.html +++ b/previews/PR187/generated/heterogeneous_system/index.html @@ -1,5 +1,5 @@ -Heterogeneous Systems · NetworkDynamics

Modeling a heterogeneous system

This example can be dowloaded as a normal Julia script here.

One of the main purposes of NetworkDynamics.jl is to facilitate modeling coupled systems with heterogenities. This means that components can differ in their parameters as well as in their dynamics.

Heterogenous parameters

We start by setting up a simple system of Kuramoto oscillators.

using NetworkDynamics, OrdinaryDiffEqTsit5, Plots, Graphs
+Heterogeneous Systems · NetworkDynamics

Modeling a heterogeneous system

This example can be dowloaded as a normal Julia script here.

One of the main purposes of NetworkDynamics.jl is to facilitate modeling coupled systems with heterogenities. This means that components can differ in their parameters as well as in their dynamics.

Heterogenous parameters

We start by setting up a simple system of Kuramoto oscillators.

using NetworkDynamics, OrdinaryDiffEqTsit5, Plots, Graphs
 
 N = 8
 g = watts_strogatz(N, 2, 0) # ring network
@@ -44,7 +44,7 @@
 tspan = (0.0, 10.0)
 prob = ODEProblem(nw, x0, tspan, pflat(p))
 sol = solve(prob, Tsit5())
-plot(sol; ylabel="θ", fmt=:png)
Example block output

Heterogeneous dynamics

Two paradigmatic modifications of the node model above are static nodes and nodes with inertia. A static node has no internal states and instead fixes the variable at a constant value.

function static_g(out, u, p, t)
+plot(sol; ylabel="θ", fmt=:png)
Example block output

Heterogeneous dynamics

Two paradigmatic modifications of the node model above are static nodes and nodes with inertia. A static node has no internal states and instead fixes the variable at a constant value.

function static_g(out, u, p, t)
     out[1] = p[1]
     nothing
 end
@@ -82,7 +82,7 @@
  p = NWParameter([-0.4375, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0])
  t = nothing

The node with inertia is two-dimensional, hence we need to specify two initial conditions. For the first dimension we keep the initial conditions from above and insert! another one into x0 at the correct index.

For the θ states we will use the same initial conditins as before:

state.v[2:8,:θ] = x0[2:8]

We're still missing one initial condition: the second variable ω of the 5th vertex.

state.v[5,:ω] = 5

The NWState object also contains a parameter object accessible via state.p. The edge parameters are already filled with default values. The vertex parameters can be copied from our old parmeter object p.

state.p.v[2:8, :ω0] = p.v[2:8, :ω0]

For the problem construction, we need to convert the nested stuctures to flat arrays using the uflat and pflat methods.

prob_hetero = ODEProblem(nw_hetero!, uflat(state), tspan, pflat(state))
 sol_hetero = solve(prob_hetero, Tsit5());
-plot(sol_hetero)
Example block output

For clarity we plot only the variables referring to the oscillator's angle θ and color them according to their type.

colors = map(vertex_array) do vertexf
+plot(sol_hetero)
Example block output

For clarity we plot only the variables referring to the oscillator's angle θ and color them according to their type.

colors = map(vertex_array) do vertexf
     if vertexf.name == :kuramoto
         colorant"lightseagreen"
     elseif vertexf.name == :static
@@ -92,4 +92,4 @@
     end
 end
 
-plot(sol_hetero; ylabel="θ", idxs=vidxs(1:8,:θ), lc=colors', fmt=:png)
Example block output

This page was generated using Literate.jl.

+plot(sol_hetero; ylabel="θ", idxs=vidxs(1:8,:θ), lc=colors', fmt=:png)
Example block output

This page was generated using Literate.jl.

diff --git a/previews/PR187/generated/stress_on_truss/index.html b/previews/PR187/generated/stress_on_truss/index.html index a06c24f0..56fd4f61 100644 --- a/previews/PR187/generated/stress_on_truss/index.html +++ b/previews/PR187/generated/stress_on_truss/index.html @@ -1,5 +1,5 @@ -Stress on Truss · NetworkDynamics

Stress on Truss

In this exampe we'll simulate the time evolution of a truss structure consisting of joints and stiff springs. This example can be dowloaded as a normal Julia script here.

The mathematical model is quite simple: the vertices are point masses with positions $x$ and $y$ and velocities $v_x$ and $v_y$. For simplicity, we add some damping to the nodal motion based on the velocity.

\[\begin{aligned} +Stress on Truss · NetworkDynamics

Stress on Truss

In this exampe we'll simulate the time evolution of a truss structure consisting of joints and stiff springs. This example can be dowloaded as a normal Julia script here.

The mathematical model is quite simple: the vertices are point masses with positions $x$ and $y$ and velocities $v_x$ and $v_y$. For simplicity, we add some damping to the nodal motion based on the velocity.

\[\begin{aligned} \dot{x} &= v_x\\ \dot{y} &= v_y\\ \dot{v}_x &= \frac{1}{M}\left(\sum F_x - γ\,v_x\right)\\ @@ -129,4 +129,4 @@ p.edge_color[] = load p.elabels = [@sprintf("%.0f", l) for l in load] fig -end

"truss.mp4"

This page was generated using Literate.jl.

+end
"truss.mp4"

This page was generated using Literate.jl.

diff --git a/previews/PR187/index.html b/previews/PR187/index.html index 95989bee..94328b93 100644 --- a/previews/PR187/index.html +++ b/previews/PR187/index.html @@ -1,5 +1,5 @@ -General · NetworkDynamics

NetworkDynamics

A package for working with dynamical systems on complex networks. NetworkDynamics.jl provides an interface between Graphs.jl and DifferentialEquations.jl. It allows for high performant simulation of dynamic networks by describing the local dynamics on the edges and vertices of the graph.

The behavior of a node or an edge can be described by algebraic equations, by differential algebraic equation (DAEs) in mass matrix form or by ordinary differential equations (ODE).

The central construction is the function Network that receives functions describing the local dynamics on the edges and nodes of a graph g as inputs, and returns a composite function compatible with the DifferentialEquations.jl calling syntax.

nd = Network(g, vertex_dynamics,  edge_dynamics)
+General · NetworkDynamics

NetworkDynamics

A package for working with dynamical systems on complex networks. NetworkDynamics.jl provides an interface between Graphs.jl and DifferentialEquations.jl. It allows for high performant simulation of dynamic networks by describing the local dynamics on the edges and vertices of the graph.

The behavior of a node or an edge can be described by algebraic equations, by differential algebraic equation (DAEs) in mass matrix form or by ordinary differential equations (ODE).

The central construction is the function Network that receives functions describing the local dynamics on the edges and nodes of a graph g as inputs, and returns a composite function compatible with the DifferentialEquations.jl calling syntax.

nd = Network(g, vertex_dynamics,  edge_dynamics)
 nd(dx, x, p, t)

Main features:

  • Clear separation of local dynamics and topology: you can easily change topology of your system or switching out dynamical components.
  • High performance when working with heterogeneous models (which means heaving different local dynamics in different parts of your network).
  • Symbolic Indexing into solutions and states: NetworkDynamics keeps track of the states of the individual subsystems.
  • Different execution schemes: NetworkDynamics exploits the known inter-dependencies between components to auto parallelize execution, even on GPUs!
  • Equation based models: use ModelingToolkit.jl to define local dynamics, use NetworkDynamics.jl to combine them into large networks!

Where to begin?

Check out the Mathematical Model to understand the underlying modelling ideas of NetworkDynamics followed by the page on Network Construction to learn how to implement you own models.

If you prefer to look at some concrete code first check out the Getting Started tutorial!

Installation

Installation is straightforward with Julia's package manager.

(v1.11) pkg> add NetworkDynamics

Reproducibility

Direct dependencies used for this documentation:
Status `~/work/NetworkDynamics.jl/NetworkDynamics.jl/docs/Project.toml`
   [13f3f980] CairoMakie v0.12.16
   [459566f4] DiffEqCallbacks v4.2.2
@@ -11,7 +11,7 @@
   [b964fa9f] LaTeXStrings v1.4.0
  [b20c7882] LinearInterpolations v0.1.4
   [98b081ad] Literate v2.20.1
-  [961ee093] ModelingToolkit v9.53.0
+  [961ee093] ModelingToolkit v9.54.0
   [22e9dc34] NetworkDynamics v0.9.2 `~/work/NetworkDynamics.jl/NetworkDynamics.jl`
   [127b3ac7] OrdinaryDiffEqNonlinearSolve v1.2.4
   [43230ef6] OrdinaryDiffEqRosenbrock v1.3.1
@@ -108,7 +108,7 @@
   [7c1d4256] DynamicPolynomials v0.6.1
   [06fc5a27] DynamicQuantities v1.4.0
   [4e289a0a] EnumX v1.0.4
-  [f151be2c] EnzymeCore v0.8.6
+  [f151be2c] EnzymeCore v0.8.7
   [429591f6] ExactPredicates v2.2.8
   [460bff9d] ExceptionUnwrapping v0.1.11
   [d4d017d3] ExponentialUtilities v1.27.0
@@ -134,7 +134,7 @@
   [663a7486] FreeTypeAbstraction v0.10.6
   [069b7b12] FunctionWrappers v1.1.3
   [77dc65aa] FunctionWrappersWrappers v0.1.3
-  [d9f16b24] Functors v0.5.1
+  [d9f16b24] Functors v0.5.2
  [46192b85] GPUArraysCore v0.1.6
   [28b8d3ca] GR v0.73.8
   [c145ed77] GenericSchur v0.5.4
@@ -184,7 +184,7 @@
   [23fbe1c1] Latexify v0.16.5
   [10f19ff3] LayoutPointers v0.1.17
   [0e77f7df] LazilyInitializedFields v1.3.0
-  [5078a376] LazyArrays v2.2.2
+  [5078a376] LazyArrays v2.2.3
   [8cdb02fc] LazyModules v0.3.1
   [2d8b4e74] LevyArea v1.0.0
   [87fe0de2] LineSearch v0.1.4
@@ -208,7 +208,7 @@
   [442fdcdd] Measures v0.3.2
   [e1d29d7a] Missings v1.2.0
   [2a8e4939] Mixers v0.1.2
-  [961ee093] ModelingToolkit v9.53.0
+  [961ee093] ModelingToolkit v9.54.0
   [e94cdb99] MosaicViews v0.3.4
   [46d2c3a1] MuladdMacro v0.2.4
   [102ac46a] MultivariatePolynomials v0.5.7
@@ -226,7 +226,7 @@
   [52e1d378] OpenEXR v0.3.3
   [4d8831e6] OpenSSL v1.4.3
   [429524aa] Optim v1.10.0
-  [bac558e1] OrderedCollections v1.6.3
+  [bac558e1] OrderedCollections v1.7.0
   [1dea7af3] OrdinaryDiffEq v6.90.1
   [89bda076] OrdinaryDiffEqAdamsBashforthMoulton v1.1.0
   [6ad6398a] OrdinaryDiffEqBDF v1.1.2
@@ -518,4 +518,4 @@
   [8e850b90] libblastrampoline_jll v5.11.0+0
   [8e850ede] nghttp2_jll v1.59.0+0
   [3f19e933] p7zip_jll v17.4.0+2
-Info Packages marked with  and  have new versions available. Those with  may be upgradable, but those with  are restricted by compatibility constraints from upgrading. To see why use `status --outdated -m`

Funding

Development of this project was in part funded by the German Federal Ministry for Economic Affairs and Climate Action as part of the OpPoDyn-Project (Project ID 01258425/1, 2024-2027).

+Info Packages marked with and have new versions available. Those with may be upgradable, but those with are restricted by compatibility constraints from upgrading. To see why use `status --outdated -m`

Funding

Development of this project was in part funded by the German Federal Ministry for Economic Affairs and Climate Action as part of the OpPoDyn-Project (Project ID 01258425/1, 2024-2027).

diff --git a/previews/PR187/initialization/index.html b/previews/PR187/initialization/index.html index 35ae2843..7ad7e2a4 100644 --- a/previews/PR187/initialization/index.html +++ b/previews/PR187/initialization/index.html @@ -1,5 +1,5 @@ -Initialization · NetworkDynamics

Initialization

Initialization of the system describes the process of finding valid initial conditions, mostly a fixpoint of the system. We distinguish between two types of initialization: full system initialziation and component initialization.

Full-System Initialization

Full system initialization describs the process of finding a fixpoint/steady state of th entire system.

To do so, you can use find_fixpoint, which creates a SteadyStateProblem of the whole network and tries do solve it.

Component-wise Initialization

In contrast to full-system initialization the goal of component-wise initialization is to find a valid initial condition for a single component first, given a network coupling.

This can be usefull in cases, where there are nontrivial internal dynamics and states within a single vertex or edge. The idea of component-wise initialisation is to find internal states which match a given "network coupling" (fixed inputs and outputs).

Lets consider the following example of a Swing-equation generator model.

using NetworkDynamics, ModelingToolkit
+Initialization · NetworkDynamics

Initialization

Initialization of the system describes the process of finding valid initial conditions, mostly a fixpoint of the system. We distinguish between two types of initialization: full system initialziation and component initialization.

Full-System Initialization

Full system initialization describs the process of finding a fixpoint/steady state of th entire system.

To do so, you can use find_fixpoint, which creates a SteadyStateProblem of the whole network and tries do solve it.

Component-wise Initialization

In contrast to full-system initialization the goal of component-wise initialization is to find a valid initial condition for a single component first, given a network coupling.

This can be usefull in cases, where there are nontrivial internal dynamics and states within a single vertex or edge. The idea of component-wise initialisation is to find internal states which match a given "network coupling" (fixed inputs and outputs).

Lets consider the following example of a Swing-equation generator model.

using NetworkDynamics, ModelingToolkit
 using ModelingToolkit: t_nounits as t, D_nounits as Dt
 
 @mtkmodel Swing begin
@@ -37,4 +37,4 @@
  ├─ 2 inputs:  [i_r=1, i_i=0.1]
  ├─ 2 states:  [θ=0.099669, ω=3.4499e-23]
  ├─ 2 outputs: [u_r=1, u_i=0.1]
- └─ 5 params:  [M=0.005, Pm=1.01, V=1.005, D=0.1, ω_ref=0]

Which lead to a successfull initialization of states and as well as parameter :Pm. To retrieve the residual you can use init_residual.

As a quick test we can ensure that the angle indeed matches the voltag angel:

get_init(vf, :θ) ≈ atan(get_default(vf, :u_i), get_default(vf, :u_r))
true
+ └─ 5 params: [M=0.005, Pm=1.01, V=1.005, D=0.1, ω_ref=0]

Which lead to a successfull initialization of states and as well as parameter :Pm. To retrieve the residual you can use init_residual.

As a quick test we can ensure that the angle indeed matches the voltag angel:

get_init(vf, :θ) ≈ atan(get_default(vf, :u_i), get_default(vf, :u_r))
true
diff --git a/previews/PR187/mathematical_model/index.html b/previews/PR187/mathematical_model/index.html index 5280a343..7e9789cb 100644 --- a/previews/PR187/mathematical_model/index.html +++ b/previews/PR187/mathematical_model/index.html @@ -1,5 +1,5 @@ -Mathematical Model · NetworkDynamics

Mathematical Model

The basic mathematical model of NetworkDynamics.jl splits up the system it two parts: vertex and edge components.

The main goal of NetworkDynamics.jl is, to express the overall network dynamics as a Differential-Algebraic-Equation (DAE)

\[M\,\frac{\mathrm{d}}{\mathrm{d}t}u = f^{\mathrm{nw}}(u, p, t)\]

where M is a (possibly singular) mass matrix, $u$ is the internal state vector of the system, $p$ are the parameters and $t$ is the time. To make this compatible with the solvers for OrdinaryDiffEq.jl, the created Network object is a callable object

nw(du, u, p, t) # mutates du

with stored mass matrix information to build an ODEProblem based on the Network.

Instead of defining $f^{\mathrm{nw}}$ by hand, ND.jl helps you to build it automatically based on a list of decentralized nodal and edge dynamics, so-called VertexModel and EdgeModel objects. Each component model $\mathrm c$ is modeled as general input-output-system

\[\begin{aligned} +Mathematical Model · NetworkDynamics

Mathematical Model

The basic mathematical model of NetworkDynamics.jl splits up the system it two parts: vertex and edge components.

The main goal of NetworkDynamics.jl is, to express the overall network dynamics as a Differential-Algebraic-Equation (DAE)

\[M\,\frac{\mathrm{d}}{\mathrm{d}t}u = f^{\mathrm{nw}}(u, p, t)\]

where M is a (possibly singular) mass matrix, $u$ is the internal state vector of the system, $p$ are the parameters and $t$ is the time. To make this compatible with the solvers for OrdinaryDiffEq.jl, the created Network object is a callable object

nw(du, u, p, t) # mutates du

with stored mass matrix information to build an ODEProblem based on the Network.

Instead of defining $f^{\mathrm{nw}}$ by hand, ND.jl helps you to build it automatically based on a list of decentralized nodal and edge dynamics, so-called VertexModel and EdgeModel objects. Each component model $\mathrm c$ is modeled as general input-output-system

\[\begin{aligned} M_{\mathrm c}\,\frac{\mathrm{d}}{\mathrm{d}t}x_{\mathrm c} &= f^{\mathrm c}(x^{\mathrm c}, i_{\mathrm c}, p_{\mathrm c}, t)\\ y^{\mathrm c} &= g^{\mathrm c}(x^\mathrm{c}, i_{\mathrm c}, p_{\mathrm c}, t) \end{aligned}\]

where $M_{\mathrm{c}}$ is the component mass matrix, $x^{\mathrm c}$ are the component states, $i^{\mathrm c}$ are the inputs of the component and $y^{\mathrm c}$ is the output of the component. It is possible to have $\mathrm{dim}(x^{\mathrm{c}}) = 0$ and thus no internal states.

In the network context, the output of the edges are flow variables. The outputs of vertices are potential variables. In interconnection, the flow on the edges depends on the potentials at both ends as inputs. The potentials of the nodes depend on the incoming flows from all connected edges as an input. (Here, flow and potentials are meant in a conceptional and not necessarily physical way.)

Vertex Models

Specifically, a (single-layer) vertex model has one input, and one output. The input is an aggregation/reduction over all incident edge outputs,

\[i^{\mathrm v} = \mathop{\mathrm{agg}}\limits_k^{\text{incident}} y^{\mathrm e}_k \qquad\text{often}\qquad @@ -45,4 +45,4 @@ g!(v_out, x, p, t) # single layer vertex

PureStateMap()

g!(outs...,          x) # abstractly
 g!(out_dst,          x) # single-sided edge
 g!(out_src, out_dst, x) # double-sided edge
-g!(v_out,            x) # single layer vertex
+g!(v_out, x) # single layer vertex
diff --git a/previews/PR187/metadata/index.html b/previews/PR187/metadata/index.html index b9c09689..de72b2fa 100644 --- a/previews/PR187/metadata/index.html +++ b/previews/PR187/metadata/index.html @@ -1,2 +1,2 @@ -Metadata · NetworkDynamics

Metadata

Component model such as VertexModel and EdgeModel can store metadata. We distinguish between two kinds of metadata: component metadata and symbol metadata.

Component Metadata

Component metadata is a Dict{Symbol,Any} attached to each component to store various information. Use metadata to retrieve the full dict.

To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Component Metadata API).

Special metadata:

  • :init_residual: after Component-wise Initialization, this field stores the residual vector of the nonlinear problem.
  • :graphelement: optional field to specialize the graphelement for each component (vidx) for vertices, (;src,dst) named tuple of either vertex names or vertex indices for edges. Has special accessors has_/get_/set_graphelement.

Symbol Metadata

Each component stores symbol metadata. The symbol metadata is a Dict{Symbol, Dict{Symbol, Any}} which stores a metadate dict per symbol. Symbols are everything that appears in sym, psym, obssym and insym.

To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Per Symbol Metadata API).

Special cases for symbol metadata are:

  • default: Stores default values for states/parameters. In initialization, those are considered fixed.
  • guess: Stores a guess for a state/parameter which needs to solved during initialization ("free" variables).
  • bounds: Store bounds for variables/parameters
  • init: Stores the solution of the "free" variables during initialization.

Fore those, there are special functions has_*, get_* and set_*!. See Per Symbol Metadata API.

Those are closely aligned to the metadata use in ModelingToolkit. They are automatically copied from the ODESystem if you use MTK models to create NetworkDynamics models.

+Metadata · NetworkDynamics

Metadata

Component model such as VertexModel and EdgeModel can store metadata. We distinguish between two kinds of metadata: component metadata and symbol metadata.

Component Metadata

Component metadata is a Dict{Symbol,Any} attached to each component to store various information. Use metadata to retrieve the full dict.

To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Component Metadata API).

Special metadata:

  • :init_residual: after Component-wise Initialization, this field stores the residual vector of the nonlinear problem.
  • :graphelement: optional field to specialize the graphelement for each component (vidx) for vertices, (;src,dst) named tuple of either vertex names or vertex indices for edges. Has special accessors has_/get_/set_graphelement.

Symbol Metadata

Each component stores symbol metadata. The symbol metadata is a Dict{Symbol, Dict{Symbol, Any}} which stores a metadate dict per symbol. Symbols are everything that appears in sym, psym, obssym and insym.

To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Per Symbol Metadata API).

Special cases for symbol metadata are:

  • default: Stores default values for states/parameters. In initialization, those are considered fixed.
  • guess: Stores a guess for a state/parameter which needs to solved during initialization ("free" variables).
  • bounds: Store bounds for variables/parameters
  • init: Stores the solution of the "free" variables during initialization.

Fore those, there are special functions has_*, get_* and set_*!. See Per Symbol Metadata API.

Those are closely aligned to the metadata use in ModelingToolkit. They are automatically copied from the ODESystem if you use MTK models to create NetworkDynamics models.

diff --git a/previews/PR187/mtk_integration/index.html b/previews/PR187/mtk_integration/index.html index 61beca9b..11c3d51e 100644 --- a/previews/PR187/mtk_integration/index.html +++ b/previews/PR187/mtk_integration/index.html @@ -1,5 +1,5 @@ -ModelingToolkit Integration · NetworkDynamics

ModelingToolkit Integration

NetworkDynamics.jl is compatible with ModelingTookit.jl (MTK). The general idea is to use MTK to define component models (i.e. edge and vertex dynamics) which are then connected on network scale using NetworkDynamics.

The main entry point for this interop are the constructors

VertexModel(::ODESystem, inputs, outputs)
+ModelingToolkit Integration · NetworkDynamics

ModelingToolkit Integration

NetworkDynamics.jl is compatible with ModelingTookit.jl (MTK). The general idea is to use MTK to define component models (i.e. edge and vertex dynamics) which are then connected on network scale using NetworkDynamics.

The main entry point for this interop are the constructors

VertexModel(::ODESystem, inputs, outputs)
 EdgeModel(::ODESystem, srcin, dstin, [srscout], dstout)

whose docstrings can be found in the Component Models with MTK section in the API.

These constructors will:

  • transforming the states marked as input to parameters and structural_simplifying the system,
  • generating the f and g functions,
  • generate code for observables,
  • port all supported Metadata from MTK symbols to component symbols and
  • output a Vertex-/EdgeModel function compatible with NetworkDynamics.jl.

The main usecase for this feature is when you want to build relatively complex component models but interconnect them in a very homogeneous way (i.e. having the same output/input pairings in the whole system).

In theory, you can achieve everything you want to do with plain MTK. The idea of combining the two is, that NetworkDynamics offers far less flexibility when in comes to interconnection of subsystems on the network level. This might allow ND to exploit more knowledge of the structure without very expensive operations such as tearing of thousands of equations.

Warning

ModelingToolkit is a fast paced library with lots of functionality and ever growing complexity. As such the provided interface is kinda experimental. Some features of MTK are straight up unsupported, for example events within models or delay differential equations.

RC-Circuit Example

In good MTK tradition, this feature will be explained along a simple RC circuit example. The Dynamic Flow in simple Gas Network example is another showcase of the MTK constructors.

The system to model is 2 node, 1 edge network. The node output states are the voltage (to ground), the edge output sates are the currents at both ends.


 ideal v source      Resistor     Capacitor
             v1 o─←────MMM────→─o v2
@@ -81,4 +81,4 @@
 axislegend(ax1)
 ax2 = Axis(fig[2,1])
 plot!(ax2, sol; idxs=VIndex(:vs, :i), label="Current produced by ideal v source")
-axislegend(ax2)
Example block output
+axislegend(ax2)
Example block output
diff --git a/previews/PR187/network_construction/index.html b/previews/PR187/network_construction/index.html index 9dc4bcde..c9d9ea61 100644 --- a/previews/PR187/network_construction/index.html +++ b/previews/PR187/network_construction/index.html @@ -1,5 +1,5 @@ -Network Construction · NetworkDynamics

Network Construction

Building a Network

The main type of NetworkDyanmics.jl is a Network. A network bundles various component models (edge and vertex models) together with a graph to form a callable object which represents the RHS of the overall dynamical system, see Mathematical Model.

A Network is build by passing a graph g, vertex models vertexm and edge models edgem.

nw = Network(g, vertexm, edgem; kwargs...)

Two important keywords for the Network constructor are:

  • execution: Defines the ExecutionStyle of the coreloop, e.g. SequentialExecution{true}(). A execution style is a special struct which tells the backend how to parallelize for example. A list of available executions styles can be found under Execution Types in the API.

  • aggregator: Tells the backend how to aggregate and which aggregation function to use. Aggregation is the process of creating a single vertex input by reducing over the outputs of adjecent edges of said vertex. The aggregator contains both the function and the algorith. E.g. SequentialAggregator(+) is a sequential aggregation by summation. A list of availabe Aggregators can be found under Aggregators in the API.

Building VertexModels

This chapter walks through the most important aspects when defining custom vertex model. For a list of all keyword arguments please check out the docstring of VertexModel. As an example, we'll construct an second order kuramoto model, because that's what we do.

function kuramoto_f!(dv, v, esum, p, t)
+Network Construction · NetworkDynamics

Network Construction

Building a Network

The main type of NetworkDyanmics.jl is a Network. A network bundles various component models (edge and vertex models) together with a graph to form a callable object which represents the RHS of the overall dynamical system, see Mathematical Model.

A Network is build by passing a graph g, vertex models vertexm and edge models edgem.

nw = Network(g, vertexm, edgem; kwargs...)

Two important keywords for the Network constructor are:

  • execution: Defines the ExecutionStyle of the coreloop, e.g. SequentialExecution{true}(). A execution style is a special struct which tells the backend how to parallelize for example. A list of available executions styles can be found under Execution Types in the API.

  • aggregator: Tells the backend how to aggregate and which aggregation function to use. Aggregation is the process of creating a single vertex input by reducing over the outputs of adjecent edges of said vertex. The aggregator contains both the function and the algorith. E.g. SequentialAggregator(+) is a sequential aggregation by summation. A list of availabe Aggregators can be found under Aggregators in the API.

Building VertexModels

This chapter walks through the most important aspects when defining custom vertex model. For a list of all keyword arguments please check out the docstring of VertexModel. As an example, we'll construct an second order kuramoto model, because that's what we do.

function kuramoto_f!(dv, v, esum, p, t)
     M, P, D = p
     dv[1] = v[2]
     dv[2] = (P - D*v[2] + esum[1])/M
@@ -58,4 +58,4 @@
  ├─ 1/1 inputs:  src=[src₊θ] dst=[dst₊θ]
  ├─   0 states:  []  
  ├─ 1/1 outputs: src=[₋P] dst=[P]
- └─   1 param:   [K=1]
+ └─ 1 param: [K=1]
diff --git a/previews/PR187/objects.inv b/previews/PR187/objects.inv index 86279a8c..b48c656d 100644 Binary files a/previews/PR187/objects.inv and b/previews/PR187/objects.inv differ diff --git a/previews/PR187/search_index.js b/previews/PR187/search_index.js index f60c051c..dc70fb73 100644 --- a/previews/PR187/search_index.js +++ b/previews/PR187/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"mtk_integration/#ModelingToolkit-Integration","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"","category":"section"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"NetworkDynamics.jl is compatible with ModelingTookit.jl (MTK). The general idea is to use MTK to define component models (i.e. edge and vertex dynamics) which are then connected on network scale using NetworkDynamics.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"The main entry point for this interop are the constructors","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"VertexModel(::ODESystem, inputs, outputs)\nEdgeModel(::ODESystem, srcin, dstin, [srscout], dstout)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"whose docstrings can be found in the Component Models with MTK section in the API.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"These constructors will:","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"transforming the states marked as input to parameters and structural_simplifying the system,\ngenerating the f and g functions,\ngenerate code for observables,\nport all supported Metadata from MTK symbols to component symbols and\noutput a Vertex-/EdgeModel function compatible with NetworkDynamics.jl.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"The main usecase for this feature is when you want to build relatively complex component models but interconnect them in a very homogeneous way (i.e. having the same output/input pairings in the whole system).","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"In theory, you can achieve everything you want to do with plain MTK. The idea of combining the two is, that NetworkDynamics offers far less flexibility when in comes to interconnection of subsystems on the network level. This might allow ND to exploit more knowledge of the structure without very expensive operations such as tearing of thousands of equations.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"warning: Warning\nModelingToolkit is a fast paced library with lots of functionality and ever growing complexity. As such the provided interface is kinda experimental. Some features of MTK are straight up unsupported, for example events within models or delay differential equations.","category":"page"},{"location":"mtk_integration/#RC-Circuit-Example","page":"ModelingToolkit Integration","title":"RC-Circuit Example","text":"","category":"section"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"In good MTK tradition, this feature will be explained along a simple RC circuit example. The Dynamic Flow in simple Gas Network example is another showcase of the MTK constructors.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"The system to model is 2 node, 1 edge network. The node output states are the voltage (to ground), the edge output sates are the currents at both ends.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"\nideal v source Resistor Capacitor\n v1 o─←────MMM────→─o v2\n │ ┴ \n (↗) ┬\n │ │\n ⏚ ⏚","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"Obviously there is no need in modeling such a small system using NetworkDynamics, however the method extends quite easily to construct large electrical networks reusing the same fundamental building blocks.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"using NetworkDynamics\nusing ModelingToolkit\nusing ModelingToolkit: t_nounits as t, D_nounits as D\nusing OrdinaryDiffEqTsit5\nusing CairoMakie","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"All our components have \"terminals\", which have a voltage and current. We don't use the @connector from MTK here because our pins mark the interface towards the network and do not follow the MTK connector semantics.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel NWTerminal begin\n @variables begin\n v(t), [description=\"Voltage at node\"]\n i(t), [description=\"Current flowing into node\"]\n end\nend\nnothing #hide","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"An ideal voltage source is just a model which pins its output voltage to a fixed parameter. 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 mechanism. The VertexModel can be generated from an ODESystem by providing names of the input and output states:","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel VoltageSource begin\n @components begin\n p = NWTerminal()\n end\n @parameters begin\n V = 1.0\n end\n @variables begin\n i(t), [description=\"produced current by ideal voltage source (observable)\"]\n end\n @equations begin\n i ~ -p.i\n p.v ~ V\n end\nend\n@named vs = VoltageSource()\nvs_vertex = VertexModel(vs, [:p₊i], [:p₊v]; vidx=1)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"A capacitor is a slightly more complicated model. Its voltage is defined as an differential equation based on the inflowing current.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel Capacitor begin\n @components begin\n p = NWTerminal(;v=0)\n end\n @parameters begin\n C = 1.0\n end\n @equations begin\n D(p.v) ~ p.i / C\n end\nend\n@named cap = Capacitor()\ncap_vertex = VertexModel(cap, [:p₊i], [:p₊v], vidx=2)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"For the resistor we need two pins, one for the src and one for the dst side. The equations are straight forward.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel Resistor begin\n @components begin\n src = NWTerminal()\n dst = NWTerminal()\n end\n @parameters begin\n R = 1.0\n end\n @equations begin\n dst.i ~ (src.v - dst.v)/R\n src.i ~ -dst.i\n end\nend\n@named resistor = Resistor()\nresistor_edge = EdgeModel(resistor, [:src₊v], [:dst₊v], [:src₊i], [:dst₊i]; src=:vs, dst=:cap)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"Having all those components defined, we can build the network. We don't need to provide a graph object here because we specified the placement in the graph on a per component basis.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"nw = Network([vs_vertex, cap_vertex], [resistor_edge])","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"We can see, that NetworkDynamics internally is able to reduce all of the \"output\" states. We end up with a plain ODE of a single state.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"Now we can simulate the system. For that we generate the u0 object. Since the metadata (such as default values) was automatically transferred, we can straight away construct the ODEProblem and solve the system.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"u0 = NWState(nw) # generate state based on default values\nprob = ODEProblem(nw, uflat(u0), (0, 10.0), pflat(u0))\nsol = solve(prob, Tsit5())\n\n# plot the solution\nfig, ax1, p = plot(sol; idxs=VIndex(:cap, :p₊v), label=\"Capacitor Voltage\");\naxislegend(ax1)\nax2 = Axis(fig[2,1])\nplot!(ax2, sol; idxs=VIndex(:vs, :i), label=\"Current produced by ideal v source\")\naxislegend(ax2)\nfig # hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"EditURL = \"../../examples/getting_started_with_network_dynamics.jl\"","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Getting-Started","page":"Getting Started","title":"Network Diffusion","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This introductory example explains the use of the basic types and constructors in NetworkDynamics.jl by modeling a simple diffusion on an undirected network.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Theoretical-background","page":"Getting Started","title":"Theoretical background","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Diffusion processes are relevant for phenomena as diverse as heat conduction, electrical currents, and random walks. Generally speaking they describe the tendency of systems to evolve towards a state of equally distributed heat, charge or concentration. In such system the local temperature (or concentration) changes according to its difference with its neighborhood, i.e. the temperature gradient.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Let g be a graph with N nodes and adjacency matrix A. Let v = (v_1 dots v_n) be a vector of (abstract) temperatures or concentrations at each node i = 1 dots N. Then the rate of change of state v_i is described by its difference with its neighbors and we obtain the following ordinary differential equation","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v_i = sum_j=1^N A_ji (v_j - v_i)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The sum on the right hand side plays the role of a (discrete) gradient. If the temperature at node i is higher than at its neighboring node j it will decrease along that edge.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Modeling-diffusion-in-NetworkDynamics.jl","page":"Getting Started","title":"Modeling diffusion in NetworkDynamics.jl","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"We begin by loading the necessary packages.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"using Graphs\nusing NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing StableRNGs\nusing Plots\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"From the above considerations we see that in this model the nodes do not have any internal dynamics - if a node was disconnected from the rest of the network its state would never change, since then A_ji = 0 forall j and hence dot v_i = 0. This means that the evolution of a node depends only on the interaction with its neighbors. In NetworkDynamics.jl, interactions with neighbors are described by equations for the edges.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"In order to bring this equation into the form required by NetworkDynamics.jl we need split the dynamics into edge and vertex parts and bring them into the correct input-output formulation. The vertices have one internal state v which is also the output. The input is the sum over all flows of connected edges. This directly correspons to the component model definition outlined in Mathematical Model:","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"beginaligned\ndot x^mathrmv = f^mathrm v(u^mathrm v sum_k^textincident y^mathrm e_k p^mathrm v t) = sum_k^mathrmincident y^mathrme_k \ny^mathrmv = g^mathrm v(u^mathrm v sum_k^textincident y^mathrm e_k p^mathrm v t) = x^mathrmv\nendaligned","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The edge dynamics on the other hand do not have any internal states. Thus we only define the output as the difference between the source and destination vertex:","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"beginaligned\ny^mathrm e_mathrmdst = g_mathrmdst^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t) = y^mathrm v_mathrmsrc - y^mathrm v_mathrmdst\ny^mathrm e_mathrmsrc = g_mathrmsrc^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t) = y^mathrm v_mathrmdst - y^mathrm v_mathrmsrc\nendaligned","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Definition-of-EdgeModel","page":"Getting Started","title":"Definition of EdgeModel","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"function diffusionedge_g!(e_dst, v_src, v_dst, p, t)\n # e_dst, v_src, v_dst are arrays, hence we use the broadcasting operator\n e_dst .= v_src .- v_dst\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The function diffusionedge_g! takes as inputs the current state of the edge e, its source vertex v_src, its destination vertex v_dst, a vector of parameters p and the time t. In order to comply with the syntax of NetworkDynamics.jl we always have to define functions for edges with exactly these arguments, even though we do not need p and t for the diffusion example.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"diffusionedge_g! is called a mutating function, since it modifies (or mutates) one of its inputs, namely the edge state e. As a convention in Julia names of mutating functions end with an !. The use of mutating functions reduces allocations and thereby speeds up computations. After the function call the edge's output value e equals the difference between its source and its destination vertex (i.e. the discrete gradient along that edge).","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Notably, this function only models g_mathrmdst. However we can wrap this single-sided output function in an AntiSymmetric output wrapper to construct the EdgeModel:","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"nd_diffusion_edge = EdgeModel(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow])","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Definition-of-VertexModel","page":"Getting Started","title":"Definition of VertexModel","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"For undirected graphs, the edgefunction! specifies the coupling from a source- to a destination vertex. The contributions of the connected edges to a single vertex are \"aggregated\". Default aggregation is the summation of all incident edge states. The aggregated edge state is made available via the esum argument of the vertex function.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"function diffusionvertex_f!(dv, v, esum, p, t)\n # dv, v and esum are arrays, hence we use the broadcasting operator .\n dv .= esum\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Just like above the input arguments v, esum, p, t are mandatory for the syntax of vertex functions. The additional input dv corresponding to the derivative of the vertex' state is mandatory for vertices described by ordinary differential equations.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The output function g is just taking part of the internal states. For that we can use the StateMask helper function g = StateMaks(1:1)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"nd_diffusion_vertex = VertexModel(; f=diffusionvertex_f!, g=StateMask(1:1), dim=1)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Constructing-the-network","page":"Getting Started","title":"Constructing the network","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"With the components defined, we can define the topology and assemble the network dynamics.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"N = 20 # number of nodes\nk = 4 # average degree\ng = barabasi_albert(N, k) # a little more exciting than a bare random graph\n\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The Barabási–Albert model generates a scale-free random graph.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"nd = Network(g, nd_diffusion_vertex, nd_diffusion_edge)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The constructor Network combines the component model with the topological information contained in the graph g and returns an Network compatible with the solvers of DifferentialEquations.jl.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"rng = StableRNG(1)\nx0 = randn(rng, N) # random initial conditions\node_prob = ODEProblem(nd, x0, (0.0, 2.0))\nsol = solve(ode_prob, Tsit5());\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"We are solving the diffusion problem on the time interval 0 2 with the Tsit5() algorithm, which is recommended by the authors of DifferentialEquations.jl for most non-stiff problems.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"plot(sol; idxs=vidxs(nd, :, :), fmt=:png)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The plotting is straightforward. The idxs keyword allows us to pass a list of indices. Indices can be also \"symbolic\" indices which specify components and their symbols directly. For example idxs = VIndex(1, :v) acesses state :v of vertex 1. See Symbolic Indexing for more details.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"In oder to collect multiple indices we can use the helper function vidxs and eidxs, which help to collect all symbolic indices matching a certain criteria.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Two-Dimensional-Extension","page":"Getting Started","title":"Two Dimensional Extension","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"To illustrate a very simple multi-dimensional case, in the following we simulate two independent diffusions on an identical graph. The first uses the symbol x and is started with initial conditions drawn from the standard normal distribution N(01), the second uses the symbol ϕ with squared standard normal inital conditions.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The symbols have to be passed with the keyword sym to VertexModel.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"N = 10 # number of nodes\nk = 4 # average degree\ng = barabasi_albert(N, k) # a little more exciting than a bare random graph\n\n# We will have two independent diffusions on the network, hence dim = 2\nnd_diffusion_vertex_2 = VertexModel(; f=diffusionvertex_f!, g=1:2, dim=2, sym=[:x, :ϕ])\nnd_diffusion_edge_2 = EdgeModel(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow_x, :flow_ϕ])\nnd_2 = Network(g, nd_diffusion_vertex_2, nd_diffusion_edge_2)\n\nx0_2 = vec(transpose([randn(rng, N) .^ 2 randn(rng, N)])) # x ~ N(0,1)^2; ϕ ~ N(0,1)\node_prob_2 = ODEProblem(nd_2, x0_2, (0.0, 3.0))\nsol_2 = solve(ode_prob_2, Tsit5());\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Try plotting the variables ϕ_i yourself. [To write ϕ type \\phi and press TAB]","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"plot(sol_2; idxs=vidxs(nd_2, :, :x), fmt=:png)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Using the eidxs helper function we can also plot the flow variables","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"plot(sol_2; idxs=eidxs(nd_2, :, :flow_x), fmt=:png)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Appendix:-The-network-Laplacian-L","page":"Getting Started","title":"Appendix: The network Laplacian L","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The diffusion equation on a network can be rewritten as","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v_i = sum_j=1^N A_ji v_j - d_i v_i = e_i^T A v - d_i v_i","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"where d_i is the degree of node i and e_i^T is the i-th standard basis vector. Introducing the diagonal matrix D that has the degree of node i in its i-th row and the Laplacian matrix L = D - A we arrive at","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v = e_i^T(A - D) v","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"and finally","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v = - L v","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This is a linear system of ODEs and its solution is a matrix exponential. To study the asymptotic behaviour of the system it suffices to analyze the eigenspectrum of L. For this reason L is an important construction in network science.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"EditURL = \"../../examples/stress_on_truss.jl\"","category":"page"},{"location":"generated/stress_on_truss/#Stress-on-Truss","page":"Stress on Truss","title":"Stress on Truss","text":"","category":"section"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"In this exampe we'll simulate the time evolution of a truss structure consisting of joints and stiff springs. This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"(Image: truss animation)","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"The mathematical model is quite simple: the vertices are point masses with positions x and y and velocities v_x and v_y. For simplicity, we add some damping to the nodal motion based on the velocity.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"beginaligned\ndotx = v_x\ndoty = v_y\ndotv_x = frac1Mleft(sum F_x - γv_xright)\ndotv_y = frac1Mleft(sum F_y - γv_yright) - g\nendaligned","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"The vertices cannot absorb any torque, so the beams only exert forces in the direction of the beam.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"F = Kcdot(L - Δd)","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"where L is the nominal lenth and Δd is the actual length of the beam.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"We start by loading the necessary packages.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"using NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing Graphs\nusing GraphMakie\nusing LinearAlgebra: norm\nusing Printf\nusing CairoMakie\nCairoMakie.activate!()","category":"page"},{"location":"generated/stress_on_truss/#Definition-of-the-dynamical-system","page":"Stress on Truss","title":"Definition of the dynamical system","text":"","category":"section"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"We need 3 models:","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"a fixed vertex which cannot change its position,\na free vertex which can move, and\na beam which connects two vertices.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"function fixed_g(pos, x, p, t)\n pos .= p\nend\nvertex_fix = VertexModel(g=fixed_g, psym=[:xfix, :yfix], outsym=[:x, :y], ff=NoFeedForward())","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Here we need to specify the ff keyword manually, because NetworkDynamics cannot distinguish between g(out, x, p, t) (NoFeedForwarwd) and g(out, in, p, t) (PureFeedForward()) and guesses the latter when mathrmdim(x)=0.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"function free_f(dx, x, Fsum, (M, γ, g), t)\n v = view(x, 1:2)\n dx[1:2] .= (Fsum .- γ .* v) ./ M\n dx[2] -= g\n dx[3:4] .= v\n nothing\nend\nvertex_free = VertexModel(f=free_f, g=3:4, sym=[:vx=>0, :vy=>0, :x, :y],\n psym=[:M=>10, :γ=>200, :g=>9.81], insym=[:Fx, :Fy])","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"For the edge, we want to do something special. Later on, we want to color the edges according to the force they exert. Therefore, are interested in the absolut force rather than just the force vector. NetworkDynamics allows you to define so called Observed functions, which can recover additional states, so called observed, after the simulations. We can use this mechanis, to define a \"recipe\" for calculating the beam force based on the inputs (nodal positions) and the beam parameters.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"function edge_g!(F, pos_src, pos_dst, (K, L), t)\n dx = pos_dst[1] - pos_src[1]\n dy = pos_dst[2] - pos_src[2]\n d = sqrt(dx^2 + dy^2)\n Fabs = K * (L - d)\n F[1] = Fabs * dx / d\n F[2] = Fabs * dy / d\n nothing\nend\nfunction observedf(obsout, u, pos_src, pos_dst, (K, L), t)\n dx = pos_dst[1] .- pos_src[1]\n dy = pos_dst[2] .- pos_src[2]\n d = sqrt(dx^2 + dy^2)\n obsout[1] = K * (L - d)\n nothing\nend\nbeam = EdgeModel(g=AntiSymmetric(edge_g!), psym=[:K=>0.5e6, :L], outsym=[:Fx, :Fy], obsf=observedf, obssym=[:Fabs])","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"With the models define we can set up graph topology and initial positions.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"N = 5\ndx = 1.0\nshift = 0.2\ng = SimpleGraph(2*N + 1)\nfor i in 1:N\n add_edge!(g, i, i+N); add_edge!(g, i, i+N)\n if i < N\n add_edge!(g, i+1, i+N); add_edge!(g, i, i+1); add_edge!(g, i+N, i+N+1)\n end\nend\nadd_edge!(g, 2N, 2N+1)\npos0 = zeros(Point2f, 2N + 1)\npos0[1:N] = [Point((i-1)dx,0) for i in 1:N]\npos0[N+1:2*N] = [Point(i*dx + shift, 1) for i in 1:N]\npos0[2N+1] = Point(N*dx + 1, -1)\nfixed = [1,4] # set fixed vertices\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Now can collect the vertex models and construct the Network object.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"verts = VertexModel[vertex_free for i in 1:nv(g)]\nfor i in fixed\n verts[i] = vertex_fix # use the fixed vertex for the fixed points\nend\nnw = Network(g, verts, beam)","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"In order to simulate the system we need to initialize the state and parameter vectors. Some states and parameters are shared between all vertices/edges. Those have been allready set in their constructors. The free symbols are","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"x and y for the position of the free vertices,\nxfix and yfix for the position of the fixed vertices,\nL for the nominal length of the beams.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"s = NWState(nw)\n# set x/y and xfix/yfix\nfor i in eachindex(pos0, verts)\n if i in fixed\n s.p.v[i, :xfix] = pos0[i][1]\n s.p.v[i, :yfix] = pos0[i][2]\n else\n s.v[i, :x] = pos0[i][1]\n s.v[i, :y] = pos0[i][2]\n end\nend\n# set L for edges\nfor (i,e) in enumerate(edges(g))\n s.p.e[i, :L] = norm(pos0[src(e)] - pos0[dst(e)])\nend\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Lastly there is a special vertex at the end of the truss which has a higher mass and reduced damping.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"s.p.v[11, :M] = 200\ns.p.v[11, :γ] = 100\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"No we have everything ready to build the ODEProblem and simulate the system.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"tspan = (0.0, 12.0)\nprob = ODEProblem(nw, uflat(s), tspan, pflat(s))\nsol = solve(prob, Tsit5())\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/#Plot-the-solution","page":"Stress on Truss","title":"Plot the solution","text":"","category":"section"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Plotting trajectories of points is kinda boring. So instead we're going to use GraphMakie.jl to create a animation of the timeseries.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"fig = Figure(size=(1000,550));\nfig[1,1] = title = Label(fig, \"Stress on truss\", fontsize=30)\ntitle.tellwidth = false\n\nfig[2,1] = ax = Axis(fig)\nax.aspect = DataAspect();\nhidespines!(ax); # no borders\nhidedecorations!(ax); # no grid, axis, ...\nlimits!(ax, -0.1, pos0[end][1]+0.3, pos0[end][2]-0.5, 1.15) # axis limits to show full plot\n\n# get the maximum force during the simulation to get the color scale\n# It is only possible to access `:Fabs` directly becaus we've define the observable function for it!\n(fmin, fmax) = 0.3 .* extrema(Iterators.flatten(sol(sol.t, idxs=eidxs(nw, :, :Fabs))))\np = graphplot!(ax, g;\n edge_width = 4.0,\n node_size = 3*sqrt.(try s.p.v[i, :M] catch; 10.0 end for i in 1:nv(g)),\n nlabels = [i in fixed ? \"Δ\" : \"\" for i in 1:nv(g)],\n nlabels_align = (:center,:top),\n nlabels_fontsize = 30,\n elabels = [\"edge $i\" for i in 1:ne(g)],\n elabels_side = Dict(ne(g) => :right),\n edge_color = [0.0 for i in 1:ne(g)],\n edge_attr = (colorrange=(fmin,fmax),\n colormap=:diverging_bkr_55_10_c35_n256))\n\n# draw colorbar\nfig[3,1] = cb = Colorbar(fig, get_edge_plot(p), label = \"Axial force\", vertical=false)\n\nT = tspan[2]\nfps = 30\ntrange = range(0.0, sol.t[end], length=Int(T * fps))\nrecord(fig, \"truss.mp4\", trange; framerate=fps) do t\n title.text = @sprintf \"Stress on truss (t = %.2f )\" t\n s_at_t = NWState(sol, t)\n for i in eachindex(pos0)\n p[:node_pos][][i] = (s_at_t.v[i, :x], s_at_t.v[i, :y])\n end\n notify(p[:node_pos])\n load = s_at_t.e[:, :Fabs]\n p.edge_color[] = load\n p.elabels = [@sprintf(\"%.0f\", l) for l in load]\n fig\nend","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"EditURL = \"../../examples/directed_and_weighted_graphs.jl\"","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Neurodynamic-model-of-synchronization-in-the-human-brain","page":"Directed and Weighted Graphs","title":"Neurodynamic model of synchronization in the human brain","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Topics-covered-in-this-tutorial-include:","page":"Directed and Weighted Graphs","title":"Topics covered in this tutorial include:","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"constructing a directed, weighted graph from data\nsome useful macros\nparameter handling\nstiff equations","category":"page"},{"location":"generated/directed_and_weighted_graphs/#The-FitzHugh-Nagumo-model","page":"Directed and Weighted Graphs","title":"The FitzHugh-Nagumo model","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Dynamics of spiking neurons have been described in a simplified manner by the FitzHugh-Nagumo model.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"beginaligned\nvarepsilon dot u = u - fracu^33 - v \ndot v = u + a\nendaligned","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Here u is a fast, excitatory variable corresponding to the membrane potential and v is a slower, inhibitory varibale. varepsilon is a parameter separating these time-scales, and a is a control parameter.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"In simplified models of the brain, such relaxation oscillators may be used to model individual neurons, clusters of neurons or even larger areas in the brain. The FitzHugh-Nagumo model has been widely used for studying synchronization in neuronal activity, which in turn has been connected to physiological phenomena such as epileptic seizures.","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Coupling-relaxation-oscillators","page":"Directed and Weighted Graphs","title":"Coupling relaxation oscillators","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"While different coupling schemes for FitzHugh-Nagumo oscillators have been proposed, in this tutorial we focus on coupling of the excitatory variables via electrical gap junctions, as described by the following system of equations.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"beginaligned\nvarepsilon dot u_i = u_i - fracu_i^33 - v_i - sigma sum_j=1^N G_ij(u_i - u_j) \ndot v_i = u_i + a\nendaligned","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"This is a simple diffusive coupling mediated by the difference between activation potentials in pairs of neurons. A similar coupling term was introduced in the \"getting started\" tutorial.","category":"page"},{"location":"generated/directed_and_weighted_graphs/#The-network-topology-a-brain-atlas","page":"Directed and Weighted Graphs","title":"The network topology - a brain atlas","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"In the following we will use a directed and weighted network encoding the strength and directionality of coupling between 90 different areas of the brain [Nathalie Tzourio-Mazoyer et al., 2002, Neuroimage].","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The network weight matrix is given as a text file containing 90 lines with 90 numbers representing the coupling strength and separated by commas ,. The data can be conveniently read into a matrix with the DelimitedFiles module.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"using DelimitedFiles\nusing SimpleWeightedGraphs, Graphs\nusing NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing OrdinaryDiffEqSDIRK\nusing StableRNGs\nusing Plots\n\n# adjust the load path for your filesystem!\nfile = joinpath(pkgdir(NetworkDynamics), \"docs\", \"examples\", \"Norm_G_DTI.txt\")\nG = readdlm(file, ',', Float64, '\\n')\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The data structure for directed, weighted graphs is provided by the package SimpleWeightedGraphs.jl which is based on Graphs.jl.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"# First we construct a weighted, directed graph\ng_weighted = SimpleWeightedDiGraph(G)\n\n# For later use we extract the edge.weight attributes\n# . is the broadcasting operator and gets the attribute :weight for every edge\nedge_weights = getfield.(collect(edges(g_weighted)), :weight)\n\n# we promote the g_weighted graph as a directed graph (weights of the edges are included in parameters)\ng_directed = SimpleDiGraph(g_weighted)\n\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Setting-up-the-ODEProblem","page":"Directed and Weighted Graphs","title":"Setting up the ODEProblem","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Defining the VertexModel and EdgeModel is similar to the example before. The macro Base.@propagate_inbounds tells the compiler to inline the function and propagate the inbounds context. For more details see the julia documentation.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Base.@propagate_inbounds function fhn_electrical_vertex!(dv, v, esum, p, t)\n (a, ϵ) = p\n dv[1] = v[1] - v[1]^3 / 3 - v[2] + esum[1]\n dv[2] = (v[1] - a) * ϵ\n nothing\nend\nvertex = VertexModel(f=fhn_electrical_vertex!, g=1, sym=[:u, :v], psym=[:a=>0.5, :ϵ=>0.05])","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Base.@propagate_inbounds function electrical_edge!(e, v_s, v_d, (w, σ), t)\n e[1] = w * (v_s[1] - v_d[1]) * σ\n nothing\nend\nelectricaledge = EdgeModel(g=Directed(electrical_edge!), outdim=1, psym=[:weight, :σ=>0.5])","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"fhn_network! = Network(g_directed, vertex, electricaledge)","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Since this system is a directed one with thus directed edges, the keyword argument coupling is used to set the coupling of the edges to Directed().","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Parameter-handling","page":"Directed and Weighted Graphs","title":"Parameter handling","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Some of the parameters have been declared with default values. Those default values will be used when creating the NWParameter object. We can use getindex on the parameter objects to set the missing weight values.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"p = NWParameter(fhn_network!)\np.e[1:ne(g_directed), :weight] = edge_weights\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The initial conditions could be created similarly to the parameters as an indexable NWState obejct. Since we chose a random initial condition we initialize the flat array directly:","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"x0 = randn(StableRNG(42), dim(fhn_network!)) * 5\n\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Solving-the-system","page":"Directed and Weighted Graphs","title":"Solving the system","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Now we are ready to create an ODEProblem. Since for some choices of parameters the FitzHugh-Nagumo model is stiff (i.e. numerically unstable), we use a solver with automated stiffness detection. Such a solver switches to a more stable solver only when the solution enters a region of phase space where the problem is numerically unstable. In this case we use Tsit5 and switch to TRBDF2 when necessary. AutoTsit5 is the switching version of the Tsit5 algorithm.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Not that we call pflat on the NWParameter object to get the flat array of parameters.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"tspan = (0.0, 200.0)\nprob = ODEProblem(fhn_network!, x0, tspan, pflat(p))\nsol = solve(prob, AutoTsit5(TRBDF2()));\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Plotting","page":"Directed and Weighted Graphs","title":"Plotting","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The plot of the excitatory variables shows that they synchronize for this choice of parameters.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"plot(sol; idxs=vidxs(fhn_network!, :, :u), legend=false, ylim=(-5, 5), fmt=:png)","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"This page was generated using Literate.jl.","category":"page"},{"location":"initialization/#Initialization","page":"Initialization","title":"Initialization","text":"","category":"section"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Initialization of the system describes the process of finding valid initial conditions, mostly a fixpoint of the system. We distinguish between two types of initialization: full system initialziation and component initialization.","category":"page"},{"location":"initialization/#Full-System-Initialization","page":"Initialization","title":"Full-System Initialization","text":"","category":"section"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Full system initialization describs the process of finding a fixpoint/steady state of th entire system.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"To do so, you can use find_fixpoint, which creates a SteadyStateProblem of the whole network and tries do solve it. ","category":"page"},{"location":"initialization/#Component-wise-Initialization","page":"Initialization","title":"Component-wise Initialization","text":"","category":"section"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"In contrast to full-system initialization the goal of component-wise initialization is to find a valid initial condition for a single component first, given a network coupling.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"This can be usefull in cases, where there are nontrivial internal dynamics and states within a single vertex or edge. The idea of component-wise initialisation is to find internal states which match a given \"network coupling\" (fixed inputs and outputs).","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Lets consider the following example of a Swing-equation generator model.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"using NetworkDynamics, ModelingToolkit\nusing ModelingToolkit: t_nounits as t, D_nounits as Dt\n\n@mtkmodel Swing begin\n @variables begin\n u_r(t)=1, [description=\"bus d-voltage\", output=true]\n u_i(t)=0.1, [description=\"bus q-voltage\", output=true]\n i_r(t)=1, [description=\"bus d-current (flowing into bus)\", input=true]\n i_i(t)=0.1, [description=\"bus d-current (flowing into bus)\", input=true]\n ω(t), [guess=0.0, description=\"Rotor frequency\"]\n θ(t), [guess=0.0, description=\"Rotor angle\"]\n Pel(t), [guess=1, description=\"Electrical Power injected into the grid\"]\n end\n @parameters begin\n M=0.005, [description=\"Inertia\"]\n D=0.1, [description=\"Damping\"]\n V=sqrt(u_r^2 + u_i^2), [description=\"Voltage magnitude\"]\n ω_ref=0, [description=\"Reference frequency\"]\n Pm, [guess=0.1,description=\"Mechanical Power\"]\n end\n @equations begin\n Dt(θ) ~ ω - ω_ref\n Dt(ω) ~ 1/M * (Pm - D*ω - Pel)\n Pel ~ u_r*i_r + u_i*i_i\n u_r ~ V*cos(θ)\n u_i ~ V*sin(θ)\n end\nend\nsys = Swing(name=:swing)\nvf = VertexModel(sys, [:i_r, :i_i], [:u_r, :u_i])","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"You can see in the provided metadata, that we've set default values for the node outputs u_r, u_i, the node inputs i_r, i_i and most parameters. For some states and parameters, we've onlye provided a guess rather than a default. Variables which only have guesses are considered \"tunable\" for the initialization algorithm.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"In order to initialize the remaining variables we use initialize_component!, which is a mutating function which tries to solve the nonlinear initialization problem and store the found values for the \"free\" variables as init metadata.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"initialize_component!(vf; verbose=true)\nnothing #hide","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"vf #hide","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Which lead to a successfull initialization of states :θ and :ω as well as parameter :Pm. To retrieve the residual you can use init_residual.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"As a quick test we can ensure that the angle indeed matches the voltag angel:","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"get_init(vf, :θ) ≈ atan(get_default(vf, :u_i), get_default(vf, :u_r))","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"EditURL = \"../../examples/heterogeneous_system.jl\"","category":"page"},{"location":"generated/heterogeneous_system/#Modeling-a-heterogeneous-system","page":"Heterogeneous Systems","title":"Modeling a heterogeneous system","text":"","category":"section"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"One of the main purposes of NetworkDynamics.jl is to facilitate modeling coupled systems with heterogenities. This means that components can differ in their parameters as well as in their dynamics.","category":"page"},{"location":"generated/heterogeneous_system/#Heterogenous-parameters","page":"Heterogeneous Systems","title":"Heterogenous parameters","text":"","category":"section"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"We start by setting up a simple system of Kuramoto oscillators.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"using NetworkDynamics, OrdinaryDiffEqTsit5, Plots, Graphs\n\nN = 8\ng = watts_strogatz(N, 2, 0) # ring network\n\nfunction kuramoto_edge!(e, θ_s, θ_d, (K,), t)\n e[1] = K * sin(θ_s[1] - θ_d[1])\n nothing\nend\nedge! = EdgeModel(g=AntiSymmetric(kuramoto_edge!), outdim=1, psym=[:K=>3])","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"function kuramoto_vertex!(dθ, θ, esum, (ω0,), t)\n dθ[1] = ω0 + esum[1]\n nothing\nend\nvertex! = VertexModel(f=kuramoto_vertex!, g=StateMask(1:1), sym=[:θ], psym=[:ω0], name=:kuramoto)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"nw = Network(g, vertex!, edge!)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"To assign parameters, we can create a NWParameter object based on the nw definition. This parameter object will be pre-filled with the default parameters.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"p = NWParameter(nw)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"To set the vertex parameters, we can use indexing of the p.v field:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"ω = collect(1:N) ./ N\nω .-= sum(ω) / N\np.v[:, :ω0] = ω\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Here, the index pairing :, :ω is used to index state ω for all node indices.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"The parameter object contains information about the network structure. For the actual problem definition we need to throw away this wrapper and use the flat-vector representation of the parameters pflat(p). Note that pflat(p)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Similarily, we could use NWState(nw) to create an indexable wrapper of the initial state. However in this case we can also fill create the flat state array manually:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"x0 = collect(1:N) ./ N\nx0 .-= sum(x0) ./ N\ntspan = (0.0, 10.0)\nprob = ODEProblem(nw, x0, tspan, pflat(p))\nsol = solve(prob, Tsit5())\nplot(sol; ylabel=\"θ\", fmt=:png)","category":"page"},{"location":"generated/heterogeneous_system/#Heterogeneous-dynamics","page":"Heterogeneous Systems","title":"Heterogeneous dynamics","text":"","category":"section"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Two paradigmatic modifications of the node model above are static nodes and nodes with inertia. A static node has no internal states and instead fixes the variable at a constant value.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"function static_g(out, u, p, t)\n out[1] = p[1]\n nothing\nend\nstatic! = VertexModel(g=static_g, outsym=[:θ], psym=[:θfix => ω[1]], name=:static)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"But wait! NetworkDynamics classified this as PureFeedForward, because it cannot distinguish between the function signatures","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"g(out, u, p, t) # PureFeedForward\ng(out, ins, p, t) # NoFeedForward","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"and since dim(u)=0 it wrongfully assumes that the latter is meant. We can overwrite the classification by passing the ff keyword:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"static! = VertexModel(g=static_g, outsym=[:θ], psym=[:θfix => ω[1]], ff=NoFeedForward(), name=:static)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"A Kuramoto model with inertia consists of two internal variables leading to more complicated (and for many applications more realistic) local dynamics.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"function kuramoto_inertia!(dv, v, esum, (ω0,), t)\n dv[1] = v[2]\n dv[2] = ω0 - 1.0 * v[2] + esum[1]\n nothing\nend\n\ninertia! = VertexModel(f=kuramoto_inertia!, g=1:1, sym=[:θ, :ω], psym=[:ω0], name=:inertia)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Since now we model a system with heterogeneous node dynamics we can no longer straightforwardly pass a single VertexModel to the Network constructor but instead have to hand over an Array.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"vertex_array = VertexModel[vertex! for i in 1:N]\nvertex_array[1] = static!\nvertex_array[5] = inertia! # index should correspond to the node's index in the graph\nnw_hetero! = Network(g, vertex_array, edge!)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Now we have to take a bit more care with defining initial conditions and parameters.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"First, we can generate a NWState object based on the nw_hetero! object which will be populated with the default values.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state = NWState(nw_hetero!)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"The node with inertia is two-dimensional, hence we need to specify two initial conditions. For the first dimension we keep the initial conditions from above and insert! another one into x0 at the correct index.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"For the θ states we will use the same initial conditins as before:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state.v[2:8,:θ] = x0[2:8]\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"We're still missing one initial condition: the second variable ω of the 5th vertex.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state.v[5,:ω] = 5\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"The NWState object also contains a parameter object accessible via state.p. The edge parameters are already filled with default values. The vertex parameters can be copied from our old parmeter object p.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state.p.v[2:8, :ω0] = p.v[2:8, :ω0]\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"For the problem construction, we need to convert the nested stuctures to flat arrays using the uflat and pflat methods.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"prob_hetero = ODEProblem(nw_hetero!, uflat(state), tspan, pflat(state))\nsol_hetero = solve(prob_hetero, Tsit5());\nnothing #hide\nplot(sol_hetero)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"For clarity we plot only the variables referring to the oscillator's angle θ and color them according to their type.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"colors = map(vertex_array) do vertexf\n if vertexf.name == :kuramoto\n colorant\"lightseagreen\"\n elseif vertexf.name == :static\n colorant\"orange\"\n elseif vertexf.name == :inertia\n colorant\"darkred\"\n end\nend\n\nplot(sol_hetero; ylabel=\"θ\", idxs=vidxs(1:8,:θ), lc=colors', fmt=:png)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"This page was generated using Literate.jl.","category":"page"},{"location":"metadata/#Metadata","page":"Metadata","title":"Metadata","text":"","category":"section"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Component model such as VertexModel and EdgeModel can store metadata. We distinguish between two kinds of metadata: component metadata and symbol metadata.","category":"page"},{"location":"metadata/#Component-Metadata","page":"Metadata","title":"Component Metadata","text":"","category":"section"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Component metadata is a Dict{Symbol,Any} attached to each component to store various information. Use metadata to retrieve the full dict.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Component Metadata API).","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Special metadata: ","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":":init_residual: after Component-wise Initialization, this field stores the residual vector of the nonlinear problem.\n:graphelement: optional field to specialize the graphelement for each component (vidx) for vertices, (;src,dst) named tuple of either vertex names or vertex indices for edges. Has special accessors has_/get_/set_graphelement.","category":"page"},{"location":"metadata/#Symbol-Metadata","page":"Metadata","title":"Symbol Metadata","text":"","category":"section"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Each component stores symbol metadata. The symbol metadata is a Dict{Symbol, Dict{Symbol, Any}} which stores a metadate dict per symbol. Symbols are everything that appears in sym, psym, obssym and insym.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Per Symbol Metadata API).","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Special cases for symbol metadata are:","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"default: Stores default values for states/parameters. In initialization, those are considered fixed.\nguess: Stores a guess for a state/parameter which needs to solved during initialization (\"free\" variables).\nbounds: Store bounds for variables/parameters\ninit: Stores the solution of the \"free\" variables during initialization.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Fore those, there are special functions has_*, get_* and set_*!. See Per Symbol Metadata API.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Those are closely aligned to the metadata use in ModelingToolkit. They are automatically copied from the ODESystem if you use MTK models to create NetworkDynamics models.","category":"page"},{"location":"symbolic_indexing/#Symbolic-Indexing","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Using SciML's SymblicIndexingInterface.jl, ND.jl provides lots of methods to access and change variables and Parameters.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"details: Setup code to make following examples work\nusing NetworkDynamics\nusing Graphs\nusing OrdinaryDiffEqTsit5\nusing Plots","category":"page"},{"location":"symbolic_indexing/#Provide-Symbol-Names","page":"Symbolic Indexing","title":"Provide Symbol Names","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"When construction component models, you can pass symbolic names using the sym and psym keywords.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"function _edgef!(e, v_s, v_d, (K,), t)\n e .= K * (v_s[1] .- v_d[1])\nend\nedgef = EdgeModel(;g=AntiSymmetric(_edgef!), outsym=[:flow], psym=[:K=>1])","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Here we created a static diffusion edge with suitable variable and parameter names. Similarly, we define the diffusion vertex with symbolic names.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"function _vertexf!(dv, v, esum, p, t)\n dv[1] = esum[1]\nend\nvertexf = VertexModel(f=_vertexf!, g=1, sym=[:storage])","category":"page"},{"location":"symbolic_indexing/#Fundamental-Symblic-Indices","page":"Symbolic Indexing","title":"Fundamental Symblic Indices","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"The default types for this access are the types VIndex, EIndex, VPIndex and EPIndex. Each of those symbolic indices consists of 2 elements: a reference to the network componen and a reference to the symbol within that component. As such, VIndex(2, :x) refers to variable with symbolic name :x in vertex number 2. EPIndex(4, 2) would refer to the second parameter of the edge component for the 4th edge.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"details: Setup code to make following examples work\ng = wheel_graph(5)\nnw = Network(g, vertexf, edgef)\ns = NWState(nw)\ns.v[:,:storage] .= randn(5)\nprob = ODEProblem(nw, uflat(s), (0,2), pflat(s))\nsol = solve(prob, Tsit5()) \nnothing #hide","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Those fundamental indices can be used in a lot of scenarios. Most importantly you can use them to","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"sol(sol.t; idxs=VIndex(1, :storage)) # extract timeseries out ouf solution object\nplot(sol; idxs=[VIndex(1, :storage), VIndex(5,:storage)]) # plot storage of two nodes","category":"page"},{"location":"symbolic_indexing/#Generate-Symbolic-Indices","page":"Symbolic Indexing","title":"Generate Symbolic Indices","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Often, you need many individual symbolic indices. For that there are the helper methods vidxs, eidxs, vpidxs and epidxs. With the help of those methods you can generate arrays of symbolic indices:","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"vidxs(nw, :, :storage) # get variable \"storage\" for all nodes","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"plot(sol; idxs=vidxs(nw, :, :storage))","category":"page"},{"location":"symbolic_indexing/#NWState-and-NWParameter-Objects","page":"Symbolic Indexing","title":"NWState and NWParameter Objects","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Internally, both state and parameters of a Network are represented using flat arrays. To access the state or parameters of a network, you can use the NWState and NWParameter objects.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"p = NWParameter(nw)","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"creates a NWParameter object for the network nw. It essentially creates a new flat parameter array and fills it with the default parameter values define in the component. The parameters in the NWParameter object can be accessed using the symbolic indices.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"p[EPIndex(5, :K)] = 2.0 # change the parameter K of the 5th edge\nnothing #hide","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Similarly, you can create a NWState object for the network nw using","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"s = NWState(nw)","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"No default values were provided in the network components, so the state array is filled with NaNs.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"s[VIndex(:, :storage)] .= randn(5) # set the (initial) storage for alle nodes \ns #hide","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"For both NWState and NWParameter objects, the there is a more convenient way to access the variables and parameters.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"@assert s.v[1, :storage] == s[VIndex(1, :storage)] # s.v -> access vertex states\n@assert s.e[1, :flow] == s[EIndex(1, :flow)] # s.e -> access edge states\n@assert s.p.e[1,:K] == p[EPIndex(1, :K)] # s.p -> access parameters","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"The NWState and NWParameter objects are mutable, thus changing them will also change the underlying wrapped flat arrays. You can allways access the flat representations by calling uflat and pflat.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"note: Note\nThe NWState and NWParameter wrappers can be constructed from various objects. Fore example, within a callback you might construct p = NWParameter(integrator) to then change the parameters of the network within the callback.","category":"page"},{"location":"symbolic_indexing/#Observables","page":"Symbolic Indexing","title":"Observables","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Sometimes, the \"states\" you're interested in aren't really states in the DAE sense but rather algebraic derivations from DAE states, parameters and time – in accordance with the naming in the SciML-ecosystem those states are called Observables.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"A prime example of Observables are edge/vertex-outputs, such as the flow in the edge model defined above. It is also possible to define additional Observables manually by using the obssym and obsf keyword on the EdgeModel/VertexModel constructors. When building models using ModelingToolkit, the reduced algebraic states will be preserved as observables automatically.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Observables can be accessed like any other state, for example, the flows in the network don't show up in the state array but can be accessed in all the ways discussed above, for example","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"plot(sol; idxs=eidxs(nw, :, :flow))","category":"page"},{"location":"mathematical_model/#Mathematical-Model","page":"Mathematical Model","title":"Mathematical Model","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The basic mathematical model of NetworkDynamics.jl splits up the system it two parts: vertex and edge components.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The main goal of NetworkDynamics.jl is, to express the overall network dynamics as a Differential-Algebraic-Equation (DAE)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Mfracmathrmdmathrmdtu = f^mathrmnw(u p t)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"where M is a (possibly singular) mass matrix, u is the internal state vector of the system, p are the parameters and t is the time. To make this compatible with the solvers for OrdinaryDiffEq.jl, the created Network object is a callable object","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"nw(du, u, p, t) # mutates du","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"with stored mass matrix information to build an ODEProblem based on the Network.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Instead of defining f^mathrmnw by hand, ND.jl helps you to build it automatically based on a list of decentralized nodal and edge dynamics, so-called VertexModel and EdgeModel objects. Each component model mathrm c is modeled as general input-output-system","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"beginaligned\nM_mathrm cfracmathrmdmathrmdtx_mathrm c = f^mathrm c(x^mathrm c i_mathrm c p_mathrm c t)\ny^mathrm c = g^mathrm c(x^mathrmc i_mathrm c p_mathrm c t)\nendaligned","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"where M_mathrmc is the component mass matrix, x^mathrm c are the component states, i^mathrm c are the inputs of the component and y^mathrm c is the output of the component. It is possible to have mathrmdim(x^mathrmc) = 0 and thus no internal states.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"In the network context, the output of the edges are flow variables. The outputs of vertices are potential variables. In interconnection, the flow on the edges depends on the potentials at both ends as inputs. The potentials of the nodes depend on the incoming flows from all connected edges as an input. (Here, flow and potentials are meant in a conceptional and not necessarily physical way.)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"","category":"page"},{"location":"mathematical_model/#Vertex-Models","page":"Mathematical Model","title":"Vertex Models","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Specifically, a (single-layer) vertex model has one input, and one output. The input is an aggregation/reduction over all incident edge outputs,","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"i^mathrm v = mathopmathrmagglimits_k^textincident y^mathrm e_k qquadtextoftenqquad\ni^mathrm v = sum_k^textincident y^mathrm e_k","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The full vertex model","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"beginaligned\nM^mathrm vfracmathrmdmathrmdtx^mathrm v = f^mathrm v(u^mathrm v i^mathrm v p^mathrm v t)\ny^mathrm v = g^mathrm v(u^mathrm v i^mathrm v p^mathrm v t)\nendaligned","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"corresponds to the Julia functions","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"function fᵥ(dxᵥ, xᵥ, e_aggr, pᵥ, t)\n # mutate dxᵥ\n nothing\nend\nfunction gᵥ(yᵥ, xᵥ, e_aggr, pᵥ, t)\n # mutate yᵥ\n nothing\nend\nvertf = VertexModel(; f=fᵥ, g=gᵥ, mass_matrix=Mᵥ, ...)","category":"page"},{"location":"mathematical_model/#Edge-Models","page":"Mathematical Model","title":"Edge Models","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"In contrast to vertex models, edge models in general have two inputs and two outputs, both for source and destination end of the edge. We commonly use src and dst to describe the source and destination end of an edge respectively. ","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"note: On the directionality of edges\nMathematically, in a system defined on an undirected graph there is no difference between the edge (12) and (21), the edge has no direction. However, from an implementation point of view we always need to have some kind of ordering for function arguments, state order and so on. For undirected graphs, Graphs.jl chooses the direction of an edge v1->v2 such that v1 < v2.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The inputs of the edge are just the outputs of the two nodes at both ends. The output is split into two: the dst output goes to the input of the vertex at the destination end, the src output goes to the input of the vertex at the src end.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The full model of an edge","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"beginaligned\nM^mathrm efracmathrmdmathrmdtx^mathrm e = f^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t)\ny^mathrm e_mathrmdst = g_mathrmdst^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t)\ny^mathrm e_mathrmsrc = g_mathrmsrc^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t)\nendaligned","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"corresponds to the Julia functions","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"function fₑ(dxₑ, xₑ, v_src, v_dst, pₑ, t)\n # mutate dxᵥ\n nothing\nend\nfunction gₑ(y_src, y_dst, xᵥ, v_src, v_dst, pₑ, t)\n # mutate y_src and y_dst\n nothing\nend\nvertf = EdgeModel(; f=fₑ, g=gₑ, mass_matrix=Mₑ, ...)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The sign convention for both outputs of an edge must be identical, typically, a positive flow represents a flow into the connected vertex. This is important, because the vertex only receives the flows, it does not know whether the flow was produce by the source or destination end of an edge.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":" y_src y_dst \n V_src o───←─────────→───o V_dst\n","category":"page"},{"location":"mathematical_model/#Single-Sided-Edge-Outputs","page":"Mathematical Model","title":"Single Sided Edge Outputs","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Often, edge outputs will possess some symmetry which makes it more convenient to define \"single sided\" edge output functions","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"function g_single(y, xᵥ, v_src, v_dst, pₑ, t)\n # mutate y\n nothing\nend","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"There are multiple wrappers available to automaticially convert them into double-sided edge output functions:","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Directed(g_single) builds a double-sided function which only couples to the destination side.\nSymmetric(g_single) builds a double-sided function in which both ends receive y.\nAntiSymmetric(g_single) builds a double-sided function where the destination receives y and the source receives -y.\nFiducial(g_single_src, g_singl_dst) builds a double-sided edge output function based on two single sided functions.","category":"page"},{"location":"mathematical_model/#Feed-Forward-Behavior","page":"Mathematical Model","title":"Feed Forward Behavior","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The most general version of the component models can contain direct feed forwards from the input, i.e. the edge output might depend directly on the connected vertices or the vertex output might depend directly on the aggregated edge input.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Whenever possible, you should define output functions without feed forwards, i.e.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"gᵥ_noff(yᵥ, xᵥ, pᵥ, t)\ngₑ_noff([y_src,] y_dst, xᵥ, pₑ, t)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"instead of the more general","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"gᵥ(yᵥ, xᵥ, e_aggr, pᵥ, t)\ngₑ([y_src], y_dst, xᵥ, v_src, v_dst, pₑ, t)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"NetworkDynamics cannot couple two components with feed forward to each other. It is always possible to transform feed forward behavior to an internal state x with mass matrix entry zero to circumvent this problem. This transformation can be performed automatically by using ff_to_constraint.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"warning: Feed Forward Vertices\nAs of 11/2024, vertices with feed forward are not supported at all. Use ff_to_constraint to transform them into vertex model without FF.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Concretely, NetworkDynamics distinguishes between 4 types of feed forward behaviors of g functions based on the FeedForwardType trait. The different types the signature of provided function g. Based on the signatures avaialable, ND.jl will try to find the correct type automaticially. Using the ff keyword in the constructors, the user can enforce a specific type.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"PureFeedForward()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., ins..., p, t) # abstractly\ng!(out_dst, v_src, v_dst, p, t) # single-sided edge\ng!(out_src, out_dst, v_src, v_dst, p, t) # double-sided edge\ng!(v_out, e_aggr, p, t) # single layer vertex","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"FeedForward()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., x, ins..., p, t) # abstractly\ng!(out_dst, x, v_src, v_dst, p, t) # single-sided edge\ng!(out_src, out_dst, x, v_src, v_dst, p, t) # double-sided edge\ng!(v_out, x, e_aggr, p, t) # single layer vertex","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"NoFeedForward()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., x, p, t) # abstractly\ng!(out_dst, x, p, t) # single-sided edge\ng!(out_src, out_dst, x, p, t) # double-sided edge\ng!(v_out, x, p, t) # single layer vertex","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"PureStateMap()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., x) # abstractly\ng!(out_dst, x) # single-sided edge\ng!(out_src, out_dst, x) # double-sided edge\ng!(v_out, x) # single layer vertex","category":"page"},{"location":"API/#API","page":"API","title":"API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"The following functions are designed for public use.","category":"page"},{"location":"API/#Network-Construction-API","page":"API","title":"Network Construction API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"Network\ndim(::Network)\npdim(::Network)","category":"page"},{"location":"API/#NetworkDynamics.Network","page":"API","title":"NetworkDynamics.Network","text":"Network([g,] vertexf, edgef; kwarg...)\n\nConstruct a Network object from a graph g and edge and component models vertexf and edgef.\n\nArguments:\n\ng::AbstractGraph: The graph on which the network is defined. Optional, can be ommittet if all component models have a defined graphelement. See vidx and src/dst keywors for VertexModel and EdgeModel constructors respectively.\nvertexm: A single VertexModel or a vector of VertexModel objects. The order of the vertex models must mirror the order of the vertices(g) iterator.\nedgem: A single EdgeModel or a vector of EdgeModel objects. The order of the edge models must mirror the order of the edges(g) iterator.\n\nOptional keyword arguments:\n\nexecution=SequentialExecution{true}(): Execution model of the network. E.g. SequentialExecution, KAExecution, PolyesterExecution or ThreadedExecution.\naggregator=execution isa SequentialExecution ? SequentialAggregator(+) : PolyesterAggregator(+): Aggregation function applied to the edge models. E.g. SequentialAggregator, PolyesterAggregator, ThreadedAggregator, SparseAggregator.\ncheck_graphelement=true: Check if the graphelement metadata is consistent with the graph.\ndealias=false Check if the components alias eachother and create copies if necessary. This is necessary if the same component model is referenced in multiple places in the Network but you want to dynamicially asign metadata, such as initialization information to specific instances.\nverbose=false: Show additional information during construction.\n\n\n\n\n\nNetwork(nw::Network; g, vertexm, edgem, kwargs...)\n\nRebuild the Network with same graph and vertex/edge models but possibly different kwargs.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.dim-Tuple{Network}","page":"API","title":"NetworkDynamics.dim","text":"dim(nw::Network)\n\nReturns the number of dynamic states in the network, corresponts to the length of the flat state vector.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.pdim-Tuple{Network}","page":"API","title":"NetworkDynamics.pdim","text":"pdim(nw::Network)\n\nReturns the number of parameters in the network, corresponts to the length of the flat parameter vector.\n\n\n\n\n\n","category":"method"},{"location":"API/#Component-Models","page":"API","title":"Component Models","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"VertexModel()\nEdgeModel()","category":"page"},{"location":"API/#NetworkDynamics.VertexModel-Tuple{}","page":"API","title":"NetworkDynamics.VertexModel","text":"VertexModel(; kwargs...)\n\nBuild a VertexModel according to the keyword arguments.\n\nMain Arguments:\n\nf=nothing: Dynamic function of the component. Can be nothing if dim is 0.\ng: Output function of the component. Usefull helpers: StateMask\nsym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.\noutsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. Can be infered automaticially if g isa StateMask.\npsym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.\nmass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).\nname=dim>0 ? :VertexM : :StaticVertexM: Name of the component.\n\nOptional Arguments:\n\ninsym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially.\nvidx: Index of the vertex in the graph, enables graphless constructor.\nff: FeedForwardType of component. Will be typically infered from g automaticially.\nobssym/obsf: Define additional \"observable\" states.\nsymmetadata/metadata: Provide prefilled metadata dictionaries.\n\nAll Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.EdgeModel-Tuple{}","page":"API","title":"NetworkDynamics.EdgeModel","text":"EdgeModel(; kwargs...)\n\nBuild a EdgeModel according to the keyword arguments.\n\nMain Arguments:\n\nf=nothing: Dynamic function of the component. Can be nothing if dim is 0.\ng: Output function of the component. Usefull helpers: AntiSymmetric, Symmetric, Fiducial, Directed and StateMask.\nsym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.\noutsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. In general, outsym for edges isa named tuple (; src, dst). However, depending on the g function, it might be enough to provide a single vector or even nothing (e.g. AntiSymmetric(StateMask(1:2))). See Building EdgeModels for examples.\npsym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.\nmass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).\nname=dim>0 ? :EdgeM : :StaticEdgeM: Name of the component.\n\nOptional Arguments:\n\ninsym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially. For edges, insym is a named tuple (; src, dst). If give as vector tuple is created automaticially.\nsrc/dst: Index or name of the vertices at src and dst end. Enables graphless constructor.\nff: FeedForwardType of component. Will be typically infered from g automaticially.\nobssym/obsf: Define additional \"observable\" states.\nsymmetadata/metadata: Provide prefilled metadata dictionaries.\n\nAll Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].\n\n\n\n\n\n","category":"method"},{"location":"API/#Component-Models-with-MTK","page":"API","title":"Component Models with MTK","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"VertexModel(::ModelingToolkit.ODESystem, ::Any, ::Any)\nEdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any, ::Any)\nEdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any)","category":"page"},{"location":"API/#NetworkDynamics.VertexModel-Tuple{ODESystem, Any, Any}","page":"API","title":"NetworkDynamics.VertexModel","text":"VertexModel(sys::ODESystem, inputs, outputs; ff_to_constraint=true, kwargs...)\n\nCreate a VertexModel object from a given ODESystem created with ModelingToolkit. You need to provide 2 lists of symbolic names (Symbol or Vector{Symbols}):\n\ninputs: names of variables in you equation representing the aggregated edge states\noutputs: names of variables in you equation representing the node output\n\nff_to_constraint controlls, whether output transformations g which depend on inputs should be\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.EdgeModel-Tuple{ODESystem, Vararg{Any, 4}}","page":"API","title":"NetworkDynamics.EdgeModel","text":"EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout; ff_to_constraint=false, kwargs...)\n\nCreate a EdgeModel object from a given ODESystem created with ModelingToolkit. You need to provide 4 lists of symbolic names (Symbol or Vector{Symbols}):\n\nsrcin: names of variables in you equation representing the node state at the source\ndstin: names of variables in you equation representing the node state at the destination\nsrcout: names of variables in you equation representing the output at the source\ndstout: names of variables in you equation representing the output at the destination\n\nff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.EdgeModel-Tuple{ODESystem, Any, Any, Any}","page":"API","title":"NetworkDynamics.EdgeModel","text":"EdgeModel(sys::ODESystem, srcin, dstin, AntiSymmetric(dstout); ff_to_constraint=false, kwargs...)\n\nCreate a EdgeModel object from a given ODESystem created with ModelingToolkit.\n\nHere 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 the symbol vector in\n\nAntiSymmetric(dstout),\nSymmetric(dstout), or\nDirected(dstout).\n\nff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.\n\n\n\n\n\n","category":"method"},{"location":"API/#Output-Function-Helpers/Wrappers","page":"API","title":"Output Function Helpers/Wrappers","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"StateMask\nSymmetric\nAntiSymmetric\nDirected\nFiducial","category":"page"},{"location":"API/#NetworkDynamics.StateMask","page":"API","title":"NetworkDynamics.StateMask","text":"StateMask(i::AbstractArray)\nStateMaks(i::Number)\n\nA StateMask is a predefined output function. It can be used to define the output of a component model by picking from the internal state.\n\nI.e. g=StateMask(2:3) in a vertex function will output the internal states 2 and 3. In many contexts, StateMasks can be constructed implicitly by just providing the indices, e.g. g=1:2.\n\nFor EdgeModel this needs to be combined with a Directed, Symmetric, AntiSymmetric or Fiducial coupling, e.g. g=Fiducial(1:2, 3:4) forwards states 1:2 to dst and states 3:4 to src.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.Symmetric","page":"API","title":"NetworkDynamics.Symmetric","text":"Symmetric(g)\n\nWraps a single-sided output function g turns it into a double sided output function which applies\n\ny_dst = g(...)\ny_src = y_dst\n\ng can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also AntiSymmetric, Directed, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.AntiSymmetric","page":"API","title":"NetworkDynamics.AntiSymmetric","text":"AntiSymmetric(g_dst)\n\nWraps a single-sided output function g_dst turns it into a double sided output function which applies\n\ny_dst = g_dst(...)\ny_src = -y_dst\n\ng_dst can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also Symmetric, Directed, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.Directed","page":"API","title":"NetworkDynamics.Directed","text":"Directed(g_dst)\n\nWraps a single-sided output function g_dst turns it into a double sided output function which applies\n\ny_dst = g_dst(...)\n\nWith Directed there is no output for the src side. g_dst can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also AntiSymmetric, Symmetric, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.Fiducial","page":"API","title":"NetworkDynamics.Fiducial","text":"Fiducial(g_src, g_dst)\n\nWraps two single-sided output function g_src and g_dst and turns them into a double sided output function which applies\n\ny_dst = g_src(...)\ny_src = g_dst(...)\n\ng can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also AntiSymmetric, Directed, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#Accessors-for-Component-Properties","page":"API","title":"Accessors for Component Properties","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"fftype\ndim(::NetworkDynamics.ComponentModel)\nsym\noutdim\noutsym\npdim(::NetworkDynamics.ComponentModel)\npsym\nobssym\nhasinsym\ninsym\nhasindim\nindim","category":"page"},{"location":"API/#NetworkDynamics.fftype","page":"API","title":"NetworkDynamics.fftype","text":"fftype(x)\n\nRetrieve the feed forward trait of x.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.dim-Tuple{NetworkDynamics.ComponentModel}","page":"API","title":"NetworkDynamics.dim","text":"dim(c::ComponentModel)::Int\n\nRetrieve the dimension of the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.sym","page":"API","title":"NetworkDynamics.sym","text":"sym(c::ComponentModel)::Vector{Symbol}\n\nRetrieve the symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.outdim","page":"API","title":"NetworkDynamics.outdim","text":"outdim(c::VertexModel)::Int\noutdim(c::EdgeModel)::@NamedTuple(src::Int, dst::Int)\n\nRetrieve the output dimension of the component\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.outsym","page":"API","title":"NetworkDynamics.outsym","text":"outsym(c::VertexModel)::Vector{Symbol} outsym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}\n\nRetrieve the output symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.pdim-Tuple{NetworkDynamics.ComponentModel}","page":"API","title":"NetworkDynamics.pdim","text":"pdim(c::ComponentModel)::Int\n\nRetrieve the parameter dimension of the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.psym","page":"API","title":"NetworkDynamics.psym","text":"psym(c::ComponentModel)::Vector{Symbol}\n\nRetrieve the parameter symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.obssym","page":"API","title":"NetworkDynamics.obssym","text":"obssym(c::ComponentModel)::Vector{Symbol}\n\nRetrieve the observation symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.hasinsym","page":"API","title":"NetworkDynamics.hasinsym","text":"hasinsym(c::ComponentModel)\n\nChecks if the optioan field insym is present in the component model.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.insym","page":"API","title":"NetworkDynamics.insym","text":"insym(c::VertexModel)::Vector{Symbol}\ninsym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}\n\nMusst be called after hasinsym/hasindim returned true. Gives the insym vector(s). For vertex model just a single vector, for edges it returns a named tuple (; src, dst) with two symbol vectors.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.hasindim","page":"API","title":"NetworkDynamics.hasindim","text":"hasindim(c::ComponentModel)\n\nChecks if the optioan field insym is present in the component model.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.indim","page":"API","title":"NetworkDynamics.indim","text":"indim(c::VertexModel)::Int\nindim(c::EdgeModel)::@NamedTuple{src::Int,dst::Int}\n\nMusst be called after hasinsym/hasindim returned true. Gives the input dimension(s).\n\n\n\n\n\n","category":"function"},{"location":"API/#FeedForwardType-Traits","page":"API","title":"FeedForwardType-Traits","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"FeedForwardType\nPureFeedForward\nFeedForward\nNoFeedForward\nPureStateMap","category":"page"},{"location":"API/#NetworkDynamics.FeedForwardType","page":"API","title":"NetworkDynamics.FeedForwardType","text":"abstract type FeedForwardType end\n\nAbstract supertype for the FeedForwardType traits.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PureFeedForward","page":"API","title":"NetworkDynamics.PureFeedForward","text":"PureFeedForward <: FeedForwardType\n\nTrait for component output functions g that have pure feed forward behavior (do not depend on x):\n\ng!(outs..., ins..., p, t)\n\nSee also FeedForward, NoFeedForward and PureStateMap.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.FeedForward","page":"API","title":"NetworkDynamics.FeedForward","text":"FeedForward <: FeedForwardType\n\nTrait for component output functions g that have feed forward behavior. May depend on everything:\n\ng!(outs..., x, ins..., p, t)\n\nSee also PureFeedForward, NoFeedForward and PureStateMap.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.NoFeedForward","page":"API","title":"NetworkDynamics.NoFeedForward","text":"NoFeedForward <: FeedForwardType\n\nTrait for component output functions g that have no feed forward behavior (do not depend on inputs):\n\ng!(outs..., x, p, t)\n\nSee also PureFeedForward, FeedForward and PureStateMap.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PureStateMap","page":"API","title":"NetworkDynamics.PureStateMap","text":"PureStateMap <: FeedForwardType\n\nTrait for component output functions g that only depends on state:\n\ng!(outs..., x)\n\nSee also PureFeedForward, FeedForward and NoFeedForward.\n\n\n\n\n\n","category":"type"},{"location":"API/#Symbolic-Indexing-API","page":"API","title":"Symbolic Indexing API","text":"","category":"section"},{"location":"API/#Network-Parameter-Object","page":"API","title":"Network Parameter Object","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"NWParameter\nNWParameter(::Any)\nNWParameter(::NWParameter)\nNWParameter(::SciMLBase.DEIntegrator)","category":"page"},{"location":"API/#NetworkDynamics.NWParameter","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(nw_or_nw_wraper, pflat)\n\nIndexable wrapper for flat parameter array pflat. Needs Network or wrapper of Network, e.g. ODEProblem.\n\np = NWParameter(nw)\np.v[idx, :sym] # get parameter :sym of vertex idx\np.e[idx, :sym] # get parameter :sym of edge idx\np[s::Union{VPIndex, EPIndex}] # get parameter for specific index\n\nGet flat array representation using pflat(p).\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.NWParameter-Tuple{Any}","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(nw_or_nw_wraper;\n ptype=Vector{Float64}, pfill=filltype(ptype), default=true)\n\nCreates \"empty\" NWParameter object for the Network/Wrapper nw with flat type ptype. The array will be prefilled with pfill (defaults to NaN).\n\nIf default=true the default parameter values attached to the network components will be loaded.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWParameter-Tuple{NWParameter}","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(p::NWParameter; ptype=typeof(p.pflat))\n\nCreate NWParameter based on other parameter object, just convert type.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWParameter-Tuple{SciMLBase.DEIntegrator}","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(int::SciMLBase.DEIntegrator)\n\nCreate NWParameter object from integrator.\n\n\n\n\n\n","category":"method"},{"location":"API/#Network-State-Object","page":"API","title":"Network State Object","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"NWState\nNWState(::Any)\nNWState(::NWState)\nNWState(::NWParameter)\nNWState(::SciMLBase.DEIntegrator)\nuflat\npflat","category":"page"},{"location":"API/#NetworkDynamics.NWState","page":"API","title":"NetworkDynamics.NWState","text":"NWState(nw_or_nw_wrapper, uflat, [pflat], [t])\n\nIndexable wrapper for flat state & parameter array. Needs Network or wrapper of Network, e.g. ODEProblem.\n\ns = NWState(nw)\ns.v[idx, :sym] # get state :sym of vertex idx\ns.e[idx, :sym] # get state :sym of edge idx\ns.p.v[idx, :sym] # get parameter :sym of vertex idx\ns.p.e[idx, :sym] # get parameter :sym of edge idx\ns[s::Union{VIndex, EIndex, EPIndex, VPIndex}] # get parameter for specific index\n\nGet flat array representation using uflat(s) and pflat(s).\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.NWState-Tuple{Any}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(nw_or_nw_wrapper;\n utype=Vector{Float64}, ufill=filltype(utype),\n ptype=Vector{Float64}, pfill=filltype(ptype), default=true)\n\nCreates \"empty\" NWState object for the Network/Wrapper nw with flat types utype & ptype. The arrays will be prefilled with ufill and pfill respectively (defaults to NaN).\n\nIf default=true the default state & parameter values attached to the network components will be loaded.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWState-Tuple{NWState}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(p::NWState; utype=typeof(uflat(s)), ptype=typeof(pflat(s)))\n\nCreate NWState based on other state object, just convert types.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWState-Tuple{NWParameter}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(p::NWParameter; utype=Vector{Float64}, ufill=filltype(utype), default=true)\n\nCreate NWState based on existing NWParameter object.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWState-Tuple{SciMLBase.DEIntegrator}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(int::SciMLBase.DEIntegrator)\n\nCreate NWState object from integrator.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.uflat","page":"API","title":"NetworkDynamics.uflat","text":"uflat(s::NWState)\n\nRetrieve the wrapped flat array representation of the state.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.pflat","page":"API","title":"NetworkDynamics.pflat","text":"pflat(p::NWParameter)\npflat(s::NWState)\n\nRetrieve the wrapped flat array representation of the parameters.\n\n\n\n\n\n","category":"function"},{"location":"API/#Symbolic-Indices","page":"API","title":"Symbolic Indices","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"VIndex\nEIndex\nVPIndex\nEPIndex","category":"page"},{"location":"API/#NetworkDynamics.VIndex","page":"API","title":"NetworkDynamics.VIndex","text":"VIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = VIndex(comp, sub)\n\nA symbolic index for a vertex state variable.\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nVIndex(1, :P) # vertex 1, variable :P\nVIndex(1:5, 1) # first state of vertices 1 to 5\nVIndex(7, (:x,:y)) # states :x and :y of vertex 7\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWState, NWParameter or ODESolution.\n\nSee also: EIndex, VPIndex, EPIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.EIndex","page":"API","title":"NetworkDynamics.EIndex","text":"EIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = EIndex(comp, sub)\n\nA symbolic index for an edge state variable.\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nEIndex(1, :P) # edge 1, variable :P\nEIndex(1:5, 1) # first state of edges 1 to 5\nEIndex(7, (:x,:y)) # states :x and :y of edge 7\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWState, NWParameter or ODESolution.\n\nSee also: VIndex, VPIndex, EPIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.VPIndex","page":"API","title":"NetworkDynamics.VPIndex","text":"VPIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = VPIndex(comp, sub)\n\nA symbolic index into the parameter a vertex:\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.\n\nSee also: EPIndex, VIndex, EIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.EPIndex","page":"API","title":"NetworkDynamics.EPIndex","text":"EPIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = VEIndex(comp, sub)\n\nA symbolic index into the parameter a vertex:\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.\n\nSee also: VPIndex, VIndex, EIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#Index-generators","page":"API","title":"Index generators","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"vidxs\neidxs\nvpidxs\nepidxs","category":"page"},{"location":"API/#NetworkDynamics.vidxs","page":"API","title":"NetworkDynamics.vidxs","text":"vidxs([inpr], components=:, variables=:) :: Vector{VIndex}\n\nGenerate vector of symbolic indexes for vertices.\n\ninpr: Only needed for name matching or : access. Can be Network, sol, prob, ...\ncomponents: Number/Vector, :, Symbol (name matches), String/Regex (name contains)\nvariables: Symbol/Number/Vector, :, String/Regex (all sym containing)\n\nExamples:\n\nvidxs(nw) # all vertex state indices\nvidxs(1:2, :u) # [VIndex(1, :u), VIndex(2, :u)]\nvidxs(nw, :, [:u, :v]) # [VIndex(i, :u), VIndex(i, :v) for i in 1:nv(nw)]\nvidxs(nw, \"ODEVertex\", :) # all symbols of all vertices with name containing \"ODEVertex\"\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.eidxs","page":"API","title":"NetworkDynamics.eidxs","text":"vidxs([inpr], components=:, variables=:) :: Vector{EIndex}\n\nGenerate vector of symbolic indexes for edges.\n\ninpr: Only needed for name matching or : access. Can be Network, sol, prob, ...\ncomponents: Number/Vector, :, Symbol (name matches), String/Regex (name contains)\nvariables: Symbol/Number/Vector, :, String/Regex (all sym containing)\n\nExamples:\n\neidxs(nw) # all edge state indices\neidxs(1:2, :u) # [EIndex(1, :u), EIndex(2, :u)]\neidxs(nw, :, [:u, :v]) # [EIndex(i, :u), EIndex(i, :v) for i in 1:ne(nw)]\neidxs(nw, \"FlowEdge\", :) # all symbols of all edges with name containing \"FlowEdge\"\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.vpidxs","page":"API","title":"NetworkDynamics.vpidxs","text":"vpidxs([inpr], components=:, variables=:) :: Vector{VPIndex}\n\nGenerate vector of symbolic indexes for parameters. See vidxs for more information.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.epidxs","page":"API","title":"NetworkDynamics.epidxs","text":"epidxs([inpr], components=:, variables=:) :: Vector{EPIndex}\n\nGenerate vector of symbolic indexes for parameters. See eidxs for more information.\n\n\n\n\n\n","category":"function"},{"location":"API/#Metadata-API","page":"API","title":"Metadata API","text":"","category":"section"},{"location":"API/#Component-Metadata-API","page":"API","title":"Component Metadata API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"metadata\nhas_metadata(::NetworkDynamics.ComponentModel, ::Symbol)\nget_metadata(::NetworkDynamics.ComponentModel, ::Symbol)\nset_metadata!(::NetworkDynamics.ComponentModel, ::Symbol, ::Any)\nhas_graphelement\nget_graphelement\nset_graphelement!","category":"page"},{"location":"API/#NetworkDynamics.metadata","page":"API","title":"NetworkDynamics.metadata","text":"metadata(c::ComponentModel)\n\nRetrieve metadata object for the component.\n\nSee also metadata\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol}","page":"API","title":"NetworkDynamics.has_metadata","text":"has_metadata(c::ComponentModel, key::Symbol)\n\nChecks if metadata key is present for the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.get_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol}","page":"API","title":"NetworkDynamics.get_metadata","text":"get_metadata(c::ComponentModel, key::Symbol)\n\nRetrieves the metadata key for the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.set_metadata!-Tuple{NetworkDynamics.ComponentModel, Symbol, Any}","page":"API","title":"NetworkDynamics.set_metadata!","text":"set_metadata!(c::ComponentModel, key::Symbol, value)\n\nSets the metadata key for the component to value.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.has_graphelement","page":"API","title":"NetworkDynamics.has_graphelement","text":"has_graphelement(c)\n\nChecks if the edge or vetex function function has the graphelement metadata.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_graphelement","page":"API","title":"NetworkDynamics.get_graphelement","text":"get_graphelement(c::EdgeModel)::@NamedTuple{src::T, dst::T}\nget_graphelement(c::VertexModel)::Int\n\nRetrieves the graphelement metadata for the component model. For edges this returns a named tupe (;src, dst) where both are either integers (vertex index) or symbols (vertex name).\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_graphelement!","page":"API","title":"NetworkDynamics.set_graphelement!","text":"set_graphelement!(c::EdgeModel, src, dst)\nset_graphelement!(c::VertexModel, vidx)\n\nSets the graphelement metadata for the edge model. For edges this takes two arguments src and dst which are either integer (vertex index) or symbol (vertex name). For vertices it takes a single integer vidx.\n\n\n\n\n\n","category":"function"},{"location":"API/#Per-Symbol-Metadata-API","page":"API","title":"Per-Symbol Metadata API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"symmetadata\nget_metadata(::NetworkDynamics.ComponentModel, ::Symbol, ::Symbol)\nhas_metadata(::NetworkDynamics.ComponentModel, ::Symbol, ::Symbol)\nset_metadata!(::NetworkDynamics.ComponentModel, ::Symbol, ::Symbol, ::Any)\nhas_default\nget_default\nset_default!\nhas_guess\nget_guess\nset_guess!\nhas_init\nget_init\nset_init!\nhas_bounds\nget_bounds\nset_bounds!","category":"page"},{"location":"API/#NetworkDynamics.symmetadata","page":"API","title":"NetworkDynamics.symmetadata","text":"symmetadata(c::ComponentModel)::Dict{Symbol,Dict{Symbol,Any}}\n\nRetrieve the metadata dictionary for the symbols. Keys are the names of the symbols as they appear in sym, psym, obssym and insym.\n\nSee also symmetadata\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol, Symbol}","page":"API","title":"NetworkDynamics.get_metadata","text":"get_metadata(c::ComponentModel, sym::Symbol, key::Symbol)\n\nRetrievs the metadata key for symbol sym.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.has_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol, Symbol}","page":"API","title":"NetworkDynamics.has_metadata","text":"has_metadata(c::ComponentModel, sym::Symbol, key::Symbol)\n\nChecks if symbol metadata key is present for symbol sym.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.set_metadata!-Tuple{NetworkDynamics.ComponentModel, Symbol, Symbol, Any}","page":"API","title":"NetworkDynamics.set_metadata!","text":"set_metadata!(c::ComponentModel, sym::Symbol, key::Symbol, value)\nset_metadata!(c::ComponentModel, sym::Symbol, pair)\n\nSets the metadata key for symbol sym to value.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.has_default","page":"API","title":"NetworkDynamics.has_default","text":"has_default(c::ComponentModel, sym::Symbol)\n\nChecks if a default value is present for symbol sym.\n\nSee also get_default, set_default!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_default","page":"API","title":"NetworkDynamics.get_default","text":"get_default(c::ComponentModel, sym::Symbol)\n\nReturns the default value for symbol sym.\n\nSee also has_default, set_default!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_default!","page":"API","title":"NetworkDynamics.set_default!","text":"set_default(c::ComponentModel, sym::Symbol, value)\n\nSets the default value for symbol sym to value.\n\nSee also has_default, get_default.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_guess","page":"API","title":"NetworkDynamics.has_guess","text":"has_guess(c::ComponentModel, sym::Symbol)\n\nChecks if a guess value is present for symbol sym.\n\nSee also get_guess, set_guess!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_guess","page":"API","title":"NetworkDynamics.get_guess","text":"get_guess(c::ComponentModel, sym::Symbol)\n\nReturns the guess value for symbol sym.\n\nSee also has_guess, set_guess!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_guess!","page":"API","title":"NetworkDynamics.set_guess!","text":"set_guess(c::ComponentModel, sym::Symbol, value)\n\nSets the guess value for symbol sym to value.\n\nSee also has_guess, get_guess.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_init","page":"API","title":"NetworkDynamics.has_init","text":"has_init(c::ComponentModel, sym::Symbol)\n\nChecks if a init value is present for symbol sym.\n\nSee also get_init, set_init!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_init","page":"API","title":"NetworkDynamics.get_init","text":"get_init(c::ComponentModel, sym::Symbol)\n\nReturns the init value for symbol sym.\n\nSee also has_init, set_init!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_init!","page":"API","title":"NetworkDynamics.set_init!","text":"set_init(c::ComponentModel, sym::Symbol, value)\n\nSets the init value for symbol sym to value.\n\nSee also has_init, get_init.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_bounds","page":"API","title":"NetworkDynamics.has_bounds","text":"has_bounds(c::ComponentModel, sym::Symbol)\n\nChecks if a bounds value is present for symbol sym.\n\nSee also get_bounds, set_bounds!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_bounds","page":"API","title":"NetworkDynamics.get_bounds","text":"get_bounds(c::ComponentModel, sym::Symbol)\n\nReturns the bounds value for symbol sym.\n\nSee also has_bounds, set_bounds!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_bounds!","page":"API","title":"NetworkDynamics.set_bounds!","text":"set_bounds(c::ComponentModel, sym::Symbol, value)\n\nSets the bounds value for symbol sym to value.\n\nSee also has_bounds, get_bounds.\n\n\n\n\n\n","category":"function"},{"location":"API/#Initialization","page":"API","title":"Initialization","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"find_fixpoint\ninitialize_component!\ninit_residual","category":"page"},{"location":"API/#NetworkDynamics.find_fixpoint","page":"API","title":"NetworkDynamics.find_fixpoint","text":"find_fixpoint(nw::Network, [x0::NWState=NWState(nw)], [p::NWParameter=x0.p]; kwargs...)\nfind_fixpoint(nw::Network, x0::AbstractVector, p::AbstractVector; kwargs...)\nfind_fixpoint(nw::Network, x0::AbstractVector; kwargs...)\n\nConvenience wrapper around SteadyStateProblem from SciML-ecosystem. Constructs and solves the steady state problem, returns found value wrapped as NWState.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.initialize_component!","page":"API","title":"NetworkDynamics.initialize_component!","text":"initialize_component!(cf::ComponentModel; verbose=true, kwargs...)\n\nInitialize a ComponentModel by solving the corresponding NonlinearLeastSquaresProblem. During initialization, everyting which has a default value (see Metadata) is considered \"fixed\". All other variables are considered \"free\" and are solved for. The initial guess for each variable depends on the guess value in the Metadata.\n\nThe result is stored in the ComponentModel itself. The values of the free variables are stored in the metadata field init.\n\nThe kwargs are passed to the nonlinear solver.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.init_residual","page":"API","title":"NetworkDynamics.init_residual","text":"init_residual(cf::T; t=NaN, recalc=false)\n\nCalculates the residual |du| for the given component model for the values provided via default and init Metadata.\n\nIf recalc=false just return the residual determined in the actual initialization process.\n\nSee also initialize_component!.\n\n\n\n\n\n","category":"function"},{"location":"API/#Execution-Types","page":"API","title":"Execution Types","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"ExecutionStyle\nSequentialExecution\nPolyesterExecution\nThreadedExecution\nKAExecution","category":"page"},{"location":"API/#NetworkDynamics.ExecutionStyle","page":"API","title":"NetworkDynamics.ExecutionStyle","text":"abstract type ExecutionStyle{buffered::Bool} end\n\nAbstract type for execution style. The coreloop dispatches based on the Execution style stored in the network object.\n\nbuffered=true means that the edge input es explicitly gathered, i.e. the vertex outputs in the output buffer will be copied into a dedicated input buffer for the edges.\nbuffered=false means, that the edge inputs are not explicitly gathered, but the corloop will perform a redirected lookup into the output buffer.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.SequentialExecution","page":"API","title":"NetworkDynamics.SequentialExecution","text":"struct SequentialExecution{buffered::Bool}\n\nSequential execution, no parallelism. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PolyesterExecution","page":"API","title":"NetworkDynamics.PolyesterExecution","text":"struct PolyesterExecution{buffered}\n\nParallel execution using Polyester.jl. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.ThreadedExecution","page":"API","title":"NetworkDynamics.ThreadedExecution","text":"struct ThreadedExecution{buffered}\n\nParallel execution using Julia threads. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.KAExecution","page":"API","title":"NetworkDynamics.KAExecution","text":"struct KAExecution{buffered}\n\nParallel execution using KernelAbstractions.jl. Works with GPU and CPU arrays. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#Aggregators","page":"API","title":"Aggregators","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"Aggregator\nSequentialAggregator\nSparseAggregator\nThreadedAggregator\nPolyesterAggregator\nKAAggregator","category":"page"},{"location":"API/#NetworkDynamics.Aggregator","page":"API","title":"NetworkDynamics.Aggregator","text":"abstract type Aggregator end\n\nAbstract sypertype for aggregators. Aggregators operate on the output buffer of all components and fill the aggregation buffer with the aggregatated edge values per vertex.\n\nAll aggregators have the constructor\n\nAggegator(aggfun)\n\nfor example\n\nSequentialAggreator(+)\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.SequentialAggregator","page":"API","title":"NetworkDynamics.SequentialAggregator","text":"SequentialAggregator(aggfun)\n\nSequential aggregation.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.SparseAggregator","page":"API","title":"NetworkDynamics.SparseAggregator","text":"SparseAggregator(+)\n\nOnly works with additive aggregation +. Aggregates via sparse inplace matrix multiplication. Works with GPU Arrays.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.ThreadedAggregator","page":"API","title":"NetworkDynamics.ThreadedAggregator","text":"ThreadedAggregator(aggfun)\n\nParallel aggregation using Julia threads.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PolyesterAggregator","page":"API","title":"NetworkDynamics.PolyesterAggregator","text":"PolyesterAggregator(aggfun)\n\nParallel aggregation using Polyester.jl.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.KAAggregator","page":"API","title":"NetworkDynamics.KAAggregator","text":"KAAggregator(aggfun)\n\nParallel aggregation using KernelAbstractions.jl. Works with both GPU and CPU arrays.\n\n\n\n\n\n","category":"type"},{"location":"API/#Utils","page":"API","title":"Utils","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"save_parameters!\nff_to_constraint\nBase.copy(::NetworkDynamics.ComponentModel)","category":"page"},{"location":"API/#NetworkDynamics.save_parameters!","page":"API","title":"NetworkDynamics.save_parameters!","text":"save_parameters!(integrator::SciMLBase.DEIntegrator)\n\nSave the current parameter values in the integrator. Call this function inside callbacks if the parameter values have changed. This will store a timeseries of said parameters in the solution object, thus alowing us to recosntruct observables which depend on time-dependet variables.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.ff_to_constraint","page":"API","title":"NetworkDynamics.ff_to_constraint","text":"ff_to_constraint(v::VertexModel)\n\nTakes VertexModel v with feed forward and turns all algebraic output states into internal states by defining algebraic constraints contraints 0 = out - g(...). The new output function is just a StateMask into the extended internal state vector.\n\nReturns the transformed VertexModel.\n\n\n\n\n\n","category":"function"},{"location":"API/#Base.copy-Tuple{NetworkDynamics.ComponentModel}","page":"API","title":"Base.copy","text":"copy(c::NetworkDynamics.ComponentModel)\n\nShallow copy of the component model. Creates a deepcopy of metadata and symmetadata but references the same objects everywhere else.\n\n\n\n\n\n","category":"method"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"EditURL = \"../../examples/cascading_failure.jl\"","category":"page"},{"location":"generated/cascading_failure/#Cascading-Failure","page":"Cascading Failure","title":"Cascading Failure","text":"","category":"section"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"This script reimplements the minimal example of a dynamic cascading failure described in Schäfer et al. (2018) [1]. In is an example how to use callback functions to change network parameters. In this case to disable certain lines. This script can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"[1] Schäfer, B., Witthaut, D., Timme, M., & Latora, V. (2018). Dynamically induced cascading failures in power grids. Nature communications, 9(1), 1-13. https://www.nature.com/articles/s41467-018-04287-5","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"The system is modeled using swing equation and active power edges. The nodes are characterized by the voltage angle δ, the active power on each line is symmetric and a function of the difference between source and destination angle δ_src - δ_dst.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"using NetworkDynamics\nusing Graphs\nusing OrdinaryDiffEqTsit5\nusing DiffEqCallbacks\nusing Plots\nusing Test #hide\nimport SymbolicIndexingInterface as SII","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"For the nodes we define the swing equation. State v[1] = δ, v[2] = ω. The swing equation has three parameters: p = (P_ref, I, γ) where P_ref is the power setpopint, I is the inertia and γ is the droop or damping coeficcient.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"The output of the node is just the first state. g=1 is a shorthand for g=StateMask(1:1) which implements a trivial output function g which just takes the first element of the state vector.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function swing_equation(dv, v, esum, p,t)\n P, I, γ = p\n dv[1] = v[2]\n dv[2] = P - γ * v[2] .+ esum[1]\n dv[2] = dv[2] / I\n nothing\nend\nvertex = VertexModel(f=swing_equation, g=1, sym=[:δ, :ω], psym=[:P_ref, :I=>1, :γ=>0.1])","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"Lets define a simple purely active power line whose active power flow is completlye determined by the connected voltage angles and the coupling constant K. We give an additonal parameter, the line limit, which we'll use later in the callback.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function simple_edge(e, v_s, v_d, (K,), t)\n e[1] = K * sin(v_s[1] - v_d[1])\nend\nedge = EdgeModel(;g=AntiSymmetric(simple_edge), outsym=:P, psym=[:K=>1.63, :limit=>1])","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"With the definition of the graph topology we can build the Network object:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"g = SimpleGraph([0 1 1 0 1;\n 1 0 1 1 0;\n 1 1 0 1 0;\n 0 1 1 0 1;\n 1 0 0 1 0])\nswing_network = Network(g, vertex, edge)","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"For the parameters, we create the NWParameter object prefilled with default p values","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"p = NWParameter(swing_network)\n# vertices 1, 3 and 4 act as loads\np.v[(1,3,4), :P_ref] .= -1\n# vertices 2 and 5 act as generators\np.v[(2,5), :P_ref] .= 1.5\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"We can use find_fixpoint to find a valid initial condition of the network","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"u0 = find_fixpoint(swing_network, p)\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"In order to implement the line failures, we need to create a VectorContinousCallback. In the callback, we compare the current flow on the line with the limit. If the limit is reached, the coupling K is set to 0.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"First we can define the affect function:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function affect!(integrator, idx)\n println(\"Line $idx tripped at t=$(integrator.t)\")\n p = NWParameter(integrator) # get indexable parameter object\n p.e[idx, :K] = 0\n auto_dt_reset!(integrator)\n save_parameters!(integrator)\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"There is one important aspect to this function: the save_parameters! call. In the callback, we change the parameters of the network, making the parameters time dependent. The flow on the line is a function P(t) = f(u(t), p(t)). Thus we need to inform the integrator, that a discrete change in parameters happend. With this, the solution object not only tracks u(t) but also p(t) and we may extract the observable P(t) directly.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"The callback trigger condition is a bit more complicated. The straight forward version looks like this:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function naive_condition(out, u, t, integrator)\n # careful, u != integrator.u\n # therefore construct nwstate with Network info from integrator but u\n s = NWState(integrator, u, integrator.p, t)\n for i in eachindex(out)\n out[i] = abs(s.e[i,:P]) - s.p.e[1,:limit] # compare flow with limit for line\n end\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"However, from a performacne perspectiv there are problems with this solution: on every call, we need to perform symbolic indexing into the NWState object. Symbolic indexing is not cheap, as it requires to gather meta data about the network. Luckily, the SymbolicIndexingInterface package which powers the symbolic indexing provides the lower level functions getp and getu which can be used to create and cache accessors to the internal states.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"This still isn't ideal beacuse both getlim and getflow getters will create arrays within the callback. But is far better then resolving the flat state indices every time.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"condition = let getlim = SII.getp(swing_network, epidxs(swing_network, :, :limit)),\n getflow = SII.getu(swing_network, eidxs(swing_network, :, :P))\n function (out, u, t, integrator)\n # careful, u != integrator.u\n # therefore construct nwstate with Network info from integrator but u\n s = NWState(integrator, u, integrator.p, t)\n out .= getlim(s) .- abs.(getflow(s))\n nothing\n end\nend\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"We can combine affect and condition to form the callback.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"trip_cb = VectorContinuousCallback(condition, affect!, ne(g));\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"However, there is another component missing. If we look at the powerflow on the lines in the initial steady state","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"u0.e[:, :P]","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"We see that every flow is below the trip value 1.0. Therefor we need to add a distrubance to the network. We do this by manually disabeling line 5 at time 1.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"trip_first_cb = PresetTimeCallback(1.0, integrator->affect!(integrator, 5));\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"With those components, we can create the problem and solve it.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"prob = ODEProblem(swing_network, uflat(u0), (0,6), copy(pflat(p));\n callback=CallbackSet(trip_cb, trip_first_cb))\nsol = solve(prob, Tsit5());\n# we want to test the reconstruction of the observables # hide\n@test all(!iszero, sol(sol.t; idxs=eidxs(sol,:,:P))[begin]) # hide\n@test all(iszero, sol(sol.t; idxs=eidxs(sol,:,:P))[end][[1:5...,7]]) # hide\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"Through the magic of symbolic indexing we can plot the power flows on all lines:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"plot(sol; idxs=eidxs(sol,:,:P))","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"EditURL = \"../../examples/gas_network.jl\"","category":"page"},{"location":"generated/gas_network/#Dynamic-Flow-in-simple-Gas-Network","page":"Gas Network","title":"Dynamic Flow in simple Gas Network","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This Example is based on the paper","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Albertus J. Malan, Lukas Rausche, Felix Strehle, Sören Hohmann, Port-Hamiltonian Modelling for Analysis and Control of Gas Networks, IFAC-PapersOnLine, Volume 56, Issue 2, 2023, https://doi.org/10.1016/j.ifacol.2023.10.193.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"and tries replicate a simple simulation of flow in a 3-node gas network.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"We start by importing the necessary packages:","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"using NetworkDynamics\nusing ModelingToolkit\nusing DynamicQuantities\nusing ModelingToolkit: D as Dt, t as t\nusing Test\nusing StaticArrays\nusing LinearInterpolations\nusing OrdinaryDiffEqTsit5\nusing CairoMakie\nCairoMakie.activate!(type=\"svg\") #hide","category":"page"},{"location":"generated/gas_network/#Node-Models","page":"Gas Network","title":"Node Models","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"In this example, we use the equation based modeling using ModelingToolkit.jl. To verify the equations on a basic level we also provide units to eveything to perform dimensionality checks.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"There are 2 node models used in the paper. The first node type has a constant pressure. Additionally, we ad some \"internal\" state q̃_inj which we want to plot later (see also Observables).","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@mtkmodel ConstantPressureNode begin\n @parameters begin\n p_set, [description=\"Constant pressure setpoint\", unit=u\"Pa\"]\n end\n @variables begin\n p(t) = p_set, [description=\"Pressure\", unit=u\"Pa\", output=true]\n q̃_nw(t), [description=\"aggregated flow from pipes into node\", unit=u\"m^3/s\", input=true]\n q̃_inj(t), [description=\"internal state for introspection\", unit=u\"m^3/s\"]\n end\n @equations begin\n p ~ p_set\n q̃_inj ~ -q̃_nw\n end\nend\nnothing #hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The second node model is a variable pressure node. It has one output state (the pressure) and one input state, the aggregated flows from the connected pipes. As an internal state we have the injected flow from our source/load. The source/load behaviour itself is provided via a time dependent function.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@mtkmodel VariablePressureNode begin\n @structural_parameters begin\n load_profile # time dependent load profile\n end\n @parameters begin\n C, [description=\"Lumped capacitance of connected pipes\", unit=u\"m^4 * s^2 / kg\"]\n end\n @variables begin\n p(t)=5e6, [description=\"Pressure\", unit=u\"Pa\", output=true]\n q̃_inj(t), [description=\"external injection into node\", unit=u\"m^3/s\"]\n q̃_nw(t), [description=\"aggregated flow from pipes into node\", unit=u\"m^3/s\", input=true]\n end\n @equations begin\n q̃_inj ~ load_profile(t)\n C * Dt(p) ~ q̃_inj + q̃_nw # (30)\n end\nend\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Pipe-Model","page":"Gas Network","title":"Pipe Model","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The pipe is modeld as a first order ODE for the volumetric flow at the dst end. It has two inputs: the pressure at the source and and the pressure at the destination end. Later on, we'll specify the model to be antisymmetric, thus the flow is calculated explicitly for the destination end, but the source end will just recive just that times (-1).","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@mtkmodel Pipe begin\n @parameters begin\n L, [description=\"Length of pipe\", unit=u\"m\"]\n D, [description=\"Diameter of pipe\", unit=u\"m\"]\n A, [description=\"Cross-sectional area of pipe\", unit=u\"m^2\"]\n sinθ, [description=\"Angle of inclination\" ]\n γ, [description=\"Friction efficiency factor\"]\n η, [description=\"Dynamic viscosity\", unit=u\"kg/(m*s)\"]\n r, [description=\"Pipe roughness\", unit=u\"m\"]\n g, [description=\"Gravitational acceleration\", unit=u\"m/s^2\"]\n T, [description=\"simulation temperature\", unit=u\"K\"]\n Tc, [description=\"crictical temperature\", unit=u\"K\"]\n pc, [description=\"critical pressure\", unit=us\"Pa\"]\n Rs, [description=\"Specific gas constant for natural gas\", unit=us\"J/(kg*K)\"]\n c̃, [description=\"Speed of sound in fluid at standard conditions\", unit=u\"m/s\"]\n ρ̃, [description=\"standard density\", unit=u\"kg/m^3\"]\n p̃, [description=\"standard pressure\", unit=us\"Pa\"]\n end\n @variables begin\n p_src(t), [description=\"Pressure at source end\", unit=us\"Pa\", input=true]\n p_dst(t), [description=\"Pressure at destination end\", unit=us\"Pa\", input=true]\n q̃(t)=1, [description=\"Flow through pipe\", unit=u\"m^3/s\", output=true]\n Re(t), [description=\"Reynolds number\"]\n λ(t), [description=\"Friction factor\"]\n λe(t), [description=\"Effective friction factor\"]\n pM(t), [description=\"mean pressure\", unit=us\"Pa\"]\n Z(t), [description=\"compressibility factor\"]\n ρ(t), [description=\"density\", unit=u\"kg/m^3\"]\n c(t), [description=\"speed of sound\", unit=u\"m/s\"]\n end\n @equations begin\n Z ~ 1 - 3.52 * pM/pc * exp(-2.26*(T/Tc)) + 0.274 * (pM/pc)^2 * exp(-1.878*(T/Tc)) # (5)\n ρ ~ pM / (Rs * T * Z) # (4)\n\n # TODO: Whats the correct speed of sound?\n c ~ sqrt(T * Rs * Z) # (4) # pressure/temp dependent\n # c ~ c̃ # \"standard\" speed of sound based on standard conditions\n\n # TODO: Whats the correct Reynolds number?\n Re ~ (ρ * abs(q̃*p̃/pM) * D) / (η * A) # (6) # based \"actual\" conditions\n # Re ~ (ρ̃ * abs(q̃) * D) / (η * A) # (6) # based on standard conditions\n\n λ ~ ifelse(Re < 2300,\n 64/Re, # laminar (7)\n (2*log10(4.518/Re * log10(Re/7) + r/(3.71*D)))^(-2) # turbulent (8)\n )\n λe ~ λ/γ^2 # (10)\n pM ~ 2/3*(p_src + p_dst - (p_src*p_dst)/(p_src + p_dst)) # (20)\n\n Dt(q̃) ~ A/(L*ρ̃)*(-(λe * ρ̃^2 * c^2 * L * abs(q̃))/(2 * D * A^2 * pM) * q̃ - (g * L * sinθ)/(c^2) * pM + (p_src - p_dst)) # (31)\n end\nend\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Parametrization","page":"Gas Network","title":"Parametrization","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The parameterization turned out to be a bit tricky. There might be errors in there.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Some of them are quite cleare and explicitly given.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"g = 9.81u\"m/s^2\" # that we just know\nRs = 518.28u\"J/(kg*K)\" # Specific gas constant for natural gas\nη = 1e-5u\"kg/(m*s)\" # Dynamic viscosity\npc = 46.5u\"bar\" # Critical pressure\np̃ = 1.01325u\"bar\" # standard pressure\nTc = 190.55u\"K\" # critical temperature\nT̃ = 273.15u\"K\" # standard temperature\nT = 278u\"K\" # simulation temperature\nγ = 0.98 # friction efficiency factor\nr = 0.012u\"mm\" # pipe roughness\nD = 0.6u\"m\" # pipe diameter\n\n# TODO: here is switched the lenths l12 and l13. The results are better. Is this a mistake in the paper?\nL₁₂ = 90u\"km\"\nL₁₃ = 80u\"km\"\nL₂₃ = 100u\"km\"\nΔh₁ = 0u\"km\" # this value is different for different sims in the paper\np₁_set = 50u\"bar\"\nnothing # hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The geometric parameters for the pipes can be directly derived.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"A = π/4 * D^2\nsinθ₁₂ = ustrip(Δh₁ / L₁₂)\nsinθ₁₃ = ustrip(Δh₁ / L₁₃)\nsinθ₂₃ = 0.0\nnothing # hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Lastly, we need to calculate the compressibility factor, the speed of sound and the density at standard conditions:","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Z̃ = 1 - 3.52 * p̃/pc * exp(-2.26*(T̃/Tc)) + 0.274 * (p̃/pc)^2 * exp(-1.878*(T̃/Tc)) # (5)\nc̃ = sqrt(T̃ * Rs * Z̃) # (4) at standard conditions\nρ̃ = p̃ / (Rs * T̃ * Z̃) # (4) at standard conditions\n\nnothing # hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The equivalent \"pressure capacity\" at the nodes is calculated as a sum of the connected pipe parameters according to (28).","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Here use defintions based on the speed and \"standard\" conditions.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"C₂ = L₁₂*A/(2*ρ̃*c̃^2) + L₂₃*A/(2*ρ̃*c̃^2) # (28)\nC₃ = L₁₃*A/(2*ρ̃*c̃^2) + L₂₃*A/(2*ρ̃*c̃^2) # (28)\nnothing #hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Alternatively, we could calculate Z2 and Z3 based on the actuel pressure and simulation temperature. Then we could calculated the speed of sound for the \"correct\" conditions at the node. It seems to have very little effect on the actual results so I kept it simple.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"nothing #hide","category":"page"},{"location":"generated/gas_network/#Load-Profile","page":"Gas Network","title":"Load Profile","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The paper specifies the load profile at two nodes. We use the package LinearInterpolations.jl to get a callable object which represents this picewise linear interpolation.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"However, this function is not Symbolics.jl compatible, so we need to stop Symbolics.jl/ModelingToolkit.jl from tracing it. To do so, we use @register_symbolic to declare it as a symbolic function which is treated as a blackbox.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Additionally, we need to tell ModelingToolkit about the units of this object. This is just used for the static unit check during construction of the model. Later one, when we generate the Julia code from the symbolic reepresentation all units will be stripped.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"note: Discontinuities in RHS\nThe picewise linear interpolated function creates discontinuities in the RHS of the system. However since we know the times exactly, we can handle this by simply giving a list of explicit tstops to the solve command, to make sure those are hit exactly.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"load2(t) = -Interpolate(SA[0, 4, 12, 20, 24]*3600, SA[20, 30, 10, 30, 20], extrapolate=LinearInterpolations.Constant(20))(t)\nload3(t) = -Interpolate(SA[0, 4, 12, 20, 24]*3600, SA[40, 50, 30, 50, 40], extrapolate=LinearInterpolations.Constant(40))(t)\n@register_symbolic load2(t)\n@register_symbolic load3(t)\nModelingToolkit.get_unit(op::typeof(load2), _) = u\"m^3/s\"\nModelingToolkit.get_unit(op::typeof(load3), _) = u\"m^3/s\"\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Building-the-Network","page":"Gas Network","title":"Building the Network","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To bild the Network we first need to define the components. This is a two step process:","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"first create the symbolic ODESystem using ModelingToolkit\nsecondly build a NetworkDynamics component model (VertexModel/EdgeModel) based on the symbolic system.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"In the first step we can use the keyword arguments to pass \"default\" values for our parameters and states. Those values will be automaticially transfered to the metadata of the component model the second step.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The second step requires to define the interface variables, i.e. what are the \"input\" states of your component model and what are the \"output\" states. For VertexModel the input state is the aggregated flow of all connected pipes. The output state is the pressure of the node.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named v1_mtk = ConstantPressureNode(p_set=p₁_set)\nv1 = VertexModel(v1_mtk, [:q̃_nw], [:p]; name=:v1, vidx=1)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named v2_mtk = VariablePressureNode(C=C₂, load_profile=load2)\nv2 = VertexModel(v2_mtk, [:q̃_nw], [:p]; name=:v2, vidx=2)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named v3_mtk = VariablePressureNode(C=C₃, load_profile=load3)\nv3 = VertexModel(v3_mtk, [:q̃_nw], [:p]; name=:v3, vidx=3)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"For the edge Model we have two inputs: the pressure on both source and destination end. There is a single output state: the volumetric flow. However we also need to tell NetworkDynamics about the coupling type. In this case we use AntiSymmetric, which meas that the source end will recieve the same flow, just inverted sign.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named e12_mtk = Pipe(; L=L₁₂, sinθ=sinθ₁₂, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)\n@named e13_mtk = Pipe(; L=L₁₃, sinθ=sinθ₁₃, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)\n@named e23_mtk = Pipe(; L=L₂₃, sinθ=sinθ₂₃, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)\n\ne12 = EdgeModel(e12_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e12, src=1, dst=2)\ne13 = EdgeModel(e13_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e13, src=1, dst=3)\ne23 = EdgeModel(e23_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e23, src=2, dst=3)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To build the network object we just need to pass the vertices and edges to the constructor.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Note that we've used the vidx and src/dst keywords in the constructors to define for each component to which \"part\" of the network it belongs.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This means, the constructor can automaticially construct a graph based on those informations and we don't need to pass it explicitly.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"nw = Network([v1, v2, v3], [e12, e13, e23])","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"As a result, we recive a network with 3 unique types (v2 and v3 are similar but structurally different, because both functions capure a unique loadprofile function).","category":"page"},{"location":"generated/gas_network/#Finding-a-Steady-State","page":"Gas Network","title":"Finding a Steady State","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To simulate the systme, we first need to find a steadystate. As a \"guess\" for that we create a NWState object from the network. This will allocate flat arrays for states u and parameters p and fill them with the default values.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"uguess = NWState(nw)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This is not a steadystate of the system however. To find a true steadystate we want to ensure that the lhs of the system is zero. We can solve for a steady state numerically by defining a Nonlinear Rootfind problem.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To do so, we need to wrap the Network object in a closure.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"nwwrap = (du, u, p) -> begin\n nw(du, u, p, 0)\n nothing\nend\ninitprob = NonlinearProblem(nwwrap, uflat(uguess), pflat(uguess))\ninitsol = solve(initprob)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"We can create a new NWState object by wrapping the solution from the nonlinear problem and the original prameters in a new NWState object.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"u0 = NWState(nw, initsol.u, uguess.p)","category":"page"},{"location":"generated/gas_network/#Solving-the-ODE","page":"Gas Network","title":"Solving the ODE","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Using this as our initial state we can create the actual ODEProblem. Since the ode allways operates on flat state and aprameter arrays we use uflat and pflat to extract them.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"prob = ODEProblem(nw, uflat(u0), (0.0,24*3600), copy(pflat(u0)))\nsol = solve(prob, Tsit5(), tstops=[0,4,12,20,24]*3600)\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Inspect-the-Solution","page":"Gas Network","title":"Inspect the Solution","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Inspecting the solution is all which is left to do.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"xticks = ((0:4:24)*3600, string.(0:4:24)) # its nice to display hours\nfig = begin\n _fig = Figure()\n row = 1\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"pressure [Pa]\", title=\"Pressure at nodes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n ylims!(ax, 47.9e5, 49.9e5)\n for i in 1:3\n lines!(ax, sol, idxs=vidxs(nw, i, :p); label=\"v$i\", color=Cycled(i))\n end\n axislegend(ax)\n row += 1\n\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"flow [m³/s]\", title=\"Flow through pipes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n ylims!(ax, 16, 44)\n for i in 1:2\n lines!(ax, sol, idxs=eidxs(nw, i, :q̃); label=\"e$i flow\", color=Cycled(i))\n end\n axislegend(ax, position=:rb)\n row += 1\n _fig\nend","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Notably, the \"internal\" states defined in the symbolic models are not \"states\" in the sense of the ODE. For example, we captured the load profile in the q̃_inj state of the VariablePressureNode. The only dynamic state of the model however is p. Using the \"observables\" mechanism from SciML, which is implemented by NetworkDynamics, we can reconstruct those \"optimized\" states which have been removed symbolicially. Here we plot the reconstructed load profile of nodes 2 and 3. Also, we know that node 1 is infinetly stiff, acting as an infinite source of volumetric flow. We can reconstruct this flow too.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"fig = begin\n _fig = Figure()\n row = 1\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"flow [m³/s]\", title=\"Flow at nodes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n lines!(ax, sol, idxs=vidxs(nw, 1, :q̃_inj); label=\"v1 compensation\", color=Cycled(1))\n for i in 2:3\n lines!(ax, sol, idxs=vidxs(nw, i, :q̃_inj); label=\"v$i load profile\", color=Cycled(i))\n end\n axislegend(ax, position=:rc)\n _fig\nend","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Lastly we want to observe two internal states of the pipes: the Reynolds number and the mean pressure. We see, that we're purely in the turbulent flow regime.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"fig = begin\n _fig = Figure()\n row = 1\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"Reynolds number\", title=\"Reynolds number\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n for i in 1:3\n lines!(ax, sol, idxs=eidxs(nw, i, :Re); label=\"e $i\", color=Cycled(i))\n end\n hlines!(ax, 2300, color=:black, linestyle=:dash, label=\"L/T transition\")\n axislegend(ax, position=:rb)\n row += 1\n\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"Mean pressure [Pa]\", title=\"Mean pressure in pipes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n for i in 1:3\n lines!(ax, sol, idxs=eidxs(nw, i, :pM); label=\"e $i\", color=Cycled(i))\n end\n axislegend(ax, position=:rb)\n _fig\nend","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This page was generated using Literate.jl.","category":"page"},{"location":"#NetworkDynamics","page":"General","title":"NetworkDynamics","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"A package for working with dynamical systems on complex networks. NetworkDynamics.jl provides an interface between Graphs.jl and DifferentialEquations.jl. It allows for high performant simulation of dynamic networks by describing the local dynamics on the edges and vertices of the graph.","category":"page"},{"location":"","page":"General","title":"General","text":"The behavior of a node or an edge can be described by algebraic equations, by differential algebraic equation (DAEs) in mass matrix form or by ordinary differential equations (ODE). ","category":"page"},{"location":"","page":"General","title":"General","text":"The central construction is the function Network that receives functions describing the local dynamics on the edges and nodes of a graph g as inputs, and returns a composite function compatible with the DifferentialEquations.jl calling syntax.","category":"page"},{"location":"","page":"General","title":"General","text":"nd = Network(g, vertex_dynamics, edge_dynamics)\nnd(dx, x, p, t)","category":"page"},{"location":"","page":"General","title":"General","text":"Main features:","category":"page"},{"location":"","page":"General","title":"General","text":"Clear separation of local dynamics and topology: you can easily change topology of your system or switching out dynamical components.\nHigh performance when working with heterogeneous models (which means heaving different local dynamics in different parts of your network).\nSymbolic Indexing into solutions and states: NetworkDynamics keeps track of the states of the individual subsystems.\nDifferent execution schemes: NetworkDynamics exploits the known inter-dependencies between components to auto parallelize execution, even on GPUs!\nEquation based models: use ModelingToolkit.jl to define local dynamics, use NetworkDynamics.jl to combine them into large networks!","category":"page"},{"location":"#Where-to-begin?","page":"General","title":"Where to begin?","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"Check out the Mathematical Model to understand the underlying modelling ideas of NetworkDynamics followed by the page on Network Construction to learn how to implement you own models.","category":"page"},{"location":"","page":"General","title":"General","text":"If you prefer to look at some concrete code first check out the Getting Started tutorial!","category":"page"},{"location":"#Installation","page":"General","title":"Installation","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"Installation is straightforward with Julia's package manager.","category":"page"},{"location":"","page":"General","title":"General","text":"(v1.11) pkg> add NetworkDynamics","category":"page"},{"location":"#Reproducibility","page":"General","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"
Direct dependencies used for this documentation:","category":"page"},{"location":"","page":"General","title":"General","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"General","title":"General","text":"
","category":"page"},{"location":"","page":"General","title":"General","text":"
Julia Version:","category":"page"},{"location":"","page":"General","title":"General","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"General","title":"General","text":"
","category":"page"},{"location":"","page":"General","title":"General","text":"
Full Manifest:","category":"page"},{"location":"","page":"General","title":"General","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"General","title":"General","text":"
","category":"page"},{"location":"#Funding","page":"General","title":"Funding","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"Development of this project was in part funded by the German Federal Ministry for Economic Affairs and Climate Action as part of the OpPoDyn-Project (Project ID 01258425/1, 2024-2027).","category":"page"},{"location":"","page":"General","title":"General","text":"","category":"page"},{"location":"network_construction/#Network-Construction","page":"Network Construction","title":"Network Construction","text":"","category":"section"},{"location":"network_construction/#Building-a-Network","page":"Network Construction","title":"Building a Network","text":"","category":"section"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"The main type of NetworkDyanmics.jl is a Network. A network bundles various component models (edge and vertex models) together with a graph to form a callable object which represents the RHS of the overall dynamical system, see Mathematical Model.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"A Network is build by passing a graph g, vertex models vertexm and edge models edgem.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"nw = Network(g, vertexm, edgem; kwargs...)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Two important keywords for the Network constructor are:","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"execution: Defines the ExecutionStyle of the coreloop, e.g. SequentialExecution{true}(). A execution style is a special struct which tells the backend how to parallelize for example. A list of available executions styles can be found under Execution Types in the API.\naggregator: Tells the backend how to aggregate and which aggregation function to use. Aggregation is the process of creating a single vertex input by reducing over the outputs of adjecent edges of said vertex. The aggregator contains both the function and the algorith. E.g. SequentialAggregator(+) is a sequential aggregation by summation. A list of availabe Aggregators can be found under Aggregators in the API.","category":"page"},{"location":"network_construction/#Building-VertexModels","page":"Network Construction","title":"Building VertexModels","text":"","category":"section"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"This chapter walks through the most important aspects when defining custom vertex model. For a list of all keyword arguments please check out the docstring of VertexModel. As an example, we'll construct an second order kuramoto model, because that's what we do.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"using NetworkDynamics #hide\nfunction kuramoto_f!(dv, v, esum, p, t)\n M, P, D = p\n dv[1] = v[2]\n dv[2] = (P - D*v[2] + esum[1])/M\n nothing\nend\nfunction kuramoto_g!(y, v, esum, p, t)\n y[1] = v[1]\n nothing\nend\nVertexModel(; f=kuramoto_f!, g=kuramoto_g!, dim=2, pdim=3, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Those keywords are the minimum metadata we need to provide.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"However there is a problem: the vertex is classified as a FeedForward vertex, which is unnecessary. We can improve the implementation of g according to the Feed Forward Behavior section.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function kuramoto_g_noff!(y, v, p, t)\n y[1] = v[1]\n nothing\nend\nVertexModel(; f=kuramoto_f!, g=kuramoto_g_noff!, dim=2, pdim=3, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"It is still annoying to explicitly write this trivial output function. You can prevent this by using StateMask. By writing","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"VertexModel(; f=kuramoto_f!, g=StateMask(1:1), dim=2, pdim=3)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"we told the vertex model, that the output is part of the states x[1:1]. This enables a few things:","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"outdim is not needed anymore, can be inferred from StateMask\noutsym is not a generic :o any more but inferred from the state symbols.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"We can be even less verbose by writing g=1:1 or just g=1.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"In a last we define better names for our states and parameters as well as assigning a position in the graph to enable the graphless network construction. Whenever you provide sym keyword the corresponding dim keyword is not neccessary anymore. We end up with a relatively short definition","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"VertexModel(; f=kuramoto_f!, g=1,\n sym=[:θ, :ω], psym=[:M=>1, :P=>0.1, :D=>0], \n insym=[:P_nw], name=:swing, vidx=1)","category":"page"},{"location":"network_construction/#Building-EdgeModels","page":"Network Construction","title":"Building EdgeModels","text":"","category":"section"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"This chapter walks through the most important aspects when defining custom edge models. For a list of all keyword arguments please check out the docstring of EdgeModel.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"As an example edge model we want to define standard sinusoidal coupling between the vertices in our network. The full definition looks like this:","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_f!(de, e, vsrc, vdst, p, t)\n nothing\nend\nfunction edge_g!(ysrc, ydst, e, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\n ysrc[1] = -ydst[1]\nend\nEdgeModel(; f=edge_f!, g=edge_g!, dim=0, pdim=1, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"This is a purely \"static\" edge without internal states. This means we can omit f and dim entirely. Also, we can define a variant of g without the e input","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_g_ff!(ysrc, ydst, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\n ysrc[1] = -ydst[1]\nend\nEdgeModel(;g=edge_g_ff!, pdim=1, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"which no classifies as a PureFeedForward edge. In cases like this, where the edge is actually anti symmetric we can alternatively define a single sided output function and wrapping it in an AntiSymmetric object","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_g_s!(ydst, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\nend\nEdgeModel(;g=AntiSymmetric(edge_g_ff!), pdim=1, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"which can also lead to briefer output naming. Available single sided wrappers are","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Directed (no coupling at src), \nAntiSymmetric (same coupling at src and dst),\nSymmetric (inverse coupling at dst) and\nFiducial (define separate g for both ends).","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Once again we can add additonal data like defining a src and dst index","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_g_s!(ydst, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\nend\nEdgeModel(;g=AntiSymmetric(edge_g_ff!), psym=:K=>1, outsym=:P, insym=:θ, src=1, dst=4)","category":"page"}] +[{"location":"mtk_integration/#ModelingToolkit-Integration","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"","category":"section"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"NetworkDynamics.jl is compatible with ModelingTookit.jl (MTK). The general idea is to use MTK to define component models (i.e. edge and vertex dynamics) which are then connected on network scale using NetworkDynamics.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"The main entry point for this interop are the constructors","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"VertexModel(::ODESystem, inputs, outputs)\nEdgeModel(::ODESystem, srcin, dstin, [srscout], dstout)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"whose docstrings can be found in the Component Models with MTK section in the API.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"These constructors will:","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"transforming the states marked as input to parameters and structural_simplifying the system,\ngenerating the f and g functions,\ngenerate code for observables,\nport all supported Metadata from MTK symbols to component symbols and\noutput a Vertex-/EdgeModel function compatible with NetworkDynamics.jl.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"The main usecase for this feature is when you want to build relatively complex component models but interconnect them in a very homogeneous way (i.e. having the same output/input pairings in the whole system).","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"In theory, you can achieve everything you want to do with plain MTK. The idea of combining the two is, that NetworkDynamics offers far less flexibility when in comes to interconnection of subsystems on the network level. This might allow ND to exploit more knowledge of the structure without very expensive operations such as tearing of thousands of equations.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"warning: Warning\nModelingToolkit is a fast paced library with lots of functionality and ever growing complexity. As such the provided interface is kinda experimental. Some features of MTK are straight up unsupported, for example events within models or delay differential equations.","category":"page"},{"location":"mtk_integration/#RC-Circuit-Example","page":"ModelingToolkit Integration","title":"RC-Circuit Example","text":"","category":"section"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"In good MTK tradition, this feature will be explained along a simple RC circuit example. The Dynamic Flow in simple Gas Network example is another showcase of the MTK constructors.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"The system to model is 2 node, 1 edge network. The node output states are the voltage (to ground), the edge output sates are the currents at both ends.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"\nideal v source Resistor Capacitor\n v1 o─←────MMM────→─o v2\n │ ┴ \n (↗) ┬\n │ │\n ⏚ ⏚","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"Obviously there is no need in modeling such a small system using NetworkDynamics, however the method extends quite easily to construct large electrical networks reusing the same fundamental building blocks.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"using NetworkDynamics\nusing ModelingToolkit\nusing ModelingToolkit: t_nounits as t, D_nounits as D\nusing OrdinaryDiffEqTsit5\nusing CairoMakie","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"All our components have \"terminals\", which have a voltage and current. We don't use the @connector from MTK here because our pins mark the interface towards the network and do not follow the MTK connector semantics.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel NWTerminal begin\n @variables begin\n v(t), [description=\"Voltage at node\"]\n i(t), [description=\"Current flowing into node\"]\n end\nend\nnothing #hide","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"An ideal voltage source is just a model which pins its output voltage to a fixed parameter. 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 mechanism. The VertexModel can be generated from an ODESystem by providing names of the input and output states:","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel VoltageSource begin\n @components begin\n p = NWTerminal()\n end\n @parameters begin\n V = 1.0\n end\n @variables begin\n i(t), [description=\"produced current by ideal voltage source (observable)\"]\n end\n @equations begin\n i ~ -p.i\n p.v ~ V\n end\nend\n@named vs = VoltageSource()\nvs_vertex = VertexModel(vs, [:p₊i], [:p₊v]; vidx=1)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"A capacitor is a slightly more complicated model. Its voltage is defined as an differential equation based on the inflowing current.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel Capacitor begin\n @components begin\n p = NWTerminal(;v=0)\n end\n @parameters begin\n C = 1.0\n end\n @equations begin\n D(p.v) ~ p.i / C\n end\nend\n@named cap = Capacitor()\ncap_vertex = VertexModel(cap, [:p₊i], [:p₊v], vidx=2)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"For the resistor we need two pins, one for the src and one for the dst side. The equations are straight forward.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"@mtkmodel Resistor begin\n @components begin\n src = NWTerminal()\n dst = NWTerminal()\n end\n @parameters begin\n R = 1.0\n end\n @equations begin\n dst.i ~ (src.v - dst.v)/R\n src.i ~ -dst.i\n end\nend\n@named resistor = Resistor()\nresistor_edge = EdgeModel(resistor, [:src₊v], [:dst₊v], [:src₊i], [:dst₊i]; src=:vs, dst=:cap)","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"Having all those components defined, we can build the network. We don't need to provide a graph object here because we specified the placement in the graph on a per component basis.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"nw = Network([vs_vertex, cap_vertex], [resistor_edge])","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"We can see, that NetworkDynamics internally is able to reduce all of the \"output\" states. We end up with a plain ODE of a single state.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"Now we can simulate the system. For that we generate the u0 object. Since the metadata (such as default values) was automatically transferred, we can straight away construct the ODEProblem and solve the system.","category":"page"},{"location":"mtk_integration/","page":"ModelingToolkit Integration","title":"ModelingToolkit Integration","text":"u0 = NWState(nw) # generate state based on default values\nprob = ODEProblem(nw, uflat(u0), (0, 10.0), pflat(u0))\nsol = solve(prob, Tsit5())\n\n# plot the solution\nfig, ax1, p = plot(sol; idxs=VIndex(:cap, :p₊v), label=\"Capacitor Voltage\");\naxislegend(ax1)\nax2 = Axis(fig[2,1])\nplot!(ax2, sol; idxs=VIndex(:vs, :i), label=\"Current produced by ideal v source\")\naxislegend(ax2)\nfig # hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"EditURL = \"../../examples/getting_started_with_network_dynamics.jl\"","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Getting-Started","page":"Getting Started","title":"Network Diffusion","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This introductory example explains the use of the basic types and constructors in NetworkDynamics.jl by modeling a simple diffusion on an undirected network.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Theoretical-background","page":"Getting Started","title":"Theoretical background","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Diffusion processes are relevant for phenomena as diverse as heat conduction, electrical currents, and random walks. Generally speaking they describe the tendency of systems to evolve towards a state of equally distributed heat, charge or concentration. In such system the local temperature (or concentration) changes according to its difference with its neighborhood, i.e. the temperature gradient.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Let g be a graph with N nodes and adjacency matrix A. Let v = (v_1 dots v_n) be a vector of (abstract) temperatures or concentrations at each node i = 1 dots N. Then the rate of change of state v_i is described by its difference with its neighbors and we obtain the following ordinary differential equation","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v_i = sum_j=1^N A_ji (v_j - v_i)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The sum on the right hand side plays the role of a (discrete) gradient. If the temperature at node i is higher than at its neighboring node j it will decrease along that edge.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Modeling-diffusion-in-NetworkDynamics.jl","page":"Getting Started","title":"Modeling diffusion in NetworkDynamics.jl","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"We begin by loading the necessary packages.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"using Graphs\nusing NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing StableRNGs\nusing Plots\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"From the above considerations we see that in this model the nodes do not have any internal dynamics - if a node was disconnected from the rest of the network its state would never change, since then A_ji = 0 forall j and hence dot v_i = 0. This means that the evolution of a node depends only on the interaction with its neighbors. In NetworkDynamics.jl, interactions with neighbors are described by equations for the edges.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"In order to bring this equation into the form required by NetworkDynamics.jl we need split the dynamics into edge and vertex parts and bring them into the correct input-output formulation. The vertices have one internal state v which is also the output. The input is the sum over all flows of connected edges. This directly correspons to the component model definition outlined in Mathematical Model:","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"beginaligned\ndot x^mathrmv = f^mathrm v(u^mathrm v sum_k^textincident y^mathrm e_k p^mathrm v t) = sum_k^mathrmincident y^mathrme_k \ny^mathrmv = g^mathrm v(u^mathrm v sum_k^textincident y^mathrm e_k p^mathrm v t) = x^mathrmv\nendaligned","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The edge dynamics on the other hand do not have any internal states. Thus we only define the output as the difference between the source and destination vertex:","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"beginaligned\ny^mathrm e_mathrmdst = g_mathrmdst^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t) = y^mathrm v_mathrmsrc - y^mathrm v_mathrmdst\ny^mathrm e_mathrmsrc = g_mathrmsrc^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t) = y^mathrm v_mathrmdst - y^mathrm v_mathrmsrc\nendaligned","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Definition-of-EdgeModel","page":"Getting Started","title":"Definition of EdgeModel","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"function diffusionedge_g!(e_dst, v_src, v_dst, p, t)\n # e_dst, v_src, v_dst are arrays, hence we use the broadcasting operator\n e_dst .= v_src .- v_dst\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The function diffusionedge_g! takes as inputs the current state of the edge e, its source vertex v_src, its destination vertex v_dst, a vector of parameters p and the time t. In order to comply with the syntax of NetworkDynamics.jl we always have to define functions for edges with exactly these arguments, even though we do not need p and t for the diffusion example.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"diffusionedge_g! is called a mutating function, since it modifies (or mutates) one of its inputs, namely the edge state e. As a convention in Julia names of mutating functions end with an !. The use of mutating functions reduces allocations and thereby speeds up computations. After the function call the edge's output value e equals the difference between its source and its destination vertex (i.e. the discrete gradient along that edge).","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Notably, this function only models g_mathrmdst. However we can wrap this single-sided output function in an AntiSymmetric output wrapper to construct the EdgeModel:","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"nd_diffusion_edge = EdgeModel(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow])","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Definition-of-VertexModel","page":"Getting Started","title":"Definition of VertexModel","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"For undirected graphs, the edgefunction! specifies the coupling from a source- to a destination vertex. The contributions of the connected edges to a single vertex are \"aggregated\". Default aggregation is the summation of all incident edge states. The aggregated edge state is made available via the esum argument of the vertex function.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"function diffusionvertex_f!(dv, v, esum, p, t)\n # dv, v and esum are arrays, hence we use the broadcasting operator .\n dv .= esum\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Just like above the input arguments v, esum, p, t are mandatory for the syntax of vertex functions. The additional input dv corresponding to the derivative of the vertex' state is mandatory for vertices described by ordinary differential equations.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The output function g is just taking part of the internal states. For that we can use the StateMask helper function g = StateMaks(1:1)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"nd_diffusion_vertex = VertexModel(; f=diffusionvertex_f!, g=StateMask(1:1), dim=1)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Constructing-the-network","page":"Getting Started","title":"Constructing the network","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"With the components defined, we can define the topology and assemble the network dynamics.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"N = 20 # number of nodes\nk = 4 # average degree\ng = barabasi_albert(N, k) # a little more exciting than a bare random graph\n\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The Barabási–Albert model generates a scale-free random graph.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"nd = Network(g, nd_diffusion_vertex, nd_diffusion_edge)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The constructor Network combines the component model with the topological information contained in the graph g and returns an Network compatible with the solvers of DifferentialEquations.jl.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"rng = StableRNG(1)\nx0 = randn(rng, N) # random initial conditions\node_prob = ODEProblem(nd, x0, (0.0, 2.0))\nsol = solve(ode_prob, Tsit5());\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"We are solving the diffusion problem on the time interval 0 2 with the Tsit5() algorithm, which is recommended by the authors of DifferentialEquations.jl for most non-stiff problems.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"plot(sol; idxs=vidxs(nd, :, :), fmt=:png)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The plotting is straightforward. The idxs keyword allows us to pass a list of indices. Indices can be also \"symbolic\" indices which specify components and their symbols directly. For example idxs = VIndex(1, :v) acesses state :v of vertex 1. See Symbolic Indexing for more details.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"In oder to collect multiple indices we can use the helper function vidxs and eidxs, which help to collect all symbolic indices matching a certain criteria.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Two-Dimensional-Extension","page":"Getting Started","title":"Two Dimensional Extension","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"To illustrate a very simple multi-dimensional case, in the following we simulate two independent diffusions on an identical graph. The first uses the symbol x and is started with initial conditions drawn from the standard normal distribution N(01), the second uses the symbol ϕ with squared standard normal inital conditions.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The symbols have to be passed with the keyword sym to VertexModel.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"N = 10 # number of nodes\nk = 4 # average degree\ng = barabasi_albert(N, k) # a little more exciting than a bare random graph\n\n# We will have two independent diffusions on the network, hence dim = 2\nnd_diffusion_vertex_2 = VertexModel(; f=diffusionvertex_f!, g=1:2, dim=2, sym=[:x, :ϕ])\nnd_diffusion_edge_2 = EdgeModel(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow_x, :flow_ϕ])\nnd_2 = Network(g, nd_diffusion_vertex_2, nd_diffusion_edge_2)\n\nx0_2 = vec(transpose([randn(rng, N) .^ 2 randn(rng, N)])) # x ~ N(0,1)^2; ϕ ~ N(0,1)\node_prob_2 = ODEProblem(nd_2, x0_2, (0.0, 3.0))\nsol_2 = solve(ode_prob_2, Tsit5());\nnothing #hide","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Try plotting the variables ϕ_i yourself. [To write ϕ type \\phi and press TAB]","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"plot(sol_2; idxs=vidxs(nd_2, :, :x), fmt=:png)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"Using the eidxs helper function we can also plot the flow variables","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"plot(sol_2; idxs=eidxs(nd_2, :, :flow_x), fmt=:png)","category":"page"},{"location":"generated/getting_started_with_network_dynamics/#Appendix:-The-network-Laplacian-L","page":"Getting Started","title":"Appendix: The network Laplacian L","text":"","category":"section"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"The diffusion equation on a network can be rewritten as","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v_i = sum_j=1^N A_ji v_j - d_i v_i = e_i^T A v - d_i v_i","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"where d_i is the degree of node i and e_i^T is the i-th standard basis vector. Introducing the diagonal matrix D that has the degree of node i in its i-th row and the Laplacian matrix L = D - A we arrive at","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v = e_i^T(A - D) v","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"and finally","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"dot v = - L v","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This is a linear system of ODEs and its solution is a matrix exponential. To study the asymptotic behaviour of the system it suffices to analyze the eigenspectrum of L. For this reason L is an important construction in network science.","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"","category":"page"},{"location":"generated/getting_started_with_network_dynamics/","page":"Getting Started","title":"Getting Started","text":"This page was generated using Literate.jl.","category":"page"},{"location":"external_inputs/#External-Inputs","page":"External Inputs","title":"External Inputs","text":"","category":"section"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"External inputs for components are way to pass signals between components outside of the network structure. The most common usecase for that are control systems: make your vertex i depend on some state of vertex j.","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"If used, this will essentially wides the number of received inputs of a component function. I.e. the baseline mathematical models for vertex models","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"beginaligned\nM^mathrm vfracmathrmdmathrmdtx^mathrm v = f^mathrm v(u^mathrm v i^mathrm v i^mathrmext p^mathrm v t)\ny^mathrm v = g^mathrm v(u^mathrm v i^mathrm v i^mathrmext p^mathrm v t)\nendaligned","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"fᵥ(dxᵥ, xᵥ, e_aggr, ext, pᵥ, t)\ngᵥ(yᵥ, xᵥ, [e_aggr, ext,] pᵥ, t)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"and edge models","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"beginaligned\nM^mathrm efracmathrmdmathrmdtx^mathrm e = f^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst i^mathrmext p^mathrm e t)\ny^mathrm e_mathrmdst = g_mathrmdst^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst i^mathrmext p^mathrm e t)\ny^mathrm e_mathrmsrc = g_mathrmsrc^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst i^mathrmext p^mathrm e t)\nendaligned","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"fₑ(dxₑ, xₑ, v_src, v_dst, ext, pₑ, t)\ngₑ(y_src, y_dst, xᵥ, [v_src, v_dst, ext,] pₑ, t)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"change. You may still oomit the input section from g according to the different Feed Forward Behaviors. However you either have to use all inputs (including ext) or none.","category":"page"},{"location":"external_inputs/#Usage","page":"External Inputs","title":"Usage","text":"","category":"section"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Vertex and Edge models with external inputs can be created using the extin keyword of the EdgeModel and VertexModel constructors.","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"You need to pass a vector of SymbolicIndices (VIndex and EIndex), e.g. ","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"VertexModel(... , extin=[VIndex(12,:x), VIndex(9, :x)], ...)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"means your vertex receives a 2 dimensional external input vector with the states x of vertices 12 and 9. See below for hands on example.","category":"page"},{"location":"external_inputs/#Limitations","page":"External Inputs","title":"Limitations","text":"","category":"section"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"There are some limitations in place. You can only reference states (i.e. things that appear in xᵥ or xₑ of some component model) or outputs of non-feed-forward components, i.e. states yᵥ or yₑ of some component model which does not have feed forward behavior in their g function.","category":"page"},{"location":"external_inputs/#Example","page":"External Inputs","title":"Example","text":"","category":"section"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"As an example system, we'll consider two capacitors with a resistor between them. Vertex 1 v1 has a controllable current source. Using a PI controller for the current source, it tries to keep the voltage at the second vertex stable under the disturbance of some time periodic current sind at v2.","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"\n v1 Resistor v2\nPI controlled ─→─o─←────MMM────→─o─→─ time dependent \ncurrent source ┴ ┴ current sink\n ┬ ┬\n ⏚ ⏚","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"The example will be implemented 2 times: in plain NetworkDynamcics and using MTK.","category":"page"},{"location":"external_inputs/#Plain-NetworkDynamics","page":"External Inputs","title":"Plain NetworkDynamics","text":"","category":"section"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"First we define the resistor:","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"using NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing CairoMakie\n\nfunction resistor_g(idst, vsrc, vdst, (R,), t)\n idst[1] = (vsrc[1] - vdst[1])/R\nend\nresistor = EdgeModel(g=AntiSymmetric(resistor_g), \n outsym=:i, insym=:v, psym=:R=>0.1, \n src=:source, dst=:load, name=:resistor)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Then we define the \"load\" vertex with sinusoidial load profile:","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"function f_load(dv, v, iin, (C,), t)\n dv[1] = 1/C*(iin[1] - (1 + 0.1*sin(t)))\nend\nload = VertexModel(f=f_load, g=1, \n sym=:V=>0.5, insym=:i, psym=:C=>1,\n vidx=2, name=:load)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Lastly, we define the \"source\" vertex","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"function f_source(dv, v, iin, extin, (C,Vref,Ki,Kp), t)\n Δ = Vref - extin[1] # tracking error\n dv[2] = Δ # integrator state of PI\n i_inj = Kp*Δ + Ki*v[2] # controller output\n dv[1] = 1/C*(iin[1] + i_inj)\nend\nsource = VertexModel(f=f_source, g=1, \n sym=[:V=>0.5,:ξ=>0], insym=:i, \n psym=[:C=>1, :Vref=>1, :Ki=>0.5, :Kp=>10],\n extin=[VIndex(:load, :V)], \n vidx=1, name=:source)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Then we can create the network and simulate:","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"nw = Network([source, load], [resistor])\nu0 = NWState(nw) # everything has default values\nprob = ODEProblem(nw, uflat(u0), (0,100), pflat(u0))\nsol = solve(prob, Tsit5())\n\nfig, ax, p = lines(sol, idxs=VIndex(:load, :V), label=\"Voltage @ load\");\nlines!(ax, sol, idxs=VPIndex(:source, :Vref), label=\"Reference\", color=Cycled(2));\naxislegend(ax; position=:rb);\nfig # hide","category":"page"},{"location":"external_inputs/#MTK-Models","page":"External Inputs","title":"MTK Models","text":"","category":"section"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"First we define the resistor:","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"using NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing ModelingToolkit\nusing ModelingToolkit: t_nounits as t, D_nounits as Dt\nusing CairoMakie\n\n@mtkmodel Resistor begin\n @variables begin\n i(t), [description=\"Current at dst end\"]\n V_src(t), [description=\"Voltage at src end\"]\n V_dst(t), [description=\"Voltage at dst end\"]\n end\n @parameters begin\n R=0.1, [description=\"Resistance\"]\n end\n @equations begin\n i ~ (V_src - V_dst)/R\n end\nend\n@named resistor = Resistor()\nresistor_edge = EdgeModel(resistor, [:V_src], [:V_dst], AntiSymmetric([:i]); src=:load, dst=:source)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Then we define the \"load\" vertex with sinusoidial load profile:","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"@mtkmodel Load begin\n @variables begin\n V(t)=0.5, [description=\"Voltage\"]\n i_load(t), [description=\"Load current\"]\n i_grid(t), [description=\"Current from grid\"]\n end\n @parameters begin\n C=1, [description=\"Capacitance\"]\n end\n @equations begin\n i_load ~ 1 + 0.1*sin(t)\n Dt(V) ~ 1/C*(i_grid - i_load)\n end\nend\n@named load = Load()\nload_vertex = VertexModel(load, [:i_grid], [:V]; vidx=2)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Lastly, we define the \"source\" vertex","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"@mtkmodel Source begin\n @variables begin\n V(t)=0.5, [description=\"Voltage\"]\n ξ(t)=0, [description=\"Integrator state\"]\n i_grid(t), [description=\"Current from grid\"]\n i_source(t), [description=\"Current from source\"]\n Δ(t), [description=\"Tracking Error\"]\n V_load(t), [description=\"Voltage at load\"]\n end\n @parameters begin\n C=1, [description=\"Capacitance\"]\n Vref=1, [description=\"Reference voltage\"]\n Ki=0.5, [description=\"Integral gain\"]\n Kp=10, [description=\"Proportional gain\"]\n end\n @equations begin\n Δ ~ Vref - V_load\n Dt(ξ) ~ Δ\n i_source ~ Kp*Δ + Ki*ξ\n Dt(V) ~ 1/C*(i_grid + i_source)\n end\nend\n@named source = Source()\nsource_vertex = VertexModel(source, [:i_grid], [:V]; \n extin=[:V_load => VIndex(:load, :V)], vidx=1)","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Then we can create the network and simulate:","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"nw = Network([source_vertex, load_vertex], [resistor_edge])\nu0 = NWState(nw) # everything has default values\nprob = ODEProblem(nw, uflat(u0), (0,100), pflat(u0))\nsol = solve(prob, Tsit5())\n\nlet\n fig = Figure();\n ax1 = Axis(fig[1,1]);\n lines!(ax1, sol, idxs=VIndex(:load, :V), label=\"Voltage @ load\");\n lines!(ax1, sol, idxs=VPIndex(:source, :Vref), label=\"Reference\", color=Cycled(2));\n axislegend(ax1; position=:rb);\n ax2 = Axis(fig[2,1]);\n lines!(ax2, sol, idxs=VIndex(:load, :i_load), label=\"load current\");\n lines!(ax2, sol, idxs=VIndex(:source, :i_source), label=\"source current\", color=Cycled(2));\n axislegend(ax2);\n fig\nend","category":"page"},{"location":"external_inputs/","page":"External Inputs","title":"External Inputs","text":"Using MTK for modeling, we can also inspect the currents i_load and i_source the MTK interface preserves the Observables.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"EditURL = \"../../examples/stress_on_truss.jl\"","category":"page"},{"location":"generated/stress_on_truss/#Stress-on-Truss","page":"Stress on Truss","title":"Stress on Truss","text":"","category":"section"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"In this exampe we'll simulate the time evolution of a truss structure consisting of joints and stiff springs. This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"(Image: truss animation)","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"The mathematical model is quite simple: the vertices are point masses with positions x and y and velocities v_x and v_y. For simplicity, we add some damping to the nodal motion based on the velocity.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"beginaligned\ndotx = v_x\ndoty = v_y\ndotv_x = frac1Mleft(sum F_x - γv_xright)\ndotv_y = frac1Mleft(sum F_y - γv_yright) - g\nendaligned","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"The vertices cannot absorb any torque, so the beams only exert forces in the direction of the beam.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"F = Kcdot(L - Δd)","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"where L is the nominal lenth and Δd is the actual length of the beam.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"We start by loading the necessary packages.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"using NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing Graphs\nusing GraphMakie\nusing LinearAlgebra: norm\nusing Printf\nusing CairoMakie\nCairoMakie.activate!()","category":"page"},{"location":"generated/stress_on_truss/#Definition-of-the-dynamical-system","page":"Stress on Truss","title":"Definition of the dynamical system","text":"","category":"section"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"We need 3 models:","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"a fixed vertex which cannot change its position,\na free vertex which can move, and\na beam which connects two vertices.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"function fixed_g(pos, x, p, t)\n pos .= p\nend\nvertex_fix = VertexModel(g=fixed_g, psym=[:xfix, :yfix], outsym=[:x, :y], ff=NoFeedForward())","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Here we need to specify the ff keyword manually, because NetworkDynamics cannot distinguish between g(out, x, p, t) (NoFeedForwarwd) and g(out, in, p, t) (PureFeedForward()) and guesses the latter when mathrmdim(x)=0.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"function free_f(dx, x, Fsum, (M, γ, g), t)\n v = view(x, 1:2)\n dx[1:2] .= (Fsum .- γ .* v) ./ M\n dx[2] -= g\n dx[3:4] .= v\n nothing\nend\nvertex_free = VertexModel(f=free_f, g=3:4, sym=[:vx=>0, :vy=>0, :x, :y],\n psym=[:M=>10, :γ=>200, :g=>9.81], insym=[:Fx, :Fy])","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"For the edge, we want to do something special. Later on, we want to color the edges according to the force they exert. Therefore, are interested in the absolut force rather than just the force vector. NetworkDynamics allows you to define so called Observed functions, which can recover additional states, so called observed, after the simulations. We can use this mechanis, to define a \"recipe\" for calculating the beam force based on the inputs (nodal positions) and the beam parameters.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"function edge_g!(F, pos_src, pos_dst, (K, L), t)\n dx = pos_dst[1] - pos_src[1]\n dy = pos_dst[2] - pos_src[2]\n d = sqrt(dx^2 + dy^2)\n Fabs = K * (L - d)\n F[1] = Fabs * dx / d\n F[2] = Fabs * dy / d\n nothing\nend\nfunction observedf(obsout, u, pos_src, pos_dst, (K, L), t)\n dx = pos_dst[1] .- pos_src[1]\n dy = pos_dst[2] .- pos_src[2]\n d = sqrt(dx^2 + dy^2)\n obsout[1] = K * (L - d)\n nothing\nend\nbeam = EdgeModel(g=AntiSymmetric(edge_g!), psym=[:K=>0.5e6, :L], outsym=[:Fx, :Fy], obsf=observedf, obssym=[:Fabs])","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"With the models define we can set up graph topology and initial positions.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"N = 5\ndx = 1.0\nshift = 0.2\ng = SimpleGraph(2*N + 1)\nfor i in 1:N\n add_edge!(g, i, i+N); add_edge!(g, i, i+N)\n if i < N\n add_edge!(g, i+1, i+N); add_edge!(g, i, i+1); add_edge!(g, i+N, i+N+1)\n end\nend\nadd_edge!(g, 2N, 2N+1)\npos0 = zeros(Point2f, 2N + 1)\npos0[1:N] = [Point((i-1)dx,0) for i in 1:N]\npos0[N+1:2*N] = [Point(i*dx + shift, 1) for i in 1:N]\npos0[2N+1] = Point(N*dx + 1, -1)\nfixed = [1,4] # set fixed vertices\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Now can collect the vertex models and construct the Network object.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"verts = VertexModel[vertex_free for i in 1:nv(g)]\nfor i in fixed\n verts[i] = vertex_fix # use the fixed vertex for the fixed points\nend\nnw = Network(g, verts, beam)","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"In order to simulate the system we need to initialize the state and parameter vectors. Some states and parameters are shared between all vertices/edges. Those have been allready set in their constructors. The free symbols are","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"x and y for the position of the free vertices,\nxfix and yfix for the position of the fixed vertices,\nL for the nominal length of the beams.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"s = NWState(nw)\n# set x/y and xfix/yfix\nfor i in eachindex(pos0, verts)\n if i in fixed\n s.p.v[i, :xfix] = pos0[i][1]\n s.p.v[i, :yfix] = pos0[i][2]\n else\n s.v[i, :x] = pos0[i][1]\n s.v[i, :y] = pos0[i][2]\n end\nend\n# set L for edges\nfor (i,e) in enumerate(edges(g))\n s.p.e[i, :L] = norm(pos0[src(e)] - pos0[dst(e)])\nend\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Lastly there is a special vertex at the end of the truss which has a higher mass and reduced damping.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"s.p.v[11, :M] = 200\ns.p.v[11, :γ] = 100\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"No we have everything ready to build the ODEProblem and simulate the system.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"tspan = (0.0, 12.0)\nprob = ODEProblem(nw, uflat(s), tspan, pflat(s))\nsol = solve(prob, Tsit5())\nnothing #hide","category":"page"},{"location":"generated/stress_on_truss/#Plot-the-solution","page":"Stress on Truss","title":"Plot the solution","text":"","category":"section"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"Plotting trajectories of points is kinda boring. So instead we're going to use GraphMakie.jl to create a animation of the timeseries.","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"fig = Figure(size=(1000,550));\nfig[1,1] = title = Label(fig, \"Stress on truss\", fontsize=30)\ntitle.tellwidth = false\n\nfig[2,1] = ax = Axis(fig)\nax.aspect = DataAspect();\nhidespines!(ax); # no borders\nhidedecorations!(ax); # no grid, axis, ...\nlimits!(ax, -0.1, pos0[end][1]+0.3, pos0[end][2]-0.5, 1.15) # axis limits to show full plot\n\n# get the maximum force during the simulation to get the color scale\n# It is only possible to access `:Fabs` directly becaus we've define the observable function for it!\n(fmin, fmax) = 0.3 .* extrema(Iterators.flatten(sol(sol.t, idxs=eidxs(nw, :, :Fabs))))\np = graphplot!(ax, g;\n edge_width = 4.0,\n node_size = 3*sqrt.(try s.p.v[i, :M] catch; 10.0 end for i in 1:nv(g)),\n nlabels = [i in fixed ? \"Δ\" : \"\" for i in 1:nv(g)],\n nlabels_align = (:center,:top),\n nlabels_fontsize = 30,\n elabels = [\"edge $i\" for i in 1:ne(g)],\n elabels_side = Dict(ne(g) => :right),\n edge_color = [0.0 for i in 1:ne(g)],\n edge_attr = (colorrange=(fmin,fmax),\n colormap=:diverging_bkr_55_10_c35_n256))\n\n# draw colorbar\nfig[3,1] = cb = Colorbar(fig, get_edge_plot(p), label = \"Axial force\", vertical=false)\n\nT = tspan[2]\nfps = 30\ntrange = range(0.0, sol.t[end], length=Int(T * fps))\nrecord(fig, \"truss.mp4\", trange; framerate=fps) do t\n title.text = @sprintf \"Stress on truss (t = %.2f )\" t\n s_at_t = NWState(sol, t)\n for i in eachindex(pos0)\n p[:node_pos][][i] = (s_at_t.v[i, :x], s_at_t.v[i, :y])\n end\n notify(p[:node_pos])\n load = s_at_t.e[:, :Fabs]\n p.edge_color[] = load\n p.elabels = [@sprintf(\"%.0f\", l) for l in load]\n fig\nend","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"","category":"page"},{"location":"generated/stress_on_truss/","page":"Stress on Truss","title":"Stress on Truss","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"EditURL = \"../../examples/directed_and_weighted_graphs.jl\"","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Neurodynamic-model-of-synchronization-in-the-human-brain","page":"Directed and Weighted Graphs","title":"Neurodynamic model of synchronization in the human brain","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Topics-covered-in-this-tutorial-include:","page":"Directed and Weighted Graphs","title":"Topics covered in this tutorial include:","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"constructing a directed, weighted graph from data\nsome useful macros\nparameter handling\nstiff equations","category":"page"},{"location":"generated/directed_and_weighted_graphs/#The-FitzHugh-Nagumo-model","page":"Directed and Weighted Graphs","title":"The FitzHugh-Nagumo model","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Dynamics of spiking neurons have been described in a simplified manner by the FitzHugh-Nagumo model.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"beginaligned\nvarepsilon dot u = u - fracu^33 - v \ndot v = u + a\nendaligned","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Here u is a fast, excitatory variable corresponding to the membrane potential and v is a slower, inhibitory varibale. varepsilon is a parameter separating these time-scales, and a is a control parameter.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"In simplified models of the brain, such relaxation oscillators may be used to model individual neurons, clusters of neurons or even larger areas in the brain. The FitzHugh-Nagumo model has been widely used for studying synchronization in neuronal activity, which in turn has been connected to physiological phenomena such as epileptic seizures.","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Coupling-relaxation-oscillators","page":"Directed and Weighted Graphs","title":"Coupling relaxation oscillators","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"While different coupling schemes for FitzHugh-Nagumo oscillators have been proposed, in this tutorial we focus on coupling of the excitatory variables via electrical gap junctions, as described by the following system of equations.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"beginaligned\nvarepsilon dot u_i = u_i - fracu_i^33 - v_i - sigma sum_j=1^N G_ij(u_i - u_j) \ndot v_i = u_i + a\nendaligned","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"This is a simple diffusive coupling mediated by the difference between activation potentials in pairs of neurons. A similar coupling term was introduced in the \"getting started\" tutorial.","category":"page"},{"location":"generated/directed_and_weighted_graphs/#The-network-topology-a-brain-atlas","page":"Directed and Weighted Graphs","title":"The network topology - a brain atlas","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"In the following we will use a directed and weighted network encoding the strength and directionality of coupling between 90 different areas of the brain [Nathalie Tzourio-Mazoyer et al., 2002, Neuroimage].","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The network weight matrix is given as a text file containing 90 lines with 90 numbers representing the coupling strength and separated by commas ,. The data can be conveniently read into a matrix with the DelimitedFiles module.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"using DelimitedFiles\nusing SimpleWeightedGraphs, Graphs\nusing NetworkDynamics\nusing OrdinaryDiffEqTsit5\nusing OrdinaryDiffEqSDIRK\nusing StableRNGs\nusing Plots\n\n# adjust the load path for your filesystem!\nfile = joinpath(pkgdir(NetworkDynamics), \"docs\", \"examples\", \"Norm_G_DTI.txt\")\nG = readdlm(file, ',', Float64, '\\n')\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The data structure for directed, weighted graphs is provided by the package SimpleWeightedGraphs.jl which is based on Graphs.jl.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"# First we construct a weighted, directed graph\ng_weighted = SimpleWeightedDiGraph(G)\n\n# For later use we extract the edge.weight attributes\n# . is the broadcasting operator and gets the attribute :weight for every edge\nedge_weights = getfield.(collect(edges(g_weighted)), :weight)\n\n# we promote the g_weighted graph as a directed graph (weights of the edges are included in parameters)\ng_directed = SimpleDiGraph(g_weighted)\n\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Setting-up-the-ODEProblem","page":"Directed and Weighted Graphs","title":"Setting up the ODEProblem","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Defining the VertexModel and EdgeModel is similar to the example before. The macro Base.@propagate_inbounds tells the compiler to inline the function and propagate the inbounds context. For more details see the julia documentation.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Base.@propagate_inbounds function fhn_electrical_vertex!(dv, v, esum, p, t)\n (a, ϵ) = p\n dv[1] = v[1] - v[1]^3 / 3 - v[2] + esum[1]\n dv[2] = (v[1] - a) * ϵ\n nothing\nend\nvertex = VertexModel(f=fhn_electrical_vertex!, g=1, sym=[:u, :v], psym=[:a=>0.5, :ϵ=>0.05])","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Base.@propagate_inbounds function electrical_edge!(e, v_s, v_d, (w, σ), t)\n e[1] = w * (v_s[1] - v_d[1]) * σ\n nothing\nend\nelectricaledge = EdgeModel(g=Directed(electrical_edge!), outdim=1, psym=[:weight, :σ=>0.5])","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"fhn_network! = Network(g_directed, vertex, electricaledge)","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Since this system is a directed one with thus directed edges, the keyword argument coupling is used to set the coupling of the edges to Directed().","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Parameter-handling","page":"Directed and Weighted Graphs","title":"Parameter handling","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Some of the parameters have been declared with default values. Those default values will be used when creating the NWParameter object. We can use getindex on the parameter objects to set the missing weight values.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"p = NWParameter(fhn_network!)\np.e[1:ne(g_directed), :weight] = edge_weights\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The initial conditions could be created similarly to the parameters as an indexable NWState obejct. Since we chose a random initial condition we initialize the flat array directly:","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"x0 = randn(StableRNG(42), dim(fhn_network!)) * 5\n\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Solving-the-system","page":"Directed and Weighted Graphs","title":"Solving the system","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Now we are ready to create an ODEProblem. Since for some choices of parameters the FitzHugh-Nagumo model is stiff (i.e. numerically unstable), we use a solver with automated stiffness detection. Such a solver switches to a more stable solver only when the solution enters a region of phase space where the problem is numerically unstable. In this case we use Tsit5 and switch to TRBDF2 when necessary. AutoTsit5 is the switching version of the Tsit5 algorithm.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"Not that we call pflat on the NWParameter object to get the flat array of parameters.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"tspan = (0.0, 200.0)\nprob = ODEProblem(fhn_network!, x0, tspan, pflat(p))\nsol = solve(prob, AutoTsit5(TRBDF2()));\nnothing #hide","category":"page"},{"location":"generated/directed_and_weighted_graphs/#Plotting","page":"Directed and Weighted Graphs","title":"Plotting","text":"","category":"section"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"The plot of the excitatory variables shows that they synchronize for this choice of parameters.","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"plot(sol; idxs=vidxs(fhn_network!, :, :u), legend=false, ylim=(-5, 5), fmt=:png)","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"","category":"page"},{"location":"generated/directed_and_weighted_graphs/","page":"Directed and Weighted Graphs","title":"Directed and Weighted Graphs","text":"This page was generated using Literate.jl.","category":"page"},{"location":"initialization/#Initialization","page":"Initialization","title":"Initialization","text":"","category":"section"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Initialization of the system describes the process of finding valid initial conditions, mostly a fixpoint of the system. We distinguish between two types of initialization: full system initialziation and component initialization.","category":"page"},{"location":"initialization/#Full-System-Initialization","page":"Initialization","title":"Full-System Initialization","text":"","category":"section"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Full system initialization describs the process of finding a fixpoint/steady state of th entire system.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"To do so, you can use find_fixpoint, which creates a SteadyStateProblem of the whole network and tries do solve it. ","category":"page"},{"location":"initialization/#Component-wise-Initialization","page":"Initialization","title":"Component-wise Initialization","text":"","category":"section"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"In contrast to full-system initialization the goal of component-wise initialization is to find a valid initial condition for a single component first, given a network coupling.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"This can be usefull in cases, where there are nontrivial internal dynamics and states within a single vertex or edge. The idea of component-wise initialisation is to find internal states which match a given \"network coupling\" (fixed inputs and outputs).","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Lets consider the following example of a Swing-equation generator model.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"using NetworkDynamics, ModelingToolkit\nusing ModelingToolkit: t_nounits as t, D_nounits as Dt\n\n@mtkmodel Swing begin\n @variables begin\n u_r(t)=1, [description=\"bus d-voltage\", output=true]\n u_i(t)=0.1, [description=\"bus q-voltage\", output=true]\n i_r(t)=1, [description=\"bus d-current (flowing into bus)\", input=true]\n i_i(t)=0.1, [description=\"bus d-current (flowing into bus)\", input=true]\n ω(t), [guess=0.0, description=\"Rotor frequency\"]\n θ(t), [guess=0.0, description=\"Rotor angle\"]\n Pel(t), [guess=1, description=\"Electrical Power injected into the grid\"]\n end\n @parameters begin\n M=0.005, [description=\"Inertia\"]\n D=0.1, [description=\"Damping\"]\n V=sqrt(u_r^2 + u_i^2), [description=\"Voltage magnitude\"]\n ω_ref=0, [description=\"Reference frequency\"]\n Pm, [guess=0.1,description=\"Mechanical Power\"]\n end\n @equations begin\n Dt(θ) ~ ω - ω_ref\n Dt(ω) ~ 1/M * (Pm - D*ω - Pel)\n Pel ~ u_r*i_r + u_i*i_i\n u_r ~ V*cos(θ)\n u_i ~ V*sin(θ)\n end\nend\nsys = Swing(name=:swing)\nvf = VertexModel(sys, [:i_r, :i_i], [:u_r, :u_i])","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"You can see in the provided metadata, that we've set default values for the node outputs u_r, u_i, the node inputs i_r, i_i and most parameters. For some states and parameters, we've onlye provided a guess rather than a default. Variables which only have guesses are considered \"tunable\" for the initialization algorithm.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"In order to initialize the remaining variables we use initialize_component!, which is a mutating function which tries to solve the nonlinear initialization problem and store the found values for the \"free\" variables as init metadata.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"initialize_component!(vf; verbose=true)\nnothing #hide","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"vf #hide","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"Which lead to a successfull initialization of states :θ and :ω as well as parameter :Pm. To retrieve the residual you can use init_residual.","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"As a quick test we can ensure that the angle indeed matches the voltag angel:","category":"page"},{"location":"initialization/","page":"Initialization","title":"Initialization","text":"get_init(vf, :θ) ≈ atan(get_default(vf, :u_i), get_default(vf, :u_r))","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"EditURL = \"../../examples/heterogeneous_system.jl\"","category":"page"},{"location":"generated/heterogeneous_system/#Modeling-a-heterogeneous-system","page":"Heterogeneous Systems","title":"Modeling a heterogeneous system","text":"","category":"section"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"One of the main purposes of NetworkDynamics.jl is to facilitate modeling coupled systems with heterogenities. This means that components can differ in their parameters as well as in their dynamics.","category":"page"},{"location":"generated/heterogeneous_system/#Heterogenous-parameters","page":"Heterogeneous Systems","title":"Heterogenous parameters","text":"","category":"section"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"We start by setting up a simple system of Kuramoto oscillators.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"using NetworkDynamics, OrdinaryDiffEqTsit5, Plots, Graphs\n\nN = 8\ng = watts_strogatz(N, 2, 0) # ring network\n\nfunction kuramoto_edge!(e, θ_s, θ_d, (K,), t)\n e[1] = K * sin(θ_s[1] - θ_d[1])\n nothing\nend\nedge! = EdgeModel(g=AntiSymmetric(kuramoto_edge!), outdim=1, psym=[:K=>3])","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"function kuramoto_vertex!(dθ, θ, esum, (ω0,), t)\n dθ[1] = ω0 + esum[1]\n nothing\nend\nvertex! = VertexModel(f=kuramoto_vertex!, g=StateMask(1:1), sym=[:θ], psym=[:ω0], name=:kuramoto)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"nw = Network(g, vertex!, edge!)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"To assign parameters, we can create a NWParameter object based on the nw definition. This parameter object will be pre-filled with the default parameters.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"p = NWParameter(nw)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"To set the vertex parameters, we can use indexing of the p.v field:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"ω = collect(1:N) ./ N\nω .-= sum(ω) / N\np.v[:, :ω0] = ω\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Here, the index pairing :, :ω is used to index state ω for all node indices.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"The parameter object contains information about the network structure. For the actual problem definition we need to throw away this wrapper and use the flat-vector representation of the parameters pflat(p). Note that pflat(p)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Similarily, we could use NWState(nw) to create an indexable wrapper of the initial state. However in this case we can also fill create the flat state array manually:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"x0 = collect(1:N) ./ N\nx0 .-= sum(x0) ./ N\ntspan = (0.0, 10.0)\nprob = ODEProblem(nw, x0, tspan, pflat(p))\nsol = solve(prob, Tsit5())\nplot(sol; ylabel=\"θ\", fmt=:png)","category":"page"},{"location":"generated/heterogeneous_system/#Heterogeneous-dynamics","page":"Heterogeneous Systems","title":"Heterogeneous dynamics","text":"","category":"section"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Two paradigmatic modifications of the node model above are static nodes and nodes with inertia. A static node has no internal states and instead fixes the variable at a constant value.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"function static_g(out, u, p, t)\n out[1] = p[1]\n nothing\nend\nstatic! = VertexModel(g=static_g, outsym=[:θ], psym=[:θfix => ω[1]], name=:static)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"But wait! NetworkDynamics classified this as PureFeedForward, because it cannot distinguish between the function signatures","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"g(out, u, p, t) # PureFeedForward\ng(out, ins, p, t) # NoFeedForward","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"and since dim(u)=0 it wrongfully assumes that the latter is meant. We can overwrite the classification by passing the ff keyword:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"static! = VertexModel(g=static_g, outsym=[:θ], psym=[:θfix => ω[1]], ff=NoFeedForward(), name=:static)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"A Kuramoto model with inertia consists of two internal variables leading to more complicated (and for many applications more realistic) local dynamics.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"function kuramoto_inertia!(dv, v, esum, (ω0,), t)\n dv[1] = v[2]\n dv[2] = ω0 - 1.0 * v[2] + esum[1]\n nothing\nend\n\ninertia! = VertexModel(f=kuramoto_inertia!, g=1:1, sym=[:θ, :ω], psym=[:ω0], name=:inertia)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Since now we model a system with heterogeneous node dynamics we can no longer straightforwardly pass a single VertexModel to the Network constructor but instead have to hand over an Array.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"vertex_array = VertexModel[vertex! for i in 1:N]\nvertex_array[1] = static!\nvertex_array[5] = inertia! # index should correspond to the node's index in the graph\nnw_hetero! = Network(g, vertex_array, edge!)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"Now we have to take a bit more care with defining initial conditions and parameters.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"First, we can generate a NWState object based on the nw_hetero! object which will be populated with the default values.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state = NWState(nw_hetero!)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"The node with inertia is two-dimensional, hence we need to specify two initial conditions. For the first dimension we keep the initial conditions from above and insert! another one into x0 at the correct index.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"For the θ states we will use the same initial conditins as before:","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state.v[2:8,:θ] = x0[2:8]\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"We're still missing one initial condition: the second variable ω of the 5th vertex.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state.v[5,:ω] = 5\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"The NWState object also contains a parameter object accessible via state.p. The edge parameters are already filled with default values. The vertex parameters can be copied from our old parmeter object p.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"state.p.v[2:8, :ω0] = p.v[2:8, :ω0]\nnothing #hide","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"For the problem construction, we need to convert the nested stuctures to flat arrays using the uflat and pflat methods.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"prob_hetero = ODEProblem(nw_hetero!, uflat(state), tspan, pflat(state))\nsol_hetero = solve(prob_hetero, Tsit5());\nnothing #hide\nplot(sol_hetero)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"For clarity we plot only the variables referring to the oscillator's angle θ and color them according to their type.","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"colors = map(vertex_array) do vertexf\n if vertexf.name == :kuramoto\n colorant\"lightseagreen\"\n elseif vertexf.name == :static\n colorant\"orange\"\n elseif vertexf.name == :inertia\n colorant\"darkred\"\n end\nend\n\nplot(sol_hetero; ylabel=\"θ\", idxs=vidxs(1:8,:θ), lc=colors', fmt=:png)","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"","category":"page"},{"location":"generated/heterogeneous_system/","page":"Heterogeneous Systems","title":"Heterogeneous Systems","text":"This page was generated using Literate.jl.","category":"page"},{"location":"metadata/#Metadata","page":"Metadata","title":"Metadata","text":"","category":"section"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Component model such as VertexModel and EdgeModel can store metadata. We distinguish between two kinds of metadata: component metadata and symbol metadata.","category":"page"},{"location":"metadata/#Component-Metadata","page":"Metadata","title":"Component Metadata","text":"","category":"section"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Component metadata is a Dict{Symbol,Any} attached to each component to store various information. Use metadata to retrieve the full dict.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Component Metadata API).","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Special metadata: ","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":":init_residual: after Component-wise Initialization, this field stores the residual vector of the nonlinear problem.\n:graphelement: optional field to specialize the graphelement for each component (vidx) for vertices, (;src,dst) named tuple of either vertex names or vertex indices for edges. Has special accessors has_/get_/set_graphelement.","category":"page"},{"location":"metadata/#Symbol-Metadata","page":"Metadata","title":"Symbol Metadata","text":"","category":"section"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Each component stores symbol metadata. The symbol metadata is a Dict{Symbol, Dict{Symbol, Any}} which stores a metadate dict per symbol. Symbols are everything that appears in sym, psym, obssym and insym.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"To access the data, you can use the methods has_metadata, get_metadata and set_metadata! (see Per Symbol Metadata API).","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Special cases for symbol metadata are:","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"default: Stores default values for states/parameters. In initialization, those are considered fixed.\nguess: Stores a guess for a state/parameter which needs to solved during initialization (\"free\" variables).\nbounds: Store bounds for variables/parameters\ninit: Stores the solution of the \"free\" variables during initialization.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Fore those, there are special functions has_*, get_* and set_*!. See Per Symbol Metadata API.","category":"page"},{"location":"metadata/","page":"Metadata","title":"Metadata","text":"Those are closely aligned to the metadata use in ModelingToolkit. They are automatically copied from the ODESystem if you use MTK models to create NetworkDynamics models.","category":"page"},{"location":"symbolic_indexing/#Symbolic-Indexing","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Using SciML's SymblicIndexingInterface.jl, ND.jl provides lots of methods to access and change variables and Parameters.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"details: Setup code to make following examples work\nusing NetworkDynamics\nusing Graphs\nusing OrdinaryDiffEqTsit5\nusing Plots","category":"page"},{"location":"symbolic_indexing/#Provide-Symbol-Names","page":"Symbolic Indexing","title":"Provide Symbol Names","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"When construction component models, you can pass symbolic names using the sym and psym keywords.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"function _edgef!(e, v_s, v_d, (K,), t)\n e .= K * (v_s[1] .- v_d[1])\nend\nedgef = EdgeModel(;g=AntiSymmetric(_edgef!), outsym=[:flow], psym=[:K=>1])","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Here we created a static diffusion edge with suitable variable and parameter names. Similarly, we define the diffusion vertex with symbolic names.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"function _vertexf!(dv, v, esum, p, t)\n dv[1] = esum[1]\nend\nvertexf = VertexModel(f=_vertexf!, g=1, sym=[:storage])","category":"page"},{"location":"symbolic_indexing/#Fundamental-Symblic-Indices","page":"Symbolic Indexing","title":"Fundamental Symblic Indices","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"The default types for this access are the types VIndex, EIndex, VPIndex and EPIndex. Each of those symbolic indices consists of 2 elements: a reference to the network componen and a reference to the symbol within that component. As such, VIndex(2, :x) refers to variable with symbolic name :x in vertex number 2. EPIndex(4, 2) would refer to the second parameter of the edge component for the 4th edge.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"details: Setup code to make following examples work\ng = wheel_graph(5)\nnw = Network(g, vertexf, edgef)\ns = NWState(nw)\ns.v[:,:storage] .= randn(5)\nprob = ODEProblem(nw, uflat(s), (0,2), pflat(s))\nsol = solve(prob, Tsit5()) \nnothing #hide","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Those fundamental indices can be used in a lot of scenarios. Most importantly you can use them to","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"sol(sol.t; idxs=VIndex(1, :storage)) # extract timeseries out ouf solution object\nplot(sol; idxs=[VIndex(1, :storage), VIndex(5,:storage)]) # plot storage of two nodes","category":"page"},{"location":"symbolic_indexing/#Generate-Symbolic-Indices","page":"Symbolic Indexing","title":"Generate Symbolic Indices","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Often, you need many individual symbolic indices. For that there are the helper methods vidxs, eidxs, vpidxs and epidxs. With the help of those methods you can generate arrays of symbolic indices:","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"vidxs(nw, :, :storage) # get variable \"storage\" for all nodes","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"plot(sol; idxs=vidxs(nw, :, :storage))","category":"page"},{"location":"symbolic_indexing/#NWState-and-NWParameter-Objects","page":"Symbolic Indexing","title":"NWState and NWParameter Objects","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Internally, both state and parameters of a Network are represented using flat arrays. To access the state or parameters of a network, you can use the NWState and NWParameter objects.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"p = NWParameter(nw)","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"creates a NWParameter object for the network nw. It essentially creates a new flat parameter array and fills it with the default parameter values define in the component. The parameters in the NWParameter object can be accessed using the symbolic indices.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"p[EPIndex(5, :K)] = 2.0 # change the parameter K of the 5th edge\nnothing #hide","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Similarly, you can create a NWState object for the network nw using","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"s = NWState(nw)","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"No default values were provided in the network components, so the state array is filled with NaNs.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"s[VIndex(:, :storage)] .= randn(5) # set the (initial) storage for alle nodes \ns #hide","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"For both NWState and NWParameter objects, the there is a more convenient way to access the variables and parameters.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"@assert s.v[1, :storage] == s[VIndex(1, :storage)] # s.v -> access vertex states\n@assert s.e[1, :flow] == s[EIndex(1, :flow)] # s.e -> access edge states\n@assert s.p.e[1,:K] == p[EPIndex(1, :K)] # s.p -> access parameters","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"The NWState and NWParameter objects are mutable, thus changing them will also change the underlying wrapped flat arrays. You can allways access the flat representations by calling uflat and pflat.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"note: Note\nThe NWState and NWParameter wrappers can be constructed from various objects. Fore example, within a callback you might construct p = NWParameter(integrator) to then change the parameters of the network within the callback.","category":"page"},{"location":"symbolic_indexing/#Observables","page":"Symbolic Indexing","title":"Observables","text":"","category":"section"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Sometimes, the \"states\" you're interested in aren't really states in the DAE sense but rather algebraic derivations from DAE states, parameters and time – in accordance with the naming in the SciML-ecosystem those states are called Observables.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"A prime example of Observables are edge/vertex-outputs, such as the flow in the edge model defined above. It is also possible to define additional Observables manually by using the obssym and obsf keyword on the EdgeModel/VertexModel constructors. When building models using ModelingToolkit, the reduced algebraic states will be preserved as observables automatically.","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"Observables can be accessed like any other state, for example, the flows in the network don't show up in the state array but can be accessed in all the ways discussed above, for example","category":"page"},{"location":"symbolic_indexing/","page":"Symbolic Indexing","title":"Symbolic Indexing","text":"plot(sol; idxs=eidxs(nw, :, :flow))","category":"page"},{"location":"mathematical_model/#Mathematical-Model","page":"Mathematical Model","title":"Mathematical Model","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The basic mathematical model of NetworkDynamics.jl splits up the system it two parts: vertex and edge components.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The main goal of NetworkDynamics.jl is, to express the overall network dynamics as a Differential-Algebraic-Equation (DAE)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Mfracmathrmdmathrmdtu = f^mathrmnw(u p t)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"where M is a (possibly singular) mass matrix, u is the internal state vector of the system, p are the parameters and t is the time. To make this compatible with the solvers for OrdinaryDiffEq.jl, the created Network object is a callable object","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"nw(du, u, p, t) # mutates du","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"with stored mass matrix information to build an ODEProblem based on the Network.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Instead of defining f^mathrmnw by hand, ND.jl helps you to build it automatically based on a list of decentralized nodal and edge dynamics, so-called VertexModel and EdgeModel objects. Each component model mathrm c is modeled as general input-output-system","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"beginaligned\nM_mathrm cfracmathrmdmathrmdtx_mathrm c = f^mathrm c(x^mathrm c i_mathrm c p_mathrm c t)\ny^mathrm c = g^mathrm c(x^mathrmc i_mathrm c p_mathrm c t)\nendaligned","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"where M_mathrmc is the component mass matrix, x^mathrm c are the component states, i^mathrm c are the inputs of the component and y^mathrm c is the output of the component. It is possible to have mathrmdim(x^mathrmc) = 0 and thus no internal states.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"In the network context, the output of the edges are flow variables. The outputs of vertices are potential variables. In interconnection, the flow on the edges depends on the potentials at both ends as inputs. The potentials of the nodes depend on the incoming flows from all connected edges as an input. (Here, flow and potentials are meant in a conceptional and not necessarily physical way.)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"","category":"page"},{"location":"mathematical_model/#Vertex-Models","page":"Mathematical Model","title":"Vertex Models","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Specifically, a (single-layer) vertex model has one input, and one output. The input is an aggregation/reduction over all incident edge outputs,","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"i^mathrm v = mathopmathrmagglimits_k^textincident y^mathrm e_k qquadtextoftenqquad\ni^mathrm v = sum_k^textincident y^mathrm e_k","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The full vertex model","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"beginaligned\nM^mathrm vfracmathrmdmathrmdtx^mathrm v = f^mathrm v(u^mathrm v i^mathrm v p^mathrm v t)\ny^mathrm v = g^mathrm v(u^mathrm v i^mathrm v p^mathrm v t)\nendaligned","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"corresponds to the Julia functions","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"function fᵥ(dxᵥ, xᵥ, e_aggr, pᵥ, t)\n # mutate dxᵥ\n nothing\nend\nfunction gᵥ(yᵥ, xᵥ, e_aggr, pᵥ, t)\n # mutate yᵥ\n nothing\nend\nvertf = VertexModel(; f=fᵥ, g=gᵥ, mass_matrix=Mᵥ, ...)","category":"page"},{"location":"mathematical_model/#Edge-Models","page":"Mathematical Model","title":"Edge Models","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"In contrast to vertex models, edge models in general have two inputs and two outputs, both for source and destination end of the edge. We commonly use src and dst to describe the source and destination end of an edge respectively. ","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"note: On the directionality of edges\nMathematically, in a system defined on an undirected graph there is no difference between the edge (12) and (21), the edge has no direction. However, from an implementation point of view we always need to have some kind of ordering for function arguments, state order and so on. For undirected graphs, Graphs.jl chooses the direction of an edge v1->v2 such that v1 < v2.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The inputs of the edge are just the outputs of the two nodes at both ends. The output is split into two: the dst output goes to the input of the vertex at the destination end, the src output goes to the input of the vertex at the src end.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The full model of an edge","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"beginaligned\nM^mathrm efracmathrmdmathrmdtx^mathrm e = f^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t)\ny^mathrm e_mathrmdst = g_mathrmdst^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t)\ny^mathrm e_mathrmsrc = g_mathrmsrc^mathrm e(u^mathrm e y^mathrm v_mathrmsrc y^mathrm v_mathrmdst p^mathrm e t)\nendaligned","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"corresponds to the Julia functions","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"function fₑ(dxₑ, xₑ, v_src, v_dst, pₑ, t)\n # mutate dxᵥ\n nothing\nend\nfunction gₑ(y_src, y_dst, xᵥ, v_src, v_dst, pₑ, t)\n # mutate y_src and y_dst\n nothing\nend\nvertf = EdgeModel(; f=fₑ, g=gₑ, mass_matrix=Mₑ, ...)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The sign convention for both outputs of an edge must be identical, typically, a positive flow represents a flow into the connected vertex. This is important, because the vertex only receives the flows, it does not know whether the flow was produce by the source or destination end of an edge.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":" y_src y_dst \n V_src o───←─────────→───o V_dst\n","category":"page"},{"location":"mathematical_model/#Single-Sided-Edge-Outputs","page":"Mathematical Model","title":"Single Sided Edge Outputs","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Often, edge outputs will possess some symmetry which makes it more convenient to define \"single sided\" edge output functions","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"function g_single(y, xᵥ, v_src, v_dst, pₑ, t)\n # mutate y\n nothing\nend","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"There are multiple wrappers available to automaticially convert them into double-sided edge output functions:","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Directed(g_single) builds a double-sided function which only couples to the destination side.\nSymmetric(g_single) builds a double-sided function in which both ends receive y.\nAntiSymmetric(g_single) builds a double-sided function where the destination receives y and the source receives -y.\nFiducial(g_single_src, g_singl_dst) builds a double-sided edge output function based on two single sided functions.","category":"page"},{"location":"mathematical_model/#Feed-Forward-Behavior","page":"Mathematical Model","title":"Feed Forward Behavior","text":"","category":"section"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"The most general version of the component models can contain direct feed forwards from the input, i.e. the edge output might depend directly on the connected vertices or the vertex output might depend directly on the aggregated edge input.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Whenever possible, you should define output functions without feed forwards, i.e.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"gᵥ_noff(yᵥ, xᵥ, pᵥ, t)\ngₑ_noff([y_src,] y_dst, xᵥ, pₑ, t)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"instead of the more general","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"gᵥ(yᵥ, xᵥ, e_aggr, pᵥ, t)\ngₑ([y_src], y_dst, xᵥ, v_src, v_dst, pₑ, t)","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"NetworkDynamics cannot couple two components with feed forward to each other. It is always possible to transform feed forward behavior to an internal state x with mass matrix entry zero to circumvent this problem. This transformation can be performed automatically by using ff_to_constraint.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"warning: Feed Forward Vertices\nAs of 11/2024, vertices with feed forward are not supported at all. Use ff_to_constraint to transform them into vertex model without FF.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"Concretely, NetworkDynamics distinguishes between 4 types of feed forward behaviors of g functions based on the FeedForwardType trait. The different types the signature of provided function g. Based on the signatures avaialable, ND.jl will try to find the correct type automaticially. Using the ff keyword in the constructors, the user can enforce a specific type.","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"PureFeedForward()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., ins..., p, t) # abstractly\ng!(out_dst, v_src, v_dst, p, t) # single-sided edge\ng!(out_src, out_dst, v_src, v_dst, p, t) # double-sided edge\ng!(v_out, e_aggr, p, t) # single layer vertex","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"FeedForward()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., x, ins..., p, t) # abstractly\ng!(out_dst, x, v_src, v_dst, p, t) # single-sided edge\ng!(out_src, out_dst, x, v_src, v_dst, p, t) # double-sided edge\ng!(v_out, x, e_aggr, p, t) # single layer vertex","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"NoFeedForward()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., x, p, t) # abstractly\ng!(out_dst, x, p, t) # single-sided edge\ng!(out_src, out_dst, x, p, t) # double-sided edge\ng!(v_out, x, p, t) # single layer vertex","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"PureStateMap()","category":"page"},{"location":"mathematical_model/","page":"Mathematical Model","title":"Mathematical Model","text":"g!(outs..., x) # abstractly\ng!(out_dst, x) # single-sided edge\ng!(out_src, out_dst, x) # double-sided edge\ng!(v_out, x) # single layer vertex","category":"page"},{"location":"API/#API","page":"API","title":"API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"The following functions are designed for public use.","category":"page"},{"location":"API/#Network-Construction-API","page":"API","title":"Network Construction API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"Network\ndim(::Network)\npdim(::Network)","category":"page"},{"location":"API/#NetworkDynamics.Network","page":"API","title":"NetworkDynamics.Network","text":"Network([g,] vertexf, edgef; kwarg...)\n\nConstruct a Network object from a graph g and edge and component models vertexf and edgef.\n\nArguments:\n\ng::AbstractGraph: The graph on which the network is defined. Optional, can be ommittet if all component models have a defined graphelement. See vidx and src/dst keywors for VertexModel and EdgeModel constructors respectively.\nvertexm: A single VertexModel or a vector of VertexModel objects. The order of the vertex models must mirror the order of the vertices(g) iterator.\nedgem: A single EdgeModel or a vector of EdgeModel objects. The order of the edge models must mirror the order of the edges(g) iterator.\n\nOptional keyword arguments:\n\nexecution=SequentialExecution{true}(): Execution model of the network. E.g. SequentialExecution, KAExecution, PolyesterExecution or ThreadedExecution.\naggregator=execution isa SequentialExecution ? SequentialAggregator(+) : PolyesterAggregator(+): Aggregation function applied to the edge models. E.g. SequentialAggregator, PolyesterAggregator, ThreadedAggregator, SparseAggregator.\ncheck_graphelement=true: Check if the graphelement metadata is consistent with the graph.\ndealias=false Check if the components alias eachother and create copies if necessary. This is necessary if the same component model is referenced in multiple places in the Network but you want to dynamicially asign metadata, such as initialization information to specific instances.\nverbose=false: Show additional information during construction.\n\n\n\n\n\nNetwork(nw::Network; g, vertexm, edgem, kwargs...)\n\nRebuild the Network with same graph and vertex/edge models but possibly different kwargs.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.dim-Tuple{Network}","page":"API","title":"NetworkDynamics.dim","text":"dim(nw::Network)\n\nReturns the number of dynamic states in the network, corresponts to the length of the flat state vector.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.pdim-Tuple{Network}","page":"API","title":"NetworkDynamics.pdim","text":"pdim(nw::Network)\n\nReturns the number of parameters in the network, corresponts to the length of the flat parameter vector.\n\n\n\n\n\n","category":"method"},{"location":"API/#Component-Models","page":"API","title":"Component Models","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"VertexModel()\nEdgeModel()","category":"page"},{"location":"API/#NetworkDynamics.VertexModel-Tuple{}","page":"API","title":"NetworkDynamics.VertexModel","text":"VertexModel(; kwargs...)\n\nBuild a VertexModel according to the keyword arguments.\n\nMain Arguments:\n\nf=nothing: Dynamic function of the component. Can be nothing if dim is 0.\ng: Output function of the component. Usefull helpers: StateMask\nsym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.\noutsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. Can be infered automaticially if g isa StateMask.\npsym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.\nmass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).\nname=dim>0 ? :VertexM : :StaticVertexM: Name of the component.\n\nOptional Arguments:\n\ninsym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially.\nvidx: Index of the vertex in the graph, enables graphless constructor.\nff: FeedForwardType of component. Will be typically infered from g automaticially.\nobssym/obsf: Define additional \"observable\" states.\nsymmetadata/metadata: Provide prefilled metadata dictionaries.\nextin=nothing: Define \"external\" inputs for the model with Network indices, i.e. extin=[VIndex(7,:x), ..]. Those inputs will be provided as another input vector f(x, in, extin, p, t) and g(y, x, in, extin, p, t).\n\nAll Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.EdgeModel-Tuple{}","page":"API","title":"NetworkDynamics.EdgeModel","text":"EdgeModel(; kwargs...)\n\nBuild a EdgeModel according to the keyword arguments.\n\nMain Arguments:\n\nf=nothing: Dynamic function of the component. Can be nothing if dim is 0.\ng: Output function of the component. Usefull helpers: AntiSymmetric, Symmetric, Fiducial, Directed and StateMask.\nsym/dim: Symbolic names of the states. If dim is provided, sym is set automaticially.\noutsym/outdim: Symbolic names of the outputs. If outdim is provided, outsym is set automaticially. In general, outsym for edges isa named tuple (; src, dst). However, depending on the g function, it might be enough to provide a single vector or even nothing (e.g. AntiSymmetric(StateMask(1:2))). See Building EdgeModels for examples.\npsym/pdim=0: Symbolic names of the parameters. Ifpdimis provided,psym` is set automaticially.\nmass_matrix=I: Mass matrix of component. Can be a vector v and is then interpreted as Diagonal(v).\nname=dim>0 ? :EdgeM : :StaticEdgeM: Name of the component.\n\nOptional Arguments:\n\ninsym/indim: Symbolic names of the inputs. If indim is provided, insym is set automaticially. For edges, insym is a named tuple (; src, dst). If give as vector tuple is created automaticially.\nsrc/dst: Index or name of the vertices at src and dst end. Enables graphless constructor.\nff: FeedForwardType of component. Will be typically infered from g automaticially.\nobssym/obsf: Define additional \"observable\" states.\nsymmetadata/metadata: Provide prefilled metadata dictionaries.\nextin=nothing: Define \"external\" inputs for the model with Network indices, i.e. extin=[VIndex(7,:x), ..]. Those inputs will be provided as another input vector f(x, insrc, indst, extin, p, t) and g(ysrc, ydst, x, insrc, indst, extin, p, t).\n\nAll Symbol arguments can be used to set default values, i.e. psym=[:K=>1, :p].\n\n\n\n\n\n","category":"method"},{"location":"API/#Component-Models-with-MTK","page":"API","title":"Component Models with MTK","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"VertexModel(::ModelingToolkit.ODESystem, ::Any, ::Any)\nEdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any, ::Any)\nEdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any)","category":"page"},{"location":"API/#NetworkDynamics.VertexModel-Tuple{ODESystem, Any, Any}","page":"API","title":"NetworkDynamics.VertexModel","text":"VertexModel(sys::ODESystem, inputs, outputs; ff_to_constraint=true, kwargs...)\n\nCreate a VertexModel object from a given ODESystem created with ModelingToolkit. You need to provide 2 lists of symbolic names (Symbol or Vector{Symbols}):\n\ninputs: names of variables in you equation representing the aggregated edge states\noutputs: names of variables in you equation representing the node output\n\nff_to_constraint controlls, whether output transformations g which depend on inputs should be\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.EdgeModel-Tuple{ODESystem, Vararg{Any, 4}}","page":"API","title":"NetworkDynamics.EdgeModel","text":"EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout; ff_to_constraint=false, kwargs...)\n\nCreate a EdgeModel object from a given ODESystem created with ModelingToolkit. You need to provide 4 lists of symbolic names (Symbol or Vector{Symbols}):\n\nsrcin: names of variables in you equation representing the node state at the source\ndstin: names of variables in you equation representing the node state at the destination\nsrcout: names of variables in you equation representing the output at the source\ndstout: names of variables in you equation representing the output at the destination\n\nff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.EdgeModel-Tuple{ODESystem, Any, Any, Any}","page":"API","title":"NetworkDynamics.EdgeModel","text":"EdgeModel(sys::ODESystem, srcin, dstin, AntiSymmetric(dstout); ff_to_constraint=false, kwargs...)\n\nCreate a EdgeModel object from a given ODESystem created with ModelingToolkit.\n\nHere 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 the symbol vector in\n\nAntiSymmetric(dstout),\nSymmetric(dstout), or\nDirected(dstout).\n\nff_to_constraint controlls, whether output transformations g which depend on inputs should be transformed into constraints.\n\n\n\n\n\n","category":"method"},{"location":"API/#Output-Function-Helpers/Wrappers","page":"API","title":"Output Function Helpers/Wrappers","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"StateMask\nSymmetric\nAntiSymmetric\nDirected\nFiducial","category":"page"},{"location":"API/#NetworkDynamics.StateMask","page":"API","title":"NetworkDynamics.StateMask","text":"StateMask(i::AbstractArray)\nStateMaks(i::Number)\n\nA StateMask is a predefined output function. It can be used to define the output of a component model by picking from the internal state.\n\nI.e. g=StateMask(2:3) in a vertex function will output the internal states 2 and 3. In many contexts, StateMasks can be constructed implicitly by just providing the indices, e.g. g=1:2.\n\nFor EdgeModel this needs to be combined with a Directed, Symmetric, AntiSymmetric or Fiducial coupling, e.g. g=Fiducial(1:2, 3:4) forwards states 1:2 to dst and states 3:4 to src.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.Symmetric","page":"API","title":"NetworkDynamics.Symmetric","text":"Symmetric(g)\n\nWraps a single-sided output function g turns it into a double sided output function which applies\n\ny_dst = g(...)\ny_src = y_dst\n\ng can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also AntiSymmetric, Directed, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.AntiSymmetric","page":"API","title":"NetworkDynamics.AntiSymmetric","text":"AntiSymmetric(g_dst)\n\nWraps a single-sided output function g_dst turns it into a double sided output function which applies\n\ny_dst = g_dst(...)\ny_src = -y_dst\n\ng_dst can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also Symmetric, Directed, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.Directed","page":"API","title":"NetworkDynamics.Directed","text":"Directed(g_dst)\n\nWraps a single-sided output function g_dst turns it into a double sided output function which applies\n\ny_dst = g_dst(...)\n\nWith Directed there is no output for the src side. g_dst can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also AntiSymmetric, Symmetric, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.Fiducial","page":"API","title":"NetworkDynamics.Fiducial","text":"Fiducial(g_src, g_dst)\n\nWraps two single-sided output function g_src and g_dst and turns them into a double sided output function which applies\n\ny_dst = g_src(...)\ny_src = g_dst(...)\n\ng can be a Number/AbstractArray to impicitly wrap the corresponding StateMask.\n\nSee also AntiSymmetric, Directed, Fiducial and StateMask.\n\n\n\n\n\n","category":"type"},{"location":"API/#Accessors-for-Component-Properties","page":"API","title":"Accessors for Component Properties","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"fftype\ndim(::NetworkDynamics.ComponentModel)\nsym\noutdim\noutsym\npdim(::NetworkDynamics.ComponentModel)\npsym\nobssym\nhasinsym\ninsym\nhasindim\nindim","category":"page"},{"location":"API/#NetworkDynamics.fftype","page":"API","title":"NetworkDynamics.fftype","text":"fftype(x)\n\nRetrieve the feed forward trait of x.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.dim-Tuple{NetworkDynamics.ComponentModel}","page":"API","title":"NetworkDynamics.dim","text":"dim(c::ComponentModel)::Int\n\nRetrieve the dimension of the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.sym","page":"API","title":"NetworkDynamics.sym","text":"sym(c::ComponentModel)::Vector{Symbol}\n\nRetrieve the symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.outdim","page":"API","title":"NetworkDynamics.outdim","text":"outdim(c::VertexModel)::Int\noutdim(c::EdgeModel)::@NamedTuple(src::Int, dst::Int)\n\nRetrieve the output dimension of the component\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.outsym","page":"API","title":"NetworkDynamics.outsym","text":"outsym(c::VertexModel)::Vector{Symbol} outsym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}\n\nRetrieve the output symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.pdim-Tuple{NetworkDynamics.ComponentModel}","page":"API","title":"NetworkDynamics.pdim","text":"pdim(c::ComponentModel)::Int\n\nRetrieve the parameter dimension of the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.psym","page":"API","title":"NetworkDynamics.psym","text":"psym(c::ComponentModel)::Vector{Symbol}\n\nRetrieve the parameter symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.obssym","page":"API","title":"NetworkDynamics.obssym","text":"obssym(c::ComponentModel)::Vector{Symbol}\n\nRetrieve the observation symbols of the component.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.hasinsym","page":"API","title":"NetworkDynamics.hasinsym","text":"hasinsym(c::ComponentModel)\n\nChecks if the optioan field insym is present in the component model.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.insym","page":"API","title":"NetworkDynamics.insym","text":"insym(c::VertexModel)::Vector{Symbol}\ninsym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol}, dst::Vector{Symbol}}\n\nMusst be called after hasinsym/hasindim returned true. Gives the insym vector(s). For vertex model just a single vector, for edges it returns a named tuple (; src, dst) with two symbol vectors.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.hasindim","page":"API","title":"NetworkDynamics.hasindim","text":"hasindim(c::ComponentModel)\n\nChecks if the optioan field insym is present in the component model.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.indim","page":"API","title":"NetworkDynamics.indim","text":"indim(c::VertexModel)::Int\nindim(c::EdgeModel)::@NamedTuple{src::Int,dst::Int}\n\nMusst be called after hasinsym/hasindim returned true. Gives the input dimension(s).\n\n\n\n\n\n","category":"function"},{"location":"API/#FeedForwardType-Traits","page":"API","title":"FeedForwardType-Traits","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"FeedForwardType\nPureFeedForward\nFeedForward\nNoFeedForward\nPureStateMap","category":"page"},{"location":"API/#NetworkDynamics.FeedForwardType","page":"API","title":"NetworkDynamics.FeedForwardType","text":"abstract type FeedForwardType end\n\nAbstract supertype for the FeedForwardType traits.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PureFeedForward","page":"API","title":"NetworkDynamics.PureFeedForward","text":"PureFeedForward <: FeedForwardType\n\nTrait for component output functions g that have pure feed forward behavior (do not depend on x):\n\ng!(outs..., ins..., p, t)\n\nSee also FeedForward, NoFeedForward and PureStateMap.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.FeedForward","page":"API","title":"NetworkDynamics.FeedForward","text":"FeedForward <: FeedForwardType\n\nTrait for component output functions g that have feed forward behavior. May depend on everything:\n\ng!(outs..., x, ins..., p, t)\n\nSee also PureFeedForward, NoFeedForward and PureStateMap.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.NoFeedForward","page":"API","title":"NetworkDynamics.NoFeedForward","text":"NoFeedForward <: FeedForwardType\n\nTrait for component output functions g that have no feed forward behavior (do not depend on inputs):\n\ng!(outs..., x, p, t)\n\nSee also PureFeedForward, FeedForward and PureStateMap.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PureStateMap","page":"API","title":"NetworkDynamics.PureStateMap","text":"PureStateMap <: FeedForwardType\n\nTrait for component output functions g that only depends on state:\n\ng!(outs..., x)\n\nSee also PureFeedForward, FeedForward and NoFeedForward.\n\n\n\n\n\n","category":"type"},{"location":"API/#Symbolic-Indexing-API","page":"API","title":"Symbolic Indexing API","text":"","category":"section"},{"location":"API/#Network-Parameter-Object","page":"API","title":"Network Parameter Object","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"NWParameter\nNWParameter(::Any)\nNWParameter(::NWParameter)\nNWParameter(::SciMLBase.DEIntegrator)","category":"page"},{"location":"API/#NetworkDynamics.NWParameter","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(nw_or_nw_wraper, pflat)\n\nIndexable wrapper for flat parameter array pflat. Needs Network or wrapper of Network, e.g. ODEProblem.\n\np = NWParameter(nw)\np.v[idx, :sym] # get parameter :sym of vertex idx\np.e[idx, :sym] # get parameter :sym of edge idx\np[s::Union{VPIndex, EPIndex}] # get parameter for specific index\n\nGet flat array representation using pflat(p).\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.NWParameter-Tuple{Any}","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(nw_or_nw_wraper;\n ptype=Vector{Float64}, pfill=filltype(ptype), default=true)\n\nCreates \"empty\" NWParameter object for the Network/Wrapper nw with flat type ptype. The array will be prefilled with pfill (defaults to NaN).\n\nIf default=true the default parameter values attached to the network components will be loaded.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWParameter-Tuple{NWParameter}","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(p::NWParameter; ptype=typeof(p.pflat))\n\nCreate NWParameter based on other parameter object, just convert type.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWParameter-Tuple{SciMLBase.DEIntegrator}","page":"API","title":"NetworkDynamics.NWParameter","text":"NWParameter(int::SciMLBase.DEIntegrator)\n\nCreate NWParameter object from integrator.\n\n\n\n\n\n","category":"method"},{"location":"API/#Network-State-Object","page":"API","title":"Network State Object","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"NWState\nNWState(::Any)\nNWState(::NWState)\nNWState(::NWParameter)\nNWState(::SciMLBase.DEIntegrator)\nuflat\npflat","category":"page"},{"location":"API/#NetworkDynamics.NWState","page":"API","title":"NetworkDynamics.NWState","text":"NWState(nw_or_nw_wrapper, uflat, [pflat], [t])\n\nIndexable wrapper for flat state & parameter array. Needs Network or wrapper of Network, e.g. ODEProblem.\n\ns = NWState(nw)\ns.v[idx, :sym] # get state :sym of vertex idx\ns.e[idx, :sym] # get state :sym of edge idx\ns.p.v[idx, :sym] # get parameter :sym of vertex idx\ns.p.e[idx, :sym] # get parameter :sym of edge idx\ns[s::Union{VIndex, EIndex, EPIndex, VPIndex}] # get parameter for specific index\n\nGet flat array representation using uflat(s) and pflat(s).\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.NWState-Tuple{Any}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(nw_or_nw_wrapper;\n utype=Vector{Float64}, ufill=filltype(utype),\n ptype=Vector{Float64}, pfill=filltype(ptype), default=true)\n\nCreates \"empty\" NWState object for the Network/Wrapper nw with flat types utype & ptype. The arrays will be prefilled with ufill and pfill respectively (defaults to NaN).\n\nIf default=true the default state & parameter values attached to the network components will be loaded.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWState-Tuple{NWState}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(p::NWState; utype=typeof(uflat(s)), ptype=typeof(pflat(s)))\n\nCreate NWState based on other state object, just convert types.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWState-Tuple{NWParameter}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(p::NWParameter; utype=Vector{Float64}, ufill=filltype(utype), default=true)\n\nCreate NWState based on existing NWParameter object.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.NWState-Tuple{SciMLBase.DEIntegrator}","page":"API","title":"NetworkDynamics.NWState","text":"NWState(int::SciMLBase.DEIntegrator)\n\nCreate NWState object from integrator.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.uflat","page":"API","title":"NetworkDynamics.uflat","text":"uflat(s::NWState)\n\nRetrieve the wrapped flat array representation of the state.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.pflat","page":"API","title":"NetworkDynamics.pflat","text":"pflat(p::NWParameter)\npflat(s::NWState)\n\nRetrieve the wrapped flat array representation of the parameters.\n\n\n\n\n\n","category":"function"},{"location":"API/#Symbolic-Indices","page":"API","title":"Symbolic Indices","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"VIndex\nEIndex\nVPIndex\nEPIndex","category":"page"},{"location":"API/#NetworkDynamics.VIndex","page":"API","title":"NetworkDynamics.VIndex","text":"VIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = VIndex(comp, sub)\n\nA symbolic index for a vertex state variable.\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nVIndex(1, :P) # vertex 1, variable :P\nVIndex(1:5, 1) # first state of vertices 1 to 5\nVIndex(7, (:x,:y)) # states :x and :y of vertex 7\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWState, NWParameter or ODESolution.\n\nSee also: EIndex, VPIndex, EPIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.EIndex","page":"API","title":"NetworkDynamics.EIndex","text":"EIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = EIndex(comp, sub)\n\nA symbolic index for an edge state variable.\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nEIndex(1, :P) # edge 1, variable :P\nEIndex(1:5, 1) # first state of edges 1 to 5\nEIndex(7, (:x,:y)) # states :x and :y of edge 7\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWState, NWParameter or ODESolution.\n\nSee also: VIndex, VPIndex, EPIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.VPIndex","page":"API","title":"NetworkDynamics.VPIndex","text":"VPIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = VPIndex(comp, sub)\n\nA symbolic index into the parameter a vertex:\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.\n\nSee also: EPIndex, VIndex, EIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.EPIndex","page":"API","title":"NetworkDynamics.EPIndex","text":"EPIndex{C,S} <: SymbolicStateIndex{C,S}\nidx = VEIndex(comp, sub)\n\nA symbolic index into the parameter a vertex:\n\ncomp: the component index, either int or a collection of ints\nsub: the subindex, either int, symbol or a collection of those.\n\nCan be used to index into objects supporting the SymbolicIndexingInterface, e.g. NWParameter or ODEProblem.\n\nSee also: VPIndex, VIndex, EIndex\n\n\n\n\n\n","category":"type"},{"location":"API/#Index-generators","page":"API","title":"Index generators","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"vidxs\neidxs\nvpidxs\nepidxs","category":"page"},{"location":"API/#NetworkDynamics.vidxs","page":"API","title":"NetworkDynamics.vidxs","text":"vidxs([inpr], components=:, variables=:) :: Vector{VIndex}\n\nGenerate vector of symbolic indexes for vertices.\n\ninpr: Only needed for name matching or : access. Can be Network, sol, prob, ...\ncomponents: Number/Vector, :, Symbol (name matches), String/Regex (name contains)\nvariables: Symbol/Number/Vector, :, String/Regex (all sym containing)\n\nExamples:\n\nvidxs(nw) # all vertex state indices\nvidxs(1:2, :u) # [VIndex(1, :u), VIndex(2, :u)]\nvidxs(nw, :, [:u, :v]) # [VIndex(i, :u), VIndex(i, :v) for i in 1:nv(nw)]\nvidxs(nw, \"ODEVertex\", :) # all symbols of all vertices with name containing \"ODEVertex\"\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.eidxs","page":"API","title":"NetworkDynamics.eidxs","text":"vidxs([inpr], components=:, variables=:) :: Vector{EIndex}\n\nGenerate vector of symbolic indexes for edges.\n\ninpr: Only needed for name matching or : access. Can be Network, sol, prob, ...\ncomponents: Number/Vector, :, Symbol (name matches), String/Regex (name contains)\nvariables: Symbol/Number/Vector, :, String/Regex (all sym containing)\n\nExamples:\n\neidxs(nw) # all edge state indices\neidxs(1:2, :u) # [EIndex(1, :u), EIndex(2, :u)]\neidxs(nw, :, [:u, :v]) # [EIndex(i, :u), EIndex(i, :v) for i in 1:ne(nw)]\neidxs(nw, \"FlowEdge\", :) # all symbols of all edges with name containing \"FlowEdge\"\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.vpidxs","page":"API","title":"NetworkDynamics.vpidxs","text":"vpidxs([inpr], components=:, variables=:) :: Vector{VPIndex}\n\nGenerate vector of symbolic indexes for parameters. See vidxs for more information.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.epidxs","page":"API","title":"NetworkDynamics.epidxs","text":"epidxs([inpr], components=:, variables=:) :: Vector{EPIndex}\n\nGenerate vector of symbolic indexes for parameters. See eidxs for more information.\n\n\n\n\n\n","category":"function"},{"location":"API/#Metadata-API","page":"API","title":"Metadata API","text":"","category":"section"},{"location":"API/#Component-Metadata-API","page":"API","title":"Component Metadata API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"metadata\nhas_metadata(::NetworkDynamics.ComponentModel, ::Symbol)\nget_metadata(::NetworkDynamics.ComponentModel, ::Symbol)\nset_metadata!(::NetworkDynamics.ComponentModel, ::Symbol, ::Any)\nhas_graphelement\nget_graphelement\nset_graphelement!","category":"page"},{"location":"API/#NetworkDynamics.metadata","page":"API","title":"NetworkDynamics.metadata","text":"metadata(c::ComponentModel)\n\nRetrieve metadata object for the component.\n\nSee also metadata\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol}","page":"API","title":"NetworkDynamics.has_metadata","text":"has_metadata(c::ComponentModel, key::Symbol)\n\nChecks if metadata key is present for the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.get_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol}","page":"API","title":"NetworkDynamics.get_metadata","text":"get_metadata(c::ComponentModel, key::Symbol)\n\nRetrieves the metadata key for the component.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.set_metadata!-Tuple{NetworkDynamics.ComponentModel, Symbol, Any}","page":"API","title":"NetworkDynamics.set_metadata!","text":"set_metadata!(c::ComponentModel, key::Symbol, value)\n\nSets the metadata key for the component to value.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.has_graphelement","page":"API","title":"NetworkDynamics.has_graphelement","text":"has_graphelement(c)\n\nChecks if the edge or vetex function function has the graphelement metadata.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_graphelement","page":"API","title":"NetworkDynamics.get_graphelement","text":"get_graphelement(c::EdgeModel)::@NamedTuple{src::T, dst::T}\nget_graphelement(c::VertexModel)::Int\n\nRetrieves the graphelement metadata for the component model. For edges this returns a named tupe (;src, dst) where both are either integers (vertex index) or symbols (vertex name).\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_graphelement!","page":"API","title":"NetworkDynamics.set_graphelement!","text":"set_graphelement!(c::EdgeModel, src, dst)\nset_graphelement!(c::VertexModel, vidx)\n\nSets the graphelement metadata for the edge model. For edges this takes two arguments src and dst which are either integer (vertex index) or symbol (vertex name). For vertices it takes a single integer vidx.\n\n\n\n\n\n","category":"function"},{"location":"API/#Per-Symbol-Metadata-API","page":"API","title":"Per-Symbol Metadata API","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"symmetadata\nget_metadata(::NetworkDynamics.ComponentModel, ::Symbol, ::Symbol)\nhas_metadata(::NetworkDynamics.ComponentModel, ::Symbol, ::Symbol)\nset_metadata!(::NetworkDynamics.ComponentModel, ::Symbol, ::Symbol, ::Any)\nhas_default\nget_default\nset_default!\nhas_guess\nget_guess\nset_guess!\nhas_init\nget_init\nset_init!\nhas_bounds\nget_bounds\nset_bounds!","category":"page"},{"location":"API/#NetworkDynamics.symmetadata","page":"API","title":"NetworkDynamics.symmetadata","text":"symmetadata(c::ComponentModel)::Dict{Symbol,Dict{Symbol,Any}}\n\nRetrieve the metadata dictionary for the symbols. Keys are the names of the symbols as they appear in sym, psym, obssym and insym.\n\nSee also symmetadata\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol, Symbol}","page":"API","title":"NetworkDynamics.get_metadata","text":"get_metadata(c::ComponentModel, sym::Symbol, key::Symbol)\n\nRetrievs the metadata key for symbol sym.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.has_metadata-Tuple{NetworkDynamics.ComponentModel, Symbol, Symbol}","page":"API","title":"NetworkDynamics.has_metadata","text":"has_metadata(c::ComponentModel, sym::Symbol, key::Symbol)\n\nChecks if symbol metadata key is present for symbol sym.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.set_metadata!-Tuple{NetworkDynamics.ComponentModel, Symbol, Symbol, Any}","page":"API","title":"NetworkDynamics.set_metadata!","text":"set_metadata!(c::ComponentModel, sym::Symbol, key::Symbol, value)\nset_metadata!(c::ComponentModel, sym::Symbol, pair)\n\nSets the metadata key for symbol sym to value.\n\n\n\n\n\n","category":"method"},{"location":"API/#NetworkDynamics.has_default","page":"API","title":"NetworkDynamics.has_default","text":"has_default(c::ComponentModel, sym::Symbol)\n\nChecks if a default value is present for symbol sym.\n\nSee also get_default, set_default!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_default","page":"API","title":"NetworkDynamics.get_default","text":"get_default(c::ComponentModel, sym::Symbol)\n\nReturns the default value for symbol sym.\n\nSee also has_default, set_default!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_default!","page":"API","title":"NetworkDynamics.set_default!","text":"set_default(c::ComponentModel, sym::Symbol, value)\n\nSets the default value for symbol sym to value.\n\nSee also has_default, get_default.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_guess","page":"API","title":"NetworkDynamics.has_guess","text":"has_guess(c::ComponentModel, sym::Symbol)\n\nChecks if a guess value is present for symbol sym.\n\nSee also get_guess, set_guess!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_guess","page":"API","title":"NetworkDynamics.get_guess","text":"get_guess(c::ComponentModel, sym::Symbol)\n\nReturns the guess value for symbol sym.\n\nSee also has_guess, set_guess!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_guess!","page":"API","title":"NetworkDynamics.set_guess!","text":"set_guess(c::ComponentModel, sym::Symbol, value)\n\nSets the guess value for symbol sym to value.\n\nSee also has_guess, get_guess.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_init","page":"API","title":"NetworkDynamics.has_init","text":"has_init(c::ComponentModel, sym::Symbol)\n\nChecks if a init value is present for symbol sym.\n\nSee also get_init, set_init!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_init","page":"API","title":"NetworkDynamics.get_init","text":"get_init(c::ComponentModel, sym::Symbol)\n\nReturns the init value for symbol sym.\n\nSee also has_init, set_init!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_init!","page":"API","title":"NetworkDynamics.set_init!","text":"set_init(c::ComponentModel, sym::Symbol, value)\n\nSets the init value for symbol sym to value.\n\nSee also has_init, get_init.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.has_bounds","page":"API","title":"NetworkDynamics.has_bounds","text":"has_bounds(c::ComponentModel, sym::Symbol)\n\nChecks if a bounds value is present for symbol sym.\n\nSee also get_bounds, set_bounds!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.get_bounds","page":"API","title":"NetworkDynamics.get_bounds","text":"get_bounds(c::ComponentModel, sym::Symbol)\n\nReturns the bounds value for symbol sym.\n\nSee also has_bounds, set_bounds!.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.set_bounds!","page":"API","title":"NetworkDynamics.set_bounds!","text":"set_bounds(c::ComponentModel, sym::Symbol, value)\n\nSets the bounds value for symbol sym to value.\n\nSee also has_bounds, get_bounds.\n\n\n\n\n\n","category":"function"},{"location":"API/#Initialization","page":"API","title":"Initialization","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"find_fixpoint\ninitialize_component!\ninit_residual","category":"page"},{"location":"API/#NetworkDynamics.find_fixpoint","page":"API","title":"NetworkDynamics.find_fixpoint","text":"find_fixpoint(nw::Network, [x0::NWState=NWState(nw)], [p::NWParameter=x0.p]; kwargs...)\nfind_fixpoint(nw::Network, x0::AbstractVector, p::AbstractVector; kwargs...)\nfind_fixpoint(nw::Network, x0::AbstractVector; kwargs...)\n\nConvenience wrapper around SteadyStateProblem from SciML-ecosystem. Constructs and solves the steady state problem, returns found value wrapped as NWState.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.initialize_component!","page":"API","title":"NetworkDynamics.initialize_component!","text":"initialize_component!(cf::ComponentModel; verbose=true, kwargs...)\n\nInitialize a ComponentModel by solving the corresponding NonlinearLeastSquaresProblem. During initialization, everyting which has a default value (see Metadata) is considered \"fixed\". All other variables are considered \"free\" and are solved for. The initial guess for each variable depends on the guess value in the Metadata.\n\nThe result is stored in the ComponentModel itself. The values of the free variables are stored in the metadata field init.\n\nThe kwargs are passed to the nonlinear solver.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.init_residual","page":"API","title":"NetworkDynamics.init_residual","text":"init_residual(cf::T; t=NaN, recalc=false)\n\nCalculates the residual |du| for the given component model for the values provided via default and init Metadata.\n\nIf recalc=false just return the residual determined in the actual initialization process.\n\nSee also initialize_component!.\n\n\n\n\n\n","category":"function"},{"location":"API/#Execution-Types","page":"API","title":"Execution Types","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"ExecutionStyle\nSequentialExecution\nPolyesterExecution\nThreadedExecution\nKAExecution","category":"page"},{"location":"API/#NetworkDynamics.ExecutionStyle","page":"API","title":"NetworkDynamics.ExecutionStyle","text":"abstract type ExecutionStyle{buffered::Bool} end\n\nAbstract type for execution style. The coreloop dispatches based on the Execution style stored in the network object.\n\nbuffered=true means that the edge input es explicitly gathered, i.e. the vertex outputs in the output buffer will be copied into a dedicated input buffer for the edges.\nbuffered=false means, that the edge inputs are not explicitly gathered, but the corloop will perform a redirected lookup into the output buffer.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.SequentialExecution","page":"API","title":"NetworkDynamics.SequentialExecution","text":"struct SequentialExecution{buffered::Bool}\n\nSequential execution, no parallelism. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PolyesterExecution","page":"API","title":"NetworkDynamics.PolyesterExecution","text":"struct PolyesterExecution{buffered}\n\nParallel execution using Polyester.jl. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.ThreadedExecution","page":"API","title":"NetworkDynamics.ThreadedExecution","text":"struct ThreadedExecution{buffered}\n\nParallel execution using Julia threads. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.KAExecution","page":"API","title":"NetworkDynamics.KAExecution","text":"struct KAExecution{buffered}\n\nParallel execution using KernelAbstractions.jl. Works with GPU and CPU arrays. For buffered see ExecutionStyle.\n\n\n\n\n\n","category":"type"},{"location":"API/#Aggregators","page":"API","title":"Aggregators","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"Aggregator\nSequentialAggregator\nSparseAggregator\nThreadedAggregator\nPolyesterAggregator\nKAAggregator","category":"page"},{"location":"API/#NetworkDynamics.Aggregator","page":"API","title":"NetworkDynamics.Aggregator","text":"abstract type Aggregator end\n\nAbstract sypertype for aggregators. Aggregators operate on the output buffer of all components and fill the aggregation buffer with the aggregatated edge values per vertex.\n\nAll aggregators have the constructor\n\nAggegator(aggfun)\n\nfor example\n\nSequentialAggreator(+)\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.SequentialAggregator","page":"API","title":"NetworkDynamics.SequentialAggregator","text":"SequentialAggregator(aggfun)\n\nSequential aggregation.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.SparseAggregator","page":"API","title":"NetworkDynamics.SparseAggregator","text":"SparseAggregator(+)\n\nOnly works with additive aggregation +. Aggregates via sparse inplace matrix multiplication. Works with GPU Arrays.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.ThreadedAggregator","page":"API","title":"NetworkDynamics.ThreadedAggregator","text":"ThreadedAggregator(aggfun)\n\nParallel aggregation using Julia threads.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.PolyesterAggregator","page":"API","title":"NetworkDynamics.PolyesterAggregator","text":"PolyesterAggregator(aggfun)\n\nParallel aggregation using Polyester.jl.\n\n\n\n\n\n","category":"type"},{"location":"API/#NetworkDynamics.KAAggregator","page":"API","title":"NetworkDynamics.KAAggregator","text":"KAAggregator(aggfun)\n\nParallel aggregation using KernelAbstractions.jl. Works with both GPU and CPU arrays.\n\n\n\n\n\n","category":"type"},{"location":"API/#Utils","page":"API","title":"Utils","text":"","category":"section"},{"location":"API/","page":"API","title":"API","text":"save_parameters!\nff_to_constraint\nBase.copy(::NetworkDynamics.ComponentModel)","category":"page"},{"location":"API/#NetworkDynamics.save_parameters!","page":"API","title":"NetworkDynamics.save_parameters!","text":"save_parameters!(integrator::SciMLBase.DEIntegrator)\n\nSave the current parameter values in the integrator. Call this function inside callbacks if the parameter values have changed. This will store a timeseries of said parameters in the solution object, thus alowing us to recosntruct observables which depend on time-dependet variables.\n\n\n\n\n\n","category":"function"},{"location":"API/#NetworkDynamics.ff_to_constraint","page":"API","title":"NetworkDynamics.ff_to_constraint","text":"ff_to_constraint(v::VertexModel)\n\nTakes VertexModel v with feed forward and turns all algebraic output states into internal states by defining algebraic constraints contraints 0 = out - g(...). The new output function is just a StateMask into the extended internal state vector.\n\nReturns the transformed VertexModel.\n\n\n\n\n\n","category":"function"},{"location":"API/#Base.copy-Tuple{NetworkDynamics.ComponentModel}","page":"API","title":"Base.copy","text":"copy(c::NetworkDynamics.ComponentModel)\n\nShallow copy of the component model. Creates a deepcopy of metadata and symmetadata but references the same objects everywhere else.\n\n\n\n\n\n","category":"method"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"EditURL = \"../../examples/cascading_failure.jl\"","category":"page"},{"location":"generated/cascading_failure/#Cascading-Failure","page":"Cascading Failure","title":"Cascading Failure","text":"","category":"section"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"This script reimplements the minimal example of a dynamic cascading failure described in Schäfer et al. (2018) [1]. In is an example how to use callback functions to change network parameters. In this case to disable certain lines. This script can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"[1] Schäfer, B., Witthaut, D., Timme, M., & Latora, V. (2018). Dynamically induced cascading failures in power grids. Nature communications, 9(1), 1-13. https://www.nature.com/articles/s41467-018-04287-5","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"The system is modeled using swing equation and active power edges. The nodes are characterized by the voltage angle δ, the active power on each line is symmetric and a function of the difference between source and destination angle δ_src - δ_dst.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"using NetworkDynamics\nusing Graphs\nusing OrdinaryDiffEqTsit5\nusing DiffEqCallbacks\nusing Plots\nusing Test #hide\nimport SymbolicIndexingInterface as SII","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"For the nodes we define the swing equation. State v[1] = δ, v[2] = ω. The swing equation has three parameters: p = (P_ref, I, γ) where P_ref is the power setpopint, I is the inertia and γ is the droop or damping coeficcient.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"The output of the node is just the first state. g=1 is a shorthand for g=StateMask(1:1) which implements a trivial output function g which just takes the first element of the state vector.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function swing_equation(dv, v, esum, p,t)\n P, I, γ = p\n dv[1] = v[2]\n dv[2] = P - γ * v[2] .+ esum[1]\n dv[2] = dv[2] / I\n nothing\nend\nvertex = VertexModel(f=swing_equation, g=1, sym=[:δ, :ω], psym=[:P_ref, :I=>1, :γ=>0.1])","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"Lets define a simple purely active power line whose active power flow is completlye determined by the connected voltage angles and the coupling constant K. We give an additonal parameter, the line limit, which we'll use later in the callback.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function simple_edge(e, v_s, v_d, (K,), t)\n e[1] = K * sin(v_s[1] - v_d[1])\nend\nedge = EdgeModel(;g=AntiSymmetric(simple_edge), outsym=:P, psym=[:K=>1.63, :limit=>1])","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"With the definition of the graph topology we can build the Network object:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"g = SimpleGraph([0 1 1 0 1;\n 1 0 1 1 0;\n 1 1 0 1 0;\n 0 1 1 0 1;\n 1 0 0 1 0])\nswing_network = Network(g, vertex, edge)","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"For the parameters, we create the NWParameter object prefilled with default p values","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"p = NWParameter(swing_network)\n# vertices 1, 3 and 4 act as loads\np.v[(1,3,4), :P_ref] .= -1\n# vertices 2 and 5 act as generators\np.v[(2,5), :P_ref] .= 1.5\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"We can use find_fixpoint to find a valid initial condition of the network","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"u0 = find_fixpoint(swing_network, p)\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"In order to implement the line failures, we need to create a VectorContinousCallback. In the callback, we compare the current flow on the line with the limit. If the limit is reached, the coupling K is set to 0.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"First we can define the affect function:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function affect!(integrator, idx)\n println(\"Line $idx tripped at t=$(integrator.t)\")\n p = NWParameter(integrator) # get indexable parameter object\n p.e[idx, :K] = 0\n auto_dt_reset!(integrator)\n save_parameters!(integrator)\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"There is one important aspect to this function: the save_parameters! call. In the callback, we change the parameters of the network, making the parameters time dependent. The flow on the line is a function P(t) = f(u(t), p(t)). Thus we need to inform the integrator, that a discrete change in parameters happend. With this, the solution object not only tracks u(t) but also p(t) and we may extract the observable P(t) directly.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"The callback trigger condition is a bit more complicated. The straight forward version looks like this:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"function naive_condition(out, u, t, integrator)\n # careful, u != integrator.u\n # therefore construct nwstate with Network info from integrator but u\n s = NWState(integrator, u, integrator.p, t)\n for i in eachindex(out)\n out[i] = abs(s.e[i,:P]) - s.p.e[1,:limit] # compare flow with limit for line\n end\n nothing\nend\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"However, from a performacne perspectiv there are problems with this solution: on every call, we need to perform symbolic indexing into the NWState object. Symbolic indexing is not cheap, as it requires to gather meta data about the network. Luckily, the SymbolicIndexingInterface package which powers the symbolic indexing provides the lower level functions getp and getu which can be used to create and cache accessors to the internal states.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"This still isn't ideal beacuse both getlim and getflow getters will create arrays within the callback. But is far better then resolving the flat state indices every time.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"condition = let getlim = SII.getp(swing_network, epidxs(swing_network, :, :limit)),\n getflow = SII.getu(swing_network, eidxs(swing_network, :, :P))\n function (out, u, t, integrator)\n # careful, u != integrator.u\n # therefore construct nwstate with Network info from integrator but u\n s = NWState(integrator, u, integrator.p, t)\n out .= getlim(s) .- abs.(getflow(s))\n nothing\n end\nend\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"We can combine affect and condition to form the callback.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"trip_cb = VectorContinuousCallback(condition, affect!, ne(g));\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"However, there is another component missing. If we look at the powerflow on the lines in the initial steady state","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"u0.e[:, :P]","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"We see that every flow is below the trip value 1.0. Therefor we need to add a distrubance to the network. We do this by manually disabeling line 5 at time 1.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"trip_first_cb = PresetTimeCallback(1.0, integrator->affect!(integrator, 5));\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"With those components, we can create the problem and solve it.","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"prob = ODEProblem(swing_network, uflat(u0), (0,6), copy(pflat(p));\n callback=CallbackSet(trip_cb, trip_first_cb))\nsol = solve(prob, Tsit5());\n# we want to test the reconstruction of the observables # hide\n@test all(!iszero, sol(sol.t; idxs=eidxs(sol,:,:P))[begin]) # hide\n@test all(iszero, sol(sol.t; idxs=eidxs(sol,:,:P))[end][[1:5...,7]]) # hide\nnothing #hide","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"Through the magic of symbolic indexing we can plot the power flows on all lines:","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"plot(sol; idxs=eidxs(sol,:,:P))","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"","category":"page"},{"location":"generated/cascading_failure/","page":"Cascading Failure","title":"Cascading Failure","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"EditURL = \"../../examples/gas_network.jl\"","category":"page"},{"location":"generated/gas_network/#Dynamic-Flow-in-simple-Gas-Network","page":"Gas Network","title":"Dynamic Flow in simple Gas Network","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This Example is based on the paper","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Albertus J. Malan, Lukas Rausche, Felix Strehle, Sören Hohmann, Port-Hamiltonian Modelling for Analysis and Control of Gas Networks, IFAC-PapersOnLine, Volume 56, Issue 2, 2023, https://doi.org/10.1016/j.ifacol.2023.10.193.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"and tries replicate a simple simulation of flow in a 3-node gas network.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This example can be dowloaded as a normal Julia script here.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"We start by importing the necessary packages:","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"using NetworkDynamics\nusing ModelingToolkit\nusing DynamicQuantities\nusing ModelingToolkit: D as Dt, t as t\nusing Test\nusing StaticArrays\nusing LinearInterpolations\nusing OrdinaryDiffEqTsit5\nusing CairoMakie\nCairoMakie.activate!(type=\"svg\") #hide","category":"page"},{"location":"generated/gas_network/#Node-Models","page":"Gas Network","title":"Node Models","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"In this example, we use the equation based modeling using ModelingToolkit.jl. To verify the equations on a basic level we also provide units to eveything to perform dimensionality checks.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"There are 2 node models used in the paper. The first node type has a constant pressure. Additionally, we ad some \"internal\" state q̃_inj which we want to plot later (see also Observables).","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@mtkmodel ConstantPressureNode begin\n @parameters begin\n p_set, [description=\"Constant pressure setpoint\", unit=u\"Pa\"]\n end\n @variables begin\n p(t) = p_set, [description=\"Pressure\", unit=u\"Pa\", output=true]\n q̃_nw(t), [description=\"aggregated flow from pipes into node\", unit=u\"m^3/s\", input=true]\n q̃_inj(t), [description=\"internal state for introspection\", unit=u\"m^3/s\"]\n end\n @equations begin\n p ~ p_set\n q̃_inj ~ -q̃_nw\n end\nend\nnothing #hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The second node model is a variable pressure node. It has one output state (the pressure) and one input state, the aggregated flows from the connected pipes. As an internal state we have the injected flow from our source/load. The source/load behaviour itself is provided via a time dependent function.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@mtkmodel VariablePressureNode begin\n @structural_parameters begin\n load_profile # time dependent load profile\n end\n @parameters begin\n C, [description=\"Lumped capacitance of connected pipes\", unit=u\"m^4 * s^2 / kg\"]\n end\n @variables begin\n p(t)=5e6, [description=\"Pressure\", unit=u\"Pa\", output=true]\n q̃_inj(t), [description=\"external injection into node\", unit=u\"m^3/s\"]\n q̃_nw(t), [description=\"aggregated flow from pipes into node\", unit=u\"m^3/s\", input=true]\n end\n @equations begin\n q̃_inj ~ load_profile(t)\n C * Dt(p) ~ q̃_inj + q̃_nw # (30)\n end\nend\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Pipe-Model","page":"Gas Network","title":"Pipe Model","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The pipe is modeld as a first order ODE for the volumetric flow at the dst end. It has two inputs: the pressure at the source and and the pressure at the destination end. Later on, we'll specify the model to be antisymmetric, thus the flow is calculated explicitly for the destination end, but the source end will just recive just that times (-1).","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@mtkmodel Pipe begin\n @parameters begin\n L, [description=\"Length of pipe\", unit=u\"m\"]\n D, [description=\"Diameter of pipe\", unit=u\"m\"]\n A, [description=\"Cross-sectional area of pipe\", unit=u\"m^2\"]\n sinθ, [description=\"Angle of inclination\" ]\n γ, [description=\"Friction efficiency factor\"]\n η, [description=\"Dynamic viscosity\", unit=u\"kg/(m*s)\"]\n r, [description=\"Pipe roughness\", unit=u\"m\"]\n g, [description=\"Gravitational acceleration\", unit=u\"m/s^2\"]\n T, [description=\"simulation temperature\", unit=u\"K\"]\n Tc, [description=\"crictical temperature\", unit=u\"K\"]\n pc, [description=\"critical pressure\", unit=us\"Pa\"]\n Rs, [description=\"Specific gas constant for natural gas\", unit=us\"J/(kg*K)\"]\n c̃, [description=\"Speed of sound in fluid at standard conditions\", unit=u\"m/s\"]\n ρ̃, [description=\"standard density\", unit=u\"kg/m^3\"]\n p̃, [description=\"standard pressure\", unit=us\"Pa\"]\n end\n @variables begin\n p_src(t), [description=\"Pressure at source end\", unit=us\"Pa\", input=true]\n p_dst(t), [description=\"Pressure at destination end\", unit=us\"Pa\", input=true]\n q̃(t)=1, [description=\"Flow through pipe\", unit=u\"m^3/s\", output=true]\n Re(t), [description=\"Reynolds number\"]\n λ(t), [description=\"Friction factor\"]\n λe(t), [description=\"Effective friction factor\"]\n pM(t), [description=\"mean pressure\", unit=us\"Pa\"]\n Z(t), [description=\"compressibility factor\"]\n ρ(t), [description=\"density\", unit=u\"kg/m^3\"]\n c(t), [description=\"speed of sound\", unit=u\"m/s\"]\n end\n @equations begin\n Z ~ 1 - 3.52 * pM/pc * exp(-2.26*(T/Tc)) + 0.274 * (pM/pc)^2 * exp(-1.878*(T/Tc)) # (5)\n ρ ~ pM / (Rs * T * Z) # (4)\n\n # TODO: Whats the correct speed of sound?\n c ~ sqrt(T * Rs * Z) # (4) # pressure/temp dependent\n # c ~ c̃ # \"standard\" speed of sound based on standard conditions\n\n # TODO: Whats the correct Reynolds number?\n Re ~ (ρ * abs(q̃*p̃/pM) * D) / (η * A) # (6) # based \"actual\" conditions\n # Re ~ (ρ̃ * abs(q̃) * D) / (η * A) # (6) # based on standard conditions\n\n λ ~ ifelse(Re < 2300,\n 64/Re, # laminar (7)\n (2*log10(4.518/Re * log10(Re/7) + r/(3.71*D)))^(-2) # turbulent (8)\n )\n λe ~ λ/γ^2 # (10)\n pM ~ 2/3*(p_src + p_dst - (p_src*p_dst)/(p_src + p_dst)) # (20)\n\n Dt(q̃) ~ A/(L*ρ̃)*(-(λe * ρ̃^2 * c^2 * L * abs(q̃))/(2 * D * A^2 * pM) * q̃ - (g * L * sinθ)/(c^2) * pM + (p_src - p_dst)) # (31)\n end\nend\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Parametrization","page":"Gas Network","title":"Parametrization","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The parameterization turned out to be a bit tricky. There might be errors in there.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Some of them are quite cleare and explicitly given.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"g = 9.81u\"m/s^2\" # that we just know\nRs = 518.28u\"J/(kg*K)\" # Specific gas constant for natural gas\nη = 1e-5u\"kg/(m*s)\" # Dynamic viscosity\npc = 46.5u\"bar\" # Critical pressure\np̃ = 1.01325u\"bar\" # standard pressure\nTc = 190.55u\"K\" # critical temperature\nT̃ = 273.15u\"K\" # standard temperature\nT = 278u\"K\" # simulation temperature\nγ = 0.98 # friction efficiency factor\nr = 0.012u\"mm\" # pipe roughness\nD = 0.6u\"m\" # pipe diameter\n\n# TODO: here is switched the lenths l12 and l13. The results are better. Is this a mistake in the paper?\nL₁₂ = 90u\"km\"\nL₁₃ = 80u\"km\"\nL₂₃ = 100u\"km\"\nΔh₁ = 0u\"km\" # this value is different for different sims in the paper\np₁_set = 50u\"bar\"\nnothing # hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The geometric parameters for the pipes can be directly derived.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"A = π/4 * D^2\nsinθ₁₂ = ustrip(Δh₁ / L₁₂)\nsinθ₁₃ = ustrip(Δh₁ / L₁₃)\nsinθ₂₃ = 0.0\nnothing # hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Lastly, we need to calculate the compressibility factor, the speed of sound and the density at standard conditions:","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Z̃ = 1 - 3.52 * p̃/pc * exp(-2.26*(T̃/Tc)) + 0.274 * (p̃/pc)^2 * exp(-1.878*(T̃/Tc)) # (5)\nc̃ = sqrt(T̃ * Rs * Z̃) # (4) at standard conditions\nρ̃ = p̃ / (Rs * T̃ * Z̃) # (4) at standard conditions\n\nnothing # hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The equivalent \"pressure capacity\" at the nodes is calculated as a sum of the connected pipe parameters according to (28).","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Here use defintions based on the speed and \"standard\" conditions.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"C₂ = L₁₂*A/(2*ρ̃*c̃^2) + L₂₃*A/(2*ρ̃*c̃^2) # (28)\nC₃ = L₁₃*A/(2*ρ̃*c̃^2) + L₂₃*A/(2*ρ̃*c̃^2) # (28)\nnothing #hide","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Alternatively, we could calculate Z2 and Z3 based on the actuel pressure and simulation temperature. Then we could calculated the speed of sound for the \"correct\" conditions at the node. It seems to have very little effect on the actual results so I kept it simple.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"nothing #hide","category":"page"},{"location":"generated/gas_network/#Load-Profile","page":"Gas Network","title":"Load Profile","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The paper specifies the load profile at two nodes. We use the package LinearInterpolations.jl to get a callable object which represents this picewise linear interpolation.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"However, this function is not Symbolics.jl compatible, so we need to stop Symbolics.jl/ModelingToolkit.jl from tracing it. To do so, we use @register_symbolic to declare it as a symbolic function which is treated as a blackbox.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Additionally, we need to tell ModelingToolkit about the units of this object. This is just used for the static unit check during construction of the model. Later one, when we generate the Julia code from the symbolic reepresentation all units will be stripped.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"note: Discontinuities in RHS\nThe picewise linear interpolated function creates discontinuities in the RHS of the system. However since we know the times exactly, we can handle this by simply giving a list of explicit tstops to the solve command, to make sure those are hit exactly.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"load2(t) = -Interpolate(SA[0, 4, 12, 20, 24]*3600, SA[20, 30, 10, 30, 20], extrapolate=LinearInterpolations.Constant(20))(t)\nload3(t) = -Interpolate(SA[0, 4, 12, 20, 24]*3600, SA[40, 50, 30, 50, 40], extrapolate=LinearInterpolations.Constant(40))(t)\n@register_symbolic load2(t)\n@register_symbolic load3(t)\nModelingToolkit.get_unit(op::typeof(load2), _) = u\"m^3/s\"\nModelingToolkit.get_unit(op::typeof(load3), _) = u\"m^3/s\"\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Building-the-Network","page":"Gas Network","title":"Building the Network","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To bild the Network we first need to define the components. This is a two step process:","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"first create the symbolic ODESystem using ModelingToolkit\nsecondly build a NetworkDynamics component model (VertexModel/EdgeModel) based on the symbolic system.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"In the first step we can use the keyword arguments to pass \"default\" values for our parameters and states. Those values will be automaticially transfered to the metadata of the component model the second step.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"The second step requires to define the interface variables, i.e. what are the \"input\" states of your component model and what are the \"output\" states. For VertexModel the input state is the aggregated flow of all connected pipes. The output state is the pressure of the node.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named v1_mtk = ConstantPressureNode(p_set=p₁_set)\nv1 = VertexModel(v1_mtk, [:q̃_nw], [:p]; name=:v1, vidx=1)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named v2_mtk = VariablePressureNode(C=C₂, load_profile=load2)\nv2 = VertexModel(v2_mtk, [:q̃_nw], [:p]; name=:v2, vidx=2)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named v3_mtk = VariablePressureNode(C=C₃, load_profile=load3)\nv3 = VertexModel(v3_mtk, [:q̃_nw], [:p]; name=:v3, vidx=3)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"For the edge Model we have two inputs: the pressure on both source and destination end. There is a single output state: the volumetric flow. However we also need to tell NetworkDynamics about the coupling type. In this case we use AntiSymmetric, which meas that the source end will recieve the same flow, just inverted sign.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"@named e12_mtk = Pipe(; L=L₁₂, sinθ=sinθ₁₂, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)\n@named e13_mtk = Pipe(; L=L₁₃, sinθ=sinθ₁₃, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)\n@named e23_mtk = Pipe(; L=L₂₃, sinθ=sinθ₂₃, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)\n\ne12 = EdgeModel(e12_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e12, src=1, dst=2)\ne13 = EdgeModel(e13_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e13, src=1, dst=3)\ne23 = EdgeModel(e23_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e23, src=2, dst=3)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To build the network object we just need to pass the vertices and edges to the constructor.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Note that we've used the vidx and src/dst keywords in the constructors to define for each component to which \"part\" of the network it belongs.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This means, the constructor can automaticially construct a graph based on those informations and we don't need to pass it explicitly.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"nw = Network([v1, v2, v3], [e12, e13, e23])","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"As a result, we recive a network with 3 unique types (v2 and v3 are similar but structurally different, because both functions capure a unique loadprofile function).","category":"page"},{"location":"generated/gas_network/#Finding-a-Steady-State","page":"Gas Network","title":"Finding a Steady State","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To simulate the systme, we first need to find a steadystate. As a \"guess\" for that we create a NWState object from the network. This will allocate flat arrays for states u and parameters p and fill them with the default values.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"uguess = NWState(nw)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This is not a steadystate of the system however. To find a true steadystate we want to ensure that the lhs of the system is zero. We can solve for a steady state numerically by defining a Nonlinear Rootfind problem.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"To do so, we need to wrap the Network object in a closure.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"nwwrap = (du, u, p) -> begin\n nw(du, u, p, 0)\n nothing\nend\ninitprob = NonlinearProblem(nwwrap, uflat(uguess), pflat(uguess))\ninitsol = solve(initprob)","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"We can create a new NWState object by wrapping the solution from the nonlinear problem and the original prameters in a new NWState object.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"u0 = NWState(nw, initsol.u, uguess.p)","category":"page"},{"location":"generated/gas_network/#Solving-the-ODE","page":"Gas Network","title":"Solving the ODE","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Using this as our initial state we can create the actual ODEProblem. Since the ode allways operates on flat state and aprameter arrays we use uflat and pflat to extract them.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"prob = ODEProblem(nw, uflat(u0), (0.0,24*3600), copy(pflat(u0)))\nsol = solve(prob, Tsit5(), tstops=[0,4,12,20,24]*3600)\nnothing #hide","category":"page"},{"location":"generated/gas_network/#Inspect-the-Solution","page":"Gas Network","title":"Inspect the Solution","text":"","category":"section"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Inspecting the solution is all which is left to do.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"xticks = ((0:4:24)*3600, string.(0:4:24)) # its nice to display hours\nfig = begin\n _fig = Figure()\n row = 1\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"pressure [Pa]\", title=\"Pressure at nodes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n ylims!(ax, 47.9e5, 49.9e5)\n for i in 1:3\n lines!(ax, sol, idxs=vidxs(nw, i, :p); label=\"v$i\", color=Cycled(i))\n end\n axislegend(ax)\n row += 1\n\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"flow [m³/s]\", title=\"Flow through pipes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n ylims!(ax, 16, 44)\n for i in 1:2\n lines!(ax, sol, idxs=eidxs(nw, i, :q̃); label=\"e$i flow\", color=Cycled(i))\n end\n axislegend(ax, position=:rb)\n row += 1\n _fig\nend","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Notably, the \"internal\" states defined in the symbolic models are not \"states\" in the sense of the ODE. For example, we captured the load profile in the q̃_inj state of the VariablePressureNode. The only dynamic state of the model however is p. Using the \"observables\" mechanism from SciML, which is implemented by NetworkDynamics, we can reconstruct those \"optimized\" states which have been removed symbolicially. Here we plot the reconstructed load profile of nodes 2 and 3. Also, we know that node 1 is infinetly stiff, acting as an infinite source of volumetric flow. We can reconstruct this flow too.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"fig = begin\n _fig = Figure()\n row = 1\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"flow [m³/s]\", title=\"Flow at nodes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n lines!(ax, sol, idxs=vidxs(nw, 1, :q̃_inj); label=\"v1 compensation\", color=Cycled(1))\n for i in 2:3\n lines!(ax, sol, idxs=vidxs(nw, i, :q̃_inj); label=\"v$i load profile\", color=Cycled(i))\n end\n axislegend(ax, position=:rc)\n _fig\nend","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"Lastly we want to observe two internal states of the pipes: the Reynolds number and the mean pressure. We see, that we're purely in the turbulent flow regime.","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"fig = begin\n _fig = Figure()\n row = 1\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"Reynolds number\", title=\"Reynolds number\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n for i in 1:3\n lines!(ax, sol, idxs=eidxs(nw, i, :Re); label=\"e $i\", color=Cycled(i))\n end\n hlines!(ax, 2300, color=:black, linestyle=:dash, label=\"L/T transition\")\n axislegend(ax, position=:rb)\n row += 1\n\n ax = Axis(_fig[row, 1]; xlabel=\"time [h]\", ylabel=\"Mean pressure [Pa]\", title=\"Mean pressure in pipes\", xticks)\n xlims!(ax, sol.t[begin], sol.t[end])\n for i in 1:3\n lines!(ax, sol, idxs=eidxs(nw, i, :pM); label=\"e $i\", color=Cycled(i))\n end\n axislegend(ax, position=:rb)\n _fig\nend","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"","category":"page"},{"location":"generated/gas_network/","page":"Gas Network","title":"Gas Network","text":"This page was generated using Literate.jl.","category":"page"},{"location":"#NetworkDynamics","page":"General","title":"NetworkDynamics","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"A package for working with dynamical systems on complex networks. NetworkDynamics.jl provides an interface between Graphs.jl and DifferentialEquations.jl. It allows for high performant simulation of dynamic networks by describing the local dynamics on the edges and vertices of the graph.","category":"page"},{"location":"","page":"General","title":"General","text":"The behavior of a node or an edge can be described by algebraic equations, by differential algebraic equation (DAEs) in mass matrix form or by ordinary differential equations (ODE). ","category":"page"},{"location":"","page":"General","title":"General","text":"The central construction is the function Network that receives functions describing the local dynamics on the edges and nodes of a graph g as inputs, and returns a composite function compatible with the DifferentialEquations.jl calling syntax.","category":"page"},{"location":"","page":"General","title":"General","text":"nd = Network(g, vertex_dynamics, edge_dynamics)\nnd(dx, x, p, t)","category":"page"},{"location":"","page":"General","title":"General","text":"Main features:","category":"page"},{"location":"","page":"General","title":"General","text":"Clear separation of local dynamics and topology: you can easily change topology of your system or switching out dynamical components.\nHigh performance when working with heterogeneous models (which means heaving different local dynamics in different parts of your network).\nSymbolic Indexing into solutions and states: NetworkDynamics keeps track of the states of the individual subsystems.\nDifferent execution schemes: NetworkDynamics exploits the known inter-dependencies between components to auto parallelize execution, even on GPUs!\nEquation based models: use ModelingToolkit.jl to define local dynamics, use NetworkDynamics.jl to combine them into large networks!","category":"page"},{"location":"#Where-to-begin?","page":"General","title":"Where to begin?","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"Check out the Mathematical Model to understand the underlying modelling ideas of NetworkDynamics followed by the page on Network Construction to learn how to implement you own models.","category":"page"},{"location":"","page":"General","title":"General","text":"If you prefer to look at some concrete code first check out the Getting Started tutorial!","category":"page"},{"location":"#Installation","page":"General","title":"Installation","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"Installation is straightforward with Julia's package manager.","category":"page"},{"location":"","page":"General","title":"General","text":"(v1.11) pkg> add NetworkDynamics","category":"page"},{"location":"#Reproducibility","page":"General","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"
Direct dependencies used for this documentation:","category":"page"},{"location":"","page":"General","title":"General","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"General","title":"General","text":"
","category":"page"},{"location":"","page":"General","title":"General","text":"
Julia Version:","category":"page"},{"location":"","page":"General","title":"General","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"General","title":"General","text":"
","category":"page"},{"location":"","page":"General","title":"General","text":"
Full Manifest:","category":"page"},{"location":"","page":"General","title":"General","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"General","title":"General","text":"
","category":"page"},{"location":"#Funding","page":"General","title":"Funding","text":"","category":"section"},{"location":"","page":"General","title":"General","text":"Development of this project was in part funded by the German Federal Ministry for Economic Affairs and Climate Action as part of the OpPoDyn-Project (Project ID 01258425/1, 2024-2027).","category":"page"},{"location":"","page":"General","title":"General","text":"","category":"page"},{"location":"network_construction/#Network-Construction","page":"Network Construction","title":"Network Construction","text":"","category":"section"},{"location":"network_construction/#Building-a-Network","page":"Network Construction","title":"Building a Network","text":"","category":"section"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"The main type of NetworkDyanmics.jl is a Network. A network bundles various component models (edge and vertex models) together with a graph to form a callable object which represents the RHS of the overall dynamical system, see Mathematical Model.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"A Network is build by passing a graph g, vertex models vertexm and edge models edgem.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"nw = Network(g, vertexm, edgem; kwargs...)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Two important keywords for the Network constructor are:","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"execution: Defines the ExecutionStyle of the coreloop, e.g. SequentialExecution{true}(). A execution style is a special struct which tells the backend how to parallelize for example. A list of available executions styles can be found under Execution Types in the API.\naggregator: Tells the backend how to aggregate and which aggregation function to use. Aggregation is the process of creating a single vertex input by reducing over the outputs of adjecent edges of said vertex. The aggregator contains both the function and the algorith. E.g. SequentialAggregator(+) is a sequential aggregation by summation. A list of availabe Aggregators can be found under Aggregators in the API.","category":"page"},{"location":"network_construction/#Building-VertexModels","page":"Network Construction","title":"Building VertexModels","text":"","category":"section"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"This chapter walks through the most important aspects when defining custom vertex model. For a list of all keyword arguments please check out the docstring of VertexModel. As an example, we'll construct an second order kuramoto model, because that's what we do.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"using NetworkDynamics #hide\nfunction kuramoto_f!(dv, v, esum, p, t)\n M, P, D = p\n dv[1] = v[2]\n dv[2] = (P - D*v[2] + esum[1])/M\n nothing\nend\nfunction kuramoto_g!(y, v, esum, p, t)\n y[1] = v[1]\n nothing\nend\nVertexModel(; f=kuramoto_f!, g=kuramoto_g!, dim=2, pdim=3, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Those keywords are the minimum metadata we need to provide.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"However there is a problem: the vertex is classified as a FeedForward vertex, which is unnecessary. We can improve the implementation of g according to the Feed Forward Behavior section.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function kuramoto_g_noff!(y, v, p, t)\n y[1] = v[1]\n nothing\nend\nVertexModel(; f=kuramoto_f!, g=kuramoto_g_noff!, dim=2, pdim=3, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"It is still annoying to explicitly write this trivial output function. You can prevent this by using StateMask. By writing","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"VertexModel(; f=kuramoto_f!, g=StateMask(1:1), dim=2, pdim=3)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"we told the vertex model, that the output is part of the states x[1:1]. This enables a few things:","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"outdim is not needed anymore, can be inferred from StateMask\noutsym is not a generic :o any more but inferred from the state symbols.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"We can be even less verbose by writing g=1:1 or just g=1.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"In a last we define better names for our states and parameters as well as assigning a position in the graph to enable the graphless network construction. Whenever you provide sym keyword the corresponding dim keyword is not neccessary anymore. We end up with a relatively short definition","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"VertexModel(; f=kuramoto_f!, g=1,\n sym=[:θ, :ω], psym=[:M=>1, :P=>0.1, :D=>0], \n insym=[:P_nw], name=:swing, vidx=1)","category":"page"},{"location":"network_construction/#Building-EdgeModels","page":"Network Construction","title":"Building EdgeModels","text":"","category":"section"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"This chapter walks through the most important aspects when defining custom edge models. For a list of all keyword arguments please check out the docstring of EdgeModel.","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"As an example edge model we want to define standard sinusoidal coupling between the vertices in our network. The full definition looks like this:","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_f!(de, e, vsrc, vdst, p, t)\n nothing\nend\nfunction edge_g!(ysrc, ydst, e, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\n ysrc[1] = -ydst[1]\nend\nEdgeModel(; f=edge_f!, g=edge_g!, dim=0, pdim=1, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"This is a purely \"static\" edge without internal states. This means we can omit f and dim entirely. Also, we can define a variant of g without the e input","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_g_ff!(ysrc, ydst, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\n ysrc[1] = -ydst[1]\nend\nEdgeModel(;g=edge_g_ff!, pdim=1, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"which no classifies as a PureFeedForward edge. In cases like this, where the edge is actually anti symmetric we can alternatively define a single sided output function and wrapping it in an AntiSymmetric object","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_g_s!(ydst, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\nend\nEdgeModel(;g=AntiSymmetric(edge_g_ff!), pdim=1, outdim=1)","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"which can also lead to briefer output naming. Available single sided wrappers are","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Directed (no coupling at src), \nAntiSymmetric (same coupling at src and dst),\nSymmetric (inverse coupling at dst) and\nFiducial (define separate g for both ends).","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"Once again we can add additonal data like defining a src and dst index","category":"page"},{"location":"network_construction/","page":"Network Construction","title":"Network Construction","text":"function edge_g_s!(ydst, vsrc, vdst, p, t)\n ydst[1] = p[1] * sin(vsrc[1] - vdst[1])\nend\nEdgeModel(;g=AntiSymmetric(edge_g_ff!), psym=:K=>1, outsym=:P, insym=:θ, src=1, dst=4)","category":"page"}] } diff --git a/previews/PR187/symbolic_indexing/09a1e1ea.svg b/previews/PR187/symbolic_indexing/09a1e1ea.svg deleted file mode 100644 index d96eae0b..00000000 --- a/previews/PR187/symbolic_indexing/09a1e1ea.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/previews/PR187/symbolic_indexing/3fa3fbb3.svg b/previews/PR187/symbolic_indexing/3fa3fbb3.svg deleted file mode 100644 index ca531a9d..00000000 --- a/previews/PR187/symbolic_indexing/3fa3fbb3.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/previews/PR187/symbolic_indexing/7da979be.svg b/previews/PR187/symbolic_indexing/7da979be.svg new file mode 100644 index 00000000..6a9198ab --- /dev/null +++ b/previews/PR187/symbolic_indexing/7da979be.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/symbolic_indexing/a6deaf28.svg b/previews/PR187/symbolic_indexing/a6deaf28.svg deleted file mode 100644 index 5320c471..00000000 --- a/previews/PR187/symbolic_indexing/a6deaf28.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/previews/PR187/symbolic_indexing/b5cf3b78.svg b/previews/PR187/symbolic_indexing/b5cf3b78.svg new file mode 100644 index 00000000..0558d37f --- /dev/null +++ b/previews/PR187/symbolic_indexing/b5cf3b78.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/symbolic_indexing/d0fedf7a.svg b/previews/PR187/symbolic_indexing/d0fedf7a.svg new file mode 100644 index 00000000..f09a4092 --- /dev/null +++ b/previews/PR187/symbolic_indexing/d0fedf7a.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR187/symbolic_indexing/index.html b/previews/PR187/symbolic_indexing/index.html index b203e3dc..763e3850 100644 --- a/previews/PR187/symbolic_indexing/index.html +++ b/previews/PR187/symbolic_indexing/index.html @@ -1,5 +1,5 @@ -Symbolic Indexing · NetworkDynamics

Symbolic Indexing

Using SciML's SymblicIndexingInterface.jl, ND.jl provides lots of methods to access and change variables and Parameters.

Setup code to make following examples work
using NetworkDynamics
+Symbolic Indexing · NetworkDynamics

Symbolic Indexing

Using SciML's SymblicIndexingInterface.jl, ND.jl provides lots of methods to access and change variables and Parameters.

Setup code to make following examples work
using NetworkDynamics
 using Graphs
 using OrdinaryDiffEqTsit5
 using Plots

Provide Symbol Names

When construction component models, you can pass symbolic names using the sym and psym keywords.

function _edgef!(e, v_s, v_d, (K,), t)
@@ -19,12 +19,12 @@
 s.v[:,:storage] .= randn(5)
 prob = ODEProblem(nw, uflat(s), (0,2), pflat(s))
 sol = solve(prob, Tsit5())

Those fundamental indices can be used in a lot of scenarios. Most importantly you can use them to

sol(sol.t; idxs=VIndex(1, :storage))   # extract timeseries out ouf solution object
-plot(sol; idxs=[VIndex(1, :storage), VIndex(5,:storage)]) # plot storage of two nodes
Example block output

Generate Symbolic Indices

Often, you need many individual symbolic indices. For that there are the helper methods vidxs, eidxs, vpidxs and epidxs. With the help of those methods you can generate arrays of symbolic indices:

vidxs(nw, :, :storage) # get variable "storage" for all nodes
5-element Vector{VIndex}:
+plot(sol; idxs=[VIndex(1, :storage), VIndex(5,:storage)]) # plot storage of two nodes
Example block output

Generate Symbolic Indices

Often, you need many individual symbolic indices. For that there are the helper methods vidxs, eidxs, vpidxs and epidxs. With the help of those methods you can generate arrays of symbolic indices:

vidxs(nw, :, :storage) # get variable "storage" for all nodes
5-element Vector{VIndex}:
  VIndex(1, :storage)
  VIndex(2, :storage)
  VIndex(3, :storage)
  VIndex(4, :storage)
- VIndex(5, :storage)
plot(sol; idxs=vidxs(nw, :, :storage))
Example block output

NWState and NWParameter Objects

Internally, both state and parameters of a Network are represented using flat arrays. To access the state or parameters of a network, you can use the NWState and NWParameter objects.

p = NWParameter(nw)
Parameter{Vector{Float64}} of Network (5 vertices, 8 edges)
+ VIndex(5, :storage)
plot(sol; idxs=vidxs(nw, :, :storage))
Example block output

NWState and NWParameter Objects

Internally, both state and parameters of a Network are represented using flat arrays. To access the state or parameters of a network, you can use the NWState and NWParameter objects.

p = NWParameter(nw)
Parameter{Vector{Float64}} of Network (5 vertices, 8 edges)
   ├─ EPIndex(1, :K) => 1.0
   ├─ EPIndex(2, :K) => 1.0
   ├─ EPIndex(3, :K) => 1.0
@@ -40,12 +40,12 @@
   └─ VIndex(5, :storage) => NaN
  p = NWParameter([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])
  t = nothing

No default values were provided in the network components, so the state array is filled with NaNs.

s[VIndex(:, :storage)] .= randn(5) # set the (initial) storage for alle nodes
NWState{Vector{Float64}} of Network (5 vertices, 8 edges)
-  ├─ VIndex(1, :storage) => -0.009414917868746786
-  ├─ VIndex(2, :storage) => -0.633720151543994
-  ├─ VIndex(3, :storage) => 0.7437538215695982
-  ├─ VIndex(4, :storage) => 0.7826503578127808
-  └─ VIndex(5, :storage) => -0.8191058507220634
+  ├─ VIndex(1, :storage) => -0.4183414458625708
+  ├─ VIndex(2, :storage) => -0.0856165818968941
+  ├─ VIndex(3, :storage) => -1.1289098618008735
+  ├─ VIndex(4, :storage) => -1.2163299453940157
+  └─ VIndex(5, :storage) => -0.8073742660020513
  p = NWParameter([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])
  t = nothing

For both NWState and NWParameter objects, the there is a more convenient way to access the variables and parameters.

@assert s.v[1, :storage] == s[VIndex(1, :storage)] # s.v -> access vertex states
 @assert s.e[1, :flow]    == s[EIndex(1, :flow)]    # s.e -> access edge states
-@assert s.p.e[1,:K]      == p[EPIndex(1, :K)]      # s.p -> access parameters

The NWState and NWParameter objects are mutable, thus changing them will also change the underlying wrapped flat arrays. You can allways access the flat representations by calling uflat and pflat.

Note

The NWState and NWParameter wrappers can be constructed from various objects. Fore example, within a callback you might construct p = NWParameter(integrator) to then change the parameters of the network within the callback.

Observables

Sometimes, the "states" you're interested in aren't really states in the DAE sense but rather algebraic derivations from DAE states, parameters and time – in accordance with the naming in the SciML-ecosystem those states are called Observables.

A prime example of Observables are edge/vertex-outputs, such as the flow in the edge model defined above. It is also possible to define additional Observables manually by using the obssym and obsf keyword on the EdgeModel/VertexModel constructors. When building models using ModelingToolkit, the reduced algebraic states will be preserved as observables automatically.

Observables can be accessed like any other state, for example, the flows in the network don't show up in the state array but can be accessed in all the ways discussed above, for example

plot(sol; idxs=eidxs(nw, :, :flow))
Example block output
+@assert s.p.e[1,:K] == p[EPIndex(1, :K)] # s.p -> access parameters

The NWState and NWParameter objects are mutable, thus changing them will also change the underlying wrapped flat arrays. You can allways access the flat representations by calling uflat and pflat.

Note

The NWState and NWParameter wrappers can be constructed from various objects. Fore example, within a callback you might construct p = NWParameter(integrator) to then change the parameters of the network within the callback.

Observables

Sometimes, the "states" you're interested in aren't really states in the DAE sense but rather algebraic derivations from DAE states, parameters and time – in accordance with the naming in the SciML-ecosystem those states are called Observables.

A prime example of Observables are edge/vertex-outputs, such as the flow in the edge model defined above. It is also possible to define additional Observables manually by using the obssym and obsf keyword on the EdgeModel/VertexModel constructors. When building models using ModelingToolkit, the reduced algebraic states will be preserved as observables automatically.

Observables can be accessed like any other state, for example, the flows in the network don't show up in the state array but can be accessed in all the ways discussed above, for example

plot(sol; idxs=eidxs(nw, :, :flow))
Example block output