Skip to content

Commit

Permalink
Merge branch 'develop' into fuse
Browse files Browse the repository at this point in the history
  • Loading branch information
rbolgaryn authored Sep 14, 2023
2 parents 97c85c4 + 5592ba1 commit 6bc4cae
Show file tree
Hide file tree
Showing 41 changed files with 1,365 additions and 191 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Change Log
- [FIXED] fixed bug in :code:`cim2pp.build_pp_net` when controller for gen is at TopologicalNode instead of ConnectivityNode
- [CHANGED] adjust default iterations for runpp_3ph
- [CHANGED] always convert RATE_A to ppc in build_branch (not only when mode == 'opf' as before)
- [FIXED] in converter from PowerFactory, collect all buses (even not relevant for the calculation) for connectivity issues
- [FIXED] fixed bug in coords conversion in cim2pp, small fixes
- [CHANGED] cim2pp: added support for multi diagram usage for DL profiles


[2.13.1] - 2023-05-12
Expand Down
1 change: 1 addition & 0 deletions doc/elements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ information about the definition and interpretation of the parameters in the fol
elements/storage
elements/svc
elements/tcsc
elements/ssc
Binary file added doc/elements/ssc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 76 additions & 0 deletions doc/elements/ssc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.. _ssc:

==============================================
Static Synchronous Compensator (SSC)
==============================================

We implement the FACTS devices based on the following source:

A. Panosyan, "Modeling of advanced power transmission system controllers",
Ph.D. dissertation, Gottfried Wilhelm Leibniz Universität Hannover, 2010.


The Static Synchronous Compensator (SSC), also known as STATCOM, is a shunt connected Flexible AC Transmission System
(FACTS) controller. It connects an AC system to a Voltage Source Converter (VSC) through a coupling transformer.
The SSC is used for reactive shunt compensation. Since the VSC is not connected to a power source, there is no active
power exchange between the SSC and the AC system. Consequently, the SSC can only control the voltage magnitude of
the AC system.


.. seealso::
:ref:`Unit Systems and Conventions <conventions>`

We demonstrate the use-case of this device in the
pandapower tutorial: `FACTS <https://github.com/e2nIEE/pandapower/blob/develop/tutorials/FACTS.ipynb>`_.

Create Function
=====================

.. autofunction:: pandapower.create.create_ssc

Input Parameters
=====================

*net.ssc*

.. tabularcolumns:: |p{0.10\linewidth}|p{0.10\linewidth}|p{0.25\linewidth}|p{0.4\linewidth}|
.. csv-table::
:file: ssc_par.csv
:delim: ;
:widths: 10, 10, 25, 40

\*necessary for executing a power flow calculation.


Electric Model
=================


.. image:: ssc.png
:width: 12em
:alt: alternate Text
:align: center

The SSC is a VSC-based shunt-connected FACTS controller and can thus be modeled as a single-terminal active component.
The corresponding terminal-admittance equation is given as:

.. math::
:nowrap:
\begin{align*}
\underline{Y}_{T} (\underline{U}_{1} - \underline{U}_{VSC}) = \underline{I}_{1}
\end{align*}
Where :math:`\underline{Y}_{T}` = 1/:math:`\underline{Z}_{T}` is the admittance of the lossless-assumed coupling transformer between
the VSC and the ac system. :math:`\underline{U}_{VSC}` stands for the VSC output Voltage.


Result Parameters
==========================
*net.res_ssc*

.. tabularcolumns:: |p{0.10\linewidth}|p{0.10\linewidth}|p{0.40\linewidth}|
.. csv-table::
:file: ssc_res.csv
:delim: ;
:widths: 10, 10, 40
10 changes: 10 additions & 0 deletions doc/elements/ssc_par.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
**Parameter**;**Datatype**;**Value Range**;**Explanation**
name;string;;name of the SSC
bus*;integer;;index of bus where the SSC is connected
r_ohm*;float;:math:`\geq` 0;resistance of the coupling transformer component of SSC
x_ohm*;float;:math:`\leq` 0;reactance of the coupling transformer component of SSC
set_vm_pu*;float;;set-point for the bus voltage magnitude at the connection bus
vm_internal_pu*;float;;The voltage magnitude of the voltage source converter VSC at the SSC component.
va_internal_degree*;float;;The voltage angle of the voltage source converter VSC at the SSC component.
controllable*;boolean;True / False;whether the element is considered as actively controlling or as a fixed shunt impedance
in_service*;boolean;True / False;specifies if the SSC is in service.
6 changes: 6 additions & 0 deletions doc/elements/ssc_res.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
**Parameter**;**Datatype**;**Explanation**
q_mvar;float;shunt reactive power consumption of ssc [MVAr]
vm_internal_pu;float;voltage magnitude at ssc internal bus [pu]
va_internal_degree;float;voltage angle at ssc internal bus [degree]
vm_pu;float;voltage magnitude at ssc bus [pu]
va_degree;float;voltage angle at ssc bus [degree]
16 changes: 8 additions & 8 deletions doc/elements/svc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The thyristor firing angle regulates the total impedance of the element.
The active range of the device is presented in the figure below:

