Nonlinear models

In the ElectricGrid.jl package, only linear components have been considered so far. Since in reality components are rarely linear, the implementation of models with non-linear characteristics will be discussed here. For this purpose, a comparison between linear cable model and a non-linear cable model shall be made.

  • Solver comparison

  • Nonlinear inductance

Comparison

using ElectricGrid
using PlotlyJS

In the first part, the comparison between the exact calculation and an ODE solver will be made. For the ODE solvers we use the implementations from the package DifferentialEquations.

In the following we will create a simple example.

CM = [0. 1.
     -1. 0.]

S_source = 1e4
S_load = 1e2
pf_load = 1
v_rms = 230
R_load, L_load, X, Z = ParallelLoadImpedance(S_load, pf_load, v_rms);

To clarify the difference, two environments are created by using the parameter dict, one linear and the other with the same value for the cable parameter L, only as a constant function.

parameters_linear = Dict{Any, Any}(
    "source" => Any[
                    Dict{Any, Any}("pwr" => S_source, "control_type" => "classic", "mode" => "Swing", "fltr" => "LC", "i_limit" => 1e4, "v_limit" => 1e4,),
                    ],
    "load"   => Any[
                    Dict{Any, Any}("impedance" => "R", "R" => R_load, "v_limit" => 1e4, "i_limit" => 1e4)
                    ],
    "cable"   => Any[
                    Dict{Any, Any}("R" => 1e-3, "L" => 1e-4, "C" => 1e-4),
                    ],
    "grid" => Dict{Any, Any}("fs"=>1e4, "phase"=>3, "v_rms"=>230, "f_grid" => 50, "ramp_end"=>0.00)
)

env_linear = ElectricGridEnv(CM = CM, parameters = parameters_linear, verbosity = 2);
[ Info: Normalization is done based on the defined parameter limits.
[ Info: Time simulation run time: 0.0499 [s] ~> 500 steps
parameters_const_func = Dict{Any, Any}(
    "source" => Any[
                    Dict{Any, Any}("pwr" => S_source, "control_type" => "classic", "mode" => "Swing", "fltr" => "LC", "i_limit" => 1e4, "v_limit" => 1e4,),
                    ],
    "load"   => Any[
                    Dict{Any, Any}("impedance" => "R", "R" => R_load, "v_limit" => 1e4, "i_limit" => 1e4)
                    ],
    "cable"   => Any[
                    Dict{Any, Any}("R" => 1e-3, "L" => x->1e-4, "C" => 1e-4),
                    ],
    "grid" => Dict{Any, Any}("fs"=>1e4, "phase"=>3, "v_rms"=>230, "f_grid" => 50, "ramp_end"=>0.00)
)

env_const_func = ElectricGridEnv(CM = CM, parameters = parameters_const_func, t_end = 0.04, verbosity = 2, nonlinear_solver = SSPRK33());
[ Info: Nonlinear Solver: Use SSPRK33 as solver.
[ Info: Normalization is done based on the defined parameter limits.
[ Info: Time simulation run time: 0.04 [s] ~> 401 steps

We'll take a closer look at the current through the cable, so we'll consider the fourth state.

idx = 4

name = GetStateIds(env_linear.nc)[idx]
"cable1_i_L_a"

On both environments we will now perform several steps and track the said state. For this purpose, the same constant action is given to all three phases on both.

result_linear = []
for i = 1:500
    env_linear([0.5, 0.5, 0.5])
    append!(result_linear, env_linear.x[idx])
end
result_const_func = []
for i = 1:500
    env_const_func([0.5, 0.5, 0.5])
    append!(result_const_func, env_const_func.x[idx])
end

The results can be viewed in the following plot.

t = collect(env_const_func.t0:env_const_func.ts:env_const_func.t)
p_const_func = scatter(x=t, y=result_const_func, mode="lines", name="constant function")

p_linear = scatter(x=t, y=result_linear, mode="lines", name="linear")

plot([p_const_func, p_linear])

The deviations are very small and suggest that the nonlinear solver for constant functions is identical to the exact solution.

Nonlinear inductance

Now we create a nonlinear function for the inductance, where the value should change with the current flowing through it. This is inspired by the following paper: Function origin. The function is plotted in the following.

Note: This non-linear function serves only as an example and should be replaced by the user with a function of his choice that covers the characteristics of a non-linear cable.

function NonlinearInductance(L_low, L_high, smoothness, value)
    return x -> L_low + (L_high - L_low) / 2 * (1 - 2/π * atan( smoothness * (abs(x-230)-value)))
end

l = NonlinearInductance(2e-5,1e-4,1,100)

x = collect(-400:0.1:400)
y = l.(x)

plot(x,y)

We now place the non-linear function in the parameter dict instead of the constant function and then use it to create another environment. We also excite this with a constant action and store the current via the inductance of the cable in a list.

parameters_nonlinear = parameters_const_func
parameters_nonlinear["cable"][1]["L"] = l

env_nonlinear = ElectricGridEnv(CM = CM, parameters = parameters_nonlinear, verbosity = 0, nonlinear_solver = SSPRK33());

result_nonlinear = []
for i = 1:500
    env_nonlinear([0.5, 0.5, 0.5])
    append!(result_nonlinear, env_nonlinear.x[idx])
end

The results can be seen in the following plot.

p_nonlinear = scatter(x=t, y=result_nonlinear, mode="lines", name="nonlinear")

plot([p_nonlinear,p_linear])

It can be seen that especially in the dynamic regions the trajectories deviate more from each other.