e2nIEE / pandapower

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

OPF - unexpected results with 2 generators on the same bus #1901

Closed MS132000 closed 1 year ago

MS132000 commented 1 year ago

Hi, I have read the documentation but I found this case where the OPF seems not to be working. I built a network with 2 generators, one being much cheaper than the other, connected to the same bus . I expected that the expensive generator does not generate anything but nevertheless, with the OPF I get that they generate exactly the same (I attached a summary with all the results evaluated at 05:00 a.m.). I appreciate any help to understand why I got these results.

OPF Example - two generators with different cost_RESULTS.pdf

Originally posted by @MS132000 in https://github.com/e2nIEE/pandapower/issues/1361#issuecomment-1461003083

LukasKur commented 1 year ago

Maybe it´s due to the negative prices (- 200 €/MW)? Did you try it out with e.g. 1 €/MW for the cheaper gen?

MS132000 commented 1 year ago

@LukasKur thanks for the answer, I tried to simulate with all the prices positive but I still get the same results

rbolgaryn commented 1 year ago

Hi @MS132000 ,

what are the max/min constraints?

Can you please post the complete code snippet from your example?

MS132000 commented 1 year ago

Hi @rbolgaryn , thanks for the reply.

the constraints are as follows:

I send attached a zip file with the Jupiter simplified code and an Excel file with the load profile(which is not controllable)

example_file.zip Load_Profile.xlsx

jwiemer112 commented 1 year ago

The OPF only starts to optimize if any constraints are violated. Maybe that's the reason why the OPF doesn't change anything.

Greez Jan

rbolgaryn commented 1 year ago

Sorry I won't be looking at the Excel file. Is the resulting power 200 MW?

MS132000 commented 1 year ago

The OPF only starts to optimize if any constraints are violated. Maybe that's the reason why the OPF doesn't change anything.

Greez Jan

but isn't it supposed that the pandapower OPF optimize regardless of the initial conditions of the generators?

On the other hand, I tried to make the constraints more flexible and the OPF gives me other results, but always generating the same thing with both generators (being the generation costs very different)

Also, I tried to start, as an initial condition of the OPF, with the results of the executed OPF, but both generators continue to provide the same power

MS132000 commented 1 year ago

Sorry I won't be looking at the Excel file. Is the resulting power 200 MW?

no, both generators start with p=0MW. Just set pmax=200MW so that the limiting constraint would not be on the generators

rbolgaryn commented 1 year ago

please add a minimal example in the comment

MS132000 commented 1 year ago

please add a minimal example in the comment

import pandapower as pp import numpy as np import os import pandas as pd import tempfile from pandapower.timeseries import DFData from pandapower.timeseries import OutputWriter from pandapower.timeseries.run_time_series import run_timeseries from pandapower.control import ConstControl import matplotlib.pyplot as plt import openpyxl.utils.cell import openpyxl from openpyxl.styles.alignment import Alignment %matplotlib inline

def simple_test_net(): net = pp.create_empty_network()

# LINES
b150 = pp.create_bus(net, 150, min_vm_pu=0.95, max_vm_pu=1.05, index=0)
b0 = pp.create_bus(net, 150, min_vm_pu=0.95, max_vm_pu=1.05, index=1)
b1 = pp.create_bus(net, 31.5, min_vm_pu=0.95, max_vm_pu=1.05, index=2)
b2 = pp.create_bus(net, 31.5, min_vm_pu=0.95, max_vm_pu=1.05, index=3)
b3 = pp.create_bus(net, 31.5, min_vm_pu=0.95, max_vm_pu=1.05, index=4) 

# TRAFO
trafo=pp.create_transformer_from_parameters(net, hv_bus=b0,
lv_bus=b1, sn_mva=63, vn_hv_kv=150, vn_lv_kv=31.5, vkr_percent=0, 

vk_percent=11.63, pfe_kw=26.108, i0_percent=0, shift_degree=0, tap_side="hv", tap_neutral=9, tap_max=17, tap_min=1, tap_step_percent=1.5, tap_step_degree=0, tap_pos=9, tap_phase_shifter=False, in_service=True, name="Trafo_TRA", vector_group=None, index=1, max_loading_percent=100, parallel=1, df=1.0, pt_percent=None, oltc=None, tap_dependent_impedance=None, vk_percent_characteristic=None, vkr_percent_characteristic=None, xn_ohm=None)

