Repository: e2nIEE/pandapower

Issue:
ConstControl: Modifying scaling of all loads based on input dataframe
Hello,
I am trying to give an input dataframe to control the scaling of all loads for each time_step in a timeseries simulation
At the end of the simulation, the res_bus[p_mw] is correctly varying according to the given dataframe.
Nevertheless, the results of bus voltages and lines loading appear constant; it sounds like the power flow results don't take into account the load scaling.

This is how I create the controller:
```python
def create_controllers(net, ds):
    ConstControl(net, element='load', variable='scaling', element_index=net.load.index,
                 data_source=ds, profile_name=["Load_p"])
```
where  `Load_p` is the dataframe column with the scaling factors.
I have the feeling that this approach should work, so I may be doing something wrong.
Do you have any suggestions about how to handle it?

Best regards,
Michele
ConstControl: Modifying scaling of all loads based on input dataframe
Hello,
I am trying to give an input dataframe to control the scaling of all loads for each time_step in a timeseries simulation
At the end of the simulation, the res_bus[p_mw] is correctly varying according to the given dataframe.
Nevertheless, the results of bus voltages and lines loading appear constant; it sounds like the power flow results don't take into account the load scaling.

This is how I create the controller:
```python
def create_controllers(net, ds):
    ConstControl(net, element='load', variable='scaling', element_index=net.load.index,
                 data_source=ds, profile_name=["Load_p"])
```
where  `Load_p` is the dataframe column with the scaling factors.
I have the feeling that this approach should work, so I may be doing something wrong.
Do you have any suggestions about how to handle it?

Best regards,
Michele


Discussion:
Hi,
actually this looks correct. Could you provide a minimal working example so I can have a look and test it?
Best
Florian
Hello Florian,
thank you for the support.

Here it is the code. It is just an adaption of the tutorial on time series, where I try to modify the scaling factor of the loads.
If you run it, you will notice from the plots how the loads actually change according to the scaling, but both voltages and line loading remain constant, like if the scaling has no effect on loadflow calculations.

```python
net = pp.create_empty_network()

bus1 = pp.create_bus(net, vn_kv=20., name="Bus 1")
bus2 = pp.create_bus(net, vn_kv=0.4, name="Bus 2")
bus3 = pp.create_bus(net, vn_kv=0.4, name="Bus 3")

pp.create_ext_grid(net, bus=bus1, vm_pu=1.02, name="Grid Connection")
pp.create_load(net, bus=bus2, p_mw=0.200, q_mvar=0, name="Load2")
pp.create_load(net, bus=bus3, p_mw=1.00, q_mvar=0, name="Load3")

trafo = pp.create_transformer(net, hv_bus=bus1, lv_bus=bus2, std_type="0.4 MVA 20/0.4 kV", name="Trafo")
line = pp.create_line(net, from_bus=bus2, to_bus=bus3, length_km=0.1, std_type="NAYY 4x50 SE", name="Line")

n_snapshots = 24
scaling_factor = 0.5*np.random.rand(n_snapshots)

def create_data_source(scaling_factor):
    profiles = pd.DataFrame()
    profiles['Load_sf'] = scaling_factor
    ds = DFData(profiles)
    return profiles, ds

def create_controllers(net, ds):
    ConstControl(net, element='load', variable='scaling', element_index=net.load.index,
                 data_source=ds, profile_name=["Load_sf"])

def create_output_writer(net, time_steps, output_dir):
    ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=".xls", log_variables=list())
    ow.log_variable('res_load', 'p_mw')
    ow.log_variable('res_bus', 'vm_pu')
    ow.log_variable('res_line', 'loading_percent')
    ow.log_variable('res_line', 'i_ka')
    return ow

output_dir = os.path.join(os.getcwd(), "time_series_example")
if not os.path.exists(output_dir):
    os.mkdir(output_dir)
profiles, ds = create_data_source(scaling_factor)
create_controllers(net, ds)
time_steps = range(0, n_snapshots)
ow = create_output_writer(net, time_steps, output_dir=output_dir)

run_timeseries(net, time_steps)

import matplotlib.pyplot as plt

# voltage results
vm_pu_file = os.path.join(output_dir, "res_bus", "vm_pu.xls")
vm_pu = pd.read_excel(vm_pu_file, index_col=0)
vm_pu.plot(label="vm_pu")
plt.xlabel("time step")
plt.ylabel("voltage mag. [p.u.]")
plt.title("Voltage Magnitude")
plt.grid()
plt.show()

# line loading results
ll_file = os.path.join(output_dir, "res_line", "loading_percent.xls")
line_loading = pd.read_excel(ll_file, index_col=0)
line_loading.plot(label="line_loading")
plt.xlabel("time step")
plt.ylabel("line loading [%]")
plt.title("Line Loading")
plt.grid()
plt.show()

# load results
load_file = os.path.join(output_dir, "res_load", "p_mw.xls")
load = pd.read_excel(load_file,index_col=0)
load.plot(label="load")
plt.xlabel("time step")
plt.ylabel("P [MW]")
plt.grid()
plt.show()
```

Regards,
Michele
Thanks for mentioning. This is indeed a bug. 

 A quick fix (which will make the calculation slower) is to set "recycle" to false like so:
