Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/integrate helm solver #2067

Draft
wants to merge 12 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions pandapower/auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
lightsim2grid_available = True
except ImportError:
lightsim2grid_available = False

try:
from helmpy.core import helm
helmpy_available = True

Check warning on line 61 in pandapower/auxiliary.py

View check run for this annotation

Codecov / codecov/patch

pandapower/auxiliary.py#L61

Added line #L61 was not covered by tests
except ImportError:
helmpy_available = False

try:
import pandaplan.core.pplog as logging
except ImportError:
Expand Down Expand Up @@ -1341,7 +1348,7 @@
calculate_voltage_angles = True

default_max_iteration = {"nr": 10, "iwamoto_nr": 10, "bfsw": 100, "gs": 10000, "fdxb": 30,
"fdbx": 30}
"fdbx": 30, "helm": 40}
with_facts = len(net.svc.query("in_service & controllable")) > 0 or \
len(net.tcsc.query("in_service & controllable")) > 0
if max_iteration == "auto":
Expand Down Expand Up @@ -1381,9 +1388,9 @@
logger.warning("Currently distributed_slack is implemented for 'ext_grid', 'gen' "
"and 'xward' only, not for '" + "', '".join(
false_slack_weight_elms) + "'.")
if algorithm != 'nr':
if algorithm != 'nr' and algorithm != 'helm':
raise NotImplementedError(
'Distributed slack is only implemented for Newton Raphson algorithm.')
'Distributed slack is only implemented for Newton Raphson algorithm and HELM.')

if tdpf:
if algorithm != 'nr':
Expand Down
141 changes: 141 additions & 0 deletions pandapower/pf/run_helmpy_pf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
from time import perf_counter
import numpy as np
from pandapower.pypower.idx_bus import PD, QD, BUS_TYPE, PQ, REF, GS, BS, BUS_I, VM, VA
from pandapower.pypower.idx_gen import PG, QG, QMAX, QMIN, GEN_BUS, GEN_STATUS, VG, MBASE
from pandapower.pypower.bustypes import bustypes
from pandapower.pypower.makeSbus import makeSbus
from pandapower.pf.ppci_variables import _get_pf_variables_from_ppci, _store_results_from_pf_in_ppci
import pandas as pd

try:
import pandaplan.core.pplog as logging
except ImportError:
import logging

logger = logging.getLogger(__name__)


def convert_complex_to_polar_voltages(complex_voltage):
"""Separate each voltage value in magnitude and phase angle (degrees)"""
polar_voltage = np.zeros((len(complex_voltage), 2), dtype=float)
polar_voltage[:, 0] = np.absolute(complex_voltage)
polar_voltage[:, 1] = np.angle(complex_voltage, deg=True)
return polar_voltage

Check warning on line 23 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L20-L23

Added lines #L20 - L23 were not covered by tests


def _runpf_helmpy_pf(ppci, options: dict, **kwargs):
"""
Runs a HELM based power flow.

INPUT
ppci (dict) - the "internal" ppc (without out ot service elements and sorted elements)
options(dict) - options for the power flow

"""

from helmpy import helm
from helmpy.core.classes import CaseData, process_branches

Check warning on line 37 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L36-L37

Added lines #L36 - L37 were not covered by tests

t0 = perf_counter()

Check warning on line 39 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L39

Added line #L39 was not covered by tests
# we cannot run DC pf before running newton with distributed slack because the slacks come pre-solved after the DC pf
# if isinstance(options["init_va_degree"], str) and options["init_va_degree"] == "dc":
# if options['distributed_slack']:
# pg_copy = ppci['gen'][:, PG].copy()
# pd_copy = ppci['bus'][:, PD].copy()
# ppci = _run_dc_pf(ppci, options["recycle"])
# ppci['gen'][:, PG] = pg_copy
# ppci['bus'][:, PD] = pd_copy
# else:
# ppci = _run_dc_pf(ppci, options["recycle"])

max_coefficients = options['max_iteration']

Check warning on line 51 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L51

Added line #L51 was not covered by tests

enforce_Q_limits = False
if options["enforce_q_lims"]:
enforce_Q_limits = True

Check warning on line 55 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L53-L55

