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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file mlpf-0.0.9.tar.gz
.
File metadata
- Download URL: mlpf-0.0.9.tar.gz
- Upload date:
- Size: 45.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.16
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 6704a5c9b1735a60d2598f4d1f82d602d190abb9245e5a061b6a182c0d7a2a85 |
|
MD5 | 55ad2ee90c0431c8fba09a55632a95e0 |
|
BLAKE2b-256 | 2c9466b2b1b1fc236a826e040a9795b3cfbc236f0a3f8f736f83af9199e1f669 |
File details
Details for the file mlpf-0.0.9-py3-none-any.whl
.
File metadata
- Download URL: mlpf-0.0.9-py3-none-any.whl
- Upload date:
- Size: 54.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.16
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b48876a3d2402b7a657b7b9311c8a4637e59e3de158d3bafa1fafbdf9a1693f0 |
|
MD5 | 0e8d960a205d6b2203554bc550bb9ba2 |
|
BLAKE2b-256 | f261143345c5f47fae762f2a8f0fe214917b1a5dd70959272146e06cfb833089 |