Repository: e2nIEE/pandapower

Issue:
[bug] Issue when running DC powerflow after AC powerflow
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Bug reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [X] Searched the [issues page](https://github.com/e2nIEE/pandapower/issues) for similar reports
* [X] Read the relevant sections of the [documentation](https://pandapower.readthedocs.io/en/latest/about.html) 
* [X] Browse the [tutorials](https://github.com/e2nIEE/pandapower/tree/develop/tutorials) and [tests](https://github.com/e2nIEE/pandapower/tree/develop/pandapower/test) for usefull code snippets and examples of use
* [X] Reproduced the issue after updating with `pip install --upgrade pandapower` (or `git pull`)
* [X] Tried basic troubleshooting (if a bug/error)
    * [X] Restarted interpreter
    * [X] Checked the pythonpath
* [X] Completed the **Problem Description**, **What steps reproduce the problem** and **Versions** sections below


## 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.

```python
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)
<!--- Copy from error dialog or View > Panes > Internal Console --->

```python-traceback
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
<!--- You can get this information from the "pip list" or "conda list" command
from the Anaconda Prompt/Terminal/command line . --->

* pandapower version: 2.12.1
* pandas version: 1.5.3
* networkx version: 2.5
* scipy version: 1.6.0
* numpy version: 1.20.3
* packaging version: 20.9
* tqdm version: 4.61.2
* deepdiff version: 6.2.1
* Operating System name/version: ubuntu 20.04

ps. it whould be nice to have a helper function from pandapower from which we could copy paste results above. Such as:
```python
import pandapower as pp
print(pp.get_installed_versions())
```

And somewhere in pandapower you define:

```python
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)
* [X] Choose a `labels` at the right side (e.g. `bug`, `controller`, `OPF`...)


Discussion:
https://stackoverflow.com/questions/29751572/how-to-find-a-python-packages-dependencies

```python
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

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
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. 
oh I see, thank you. 

```python
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.
I suspect that the old value can be coming from ppc["bus"][:, VM]
```python

net._ppc["bus"][:, VM]
Out[78]: array([1., 1., 1., 1., 1., 1., 1., 1., 1.])

```
This solves the issue:

```python
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
Making a change in _get_bus_results to always write results for vm_pu as following should solve it:

```python
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?
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 

Resolving patch:
diff --git a/pandapower/results_bus.py b/pandapower/results_bus.py
--- a/pandapower/results_bus.py
+++ b/pandapower/results_bus.py
@@ -31,12 +31,11 @@ def _set_buses_out_of_service(ppc):
 
 
 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]
+    # always read the vm_pu from ppc, even for DC power flow - otherwise old values can persist
+    net[res_table]["vm_pu"] = ppc["bus"][bus_idx][:, VM]
     # voltage angles
     net[res_table]["va_degree"] = ppc["bus"][bus_idx][:, VA]
 


Test patch:
diff --git a/pandapower/test/loadflow/test_rundcpp.py b/pandapower/test/loadflow/test_rundcpp.py
--- a/pandapower/test/loadflow/test_rundcpp.py
+++ b/pandapower/test/loadflow/test_rundcpp.py
@@ -111,5 +111,17 @@ def test_missing_gen():
     assert np.allclose(net.res_gen.values, res_gen, equal_nan=True)
 
 
+def test_res_bus_vm():
+    net = nw.case4gs()
+    # run power flow to have bus vm_pu values
+    pp.runpp(net)
+    # now run DC pf and check that the vm_pu values are reset to 1
+    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)
+
+
 if __name__ == "__main__":
     pytest.main([__file__, "-xs"])