Module ml4opf.formulations

ML4OPF Formulations

Sub-modules

ml4opf.formulations.ac

ACOPF

ml4opf.formulations.dc

DCOPF

ml4opf.formulations.ed

Economic Dispatch

ml4opf.formulations.model

Abstract base class for ML models for OPF …

ml4opf.formulations.problem

Abstract base class for each OPF problem …

ml4opf.formulations.soc

SOCOPF

ml4opf.formulations.violation

Abstract base class for OPFViolation classes …

Classes

class ACModel

OPFModel for ACOPF

Ancestors

Subclasses

Class variables

var problemACProblem
var violationACViolation

Methods

def evaluate_model(self, reduction: str | None = None, inner_reduction: str | None = None) ‑> dict[str, torch.Tensor]

Evaluate the model on the test data.

Args

reduction : str, optional
Reduction method for the metrics. Defaults to None. Must be one of "mean", "sum","max", "none". If specified, each value in the returned dictionary will be a scalar. Otherwise, they are arrays of shape (n_test_samples,)
inner_reduction : str, optional
Reduction method for turning metrics calculated per component to per sample. Defaults to None. Must be one of "mean", "sum","max", "none".

Returns

dict[str, Tensor]

Dictionary containing Tensor metrics of the model's performance.

vm_lower: Lower bound on the voltage magnitude.

vm_upper: Upper bound on the voltage magnitude.

pg_lower: Lower bound on the real power generation.

pg_upper: Upper bound on the real power generation.

qg_lower: Lower bound on the reactive power generation.

qg_upper: Upper bound on the reactive power generation.

thrm_1: Thermal limit violation from

thrm_2: Thermal limit violation to

p_balance: Active power balance violation.

q_balance: Reactive power balance violation.

pg_mae: Mean absolute error of the real power generation.

qg_mae: Mean absolute error of the reactive power generation.

vm_mae: Mean absolute error of the voltage magnitude.

va_mae: Mean absolute error of the voltage angle. (if not bus-wise and va not in predictions, skipped)

dva_mae: Mean absolute error of the angle difference. (only if not bus-wise)

obj_mape: Mean absolute percent error of the objective value.

def predict(self, pd: torch.Tensor, qd: torch.Tensor) ‑> dict[str, torch.Tensor]

Predict the ACOPF primal solution for a given set of loads.

Args

pd : Tensor
Active power demand per load.
qd : Tensor
Reactive power demand per load.

Returns

dict[str, Tensor]

Dictionary containing the predicted primal solution.

pg: Active power generation per generator or per bus.

qg: Reactive power generation per generator or per bus.

vm: Voltage magnitude per bus.

va: Voltage angle per bus.

Inherited members

class ACProblem (data_directory: str, dataset_name: str = 'ACOPF', **parse_kwargs)

OPFProblem for ACOPF

Ancestors

Instance variables

prop default_combos : dict[str, list[str]]

Default combos for ACOPF:

  • input: pd, qd

  • target: pg, qg, vm, va

prop default_order : list[str]

Default order for ACOPF: input, target

prop feasibility_check : dict[str, str]

Default feasibility check for ACOPF:

  • termination_status: "LOCALLY_SOLVED"

  • primal_status: "FEASIBLE_POINT"

  • dual_status: "FEASIBLE_POINT"

prop violationACViolation

ACPViolation object, created upon first access.

Inherited members

class ACViolation (data: dict[str, torch.Tensor])

OPFViolation for ACOPF

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

Methods

def angle_difference(self, va: torch.Tensor) ‑> torch.Tensor

Compute the angle differences per branch given the voltage angles per bus.

\text{dva} = \boldsymbol{\theta}_{f} - \boldsymbol{\theta}_{t}

Args

va : Tensor
Voltage angles per bus ( \boldsymbol{\theta} ). (batch_size, nbus)

Returns

Tensor
Angle differences per branch. (batch_size, nbranch)
def balance_residual(self,
pd: torch.Tensor,
qd: torch.Tensor,
pg: torch.Tensor,
qg: torch.Tensor,
vm: torch.Tensor,
pf: torch.Tensor,
pt: torch.Tensor,
qf: torch.Tensor,
qt: torch.Tensor,
clamp: bool = False,
embed_method: str = 'pad') ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the power balance residual.

Component-wise tensors are first embedded to the bus level using embed_method.

The shunt parameters g_s, b_s are assumed to be constant, matching the reference case.

\text{p_viol} = \text{pg_bus} - \text{pd_bus} - \text{pt_bus} - \text{pf_bus} - \text{gs_bus} \times \text{vm}^2 \text{q_viol} = \text{qg_bus} - \text{qd_bus} - \text{qt_bus} - \text{qf_bus} + \text{bs_bus} \times \text{vm}^2

Args

pd : Tensor
Active power demand per bus. (batch_size, nbus)
qd : Tensor
Reactive power demand per bus. (batch_size, nbus)
pg : Tensor
Active power generation per generator. (batch_size, ngen)
qg : Tensor
Reactive power generation per generator. (batch_size, ngen)
vm : Tensor
Voltage magnitude per bus. (batch_size, nbus)
pf : Tensor
Active power flow from bus per branch. (batch_size, nbranch)
pt : Tensor
Active power flow to bus per branch. (batch_size, nbranch)
qf : Tensor
Reactive power flow from bus per branch. (batch_size, nbranch)
qt : Tensor
Reactive power flow to bus per branch. (batch_size, nbranch)
clamp : bool, optional
Apply an absolute value to the residual. Defaults to False.
embed_method : str, optional
Embedding method for bus-level components. Defaults to 'pad'. Must be one of 'pad', 'dense_matrix', or 'matrix. See IncidenceMixin.*_to_bus.

