Skip to content

Commit

Permalink
Adds a use_name parameter to the network explorer (#15)
Browse files Browse the repository at this point in the history
* uses  use_name to display ids or names in the selection list and in the tabs content
* introduces SelectionContext class to handle it

Signed-off-by: Christian Biasuzzi <[email protected]>
  • Loading branch information
CBiasuzzi authored Jul 24, 2024
1 parent 234e4a7 commit ef08cac
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 36 deletions.
3 changes: 2 additions & 1 deletion docs/user_guide/network_explorer.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ Switches can also be clicked, causing their status in the network to change; Ple
Other than the target network, the Network explorer can be customized using additional parameters:

```python
network_explorer(network: Network, vl_id : str = None, depth: int = 0, high_nominal_voltage_bound: float = -1, low_nominal_voltage_bound: float = -1, nad_parameters: NadParameters = None, sld_parameters: SldParameters = None)
network_explorer(network: Network, vl_id : str = None, use_name:bool = True, depth: int = 0, high_nominal_voltage_bound: float = -1, low_nominal_voltage_bound: float = -1, nad_parameters: NadParameters = None, sld_parameters: SldParameters = None):
```

- vl_id: the starting VL to display. If None, display the first VL from network.get_voltage_levels()
- use_name: when available, display VLs names instead of their ids (default is to use names)
- depth: the diagram depth around the voltage level, controls the size of the sub network. In the SLD tab will be always displayed one diagram, from the VL list currently selected item.
- low_nominal_voltage_bound: low bound to filter voltage level according to nominal voltage
- high_nominal_voltage_bound: high bound to filter voltage level according to nominal voltage
Expand Down
70 changes: 35 additions & 35 deletions src/pypowsybl_jupyter/networkexplorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@
from pypowsybl.network import Network, NadParameters, SldParameters
from .nadwidget import display_nad, update_nad
from .sldwidget import display_sld, update_sld
from .selectcontext import SelectContext

import ipywidgets as widgets

def network_explorer(network: Network, vl_id : str = None, depth: int = 0, high_nominal_voltage_bound: float = -1, low_nominal_voltage_bound: float = -1, nad_parameters: NadParameters = None, sld_parameters: SldParameters = None):
def network_explorer(network: Network, vl_id : str = None, use_name:bool = True, depth: int = 0,
high_nominal_voltage_bound: float = -1, low_nominal_voltage_bound: float = -1,
nad_parameters: NadParameters = None, sld_parameters: SldParameters = None):
"""
Creates a combined NAD and SLD explorer widget for the network. Diagrams are displayed on two different tabs.
Args:
network: the input network
vl_id: the starting VL to display. If None, display the first VL from network.get_voltage_levels()
use_name: when available, display VLs names instead of their ids (default is to use names)
depth: the diagram depth around the voltage level, controls the size of the sub network. In the SLD tab will be always displayed one diagram, from the VL list currently selected item.
low_nominal_voltage_bound: low bound to filter voltage level according to nominal voltage
high_nominal_voltage_bound: high bound to filter voltage level according to nominal voltage
Expand All @@ -31,18 +35,15 @@ def network_explorer(network: Network, vl_id : str = None, depth: int = 0, high_
network_explorer(pp.network.create_eurostag_tutorial_example1_network())
"""

vls = network.get_voltage_levels(attributes=[])
sel_ctx=SelectContext(network, vl_id, use_name)

nad_widget=None
sld_widget=None

selected_vl = vls.index[0] if vl_id is None else vl_id
if selected_vl not in vls.index:
raise ValueError(f'a voltage level {vl_id} does not exist in the network.')

selected_depth=depth

npars = nad_parameters if nad_parameters is not None else NadParameters(edge_name_displayed=False,
id_displayed=False,
id_displayed=not use_name,
edge_info_along_edge=False,
power_value_precision=1,
angle_value_precision=0,
Expand All @@ -51,17 +52,15 @@ def network_explorer(network: Network, vl_id : str = None, depth: int = 0, high_
bus_legend=False,
substation_description_displayed=True)

spars=sld_parameters if sld_parameters is not None else SldParameters(use_name=True)
spars=sld_parameters if sld_parameters is not None else SldParameters(use_name=use_name, nodes_infos=True)

def go_to_vl(event: any):
nonlocal selected_vl
arrow_vl= str(event.clicked_nextvl)
vl_filtered_list=list(found.options)
if arrow_vl not in vl_filtered_list:
vl_filtered_list.append(arrow_vl)
found.options=vl_filtered_list
selected_vl=arrow_vl
found.value=arrow_vl
sel_ctx.extend_filtered_vls(arrow_vl)
found.value=None
found.options=sel_ctx.get_filtered_vls_as_list()
sel_ctx.set_selected(arrow_vl)
found.value=sel_ctx.get_selected()

def toggle_switch(event: any):
idswitch = event.clicked_switch.get('id')
Expand All @@ -70,20 +69,21 @@ def toggle_switch(event: any):
update_sld_diagram(True)
update_nad_diagram()


def update_nad_diagram():
nonlocal nad_widget
if len(selected_vl)>0:
new_diagram_data=network.get_network_area_diagram(voltage_level_ids=selected_vl, depth=selected_depth, high_nominal_voltage_bound=high_nominal_voltage_bound, low_nominal_voltage_bound=low_nominal_voltage_bound, nad_parameters=npars)
if sel_ctx.get_selected() is not None:
new_diagram_data=network.get_network_area_diagram(voltage_level_ids=sel_ctx.get_selected(),
depth=selected_depth, high_nominal_voltage_bound=high_nominal_voltage_bound,
low_nominal_voltage_bound=low_nominal_voltage_bound, nad_parameters=npars)
if nad_widget==None:
nad_widget=display_nad(new_diagram_data)
else:
update_nad(nad_widget,new_diagram_data)

def update_sld_diagram(kv: bool = False):
nonlocal sld_widget
if selected_vl is not None:
sld_diagram_data=network.get_single_line_diagram(selected_vl, spars)
if sel_ctx.get_selected() is not None:
sld_diagram_data=network.get_single_line_diagram(sel_ctx.get_selected(), spars)
if sld_widget==None:
sld_widget=display_sld(sld_diagram_data, enable_callbacks=True)
sld_widget.on_nextvl(lambda event: go_to_vl(event))
Expand All @@ -92,8 +92,8 @@ def update_sld_diagram(kv: bool = False):
else:
update_sld(sld_widget, sld_diagram_data, keep_viewbox=kv, enable_callbacks=True)


nadslider = widgets.IntSlider(value=selected_depth, min=0, max=20, step=1, description='depth:', disabled=False, continuous_update=False, orientation='horizontal', readout=True, readout_format='d')
nadslider = widgets.IntSlider(value=selected_depth, min=0, max=20, step=1, description='depth:', disabled=False,
continuous_update=False, orientation='horizontal', readout=True, readout_format='d')

def on_nadslider_changed(d):
nonlocal selected_depth
Expand All @@ -104,34 +104,34 @@ def on_nadslider_changed(d):

vl_input = widgets.Text(
value='',
placeholder='Voltage level ID',
placeholder='Voltage level Name' if use_name else 'Voltage level Id',
description='Filter',
disabled=False,
continuous_update=True
continuous_update=True,
layout=widgets.Layout(width='350px')
)

def on_text_changed(d):
nonlocal found
if d['new'] != '':
found.options = vls[vls.index.str.contains(d['new'], regex=False)].index
else:
found.options=list(vls.index)
found.value=selected_vl
sel_ctx.apply_filter(d['new'])
found.value=None
found.options = sel_ctx.get_filtered_vls_as_list()
if sel_ctx.is_selected_in_filtered_vls():
found.value=sel_ctx.get_selected()

vl_input.observe(on_text_changed, names='value')

found = widgets.Select(
options=list(vls.index),
value=selected_vl,
options=sel_ctx.get_filtered_vls_as_list(),
value=sel_ctx.get_selected(),
description='Found',
disabled=False,
layout=widgets.Layout(height='670px')
layout=widgets.Layout(width='350px', height='670px')
)

def on_selected(d):
nonlocal selected_vl
if d['new'] != None:
selected_vl=d['new']
sel_ctx.set_selected(d['new'])
update_nad_diagram()
update_sld_diagram()

Expand Down
56 changes: 56 additions & 0 deletions src/pypowsybl_jupyter/selectcontext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (c) 2024, RTE (http://www.rte-france.com)
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
#

from collections import OrderedDict
import pandas as pd
from pypowsybl.network import Network

class SelectContext:

def __init__(self, network:Network = None, vl_id : str = None, use_name:bool = True):
self.network = network
self.use_name = use_name
self.display_attribute = 'name' if use_name else 'id'

self.vls = network.get_voltage_levels(attributes=['name'])
self.vls['name'] = self.vls['name'].replace('', pd.NA).fillna(self.vls.index.to_series().astype(str))
self.vls['id'] = self.vls.index

self.vls = self.vls.sort_values(by=self.display_attribute) if use_name else self.vls.sort_index()

self.apply_filter(None)

self.set_selected(self.vls.index[0] if vl_id is None else vl_id)

def get_vls(self):
return self.vls

def set_selected(self, id):
if id in self.vls.index:
self.selected_vl = id
else:
raise ValueError(f'a voltage level with id={id} does not exist in the network.')

def get_selected(self):
return self.selected_vl

def apply_filter(self, sfilter, search_attribute = None):
if sfilter is not None and sfilter != '':
search_by = self.display_attribute if search_attribute is None else search_attribute
self.vls_filtered = self.vls[self.vls[search_by].str.contains(sfilter, case=False, na=False, regex=False)]
else:
self.vls_filtered = self.vls

def is_selected_in_filtered_vls(self):
return self.selected_vl in self.vls_filtered.index

def get_filtered_vls_as_list(self):
return list(zip(self.vls_filtered[self.display_attribute].values.tolist(), self.vls_filtered.index))

def extend_filtered_vls(self, id):
if (id in self.vls.index) and (id not in self.vls_filtered.index):
self.vls_filtered = pd.concat([self.vls_filtered, self.vls.loc[[id]]])

0 comments on commit ef08cac

Please sign in to comment.