Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
162 commits
Select commit Hold shift + click to select a range
131e2b4
Wip
geofjamg Mar 16, 2025
9c817cc
Wip
geofjamg Mar 16, 2025
a1fa9c7
Wip
geofjamg Mar 16, 2025
bbf1a97
Fix
geofjamg Mar 18, 2025
c6b4beb
Compare with OLF
geofjamg Mar 18, 2025
6ce1609
Wip
geofjamg Mar 18, 2025
938a519
Get tap phase shift from transformer dataframes
geofjamg Apr 16, 2025
d74fc7f
Merge branch 'main' into transfo_alpha
HugoKulesza Apr 16, 2025
a65257e
Add same reactive limits to generators, VSCs and batteries
geofjamg Apr 16, 2025
818be2c
Refactor OPF functionality into structured module
geofjamg Apr 17, 2025
c0bdb48
Wip
geofjamg Apr 17, 2025
50c495b
Wip
geofjamg Apr 17, 2025
0d05b07
Wip
geofjamg Apr 17, 2025
0cee9a6
Wip
geofjamg Apr 17, 2025
4560630
Wip
geofjamg Apr 19, 2025
b3ffa9d
Merge branch 'main' into opf_proto
geofjamg Apr 19, 2025
39b4dff
Merge branch 'transfo_alpha' into opf_proto
geofjamg Apr 19, 2025
23aedd2
Wip
geofjamg Apr 19, 2025
32417b6
Wip
geofjamg Apr 19, 2025
6bec168
Use losses as a cost function
geofjamg Apr 22, 2025
2b567f4
Support open branches
geofjamg Apr 22, 2025
01b9742
Fix
geofjamg Apr 23, 2025
4bcc4fe
Wip
geofjamg Apr 23, 2025
3c17a40
Wip
geofjamg Apr 23, 2025
2c7ec9d
Wip
geofjamg Apr 23, 2025
ef1cd08
Wip
geofjamg Apr 23, 2025
fca76e4
Wip
geofjamg Apr 23, 2025
0923684
Logs variables bounds
geofjamg Apr 23, 2025
0d09d87
Use min_q_at_target_p instead of min_q
geofjamg Apr 23, 2025
9459517
Wip
geofjamg Apr 24, 2025
e1583e1
Add typing
geofjamg Apr 24, 2025
80573dd
Add more logs
geofjamg Apr 24, 2025
80ce8fa
Fix
geofjamg Apr 24, 2025
2d57991
Fix
geofjamg Apr 24, 2025
0218943
Clean
geofjamg Apr 24, 2025
d9c6387
Clean
geofjamg Apr 24, 2025
35a858f
Clean
geofjamg Apr 24, 2025
e3408c1
Clean
geofjamg Apr 24, 2025
c64a169
Wip
geofjamg Apr 26, 2025
1477b8a
Wip
geofjamg Apr 26, 2025
b5f4fa4
Wip
geofjamg Apr 26, 2025
c384b8b
Wip
geofjamg Apr 26, 2025
28e7e37
Wip
geofjamg Apr 27, 2025
4aee3ab
Merge branch 'main' into min_max_q_target_p
geofjamg Apr 27, 2025
fdd6bbc
Merge branch 'main' into opf_proto
geofjamg Apr 27, 2025
b3a5600
Merge branch 'main' into min_max_q_target_p
geofjamg Apr 28, 2025
722d0a5
Merge branch 'main' into min_max_q_target_p
geofjamg Apr 29, 2025
c4301d3
Fix Sonar issues
geofjamg Apr 29, 2025
7696865
Merge branch 'main' into opf_proto
geofjamg Apr 29, 2025
6d18980
Wip
geofjamg Apr 29, 2025
01b2ab6
Wip
geofjamg Apr 29, 2025
55d6d45
Add InjectionBounds
geofjamg May 2, 2025
3fd4f32
Refactor
geofjamg May 2, 2025
8e75840
Refactor
geofjamg May 2, 2025
825f78e
Refactor
geofjamg May 4, 2025
314f331
Refactor
geofjamg May 4, 2025
271c637
Refactor
geofjamg May 4, 2025
97cf85c
Refactor
geofjamg May 4, 2025
65b49c1
Refactor
geofjamg May 4, 2025
a73d713
Refactor
geofjamg May 4, 2025
e07ea64
Add CostFunction
geofjamg May 4, 2025
37f1b1c
Refactoring
geofjamg May 4, 2025
d28b604
fix r,x,g,b for transformers
geofjamg May 4, 2025
d5a0ca8
Refactoring
geofjamg May 4, 2025
83a8b2a
Filter on main CC
geofjamg May 4, 2025
a675316
Refactoring
geofjamg May 4, 2025
e70d199
Refactoring
geofjamg May 4, 2025
41bd7d8
Refactoring
geofjamg May 4, 2025
5209204
Add SVC
geofjamg May 5, 2025
eb8b526
Update SVC
geofjamg May 5, 2025
35be06a
Merge branch 'main' into min_max_q_target_p
geofjamg May 5, 2025
299a6ef
Merge branch 'min_max_q_target_p' into opf_proto
geofjamg May 5, 2025
780debd
VSC converter stations
geofjamg May 7, 2025
0ab419a
hvdc line dataframe
geofjamg May 8, 2025
c328f76
Wip
geofjamg May 8, 2025
3c345bc
Add is_rectifier
geofjamg May 10, 2025
7021546
Fix HVDC
geofjamg May 10, 2025
afb64ee
Update p q
geofjamg May 11, 2025
9109d7e
Do not optimize generators with small reactive range
geofjamg May 11, 2025
8093bc3
Add twt_split_shunt_admittance option
geofjamg May 14, 2025
0cd2f6d
Update branch flow
geofjamg May 14, 2025
980c158
Clean
geofjamg May 14, 2025
e9ffc96
Better state update
geofjamg May 18, 2025
e29b632
Add tests
geofjamg May 18, 2025
9212080
Fix SVC limits
geofjamg May 18, 2025
8f80337
Load dangling lines
geofjamg May 18, 2025
df9c694
Remove useless variables
geofjamg May 18, 2025
9d8d82e
Revert
geofjamg May 18, 2025
2983ef7
Ac
geofjamg May 18, 2025
168766e
Ac
geofjamg May 18, 2025
acb1fe6
Refactoring
geofjamg May 18, 2025
fcf0d6a
Refactoring
geofjamg May 18, 2025
f9a2ef4
Refactoring
geofjamg May 18, 2025
9709df1
Refactoring
geofjamg May 18, 2025
fe03f06
Refactoring
geofjamg May 19, 2025
94fc012
Wip
geofjamg May 19, 2025
a47fcc2
Merge branch 'main' into opf_proto
geofjamg May 19, 2025
3e7699a
Refactoring
geofjamg May 20, 2025
e64f2c1
Refactoring
geofjamg May 20, 2025
7afa57d
Add dangling line to model
geofjamg May 20, 2025
899c35e
Fix
geofjamg May 20, 2025
07c6c4e
Merge branch 'main' into opf_proto
geofjamg May 23, 2025
e45f5ce
Update dangling lines
geofjamg May 25, 2025
b6fcb48
Load transfos 3
geofjamg May 25, 2025
cdf6972
Quad -> linear
geofjamg May 25, 2025
a936129
Add target v to cost
geofjamg May 26, 2025
5be5e4e
Wip
geofjamg May 27, 2025
2702b14
Refactoring
geofjamg May 27, 2025
52ea9e6
Fix
geofjamg May 27, 2025
8fde02a
Wip
geofjamg May 27, 2025
e7e65f3
Merge branch 'main' into opf_proto
geofjamg Jun 2, 2025
4a169c0
Fix g b perunit
geofjamg Jun 2, 2025
fd9f0fb
Fix transformer gb_at_current_tap per-uniting
geofjamg Jun 3, 2025
1c6bd5e
Merge branch 'fix_transfo_perunit' into opf_proto
geofjamg Jun 3, 2025
6409182
Fix Vsc p update
geofjamg Jun 3, 2025
3249988
Fix hvdc
geofjamg Jun 3, 2025
f6ee490
Clean
geofjamg Jun 3, 2025
0bdd29c
Add 3w transformers
geofjamg Jun 4, 2025
58771fb
Refactoring
geofjamg Jun 5, 2025
a4b0943
Add t3 balance
geofjamg Jun 5, 2025
69f61c8
Clean
geofjamg Jun 5, 2025
315011c
Refactoring
geofjamg Jun 5, 2025
38eca6c
Fix t3 peruniting
geofjamg Jun 6, 2025
c31bdd2
t3 v, angle of middle point
geofjamg Jun 6, 2025
70d71b5
Add micro grid nl test case
geofjamg Jun 6, 2025
af2293c
Add time measure
geofjamg Jun 6, 2025
014c1a4
Wip
geofjamg Jun 7, 2025
45a0a94
Fix
geofjamg Jun 8, 2025
450635d
Fix shunt active power
geofjamg Jun 9, 2025
7c3b3b0
Merge branch 'main' into opf_proto
geofjamg Jun 10, 2025
9d9c4b9
Add current limit constraints
geofjamg Jun 10, 2025
60afce3
Improve current limit constraints
geofjamg Jun 15, 2025
d3b7813
Add redispatching cost function
geofjamg Jun 15, 2025
f3b83e1
Fix loadflow parameters
geofjamg Jun 19, 2025
5fe9069
Merge branch 'main' into opf_proto
geofjamg Sep 3, 2025
891b16d
Merge branch 'main' into opf_proto
geofjamg Sep 3, 2025
f6339e1
Merge branch 'main' into opf_proto
geofjamg Sep 20, 2025
d0868ba
Fix unit test
geofjamg Sep 20, 2025
5bd3af8
Migrate to pyopt 0.5.0
geofjamg Sep 20, 2025
721edf4
Migrate to pyopt 0.5.1
geofjamg Sep 21, 2025
ec56d5f
Merge branch 'main' into opf_proto
geofjamg Oct 13, 2025
a5b04dc
Wip
geofjamg Oct 13, 2025
aaefacb
Add VoltageRegulation
geofjamg Oct 13, 2025
cc9dfe5
Read batteries
geofjamg Oct 13, 2025
b7d896d
Wip
geofjamg Oct 13, 2025
78d36ce
Battery voltage regulation extension support
geofjamg Oct 14, 2025
4027107
Merge branch 'voltage_regulation_extension' into opf_proto
geofjamg Oct 14, 2025
f2d623f
Wip
geofjamg Oct 14, 2025
b9d1a39
Wip
geofjamg Oct 14, 2025
63195df
Wip
geofjamg Oct 15, 2025
2575f1a
Wip
geofjamg Oct 15, 2025
c619e96
Wip
geofjamg Oct 15, 2025
c104006
Wip
geofjamg Oct 17, 2025
f013e48
Wip
geofjamg Oct 17, 2025
254b45b
Wip
geofjamg Oct 17, 2025
10e93a0
Wip
geofjamg Oct 18, 2025
e2603d3
Merge branch 'main' into opf_proto
geofjamg Nov 5, 2025
f1417df
Wip
geofjamg Nov 5, 2025
a75ddd1
Merge branch 'main' into opf_proto
geofjamg Nov 7, 2025
b5e2979
Merge remote-tracking branch 'origin/opf_proto' into opf_proto
geofjamg Nov 10, 2025
6671bcb
Adapt to new operational limits dataframe
geofjamg Nov 10, 2025
8a426ac
Merge branch 'main' into opf_proto
geofjamg Nov 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cpp/powsybl-cpp/powsybl-cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ std::shared_ptr<sensitivity_analysis_parameters> SensitivityAnalysisParameters::
delete ptr;
});
}

