Multi Link Redundancy#
The multi-link redundancy analysis returns the same types of results as the single-link redundancy analysis, but it considers multiple links failing simultaneously. This is particularly useful for assessing the resilience of a network to natural disasters that may affect several links at once.
Run Multi-Link Redundancy Analysis#
The workflow is therefore very similar to the single-link redundancy analysis, with the difference being that a hazard map must be specified. See the Single Link Redundancy tutorial for more details on the workflow.
We select the analysis type as AnalysisLossesEnum.MULTI_LINK_REDUNDANCY, for which we can also set a threshold for the water depth. The threshold defines the hazard value above which a link is considered impassable/disrupted.
Step 1: Import Libraries and Set Paths#
We start by importing the required libraries and defining the root directory and network path.
[1]:
from pathlib import Path
import geopandas as gpd
from IPython.core.display_functions import display
from ra2ce.analysis.analysis_config_data.analysis_config_data import AnalysisSectionLosses, AnalysisConfigData
from ra2ce.analysis.analysis_config_data.enums.analysis_losses_enum import AnalysisLossesEnum
from ra2ce.analysis.analysis_config_data.enums.weighing_enum import WeighingEnum
from ra2ce.network.network_config_data.enums.aggregate_wl_enum import AggregateWlEnum
from ra2ce.network.network_config_data.network_config_data import NetworkSection, NetworkConfigData, HazardSection
from ra2ce.network.network_config_data.enums.source_enum import SourceEnum
from ra2ce.ra2ce_handler import Ra2ceHandler
root_dir = Path('data', 'multi_link_redundancy')
network_path = root_dir / "network"
hazard_path = root_dir / "hazard"
c:\Users\hauth\miniforge3\envs\ra2ce_env\Lib\site-packages\tqdm\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
Step 2: Define Network and Analysis Configuration#
[3]:
network_section = NetworkSection(
source=SourceEnum.SHAPEFILE,
primary_file=network_path.joinpath("base_shapefile.shp"),
file_id="ID",
save_gpkg=True)
hazard_section = HazardSection(
hazard_map=[hazard_path.joinpath("max_flood_depth.tif")],
hazard_id='Flood',
hazard_field_name="waterdepthtt",
aggregate_wl=AggregateWlEnum.MIN,
hazard_crs="EPSG:32736",
)
network_config_data = NetworkConfigData(
root_path= root_dir,
static_path=root_dir.joinpath('static'),
network=network_section,
hazard=hazard_section,
)
analyse_section = AnalysisSectionLosses(
name="tutorial_multi_link_redundancy",
analysis=AnalysisLossesEnum.MULTI_LINK_REDUNDANCY,
threshold=0.3, # roads with a flood depth above this value are considered impassable
weighing=WeighingEnum.LENGTH,
save_csv=True,
save_gpkg=True,
)
analysis_config_data = AnalysisConfigData(
output_path=root_dir.joinpath("output"),
static_path=root_dir.joinpath('static'),
analyses=[analyse_section],
aggregate_wl=AggregateWlEnum.MIN,
)
handler = Ra2ceHandler.from_config(network=network_config_data, analysis=analysis_config_data)
handler.configure()
handler.run_analysis()
100%|██████████| 4217/4217 [00:00<00:00, 370052.09it/s]
2025-10-01 05:04:27 PM - [avg_speed_calculator.py:175] - root - WARNING - No valid file found with average speeds data\multi_link_redundancy\static\output_graph\avg_speed.csv, calculating and saving them instead.
2025-10-01 05:04:27 PM - [avg_speed_calculator.py:175] - root - WARNING - No valid file found with average speeds data\multi_link_redundancy\static\output_graph\avg_speed.csv, calculating and saving them instead.
2025-10-01 05:04:27 PM - [avg_speed_calculator.py:150] - root - WARNING - Default speed have been assigned to road type [<RoadTypeEnum.SECONDARY_LINK: 8>]. Please check the average speed CSV, enter the right average speed for this road type and run RA2CE again.
2025-10-01 05:04:27 PM - [avg_speed_calculator.py:150] - root - WARNING - Default speed have been assigned to road type [<RoadTypeEnum.SECONDARY_LINK: 8>]. Please check the average speed CSV, enter the right average speed for this road type and run RA2CE again.
2025-10-01 05:04:27 PM - [avg_speed_calculator.py:150] - root - WARNING - Default speed have been assigned to road type [<RoadTypeEnum.SECONDARY: 7>]. Please check the average speed CSV, enter the right average speed for this road type and run RA2CE again.
2025-10-01 05:04:27 PM - [avg_speed_calculator.py:150] - root - WARNING - Default speed have been assigned to road type [<RoadTypeEnum.SECONDARY: 7>]. Please check the average speed CSV, enter the right average speed for this road type and run RA2CE again.
2025-10-01 05:04:29 PM - [hazard_overlay.py:380] - root - WARNING - Hazard crs EPSG:32736 and graph crs EPSG:4326 are inconsistent, we try to reproject the graph crs
2025-10-01 05:04:29 PM - [hazard_overlay.py:380] - root - WARNING - Hazard crs EPSG:32736 and graph crs EPSG:4326 are inconsistent, we try to reproject the graph crs
Graph hazard overlay with max_flood_depth: 100%|██████████| 2109/2109 [00:02<00:00, 785.28it/s]
Graph fraction with hazard overlay with max_flood_depth: 100%|██████████| 2109/2109 [00:29<00:00, 71.65it/s]
2025-10-01 05:05:02 PM - [hazard_overlay.py:461] - root - WARNING - Hazard crs EPSG:32736 and gdf crs EPSG:4326 are inconsistent, we try to reproject the gdf crs
2025-10-01 05:05:02 PM - [hazard_overlay.py:461] - root - WARNING - Hazard crs EPSG:32736 and gdf crs EPSG:4326 are inconsistent, we try to reproject the gdf crs
2025-10-01 05:05:02 PM - [hazard_intersect_builder_for_tif.py:225] - root - WARNING - Some geometries have NoneType objects (no coordinate information), namely: Empty GeoDataFrame
Columns: [link_id, ID, highway, avgspeed, geometry, lanes, length, maxspeed, bridge, node_A, node_B, edge_fid, rfid_c, rfid, time]
Index: [].This could be due to segmentation, and might cause an exception in hazard overlay
2025-10-01 05:05:02 PM - [hazard_intersect_builder_for_tif.py:225] - root - WARNING - Some geometries have NoneType objects (no coordinate information), namely: Empty GeoDataFrame
Columns: [link_id, ID, highway, avgspeed, geometry, lanes, length, maxspeed, bridge, node_A, node_B, edge_fid, rfid_c, rfid, time]
Index: [].This could be due to segmentation, and might cause an exception in hazard overlay
Network hazard overlay with max_flood_depth: 100%|██████████| 2121/2121 [00:01<00:00, 1092.98it/s]
Network fraction with hazard overlay with max_flood_depth: 100%|██████████| 2121/2121 [00:37<00:00, 57.18it/s]
[3]:
[AnalysisResultWrapper(results_collection=[AnalysisResult(analysis_result= u v link_id ID highway avgspeed \
0 0 1 None None tertiary 60.0
1 0 2 None None tertiary 58.0
2 0 3 None None tertiary 58.0
3 1 465 None None tertiary 60.0
4 1 663 None None residential 60.0
... ... ... ... ... ... ...
4213 1481 1482 None None residential 60.0
4214 1481 1483 None None residential 60.0
4215 1481 1484 None None residential 60.0
4216 1508 1512 None None residential 60.0
4217 1513 1514 None None residential 60.0
geometry lanes length_x \
0 LINESTRING (34.87673 -19.85047, 34.87737 -19.8... nan 70.0
1 LINESTRING (34.87673 -19.85047, 34.87642 -19.8... nan 70.0
2 LINESTRING (34.87606 -19.85055, 34.87628 -19.8... nan 71.0
3 LINESTRING (34.87780 -19.85016, 34.87737 -19.8... nan 47.0
4 LINESTRING (34.87717 -19.84969, 34.87737 -19.8... nan 69.0
... ... ... ...
4213 LINESTRING (34.85976 -19.83977, 34.86006 -19.8... nan 113.0
4214 LINESTRING (34.86077 -19.84001, 34.86071 -19.8... nan 68.0
4215 LINESTRING (34.86003 -19.83896, 34.86014 -19.8... nan 78.0
4216 LINESTRING (34.88623 -19.83527, 34.88641 -19.8... nan 139.0
4217 LINESTRING (34.85359 -19.83575, 34.85383 -19.8... nan 55.0
maxspeed ... rfid time EV1_mi EV1_fr alt_length alt_nodes \
0 60 ... 4 0.001 0.000000 0.000000 NaN nan
1 nan ... 7 0.001 0.000000 0.000000 NaN nan
2 nan ... 10 0.001 0.000000 0.000000 NaN nan
3 60 ... 1470 0.001 0.000000 0.000000 NaN nan
4 nan ... 2066 0.001 0.000000 0.000000 NaN nan
... ... ... ... ... ... ... ... ...
4213 nan ... 4175 0.002 0.000000 0.000000 NaN nan
4214 nan ... 4176 0.001 0.000000 0.000000 NaN nan
4215 nan ... 4177 0.001 0.000000 0.000000 NaN nan
4216 nan ... 4209 0.002 0.056350 0.983086 NaN nan
4217 nan ... 4213 0.001 0.180663 0.167520 NaN nan
diff_length connected length_y hazard
0 NaN nan NaN EV1_mi
1 NaN nan NaN EV1_mi
2 NaN nan NaN EV1_mi
3 NaN nan NaN EV1_mi
4 NaN nan NaN EV1_mi
... ... ... ... ...
4213 NaN nan NaN EV1_mi
4214 NaN nan NaN EV1_mi
4215 NaN nan NaN EV1_mi
4216 NaN nan NaN EV1_mi
4217 NaN nan NaN EV1_mi
[4218 rows x 25 columns], analysis_config=AnalysisSectionLosses(name='tutorial_multi_link_redundancy', save_gpkg=True, save_csv=True, analysis=<AnalysisLossesEnum.MULTI_LINK_REDUNDANCY: 2>, weighing=<WeighingEnum.LENGTH: 1>, production_loss_per_capita_per_hour=nan, traffic_period=<TrafficPeriodEnum.DAY: 1>, hours_per_traffic_period=0, trip_purposes=[<TripPurposeEnum.NONE: 0>], resilience_curves_file=None, traffic_intensities_file=None, values_of_time_file=None, threshold=0.3, threshold_destinations=nan, equity_weight='', calculate_route_without_disruption=False, buffer_meters=nan, category_field_name='', save_traffic=False, event_type=<EventTypeEnum.NONE: 0>, risk_calculation_mode=<RiskCalculationModeEnum.NONE: 0>, risk_calculation_year=0), output_path=WindowsPath('data/multi_link_redundancy/output'), _custom_name='')])]
Inspect Results#
[ ]:
analysis_output_folder = root_dir.joinpath("output", "multi_link_redundancy")
redundancy_gdf = gpd.read_file(analysis_output_folder/"tutorial_multi_link_redundancy.gpkg") # specify the name of the geopackage holding your results (can be found in the analysis output folder)
redundancy_gdf.head() # display the attributes of the file
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 10))
redundancy_gdf.plot(column='alt_length', ax=ax, legend=False, cmap='viridis')
plt.title('Multi Link Redundancy Analysis Results')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.grid(True)
plt.show()