Skip to content

Commit

Permalink
Merge pull request e2nIEE#254 from SteffenMeinecke/develop
Browse files Browse the repository at this point in the history
enable calculating pq_from_cosphi() and cosphi_from_pq() with arrays.
  • Loading branch information
SteffenMeinecke authored Jan 2, 2019
2 parents 8afb71d + 0d48fc1 commit 75c0f5f
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 32 deletions.
61 changes: 59 additions & 2 deletions pandapower/test/api/test_toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import copy
import numpy as np
import pandas as pd
import pytest

import pandapower as pp
Expand Down Expand Up @@ -245,7 +246,7 @@ def test_overloaded_lines():
line3 = pp.create_line(net, bus0, bus1, length_km=10, std_type="149-AL1/24-ST1A 10.0")

pp.create_load(net, bus1, p_kw=200, q_kvar=50)

pp.runpp(net)
# test the overloaded lines by default value of max_load=100
overloaded_lines = tb.overloaded_lines(net, max_load=100)
Expand Down Expand Up @@ -335,7 +336,63 @@ def test_close_switch_at_line_with_two_open_switches():
tb.close_switch_at_line_with_two_open_switches(net)

# assertion: sw2 closed
assert net.switch.closed.loc[1] == True
assert net.switch.closed.loc[1]


def test_pq_from_cosphi():
p, q = pp.pq_from_cosphi(1/0.95, 0.95, "ind", "load")
assert np.isclose(p, 1)
assert np.isclose(q, 0.3286841051788632)

s = np.array([1, 1, 1])
cosphi = np.array([1, 0.5, 0])
pmode = np.array(["load", "load", "load"])
qmode = np.array(["ind", "ind", "ind"])
p, q = pp.pq_from_cosphi(s, cosphi, qmode, pmode)
excpected_values = (np.array([1, 0.5, 0]), np.array([0, 0.8660254037844386, 1]))
assert np.allclose(p, excpected_values[0])
assert np.allclose(q, excpected_values[1])

pmode = "gen"
p, q = pp.pq_from_cosphi(s, cosphi, qmode, pmode)
assert np.allclose(p, -excpected_values[0])
assert np.allclose(q, excpected_values[1])

qmode = "cap"
p, q = pp.pq_from_cosphi(s, cosphi, qmode, pmode)
assert np.allclose(p, -excpected_values[0])
assert np.allclose(q, -excpected_values[1])

try:
pp.pq_from_cosphi(1, 0.95, "ohm", "gen")
bool_ = False
except ValueError:
bool_ = True
assert bool_

p, q = pp.pq_from_cosphi(0, 0.8, "cap", "gen")
assert np.isclose(p, 0)
assert np.isclose(q, 0)


def test_cosphi_from_pq():
cosphi, s, qmode, pmode = pp.cosphi_from_pq(1, 0.4)
assert np.isclose(cosphi, 0.9284766908852593)
assert np.isclose(s, 1.077032961426901)
assert qmode == 'ind'
assert pmode == 'load'

p = np.array([1, 1, 1, 1, 1, 0, 0, 0, -1, -1, -1])
q = np.array([1, -1, 0, 0.5, -0.5, 1, -1, 0, 1, -1, 0])
cosphi, s, qmode, pmode = pp.cosphi_from_pq(p, q)
assert np.allclose(cosphi[[0, 1, 8, 9]], 2**0.5/2)
assert np.allclose(cosphi[[3, 4]], 0.89442719)
assert np.allclose(cosphi[[2, 10]], 1)
assert pd.Series(cosphi[[5, 6, 7]]).isnull().all()
assert np.allclose(s, (p ** 2 + q ** 2) ** 0.5)
assert all(pmode == np.array(["load"]*5+["undef"]*3+["gen"]*3))
ind_cap_ohm = ["ind", "cap", "ohm"]
assert all(qmode == np.array(ind_cap_ohm+["ind", "cap"]+ind_cap_ohm*2))


def test_create_replacement_switch_for_branch():
Expand Down
91 changes: 61 additions & 30 deletions pandapower/toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,10 +429,13 @@ def dataframes_equal(x_df, y_df, tol=1.e-14, ignore_index_order=True):
y_df.sort_index(axis=0, inplace=True)
# eval if two DataFrames are equal, with regard to a tolerance
if x_df.shape == y_df.shape:
# we use numpy.allclose to grant a tolerance on numerical values
numerical_equal = np.allclose(x_df.select_dtypes(include=[np.number]),
y_df.select_dtypes(include=[np.number]),
atol=tol, equal_nan=True)
if x_df.shape[0]:
# we use numpy.allclose to grant a tolerance on numerical values
numerical_equal = np.allclose(x_df.select_dtypes(include=[np.number]),
y_df.select_dtypes(include=[np.number]),
atol=tol, equal_nan=True)
else:
numerical_equal = True