void SensitivityAnalysisParameters::load_to_c_struct(sensitivity_analysis_parameters& params) const {
params.flow_flow_sensitivity_value_threshold = flow_flow_sensitivity_value_threshold;
params.voltage_voltage_sensitivity_value_threshold = voltage_voltage_sensitivity_value_threshold;
Expand Down
6 changes: 4 additions & 2 deletions pypowsybl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
grid2op,
rao,
report,
voltage_initializer
voltage_initializer,
opf
)
from pypowsybl.network import per_unit_view

Expand All @@ -48,7 +49,8 @@
"grid2op",
"dynamic",
"rao",
"report"
"report",
"opf"
]


Expand Down
1 change: 1 addition & 0 deletions pypowsybl/opf/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .impl.opf import OptimalPowerFlow, run_ac
Empty file added pypowsybl/opf/impl/__init__.py
Empty file.
Empty file.
29 changes: 29 additions & 0 deletions pypowsybl/opf/impl/bounds/battery_power_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import logging

from pyoptinterface import ipopt

from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_bounds import VariableBounds
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.bounds import Bounds
from pypowsybl.opf.impl.model.network_cache import NetworkCache
from pypowsybl.opf.impl.util import TRACE_LEVEL

logger = logging.getLogger(__name__)

class BatteryPowerBounds(VariableBounds):
def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model):
# battery active and reactive power bounds
for bat_num, row in enumerate(network_cache.batteries.itertuples()):
if row.bus_id:
p_bounds = Bounds(row.min_p, row.max_p).mirror()
logger.log(TRACE_LEVEL, f"Add active power bounds {p_bounds} to battery '{row.Index}' (num={bat_num})")
bat_p_index = variable_context.bat_p_num_2_index[bat_num]
model.set_variable_bounds(variable_context.bat_p_vars[bat_p_index], *Bounds.fix(row.Index, p_bounds.min_value, p_bounds.max_value))