Returns

Tensor
Power balance residual for active power. (batch_size, nbus)
Tensor
Power balance residual for reactive power. (batch_size, nbus)
def calc_violations(self,
pd: torch.Tensor,
qd: torch.Tensor,
pg: torch.Tensor,
qg: torch.Tensor,
vm: torch.Tensor,
va: torch.Tensor | None = None,
dva: torch.Tensor | None = None,
flows: tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
reduction: str | None = 'mean',
clamp: bool = True) ‑> dict[str, torch.Tensor]

Calculate the violation of all the constraints.

The reduction is applied across the component dimension - e.g., 'mean' will do violation.mean(dim=1) where each violation is (batch, components)

Args

pd : Tensor
Real power demand. (batch, loads)
qd : Tensor
Reactive power demand. (batch, loads)
pg : Tensor
Real power generation. (batch, gens)
qg : Tensor
Reactive power generation. (batch, gens)
vm : Tensor
Voltage magnitude. (batch, buses)
va : Tensor, optional
Voltage angle. (batch, buses)
dva : Tensor, optional
Voltage angle difference. (batch, branches)
flows : tuple[Tensor, Tensor, Tensor, Tensor], optional
Power flows. (pf, pt, qf, qt)
reduction : str, optional
Reduction method. Defaults to 'mean'. Must be one of 'mean', 'sum', 'none'.
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to True.

Returns

  • dict[str, Tensor]: Dictionary of violations.

vm_lower: Voltage magnitude lower bound violation.

vm_upper: Voltage magnitude upper bound violation.

pg_lower: Real power generation lower bound violation.

pg_upper: Real power generation upper bound violation.

qg_lower: Reactive power generation lower bound violation.

qg_upper: Reactive power generation upper bound violation.

thrm_1: Thermal limit from violation.

thrm_2: Thermal limit to violation.

p_balance: Real power balance violation.

q_balance: Reactive power balance violation.

dva_lower: Voltage angle difference lower bound violation.

dva_upper: Voltage angle difference upper bound violation.

def dva_bound_residual(self, dva: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the voltage angle difference bound residual.

g_{\text{lower}} = \text{angmin} - \text{dva} g_{\text{upper}} = \text{dva} - \text{angmax}

Args

dva : Tensor
Voltage angle difference per branch. (batch_size, nbranch)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, nbranch)
Tensor
Upper bound residual. (batch_size, nbranch)
def flows_from_voltage(self, vm: torch.Tensor, dva: torch.Tensor) ‑> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]

Compute the power flows given the voltage magnitude and angle differences.

Args

vm : Tensor
Voltage magnitude per bus ( \mathbf{v} ). (batch_size, nbus)
dva : Tensor
Angle differences per branch ( \boldsymbol{\theta}_f - \boldsymbol{\theta}_t ). (batch_size, nbranch)

Returns

Tensor
Real power flow per branch ( \mathbf{p}_f ). (batch_size, nbranch)
Tensor
Real power flow per branch ( \mathbf{p}_t ). (batch_size, nbranch)
Tensor
Reactive power flow per branch ( \mathbf{q}_f ). (batch_size, nbranch)
Tensor
Reactive power flow per branch ( \mathbf{q}_t ). (batch_size, nbranch)
def flows_from_voltage_bus(self, vm: torch.Tensor, va: torch.Tensor) ‑> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]

Compute the power flows given the voltage magnitude per bus and voltage angles per bus.

This function computes angle differences then calls ACPViolation.flows_from_voltage. See the docstring of ACPViolation.flows_from_voltage for more details.

Args

vm : Tensor
Voltage magnitude per bus ( \mathbf{v} ). (batch_size, nbus)
va : Tensor
Voltage angle per bus ( \boldsymbol{\theta} ). (batch_size, nbus)

Returns

Tensor
Real power flow per branch ( \mathbf{p}_f ). (batch_size, nbranch)
Tensor
Real power flow per branch ( \mathbf{p}_t ). (batch_size, nbranch)
Tensor
Reactive power flow per branch ( \mathbf{q}_f ). (batch_size, nbranch)
Tensor
Reactive power flow per branch ( \mathbf{q}_t ). (batch_size, nbranch)
def objective(self, pg: torch.Tensor) ‑> torch.Tensor

Compute the objective function given the active power generation per generator.

Args

pg : Tensor
Active power generation per generator. (batch_size, ngen)

Returns

