Disruptions =========== ``Network.disrupt`` reroutes OD flows whose existing ``edge_path`` includes a failed edge. The result separates rerouted flows, isolated demand, updated edge totals, and rerouting losses. Isolated demand --------------- If a failed edge removes the only available path, the affected demand appears in ``isolated_od`` and no rerouted flow is returned. >>> import pandas as pd >>> from transport_flow_model.model import Network, ODFlows >>> network = Network( ... pd.DataFrame( ... { ... "edge_from": ["A", "B"], ... "edge_to": ["B", "C"], ... "edge_id": ["AB", "BC"], ... "cost": [1, 1], ... "capacity": [100, 100], ... } ... ) ... ) >>> existing_flows = ODFlows( ... pd.DataFrame( ... { ... "origin_id": ["A"], ... "destination_id": ["C"], ... "flow": [10], ... "edge_path": [["AB", "BC"]], ... "cost": [2], ... } ... ) ... ) >>> isolated = network.disrupt(existing_flows, ["AB"], directed=True) >>> isolated.rerouted_flows.to_dataframe().empty True >>> isolated.isolated_od.to_dataframe().to_dict("records") [{'origin_id': 'A', 'destination_id': 'C', 'flow': 10}] Rerouting to an alternative path -------------------------------- If an alternative path exists, the affected flow is rerouted. Comparing the original path cost with the disrupted path cost gives rerouting losses as ``result.losses``. >>> reroute_network = Network( ... pd.DataFrame( ... { ... "edge_from": ["A", "B", "A"], ... "edge_to": ["B", "C", "C"], ... "edge_id": ["AB", "BC", "AC"], ... "cost": [1, 1, 5], ... "capacity": [100, 100, 100], ... } ... ) ... ) >>> result = reroute_network.disrupt(existing_flows, ["AB"], directed=True) >>> result.rerouted_flows.to_dataframe().to_dict("records") [{'origin_id': 'A', 'destination_id': 'C', 'flow': 10, 'edge_path': ['AC'], 'cost': 5}] >>> result.losses.to_dataframe().to_dict("records") [{'origin_id': 'A', 'destination_id': 'C', 'flow': 10, 'initial_cost': 2, 'disrupted_cost': 5, 'rerouting_loss': 3}] >>> result.network_flows.to_dataframe().set_index("edge_id")["flow"].to_dict() {'AB': 0, 'BC': 0, 'AC': 10} Unaffected flows stay on the network ------------------------------------ Only OD flows whose path includes a failed edge are rerouted. Other OD flows remain in the returned ``network_flows`` totals. >>> partial_network = Network( ... pd.DataFrame( ... { ... "edge_from": ["A", "B", "A", "C"], ... "edge_to": ["B", "C", "C", "D"], ... "edge_id": ["AB", "BC", "AC", "CD"], ... "cost": [1, 1, 5, 1], ... "capacity": [100, 100, 100, 100], ... } ... ) ... ) >>> partial_flows = ODFlows( ... pd.DataFrame( ... { ... "origin_id": ["A", "C"], ... "destination_id": ["C", "D"], ... "flow": [10, 4], ... "edge_path": [["AB", "BC"], ["CD"]], ... "cost": [2, 1], ... } ... ) ... ) >>> partial = partial_network.disrupt(partial_flows, ["AB"], directed=True) >>> partial.network_flows.to_dataframe().set_index("edge_id")["flow"].to_dict() {'AB': 0, 'BC': 0, 'AC': 10, 'CD': 4} Capacity-constrained disruption ------------------------------- Disruption rerouting is capacity constrained by default. If the alternate path has only enough capacity for part of the affected flow, any residual demand is isolated. >>> capacity_network = Network( ... pd.DataFrame( ... { ... "edge_from": ["A", "B", "A", "D"], ... "edge_to": ["B", "C", "D", "C"], ... "edge_id": ["AB", "BC", "AD", "DC"], ... "cost": [1, 1, 3, 3], ... "capacity": [15, 15, 10, 10], ... } ... ) ... ) >>> capacity_flows = ODFlows( ... pd.DataFrame( ... { ... "origin_id": ["A"], ... "destination_id": ["C"], ... "flow": [15], ... "edge_path": [["AB", "BC"]], ... "cost": [2], ... } ... ) ... ) >>> capacity_result = capacity_network.disrupt( ... capacity_flows, ["AB"], directed=True ... ) >>> capacity_result.rerouted_flows.to_dataframe().to_dict("records") [{'origin_id': 'A', 'destination_id': 'C', 'flow': 10, 'edge_path': ['AD', 'DC'], 'cost': 6}] >>> capacity_result.isolated_od.to_dataframe().to_dict("records") [{'origin_id': 'A', 'destination_id': 'C', 'flow': 5}] >>> capacity_result.network_flows.to_dataframe().set_index("edge_id")["flow"].to_dict() {'AB': 0, 'BC': 0, 'AD': 10, 'DC': 10}