@@ -390,42 +390,76 @@ def get_max_line_usage(
390390 line_locations : pd .DataFrame ,
391391 rated_line_capacities : dict [tuple [str , str ], int ],
392392 ) -> pd .DataFrame :
393+ """Calculates the maximum utilization for each transmission line.
394+
395+ This function takes the flow results from an optimization model,
396+ determines the peak flow on each line over the entire simulation horizon,
397+ and then calculates the utilization of each line as a percentage of its
398+ rated capacity. It also merges location data for the lines.
399+
400+ Args:
401+ flow_variables (pd.DataFrame): DataFrame containing flow values for each
402+ line at each timestep. Expected columns: 'node_a', 'node_b',
403+ 'value' (flow magnitude), and 'hour'.
404+ line_locations (pd.DataFrame): DataFrame containing location or other
405+ metadata for each line. Expected to be indexed by a
406+ MultiIndex ('source', 'sink').
407+ rated_line_capacities (dict[tuple[str, str], int]): Dictionary mapping
408+ line tuples (source_node, sink_node) to their rated
409+ power capacity (e.g., in MW).
410+
411+ Returns:
412+ pd.DataFrame: A DataFrame indexed by ('source', 'sink') with columns
413+ including 'max_line_usage' (peak flow / rated capacity),
414+ columns from `line_locations`, and 'rated_capacity'.
415+ """
393416
394417 # Prevent unintentional modification to the original dataframe
395- flow_variables = flow_variables .copy ()
418+ flow_vars = flow_variables .copy ()
396419
397- flow_variables = flow_variables .rename (
420+ # Standardize column names and remove unnecessary columns
421+ flow_vars = flow_vars .rename (
398422 columns = {"node_a" : "source" , "node_b" : "sink" }
399- ).drop ("hour" , axis = 1 )
400- # Flow can be negative due to flow being directional
401- flow_variables [ "value" ] = flow_variables [ "value" ]. abs ()
423+ ).drop (
424+ "hour" , axis = 1
425+ ) # Assuming 'hour' is not needed for max usage across all time
402426
403427 # Find the max_value for each line segment across the whole time horizon
404- flow_variables ["max_value" ] = flow_variables .groupby (["source" , "sink" ])[
428+ # Flow variables are non-negative, so we can use max() to find the peak flow.
429+ flow_vars ["max_value" ] = flow_vars .groupby (["source" , "sink" ])[
405430 "value"
406431 ].transform ("max" )
432+
407433 # Drop duplicates because we are only interested in the maximum flow
408- # over the whole simulation
409- flow_variables = flow_variables .drop_duplicates (subset = ["source" , "sink" ])
434+ # over the whole simulation for each unique line
435+ flow_vars = flow_vars .drop_duplicates (subset = ["source" , "sink" ])
410436
411- # Max utilization rate
412- flow_variables ["max_line_usage" ] = flow_variables .apply (
437+ # Calculate maximum utilization rate
438+ # Ensure that the (row["source"], row["sink"]) tuple exactly matches the keys in rated_line_capacities
439+ flow_vars ["max_line_usage" ] = flow_vars .apply (
413440 lambda row : row ["max_value" ]
414441 / rated_line_capacities [(row ["source" ], row ["sink" ])],
415442 axis = 1 ,
416443 ).round (4 )
417444
418- flow_variables = flow_variables [["source" , "sink" , "max_line_usage" ]].set_index (
419- ["source" , "sink" ]
420- )
421- flow_variables = flow_variables .merge (
445+ # Select and re-index the DataFrame
446+ flow_vars = flow_vars [
447+ ["source" , "sink" , "max_value" , "max_line_usage" ]
448+ ].set_index (["source" , "sink" ])
449+
450+ # Merge with line location data
451+ # The index of flow_vars is now (source, sink)
452+ # line_locations should also be indexed by (source, sink) for a clean merge
453+ flow_vars = flow_vars .merge (
422454 line_locations , how = "left" , left_index = True , right_index = True
423455 )
424- # Append rated capacities
425- flow_variables ["rated_capacity" ] = [
426- rated_line_capacities [idx ] for idx in flow_variables .index
456+
457+ # Ensure that the index of flow_vars (which is (source, sink))
458+ # correctly aligns with the keys in rated_line_capacities
459+ flow_vars ["rated_capacity" ] = [
460+ rated_line_capacities [idx ] for idx in flow_vars .index
427461 ]
428- return flow_variables
462+ return flow_vars
429463
430464 def get_fuel_mix (self , hourly_generation : pd .DataFrame ) -> pd .DataFrame :
431465 """Return the fuel mix (%) for the whole simulation period."""
0 commit comments