Added lines #L53 - L55 were not covered by tests
# else:
# ppci, success, iterations = _run_ac_pf_without_qlims_enforced(ppci, options)
# # update data matrices with solution store in ppci
# bus, gen, branch = ppci_to_pfsoln(ppci, options)

DSB_model = False
if options['distributed_slack']:
DSB_model = True

Check warning on line 63 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L61-L63

Added lines #L61 - L63 were not covered by tests

# -------------------------------------------- Calculation Case preparation --------------------------------------------
buses = ppci['bus'].copy()
generators = ppci['gen'].copy()
branches = ppci['branch'].copy()

Check warning on line 68 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L66-L68

Added lines #L66 - L68 were not covered by tests

generators[:, MBASE] = 100.

Check warning on line 70 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L70

Added line #L70 was not covered by tests

N = len(buses)
N_generators = len(generators)
N_branches = len(branches)
case = CaseData(name='pandapower', N=N, N_generators=N_generators)

Check warning on line 75 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L72-L75

Added lines #L72 - L75 were not covered by tests

case.N_branches = N_branches
case.Pd[:] = buses[:, PD] / 100
case.Qd[:] = buses[:, QD] / 100

Check warning on line 79 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L77-L79

Added lines #L77 - L79 were not covered by tests

# weird but has to be done in this order. Otherwise, the values are wrong...
case.Shunt[:] = (buses[:, BS].copy()*1j + buses[:, GS])
case.Yshunt[:] = np.copy(case.Shunt)
case.Shunt[:] = case.Shunt[:] / 100

Check warning on line 84 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L82-L84

Added lines #L82 - L84 were not covered by tests

for i in range(N):
case.Number_bus[buses[i][BUS_I]] = i
if buses[i][BUS_TYPE] == 3:
case.slack_bus = buses[i][BUS_I]
case.slack = i

Check warning on line 90 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L86-L90

Added lines #L86 - L90 were not covered by tests

pos = 0
for i in range(N_generators):
bus_i = case.Number_bus[generators[i][GEN_BUS]]
if bus_i != case.slack:
case.list_gen[pos] = bus_i
pos += 1
case.Buses_type[bus_i] = 'PVLIM'
case.V[bus_i] = generators[i][VG]
case.Pg[bus_i] = generators[i][PG]/100
case.Qgmax[bus_i] = generators[i][QMAX]/100
case.Qgmin[bus_i] = generators[i][QMIN]/100

Check warning on line 102 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L92-L102

Added lines #L92 - L102 were not covered by tests

case.Buses_type[case.slack] = 'Slack'
case.Pg[case.slack] = 0

Check warning on line 105 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L104-L105

Added lines #L104 - L105 were not covered by tests

branches_df = pd.DataFrame(branches)

Check warning on line 107 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L107

Added line #L107 was not covered by tests

process_branches(branches_df, N_branches, case)

Check warning on line 109 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L109

Added line #L109 was not covered by tests

for i in range(N):
case.branches_buses[i].sort() # Variable that saves the branches

Check warning on line 112 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L111-L112

Added lines #L111 - L112 were not covered by tests

case.Y[:] = np.copy(case.Ytrans)
for i in range(N):
if case.Yshunt[i].real != 0:
case.conduc_buses[i] = True
case.Y[i, i] += case.Yshunt[i]
if case.phase_barras[i]:
for k in range(len(case.phase_dict[i][0])):
case.Y[i, case.phase_dict[i][0][k]] += case.phase_dict[i][1][k]

Check warning on line 121 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L114-L121

Added lines #L114 - L121 were not covered by tests

run, series_large, flag_divergence = helm(case, detailed_run_print=False, mismatch=1e-8, scale=1,

Check warning on line 123 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L123

Added line #L123 was not covered by tests
max_coefficients=max_coefficients, enforce_Q_limits=enforce_Q_limits,
results_file_name=None, save_results=False, pv_bus_model=1,
DSB_model=DSB_model, DSB_model_method=None,)

et = perf_counter() - t0
complex_voltage = run.V_complex_profile.copy()
polar_voltage = convert_complex_to_polar_voltages(complex_voltage) # Calculate polar voltage

Check warning on line 130 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L128-L130

Added lines #L128 - L130 were not covered by tests

buses[:, VM] = polar_voltage[:, 0]
buses[:, VA] = polar_voltage[:, 1]

Check warning on line 133 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L132-L133

