Skip to content

Improving transmission loss approximation for DC power flow#1409

Closed
WeiAi-Energy wants to merge 4 commits intoPyPSA:masterfrom
WeiAi-Energy:issue-1320
Closed

Improving transmission loss approximation for DC power flow#1409
WeiAi-Energy wants to merge 4 commits intoPyPSA:masterfrom
WeiAi-Energy:issue-1320

Conversation

@WeiAi-Energy
Copy link
Copy Markdown

@WeiAi-Energy WeiAi-Energy commented Oct 17, 2025

Changes proposed in this Pull Request

Summary

This PR enhances the transmission loss constraint formulation in PyPSA by:

  1. Using optimized capacity s_nom_opt from the last iteration to form a tighter envelope for transmission loss approximation during iterative transmission expansion
  2. Implementing an analytically-fitted piecewise linear approximation that provides better accuracy with given segments

Mathematical Formulation (L2 Piecewise-Linear Approximation)

We model losses with the quadratic function

$$ f(s) = rs^2, $$ and approximate it on $[0,s_{\max}]$ by a symmetric piecewise-linear function $$ \ell(|s|)=\max_{k=1,\dots,N}{a_k |s| + b_k}. $$

Choice of $s_{\max}$

In transmission-expansion loops we initialize $s_{\max}$ with s_nom in the 1st iteration and then use the previous iteration’s optimized capacity s_nom_opt. This provides a tighter approximation compared to the original implementation of setting $s_{\max}$ as s_nom_max. Such choice also avoid the requirement of setting s_nom_max for lines, resolving #1320.

Single-segment case (N=1)

We seek the best linear function $a|s|$ over $[0,s_{\max}]$ with zero intercept:

$$ \min\int_{0}^{s_{\max}}\big(r s^2 - a s\big)^2ds. $$

Setting $\partial/\partial a=0$ yields

$$ a^* = r\frac{\int_0^{s_{\max}} s^3ds}{\int_0^{s_{\max}} s^2ds} = \tfrac{3}{4}rs_{\max}. $$

Multi-segment case (N>1)

Let $0=x_0<x_1<\dots<x_N=s_{\max}$ be uniform breakpoints. On each interval $[x_k,x_{k+1}]$ we fit

$$ r s^2 \approx a_k s + b_k $$ by solving the continuous least-squares problem $$ \min_{a_k,b_k}\int_{x_k}^{x_{k+1}}\big(r s^2 - a_k s - b_k\big)^2ds. $$ Define the integral moments $$ I_0 = x_{k+1}-x_k,\qquad I_1 = \tfrac{1}{2}\big(x_{k+1}^2-x_k^2\big),\qquad I_2 = \tfrac{1}{3}\big(x_{k+1}^3-x_k^3\big),\qquad I_3 = \tfrac{1}{4}\big(x_{k+1}^4-x_k^4\big). $$ The normal equations are $$ \begin{bmatrix} I_2 & I_1 ; I_1 & I_0 \end{bmatrix} \begin{bmatrix} a_k ; b_k \end{bmatrix} = r\begin{bmatrix} I_3 ; I_2 \end{bmatrix}, $$ yielding: $$ a_k = r\frac{I_3 I_0 - I_2 I_1}{I_2 I_0 - I_1^2}\qquad b_k = r\frac{I_2^2 - I_3 I_1}{I_2 I_0 - I_1^2}. $$

To preserve $\ell(0)=0$, we enforce $b_1=0$ on $[x_0,x_1]$ and minimize $\int_{x_0}^{x_1}(r s^2 - a_1 s)^2 ds$, which gives

$$ a_1 = r\frac{I_3}{I_2},\qquad b_1=0. $$

Note. This construction targets L2 approximation quality, not a conservative outer envelope. It may under- or over-estimate actual loss locally.

Short Test Summary Info

FAILED test/test_network_index.py::test_get_scenario_empty_components_bug_1402 - AssertionError: assert not True
FAILED test/test_options.py::test_params_optimize - AssertionError: Solver gurobi not installed
FAILED test/test_plot_maps_static.py::test_plot_map_flow - _tkinter.TclError: Can't find a usable tk.tcl in the following directories:
FAILED test/test_statistics_plot.py::test_networks_line_plot[withdrawal] - _tkinter.TclError: Can't find a usable tk.tcl in the following directories:
4 failed, 939 passed, 414 skipped, 4 deselected, 3 warnings in 340.71s (0:05:40)

Example (N=2)

image

Checklist

  • Code changes are sufficiently documented; i.e. new functions contain docstrings and further explanations may be given in docs.
  • Unit tests for new features were added (if applicable).
  • A note for the release notes docs/release-notes.md of the upcoming release is included.
  • I consent to the release of this PR's code under the MIT license.

@WeiAi-Energy WeiAi-Energy changed the title Issue 1320 Improving transmission loss approximation for DC power flow Oct 17, 2025
@lindnemi
Copy link
Copy Markdown
Contributor

It's great that this issue is getting some attention, i wanted to work on it myself but unfortunately did not find the time.

