Skip to content

Commit

Permalink
Merge pull request #181 from JuliaDynamics/hw/function2model
Browse files Browse the repository at this point in the history
  • Loading branch information
hexaeder authored Nov 19, 2024
2 parents f321e1e + 60e0ecc commit cd31781
Show file tree
Hide file tree
Showing 46 changed files with 604 additions and 602 deletions.
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# rename Edge/Vertex/ComponentFunction -> EdgeVertex/ComponentModel
10b8809cd185e5bc65f9c78ed5c1408b9e622802
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Users of the package should probably read the new documentation carefully.
The most important changes are:

- Explicit split in `f` and `g` function: There is no split into `ODE` and
`Static` components anymore, verything is unified in component functions with
`Static` components anymore, everything is unified in component models with
internal function `f` and output function `g`.
- Parameters handling: Parameters are allways stored in a flat array. The
Symbolic Indexing Interfaces helps to set and retrieve parameters.
Expand Down
8 changes: 4 additions & 4 deletions benchmark/benchmark_compat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ end

function _syms_old_order(nd::Network)
syms = []
for (i,cf) in enumerate(nd.im.vertexf)
for (i,cf) in enumerate(nd.im.vertexm)
isdynamic(cf) || continue
append!(syms, collect(VIndex(i, 1:dim(cf))))
end
for (i,cf) in enumerate(nd.im.edgef)
for (i,cf) in enumerate(nd.im.edgem)
isdynamic(cf) || continue
append!(syms, collect(EIndex(i, 1:dim(cf))))
end
syms
end
function _psyms_old_order(nd::Network)
syms = []
for (i,cf) in enumerate(nd.im.vertexf)
for (i,cf) in enumerate(nd.im.vertexm)
append!(syms, collect(VPIndex(i, 1:pdim(cf))))
end
for (i,cf) in enumerate(nd.im.edgef)
for (i,cf) in enumerate(nd.im.edgem)
append!(syms, collect(EPIndex(i, 1:pdim(cf))))
end
syms
Expand Down
18 changes: 9 additions & 9 deletions benchmark/benchmark_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,40 @@ Base.@propagate_inbounds function diffusionedge!(e, v_s, v_d, _, _)
e[1] = v_s[1] - v_d[1]
nothing
end
diffusion_edge() = EdgeFunction(; g=AntiSymmetric(diffusionedge!), outdim=1, pdim=0)
diffusion_edge() = EdgeModel(; g=AntiSymmetric(diffusionedge!), outdim=1, pdim=0)

Base.@propagate_inbounds function diffusion_dedge!(de, e, v_s, v_d, _, _)
de[1] = 100.0 * (sin(v_s[1] - v_d[1]) - e[1])
de[2] = 100.0 * (sin(v_d[1] - v_s[1]) - e[2])
nothing
end
diffusion_dedge() = EdgeFunction(; f=diffusion_dedge!, dim=2, pdim=0, g=Fiducial(dst=1:1, src=2:2))
diffusion_dedge() = EdgeModel(; f=diffusion_dedge!, dim=2, pdim=0, g=Fiducial(dst=1:1, src=2:2))

Base.@propagate_inbounds function diffusionvertex!(dv, _, esum, _, _)
dv[1] = esum[1]
nothing
end
diffusion_vertex() = VertexFunction(; f=diffusionvertex!, dim=1, pdim=0, g=StateMask(1:1))
diffusion_vertex() = VertexModel(; f=diffusionvertex!, dim=1, pdim=0, g=StateMask(1:1))

####
#### inhomogenious kuramoto system
####
Base.@propagate_inbounds function kuramoto_edge!(e, θ_s, θ_d, (K,), t)
e[1] = K * sin(θ_s[1] - θ_d[1])
end
static_kuramoto_edge() = EdgeFunction(; g=AntiSymmetric(kuramoto_edge!), outdim=1, pdim=1)
static_kuramoto_edge() = EdgeModel(; g=AntiSymmetric(kuramoto_edge!), outdim=1, pdim=1)

Base.@propagate_inbounds function kuramoto_vertex!(dθ, θ, esum, (ω,), t)
dθ[1] = ω + esum[1]
end
kuramoto_vertex_1d() = VertexFunction(; f=kuramoto_vertex!, pdim=1, sym=[], g=StateMask(1:1))
kuramoto_vertex_1d() = VertexModel(; f=kuramoto_vertex!, pdim=1, sym=[], g=StateMask(1:1))