.. image:: svc_range.png
:width: 12em
:width: 26em
:alt: alternate Text
:align: center

Expand Down Expand Up @@ -52,14 +52,14 @@ Input Parameters
*net.svc*

.. tabularcolumns:: |p{0.10\linewidth}|p{0.10\linewidth}|p{0.25\linewidth}|p{0.4\linewidth}|
.. csv-table::
.. csv-table::
:file: svc_par.csv
:delim: ;
:widths: 10, 10, 25, 40

\*necessary for executing a power flow calculation.


Electric Model
=================

Expand All @@ -70,10 +70,10 @@ Electric Model
:align: center

The shunt impedance :math:`X_{SVC}` of the SVC element is calculated according to the following equation:

.. math::
:nowrap:
\begin{align*}
X_{SVC} &= \frac{\pi X_L}{2 (\pi - \alpha) + \sin{(2\alpha)} + \frac{\pi X_L}{X_{Cvar}}}
\end{align*}
Expand All @@ -86,11 +86,11 @@ The reactive power consumption of the SVC element is calculated with:

.. math::
:nowrap:
\begin{align*}
Q_{SVC} = \frac{V^2}{X_{SVC}}
\end{align*}
Where V is the complex voltage observed at the connection bus of the SVC element.
The reference values for the per unit system as defined in :ref:`Unit Systems and Conventions<conventions>`.

Expand All @@ -99,7 +99,7 @@ Result Parameters
*net.res_svc*

.. tabularcolumns:: |p{0.10\linewidth}|p{0.10\linewidth}|p{0.40\linewidth}|
.. csv-table::
.. csv-table::
:file: svc_res.csv
:delim: ;
:widths: 10, 10, 40
2 changes: 1 addition & 1 deletion doc/elements/tcsc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ Result Parameters

.. tabularcolumns:: |p{0.10\linewidth}|p{0.10\linewidth}|p{0.40\linewidth}|
.. csv-table::
:file: svc_res.csv
:file: tcsc_res.csv
:delim: ;
:widths: 10, 10, 40
24 changes: 18 additions & 6 deletions pandapower/auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from pandapower.pypower.idx_brch import F_BUS, T_BUS, BR_STATUS
from pandapower.pypower.idx_bus import BUS_I, BUS_TYPE, NONE, PD, QD, VM, VA, REF, VMIN, VMAX, PV
from pandapower.pypower.idx_gen import PMIN, PMAX, QMIN, QMAX
from pandapower.pypower.idx_ssc import SSC_STATUS, SSC_BUS, SSC_INTERNAL_BUS
from pandapower.pypower.idx_tcsc import TCSC_STATUS, TCSC_F_BUS, TCSC_T_BUS

try:
Expand Down Expand Up @@ -666,13 +667,17 @@ def _check_connectivity(ppc):
notcsc = ppc["tcsc"][tcsc_status, :].shape[0]
bus_from_tcsc = ppc["tcsc"][tcsc_status, TCSC_F_BUS].real.astype(np.int64)
bus_to_tcsc = ppc["tcsc"][tcsc_status, TCSC_T_BUS].real.astype(np.int64)
ssc_status = ppc["ssc"][:, SSC_STATUS].real.astype(bool)
nossc = ppc["ssc"][ssc_status, :].shape[0]
bus_from_ssc = ppc["ssc"][ssc_status, SSC_BUS].real.astype(np.int64)
bus_to_ssc = ppc["ssc"][ssc_status, SSC_INTERNAL_BUS].real.astype(np.int64)

# we create a "virtual" bus thats connected to all slack nodes and start the connectivity
# search at this bus
bus_from = np.hstack([bus_from, bus_from_tcsc, slacks])
bus_to = np.hstack([bus_to, bus_to_tcsc, np.ones(len(slacks)) * nobus])
bus_from = np.hstack([bus_from, bus_from_tcsc, bus_from_ssc, slacks])
bus_to = np.hstack([bus_to, bus_to_tcsc, bus_to_ssc, np.ones(len(slacks)) * nobus])

