Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ og:description: See what's new in the latest release of Roseau Load Flow !

## Unreleased

- {gh-pr}`429` {gh-issue}`298` Add `rlf.plotting.voltage_profile` function to plot the voltage profile of a network. The
function returns an object with `plot_matplotlib()` and `plot_plotly()` methods to create plots using Matplotlib or
Plotly respectively.

- {gh-pr}`427` Add missing transformer tap to the edge data in the graph generated by `ElectricalNetwork.to_graph`.

- {gh-pr}`426` Add `rlf.plotting.plot_results_interactive_map` for plotting a network with load flow results on an
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
484 changes: 484 additions & 0 deletions doc/_static/Plotting/Voltage_Profile_LVFeeder36360_Plotly.html

Large diffs are not rendered by default.

185 changes: 140 additions & 45 deletions doc/usage/Plotting.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,124 @@ myst:
description lang=en: |
Learn how to plot an MV or LV network with Roseau Load Flow, a powerful load flow solver for the electrical
calculation of smart grids.
keywords lang=en: simulation, distribution grid, map, plot
keywords lang=en: simulation, distribution grid, map, voltage profile, plot
# spellchecker:off
description lang=fr: |
Apprenez à tracer une carte du réseau MT ou BT avec Roseau Load Flow, solveur d'écoulements de charge pour le
calcul électrique des réseaux intelligents.
keywords lang=fr: simulation, réseau, électrique, carte, tracé
keywords lang=fr: simulation, réseau, électrique, carte, profil de tension, tracé
# spellchecker:on
---

# Plotting

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

## Plotting a network on a map
## Plotting Networks

### Voltage Profile

The {func}`~roseau.load_flow.plotting.voltage_profile` function can be used to create a voltage profile of the network.