Base.@propagate_inbounds function kuramoto_inertia!(dv, v, esum, (P,), t)
dv[1] = v[2]
dv[2] = P - 1.0 * v[2]
dv[2] += esum[1]
end
kuramoto_vertex_2d() = VertexFunction(; f=kuramoto_inertia!, dim=2, pdim=1, sym=[, ], g=StateMask(1:1));
kuramoto_vertex_2d() = VertexModel(; f=kuramoto_inertia!, dim=2, pdim=1, sym=[, ], g=StateMask(1:1));

####
#### powergrid model
Expand All @@ -60,7 +60,7 @@ Base.@propagate_inbounds function piline!(out_src, out_dst, src, dst, p, t)
out_src[2] = -imag(isrc)
nothing
end
piline() = EdgeFunction(; g=piline!, outsym=(;src=[:src_i_r, :src_i_i], dst=[:dst_i_r, :dst_i_i]),
piline() = EdgeModel(; g=piline!, outsym=(;src=[:src_i_r, :src_i_i], dst=[:dst_i_r, :dst_i_i]),
psym=[:L, :R, :C1, :C2])

Base.@propagate_inbounds function pq!(du, u, isum, (P,Q), t)
Expand All @@ -71,7 +71,7 @@ Base.@propagate_inbounds function pq!(du, u, isum, (P,Q), t)
du[2] = imag(uc)
nothing
end
pqnode() = VertexFunction(; f=pq!, sym=[:u_r, :u_i], g=StateMask(1:2), psym=[:P, :Q], mass_matrix=0)
pqnode() = VertexModel(; f=pq!, sym=[:u_r, :u_i], g=StateMask(1:2), psym=[:P, :Q], mass_matrix=0)

Base.@propagate_inbounds function gen!(dv, v, isum, p, T)
# unpack parameters
Expand Down Expand Up @@ -107,5 +107,5 @@ Base.@propagate_inbounds function gen!(dv, v, isum, p, T)

nothing
end
generator() = VertexFunction(; f=gen!, sym=[:u_r, :u_i, , ], g=StateMask(1:2),
generator() = VertexModel(; f=gen!, sym=[:u_r, :u_i, , ], g=StateMask(1:2),
psym=[:H, :P, :D, , :E_f, :T_d_dash, :T_q_dash, :X_d_dash, :X_q_dash, :X_d, :X_q])
12 changes: 6 additions & 6 deletions docs/examples/StochasticSystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Here, $dW_i = \xi_i dt$ is the infinitesimal increment of the Wiener process. Pr
## Implementing the Swing Equation
First we will implement the node and edge functions for the deterministic case
First we will implement the node and edge models for the deterministic case
without the fluctuations. We set the defaults for the inertia and damping
parameters to be $M_i = 1.0$ and $D_i = 0.1$. The default coupling strength is $K=6$.
=#
Expand All @@ -55,13 +55,13 @@ function swing_equation!(dv, v, esum, (M, P, D), t)
dv[2] = 1/M *(P - D * v[2] + esum[1])
nothing
end
swing_vertex = VertexFunction(f=swing_equation!, g=1, sym=[, ], psym=[:M=>1, :P, :D=>0.1])
swing_vertex = VertexModel(f=swing_equation!, g=1, sym=[, ], psym=[:M=>1, :P, :D=>0.1])
#-

function powerflow!(e, v_s, v_d, (K,), t)
e[1] = K * sin(v_s[1] - v_d[1])
end
powerflow_edge = EdgeFunction(g=AntiSymmetric(powerflow!), outdim=1, psym=[:K=>6])
powerflow_edge = EdgeModel(g=AntiSymmetric(powerflow!), outdim=1, psym=[:K=>6])

#=
## Contructing the Deterministic Dynamics
Expand Down Expand Up @@ -132,11 +132,11 @@ FIXME: stochastic system should be simulated using symbolic indexing, this is js
# nothing #hide #md

#=
Now we can construct the dynamics of the second layer by using `network_dynamics()`. Since the graph structure of the stochastic layer has no edges we can take the edge function of the deterministic case as a placeholder.
Now we can construct the dynamics of the second layer by using `network_dynamics()`. Since the graph structure of the stochastic layer has no edges we can take the edge model of the deterministic case as a placeholder.
=#

# fluctuation_vertex = VertexFunction(f=fluctuation!, g=1:2, dim=2)
# nd_noise = Network(h, fluctuation_vertex, NetworkDynamics.EdgeFunction[])
# fluctuation_vertex = VertexModel(f=fluctuation!, g=1:2, dim=2)
# nd_noise = Network(h, fluctuation_vertex, NetworkDynamics.EdgeModel[])
nothing #hide #md

#=
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/cascading_failure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function swing_equation(dv, v, esum, p,t)
dv[2] = dv[2] / I
nothing
end
vertex = VertexFunction(f=swing_equation, g=1, sym=[, ], psym=[:P_ref, :I=>1, =>0.1])
vertex = VertexModel(f=swing_equation, g=1, sym=[, ], psym=[:P_ref, :I=>1, =>0.1])

#=
Lets define a simple purely active power line whose active power flow is
Expand All @@ -48,7 +48,7 @@ We give an additonal parameter, the line limit, which we'll use later in the cal
function simple_edge(e, v_s, v_d, (K,), t)
e[1] = K * sin(v_s[1] - v_d[1])
end
edge = EdgeFunction(;g=AntiSymmetric(simple_edge), outsym=:P, psym=[:K=>1.63, :limit=>1])
edge = EdgeModel(;g=AntiSymmetric(simple_edge), outsym=:P, psym=[:K=>1.63, :limit=>1])

#=
With the definition of the graph topology we can build the `Network` object:
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/directed_and_weighted_graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ nothing #hide #md
#=
## Setting up the ODEProblem
Defining `VertexFunction` and `EdgeFunction` 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](https://docs.julialang.org/en/v1/devdocs/boundscheck/).
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](https://docs.julialang.org/en/v1/devdocs/boundscheck/).
=#

Expand All @@ -86,14 +86,14 @@ Base.@propagate_inbounds function fhn_electrical_vertex!(dv, v, esum, p, t)
dv[2] = (v[1] - a) * ϵ
nothing
end
vertex = VertexFunction(f=fhn_electrical_vertex!, g=1, sym=[:u, :v], psym=[:a=>0.5, =>0.05])
vertex = VertexModel(f=fhn_electrical_vertex!, g=1, sym=[:u, :v], psym=[:a=>0.5, =>0.05])
#-

Base.@propagate_inbounds function electrical_edge!(e, v_s, v_d, (w, σ), t)
e[1] = w * (v_s[1] - v_d[1]) * σ
nothing
end
electricaledge = EdgeFunction(g=Directed(electrical_edge!), outdim=1, psym=[:weight, =>0.5])
electricaledge = EdgeModel(g=Directed(electrical_edge!), outdim=1, psym=[:weight, =>0.5])
#-

fhn_network! = Network(g_directed, vertex, electricaledge)
Expand Down
20 changes: 10 additions & 10 deletions docs/examples/gas_network.jl
Original file line number Diff line number Diff line change
Expand Up @@ -234,25 +234,25 @@ nothing #hide
To bild the Network we first need to define the components. This is a two step process:
- first create the symbolic `ODESystem` using ModelingToolkit
- secondly build a NetworkDynamics component function ([`VertexFunction`](@ref)/[`EdgeFunction`](@ref)) based on the symbolic system.
- secondly build a NetworkDynamics component model ([`VertexModel`](@ref)/[`EdgeModel`](@ref)) based on the symbolic system.
In the first step we can use the keyword arguments to pass "default" values for our parameters and states.
Those values will be automaticially transfered to the metadata of the component function the second step.
Those values will be automaticially transfered to the metadata of the component model the second step.
The second step requires to define the interface variables, i.e. what are the "input" states of your
component function and what are the "output" states.
For `VertexFunction` the input state is the aggregated flow of all connected pipes. The output state is the pressure of the node.
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.
=#
@named v1_mtk = ConstantPressureNode(p_set=p₁_set)
v1 = VertexFunction(v1_mtk, [:q̃_nw], [:p]; name=:v1, vidx=1)
v1 = VertexModel(v1_mtk, [:q̃_nw], [:p]; name=:v1, vidx=1)
#

@named v2_mtk = VariablePressureNode(C=C₂, load_profile=load2)
v2 = VertexFunction(v2_mtk, [:q̃_nw], [:p]; name=:v2, vidx=2)
v2 = VertexModel(v2_mtk, [:q̃_nw], [:p]; name=:v2, vidx=2)
#

@named v3_mtk = VariablePressureNode(C=C₃, load_profile=load3)
v3 = VertexFunction(v3_mtk, [:q̃_nw], [:p]; name=:v3, vidx=3)
v3 = VertexModel(v3_mtk, [:q̃_nw], [:p]; name=:v3, vidx=3)

#=
For the edge Model we have two inputs: the pressure on both source and destination end.
Expand All @@ -264,9 +264,9 @@ meas that the source end will recieve the same flow, just inverted sign.
@named e13_mtk = Pipe(; L=L₁₃, sinθ=sinθ₁₃, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)
@named e23_mtk = Pipe(; L=L₂₃, sinθ=sinθ₂₃, D, A, γ, η, r, g, T, Tc, pc, Rs, c̃, ρ̃, p̃)

e12 = EdgeFunction(e12_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e12, src=1, dst=2)
e13 = EdgeFunction(e13_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e13, src=1, dst=3)
e23 = EdgeFunction(e23_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e23, src=2, dst=3)
e12 = EdgeModel(e12_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e12, src=1, dst=2)
e13 = EdgeModel(e13_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e13, src=1, dst=3)
e23 = EdgeModel(e23_mtk, [:p_src], [:p_dst], AntiSymmetric([:q̃]); name=:e23, src=2, dst=3)

#=
To build the network object we just need to pass the vertices and edges to the constructor.
Expand Down
20 changes: 10 additions & 10 deletions docs/examples/getting_started_with_network_dynamics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ From the above considerations we see that in this model the nodes do not have an
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 function definition outlined in [Mathematical Model](@ref):
the sum over all flows of connected edges. This directly correspons to the component model definition outlined in [Mathematical Model](@ref):
```math
\begin{aligned}
\dot x^\mathrm{v} &= f^{\mathrm v}(u^{\mathrm v}, \sum_k^{\text{incident}} y^{\mathrm e}_k, p^{\mathrm v}, t) &&= \sum_k^\mathrm{incident} y^\mathrm{e}_k \\
Expand All @@ -50,7 +50,7 @@ y^{\mathrm e}_{\mathrm{src}} &= g_\mathrm{src}^{\mathrm e}(u^{\mathrm e}, y^{\ma
\end{aligned}
```
### Definition of `EdgeFunction`
### Definition of `EdgeModel`
=#
function diffusionedge_g!(e_dst, v_src, v_dst, p, t)
## e_dst, v_src, v_dst are arrays, hence we use the broadcasting operator
Expand All @@ -64,12 +64,12 @@ The function `diffusionedge_g!` takes as inputs the current state of the edge `e
`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).
Notably, this function only models $g_\mathrm{dst}$. However we can wrap this single-sided output function in an [`AntiSymmetric`](@ref) output wrapper to construct the [`EdgeFunction`](@ref):
Notably, this function only models $g_\mathrm{dst}$. However we can wrap this single-sided output function in an [`AntiSymmetric`](@ref) output wrapper to construct the [`EdgeModel`](@ref):
=#
nd_diffusion_edge = EdgeFunction(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow])
nd_diffusion_edge = EdgeModel(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow])