Added lines #L132 - L133 were not covered by tests

ppci["bus"], ppci["gen"], ppci["branch"] = buses, generators, branches
ppci["success"] = not flag_divergence
ppci["internal"]["V"] = complex_voltage
ppci["iterations"] = series_large
ppci["et"] = et

Check warning on line 139 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L135-L139

Added lines #L135 - L139 were not covered by tests

return ppci

Check warning on line 141 in pandapower/pf/run_helmpy_pf.py

View check run for this annotation

Codecov / codecov/patch

pandapower/pf/run_helmpy_pf.py#L141

Added line #L141 was not covered by tests
3 changes: 3 additions & 0 deletions pandapower/powerflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from pandapower.pf.run_dc_pf import _run_dc_pf
from pandapower.pf.run_newton_raphson_pf import _run_newton_raphson_pf
from pandapower.pf.runpf_pypower import _runpf_pypower
from pandapower.pf.run_helmpy_pf import _runpf_helmpy_pf
from pandapower.pypower.bustypes import bustypes
from pandapower.pypower.idx_bus import VM
from pandapower.pypower.makeYbus import makeYbus as makeYbus_pypower
Expand Down Expand Up @@ -166,6 +167,8 @@
result = _run_newton_raphson_pf(ppci, options)
elif algorithm in ['fdbx', 'fdxb', 'gs']: # algorithms existing within pypower
result = _runpf_pypower(ppci, options, **kwargs)[0]
elif algorithm == 'helm':
result = _runpf_helmpy_pf(ppci, options, **kwargs)

Check warning on line 171 in pandapower/powerflow.py

View check run for this annotation

Codecov / codecov/patch

pandapower/powerflow.py#L170-L171

Added lines #L170 - L171 were not covered by tests
else:
raise AlgorithmUnknown("Algorithm {0} is unknown!".format(algorithm))
else:
Expand Down
1 change: 1 addition & 0 deletions pandapower/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def runpp(net, algorithm='nr', calculate_voltage_angles="auto", init="auto",
- "gs" gauss-seidel (pypower implementation)
- "fdbx" fast-decoupled (pypower implementation)
- "fdxb" fast-decoupled (pypower implementation)
- "helm" holomorphic embedded loadflow method

**calculate_voltage_angles** (str or bool, "auto") - consider voltage angles in loadflow calculation

Expand Down
36 changes: 35 additions & 1 deletion pandapower/test/loadflow/test_runpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import pandapower as pp
import pandapower.control
from pandapower.auxiliary import _check_connectivity, _add_ppc_options, lightsim2grid_available
from pandapower.auxiliary import _check_connectivity, _add_ppc_options, lightsim2grid_available, helmpy_available
from pandapower.networks import create_cigre_network_mv, four_loads_with_branches_out, \
example_simple, simple_four_bus_system, example_multivoltage
from pandapower.pd2ppc import _pd2ppc
Expand Down Expand Up @@ -510,6 +510,40 @@ def test_bsfw_algorithm():
assert np.allclose(va_nr, va_alg)


@pytest.mark.skipif(not helmpy_available, reason="HELMpy is not installed")
def test_helm_algorithm_simple():
import pandapower.networks as nw
net = nw.case9()

pp.runpp(net)
vm_nr = copy.copy(net.res_bus.vm_pu)
va_nr = copy.copy(net.res_bus.va_degree)

pp.runpp(net, algorithm='helm')
vm_alg = net.res_bus.vm_pu
va_alg = net.res_bus.va_degree

assert np.allclose(vm_nr, vm_alg)
assert np.allclose(va_nr, va_alg)


@pytest.mark.skipif(not helmpy_available, reason="HELMpy is not installed")
def test_helm_algorithm_complex():
import pandapower.networks as nw
net = nw.case9()

pp.runpp(net)
vm_nr = copy.copy(net.res_bus.vm_pu)
va_nr = copy.copy(net.res_bus.va_degree)

pp.runpp(net, algorithm='helm')
vm_alg = net.res_bus.vm_pu
va_alg = net.res_bus.va_degree

assert np.allclose(vm_nr, vm_alg)
assert np.allclose(va_nr, va_alg)


@pytest.mark.xfail(reason="unknown")
def test_bsfw_algorithm_multi_net():
net = example_simple()
Expand Down