Tensor
Objective function value. (batch_size)
def pg_bound_residual(self, pg: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the active power generation bound residual.

g_{\text{lower}} = \text{pmin} - \text{pg} g_{\text{upper}} = \text{pg} - \text{pmax}

Args

pg : Tensor
Active power generation per generator. (batch_size, ngen)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, ngen)
Tensor
Upper bound residual. (batch_size, ngen)
def qg_bound_residual(self, qg: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the reactive power generation bound residual.

g_{\text{lower}} = \text{qmin} - \text{qg} g_{\text{upper}} = \text{qg} - \text{qmax}

Args

qg : Tensor
Reactive power generation per generator. (batch_size, ngen)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, ngen)
Tensor
Upper bound residual. (batch_size, ngen)
def thermal_residual(self,
pf: torch.Tensor,
pt: torch.Tensor,
qf: torch.Tensor,
qt: torch.Tensor,
clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the thermal limit residual.

g_{\text{thrm}_1} = \text{pf}^2 + \text{qf}^2 - \text{s1max} g_{\text{thrm}_2} = \text{pt}^2 + \text{qt}^2 - \text{s2max}

Args

pf : Tensor
Active power flow from bus per branch. (batch_size, nbranch)
pt : Tensor
Active power flow to bus per branch. (batch_size, nbranch)
qf : Tensor
Reactive power flow from bus per branch. (batch_size, nbranch)
qt : Tensor
Reactive power flow to bus per branch. (batch_size, nbranch)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Thermal limit residual for from branch. (batch_size, nbranch)
Tensor
Thermal limit residual for to branch. (batch_size, nbranch)
def vm_bound_residual(self, vm: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the voltage magnitude bound residual.

g_{\text{lower}} = \text{vmin} - \text{vm} g_{\text{upper}} = \text{vm} - \text{vmax}

Args

vm : Tensor
Voltage magnitude per bus. (batch_size, nbus)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, nbus)
Tensor
Upper bound residual. (batch_size, nbus)

Inherited members

class DCModel

OPFModel for DCOPF

Ancestors

Subclasses

Class variables

var problemDCProblem
var violationDCViolation

Methods

def evaluate_model(self, reduction: str | None = None, inner_reduction: str | None = None) ‑> dict[str, torch.Tensor]

Evaluate the model on the test data.

Args

reduction : str, optional
Reduction method for the metrics. Defaults to None. Must be one of "mean", "sum","max", "none". If specified, each value in the returned dictionary will be a scalar. Otherwise, they are arrays of shape (n_test_samples,)
inner_reduction : str, optional
Reduction method for turning metrics calculated per component to per sample. Defaults to None. Must be one of "mean", "sum","max", "none".

Returns

dict[str, Tensor]

Dictionary containing Tensor metrics of the model's performance.

pg_lower: Generator lower bound violation.

pg_upper: Generator upper bound violation.

dva_lower: Angle difference limit lower bound violation.

dva_upper: Angle difference limit upper bound violation.

pf_lower: Flow limit lower bound violation.

pf_upper: Flow limit upper bound violation.

p_balance: Power balance violation.

pg_mae: Mean absolute error of the real power generation.

va_mae: Mean absolute error of the voltage angle. (if not bus-wise and va not in predictions, skipped)

pf_mae: Mean absolute error of the real power flow.

obj_mape: Mean absolute percent error of the objective value.

def predict(self, pd: torch.Tensor) ‑> dict[str, torch.Tensor]

Predict the DCOPF primal solution for a given set of loads.

Args

pd : Tensor
Active power demand per load.

Returns

dict[str, Tensor]

Dictionary containing the predicted primal solution.

pg: Active power generation per generator or per bus.

va: Voltage angle per bus.

Inherited members

class DCProblem (data_directory: str, dataset_name: str = 'DCOPF', **parse_kwargs)

OPFProblem for DCOPF

Ancestors

Instance variables

prop default_combos : dict[str, list[str]]

Default combos for DCOPF:

  • input: pd

  • target: pg, va

prop default_order : list[str]

Default order for DCOPF. input, target

prop feasibility_check : dict[str, str]

Default feasibility check for DCOPF:

  • termination_status: "OPTIMAL"

  • primal_status: "FEASIBLE_POINT"

  • dual_status: "FEASIBLE_POINT"

prop violationDCViolation

OPFViolation object for DCOPF constraint calculations.

Inherited members

class DCViolation (data: dict[str, torch.Tensor])

OPFViolation for DCPPowerModel/DCOPF

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

Methods

def angle_difference(self, va: torch.Tensor) ‑> torch.Tensor

Compute the angle differences per branch given the voltage angles per bus.

The branch indices are assumed to be constant for the batch, matching the reference case.

\text{dva} = \boldsymbol{\theta}_{f} - \boldsymbol{\theta}_{t}

Args

va : Tensor
Voltage angles per bus ( \boldsymbol{\theta} ). (batch_size, nbus)

Returns

Tensor
Angle differences per branch. (batch_size, nbranch)
def balance_residual(self,
pd: torch.Tensor,
pg: torch.Tensor,
pf: torch.Tensor,
embed_method: str = 'pad',
clamp: bool = True) ‑> torch.Tensor

Compute power balance residual.

\text{g_balance} = \text{pg_bus} - \text{pd_bus} - \text{gs_bus} - \text{pf_bus} - \text{pt_bus}

Args

pd : Tensor
Power demand per bus. (batch_size, nbus)
pg : Tensor
Power generation per generator. (batch_size, ngen)
pf : Tensor
Power flow per branch. (batch_size, nbranch)
embed_method : str, optional
Embedding method to convert component-wise values to bus-wise – one of "pad", "dense_matrix", or "matrix". Defaults to "pad".
clamp : bool, optional
Clamp to extract only violations. Defaults to True.

Returns

Tensor
Power balance residual. (batch_size, nbus)
def calc_violations(self,
pd: torch.Tensor,
pg: torch.Tensor,
va: torch.Tensor,
pf: torch.Tensor | None = None,
reduction: str = 'mean',
clamp: bool = True,
embed_method: str = 'pad') ‑> dict[str, torch.Tensor]

Compute all DCOPF violations.

Args

pd : Tensor
Power demand per bus. (batch_size, nbus)
pg : Tensor
Power generation per generator. (batch_size, ngen)
va : Tensor
Voltage angles per bus. (batch_size, nbus)
pf : Tensor, optional
Power flow per branch. Defaults to None.
reduction : str, optional
Reduction method. Defaults to "mean".
clamp : bool, optional
Clamp to extract only violations. Defaults to True.
embed_method : str, optional
Method to convert component-wise values to bus-wise – one of "pad", "dense_matrix", or "matrix". Defaults to "pad".

Returns

dict[str, Tensor]
Dictionary of all violations:
  • "pg_lower": Lower bound violation of power generation. (batch_size, ngen)

  • "pg_upper": Upper bound violation of power generation. (batch_size, ngen)

  • "dva_lower": Lower bound violation of voltage angle difference. (batch_size, nbranch)

  • "dva_upper": Upper bound violation of voltage angle difference. (batch_size, nbranch)

  • "pf_lower": Lower bound violation of power flow. (batch_size, nbranch)

  • "pf_upper": Upper bound violation of power flow. (batch_size, nbranch)

  • "p_balance": Power balance violation. (batch_size, nbus)

  • "ohm": Ohm's law violation. (batch_size, nbranch)

def dva_bound_residual(self, dva: torch.Tensor, clamp: bool = False) ‑> torch.Tensor

Calculate the voltage angle difference bound residual.

g_{\text{lower}} = \text{angmin} - \text{dva} g_{\text{upper}} = \text{dva} - \text{angmax}

Args

dva : Tensor
Voltage angle differences per branch. (batch_size, nbranch)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, nbranch)
Tensor
Upper bound residual. (batch_size, nbranch)
def objective(self, pg: torch.Tensor) ‑> torch.Tensor

Compute DCOPF objective function.

Cost is assumed to be constant for the batch, matching the reference case.

\text{objective} = \sum_i^n \text{cost}_{2,i} + \text{cost}_{1,i} \cdot \text{pg}_i

Args

pg : Tensor
Power generation per generator. (batch_size, ngen)

Returns

Tensor
Objective function value. (batch_size, 1)
def ohm_residual(self, pf: torch.Tensor, dva: torch.Tensor, clamp: bool = False) ‑> torch.Tensor

Compute Ohm's law violation.

\text{g_ohm} = - b \cdot \text{dva} - \text{pf}

Args

pf : Tensor
Power flow per branch. (batch_size, nbranch)
dva : Tensor
Voltage angle differences per branch. (batch_size, nbranch)
clamp : bool, optional
Clamp to extract only violations. Defaults to False.

Returns

Tensor
Ohm's law violation. (batch_size, nbranch)
def pf_bound_residual(self, pf: torch.Tensor, clamp: bool = False) ‑> torch.Tensor

Calculate the power flow bound residual.

g_{\text{lower}} = -\text{rate_a} - \text{pf} g_{\text{upper}} = \text{pf} - \text{rate_a}

Args

pf : Tensor
Power flow per branch. (batch_size, nbranch)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, nbranch)
Tensor
Upper bound residual. (batch_size, nbranch)
def pf_from_va(self, va: torch.Tensor) ‑> torch.Tensor

Compute power flow given voltage angles.

\mathbf{p}_f = -\text{b} \cdot (\boldsymbol{\theta}_{f} - \boldsymbol{\theta}_{t})

Args

va : Tensor
Voltage angles per bus ( \boldsymbol{\theta} ). (batch_size, nbus)

Returns

Tensor
Power flow per branch ( \mathbf{p}_f ). (batch_size, nbranch)
def pg_bound_residual(self, pg: torch.Tensor, clamp: bool = False) ‑> torch.Tensor

Calculate the power generation bound residual.

g_{\text{lower}} = \text{pmin} - \text{pg} g_{\text{upper}} = \text{pg} - \text{pmax}

Args

pg : Tensor
Active power generation per generator. (batch_size, ngen)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, ngen)
Tensor
Upper bound residual. (batch_size, ngen)

Inherited members

class EDModel

OPFModel for EconomicDispatch

Ancestors

Subclasses

Class variables

var problemEDProblem
var violationEDViolation

Methods

def evaluate_model(self, reduction: str | None = None, inner_reduction: str | None = None) ‑> dict[str, torch.Tensor]

Evaluate the model on the test data.

Args

reduction : str, optional
Reduction method for the metrics. Defaults to None. Must be one of "mean", "sum","max", "none". If specified, each value in the returned dictionary will be a scalar. Otherwise, they are arrays of shape (n_test_samples,)
inner_reduction : str, optional
Reduction method for turning metrics calculated per component to per sample. Defaults to None. Must be one of "mean", "sum","max", "none".

Returns

dict[str, Tensor]

Dictionary containing Tensor metrics of the model's performance.

pg_lower: Generator lower bound violation.

pg_upper: Generator upper bound violation.

pf_lower: Branch power flow lower bound violation.

pf_upper: Branch power flow upper bound violation.

p_balance: Power balance violation.

pg_mae: Mean absolute error of the real power generation.

obj_mape: Mean absolute percent error of the objective value.

Inherited members

class EDProblem (data_directory: str, ptdf_path: str, dataset_name: str = 'ED', **parse_kwargs)

OPFProblem for EconomicDispatch

Ancestors

Instance variables

prop default_combos : dict[str, list[str]]

Default combos for EconomicDispatch. input: pd, target: pg, va

prop default_order : list[str]

Default order for EconomicDispatch. input, target

prop feasibility_check : dict[str, str]