bat_q_index = variable_context.bat_q_num_2_index[bat_num]
if bat_q_index != -1: # valid
q_bounds = Bounds.get_reactive_power_bounds(row).reduce(parameters.reactive_bounds_reduction).mirror()
logger.log(TRACE_LEVEL, f"Add reactive power bounds {q_bounds} to battery '{row.Index}' (num={bat_num})")
model.set_variable_bounds(variable_context.bat_q_vars[bat_q_index], *Bounds.fix(row.Index, q_bounds.min_value, q_bounds.max_value))
23 changes: 23 additions & 0 deletions pypowsybl/opf/impl/bounds/bus_voltage_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import logging

from pyoptinterface import ipopt

from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_bounds import VariableBounds
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.bounds import Bounds
from pypowsybl.opf.impl.model.network_cache import NetworkCache
from pypowsybl.opf.impl.util import TRACE_LEVEL

logger = logging.getLogger(__name__)


class BusVoltageBounds(VariableBounds):
def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model):
for bus_num, row in enumerate(network_cache.buses.itertuples()):
v_bounds = Bounds.get_voltage_bounds(row.low_voltage_limit, row.high_voltage_limit, parameters.default_voltage_bounds)
logger.log(TRACE_LEVEL, f"Add voltage magnitude bounds {v_bounds} to bus '{row.Index}' (num={bus_num})'")
model.set_variable_bounds(variable_context.v_vars[bus_num],
*Bounds.fix(row.Index, v_bounds.min_value, v_bounds.max_value))
model.set_variable_start(variable_context.v_vars[bus_num], 1.0)
25 changes: 25 additions & 0 deletions pypowsybl/opf/impl/bounds/dangling_line_voltage_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import logging