# ... use pandas .equals for the rest, which also evaluates NaNs to be equal
rest_equal = x_df.select_dtypes(exclude=[np.number]).equals(
Expand Down Expand Up @@ -1714,6 +1717,17 @@ def get_connected_switches(net, buses, consider=('b', 'l', 't'), status="all"):
return cs


def ensure_iterability(var, len_=None):
""" This function ensures iterability of a variable (and optional length). """
if hasattr(var, "__iter__") and not isinstance(var, str):
if isinstance(len_, int) and len(var) != len_:
raise ValueError("Length of variable differs from %i." % len_)
else:
len_ = len_ or 1
var = [var]*len_
return var


def pq_from_cosphi(s, cosphi, qmode, pmode):
"""
Calculates P/Q values from rated apparent power and cosine(phi) values.
Expand All @@ -1727,43 +1741,60 @@ def pq_from_cosphi(s, cosphi, qmode, pmode):
power, that means that loads are positive and generation is negative. For reactive power,
inductive behaviour is modeled with positive values, capacitive behaviour with negative values.
"""
if qmode == "ind":
qsign = 1
elif qmode == "cap":
qsign = -1
elif qmode == "ohm":
qsign = 1
if cosphi != 1:
raise ValueError("qmode cannot be 'ohm' if cosphi is not 1.")
else:
raise ValueError("Unknown mode %s - specify 'ind' or 'cap'" % qmode)

if pmode == "load":
psign = 1
elif pmode == "gen":
psign = -1
else:
raise ValueError("Unknown mode %s - specify 'load' or 'gen'" % pmode)

s = np.array(ensure_iterability(s))
cosphi = np.array(ensure_iterability(cosphi, len(s)))
qmode = np.array(ensure_iterability(qmode, len(s)))
pmode = np.array(ensure_iterability(pmode, len(s)))

# qmode consideration
unknown_qmode = set(qmode) - set(["ind", "cap", "ohm"])
if len(unknown_qmode):
raise ValueError("Unknown qmodes: " + str(list(unknown_qmode)))
qmode_is_ohm = qmode == "ohm"
if any(cosphi[qmode_is_ohm] != 1):
raise ValueError("qmode cannot be 'ohm' if cosphi is not 1.")
qsign = np.ones(qmode.shape)
qsign[qmode == "cap"] = -1

# pmode consideration
unknown_pmode = set(pmode) - set(["load", "gen"])
if len(unknown_pmode):
raise ValueError("Unknown pmodes: " + str(list(unknown_pmode)))
psign = np.ones(pmode.shape)
psign[pmode == "gen"] = -1

# calculate p and q
p = psign * s * cosphi
q = qsign * np.sqrt(s ** 2 - p ** 2)
return p, q

if len(p) > 1:
return p, q
else:
return p[0], q[0]


def cosphi_from_pq(p, q):
"""
Analog to pq_from_cosphi, but other way around.
In consumer viewpoint (pandapower): cap=overexcited and ind=underexcited
"""
if p == 0:
cosphi = np.nan
p = np.array(ensure_iterability(p))
q = np.array(ensure_iterability(q, len(p)))
if len(p) != len(q):
raise ValueError("p and q must have the same length.")
p_is_zero = np.array(p == 0)
cosphi = np.empty(p.shape)
if sum(p_is_zero):
cosphi[p_is_zero] = np.nan
logger.warning("A cosphi from p=0 is undefined.")
else:
cosphi = np.cos(np.arctan(q / p))
cosphi[~p_is_zero] = np.cos(np.arctan(q[~p_is_zero] / p[~p_is_zero]))
s = (p ** 2 + q ** 2) ** 0.5
pmode = ["undef", "load", "gen"][int(np.sign(p))]
qmode = ["ohm", "ind", "cap"][int(np.sign(q))]
return cosphi, s, qmode, pmode
pmode = np.array(["undef", "load", "gen"])[np.sign(p).astype(int)]
qmode = np.array(["ohm", "ind", "cap"])[np.sign(q).astype(int)]
if len(p) > 1:
return cosphi, s, qmode, pmode
else:
return cosphi[0], s[0], qmode[0], pmode[0]


def create_replacement_switch_for_branch(net, element, idx):
Expand Down

0 comments on commit 75c0f5f

Please sign in to comment.