Tutorial Active Constant Power Load model

Originally Contributed by: Rodrigo Henriquez-Auba

Introduction

This tutorial will introduce you to the functionality of PowerSimulationsDynamics and PowerSystems to explore active load components and a small-signal analysis.

This tutorial presents a simulation of a two-bus system with a GFM inverter at bus 1, and a load on bus 2. We will change the model from a constant power load model, to a constant impedance model and then to a 12-state active constant power load model.

Dependencies

julia> using PowerSimulationsDynamics;
julia> PSID = PowerSimulationsDynamicsPowerSimulationsDynamics
julia> using PowerSystemCaseBuilder
julia> using PowerSystems
julia> const PSY = PowerSystems;
Note

PowerSystemCaseBuilder.jl is a helper library that makes it easier to reproduce examples in the documentation and tutorials. Normally you would pass your local files to create the system data instead of calling the function build_system. For more details visit PowerSystemCaseBuilder Documentation

PowerSystems (abbreviated with PSY) is used to properly define the data structure and establish an equilibrium point initial condition with a power flow routine using PowerFlows.

Load the system

We load the system using PowerSystemCaseBuilder.jl. This system has an inverter located at bus 1.

julia> sys = build_system(PSIDSystems, "2 Bus Load Tutorial Droop")ERROR: UndefVarError: `build_system` not defined
julia> first(get_components(DynamicInverter, sys))ERROR: UndefVarError: `sys` not defined

The load is an exponential load modeled as a constant power load since the coefficients are set to zero.

julia> first(get_components(PSY.ExponentialLoad, sys))ERROR: UndefVarError: `sys` not defined

Run a small-signal analysis

We set up the Simulation. Since the droop model does not have a frequency state, we use a constant frequency reference frame for the network.

julia> sim = Simulation(ResidualModel,
                       sys,
                       mktempdir(),
                       (0.0, 1.0),
                       frequency_reference = ConstantFrequency())ERROR: UndefVarError: `sys` not defined

The following provides a summary of eigenvalues for this droop system with a constant power load:

julia> sm = small_signal_analysis(sim);ERROR: UndefVarError: `sim` not defined
julia> df = summary_eigenvalues(sm);ERROR: UndefVarError: `sm` not defined
julia> show(df, allrows = true, allcols = true)ERROR: UndefVarError: `df` not defined

In this inverter model, the filter is modeled using differential equations, and as described in the literature, interfacing a RL filter against an algebraic constant power load usually results in unstable behavior as observed with the positive real part eigenvalue.

Change to a constant impedance load model

Since the load is an exponential load model we can change the exponent coefficients to 2.0 to behave as a constant impedance model:

julia> # Update load coefficients to 2.0
       load = first(get_components(PSY.ExponentialLoad, sys));ERROR: UndefVarError: `sys` not defined
julia> PSY.set_active_power_coefficient!(load, 2.0);ERROR: UndefVarError: `load` not defined
julia> PSY.set_reactive_power_coefficient!(load, 2.0);ERROR: UndefVarError: `load` not defined

We then re-run the small-signal analysis:

julia> sim = Simulation(ResidualModel,
                       sys,
                       mktempdir(),
                       (0.0, 1.0),
                       frequency_reference = ConstantFrequency())ERROR: UndefVarError: `sys` not defined
julia> sm = small_signal_analysis(sim);ERROR: UndefVarError: `sim` not defined
julia> df = summary_eigenvalues(sm);ERROR: UndefVarError: `sm` not defined
julia> show(df, allrows = true, allcols = true)ERROR: UndefVarError: `df` not defined

Observe that now the system is small-signal stable (since there is only one device the angle of the inverter is used as a reference, and hence is zero).

Adding a dynamic active load model

To consider a dynamic model in the load it is only required to attach a dynamic component to the static load model. When a dynamic load model is attached, the active and reactive power of the static model are used to define reference parameters to ensure that the dynamic load model matches the static load output power.

Note that when a dynamic model is attached to a static model, the static model does not participate in the dynamic system equations, i.e. the only model interfacing to the network equations is the dynamic model and not the static model (the exponential load).

We define a function to create a active load model with the specific parameters:

julia> # Parameters taken from active load model from N. Bottrell Masters
       # Thesis "Small-Signal Analysis of Active Loads and Large-signal Analysis
       # of Faults in Inverter Interfaced Microgrid Applications", 2014.
       
       # The parameters are then per-unitized to be scalable to represent an aggregation
       # of multiple active loads
       
       # Base AC Voltage: Vb = 380 V
       # Base Power (AC and DC): Pb = 10000 VA
       # Base AC Current: Ib = 10000 / 380 = 26.32 A
       # Base AC Impedance: Zb = 380 / 26.32 =  14.44 Ω
       # Base AC Inductance: Lb = Zb / Ωb = 14.44 / 377 = 0.3831 H
       # Base AC Capacitance: Cb = 1 / (Zb * Ωb) = 0.000183697 F
       # Base DC Voltage: Vb_dc = (√8/√3) Vb = 620.54 V
       # Base DC Current: Ib_dc = Pb / V_dc = 10000/620.54 = 16.12 A
       # Base DC Impedance: Zb_dc = Vb_dc / Ib_dc = 38.50 Ω
       # Base DC Capacitance: Cb_dc = 1 / (Zb_dc * Ωb) = 6.8886315e-5 F
       
       Ωb = 2*pi*60;
julia> Vb = 380;
julia> Pb = 10000;
julia> Ib = Pb / Vb;
julia> Zb = Vb / Ib;
julia> Lb = Zb / Ωb;
julia> Cb = 1 / (Zb * Ωb);
julia> Vb_dc = sqrt(8)/sqrt(3) * Vb;
julia> Ib_dc = Pb / Vb_dc;
julia> Zb_dc = Vb_dc / Ib_dc;
julia> Cb_dc = 1/(Zb_dc * Ωb);
julia> function active_cpl(load) return PSY.ActiveConstantPowerLoad( name = get_name(load), r_load = 70.0 / Zb_dc, c_dc = 2040e-6 / Cb_dc, rf = 0.1 / Zb, lf = 2.3e-3 / Lb, cf = 8.8e-6 / Cb, rg = 0.03 / Zb, lg = 0.93e-3 / Lb, kp_pll = 0.4, ki_pll = 4.69, kpv = 0.5 * (Vb_dc / Ib_dc), kiv = 150.0 * (Vb_dc / Ib_dc), kpc = 15.0 * (Ib / Vb), kic = 30000.0 * (Ib / Vb), base_power = 100.0, ) endactive_cpl (generic function with 1 method)

We then attach the model to the system:

julia> load = first(get_components(PSY.ExponentialLoad, sys));ERROR: UndefVarError: `sys` not defined
julia> dyn_load = active_cpl(load)ERROR: UndefVarError: `load` not defined
julia> add_component!(sys, dyn_load, load)ERROR: UndefVarError: `sys` not defined

Finally, we set up the simulation:

julia> sim = Simulation(ResidualModel,
                       sys,
                       mktempdir(),
                       (0.0, 1.0),
                       frequency_reference = ConstantFrequency())ERROR: UndefVarError: `sys` not defined
julia> sm = small_signal_analysis(sim);ERROR: UndefVarError: `sim` not defined
julia> df = summary_eigenvalues(sm);ERROR: UndefVarError: `sm` not defined
julia> show(df, allrows = true, allcols = true)ERROR: UndefVarError: `df` not defined

Observe the new states of the active load model and that the system is small-signal stable.