from pyoptinterface import ipopt

from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_bounds import VariableBounds
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.bounds import Bounds
from pypowsybl.opf.impl.model.network_cache import NetworkCache
from pypowsybl.opf.impl.util import TRACE_LEVEL

logger = logging.getLogger(__name__)


class DanglingLineVoltageBounds(VariableBounds):
def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model):
for dl_num, row in enumerate(network_cache.dangling_lines.itertuples()):
if row.bus_id:
v_bounds = Bounds.get_voltage_bounds(None, None, parameters.default_voltage_bounds)
logger.log(TRACE_LEVEL, f"Add voltage magnitude bounds {v_bounds} to dangling line bus '{row.Index}' (num={dl_num})'")
dl_index = variable_context.dl_num_2_index[dl_num]
model.set_variable_bounds(variable_context.dl_v_vars[dl_index],
*Bounds.fix(row.Index, v_bounds.min_value, v_bounds.max_value))
model.set_variable_start(variable_context.dl_v_vars[dl_index], 1.0)
29 changes: 29 additions & 0 deletions pypowsybl/opf/impl/bounds/generator_power_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import logging

from pyoptinterface import ipopt

from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_bounds import VariableBounds
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.bounds import Bounds
from pypowsybl.opf.impl.model.network_cache import NetworkCache
from pypowsybl.opf.impl.util import TRACE_LEVEL

logger = logging.getLogger(__name__)