pp.create_line(net, b150, b0, 100, "149-AL1/24-ST1A 110.0", max_loading_percent=100,parallel=1)
pp.create_line(net, b1, b2, 10, "149-AL1/24-ST1A 110.0", max_loading_percent=100,parallel=1)
pp.create_line(net, b2, b3, 10, "149-AL1/24-ST1A 110.0", max_loading_percent=100,parallel=1)

# LOAD
pp.create_load(net, b2, p_mw=10., q_mvar=-5., name='load1', controllable=False)

# EXTERNAL GRID
e=pp.create_ext_grid(net, b150, min_p_mw=-200, max_p_mw=200,max_q_mvar=200, min_q_mvar=-200,controllable=True)
costeg = pp.create_poly_cost(net, e, 'ext_grid', cp1_eur_per_mw=500)#,cq1_eur_per_mvar=200)

# GENERATOR G1
g1=pp.create_sgen(net, b3, p_mw=0, q_mvar=0, min_p_mw=0, max_p_mw=200, min_q_mvar=-200, max_q_mvar=200, name='sgen1', controllable=True,index=0)
costo_g1=pp.create_poly_cost(net, g1, 'sgen1', cp1_eur_per_mw=200)#,cq1_eur_per_mvar=200)

# GENERATOR G2
g2=pp.create_sgen(net, b3, p_mw=0, q_mvar=0, min_p_mw=0, max_p_mw=200, min_q_mvar=-200, max_q_mvar=200, name='sgen2', controllable=True,index=1)
costo_g2=pp.create_poly_cost(net, g2, 'sgen2', cp1_eur_per_mw=1)#-200)#,cq1_eur_per_mvar=1)

return net

def create_output_writer(net, time_steps, output_dir): ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=".xlsx", log_variables=list()) ow.log_variable('res_sgen', 'p_mw') ow.log_variable('res_sgen', 'q_mvar') ow.log_variable('res_bus', 'vm_pu') ow.log_variable('res_line', 'loading_percent') ow.log_variable('res_line', 'i_ka')

ow.log_variable('res_line', 'p_from_mw') 
ow.log_variable('res_line', 'q_from_mvar') 
ow.log_variable('res_line', 'p_to_mw') 
ow.log_variable('res_line', 'q_to_mvar') 

ow.log_variable('res_load', 'p_mw')
ow.log_variable('res_load', 'q_mvar')
ow.log_variable("res_ext_grid", "p_mw")
ow.log_variable("res_ext_grid", "q_mvar")
ow.log_variable("res_trafo", "pl_mw") 
ow.log_variable("res_trafo", "ql_mvar") 
ow.log_variable('res_trafo', 'loading_percent')
ow.log_variable("res_trafo", "p_lv_mw") 
ow.log_variable("res_trafo", "q_lv_mvar") 

return ow

output_dir = os.path.abspath('C:/Results')

print("Results can be found in the folder folder: {}".format(output_dir)) if not os.path.exists(output_dir): os.mkdir(output_dir)

net = simple_test_net()

n_timesteps = 24

profile_p_load = pd.DataFrame() profile_q_load = pd.DataFrame()

Load_Profile = os.path.abspath('C:/Load_Profile.xlsx'')

DF_load_p_mw = pd.read_excel(Load_Profile, index_col=0) DF_load_q_mvar = pd.read_excel(Load_Profile, index_col=1)

profile_p_load['load1_p'] = DF_load_p_mw[0].tolist()
profile_q_load['load1_q'] = DF_load_q_mvar[1].tolist()

ds_p_load = DFData(profile_p_load) ds_q_load = DFData(profile_q_load)

ConstControl(net, element='load', variable='p_mw', element_index=[0], data_source=ds_p_load, profile_name=["load1_p"]) ConstControl(net, element='load', variable='q_mvar', element_index=[0], data_source=ds_q_load, profile_name=["load1_q"])

time_steps = range(0, n_timesteps)

ow = create_output_writer(net, time_steps, output_dir=output_dir)

run_timeseries(net, time_steps, run=pp.runopp, delta=1e-16)

MS132000 commented 1 year ago

Thank you very much @SteffenMeinecke !! (The question was answered in issue #1361 )