Skip to main content

Machine learning for power flow

Project description

MLPF is a python library for (optimal) power flow calculations with machine learning.

It offers a few main features such as:

:chart_with_downwards_trend: Efficient loss functions compatible with both PyTorch and scikit-learn .

💱 Conversion functions to go from PPCs to NumPy arrays or Torch tensors.


As well as some optional high level utilities:

ðŸ—‚ï¸ Data classes that make it easy to go from PPCs to custom (or PyTorch Geometric ) data objects with all the info needed for (O)PF extracted.

📊 Metrics specific to (O)PF for logging and evaluation.

🔎 Visualization and description tools to take a quick look at your data.

Contributions welcome!

Installation

pip install mlpf

The previous command will install all the dependencies for working with numpy and scikit-learn. It will not, however, install all the dependencies needed for working with torch. To use the torch functionalities, please install PyTorch, PyTorch Geometric with its dependencies(torch-scatter etc.) and optionally TorchMetrics.

Installation directly from the master branch:

pip install git+https://github.com/viktor-ktorvi/mlpf.git

Basic usage

:cd: :file_folder: Load your data in the PYPOWER case format.

:open_file_folder: :arrow_heading_up: Extract powers, voltages, limits and cost coefficients from the PPCs into either NumPy arrays or torch tensors.

ðŸ‹ðŸ»â€â™€ï¸ :chart_with_downwards_trend: Plug the data into your machine learning models.

:chart_with_upwards_trend: :bar_chart: Use the loss functions to evaluate your results or to train your models(the torch versions of the loss functions are differentiable!).

In depth examples

Power flow Optimal power flow
Data extraction and loss
Supervised learning
Unsupervised learning

Quick start

Power flow

ppc
import copy

import pandapower as pp
import pandapower.networks as pn

from pypower.ppoption import ppoption
from pypower.runpf import runpf

net = pn.case118()

ppc = pp.converter.to_ppc(net, init="flat")

ppopt = ppoption(OUT_ALL=0, VERBOSE=0)
ppc, converged = runpf(copy.deepcopy(ppc), ppopt=ppopt)
NumPy/scikit-learn
import numpy as np

from mlpf.data.conversion.numpy.power_flow import (
    extract_line_arrays,
    extract_power_arrays,
    extract_voltage_arrays
)

from mlpf.loss.numpy.power_flow import (
    active_power_errors,
    reactive_power_errors
)

# extract quantities
active_powers, reactive_powers = extract_power_arrays(ppc)
voltage_magnitudes, voltage_angles = extract_voltage_arrays(ppc)
edge_index, conductances, susceptances = extract_line_arrays(ppc)

active_errors = active_power_errors(edge_index, active_powers, voltage_magnitudes, voltage_angles, conductances, susceptances)
reactive_errors = reactive_power_errors(edge_index, reactive_powers, voltage_magnitudes, voltage_angles, conductances, susceptances)

print(f"Total P loss = {np.sum(active_errors):.3e} p.u.")
print(f"Total Q loss = {np.sum(reactive_errors):.3e} p.u.")
Torch
import torch

from mlpf.data.conversion.torch.power_flow import (
    extract_line_tensors,
    extract_power_tensors,
    extract_voltage_tensors
)
from mlpf.loss.torch.power_flow import (
    active_power_errors,
    reactive_power_errors
)

# extract quantities
# note: going from float64 to float32(the standard in torch) will increase the PF loss significantly
active_powers, reactive_powers = extract_power_tensors(ppc, dtype=torch.float64)
voltage_magnitudes, voltage_angles = extract_voltage_tensors(ppc, dtype=torch.float64)
edge_index, conductances, susceptances = extract_line_tensors(ppc, dtype=torch.float64)

active_errors = active_power_errors(edge_index, active_powers, voltage_magnitudes, voltage_angles, conductances, susceptances)
reactive_errors = reactive_power_errors(edge_index, reactive_powers, voltage_magnitudes, voltage_angles, conductances, susceptances)

print(f"Total P loss = {torch.sum(active_errors):.3e} p.u.")
print(f"Total Q loss = {torch.sum(reactive_errors):.3e} p.u.")

Optimal power flow

ppc
import copy

import pandapower as pp
import pandapower.networks as pn

from pypower.ppoption import ppoption
from pypower.runopf import runopf

net = pn.case118()
ppc = pp.converter.to_ppc(net, init="flat")

ppopt = ppoption(OUT_ALL=0, VERBOSE=0)
ppc = runopf(copy.deepcopy(ppc), ppopt=ppopt)
NumPy/scikit-learn
import numpy as np

from mlpf.data.conversion.numpy.optimal_power_flow import (
    extract_active_power_limits_arrays,
    extract_cost_coefficients_array,
    extract_demand_arrays,
    extract_reactive_power_limits_arrays,
    extract_voltage_limits_arrays,
)

from mlpf.data.conversion.numpy.power_flow import (
    extract_power_arrays,
    extract_voltage_arrays
)

from mlpf.loss.numpy.bound_errors import (
    lower_bound_errors,
    upper_bound_errors
)

from mlpf.loss.numpy.costs import polynomial_costs

# extract quantities
active_powers, reactive_powers = extract_power_arrays(ppc)
voltage_magnitudes, voltage_angles = extract_voltage_arrays(ppc)

voltages_min, voltages_max = extract_voltage_limits_arrays(ppc)
active_powers_min, active_powers_max = extract_active_power_limits_arrays(ppc)
reactive_powers_min, reactive_powers_max = extract_reactive_power_limits_arrays(ppc)

active_power_demands, _ = extract_demand_arrays(ppc)
active_powers_generation = (active_powers + active_power_demands) * ppc["baseMVA"]

cost_coefficients = extract_cost_coefficients_array(ppc)

# calculate errors
voltage_upper_errors = upper_bound_errors(voltage_magnitudes, voltages_max)
voltage_lower_errors = lower_bound_errors(voltage_magnitudes, voltages_min)

active_upper_errors = upper_bound_errors(active_powers, active_powers_max)
active_lower_errors = lower_bound_errors(active_powers, active_powers_min)

reactive_upper_errors = upper_bound_errors(reactive_powers, reactive_powers_max)
reactive_lower_errors = lower_bound_errors(reactive_powers, reactive_powers_min)

cost_errors = np.sum(polynomial_costs(active_powers_generation, cost_coefficients)) - ppc["f"]

print(f"Total V max violation = {np.sum(voltage_upper_errors):.3e} p.u.")
print(f"Total V min violation = {np.sum(voltage_lower_errors):.3e} p.u.")

print(f"Total P max violation = {np.sum(active_upper_errors):.3e} p.u.")
print(f"Total P min violation = {np.sum(active_lower_errors):.3e} p.u.")

print(f"Total Q max violation = {np.sum(reactive_upper_errors):.3e} p.u.")
print(f"Total Q min violation = {np.sum(reactive_lower_errors):.3e} p.u.")

print(f"Total costs = {cost_errors:.3e} $/h")
Torch
import torch

from mlpf.data.conversion.torch.optimal_power_flow import (
    extract_active_power_limits_tensors,
    extract_cost_coefficients_tensor,
    extract_demand_tensors,
    extract_reactive_power_limits_tensors,
    extract_voltage_limits_tensors,
)

from mlpf.data.conversion.torch.power_flow import (
    extract_power_tensors,
    extract_voltage_tensors
)

from mlpf.loss.torch.bound_errors import (
    lower_bound_errors,
    upper_bound_errors
)

from mlpf.loss.torch.costs import polynomial_costs

# extract quantities
active_powers, reactive_powers = extract_power_tensors(ppc, dtype=torch.float64)
voltage_magnitudes, voltage_angles = extract_voltage_tensors(ppc, dtype=torch.float64)

voltages_min, voltages_max = extract_voltage_limits_tensors(ppc, dtype=torch.float64)
active_powers_min, active_powers_max = extract_active_power_limits_tensors(ppc, dtype=torch.float64)
reactive_powers_min, reactive_powers_max = extract_reactive_power_limits_tensors(ppc, dtype=torch.float64)

active_power_demands, _ = extract_demand_tensors(ppc, dtype=torch.float64)
active_powers_generation = (active_powers + active_power_demands) * ppc["baseMVA"]

cost_coefficients = extract_cost_coefficients_tensor(ppc, dtype=torch.float64)

# calculate errors
voltage_upper_errors = upper_bound_errors(voltage_magnitudes, voltages_max)
voltage_lower_errors = lower_bound_errors(voltage_magnitudes, voltages_min)

active_upper_errors = upper_bound_errors(active_powers, active_powers_max)
active_lower_errors = lower_bound_errors(active_powers, active_powers_min)

reactive_upper_errors = upper_bound_errors(reactive_powers, reactive_powers_max)
reactive_lower_errors = lower_bound_errors(reactive_powers, reactive_powers_min)

cost_errors = torch.sum(polynomial_costs(active_powers_generation, cost_coefficients)) - ppc["f"]

print(f"Total V max violation = {torch.sum(voltage_upper_errors):.3e} p.u.")
print(f"Total V min violation = {torch.sum(voltage_lower_errors):.3e} p.u.")

print(f"Total P max violation = {torch.sum(active_upper_errors):.3e} p.u.")
print(f"Total P min violation = {torch.sum(active_lower_errors):.3e} p.u.")

print(f"Total Q max violation = {torch.sum(reactive_upper_errors):.3e} p.u.")
print(f"Total Q min violation = {torch.sum(reactive_lower_errors):.3e} p.u.")

print(f"Total costs = {cost_errors:.3e} $/h")

Development

git clone https://github.com/viktor-ktorvi/mlpf.git
cd mlpf

conda env create -f environment.yml
conda activate mlpfenv

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mlpf-0.0.9.tar.gz (45.2 kB view hashes)

Uploaded Source

Built Distribution

mlpf-0.0.9-py3-none-any.whl (54.1 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page