adj_matrix = sp.sparse.coo_matrix((np.ones(nobranch + notcsc + len(slacks)),
adj_matrix = sp.sparse.coo_matrix((np.ones(nobranch + notcsc + nossc + len(slacks)),
(bus_from, bus_to)),
shape=(nobus + 1, nobus + 1))

Expand Down Expand Up @@ -754,7 +759,7 @@ def _select_is_elements_numba(net, isolated_nodes=None, sequence=None):
set_isolated_buses_oos(bus_in_service, ppc_bus_isolated, net["_pd2ppc_lookups"]["bus"])
# mode = net["_options"]["mode"]
elements = ["load", "motor", "sgen", "asymmetric_load", "asymmetric_sgen", "gen",
"ward", "xward", "shunt", "ext_grid", "storage", "svc"] # ,"impedance_load"
"ward", "xward", "shunt", "ext_grid", "storage", "svc", "ssc"] # ,"impedance_load"
is_elements = dict()
for element in elements:
len_ = len(net[element].index)
Expand Down Expand Up @@ -1030,6 +1035,11 @@ def _check_lightsim2grid_compatibility(net, lightsim2grid, voltage_depend_loads,
return False
raise NotImplementedError("option 'lightsim2grid' is True and SVC controllable shunt elements are present, "
"SVC controllable shunt elements not implemented.")
if len(net.ssc):
if lightsim2grid == "auto":
return False
raise NotImplementedError("option 'lightsim2grid' is True and SSC controllable shunt elements are present, "
"SVC controllable shunt elements not implemented.")

return True

Expand Down Expand Up @@ -1342,8 +1352,9 @@ def _init_runpp_options(net, algorithm, calculate_voltage_angles, init,

default_max_iteration = {"nr": 10, "iwamoto_nr": 10, "bfsw": 100, "gs": 10000, "fdxb": 30,
"fdbx": 30}
with_ssc = len(net.ssc.query("in_service & controllable")) > 0
with_facts = len(net.svc.query("in_service & controllable")) > 0 or \
len(net.tcsc.query("in_service & controllable")) > 0
len(net.tcsc.query("in_service & controllable")) > 0 or with_ssc
if max_iteration == "auto":
# tdpf is an option rather than algorithm; svc need more iterations to converge
max_iteration = 30 if tdpf or with_facts else default_max_iteration[algorithm]
Expand All @@ -1360,9 +1371,10 @@ def _init_runpp_options(net, algorithm, calculate_voltage_angles, init,
init_vm_pu = None
init_va_degree = None

# SSC devices can lead to the grid having isolated buses from the point of view of DC power flow, so choose 'flat'
if init == "auto":
if init_va_degree is None or (isinstance(init_va_degree, str) and init_va_degree == "auto"):
init_va_degree = "dc" if calculate_voltage_angles else "flat"
init_va_degree = "dc" if calculate_voltage_angles and not with_ssc else "flat"
if init_vm_pu is None or (isinstance(init_vm_pu, str) and init_vm_pu == "auto"):
init_vm_pu = (net.ext_grid.vm_pu.values.sum() + net.gen.vm_pu.values.sum()) / \
(len(net.ext_grid.vm_pu.values) + len(net.gen.vm_pu.values))
Expand Down
56 changes: 52 additions & 4 deletions pandapower/build_bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

from pandapower.auxiliary import _sum_by_group, phase_to_sequence, version_check
from pandapower.pypower.idx_bus import BUS_I, BASE_KV, PD, QD, GS, BS, VMAX, VMIN, BUS_TYPE, NONE, \
VM, VA, CID, CZD, bus_cols, REF
VM, VA, CID, CZD, bus_cols, REF, PV
from pandapower.pypower.idx_bus_sc import C_MAX, C_MIN, bus_cols_sc
from .pypower.idx_ssc import ssc_cols, SSC_BUS, SSC_R, SSC_X, SSC_SET_VM_PU, SSC_X_CONTROL_VA, SSC_X_CONTROL_VM, \
SSC_STATUS, SSC_CONTROLLABLE, SSC_INTERNAL_BUS
from .pypower.idx_svc import svc_cols, SVC_BUS, SVC_SET_VM_PU, SVC_MIN_FIRING_ANGLE, SVC_MAX_FIRING_ANGLE, SVC_STATUS, \
SVC_CONTROLLABLE, SVC_X_L, SVC_X_CVAR, SVC_THYRISTOR_FIRING_ANGLE

Expand Down Expand Up @@ -261,8 +263,9 @@ def _build_bus_ppc(net, ppc, sequence=None):
# get bus indices
nr_xward = len(net.xward)
nr_trafo3w = len(net.trafo3w)
nr_ssc = len(net.ssc)
aux = dict()
if nr_xward > 0 or nr_trafo3w > 0:
if nr_xward > 0 or nr_trafo3w > 0 or nr_ssc > 0:
bus_indices = [net["bus"].index.values, np.array([], dtype=np.int64)]
max_idx = max(net["bus"].index) + 1
if nr_xward > 0:
Expand All @@ -273,6 +276,10 @@ def _build_bus_ppc(net, ppc, sequence=None):
aux_trafo3w = np.arange(max_idx + nr_xward, max_idx + nr_xward + nr_trafo3w)
aux["trafo3w"] = aux_trafo3w
bus_indices.append(aux_trafo3w)
if nr_ssc:
aux_ssc = np.arange(max_idx + nr_xward + nr_trafo3w, max_idx + nr_xward + nr_trafo3w + nr_ssc)
aux["ssc"] = aux_ssc
bus_indices.append(aux_ssc)
bus_index = np.concatenate(bus_indices)
else:
bus_index = net["bus"].index.values
Expand Down Expand Up @@ -309,10 +316,11 @@ def _build_bus_ppc(net, ppc, sequence=None):
# init voltages from net
ppc["bus"][:n_bus, BASE_KV] = net["bus"]["vn_kv"].values
# set buses out of service (BUS_TYPE == 4)
if nr_xward > 0 or nr_trafo3w > 0:
if nr_xward > 0 or nr_trafo3w > 0 or nr_ssc > 0:
in_service = np.concatenate([net["bus"]["in_service"].values,
net["xward"]["in_service"].values,
net["trafo3w"]["in_service"].values])
net["trafo3w"]["in_service"].values,
net["ssc"]["in_service"].values])
else:
in_service = net["bus"]["in_service"].values
ppc["bus"][~in_service, BUS_TYPE] = NONE
Expand Down Expand Up @@ -344,6 +352,10 @@ def _build_bus_ppc(net, ppc, sequence=None):

if len(net.trafo3w):
_fill_auxiliary_buses(net, ppc, bus_lookup, "trafo3w", "hv_bus", aux)

if len(net.ssc):
_fill_auxiliary_buses(net, ppc, bus_lookup, "ssc", "bus", aux)

net["_pd2ppc_lookups"]["bus"] = bus_lookup
net["_pd2ppc_lookups"]["aux"] = aux

Expand Down Expand Up @@ -552,6 +564,42 @@ def _build_svc_ppc(net, ppc, mode):
"in_service"].values.astype(bool)


def _build_ssc_ppc(net, ppc, mode):
length = len(net.ssc)
ppc["ssc"] = np.zeros(shape=(length, ssc_cols), dtype=np.float64)

if mode != "pf":
return

if length > 0:
baseMVA = ppc["baseMVA"]
bus_lookup = net["_pd2ppc_lookups"]["bus"]
aux = net["_pd2ppc_lookups"]["aux"]
f = 0
t = length

bus = bus_lookup[net.ssc["bus"].values]
controllable = net["ssc"]["controllable"].values.astype(bool)

ssc = ppc["ssc"]
baseV = ppc["bus"][bus, BASE_KV]
baseZ = baseV ** 2 / baseMVA

ssc[f:t, SSC_BUS] = bus
ssc[f:t, SSC_INTERNAL_BUS] = bus_lookup[aux["ssc"]]

ssc[f:t, SSC_R] = net["ssc"]["r_ohm"].values / baseZ
ssc[f:t, SSC_X] = net["ssc"]["x_ohm"].values / baseZ
ssc[f:t, SSC_SET_VM_PU] = net["ssc"]["set_vm_pu"].values
ssc[f:t, SSC_X_CONTROL_VA] = np.deg2rad(net["ssc"]["va_internal_degree"].values)
ssc[f:t, SSC_X_CONTROL_VM] = net["ssc"]["vm_internal_pu"].values

ssc[f:t, SSC_STATUS] = net["ssc"]["in_service"].values
ssc[f:t, SSC_CONTROLLABLE] = controllable & net["ssc"]["in_service"].values.astype(bool)
ppc["bus"][aux["ssc"][~controllable], BUS_TYPE] = PV
ppc["bus"][aux["ssc"][~controllable], VM] = net["ssc"].loc[~controllable, "vm_internal_pu"].values


# Short circuit relevant routines
def _add_ext_grid_sc_impedance(net, ppc):
mode = net._options["mode"]
Expand Down
Loading

0 comments on commit 6bc4cae

Please sign in to comment.