Skip to content

Commit

Permalink
Merge branch 'develop' of gitlab:pp/pandapower into rbolgaryn
Browse files Browse the repository at this point in the history
  • Loading branch information
rbolgaryn committed Apr 23, 2018
2 parents 239587c + 0c1cfc2 commit 68c13c7
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 78 deletions.
18 changes: 10 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,20 @@ There is a project to develop a GUI for pandapower: https://github.com/johaack/p
Citing pandapower
==================

A paper describing pandapower has been accepted for publication in IEEE Transaction on Power Systems, a preprint of this paper is available on `arXiv <https://arxiv.org/abs/1709.06743>`_ Please acknowledge the usage of pandapower by citing the Paper as follows:
A paper describing pandapower has been accepted for publication in IEEE Transaction on Power Systems, a preprint of this paper is available on `arXiv <https://arxiv.org/abs/1709.06743>`_. Please acknowledge the usage of pandapower by citing the Paper as follows:

- **L. Thurner, A. Scheidler, F. Schäfer et al.**, pandapower - an Open Source Python Tool for Convenient Modeling, Analysis and Optimization of Electric Power Systems,” IEEE Transaction on Power Systems (to be published), 2018.
- **L. Thurner, A. Scheidler, F. Schäfer et al**, `pandapower - an Open Source Python Tool for Convenient Modeling, Analysis and Optimization of Electric Power Systems <https://arxiv.org/abs/1709.06743>`_, IEEE Transactions on Power Systems, `DOI:10.1109/TPWRS.2018.2829021 <https://doi.org/10.1109/TPWRS.2018.2829021>`_, 2018.

You can use the following BibTex entry: ::