Default feasibility check for EconomicDispatch.

prop violationEDViolation

OPFViolation object for EconomicDispatch constraint calculations.

Inherited members

class EDViolation (data: dict, ptdf: torch.Tensor)

OPFViolation for EconomicDispatch

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

Methods

def balance_residual(self,
pd: torch.Tensor,
pg: torch.Tensor,
dpb_surplus: torch.Tensor | None = None,
dpb_shortage: torch.Tensor | None = None,
clamp: bool = False) ‑> torch.Tensor

Compute power balance residual.

def calc_violations(self,
pd: torch.Tensor,
pg: torch.Tensor,
reduction: str = 'mean',
clamp: bool = True) ‑> dict[str, torch.Tensor]

Compute all EconomicDispatch violations.

def objective(self, pd: torch.Tensor, pg: torch.Tensor) ‑> torch.Tensor

Compute ED objective function.

def pf_bound_residual(self, pf: torch.Tensor, df: torch.Tensor | None = None, clamp: bool = False) ‑> torch.Tensor

Compute power flow bound residual.

def pf_from_pdpg(self, pd: torch.Tensor, pg: torch.Tensor, dense_incidence: bool = False) ‑> torch.Tensor

Compute power flow from power demand and power generation.

def pg_bound_residual(self, pg: torch.Tensor, clamp: bool = False) ‑> torch.Tensor

Compute power generation bound residual.

Inherited members

class OPFModel

An abstract base class for ACOPF models.

Ancestors

  • abc.ABC

Subclasses

Static methods

def load_from_checkpoint(path_to_folder: str,
problem: OPFProblem)

Load the model's checkpoint from a file.

Args

path : str
Path to load the checkpoint from.

Methods

def evaluate_model(self, reduction: str | None = None, inner_reduction: str | None = None) ‑> dict[str, torch.Tensor]

Evaluate the model on the test data.

Args

reduction : str, optional
Reduction method for the metrics. Defaults to None. Must be one of "mean", "sum", "none". If specified, each value in the returned dictionary will be a scalar. Otherwise, they are arrays of shape (n_test_samples,)

Returns

dict[str, Tensor]
Dictionary containing Tensor metrics of the model's performance.
def predict(self, *inputs: torch.Tensor) ‑> dict[str, torch.Tensor]

Predict the solution for a given set of inputs.

Args

*inputs : Tensor
Input tensors to the model.

Returns

dict[str, Tensor]
Dictionary containing the solution.
def save_checkpoint(self, path_to_folder: str)

Save the model's checkpoint to a file.

Args

path : str
Path to save the checkpoint.
class OPFProblem (data_directory: str, dataset_name: str, **parse_kwargs)

OPF Problem

This class parses the JSON/HDF5 files on initialization, providing a standard interface for accessing OPF data.

OPFProblem also includes methods for creating input/target tensors from the HDF5 data by concatenating keys, though more complex datasets (e.g., for graph neural networks) can be created by accessing train_data and json_data directly.

By default, initializing OPFProblem will parse the HDF5/JSON files, remove infeasible samples, and set aside 5000 samples for testing. The test data can be accessed via test_data - train_data will only contain the training data. Models should split the training data into training/validation sets themselves downstream.

Initialization Arguments:

  • data_directory (str): Path to the folder containing the problem files

  • dataset_name (str): Name of the problem to use

  • primal (bool): Whether to parse the primal data (default: True)

  • dual (bool): Whether to parse the dual data (default: True)

  • train_set (bool): Whether to parse the training set (default: True)

  • test_set (bool): Whether to parse the test set (default: True)

  • convert_to_float32 (bool): Whether to convert the data to float32 (default: True)

  • sanity_check (bool): Whether to perform a sanity check on the parsed data (default: True)

Attributes:

  • path (Path): Path to the problem file folder

  • name (str): Name of the problem to use

  • train_data (dict): Dictionary of parsed HDF5 data. If make_test_set is True, this is only the training set.

  • test_data (dict): Dictionary of parsed HDF5 data for the test set. If make_test_set is False, this is None.

  • json_data (dict): Dictionary of parsed JSON data.

  • ml4opf.formulations.violation (OPFViolation): OPFViolation object for computing constraint violations for this problem.

Methods:

  • parse: Parse the JSON and HDF5 files for the problem

  • make_dataset: Create input/target tensors by combining keys from the h5 data. Returns the TensorDataset and slices for extracting the original components.

  • slice_batch: Extract the original components from a batch of data given the slices.

  • slice_tensor: Extract the original components from a tensor given the slices.

Ancestors

  • abc.ABC

Subclasses

Static methods

def slice_batch(batch: tuple[torch.Tensor, ...], slices: list[dict[str, slice]])

Slice the batch tensors into the original tensors

Args

batch : tuple[Tensor, …]
Batch of tensors from the TensorDataset
slices : list[dict[str, slice]]
List of dictionaries of slices

Returns

tuple[dict[str, Tensor], …]
Sliced tensors
def slice_tensor(tensor: torch.Tensor, slices: dict[str, slice])

Slice the tensor into the original tensors

Args

tensor : Tensor
Tensor to slice
slices : dict[str, slice]
Dictionary of slices

Returns

dict[str, Tensor]: Sliced tensors

Instance variables

prop default_combos : dict[str, list[str]]

A dictionary where keys represent elements of the tuple from the TensorDataset and values are keys of the train_data dictionary which are concatenated. Used by make_dataset.

prop default_order : list[str]