```
run_timeseries(net, time_steps, recycle=False)
``` 
I'll fix the recycle feature for this case. 
Hi,
actually this looks correct. Could you provide a minimal working example so I can have a look and test it?
Best
Florian
Hello Florian,
thank you for the support.

Here it is the code. It is just an adaption of the tutorial on time series, where I try to modify the scaling factor of the loads.
If you run it, you will notice from the plots how the loads actually change according to the scaling, but both voltages and line loading remain constant, like if the scaling has no effect on loadflow calculations.

```python
net = pp.create_empty_network()

bus1 = pp.create_bus(net, vn_kv=20., name="Bus 1")
bus2 = pp.create_bus(net, vn_kv=0.4, name="Bus 2")
bus3 = pp.create_bus(net, vn_kv=0.4, name="Bus 3")

pp.create_ext_grid(net, bus=bus1, vm_pu=1.02, name="Grid Connection")
pp.create_load(net, bus=bus2, p_mw=0.200, q_mvar=0, name="Load2")
pp.create_load(net, bus=bus3, p_mw=1.00, q_mvar=0, name="Load3")

trafo = pp.create_transformer(net, hv_bus=bus1, lv_bus=bus2, std_type="0.4 MVA 20/0.4 kV", name="Trafo")
line = pp.create_line(net, from_bus=bus2, to_bus=bus3, length_km=0.1, std_type="NAYY 4x50 SE", name="Line")

n_snapshots = 24
scaling_factor = 0.5*np.random.rand(n_snapshots)

def create_data_source(scaling_factor):
    profiles = pd.DataFrame()
    profiles['Load_sf'] = scaling_factor
    ds = DFData(profiles)
    return profiles, ds

def create_controllers(net, ds):
    ConstControl(net, element='load', variable='scaling', element_index=net.load.index,
                 data_source=ds, profile_name=["Load_sf"])

def create_output_writer(net, time_steps, output_dir):
    ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=".xls", log_variables=list())
    ow.log_variable('res_load', 'p_mw')
    ow.log_variable('res_bus', 'vm_pu')
    ow.log_variable('res_line', 'loading_percent')
    ow.log_variable('res_line', 'i_ka')
    return ow

output_dir = os.path.join(os.getcwd(), "time_series_example")
if not os.path.exists(output_dir):
    os.mkdir(output_dir)
profiles, ds = create_data_source(scaling_factor)
create_controllers(net, ds)
time_steps = range(0, n_snapshots)
ow = create_output_writer(net, time_steps, output_dir=output_dir)

run_timeseries(net, time_steps)

import matplotlib.pyplot as plt

# voltage results
vm_pu_file = os.path.join(output_dir, "res_bus", "vm_pu.xls")
vm_pu = pd.read_excel(vm_pu_file, index_col=0)
vm_pu.plot(label="vm_pu")
plt.xlabel("time step")
plt.ylabel("voltage mag. [p.u.]")
plt.title("Voltage Magnitude")
plt.grid()
plt.show()

# line loading results
ll_file = os.path.join(output_dir, "res_line", "loading_percent.xls")
line_loading = pd.read_excel(ll_file, index_col=0)
line_loading.plot(label="line_loading")
plt.xlabel("time step")
plt.ylabel("line loading [%]")
plt.title("Line Loading")
plt.grid()
plt.show()

# load results
load_file = os.path.join(output_dir, "res_load", "p_mw.xls")
load = pd.read_excel(load_file,index_col=0)
load.plot(label="load")
plt.xlabel("time step")
plt.ylabel("P [MW]")
plt.grid()
plt.show()
```

Regards,
Michele
Thanks for mentioning. This is indeed a bug. 

 A quick fix (which will make the calculation slower) is to set "recycle" to false like so:
```
run_timeseries(net, time_steps, recycle=False)
``` 
I'll fix the recycle feature for this case. 

Resolving patch:
diff --git a/pandapower/control/controller/const_control.py b/pandapower/control/controller/const_control.py
--- a/pandapower/control/controller/const_control.py
+++ b/pandapower/control/controller/const_control.py
@@ -100,14 +100,16 @@ def set_recycle(self):
             return
         # these variables determine what is re-calculated during a time series run
         recycle = dict(trafo=False, gen=False, bus_pq=False)
-        if self.element in ["sgen", "load", "storage"] and self.variable in ["p_mw", "q_mvar"]:
+        if self.element in ["sgen", "load", "storage"] and self.variable in ["p_mw", "q_mvar", "scaling"]:
             recycle["bus_pq"] = True
-        if self.element in ["gen"] and self.variable in ["p_mw", "vm_pu"] \
+        if self.element in ["gen"] and self.variable in ["p_mw", "vm_pu", "scaling"] \
                 or self.element in ["ext_grid"] and self.variable in ["vm_pu", "va_degree"]:
             recycle["gen"] = True
         if self.element in ["trafo", "trafo3w", "line"]:
             recycle["trafo"] = True
-        self.recycle = recycle
+        # recycle is either the dict what should be recycled
+        # or False if the element + variable combination is not supported
+        self.recycle = recycle if not any(list(recycle.values())) else False
 
     def write_to_net(self):
         """


Test patch: