@@ -152,6 +152,7 @@ def arc_ceil(
152152 key = f"{ adj_id } :{ i } -{ j } " ,
153153 link_type = "intra_metro" ,
154154 base_capacity = base_capacity ,
155+ target_capacity = base_capacity ,
155156 cost = cost ,
156157 adjacency_id = adj_id ,
157158 distance_km = cost ,
@@ -275,6 +276,7 @@ def arc_ceil(
275276 key = f"{ adj_id } :{ dc } -{ p } " ,
276277 link_type = "dc_to_pop" ,
277278 base_capacity = base_capacity ,
279+ target_capacity = base_capacity ,
278280 cost = cost_arc ,
279281 adjacency_id = adj_id ,
280282 distance_km = cost_arc ,
@@ -345,6 +347,7 @@ def _add_inter_metro_edges(
345347 key = f"{ adj_id } :{ p } -{ q } " ,
346348 link_type = "inter_metro_corridor" ,
347349 base_capacity = base_capacity ,
350+ target_capacity = base_capacity ,
348351 cost = cost ,
349352 adjacency_id = adj_id ,
350353 distance_km = cost ,
@@ -822,6 +825,19 @@ def tm_based_size_capacities(
822825 h_factor = float (getattr (sizing_cfg , "headroom" , 1.3 ))
823826 respect_min = bool (getattr (sizing_cfg , "respect_min_base_capacity" , True ))
824827
828+ # Snapshot pre-adjustment aggregated capacities per directed metro corridor
829+ prev_corridor_caps : dict [tuple [str , str ], float ] = {}
830+ for _u , _v , _k , data in G .edges (keys = True , data = True ):
831+ if str (data .get ("link_type" )) != "inter_metro_corridor" :
832+ continue
833+ src_name = data .get ("source_metro" )
834+ tgt_name = data .get ("target_metro" )
835+ if not isinstance (src_name , str ) or not isinstance (tgt_name , str ):
836+ continue
837+ prev_cap = float (data .get ("base_capacity" , data .get ("capacity" , 0.0 )))
838+ key = (src_name , tgt_name )
839+ prev_corridor_caps [key ] = prev_corridor_caps .get (key , 0.0 ) + prev_cap
840+
825841 # Apply sized capacities to inter-metro edges in G
826842 for (u_g , v_g , k_g ), L in edge_loads .items ():
827843 try :
@@ -840,6 +856,55 @@ def tm_based_size_capacities(
840856 prev = float (data .get ("base_capacity" , 0.0 ))
841857 new_base = max (prev , sized ) if respect_min else sized
842858 G .edges [u_g , v_g , k_g ]["base_capacity" ] = new_base
859+ # Keep target_capacity aligned with total intended capacity for the adjacency
860+ G .edges [u_g , v_g , k_g ]["target_capacity" ] = new_base
861+
862+ # Aggregate post-adjustment capacities per directed metro corridor
863+ post_corridor_caps : dict [tuple [str , str ], float ] = {}
864+ for _u2 , _v2 , _k2 , data in G .edges (keys = True , data = True ):
865+ if str (data .get ("link_type" )) != "inter_metro_corridor" :
866+ continue
867+ src_name = data .get ("source_metro" )
868+ tgt_name = data .get ("target_metro" )
869+ if not isinstance (src_name , str ) or not isinstance (tgt_name , str ):
870+ continue
871+ cap = float (data .get ("base_capacity" , data .get ("capacity" , 0.0 )))
872+ key = (src_name , tgt_name )
873+ post_corridor_caps [key ] = post_corridor_caps .get (key , 0.0 ) + cap
874+
875+ # Log corridor capacity deltas (per directed pair)
876+ if post_corridor_caps :
877+ try :
878+ total_before = sum (
879+ prev_corridor_caps .get (k , 0.0 ) for k in post_corridor_caps
880+ )
881+ total_after = sum (post_corridor_caps .values ())
882+ logger .info (
883+ "TM sizing: corridor capacity totals (Gbps) before=%s after=%s delta=%s" ,
884+ f"{ total_before :,.1f} " ,
885+ f"{ total_after :,.1f} " ,
886+ f"{ (total_after - total_before ):,.1f} " ,
887+ )
888+ # Only emit lines for pairs that changed
889+ for pair in sorted (post_corridor_caps ):
890+ before = float (prev_corridor_caps .get (pair , 0.0 ))
891+ after = float (post_corridor_caps [pair ])
892+ if abs (after - before ) <= 0.0 :
893+ continue
894+ factor = (after / before ) if before > 0.0 else float ("inf" )
895+ src , dst = pair
896+ logger .info (
897+ "TM sizing: %s -> %s corridor capacity %s -> %s (delta=%s, x%s)" ,
898+ src ,
899+ dst ,
900+ f"{ before :,.1f} " ,
901+ f"{ after :,.1f} " ,
902+ f"{ (after - before ):,.1f} " ,
903+ "inf" if not (factor < float ("inf" )) else f"{ factor :.2f} " ,
904+ )
905+ except Exception :
906+ # Logging must not affect sizing; swallow any formatting errors
907+ pass
843908
844909 # Compute PoP egress based on sized corridor capacities
845910 pop_egress : dict [str , float ] = {}
@@ -881,6 +946,7 @@ def tm_based_size_capacities(
881946 prev = float (data .get ("base_capacity" , 0.0 ))
882947 new_base = max (prev , sized ) if respect_min else sized
883948 G .edges [u_g , v_g , k_g ]["base_capacity" ] = new_base
949+ G .edges [u_g , v_g , k_g ]["target_capacity" ] = new_base
884950
885951 # Derive PoP<->PoP (intra-metro) base capacities
886952 for u_g , v_g , k_g , data in G .edges (keys = True , data = True ):
@@ -895,6 +961,7 @@ def tm_based_size_capacities(
895961 prev = float (data .get ("base_capacity" , 0.0 ))
896962 new_base = max (prev , sized ) if respect_min else sized
897963 G .edges [u_g , v_g , k_g ]["base_capacity" ] = new_base
964+ G .edges [u_g , v_g , k_g ]["target_capacity" ] = new_base
898965
899966 logger .info (
900967 "TM sizing: applied capacities (Q=%s Gb/s, h=%s, alpha=%s, beta=%s)" ,
@@ -990,6 +1057,12 @@ def to_network_sections(
9901057 "source_metro" : data .get ("source_metro" ),
9911058 "target_metro" : data .get ("target_metro" ),
9921059 }
1060+ # Emit total expected capacity for the adjacency (pre per-link split)
1061+ try :
1062+ tcap = float (data .get ("target_capacity" , data .get ("base_capacity" , 0.0 )))
1063+ except Exception :
1064+ tcap = float (data .get ("base_capacity" , 0.0 ))
1065+ attrs ["target_capacity" ] = tcap
9931066 # Tag this adjacency with a stable site-edge key and optional adjacency id
9941067 attrs ["site_edge" ] = f"{ u } |{ v } |{ k } "
9951068 if "adjacency_id" in data :
0 commit comments