The order of the keys in the default_combos dictionary.

prop feasibility_check : dict[str, str]

Dictionary of keys and values to check feasibility of the problem.

Each key is checked to have the corresponding value. If any of them does not match, the sample is removed from the dataset in PGLearnParser. See ACOPFProblem.feasibility_check for an example.

Methods

def make_dataset(self,
combos: dict[str, list[str]] | None = None,
order: list[str] | None = None,
data: dict[str, torch.Tensor] | None = None,
test_set: bool = False,
sanity_check: bool = True) ‑> tuple[dict[str, torch.Tensor], list[dict[str, slice]]]

Make a TensorDataset from self.train_data given the keys in combos and the order of the keys in order.

def parse(self,
primal: bool = True,
dual: bool = True,
train_set: bool = True,
test_set: bool = True,
convert_to_float32: bool = True,
sanity_check: bool = True)

Parse the JSON and HDF5 files for the problem

class OPFViolation (data: dict[str, torch.Tensor])

The OPFViolation class is where all the problem expressions (objective, constraints, etc.) are defined. The classes provide a convenient interface for when the only varying quantity in the formulation per sample is the load demand pd. If other quantities vary, the user should use the functional interface at ml4opf.functional.

When clamp is true, the values of g(x) are clamped to be non-negative and the values of h(x) are absolute-valued. Otherwise the raw values are returned.

OPFViolation is a torch.nn.Module; all tensors used in computation are registered as non-persistent buffers. To move the module to a different device, use .to(device) as you would with any other nn.Module. Make sure that when you pass data to OPFViolation, it is on the same device as OPFViolation.

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

  • torch.nn.modules.module.Module
  • abc.ABC

Subclasses

Class variables

var SUPPORTED_REDUCTIONS

Static methods

def clamped_bound_residual(x: torch.Tensor, xmin: torch.Tensor, xmax: torch.Tensor, clamp: bool)
def reduce_violation(violation: torch.Tensor, reduction: str, dim: int = 1)

Apply mean/sum/max to a single tensor.

def reduce_violations(violations: dict[str, torch.Tensor], reduction: str, dim: int = 1)

Apply mean/sum/max to every value in a dictionary.

Instance variables

prop adjacency_matrix : torch.Tensor

Sparse adjacency matrix.

Each row corresponds to a bus and each column corresponds to a bus. The value is 1 if there is a branch between the buses, and 0 otherwise.

Args

fbus : Tensor
From bus indices. (nbranch,)
tbus : Tensor
To bus indices. (nbranch,)
n_bus : int
Number of buses.
n_branch : int
Number of branches.

Returns

Tensor
Sparse adjacency matrix. (nbus, nbus)
prop adjacency_matrix_dense : torch.Tensor
prop branch_from_incidence : torch.Tensor

Sparse branch from incidence matrix.

Each row corresponds to a bus and each column corresponds to a branch. The value is 1 if the branch is from the bus, and 0 otherwise.

Returns

Tensor
Sparse branch from incidence matrix. (nbus, nbranch)
prop branch_from_incidence_dense : torch.Tensor
prop branch_incidence : torch.Tensor

Sparse branch incidence matrix.

Each row corresponds to a bus and each column corresponds to a branch. The value is 1 if the branch is from the bus, -1 if the branch is to the bus, and 0 otherwise.

Returns

Tensor
Sparse branch incidence matrix. (nbus, nbranch)
prop branch_incidence_dense : torch.Tensor
prop branch_to_incidence : torch.Tensor

Sparse branch to incidence matrix.

Each row corresponds to a bus and each column corresponds to a branch. The value is 1 if the branch is to the bus, and 0 otherwise.

Returns

Tensor
Sparse branch to incidence matrix. (nbus, nbranch)
prop branch_to_incidence_dense : torch.Tensor
prop generator_incidence : torch.Tensor

Sparse generator incidence matrix.

Each row corresponds to a bus and each column corresponds to a generator. The value is 1 if the generator is at the bus, and 0 otherwise.

Returns

Tensor
Sparse generator incidence matrix. (nbus, ngen)
prop generator_incidence_dense : torch.Tensor
prop load_incidence : torch.Tensor

Sparse load incidence matrix.

Each row corresponds to a bus and each column corresponds to a load. The value is 1 if the load is at the bus, and 0 otherwise.

Returns

Tensor
Sparse load incidence matrix. (nbus, nload)
prop load_incidence_dense : torch.Tensor

Methods

def branch_from_to_bus(self, pf_or_qf: torch.Tensor, method: str = 'pad') ‑> torch.Tensor

Embed batched branch-wise values to batched bus-wise.

The default method "pad" sums over any flows on branches from the same bus. The matrix methods "dense_matrix" and "matrix" use the incidence matrix.

Args

pf_or_qf : Tensor
Branch-wise values. (batch, nbranch)
method : str
Method to use. Supported: ['pad', 'dense_matrix', 'matrix']

Returns

Tensor
Bus-wise values. (batch, nbus)
def branch_to_to_bus(self, pt_or_qt: torch.Tensor, method: str = 'pad') ‑> torch.Tensor

Embed batched branch-wise values to batched bus-wise.

The default method "pad" sums over any flows on branches to the same bus. The matrix methods "dense_matrix" and "matrix" use the incidence matrix.

Args

pt_or_qt : Tensor
Branch-wise values. (batch, nbranch)
method : str
Method to use. Supported: ['pad', 'dense_matrix', 'matrix']

Returns

