#PowerDynamics.jl Example This example shows the basic usage of the AmbientForcing and PowerDynamics.jl.
We begin with the Setup. First we include all necessary packages.
using AmbientForcing
using PowerDynamics, Distributions
Lets create a small test power grid with three nodes:
line_list = []
append!(line_list, [StaticLine(from=1, to=2, Y=-1im / 0.02 + 4)])
append!(line_list, [StaticLine(from=1, to=3, Y=-1im / 0.02 + 4)])
The PQAlgebraic Node is our constraint. The Power Output of node 3 is fixed.
node_list = []
append!(node_list, [SlackAlgebraic(U=1, Y_n=0)])
append!(node_list, [FourthOrderEq(H=3.318, P=-0.6337, D=0.1, Ω=50, E_f=0.5, T_d_dash=0.1, T_q_dash=8.690, X_d_dash=0.111, X_q_dash=0.103, X_d=0.1, X_q=0.6)])
append!(node_list, [PQAlgebraic(P=-0.6337, Q=0.0)])
We use the right hand side as our ODEFunction
pg = PowerGrid(node_list, line_list)
rpg = rhs(pg)
We access the constraint equations $g$ of the power grid:
g = constraint_equations(rpg)
The operation point is a fixed point and naturally lies on the manifold. Thus it can be used as the initial condition for AmbientForcing. We can easily find the operation point using PowerDynamics in-build function:
op = find_operationpoint(pg)
Lets check if $g(op) ≈ 0$, which means that the constraint is fulfilled.
isapprox(sum(g(op.vec)), 0.0, atol=1e-8)
Lets generate a random vector from the ambient space. First we want to perturb all variables in the grid:
Frand = random_force(rpg, [0.0, 1], Uniform)
afoprob = ambient_forcing_problem(rpg, op.vec, 2.0, Frand, method=:ForwardDiff)
z_new_all = ambient_forcing(afoprob, op.vec, 2.0, Frand) # Our new valid initial condition
isapprox(sum(g(z_new_all)), 0.0, atol=1e-8)
Next: let's perturb just the voltage at node 3! We begin by getting the index of the real and imaginary part of the voltage:
idx = idx_exclusive(rpg, ["u_r_3", "u_i_3"])
Then we generate a vector only with non-vanishing components the voltage at node 3:
Frand = random_force(rpg, [0.0, 1.0], Uniform, idx)
z_new_node_3 = ambient_forcing(afoprob, Frand)
isapprox(sum(g(z_new_node_3)), 0.0, atol=1e-8)
We see that the constraints are fulfilled.
It is also possible to perturb the variable using different distributions. Typically SNBS the angle θ and ω are perturbed differently:
idx = idx_exclusive(rpg, ["u_r_2", "u_i_2", "θ_2", "ω_2"])
τ = 2.0 # the integration time
When you want to sample the angle θ from a box of $[0, 2π]$. You have to make sure to dived the distribution argument by the integration time $τ$.
dist_vec = [[0, 1], [0, 2], [0, 2π] ./ τ, [-5, 5] ./ τ]
Frand = random_force(rpg, dist_vec, Uniform, idx)
z_new_node_2 = ambient_forcing(afoprob, op.vec, τ, Frand)
isapprox(sum(g(z_new_node_2)), 0.0, atol=1e-8)
Still we find a valid initial condition!