"""Pipe-length-weighted demand allocation. This module deliberately accepts plain topology data and performs no database or file access. Application services are responsible for loading topology. """ from typing import Any, Mapping def allocate_demand_by_pipe_length( demand: float, topology_nodes: Mapping[str, Mapping[str, Any]], topology_links: Mapping[str, Mapping[str, Any]], ) -> dict[str, float]: """Allocate total demand to junctions by half of each incident link length.""" if not topology_nodes or not topology_links or demand == 0.0: return {} total_link_length = sum( abs(float(link["length"])) for link in topology_links.values() ) if total_link_length <= 0.0: return {} demand_per_length = demand / total_link_length result: dict[str, float] = {} for node_id, node in topology_nodes.items(): if node["type"] != "junction": continue incident_length = sum( abs(float(topology_links[link_id]["length"])) for link_id in node["links"] ) result[node_id] = incident_length * demand_per_length * 0.5 return result