@article{pandapower,
author = {{Thurner}, L. and {Scheidler}, A. and {Sch{\"a}fer}, F. and {Menke}, J.-H. and {Dollichon}, J. and {Meier}, F. and {Meinecke}, S. and {Braun}, M.},
title = "{pandapower - an Open Source Python Tool for Convenient Modeling, Analysis and Optimization of Electric Power Systems}",
year = 2018,
journal = {IEEE Transaction on Power Systems (to be published)},
url = {https://arxiv.org/abs/1709.06743}
@ARTICLE{pandapower.2018,
author={L. Thurner and A. Scheidler and F. Schafer and J. H. Menke and J. Dollichon and F. Meier and S. Meinecke and M. Braun},
journal={IEEE Transactions on Power Systems},
title={pandapower - an Open Source Python Tool for Convenient Modeling, Analysis and Optimization of Electric Power Systems},
year={2018},
doi={10.1109/TPWRS.2018.2829021},
url={https://arxiv.org/abs/1709.06743},
ISSN={0885-8950}
}

Minimal Example
Expand Down
25 changes: 15 additions & 10 deletions doc/about/citing.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
Citing pandapower
==================

We are currently working on publishing a paper about pandapower, which is available as a `preprint on arxiv <https://arxiv.org/abs/1709.06743>`_.
Until the paper has passed peer-review, please acknowledge the usage of pandapower in your paper as follows: ::

@online{pandapower,
author = {{Thurner}, L. and {Scheidler}, A. and {Sch{\"a}fer}, F. and {Menke}, J.-H. and {Dollichon}, J. and {Meier}, F. and {Meinecke}, S. and {Braun}, M.},
title = "{pandapower - an Open Source Python Tool for Convenient Modeling, Analysis and Optimization of Electric Power Systems}",
year = 2017,
url = {https://arxiv.org/abs/1709.06743},
note = {preprint}
}
A paper describing pandapower has been accepted for publication in IEEE Transaction on Power Systems, a preprint of this paper is available on `arXiv <https://arxiv.org/abs/1709.06743>`_. Please acknowledge the usage of pandapower by citing the Paper as follows:

- **L. Thurner, A. Scheidler, F. Schäfer et al**, `pandapower - an Open Source Python Tool for Convenient Modeling, Analysis and Optimization of Electric Power Systems <https://arxiv.org/abs/1709.06743>`_, IEEE Transactions on Power Systems, `DOI:10.1109/TPWRS.2018.2829021 <https://doi.org/10.1109/TPWRS.2018.2829021>`_, 2018.

You can use the following BibTex entry: ::

@ARTICLE{pandapower.2018,
author={L. Thurner and A. Scheidler and F. Schafer and J. H. Menke and J. Dollichon and F. Meier and S. Meinecke and M. Braun},
journal={IEEE Transactions on Power Systems},
title={pandapower - an Open Source Python Tool for Convenient Modeling, Analysis and Optimization of Electric Power Systems},
year={2018},
doi={10.1109/TPWRS.2018.2829021},
url={https://arxiv.org/abs/1709.06743},
ISSN={0885-8950}
}
Binary file added doc/elements/storage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/elements/storage.psd
Binary file not shown.
16 changes: 8 additions & 8 deletions doc/elements/storage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,33 @@ Input Parameters
*net.storage*

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

.. |br| raw:: html

<br />

\*necessary for executing a power flow calculation |br| \*\*optimal power flow parameter


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

Storages are modelled as PQ-buses in the power flow calculation:

.. image:: pq.png
.. image:: storage.png
:width: 8em
:alt: alternate Text
:align: center

The PQ-Values are calculated from the parameter table values as:

.. math::
:nowrap:
\begin{align*}
P_{storage} &= p\_kw \cdot scaling \\
Q_{storage} &= q\_kvar \cdot scaling \\
Expand All @@ -53,15 +53,15 @@ The PQ-Values are calculated from the parameter table values as:
Since all power values are given in the consumer system, negative power models charging and positive activee power models discharging.

.. note::

The apparent power value sn_kva, state of charge soc and storage capacity max_e_kwh are provided as additional information for usage in controller or other applications based on panadapower. It is not considered in the power flow!

Result Parameters
==========================
*net.res_storage*

.. tabularcolumns:: |p{0.10\linewidth}|p{0.10\linewidth}|p{0.50\linewidth}|
.. csv-table::
.. csv-table::
:file: storage_res.csv
:delim: ;
:widths: 10, 10, 50
Expand Down
16 changes: 8 additions & 8 deletions pandapower/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def create_empty_network(name="", f_hz=50., sn_kva=1e3):
("p_kw", "f8"),
("q_kvar", "f8"),
("sn_kva", "f8"),
("soc", "f8"),
("soc_percent", "f8"),
("min_e_kwh", "f8"),
("max_e_kwh", "f8"),
("scaling", "f8"),
Expand Down Expand Up @@ -267,7 +267,7 @@ def create_empty_network(name="", f_hz=50., sn_kva=1e3):
("q_kvar", "f8")],
"_empty_res_storage": [("p_kw", "f8"),
("q_kvar", "f8"),
("soc", "f8")],
("soc_percent", "f8")],
"_empty_res_gen": [("p_kw", "f8"),
("q_kvar", "f8"),
("va_degree", "f8"),
Expand Down Expand Up @@ -792,10 +792,10 @@ def create_sgen_from_cosphi(net, bus, sn_kva, cos_phi, mode, **kwargs):
return create_sgen(net, bus, sn_kva=sn_kva, p_kw=p_kw, q_kvar=q_kvar, **kwargs)


def create_storage(net, bus, p_kw, max_e_kwh, q_kvar=0, sn_kva=nan, soc=nan, min_e_kwh=0.0,
def create_storage(net, bus, p_kw, max_e_kwh, q_kvar=0, sn_kva=nan, soc_percent=nan, min_e_kwh=0.0,
name=None, index=None, scaling=1., type=None, in_service=True, max_p_kw=nan,
min_p_kw=nan, max_q_kvar=nan, min_q_kvar=nan, controllable = nan):
"""create_storage(net, bus, p_kw, max_e_kwh, q_kvar=0, sn_kva=nan, soc=nan, min_e_kwh=0.0, \
"""create_storage(net, bus, p_kw, max_e_kwh, q_kvar=0, sn_kva=nan, soc_percent=nan, min_e_kwh=0.0, \
name=None, index=None, scaling=1., type=None, in_service=True, max_p_kw=nan, \
min_p_kw=nan, max_q_kvar=nan, min_q_kvar=nan, controllable = nan)
Adds a storage to the network.
Expand Down Expand Up @@ -828,7 +828,7 @@ def create_storage(net, bus, p_kw, max_e_kwh, q_kvar=0, sn_kva=nan, soc=nan, min
**sn_kva** (float, default None) - Nominal power of the storage
**soc** (float, NaN) - The state of charge of the storage
**soc_percent** (float, NaN) - The state of charge of the storage
**min_e_kwh** (float, 0) - The minimum energy content of the storage \
(minimum charge level)
Expand Down Expand Up @@ -863,7 +863,7 @@ def create_storage(net, bus, p_kw, max_e_kwh, q_kvar=0, sn_kva=nan, soc=nan, min
**index** (int) - The unique ID of the created storage
EXAMPLE:
create_storage(net, 1, p_kw = -30, max_e_kwh = 60, soc = 1.0, min_e_kwh = 5)
create_storage(net, 1, p_kw = -30, max_e_kwh = 60, soc_percent = 1.0, min_e_kwh = 5)
"""
if bus not in net["bus"].index.values:
Expand All @@ -879,9 +879,9 @@ def create_storage(net, bus, p_kw, max_e_kwh, q_kvar=0, sn_kva=nan, soc=nan, min
dtypes = net.storage.dtypes

net.storage.loc[index, ["name", "bus", "p_kw", "q_kvar", "sn_kva", "scaling",
"soc", "min_e_kwh", "max_e_kwh", "in_service", "type"]] = \
"soc_percent", "min_e_kwh", "max_e_kwh", "in_service", "type"]] = \
[name, bus, p_kw, q_kvar, sn_kva, scaling,
soc, min_e_kwh, max_e_kwh, bool(in_service), type]
soc_percent, min_e_kwh, max_e_kwh, bool(in_service), type]

# and preserve dtypes
_preserve_dtypes(net.storage, dtypes)
Expand Down
81 changes: 47 additions & 34 deletions pandapower/opf/make_objective.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# and Energy System Technology (IEE), Kassel. All rights reserved.


import pandas as pd
from numpy import zeros, array, concatenate, power
from pandapower.idx_cost import MODEL, NCOST, COST

Expand Down Expand Up @@ -77,50 +78,55 @@ def _make_objective(ppci, net):
ppci["gencost"] = zeros((len_gencost, 4 + n_coefficients), dtype=float)
ppci["gencost"][:, MODEL:COST] = array([2, 0, 0, n_coefficients])

for cost_type in ["p", "q"]:

if cost_type == "q":
shift_idx = ng
sign_corr = -1
else:
shift_idx = 0
sign_corr = 1

for el in ["gen", "sgen", "ext_grid", "load", "storage", "dcline"]:

if el == "gen":
idx = gen_idx
elif el == "sgen":
idx = sgen_idx
elif el == "ext_grid":
idx = eg_idx
elif el == "load":
idx = load_idx
elif el == "storage":
idx = stor_idx
elif el == "dcline":
idx = dcline_idx
if len(net.piecewise_linear_cost):
for cost_type in ["p", "q"]:
if (net.piecewise_linear_cost.type == cost_type).any():
costs = net.piecewise_linear_cost[net.piecewise_linear_cost.type == cost_type].reset_index(
drop=True)

if len(net.piecewise_linear_cost):
if cost_type == "q":
shift_idx = ng
sign_corr = -1
else:
shift_idx = 0
sign_corr = 1

if (net.piecewise_linear_cost.type == cost_type).any():
costs = net.piecewise_linear_cost[
net.piecewise_linear_cost.type == cost_type].reset_index(drop=True)
# for element types with costs defined
for el in pd.unique(costs.element_type):

if not costs.element[costs.element_type == el].empty:

if el == "gen":
idx = gen_idx
elif el == "sgen":
idx = sgen_idx
elif el == "ext_grid":
idx = eg_idx
elif el == "load":
idx = load_idx
elif el == "storage":
idx = stor_idx
elif el == "dcline":
idx = dcline_idx

# cost data to write into gencost
el_is = net[el].loc[(net[el].in_service) & net[el].index.isin(
# (only write cost data of controllable and in service elements)
if el == "ext_grid" or el == "dcline":
el_is = net[el].loc[net[el].in_service & net[el].index.isin(
costs.loc[costs.element_type == el].element)].index
else:
el_is = net[el].loc[net[el].controllable & net[el].in_service & net[el].index.isin(
costs.loc[costs.element_type == el].element)].index

p = costs.loc[(costs.element_type == el) & (
costs.element.isin(el_is))].p.reset_index(drop=True)
f = costs.loc[(costs.element_type == el) & (
costs.element.isin(el_is))].f.reset_index(drop=True)

if len(p) > 0:
p = concatenate(p)
f = concatenate(f)
# gencost indices

elements = idx[el_is] + shift_idx

ppci["gencost"][elements, COST:COST+n_piece_lin_coefficients:2] = p
Expand All @@ -140,8 +146,7 @@ def _make_objective(ppci, net):
if len(net.polynomial_cost):
for cost_type in ["p", "q"]:
if (net.polynomial_cost.type == cost_type).any():
costs = net.polynomial_cost[net.polynomial_cost.type == cost_type].reset_index(
drop=True)
costs = net.polynomial_cost[net.polynomial_cost.type == cost_type].reset_index(drop=True)

if cost_type == "q":
shift_idx = ng
Expand All @@ -150,7 +155,8 @@ def _make_objective(ppci, net):
shift_idx = 0
sign_corr = 1

for el in ["gen", "sgen", "ext_grid", "load", "storage", "dcline"]:
# for element types with costs defined
for el in pd.unique(costs.element_type):

if not costs.element[costs.element_type == el].empty:
if el == "gen":
Expand All @@ -166,8 +172,15 @@ def _make_objective(ppci, net):
if el == "dcline":
idx = dcline_idx

el_is = net[el].loc[(net[el].in_service) & net[el].index.isin(
costs.loc[costs.element_type == el].element)].index
# cost data to write into gencost
# (only write cost data of controllable and in service elements)
if el == "ext_grid" or el == "dcline":
el_is = net[el].loc[net[el].in_service & net[el].index.isin(
costs.loc[costs.element_type == el].element)].index
else:
el_is = net[el].loc[net[el].controllable & net[el].in_service & net[el].index.isin(
costs.loc[costs.element_type == el].element)].index

c = costs.loc[(costs.element_type == el) &
(costs.element.isin(el_is))].c.reset_index(drop=True)

Expand Down
58 changes: 50 additions & 8 deletions pandapower/test/opf/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,17 +578,11 @@ def test_storage_opf():
min_p_kw=-25, max_q_kvar=25, min_q_kvar=-25)
pp.create_sgen(net, b1, p_kw=-25, controllable=True, max_p_kw=0, min_p_kw=-25,
max_q_kvar=25, min_q_kvar=-25)
#pp.create_load(net, b1, p_kw=2.5, controllable=True, max_p_kw=2.5, min_p_kw=0,
# max_q_kvar=2.5, min_q_kvar=-2.5)
pp.create_load(net, b1, p_kw=25, controllable=True, max_p_kw=25, min_p_kw=0,
max_q_kvar=25, min_q_kvar=-25)



# costs
pp.create_polynomial_cost(net, 0, "ext_grid", np.array([0, 3, 0]))
#pp.create_polynomial_cost(net, 0, "load", np.array([0, -1, 0]))
# TODO - Gitlab Issue #27 - OPF error wenn Kosten für non-controllable PQ-Element zugewiesen
# vgl. make_objective.py, Z.47ff.
pp.create_polynomial_cost(net, 0, "sgen", np.array([0, 2, 0]))
pp.create_polynomial_cost(net, 0, "storage", np.array([0, 1, 0]))
pp.create_polynomial_cost(net, 1, "sgen", np.array([0, 1, 0]))
Expand Down Expand Up @@ -662,9 +656,57 @@ def test_storage_opf():
assert np.isclose(res_cost_stor, res_cost_load)


def test_opf_no_controllables_vs_pf():
""" Comparing the calculation results of PF and OPF in a simple network with non-controllable
elements """

# boundaries
vm_max = 1.3
vm_min = 0.9
max_line_loading_percent = 100

# create network
net = pp.create_empty_network()

b1 = pp.create_bus(net, vn_kv=0.4, max_vm_pu=vm_max, min_vm_pu=vm_min)
b2 = pp.create_bus(net, vn_kv=0.4, max_vm_pu=vm_max, min_vm_pu=vm_min)

pp.create_line(net, b1, b2, length_km=5, std_type="NAYY 4x50 SE",
max_loading_percent=max_line_loading_percent)

# test elements static
pp.create_ext_grid(net, b2)
pp.create_load(net, b1, p_kw=7.5, controllable=False)
pp.create_sgen(net, b1, p_kw=-25, controllable=False, max_p_kw=-10, min_p_kw=-25,
max_q_kvar=25, min_q_kvar=-25)

# testing cost assignment (for non-controllable elements - see Gitlab Issue #27)
pp.create_polynomial_cost(net, 0, "ext_grid", np.array([0, 3, 0]))
pp.create_polynomial_cost(net, 0, "load", np.array([0, -3, 0]))
pp.create_polynomial_cost(net, 0, "sgen", np.array([0, 2, 0]))

# do calculations
pp.runopp(net, verbose=False)
assert net["OPF_converged"]

res_opf_line_loading = net.res_line.loading_percent
res_opf_bus_voltages = net.res_bus.vm_pu

pp.runpp(net, verbose=False)
assert net["converged"]

res_pf_line_loading = net.res_line.loading_percent
res_pf_bus_voltages = net.res_bus.vm_pu

# assert calculation behaviour
assert np.isclose(res_opf_line_loading, res_pf_line_loading).all()
assert np.isclose(res_opf_bus_voltages, res_pf_bus_voltages).all()


if __name__ == "__main__":
pytest.main(['-s', __file__])
#pytest.main(['-s', __file__])
#test_storage_opf()
test_opf_no_controllables_vs_pf()
#test_opf_varying_max_line_loading()
# pytest.main(["test_basic.py", "-s"])
# test_simplest_dispatch()
Expand Down
Loading

0 comments on commit 68c13c7

Please sign in to comment.