Repository: e2nIEE/pandapower

Issue:
PowerModels vs internal OPF
There is a slight difference in the calculation method between the internal pandapower OPF calculation and PowerModels with default configuration. 
The following example only converges for the internal OPF, but not with PowerModels.
```python
import pandapower as pp
net = pp.create_empty_network()
b1 = pp.create_bus(net, vn_kv=20., max_vm_pu=1.1, min_vm_pu=0.9)
b2 = pp.create_bus(net, vn_kv=.4, max_vm_pu=1.1, min_vm_pu=0.9)
pp.create_transformer(net, b1, b2, std_type="0.4 MVA 20/0.4 kV", max_loading_percent=80)
# net.trafo['shift_degree'] = 0
ext = pp.create_ext_grid(net, b1, min_p_mw=-0.4, max_p_mw=0.4, min_q_mvar=-0.4, max_q_mvar=0.4)
pp.create_load(net, b2, p_mw=0.01, controllable=False)

# Run OPF pandapower
pp.runopp(net)

# Run OPF PowerModels
pp.runpm_ac_opf(net)
```
Setting `shift_degree` of the transformer to `0` instead of `150` according to the [std_type](https://pandapower.readthedocs.io/en/v2.2.2/std_types/basic.html#transformers), or setting `calculate_voltage_angles=False` for PowerModels, both OPF yield the same result.
I am not sure whether this is intended, but it might be a good idea to give a quick hint to that issue in the [document](https://pandapower.readthedocs.io/en/v2.2.2/opf/powermodels.html) or the [PowerModels Tutorial](https://github.com/e2nIEE/pandapower/blob/develop/tutorials/opf_powermodels.ipynb). Other option is to use the same default configuration of `calculate_voltage_angles=` for internal OPF and PowerModels.
Timeseries_example does not compile
I attempted to run the code from the timeseries_example, as the title shows, but the code does not compile. I have a fix for the code but it is not a complete fix. The fix compiles all but one command, when I attempt to run the ts.run_timeseries command I get "IndexingError: Too many indexers". So I don't know if my fix doesn't acutally work or there is an indexing error in the run_time_series command. I will reference this Issue in the code Push since I cannot upload the .rst file here.

This brings another issue up, the naming conventions throughout the documentation are inconsistent and incomplete. I've tried to push some updates concerning this, and will to go through and fix the rest if thats helpful. I would like some direction on the requested naming conventions, as I am not a primary caretaker of this database.


Discussion:
This seems to be a PowerModels issue, there is already a ticket about this on the PowerModels issue board: https://github.com/lanl-ansi/PowerModels.jl/issues/422. A hint in the documentation would definetely be a good idea. Do you want to put in a PR for this?
I just added another observation to the description above at the time you responded. 
It seems that the pandapower OPF by default sets `calculate_voltage_angles=False`. When enabling it, OPF does not converge as well (similar to PowerModels). Hence, I simply messed up with the default value of `calculate_voltage_angles=`, which is `True` for PowerModels call ([see](https://github.com/e2nIEE/pandapower/blob/develop/pandapower/runpm.py#L158)) and `False` for internal OPF ([see](https://github.com/e2nIEE/pandapower/blob/develop/pandapower/run.py#L270)).

Is it intended to have different default parameters for OPF?
>Is it intended to have different default parameters for OPF?

@FlorianShepherd   do you know this?
> I just added another observation to the description above at the time you responded.
> It seems that the pandapower OPF by default sets `calculate_voltage_angles=False`. When enabling it, OPF does not converge as well (similar to PowerModels). Hence, I simply messed up with the default value of `calculate_voltage_angles=`, which is `True` for PowerModels call ([see](https://github.com/e2nIEE/pandapower/blob/develop/pandapower/runpm.py#L158)) and `False` for internal OPF ([see](https://github.com/e2nIEE/pandapower/blob/develop/pandapower/run.py#L270)).
> 
> Is it intended to have different default parameters for OPF?

Thank you for highlighting this. No, it's not the intended behaviour. I'd suggest changing the default parameters so that both match. I think it would be best to set it to True for both.

About the angles: PowerModels doesn't support angles larger than 60 degree as said in their documentation

What @lthurner mentioned is another issue I think with the DC OPF. Here, the tap changer values are also not considered in the "DCPPowerModel". That's why we added a model called "DCMPPowerModel", which imitates the behaviour of the matpower DC OPF (which is also used in pandapower/pypower).


Resolving patch:
diff --git a/pandapower/auxiliary.py b/pandapower/auxiliary.py
--- a/pandapower/auxiliary.py
+++ b/pandapower/auxiliary.py
@@ -1037,6 +1037,7 @@ def _init_runopp_options(net, calculate_voltage_angles, check_connectivity, swit
     # scipy spsolve options in NR power flow
     use_umfpack = kwargs.get("use_umfpack", True)
     permc_spec = kwargs.get("permc_spec", None)
+    lightsim2grid = kwargs.get("lightsim2grid", False)
 
     net._options = {}
     _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
@@ -1045,7 +1046,7 @@ def _init_runopp_options(net, calculate_voltage_angles, check_connectivity, swit
                      init_va_degree=init, enforce_q_lims=enforce_q_lims, recycle=recycle,
                      voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses,
                      consider_line_temperature=consider_line_temperature)
-    _add_opf_options(net, trafo_loading=trafo_loading, ac=ac, init=init, numba=numba,
+    _add_opf_options(net, trafo_loading=trafo_loading, ac=ac, init=init, numba=numba, lightsim2grid=lightsim2grid,
                      only_v_results=only_v_results, use_umfpack=use_umfpack, permc_spec=permc_spec)
 
 
diff --git a/pandapower/run.py b/pandapower/run.py
--- a/pandapower/run.py
+++ b/pandapower/run.py
@@ -81,7 +81,7 @@ def runpp(net, algorithm='nr', calculate_voltage_angles="auto", init="auto",
                 - "fdbx" fast-decoupled (pypower implementation)
                 - "fdxb" fast-decoupled (pypower implementation)
 
-        **calculate_voltage_angles** (bool, "auto") - consider voltage angles in loadflow calculation
+        **calculate_voltage_angles** (str or bool, "auto") - consider voltage angles in loadflow calculation
 
             If True, voltage angles of ext_grids and transformer shifts are considered in the
             loadflow calculation. Considering the voltage angles is only necessary in meshed
@@ -267,7 +267,7 @@ def rundcpp(net, trafo_model="t", trafo_loading="current", recycle=None, check_c
     _powerflow(net, **kwargs)
 
 
-def runopp(net, verbose=False, calculate_voltage_angles=False, check_connectivity=True,
+def runopp(net, verbose=False, calculate_voltage_angles=True, check_connectivity=True,
            suppress_warnings=True, switch_rx_ratio=2, delta=1e-10, init="flat", numba=True,
            trafo3w_losses="hv", consider_line_temperature=False, **kwargs):
     """


Test patch:
diff --git a/pandapower/test/opf/test_basic.py b/pandapower/test/opf/test_basic.py
--- a/pandapower/test/opf/test_basic.py
+++ b/pandapower/test/opf/test_basic.py
@@ -228,7 +228,7 @@ def test_opf_gen_voltage():
 
     # run OPF
     for init in ["pf", "flat"]:
-        pp.runopp(net, init=init)
+        pp.runopp(net, init=init, calculate_voltage_angles=False)
         assert net["OPF_converged"]
 
     # check and assert result
@@ -274,7 +274,7 @@ def test_opf_sgen_voltage():
 
     # run OPF
     for init in ["pf", "flat"]:
-        pp.runopp(net, init=init)
+        pp.runopp(net, init=init, calculate_voltage_angles=False)
         assert net["OPF_converged"]
 
     # assert and check result
@@ -323,7 +323,7 @@ def test_opf_gen_loading():
     # run OPF
 
     pp.runopp(net, OPF_VIOLATION=1e-1, OUT_LIM_LINE=2,
-              PDIPM_GRADTOL=1e-10, PDIPM_COMPTOL=1e-10, PDIPM_COSTTOL=1e-10)
+              PDIPM_GRADTOL=1e-10, PDIPM_COMPTOL=1e-10, PDIPM_COSTTOL=1e-10, calculate_voltage_angles=False)
     assert net["OPF_converged"]
 
     # assert and check result
@@ -373,7 +373,7 @@ def test_opf_sgen_loading():
 
     # run OPF
     for init in ["pf", "flat"]:
-        pp.runopp(net, init=init)
+        pp.runopp(net, init=init, calculate_voltage_angles=False)
         assert net["OPF_converged"]
 
     # assert and check result
@@ -386,7 +386,7 @@ def test_opf_sgen_loading():
     assert max(net.res_bus.vm_pu) < vm_max
     assert min(net.res_bus.vm_pu) > vm_min
     # check connectivity check
-    pp.runopp(net, check_connectivity=True)
+    pp.runopp(net, check_connectivity=True, calculate_voltage_angles=False)
 
 
 def test_unconstrained_line():
@@ -410,7 +410,7 @@ def test_unconstrained_line():
     pp.create_poly_cost(net, 0, "gen", cp1_eur_per_mw=1)
     # run OPF
     for init in ["pf", "flat"]:
-        pp.runopp(net, init=init)
+        pp.runopp(net, init=init, calculate_voltage_angles=False)
         assert net["OPF_converged"]
 
     # check and assert result
@@ -546,7 +546,7 @@ def test_opf_varying_max_line_loading():
                                    max_loading_percent=10)
 
     # run OPF
-    pp.runopp(net, init="flat")
+    pp.runopp(net, init="flat", calculate_voltage_angles=False)
     assert net["OPF_converged"]
 
     assert np.allclose(net["_ppc"]["branch"][:, 5], np.array([0.02771281 + 0.j, 0.00692820 + 0.j, 0.12800000 + 0.j]))
@@ -816,7 +816,7 @@ def test_three_slacks_vm_setpoint(four_bus_net):
     pp.runpp(net)
     # assert if voltage limits are correct in result in pf an opf
     assert np.allclose(net.res_bus.loc[[0, 1, 3], "vm_pu"], [1., 1.01, 1.02])
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
     assert np.allclose(net.res_bus.loc[[0, 1, 3], "vm_pu"], [1., 1.01, 1.02])
 
 
@@ -836,7 +836,7 @@ def test_only_gen_slack_vm_setpoint(four_bus_net):
     pp.runpp(net)
     # assert if voltage limits are correct in result in pf an opf
     assert np.allclose(net.res_bus.loc[[0, 1, 3], "vm_pu"], [1., 1.01, 1.02])
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
 
     # controllable == True is more important than  slack == True -> vm_pu is between bus limits
     assert not np.allclose(net.res_bus.at[0, "vm_pu"], 1.)
@@ -862,7 +862,7 @@ def test_gen_p_vm_fixed(four_bus_net):
     # controllable == False -> limits are ignored and p_mw / vm_pu values are enforced
     pp.create_gen(net, bus, p_mw=p_mw, vm_pu=vm_pu, controllable=False,
                   min_vm_pu=min_vm_pu, max_vm_pu=max_vm_pu, min_p_mw=min_p_mw, max_p_mw=max_p_mw)
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
     assert np.allclose(net.res_bus.at[bus, "vm_pu"], vm_pu)
     assert np.allclose(net.res_bus.at[bus, "p_mw"], -p_mw)
 
@@ -878,7 +878,7 @@ def test_gen_p_vm_limits(four_bus_net):
     # controllable == False -> limits are ignored and p_mw / vm_pu values are enforced
     pp.create_gen(net, bus, p_mw=0.02, vm_pu=1.01, controllable=True,
                   min_vm_pu=min_vm_pu, max_vm_pu=max_vm_pu, min_p_mw=min_p_mw, max_p_mw=max_p_mw)
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
     assert not np.allclose(net.res_bus.at[bus, "vm_pu"], 1.01)
     assert not np.allclose(net.res_bus.at[bus, "p_mw"], 0.02)
     assert min_vm_pu < net.res_bus.at[bus, "vm_pu"] < max_vm_pu
@@ -897,13 +897,13 @@ def test_gen_violated_p_vm_limits(four_bus_net):
     # controllable == False -> limits are ignored and p_mw / vm_pu values are enforced
     g = pp.create_gen(net, bus, p_mw=0.02, vm_pu=1.01, controllable=True,
                       min_vm_pu=.9, max_vm_pu=1.1, min_p_mw=min_p_mw, max_p_mw=max_p_mw)
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
     assert not np.allclose(net.res_bus.at[bus, "vm_pu"], 1.01)
     assert not np.allclose(net.res_bus.at[bus, "p_mw"], 0.02)
     assert min_vm_pu < net.res_bus.at[bus, "vm_pu"] < max_vm_pu
     assert min_p_mw <= -net.res_bus.at[bus, "p_mw"] < max_p_mw
     net.gen.at[g, "vm_pu"] = 0.9  # lower bus vm_pu limit violation
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
     assert min_vm_pu < net.res_bus.at[bus, "vm_pu"] < max_vm_pu
 
 
diff --git a/pandapower/test/opf/test_oberrhein.py b/pandapower/test/opf/test_oberrhein.py
--- a/pandapower/test/opf/test_oberrhein.py
+++ b/pandapower/test/opf/test_oberrhein.py
@@ -32,8 +32,8 @@ def test_opf_oberrhein():
     net.sgen["controllable"] = True
     net.load["controllable"] = False
     # run OPF
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
     assert net["OPF_converged"]
 
 if __name__ == "__main__":
-    pytest.main(["-xs"])
+    pytest.main([__file__, "-xs"])
diff --git a/pandapower/test/opf/test_opf_cigre.py b/pandapower/test/opf/test_opf_cigre.py
--- a/pandapower/test/opf/test_opf_cigre.py
+++ b/pandapower/test/opf/test_opf_cigre.py
@@ -71,7 +71,7 @@ def test_some_sgens_not_controllable():
         net.poly_cost.cp1_eur_per_mw.at[cost_sgen] = 100
 
     # run OPF
-    pp.runopp(net)
+    pp.runopp(net, calculate_voltage_angles=False)
     assert net["OPF_converged"]
     # check if p_mw of non conrollable sgens are unchanged
     assert np.allclose(net.res_sgen.p_mw[net.sgen.controllable == False], net.sgen.p_mw[net.sgen.controllable == False])
@@ -80,4 +80,4 @@ def test_some_sgens_not_controllable():
 
 
 if __name__ == "__main__":
-    pytest.main(["-xs"])
+    pytest.main([__file__, "-xs"])