class GeneratorPowerBounds(VariableBounds):
def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model):
# generator active and reactive power bounds
for gen_num, row in enumerate(network_cache.generators.itertuples()):
if row.bus_id:
p_bounds = Bounds(row.min_p, row.max_p).mirror()
logger.log(TRACE_LEVEL, f"Add active power bounds {p_bounds} to generator '{row.Index}' (num={gen_num})")
gen_p_index = variable_context.gen_p_num_2_index[gen_num]
model.set_variable_bounds(variable_context.gen_p_vars[gen_p_index], *Bounds.fix(row.Index, p_bounds.min_value, p_bounds.max_value))

gen_q_index = variable_context.gen_q_num_2_index[gen_num]
if gen_q_index != -1: # valid
q_bounds = Bounds.get_reactive_power_bounds(row).reduce(parameters.reactive_bounds_reduction).mirror()
logger.log(TRACE_LEVEL, f"Add reactive power bounds {q_bounds} to generator '{row.Index}' (num={gen_num})")
model.set_variable_bounds(variable_context.gen_q_vars[gen_q_index], *Bounds.fix(row.Index, q_bounds.min_value, q_bounds.max_value))
20 changes: 20 additions & 0 deletions pypowsybl/opf/impl/bounds/slack_bus_angle_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import logging

from pyoptinterface import ipopt

from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_bounds import VariableBounds
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.network_cache import NetworkCache
from pypowsybl.opf.impl.util import TRACE_LEVEL

logger = logging.getLogger(__name__)

class SlackBusAngleBounds(VariableBounds):
def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model):
# slack bus angle forced to 0
slack_bus_id = network_cache.slack_terminal.iloc[0].bus_id if len(network_cache.slack_terminal) > 0 else network_cache.buses.iloc[0].name
slack_bus_num = network_cache.buses.index.get_loc(slack_bus_id)
model.set_variable_bounds(variable_context.ph_vars[slack_bus_num], 0.0, 0.0)
logger.info(f"Angle reference is at bus '{slack_bus_id}' (num={slack_bus_num})")
25 changes: 25 additions & 0 deletions pypowsybl/opf/impl/bounds/transformer_3w_middle_voltage_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import logging

from pyoptinterface import ipopt

from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_bounds import VariableBounds
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.bounds import Bounds
from pypowsybl.opf.impl.model.network_cache import NetworkCache
from pypowsybl.opf.impl.util import TRACE_LEVEL

logger = logging.getLogger(__name__)


class Transformer3wMiddleVoltageBounds(VariableBounds):
def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model):
for t3_num, t3_row in enumerate(network_cache.transformers_3w.itertuples()):
if t3_row.bus1_id or t3_row.bus2_id or t3_row.bus3_id:
v_bounds = Bounds.get_voltage_bounds(None, None, parameters.default_voltage_bounds)
logger.log(TRACE_LEVEL, f"Add voltage magnitude bounds {v_bounds} to 3 windings transformer middle '{t3_row.Index}' (num={t3_num})'")
t3_index = variable_context.t3_num_2_index[t3_num]
model.set_variable_bounds(variable_context.t3_middle_v_vars[t3_index],
*Bounds.fix(t3_row.Index, v_bounds.min_value, v_bounds.max_value))
model.set_variable_start(variable_context.t3_middle_ph_vars[t3_index], 1.0)
34 changes: 34 additions & 0 deletions pypowsybl/opf/impl/bounds/vsc_cs_power_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import logging

from pyoptinterface import ipopt

from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_bounds import VariableBounds
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.bounds import Bounds
from pypowsybl.opf.impl.model.network_cache import NetworkCache
from pypowsybl.opf.impl.util import TRACE_LEVEL

logger = logging.getLogger(__name__)

class VscCsPowerBounds(VariableBounds):
def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model):
# VSC converter station active and reactive power bounds
for vsc_cs_num, row in enumerate(network_cache.vsc_converter_stations.itertuples()):
if row.bus_id:
p_bounds = Bounds(-row.max_p, row.max_p).mirror()
logger.log(TRACE_LEVEL,
f"Add active power bounds {p_bounds} to VSC converter station '{row.Index}' (num={vsc_cs_num})")
model.set_variable_bounds(variable_context.vsc_cs_p_vars[vsc_cs_num],
*Bounds.fix(row.Index, p_bounds.min_value, p_bounds.max_value))