#=
### Definition of `VertexFunction`
### Definition of `VertexModel`
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.
=#
function diffusionvertex_f!(dv, v, esum, p, t)
Expand All @@ -84,7 +84,7 @@ Just like above the input arguments `v, esum, p, t` are mandatory for the syntax
The output function `g` is just taking part of the internal states. For that we can use the [`StateMask`](@ref) helper function `g = StateMaks(1:1)`
=#
nd_diffusion_vertex = VertexFunction(; f=diffusionvertex_f!, g=StateMask(1:1), dim=1)
nd_diffusion_vertex = VertexModel(; f=diffusionvertex_f!, g=StateMask(1:1), dim=1)


#=
Expand All @@ -104,7 +104,7 @@ nothing #hide #md
nd = Network(g, nd_diffusion_vertex, nd_diffusion_edge)

#=
The constructor `Network` combines the component function with the topological information contained in the graph **`g`** and returns an `Network` compatible with the solvers of `DifferentialEquations.jl`.
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)
Expand All @@ -129,16 +129,16 @@ In oder to collect multiple indices we can use the helper function [`vidxs`](@re
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 `VertexFunction`.
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

## We will have two independent diffusions on the network, hence dim = 2
nd_diffusion_vertex_2 = VertexFunction(; f=diffusionvertex_f!, g=1:2, dim=2, sym=[:x, ])
nd_diffusion_edge_2 = EdgeFunction(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow_x, :flow_ϕ])
nd_diffusion_vertex_2 = VertexModel(; f=diffusionvertex_f!, g=1:2, dim=2, sym=[:x, ])
nd_diffusion_edge_2 = EdgeModel(; g=AntiSymmetric(diffusionedge_g!), outsym=[:flow_x, :flow_ϕ])
nd_2 = Network(g, nd_diffusion_vertex_2, nd_diffusion_edge_2)