A voltage profile represents the voltage (in %) of network nodes as a function of their distance from a reference node.
Branches connecting nodes (buses) are shown as lines between bus locations. Buses are color-coded according to their
voltages, while branches are colored based on their loading as described in the [Results Colors](#results-colors)
section below.

The network must have nominal voltages defined for its buses and valid load flow results.

For multiphase networks, the voltage profile must be selected for a specific mode: either `"min"` or `"max"`, which
represent the minimum or maximum voltage magnitude across all phases at each bus.

To visualize the voltage profile of a network, use one of the `plot_<backend>` methods on the object returned by the
{func}`~roseau.load_flow.plotting.voltage_profile` function. Example:

```pycon
>>> import roseau.load_flow as rlf
... en = rlf.ElectricalNetwork.from_catalogue("LVFeeder36360", "Winter")
... en
<ElectricalNetwork: 9 buses, 7 lines, 1 transformer, 0 switches, 14 loads, 1 source, 1 ground, 2 potential refs, 1 ground connection>

>>> en.solve_load_flow()
(3, 4.206412995699793e-12)

>>> rlf.plotting.voltage_profile(
... en, mode="min", traverse_transformers=True, distance_unit="m"
... ).plot_plotly().show()
```

<!-- en = rlf.ElectricalNetwork.from_catalogue("LVFeeder36360", "Winter")
en.solve_load_flow()
(
rlf.plotting.voltage_profile(en, mode="min", traverse_transformers=True, distance_unit="m")
.plot_plotly()
.write_html(
"doc/_static/Plotting/Voltage_Profile_LVFeeder36360_Plotly2.html", include_plotlyjs="cdn"
)
) -->

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

**Features**

- **Reference bus**: select the reference bus for distance; defaults to the source bus with the highest voltage
- **Transformer traversal**: plot the entire network by traversing transformers; defaults to plotting the subnetwork
connected to the reference bus only
- **Distance unit**: choose the distance display unit; defaults to kilometers (`"km"`)
- **Switch length**: set a custom length for switches; defaults to 2 m or the shortest line length if smaller.

**Supported Backends**

- **Matplotlib**: use the `plot_matplotlib` method to create a static plot using the `matplotlib` library. You can
optionally pass an `Axes` object to the method to customize the plot further.
- **Plotly**: use the `plot_plotly` method to create an interactive plot using the `plotly` library.

**Tip**

You can plot both minimum and maximum voltage profiles on the same plot by passing the same `Axes` object to the
`plot_matplotlib` method for both modes. Example:

```pycon
>>> import matplotlib.pyplot as plt
... import roseau.load_flow as rlf
... en = rlf.ElectricalNetwork.from_catalogue("LVFeeder36360", "Winter")
... en.solve_load_flow()
... ax = plt.figure(figsize=(8, 4)).gca()
... rlf.plotting.voltage_profile(
... en, mode="min", traverse_transformers=True, distance_unit="m"
... ).plot_matplotlib(ax=ax)
... rlf.plotting.voltage_profile(
... en, mode="max", traverse_transformers=True, distance_unit="m"
... ).plot_matplotlib(ax=ax)
... ax.set_title("Voltage Profile (min and max)")
... ax.set_ylabel("Voltage (%)")
... plt.show()
```

```{image} /_static/Plotting/Voltage_Profile_LVFeeder36360_Min_Max.png
---
alt: The voltage profile (min and max) of the network LVFeeder36360
align: center
---
```

### Interactive Map

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

```pycon
>>> import roseau.load_flow as rlf
>>> en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
>>> en
<ElectricalNetwork: 128 buses, 126 lines, 0 transformers, 1 switch, 82 loads, 1 source, 1 ground, 1 potential ref, 0 ground connections>
>>> rlf.plotting.plot_interactive_map(en)
... en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
... rlf.plotting.plot_interactive_map(en)
```

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

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

### Features
**Features**

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

Only buses, lines and transformers are currently plotted.

## Plotting a network with results on a map
### Interactive Map with Load Flow Results

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

```pycon
>>> import roseau.load_flow as rlf
>>> en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
>>> # Let's create some extreme conditions to see voltage drops/rises and line overloads
>>> en.loads["MVLV14633_consumption"].powers = 3.5e6
>>> en.loads["MVLV15838_production"].powers = -5.5e6
>>> en.solve_load_flow()
(3, 3.725290298461914e-09)
>>> rlf.plotting.plot_results_interactive_map(en)
... en = rlf.ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
... # Let's create some extreme conditions to see voltage drops/rises and line overloads
... en.loads["MVLV14633_consumption"].powers = 3.5e6
... en.loads["MVLV15838_production"].powers = -5.5e6
... en.solve_load_flow()
... rlf.plotting.plot_results_interactive_map(en)
```

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

The plot shows the color-coded voltage levels at buses and color-coded loading of lines and transformers. The following
states are represented:

- **very-low (blue)**: bus voltage below {math}`U_{min}`
- **low (light blue)**: bus voltage in the first quadrant of the {math}`(U_{min}, U_{n})` range
- **normal (green)**: bus voltage in the last three quadrants of the {math}`(U_{min}, U_{n})` range or in the first
three quadrants of the {math}`(U_{n}, U_{max})` range; line or transformer loading below 75% {math}`load_{max}`
- **high (orange)**: bus voltage in the last quadrant of the {math}`(U_{n}, U_{max})` range; line or transformer loading
between 75% and 100% {math}`load_{max}`
- **very-high (red)**: bus voltage above {math}`U_{max}`; line or transformer loading above 100% {math}`load_{max}`
- **unknown (gray)**: bus nominal voltage or limits not defined; line ampacity not defined

```{image} /_static/Plotting/Result_States.png
---
alt: The different states for bus voltages and line/transformer loadings
align: center
---
```
The plot shows the buses color-coded according to their voltages and the lines/transformers color-coded according to
their loading as described in the [Results Colors](#results-colors) section below.

## Plotting a network with no geometries
### Graph Plot

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

```pycon
>>> import networkx as nx
Expand All @@ -117,12 +186,14 @@ align: center

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

## Plotting voltage phasors
## Plotting Elements

The {func}`~roseau.load_flow.plotting.plot_voltage_phasors` function plots the voltage phasors of a bus, load or source
in the complex plane. This function can be used to visualize voltage unbalance in multi-phase systems for instance. It
takes the element and an optional matplotlib `Axes` object to use for the plot. Note that the element must have load
flow results to plot the voltage phasors.
### Voltage Phasors

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

```pycon
>>> import matplotlib.pyplot as plt
Expand All @@ -149,5 +220,29 @@ align: center
---
```

### Symmetrical Voltages

A similar function {func}`~roseau.load_flow.plotting.plot_symmetrical_voltages` plots the symmetrical components of the
voltage phasors of a three-phase bus, load or source.
voltage phasors of a three-phase terminal element.

## Results Colors

The results in plots are color-coded based on the following predefined states:

- **very-low** (blue): bus voltage below {math}`U_{min}`
- **low** (light blue): bus voltage in the first quadrant of the {math}`(U_{min}, U_{n})` range
- **normal** (green): bus voltage in the last three quadrants of the {math}`(U_{min}, U_{n})` range or in the first
three quadrants of the {math}`(U_{n}, U_{max})` range; line or transformer loading below 75% {math}`load_{max}`
- **high** (orange): bus voltage in the last quadrant of the {math}`(U_{n}, U_{max})` range; line or transformer loading
between 75% and 100% {math}`load_{max}`
- **very-high** (red): bus voltage above {math}`U_{max}`; line or transformer loading above 100% {math}`load_{max}`
- **unknown** (gray): bus nominal voltage or limits not defined; line ampacity not defined

```{image} /_static/Plotting/Result_States.png
---
alt: The different states for bus voltages and line/transformer loadings
align: center
---
```

Colors are currently not customizable. Let us know if you need this feature.
4 changes: 4 additions & 0 deletions roseau/load_flow/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,10 @@ def _get_starting_potentials(self, all_phases: set[str]) -> tuple[dict[str, comp

return potentials, starting_source

def _get_starting_bus_id(self) -> Id:
_, starting_source = self._get_starting_potentials(all_phases=set())
return starting_source.bus.id

@staticmethod
def _check_ref(elements: Iterable[Element]) -> None:
"""Check the number of potential references to avoid having a singular jacobian matrix."""
Expand Down
Loading