q_bounds = Bounds.get_reactive_power_bounds(row).reduce(
parameters.reactive_bounds_reduction).mirror()
logger.log(TRACE_LEVEL,
f"Add reactive power bounds {q_bounds} to VSC converter station '{row.Index}' (num={vsc_cs_num})")
if abs(q_bounds.max_value - q_bounds.min_value) < 1.0 / network_cache.network.nominal_apparent_power:
logger.error(
f"Too small reactive power bounds {q_bounds} for VSC converter station '{row.Index}' (num={vsc_cs_num})")
model.set_variable_bounds(variable_context.vsc_cs_q_vars[vsc_cs_num],
*Bounds.fix(row.Index, q_bounds.min_value, q_bounds.max_value))
Empty file.
137 changes: 137 additions & 0 deletions pypowsybl/opf/impl/constraints/branch_flow_constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from math import hypot, atan2

from pyoptinterface import ipopt, nl

from pypowsybl.opf.impl.model.constraints import Constraints
from pypowsybl.opf.impl.model.model_parameters import ModelParameters
from pypowsybl.opf.impl.model.variable_context import VariableContext
from pypowsybl.opf.impl.model.network_cache import NetworkCache

R2 = 1.0
A2 = 0.0

class BranchFlowConstraints(Constraints):
@staticmethod
def add_closed_branch_constraint(a1: float, b1: float, b2: float, g1: float, g2: float, ksi: float, model, p1_var, p2_var, ph1_var,
ph2_var, q1_var, q2_var, r1: float, v1_var, v2_var, y: float):
with nl.graph():
sin_ksi = nl.sin(ksi)
cos_ksi = nl.cos(ksi)
theta1 = ksi - a1 + A2 - ph1_var + ph2_var
theta2 = ksi + a1 - A2 + ph1_var - ph2_var
sin_theta1 = nl.sin(theta1)
cos_theta1 = nl.cos(theta1)
sin_theta2 = nl.sin(theta2)
cos_theta2 = nl.cos(theta2)

p1_eq = r1 * v1_var * (g1 * r1 * v1_var + y * r1 * v1_var * sin_ksi - y * R2 * v2_var * sin_theta1) - p1_var
q1_eq = r1 * v1_var * (-b1 * r1 * v1_var + y * r1 * v1_var * cos_ksi - y * R2 * v2_var * cos_theta1) - q1_var
p2_eq = R2 * v2_var * (g2 * R2 * v2_var - y * r1 * v1_var * sin_theta2 + y * R2 * v2_var * sin_ksi) - p2_var
q2_eq = R2 * v2_var * (-b2 * R2 * v2_var - y * r1 * v1_var * cos_theta2 + y * R2 * v2_var * cos_ksi) - q2_var

model.add_nl_constraint(p1_eq == 0.0)
model.add_nl_constraint(q1_eq == 0.0)
model.add_nl_constraint(p2_eq == 0.0)
model.add_nl_constraint(q2_eq == 0.0)

@staticmethod
def add_open_side1_branch_constraint(b1: float, b2: float, g1: float, g2: float, ksi: float, model, p2_var, q2_var, v2_var,
y: float):
with nl.graph():
sin_ksi = nl.sin(ksi)
cos_ksi = nl.cos(ksi)

shunt = (g1 + y * sin_ksi) * (g1 + y * sin_ksi) + (-b1 + y * cos_ksi) * (-b1 + y * cos_ksi)
p2_eq = R2 * R2 * v2_var * v2_var * (
g2 + y * y * g1 / shunt + (b1 * b1 + g1 * g1) * y * sin_ksi / shunt) - p2_var
q2_eq = -R2 * R2 * v2_var * v2_var * (
b2 + y * y * b1 / shunt - (b1 * b1 + g1 * g1) * y * cos_ksi / shunt) - q2_var

model.add_nl_constraint(p2_eq == 0.0)
model.add_nl_constraint(q2_eq == 0.0)

@staticmethod
def add_open_side2_branch_constraint(b1: float, b2: float, g1: float, g2: float, ksi: float, model, p1_var, q1_var,
r1: float, v1_var, y: float):
with nl.graph():
sin_ksi = nl.sin(ksi)
cos_ksi = nl.cos(ksi)

shunt = (g2 + y * sin_ksi) * (g2 + y * sin_ksi) + (-b2 + y * cos_ksi) * (-b2 + y * cos_ksi)
p1_eq = r1 * r1 * v1_var * v1_var * (
g1 + y * y * g2 / shunt + (b2 * b2 + g2 * g2) * y * sin_ksi / shunt) - p1_var
q1_eq = -r1 * r1 * v1_var * v1_var * (
b1 + y * y * b2 / shunt - (b2 * b2 + g2 * g2) * y * cos_ksi / shunt) - q1_var

model.add_nl_constraint(p1_eq == 0.0)
model.add_nl_constraint(q1_eq == 0.0)

@staticmethod
def add_branch_constraint(branch_index: int, bus1_id: str, bus2_id: str, network_cache: NetworkCache, model,
r: float, x: float, g1: float, b1: float, g2: float, b2: float, r1: float, a1: float,
variable_context: VariableContext) -> None:
z = hypot(r, x)
y = 1.0 / z
ksi = atan2(r, x)

if bus1_id and bus2_id:
bus1_num = network_cache.buses.index.get_loc(bus1_id)
bus2_num = network_cache.buses.index.get_loc(bus2_id)
v1_var = variable_context.v_vars[bus1_num]
v2_var = variable_context.v_vars[bus2_num]
ph1_var = variable_context.ph_vars[bus1_num]
ph2_var = variable_context.ph_vars[bus2_num]
p1_var = variable_context.closed_branch_p1_vars[branch_index]
q1_var = variable_context.closed_branch_q1_vars[branch_index]
p2_var = variable_context.closed_branch_p2_vars[branch_index]
q2_var = variable_context.closed_branch_q2_vars[branch_index]

BranchFlowConstraints.add_closed_branch_constraint(a1, b1, b2, g1, g2, ksi, model, p1_var, p2_var,
ph1_var, ph2_var, q1_var, q2_var, r1, v1_var, v2_var, y)
elif bus2_id:
bus2_num = network_cache.buses.index.get_loc(bus2_id)
v2_var = variable_context.v_vars[bus2_num]
p2_var = variable_context.open_side1_branch_p2_vars[branch_index]
q2_var = variable_context.open_side1_branch_q2_vars[branch_index]

BranchFlowConstraints.add_open_side1_branch_constraint(b1, b2, g1, g2, ksi, model, p2_var, q2_var, v2_var, y)
elif bus1_id:
bus1_num = network_cache.buses.index.get_loc(bus1_id)
v1_var = variable_context.v_vars[bus1_num]
p1_var = variable_context.open_side2_branch_p1_vars[branch_index]
q1_var = variable_context.open_side2_branch_q1_vars[branch_index]

BranchFlowConstraints.add_open_side2_branch_constraint(b1, b2, g1, g2, ksi, model, p1_var, q1_var, r1,
v1_var, y)

def add(self, parameters: ModelParameters, network_cache: NetworkCache,
variable_context: VariableContext, model: ipopt.Model) -> None:
for branch_num, row in enumerate(network_cache.lines.itertuples(index=False)):
r, x, g1, b1, g2, b2 = row.r, row.x, row.g1, row.b1, row.g2, row.b2
r1 = 1.0
a1 = 0.0
branch_index = variable_context.branch_num_2_index[branch_num]
self.add_branch_constraint(branch_index, row.bus1_id, row.bus2_id, network_cache, model,
r, x, g1, b1, g2, b2, r1, a1,
variable_context)

for transfo_num, row in enumerate(network_cache.transformers_2w.itertuples(index=True)):
r, x, g, b, rho, alpha = row.r_at_current_tap, row.x_at_current_tap, row.g_at_current_tap, row.b_at_current_tap, row.rho, row.alpha
if parameters.twt_split_shunt_admittance:
g1 = g / 2
g2 = g / 2
b1 = b / 2
b2 = b / 2
else:
g1 = g
g2 = 0
b1 = b
b2 = 0
r1 = rho
a1 = alpha
branch_num = len(network_cache.lines) + transfo_num
branch_index = variable_context.branch_num_2_index[branch_num]
self.add_branch_constraint(branch_index, row.bus1_id, row.bus2_id, network_cache, model,
r, x, g1, b1, g2, b2, r1, a1,
variable_context)

Loading
Loading