Skip to content

Commit 1d9dea0

Browse files
committed
Add voltage profile plotting
1 parent 0490688 commit 1d9dea0

File tree

12 files changed

+4866
-88
lines changed

12 files changed

+4866
-88
lines changed

doc/Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ og:description: See what's new in the latest release of Roseau Load Flow !
2121

2222
## Unreleased
2323

24+
- {gh-pr}`429` {gh-issue}`298` Add `rlf.plotting.voltage_profile` function to plot the voltage profile of a network. The
25+
function returns an object with `plot_matplotlib()` and `plot_plotly()` methods to create plots using Matplotlib or
26+
Plotly respectively.
27+
2428
- {gh-pr}`427` Add missing transformer tap to the edge data in the graph generated by `ElectricalNetwork.to_graph`.
2529

2630
- {gh-pr}`426` Add `rlf.plotting.plot_results_interactive_map` for plotting a network with load flow results on an
Lines changed: 3 additions & 0 deletions
Loading

doc/_static/Plotting/Voltage_Profile_LVFeeder36360_Plotly.html

Lines changed: 3888 additions & 0 deletions
Large diffs are not rendered by default.

doc/usage/Plotting.md

Lines changed: 130 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,114 @@ myst:
44
description lang=en: |
55
Learn how to plot an MV or LV network with Roseau Load Flow, a powerful load flow solver for the electrical
66
calculation of smart grids.
7-
keywords lang=en: simulation, distribution grid, map, plot
7+
keywords lang=en: simulation, distribution grid, map, voltage profile, plot
88
# spellchecker:off
99
description lang=fr: |
1010
Apprenez à tracer une carte du réseau MT ou BT avec Roseau Load Flow, solveur d'écoulements de charge pour le
1111
calcul électrique des réseaux intelligents.
12-
keywords lang=fr: simulation, réseau, électrique, carte, tracé
12+
keywords lang=fr: simulation, réseau, électrique, carte, profil de tension, tracé
1313
# spellchecker:on
1414
---
1515

1616
# Plotting
1717

1818
_Roseau Load Flow_ provides plotting functionality in the `rlf.plotting` module.
1919

20-
## Plotting a network on a map
20+
## Plotting Networks
21+
22+
### Voltage Profile
23+
24+
The {func}`~roseau.load_flow.plotting.voltage_profile` function can be used to create a voltage profile of the network.
25+
26+
A voltage profile represents the voltage (in %) of network nodes as a function of their distance from a reference node.
27+
Branches connecting nodes (buses) are shown as lines between bus locations. Buses are color-coded according to their
28+
voltages, while branches are colored based on their loading as described in the [Results Colors](#results-colors)
29+
section below.
30+
31+
The network must have nominal voltages defined for its buses and valid load flow results.
32+
33+
For multiphase networks, the voltage profile must be selected for a specific mode: either `"min"` or `"max"`, which
34+
represent the minimum or maximum voltage magnitude across all phases at each bus.
35+
36+
To visualize the voltage profile of a network, use one of the `plot_<backend>` methods on the object returned by the
37+
{func}`~roseau.load_flow.plotting.voltage_profile` function. Example:
38+
39+
```pycon
40+
>>> import roseau.load_flow as rlf
41+
... en = rlf.ElectricalNetwork.from_catalogue("LVFeeder36360", "Winter")
42+
... en
43+
<ElectricalNetwork: 9 buses, 7 lines, 1 transformer, 0 switches, 14 loads, 1 source, 1 ground, 2 potential refs, 1 ground connection>
44+
45+
>>> en.solve_load_flow()
46+
(3, 4.206412995699793e-12)
47+
48+
>>> rlf.plotting.voltage_profile(
49+
... en, mode="min", traverse_transformers=True, distance_unit="m"
50+
... ).plot_plotly().show()
51+
```
52+
53+
<iframe src="../_static/Plotting/Voltage_Profile_LVFeeder36360_Plotly.html" height="500px" width="100%" frameborder="0"></iframe>
54+
55+
**Features**
56+
57+
- **Reference bus**: select the reference bus for distance; defaults to the source bus with the highest voltage
58+
- **Transformer traversal**: plot the entire network by traversing transformers; defaults to plotting the subnetwork
59+
connected to the reference bus only
60+
- **Distance unit**: choose the distance display unit; defaults to kilometers (`"km"`)
61+
- **Switch length**: set a custom length for switches; defaults to 2 m or the shortest line length if smaller.
62+
63+
**Supported Backends**
64+
65+
- **Matplotlib**: use the `plot_matplotlib` method to create a static plot using the `matplotlib` library. You can
66+
optionally pass an `Axes` object to the method to customize the plot further.
67+
- **Plotly**: use the `plot_plotly` method to create an interactive plot using the `plotly` library.
68+
69+
**Tip**
70+
71+
You can plot both minimum and maximum voltage profiles on the same plot by passing the same `Axes` object to the
72+
`plot_matplotlib` method for both modes. Example:
73+
74+
```pycon
75+
>>> import matplotlib.pyplot as plt
76+
... import roseau.load_flow as rlf
77+
... en = rlf.ElectricalNetwork.from_catalogue("LVFeeder36360", "Winter")
78+
... en.solve_load_flow()
79+
... ax = plt.figure(figsize=(8, 4)).gca()
80+
... rlf.plotting.voltage_profile(
81+
... en, mode="min", traverse_transformers=True, distance_unit="m"
82+
... ).plot_matplotlib(ax=ax)
83+
... rlf.plotting.voltage_profile(
84+
... en, mode="max", traverse_transformers=True, distance_unit="m"
85+
... ).plot_matplotlib(ax=ax)
86+
... ax.set_title("Voltage Profile (min and max)")
87+
... ax.set_ylabel("Voltage (%)")
88+
... plt.show()
89+
```
90+
91+
```{image} /_static/Plotting/Voltage_Profile_LVFeeder36360_Min_Max.png
92+
---
93+
alt: The voltage profile (min and max) of the network LVFeeder36360
94+
align: center
95+
---
96+
```
97+
98+
### Interactive Map
2199

22100
The simplest way to visualize an electrical network with bus and line geometries is to plot it on a map using the
23101
{func}`~roseau.load_flow.plotting.plot_interactive_map` function. Example:
24102

25103
```pycon
26104
>>> import roseau.load_flow as rlf
27-
>>> en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
28-
>>> en
29-
<ElectricalNetwork: 128 buses, 126 lines, 0 transformers, 1 switch, 82 loads, 1 source, 1 ground, 1 potential ref, 0 ground connections>
30-
>>> rlf.plotting.plot_interactive_map(en)
105+
... en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
106+
... rlf.plotting.plot_interactive_map(en)
31107
```
32108

33109
<iframe src="../_static/Plotting/MVFeeder210.html" height="500px" width="100%" frameborder="0"></iframe>
34110

35111
Make sure you have [folium](https://python-visualization.github.io/folium/latest) installed in your Python environment
36112
and that your network has a coordinate reference system (CRS) set via the `en.crs` attribute.
37113

38-
### Features
114+
**Features**
39115

40116
1. **Interactive map**: zoom in/out, pan, hover or click on elements to see their properties
41117
2. **Base maps**: all
@@ -54,49 +130,32 @@ documentation for more details.
54130

55131
Only buses, lines and transformers are currently plotted.
56132

57-
## Plotting a network with results on a map
133+
### Interactive Map with Load Flow Results
58134

59135
The {func}`~roseau.load_flow.plotting.plot_results_interactive_map` function can be used to plot load flow results on
60136
the map. The network must have valid results before calling this function. Example:
61137

62138
```pycon
63139
>>> import roseau.load_flow as rlf
64-
>>> en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
65-
>>> # Let's create some extreme conditions to see voltage drops/rises and line overloads
66-
>>> en.loads["MVLV14633_consumption"].powers = 3.5e6
67-
>>> en.loads["MVLV15838_production"].powers = -5.5e6
68-
>>> en.solve_load_flow()
69-
(3, 3.725290298461914e-09)
70-
>>> rlf.plotting.plot_results_interactive_map(en)
140+
... en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
141+
... # Let's create some extreme conditions to see voltage drops/rises and line overloads
142+
... en.loads["MVLV14633_consumption"].powers = 3.5e6
143+
... en.loads["MVLV15838_production"].powers = -5.5e6
144+
... en.solve_load_flow()
145+
... rlf.plotting.plot_results_interactive_map(en)
71146
```
72147

73148
<iframe src="../_static/Plotting/MVFeeder210_Results.html" height="500px" width="100%" frameborder="0"></iframe>
74149

75-
The plot shows the color-coded voltage levels at buses and color-coded loading of lines and transformers. The following
76-
states are represented:
77-
78-
- **very-low (blue)**: bus voltage below {math}`U_{min}`
79-
- **low (light blue)**: bus voltage in the first quadrant of the {math}`(U_{min}, U_{n})` range
80-
- **normal (green)**: bus voltage in the last three quadrants of the {math}`(U_{min}, U_{n})` range or in the first
81-
three quadrants of the {math}`(U_{n}, U_{max})` range; line or transformer loading below 75% {math}`load_{max}`
82-
- **high (orange)**: bus voltage in the last quadrant of the {math}`(U_{n}, U_{max})` range; line or transformer loading
83-
between 75% and 100% {math}`load_{max}`
84-
- **very-high (red)**: bus voltage above {math}`U_{max}`; line or transformer loading above 100% {math}`load_{max}`
85-
- **unknown (gray)**: bus nominal voltage or limits not defined; line ampacity not defined
86-
87-
```{image} /_static/Plotting/Result_States.png
88-
---
89-
alt: The different states for bus voltages and line/transformer loadings
90-
align: center
91-
---
92-
```
150+
The plot shows the buses color-coded according to their voltages and the lines/transformers color-coded according to
151+
their loading as described in the [Results Colors](#results-colors) section below.
93152

94-
## Plotting a network with no geometries
153+
### Graph Plot
95154

96-
If the network does not have geometries defined for its elements, the `plot_interactive_map` function will not work. In
97-
this case, you can use the {meth}`~roseau.load_flow.ElectricalNetwork.to_graph` method to convert the network to a
98-
networkx `MultiGraph` and plot it using the `networkx` library. In the following example we plot the graph of the
99-
network `MVFeeder210` from the previous example:
155+
If a network does not have geometries nor nominal voltages defined, the plotting functions mentioned above will not
156+
work. In this case, you can have a visual representation of the network by converting it to a networkx graph using the
157+
{meth}`~roseau.load_flow.ElectricalNetwork.to_graph` method and plotting it using the `networkx` library. In the
158+
following example we plot the graph of the network `MVFeeder210` from the previous example:
100159

101160
```pycon
102161
>>> import networkx as nx
@@ -117,12 +176,14 @@ align: center
117176

118177
See the [networkx docs](https://networkx.org/documentation/stable/tutorial.html#drawing-graphs) for more information.
119178

120-
## Plotting voltage phasors
179+
## Plotting Elements
121180

122-
The {func}`~roseau.load_flow.plotting.plot_voltage_phasors` function plots the voltage phasors of a bus, load or source
123-
in the complex plane. This function can be used to visualize voltage unbalance in multi-phase systems for instance. It
124-
takes the element and an optional matplotlib `Axes` object to use for the plot. Note that the element must have load
125-
flow results to plot the voltage phasors.
181+
### Voltage Phasors
182+
183+
The {func}`~roseau.load_flow.plotting.plot_voltage_phasors` function plots the voltage phasors of a terminal element
184+
(bus, load, source or a branch side) in the complex plane. This function can be used to visualize voltage unbalance in
185+
multi-phase systems for instance. It takes the element and an optional matplotlib `Axes` object to use for the plot.
186+
Note that the element must have load flow results to plot the voltage phasors.
126187

127188
```pycon
128189
>>> import matplotlib.pyplot as plt
@@ -149,5 +210,29 @@ align: center
149210
---
150211
```
151212

213+
### Symmetrical Voltages
214+
152215
A similar function {func}`~roseau.load_flow.plotting.plot_symmetrical_voltages` plots the symmetrical components of the
153-
voltage phasors of a three-phase bus, load or source.
216+
voltage phasors of a three-phase terminal element.
217+
218+
## Results Colors
219+
220+
The results in plots are color-coded based on the following predefined states:
221+
222+
- **very-low** (blue): bus voltage below {math}`U_{min}`
223+
- **low** (light blue): bus voltage in the first quadrant of the {math}`(U_{min}, U_{n})` range
224+
- **normal** (green): bus voltage in the last three quadrants of the {math}`(U_{min}, U_{n})` range or in the first
225+
three quadrants of the {math}`(U_{n}, U_{max})` range; line or transformer loading below 75% {math}`load_{max}`
226+
- **high** (orange): bus voltage in the last quadrant of the {math}`(U_{n}, U_{max})` range; line or transformer loading
227+
between 75% and 100% {math}`load_{max}`
228+
- **very-high** (red): bus voltage above {math}`U_{max}`; line or transformer loading above 100% {math}`load_{max}`
229+
- **unknown** (gray): bus nominal voltage or limits not defined; line ampacity not defined
230+
231+
```{image} /_static/Plotting/Result_States.png
232+
---
233+
alt: The different states for bus voltages and line/transformer loadings
234+
align: center
235+
---
236+
```
237+
238+
Colors are currently not customizable. Let us know if you need this feature.

roseau/load_flow/network.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,10 @@ def _get_starting_potentials(self, all_phases: set[str]) -> tuple[dict[str, comp
12051205

12061206
return potentials, starting_source
12071207

1208+
def _get_starting_bus_id(self) -> Id:
1209+
_, starting_source = self._get_starting_potentials(all_phases=set())
1210+
return starting_source.bus.id
1211+
12081212
@staticmethod
12091213
def _check_ref(elements: Iterable[Element]) -> None:
12101214
"""Check the number of potential references to avoid having a singular jacobian matrix."""

0 commit comments

Comments
 (0)