Tensor
Bus-wise values. (batch, nbus)
def calc_violations(self, *args, reduction: str = 'mean', clamp: bool = True) ‑> dict[str, torch.Tensor]

Calculate the violations of the constraints. Returns a dictionary of tensors.

def forward(self, *args, **kwargs) ‑> Callable[..., Any]
def gen_to_bus(self, pg_or_qg: torch.Tensor, method: str = 'pad') ‑> torch.Tensor

Embed generator-wise values to bus-wise.

The default method "pad" sums over any generators at the same bus. The matrix methods "dense_matrix" and "matrix" use the incidence matrix.

Args

pg_or_qg : Tensor
Generator-wise values. (batch, ngen)
method : str
Method to use. Supported: ['pad', 'dense_matrix', 'matrix']

Returns

Tensor
Bus-wise values. (batch, nbus)
def load_to_bus(self, pd_or_qd: torch.Tensor, method: str = 'pad') ‑> torch.Tensor

Embed load-wise values to bus-wise.

The default method "pad" sums over any loads at the same bus. The matrix methods "dense_matrix" and "matrix" use the incidence matrix.

Args

pd_or_qd : Tensor
Load-wise values. (batch, ngen)
method : str
Method to use. Supported: ['pad', 'dense_matrix', 'matrix']

Returns

Tensor
Bus-wise values. (batch, nbus)
def objective(self, *args) ‑> torch.Tensor

Compute the objective value for a batch of samples.

def violation_shapes(self) ‑> dict[str, int]

Return the shapes of the violations returned by OPFViolation.calc_violations().

class SOCModel

OPFModel for SOCOPF

Ancestors

Subclasses

Class variables

var problemSOCProblem
var violationSOCViolation

Methods

def predict(self, pd: torch.Tensor, qd: torch.Tensor) ‑> dict[str, torch.Tensor]

Predict the SOCOPF primal solution for a given set of loads.

Args

pd : Tensor
Active power demand per load.
qd : Tensor
Reactive power demand per load.

Returns

dict[str, Tensor]

Dictionary containing the predicted primal solution.

pg: Active power generation per generator or per bus.

qg: Reactive power generation per generator or per bus.

w: Squared voltage magnitude per bus.

wr: Real part of the voltage phasor.

wi: Imaginary part of the voltage phasor.

Inherited members

class SOCProblem (data_directory: str, dataset_name: str = 'SOCOPF', **parse_kwargs)

OPFProblem for SOCOPF

Ancestors

Instance variables

prop default_combos : dict[str, list[str]]

Default combos for SOCOPF:

  • input: pd, qd

  • target: pg, qg, w, wr, wi

prop default_order : list[str]

Default order for SOCOPF: input, target

prop feasibility_check : dict[str, str]

Default feasibility check for SOCOPF:

  • termination_status: "LOCALLY_SOLVED"

  • primal_status: "FEASIBLE_POINT"

  • dual_status: "FEASIBLE_POINT"

prop violationSOCViolation

SOCViolation object, created upon first access.

Inherited members

class SOCViolation (data: dict)

OPFViolation for SOCOPF

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

Methods

def angle_difference_residual(self, wr: torch.Tensor, wi: torch.Tensor, clamp: bool = False) ‑> torch.Tensor

Compute the angle difference bound residual.

def balance_residual(self,
pd: torch.Tensor,
qd: torch.Tensor,
pg: torch.Tensor,
qg: torch.Tensor,
w: torch.Tensor,
pf: torch.Tensor,
pt: torch.Tensor,
qf: torch.Tensor,
qt: torch.Tensor,
clamp: bool = False,
embed_method: str = 'pad') ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the power balance residual.

Component-wise tensors are first embedded to the bus level using embed_method.

The shunt parameters g_s, b_s are assumed to be constant, matching the reference case.

\text{p_viol} = \text{pg_bus} - \text{pd_bus} - \text{pt_bus} - \text{pf_bus} - \text{gs_bus} \times \text{vm}^2 \text{q_viol} = \text{qg_bus} - \text{qd_bus} - \text{qt_bus} - \text{qf_bus} + \text{bs_bus} \times \text{vm}^2

Args

pd : Tensor
Active power demand per bus. (batch_size, nbus)
qd : Tensor
Reactive power demand per bus. (batch_size, nbus)
pg : Tensor
Active power generation per generator. (batch_size, ngen)
qg : Tensor
Reactive power generation per generator. (batch_size, ngen)
vm : Tensor
Voltage magnitude per bus. (batch_size, nbus)
pf : Tensor
Active power flow from bus per branch. (batch_size, nbranch)
pt : Tensor
Active power flow to bus per branch. (batch_size, nbranch)
qf : Tensor
Reactive power flow from bus per branch. (batch_size, nbranch)
qt : Tensor
Reactive power flow to bus per branch. (batch_size, nbranch)
clamp : bool, optional
Apply an absolute value to the residual. Defaults to False.
embed_method : str, optional
Embedding method for bus-level components. Defaults to 'pad'. Must be one of 'pad', 'dense_matrix', or 'matrix. See IncidenceMixin.*_to_bus.

Returns

Tensor
Power balance residual for active power. (batch_size, nbus)
Tensor
Power balance residual for reactive power. (batch_size, nbus)
def calc_violations(self,
pd: torch.Tensor,
qd: torch.Tensor,
pg: torch.Tensor,
qg: torch.Tensor,
w: torch.Tensor,
wr: torch.Tensor,
wi: torch.Tensor,
flows: tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
reduction: str | None = 'mean',
clamp: bool = True) ‑> dict[str, torch.Tensor]

Calculate the violation of all the constraints.

The reduction is applied across the component dimension - e.g., 'mean' will do violation.mean(dim=1) where each violation is (batch, components)

Args

pd : Tensor
Real power demand. (batch, loads)
qd : Tensor
Reactive power demand. (batch, loads)
pg : Tensor
Real power generation. (batch, gens)
qg : Tensor
Reactive power generation. (batch, gens)
vm : Tensor
Voltage magnitude. (batch, buses)
va : Tensor, optional
Voltage angle. (batch, buses)
dva : Tensor, optional
Voltage angle difference. (batch, branches)
flows : tuple[Tensor, Tensor, Tensor, Tensor], optional
Power flows. (pf, pt, qf, qt)
reduction : str, optional
Reduction method. Defaults to 'mean'. Must be one of 'mean', 'sum', 'none'.
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to True.

Returns

  • dict[str, Tensor]: Dictionary of violations.

vm_lower: Voltage magnitude lower bound violation.

vm_upper: Voltage magnitude upper bound violation.

pg_lower: Real power generation lower bound violation.

pg_upper: Real power generation upper bound violation.

qg_lower: Reactive power generation lower bound violation.

qg_upper: Reactive power generation upper bound violation.

thrm_1: Thermal limit from violation.

thrm_2: Thermal limit to violation.

p_balance: Real power balance violation.

q_balance: Reactive power balance violation.

dva_lower: Voltage angle difference lower bound violation.

dva_upper: Voltage angle difference upper bound violation.

def flows_from_voltage(self, w: torch.Tensor, wr: torch.Tensor, wi: torch.Tensor) ‑> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]

Compute the power flows.

Returns

Tensor
Real power flow per branch ( \mathbf{p}_f ). (batch_size, nbranch)
Tensor
Real power flow per branch ( \mathbf{p}_t ). (batch_size, nbranch)
Tensor
Reactive power flow per branch ( \mathbf{q}_f ). (batch_size, nbranch)
Tensor
Reactive power flow per branch ( \mathbf{q}_t ). (batch_size, nbranch)
def jabr_residual(self, w: torch.Tensor, wr: torch.Tensor, wi: torch.Tensor, clamp: bool = False) ‑> torch.Tensor

Compute the Jabr constraint residual.

g_{\text{jabr}} = \text{wr}^2 + \text{wi}^2 - \text{w}_{fr} * \text{w}_{to}

Args

w : Tensor
Squared voltage magnitude per bus. (batch_size, nbus)
wr : Tensor
Real part of the voltage phasor. (batch_size, nbus)
wi : Tensor
Imaginary part of the voltage phasor. (batch_size, nbus)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Jabr constraint residual. (batch_size, nbus)
def objective(self, pg: torch.Tensor) ‑> torch.Tensor

Compute the objective function given the active power generation per generator.

Args

pg : Tensor
Active power generation per generator. (batch_size, ngen)

Returns

Tensor
Objective function value. (batch_size)
def pg_bound_residual(self, pg: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the active power generation bound residual.

g_{\text{lower}} = \text{pmin} - \text{pg} g_{\text{upper}} = \text{pg} - \text{pmax}

Args

pg : Tensor
Active power generation per generator. (batch_size, ngen)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, ngen)
Tensor
Upper bound residual. (batch_size, ngen)
def qg_bound_residual(self, qg: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the reactive power generation bound residual.

g_{\text{lower}} = \text{qmin} - \text{qg} g_{\text{upper}} = \text{qg} - \text{qmax}

Args

qg : Tensor
Reactive power generation per generator. (batch_size, ngen)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Lower bound residual. (batch_size, ngen)
Tensor
Upper bound residual. (batch_size, ngen)
def thermal_residual(self,
pf: torch.Tensor,
pt: torch.Tensor,
qf: torch.Tensor,
qt: torch.Tensor,
clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the thermal limit residual.

g_{\text{thrm}_1} = \text{pf}^2 + \text{qf}^2 - \text{s1max} g_{\text{thrm}_2} = \text{pt}^2 + \text{qt}^2 - \text{s2max}

Args

pf : Tensor
Active power flow from bus per branch. (batch_size, nbranch)
pt : Tensor
Active power flow to bus per branch. (batch_size, nbranch)
qf : Tensor
Reactive power flow from bus per branch. (batch_size, nbranch)
qt : Tensor
Reactive power flow to bus per branch. (batch_size, nbranch)
clamp : bool, optional
Clamp the residual to be non-negative (extract violations). Defaults to False.

Returns

Tensor
Thermal limit residual for from branch. (batch_size, nbranch)
Tensor
Thermal limit residual for to branch. (batch_size, nbranch)
def w_bound_residual(self, w: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the bound residual of w.

Returns

Tensor
Lower bound residual. (batch_size, nbranch)
Tensor
Upper bound residual. (batch_size, nbranch)
def wi_bound_residual(self, wr: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the bound residual of wi.

Returns

Tensor
Lower bound residual. (batch_size, nbranch)
Tensor
Upper bound residual. (batch_size, nbranch)
def wr_bound_residual(self, wr: torch.Tensor, clamp: bool = False) ‑> tuple[torch.Tensor, torch.Tensor]

Calculate the bound residual of wr.

Returns

Tensor
Lower bound residual. (batch_size, nbranch)
Tensor
Upper bound residual. (batch_size, nbranch)

Inherited members