My main issue outlined in #1320 is that the value of s_nom_max affects the losses and hence the solution. The reason is that the tangents depend on s_nom_max (both slope and offset). This PR improves the situation by using s_nom_opt in suitable situations during iterative expansion modeling. However, in other situations it still relies on s_nom_max, so unfortunately i does not resolve #1320 completly.

I think the losses can be made independent of s_nom_max, because the shape of the loss parabola is independent of s_nom_max. In the loss constraint s_nom_max is only really needed to provide an upper bound on the losses (and should only be relevant for the model in negative price hours, if I understand the theory right). So my suggestion is to choose the approximation independently of s_nom_max, only relying on a specified loss_approximation_tolerance config, which gives the maximum error between the approximation and the parabola. The approach here - using an L2 approximation - seems to be one promising for constructing such a tighter approximation. I can't judge however, if there are reasons why an outer envelope would be more desirable.

@WeiAi-Energy
Copy link
Copy Markdown
Author

It's great that this issue is getting some attention, i wanted to work on it myself but unfortunately did not find the time.

My main issue outlined in #1320 is that the value of s_nom_max affects the losses and hence the solution. The reason is that the tangents depend on s_nom_max (both slope and offset). This PR improves the situation by using s_nom_opt in suitable situations during iterative expansion modeling. However, in other situations it still relies on s_nom_max, so unfortunately i does not resolve #1320 completly.

I think the losses can be made independent of s_nom_max, because the shape of the loss parabola is independent of s_nom_max. In the loss constraint s_nom_max is only really needed to provide an upper bound on the losses (and should only be relevant for the model in negative price hours, if I understand the theory right). So my suggestion is to choose the approximation independently of s_nom_max, only relying on a specified loss_approximation_tolerance config, which gives the maximum error between the approximation and the parabola. The approach here - using an L2 approximation - seems to be one promising for constructing such a tighter approximation. I can't judge however, if there are reasons why an outer envelope would be more desirable.

I believe that even if we implement the loss approximation using the idea of setting a loss_approximation_tolerance, the concept of the maximum s is still relevant, because we need to define the range of the power over which the approximation is applied. In other words, the number of line segments is a function of both the maximum s and the tolerance. For example, if we only aim to achieve an approximation error below 0.01 within the interval [-1, 1], we may only need 3 segments; however, if we want to maintain an error below 0.01 over the entire range [-∞, +∞], we would in fact need an infinite number of segments.

@fneum
Copy link
Copy Markdown
Member

fneum commented Nov 1, 2025

Great, I will try to look into it soon! For now, just one question: In your image, it looks like the first tangent cuts through the red line. Shouldn't it be on the outside of the red line?

@WeiAi-Energy
Copy link
Copy Markdown
Author

In your image, it looks like the first tangent cuts through the red line. Shouldn't it be on the outside of the red line?

Hi @fneum, this modification is intended to generate line segments that minimize approximation error without restricting the segments to be tangents.

@lkstrp lkstrp requested a review from fneum November 17, 2025 08:09
@lindnemi lindnemi mentioned this pull request Dec 16, 2025
6 tasks
@fneum
Copy link
Copy Markdown
Member

fneum commented Dec 17, 2025

Hey @WeiAi-Energy, @lindnemi proposed a secant model in #1495. What do you think of that? Would you see your PR as a third mode or would you say it's unnecessary if we go forward with #1495?

@WeiAi-Energy
Copy link
Copy Markdown
Author

Hey @WeiAi-Energy, @lindnemi proposed a secant model in #1495. What do you think of that? Would you see your PR as a third mode or would you say it's unnecessary if we go forward with #1495?

Hi @fneum, I think the error control proposed in #1495 is a great idea. My only concern is that it still requires a s_nom_max, but in practice, it's often hard to assign an appropriate s_nom_max for every single line, which may increase unnecessary complexity in the model. For example, if s_nom_max is set to 10 GW but the optimal solution is 0.1 GW, then most of the constraint segments won’t be active. This can be avoided in the iterative case in this PR, so I would suggest combining the two PRs.

@lindnemi
Copy link
Copy Markdown
Contributor

lindnemi commented Jan 5, 2026

Hi @fneum, I think the error control proposed in #1495 is a great idea. My only concern is that it still requires a s_nom_max, but in practice, it's often hard to assign an appropriate s_nom_max for every single line, which may increase unnecessary complexity in the model. For example, if s_nom_max is set to 10 GW but the optimal solution is 0.1 GW, then most of the constraint segments won’t be active. This can be avoided in the iterative case in this PR, so I would suggest combining the two PRs.

I totally agree that it's undesirable to have many inactive constraints. Using s_nom instead of s_nom_max in #1495 when solving iteratively would reduce the number of constraints, however, it would also mean that the approximation error can no longer be guaranteed to be below the specified error tolerances for flows larger than s_nom. Because of this I don't think using s_nom works well in #1495, but we could add a further mode of loss approximation which trades the strict error control for sparser constraints.

@FabianHofmann
Copy link
Copy Markdown
Contributor

FabianHofmann commented Feb 23, 2026

@WeiAi-Energy thanks for the pr. After tackling it partially in #1495 (as it seems), I am wondering if is makes sense to pull this in. the code changes are not a lot and don't seem to be invasive. so I am happy to give this a go and review properly. otherwise we can close this. also tagging @lindnemi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants