e2nIEE / pandapower

Convenient Power System Modelling and Analysis based on PYPOWER and pandas
https://www.pandapower.org
Other
848 stars 478 forks source link

[bug] Issue when running DC powerflow after AC powerflow #1996

Closed BDonnot closed 1 year ago

BDonnot commented 1 year ago

Issue Report Checklist

Problem Description

When I run a DC powerflow after an AC powerflow, some columns of the net.res_bus (such as vm_pu and q_mvar) does not seem to be "updated" with the powerflow results.

This results in strange behaviour such as (see reproducible code below) net.res_line["vm_from_pu"] which is not equal to net.res_bus.iloc[THE BUS WHERE THE LINE IS CONNECTED]["vm_pu"].

The doc (see eg https://pandapower.readthedocs.io/en/latest/elements/line.html#result-parameters) clearly specify : vm_from_pu: voltage magnitude at from bus which is not the case in the example.

What steps reproduce the problem?

  1. install pandapower
  2. run the code

What is the expected output? What do you see instead?

If you look at the bus to which a line is connected and the value of "vm_from_pu" then there should not be a difference. The voltage magnitude is unique at a given bus.

import pandapower as pp
import pandapower.networks as pn
import numpy as np

def get_installed_versions():
    res = []
    import pandapower as pp
    res.append(f"* pandapower version: {pp.__version__}")
    import pandas as pd
    res.append(f"* pandas version: {pd.__version__}")
    import networkx
    res.append(f"* networkx version: {networkx.__version__}")
    import scipy
    res.append(f"* scipy version: {scipy.__version__}")
    import numpy as np
    res.append(f"* numpy version: {np.__version__}")
    import packaging
    res.append(f"* packaging version: {packaging.__version__}")
    import tqdm
    res.append(f"* tqdm version: {tqdm.__version__}")
    import deepdiff
    res.append(f"* deepdiff version: {deepdiff.__version__}")
    return "\n".join(res)
print(get_installed_versions())

# now the test
net = pn.case14()

line_id = 6
# run an ac powerflow (simple setting)
pp.runpp(net, init="flat", lightsim2grid=False, distributed_slack=False)
# normal behaviour (see below for detail about this line of code)
from_bus, to_bus = net.line.iloc[line_id][["from_bus", "to_bus"]]
assert np.allclose(net.res_bus.iloc[[from_bus, to_bus]]["vm_pu"].values,
                   net.res_line.iloc[line_id][["vm_from_pu", "vm_to_pu"]].values)

# then run a dc powerflow
pp.rundcpp(net)
assert( net.res_line.iloc[line_id][["vm_from_pu", "vm_to_pu"]] == [1.0, 1.0]).all()
net.line.iloc[line_id][["from_bus", "to_bus"]]  # [3, 4]
from_bus, to_bus = net.line.iloc[line_id][["from_bus", "to_bus"]]  # [3, 4]
assert from_bus == 3
assert to_bus == 4
assert np.allclose(net.res_bus.iloc[[3, 4]]["vm_pu"], [1.017671, 1.019514])

# weird behaviour (same as above, in one line of code)
assert np.allclose(net.res_bus.iloc[[from_bus, to_bus]]["vm_pu"].values,
                   net.res_line.iloc[line_id][["vm_from_pu", "vm_to_pu"]].values)

Paste Traceback/Error Below (if applicable)

Traceback (most recent call last):
  File "test_pp_dc.py", line 48, in <module>
    assert np.allclose(net.res_bus.iloc[[from_bus, to_bus]]["vm_pu"].values,
AssertionError

Versions

ps. it whould be nice to have a helper function from pandapower from which we could copy paste results above. Such as:

import pandapower as pp
print(pp.get_installed_versions())

And somewhere in pandapower you define:

def get_installed_versions():
    res = []
    import pandapower as pp
    res.append(f"* pandapower version: {pp.__version__}")
    import pandas as pd
    res.append(f"* pandas version: {pd.__version__}")
    import networkx
    res.append(f"* networkx version: {networkx.__version__}")
    import scipy
    res.append(f"* scipy version: {scipy.__version__}")
    import numpy as np
    res.append(f"* numpy version: {np.__version__}")
    import packaging
    res.append(f"* packaging version: {packaging.__version__}")
    import tqdm
    res.append(f"* tqdm version: {tqdm.__version__}")
    import deepdiff
    res.append(f"* deepdiff version: {deepdiff.__version__}")
    return "\n".join(res)

Labels (if access available)

rbolgaryn commented 1 year ago

https://stackoverflow.com/questions/29751572/how-to-find-a-python-packages-dependencies

from pip._vendor import pkg_resources

_package_name = 'somepackage'
_package = pkg_resources.working_set.by_key[_package_name]

print([str(r) for r in _package.requires()])  # retrieve deps from setup.py

update: above code is stated to be deprecated

rbolgaryn commented 1 year ago

pkg_resources is deprecated, importlib should be used instead. Here documentation to importlib, hopefully it can be used to solve this easily. If not, then it is worth to write own implementation for covenience.

https://docs.python.org/3/library/importlib.resources.html

https://docs.python.org/3/library/importlib.metadata.html

BDonnot commented 1 year ago

Hello,

Maybe I have misunderstood your comment, but my issue was related to DC powerflow and not packages version or anything else that could be addressed by importlib or pkg_resources.

rbolgaryn commented 1 year ago

oh I see, thank you.

net = pp.networks.case9()
    pp.runpp(net)
    pp.rundcpp(net)
    assert np.allclose(net.res_bus.loc[net.line.from_bus.values, "vm_pu"], net.res_line.vm_from_pu, equal_nan=True)
    assert np.allclose(net.res_bus.loc[net.line.from_bus.values, "va_degree"], net.res_line.va_from_degree, equal_nan=True)
    assert np.allclose(net.res_bus.loc[net.line.to_bus.values, "vm_pu"], net.res_line.vm_to_pu, equal_nan=True)
    assert np.allclose(net.res_bus.loc[net.line.to_bus.values, "va_degree"], net.res_line.va_to_degree, equal_nan=True)

This test passes when rundcpp is skipped.

When runpp is skipped, res_bus has vm_pu = np.nan, and res_line.vm_xxx_pu = 1.0.

When runpp is not skipped, res_bus has the old results from the runpp, and res_line has 1.0

Seems like the value in res_line is always 1.0 for rundcpp, and res_bus has np.nan except when the runpp result is there as an artifact.

rbolgaryn commented 1 year ago

I suspect that the old value can be coming from ppc["bus"][:, VM]

rbolgaryn commented 1 year ago

net._ppc["bus"][:, VM]
Out[78]: array([1., 1., 1., 1., 1., 1., 1., 1., 1.])
rbolgaryn commented 1 year ago

This solves the issue:

net = pp.networks.case9()
pp.runpp(net)
net.res_bus.drop(net.res_bus.index, inplace=True)
pp.rundcpp(net)

So the old value is coming from the net.res_bus table

rbolgaryn commented 1 year ago

Making a change in _get_bus_results to always write results for vm_pu as following should solve it:

def _get_bus_v_results(net, ppc, suffix=None):
    ac = net["_options"]["ac"]
    bus_idx = _get_bus_idx(net)

    res_table = "res_bus" if suffix is None else "res_bus%s" % suffix
    # if ac:
    #     net[res_table]["vm_pu"] = ppc["bus"][bus_idx][:, VM]
    net[res_table]["vm_pu"] = ppc["bus"][bus_idx][:, VM]
    # voltage angles
    net[res_table]["va_degree"] = ppc["bus"][bus_idx][:, VA]

This will result in having vm_pu = 1 in net.res_bus.

Previously, the result was always np.nan (without the AC power flow). The results for vm_xxx_pu has always been taken from ppc["bus"][:, VM_PU] so it has always been 1.0

Does this seem right as a fix?

BDonnot commented 1 year ago

Hello again 😊

My suggestion about having something to output the versions of the packages was because I needed this information to write correct issues. So this seemed fair to have an easy solution directly in pandapower. Hence the "quick and dirty" proposition I made.

For the DC powerflow your fix seems reasonable indeed. I suspected it was because some values were re used from the ax powerflow.

Thanks for the reactivity