Skip to content

Commit

Permalink
adds tie lines and hvdc lines to the map-viewer widget
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Biasuzzi <[email protected]>
  • Loading branch information
CBiasuzzi committed Aug 29, 2024
1 parent 69fd6c0 commit 629dd2b
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 7 deletions.
39 changes: 39 additions & 0 deletions examples/demo_mapviewer_features.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,45 @@
"# center the map on a specific substation\n",
"network_map2.center_on_substation('P1')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Example 3\n",
"# creates a network that contains lines and hvdc lines, and adds a substation and a tie line.\n",
"# Then, adds some geographical data extensions, not present in the original network (fictitious coordinates).\n",
"# Finally, display the network using the mapviewer widget\n",
"\n",
"four_substations_network = pn.create_four_substations_node_breaker_network()\n",
"\n",
"import pandas as pd\n",
"four_substations_network.create_substations(id='S5')\n",
"four_substations_network.create_voltage_levels(id='S5VL1', substation_id='S5', topology_kind='BUS_BREAKER', nominal_v=400)\n",
"four_substations_network.create_buses(id='S5VL1B1', voltage_level_id='S5VL1')\n",
"four_substations_network.create_dangling_lines(id='d1', voltage_level_id='S4VL1', node=1, p0=10, q0=3, r=0, x=5, g=0, b=1e-6)\n",
"four_substations_network.create_dangling_lines(id='d2', voltage_level_id='S5VL1', bus_id='S5VL1B1', p0=10, q0=3, r=0, x=5, g=0, b=1e-6)\n",
"four_substations_network.create_tie_lines(id='t1', dangling_line1_id='d1', dangling_line2_id='d2')\n",
"voltage_levels = four_substations_network.get_voltage_levels()\n",
"voltage_levels['name']=voltage_levels.index\n",
"coords = pd.DataFrame(\n",
" [\n",
" [voltage_levels.loc[voltage_levels[\"name\"] == \"S1VL1\", \"substation_id\"].iloc[0], 2.9653506942899255, 46.648820589226624],\n",
" [voltage_levels.loc[voltage_levels[\"name\"] == \"S2VL1\", \"substation_id\"].iloc[0], 4.4868965299190675, 45.224159481986970],\n",
" [voltage_levels.loc[voltage_levels[\"name\"] == \"S3VL1\", \"substation_id\"].iloc[0], 2.4022589283484335, 44.090773206380350],\n",
" [voltage_levels.loc[voltage_levels[\"name\"] == \"S4VL1\", \"substation_id\"].iloc[0], 6.0803264207747825, 44.279779791063575],\n",
" [voltage_levels.loc[voltage_levels[\"name\"] == \"S5VL1\", \"substation_id\"].iloc[0], 7.4221621183374900, 44.279779791063575]\n",
" ], columns=[\"id\", \"longitude\", \"latitude\"]\n",
").set_index(\"id\")\n",
"\n",
"four_substations_network.remove_extensions('substationPosition', four_substations_network.get_extensions('substationPosition').index)\n",
"four_substations_network.create_extensions('substationPosition', coords[[\"latitude\", \"longitude\"]])\n",
"\n",
"network_map3=NetworkMapWidget(four_substations_network)\n",
"display(network_map3)"
]
}
],
"metadata": {
Expand Down
22 changes: 17 additions & 5 deletions js/networkmapwidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,16 @@ const darkTheme = createTheme({
});

class WidgetMapEquipments extends MapEquipments {
initEquipments(smapdata, lmapdata) {
initEquipments(smapdata, lmapdata, tlmapdata, hlmapdata) {
this.updateSubstations(smapdata, true);
this.updateLines(lmapdata, true);
this.updateTieLines(tlmapdata, true);
this.updateHvdcLines(hlmapdata, true);
}

constructor(smapdata, lmapdata) {
constructor(smapdata, lmapdata, tlmapdata, hlmapdata) {
super();
this.initEquipments(smapdata, lmapdata);
this.initEquipments(smapdata, lmapdata, tlmapdata, hlmapdata);
}
}

Expand All @@ -86,6 +88,8 @@ const render = createRender(() => {
const [lpos] = useModelState('lpos');
const [smap] = useModelState('smap');
const [lmap] = useModelState('lmap');
const [tlmap] = useModelState('tlmap');
const [hlmap] = useModelState('hlmap');

const [use_name] = useModelState('use_name');

Expand All @@ -101,7 +105,7 @@ const render = createRender(() => {

const [equipmentData, setEquipmentData] = useState({
gdata: new GeoData(new Map(), new Map()),
edata: new WidgetMapEquipments([], []),
edata: new WidgetMapEquipments([], [], [], []),
});

useEffect(() => {
Expand All @@ -111,7 +115,9 @@ const render = createRender(() => {
geoData.setLinePositions(JSON.parse(lpos));
const mapEquipments = new WidgetMapEquipments(
JSON.parse(smap),
JSON.parse(lmap)
JSON.parse(lmap),
JSON.parse(tlmap),
JSON.parse(hlmap)
);
resolve({ gdata: geoData, edata: mapEquipments });
});
Expand Down Expand Up @@ -251,6 +257,12 @@ const render = createRender(() => {
onLineMenuClick={(equipment, x, y) =>
showEquipmentMenu(equipment, x, y, 'line')
}
onTieLineMenuClick={(equipment, x, y) =>
showEquipmentMenu(equipment, x, y, 'tie-line')
}
onHvdcLineMenuClick={(equipment, x, y) =>
showEquipmentMenu(equipment, x, y, 'hvdc-line')
}
onVoltageLevelMenuClick={(equipment, x, y) => {
console.log(
`# VoltageLevel menu click: ${JSON.stringify(
Expand Down
48 changes: 46 additions & 2 deletions src/pypowsybl_jupyter/networkmapwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
CallbackDispatcher
)

import pandas as pd

from pypowsybl.network import Network

class NetworkMapWidget(anywidget.AnyWidget):
Expand Down Expand Up @@ -48,6 +50,8 @@ class NetworkMapWidget(anywidget.AnyWidget):
lpos = traitlets.Unicode().tag(sync=True)
smap = traitlets.Unicode().tag(sync=True)
lmap = traitlets.Unicode().tag(sync=True)
tlmap = traitlets.Unicode().tag(sync=True)
hlmap = traitlets.Unicode().tag(sync=True)

use_name = traitlets.Bool().tag(sync=True)

Expand All @@ -61,11 +65,13 @@ class NetworkMapWidget(anywidget.AnyWidget):
def __init__(self, network:Network, subId:str = None, use_name:bool = True, display_lines:bool = True, use_line_extensions = False, nominal_voltages_top_tiers_filter = -1, **kwargs):
super().__init__(**kwargs)

(lmap, lpos, smap, spos, vl_subs, sub_vls, subs_ids) = self.extract_map_data(network, display_lines, use_line_extensions)
(lmap, lpos, smap, spos, vl_subs, sub_vls, subs_ids, tlmap, hlmap) = self.extract_map_data(network, display_lines, use_line_extensions)
self.lmap=json.dumps(lmap)
self.lpos=json.dumps(lpos)
self.smap=json.dumps(smap)
self.spos=json.dumps(spos)
self.tlmap=json.dumps(tlmap)
self.hlmap=json.dumps(hlmap)
self.use_name=use_name
self.params={"subId": subId}
self.vl_subs=vl_subs
Expand Down Expand Up @@ -101,11 +107,45 @@ def center_on_voltage_level(self, vl_id):
if sub_id is not None:
self.params = {"subId": sub_id}

def get_tie_lines_info(self, network, vls_with_coords):
ties_df=network.get_tie_lines().reset_index()[['id', 'name', 'dangling_line1_id', 'dangling_line2_id']]
danglings_df=network.get_dangling_lines().reset_index()[['id', 'name', 'p', 'i', 'voltage_level_id', 'connected']]
tie_lines_info=[]
if not(ties_df.empty or danglings_df.empty):
tie_d_1 = pd.merge(ties_df, danglings_df, left_on='dangling_line1_id', right_on='id', suffixes=('', '_d1'))
tie_d_1.rename(columns={'name': 'name_T', 'dangling_line1_id_d1': 'dangling_line1_id_d1_D', 'id_d1': 'd_id1_D'}, inplace=True)
tie_d_2 = pd.merge(tie_d_1, danglings_df, left_on='dangling_line2_id', right_on='id', suffixes=('_d1', '_d2'))
tie_res = tie_d_2[['id_d1' ,'name_T', 'voltage_level_id_d1', 'voltage_level_id_d2', 'connected_d1', 'connected_d2', 'p_d1', 'p_d2', 'i_d1', 'i_d2']]
tie_res = tie_res.fillna(0)
tie_res.columns = ['id', 'name', 'voltageLevelId1', 'voltageLevelId2', 'terminal1Connected', 'terminal2Connected', 'p1', 'p2', 'i1', 'i2']
tie_res=tie_res[tie_res['voltageLevelId1'].isin(vls_with_coords.index) & tie_res['voltageLevelId2'].isin(vls_with_coords.index)]
tie_lines_info = tie_res.to_dict(orient='records')
return tie_lines_info

def get_hvdc_lines_info(self, network, vls_with_coords):
hvdc_lines_df = network.get_hvdc_lines().reset_index()[['id', 'name', 'converters_mode', 'converter_station1_id', 'converter_station2_id', 'connected1', 'connected2']]
lcc_stations_df = network.get_lcc_converter_stations().reset_index()[['id', 'name', 'p', 'i', 'voltage_level_id']]
vsc_stations_df = network.get_vsc_converter_stations().reset_index()[['id', 'name', 'p', 'i', 'voltage_level_id']]
stations_df = pd.concat([lcc_stations_df, vsc_stations_df])
hvdc_lines_info = []
if not(hvdc_lines_df.empty or stations_df.empty):
hvdc_s_1=pd.merge(hvdc_lines_df, stations_df, left_on='converter_station1_id', right_on='id', suffixes=('', '_s1'))
hvdc_s_1.rename(columns={'name': 'name_S', 'id_s1': 'id_s1_S'}, inplace=True)
hvdc_s_2=pd.merge(hvdc_s_1, stations_df, left_on='converter_station2_id', right_on='id', suffixes=('_s1', '_s2'))
h_res= hvdc_s_2[['id_s1' ,'name_S', 'voltage_level_id_s1', 'voltage_level_id_s2', 'connected1', 'connected2', 'p_s1', 'p_s2', 'i_s1', 'i_s2']]
h_res = h_res.fillna(0)
h_res.columns = ['id', 'name', 'voltageLevelId1', 'voltageLevelId2', 'terminal1Connected', 'terminal2Connected', 'p1', 'p2', 'i1', 'i2']
h_res=h_res[h_res['voltageLevelId1'].isin(vls_with_coords.index) & h_res['voltageLevelId2'].isin(vls_with_coords.index)]
hvdc_lines_info = h_res.to_dict(orient='records')
return hvdc_lines_info

def extract_map_data(self, network, display_lines, use_line_extensions):
lmap = []
lpos = []
smap = []
spos = []
tlmap = []
hlmap = []

vl_subs = dict()
sub_vls = dict()
Expand Down Expand Up @@ -151,6 +191,10 @@ def extract_map_data(self, network, display_lines, use_line_extensions):
coordinates = [{'lat': coord['latitude'], 'lon': coord['longitude']} for coord in lines_positions_from_extensions_grouped_df.get(id_val, [])]
if coordinates:
lpos.append({'id': id_val, 'coordinates': coordinates})

vls_with_coords = vls_subs_df.set_index('id')[[]]
tlmap = self.get_tie_lines_info(network, vls_with_coords)
hlmap = self.get_hvdc_lines_info(network, vls_with_coords)

# note that if there are no linePositions for a line, the viewer component draws the lines using the substation positions

Expand Down Expand Up @@ -184,7 +228,7 @@ def extract_map_data(self, network, display_lines, use_line_extensions):
sub_vls = vls_df.groupby('substation_id')['id'].apply(list).to_dict()
subs_ids = set(network.get_substations().reset_index()['id'])

return (lmap, lpos, smap, spos, vl_subs, sub_vls, subs_ids)
return (lmap, lpos, smap, spos, vl_subs, sub_vls, subs_ids, tlmap, hlmap)

def extract_nominal_voltage_list(self, network, nvls_top_tiers):
nvls_filtered = []
Expand Down

0 comments on commit 629dd2b

Please sign in to comment.