x0_2 = vec(transpose([randn(rng, N) .^ 2 randn(rng, N)])) # x ~ N(0,1)^2; ϕ ~ N(0,1)
Expand Down
14 changes: 7 additions & 7 deletions docs/examples/heterogeneous_system.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ function kuramoto_edge!(e, θ_s, θ_d, (K,), t)
e[1] = K * sin(θ_s[1] - θ_d[1])
nothing
end
edge! = EdgeFunction(g=AntiSymmetric(kuramoto_edge!), outdim=1, psym=[:K=>3])
edge! = EdgeModel(g=AntiSymmetric(kuramoto_edge!), outdim=1, psym=[:K=>3])
#-

function kuramoto_vertex!(dθ, θ, esum, (ω0,), t)
dθ[1] = ω0 + esum[1]
nothing
end
vertex! = VertexFunction(f=kuramoto_vertex!, g=StateMask(1:1), sym=[], psym=[:ω0], name=:kuramoto)
vertex! = VertexModel(f=kuramoto_vertex!, g=StateMask(1:1), sym=[], psym=[:ω0], name=:kuramoto)
#-

nw = Network(g, vertex!, edge!)
Expand Down Expand Up @@ -78,7 +78,7 @@ function static_g(out, u, p, t)
out[1] = p[1]
nothing
end
static! = VertexFunction(g=static_g, outsym=[], psym=[:θfix => ω[1]], name=:static)
static! = VertexModel(g=static_g, outsym=[], psym=[:θfix => ω[1]], name=:static)

#=
But wait! NetworkDynamics classified this as [`PureFeedForward`](@ref), because it cannot
Expand All @@ -90,7 +90,7 @@ g(out, ins, p, t) # NoFeedForward
and since `dim(u)=0` it wrongfully assumes that the latter is meant.
We can overwrite the classification by passing the ff keyword:
=#
static! = VertexFunction(g=static_g, outsym=[], psym=[:θfix => ω[1]], ff=NoFeedForward(), name=:static)
static! = VertexModel(g=static_g, outsym=[], psym=[:θfix => ω[1]], ff=NoFeedForward(), name=:static)

#=
A Kuramoto model with inertia consists of two internal variables leading to
Expand All @@ -102,15 +102,15 @@ function kuramoto_inertia!(dv, v, esum, (ω0,), t)
nothing
end

inertia! = VertexFunction(f=kuramoto_inertia!, g=1:1, sym=[, ], psym=[:ω0], name=:inertia)
inertia! = VertexModel(f=kuramoto_inertia!, g=1:1, sym=[, ], psym=[:ω0], name=:inertia)

#=
Since now we model a system with heterogeneous node dynamics we can no longer
straightforwardly pass a single VertexFunction to the `Network` constructor but
straightforwardly pass a single VertexModel to the `Network` constructor but
instead have to hand over an Array.
=#

vertex_array = VertexFunction[vertex! for i in 1:N]
vertex_array = VertexModel[vertex! for i in 1:N]
vertex_array[1] = static!
vertex_array[5] = inertia! # index should correspond to the node's index in the graph
nw_hetero! = Network(g, vertex_array, edge!)
Expand Down
Loading

4 comments on commit cd31781

@hexaeder
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while trying to register: Changing package repo URL not allowed, please submit a pull request with the URL change to the target registry and retry.

@hexaeder
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/119801

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.9.1 -m "<description of version>" cd3178180b8ee529b5ec2aab31bdfc8894caee03
git push origin v0.9.1

Also, note the warning: Version 0.9.1 skips over 0.9.0
This can be safely ignored. However, if you want to fix this you can do so. Call register() again after making the fix. This will update the Pull request.

Please sign in to comment.