@@ -695,7 +695,6 @@ def graph_hourly_curtailment(tools):
695695@graph (
696696 "total_dispatch" ,
697697 title = "Total dispatched electricity" ,
698- is_long = True ,
699698)
700699def graph_total_dispatch (tools ):
701700 # ---------------------------------- #
@@ -741,6 +740,165 @@ def graph_total_dispatch(tools):
741740 ylabel = "Total dispatched electricity (TWh)" ,
742741 )
743742
743+ tools .bar_label ()
744+
745+
746+ @graph (
747+ "energy_balance" ,
748+ title = "Energy Balance For Every Month" ,
749+ supports_multi_scenario = True ,
750+ is_long = True ,
751+ )
752+ def energy_balance (tools ):
753+ # Get dispatch dataframe
754+ cols = [
755+ "timestamp" ,
756+ "gen_tech" ,
757+ "gen_energy_source" ,
758+ "DispatchGen_MW" ,
759+ "scenario_name" ,
760+ "scenario_index" ,
761+ "Curtailment_MW" ,
762+ ]
763+ df = tools .get_dataframe ("dispatch.csv" , drop_scenario_info = False )[cols ]
764+ df = tools .transform .gen_type (df )
765+
766+ # Rename and add needed columns
767+ df ["Dispatch Limit" ] = df ["DispatchGen_MW" ] + df ["Curtailment_MW" ]
768+ df = df .drop ("Curtailment_MW" , axis = 1 )
769+ df = df .rename ({"DispatchGen_MW" : "Dispatch" }, axis = 1 )
770+ # Sum dispatch across all the projects of the same type and timepoint
771+ key_columns = ["timestamp" , "gen_type" , "scenario_name" , "scenario_index" ]
772+ df = df .groupby (key_columns , as_index = False ).sum ()
773+ df = df .melt (
774+ id_vars = key_columns , value_vars = ["Dispatch" , "Dispatch Limit" ], var_name = "Type"
775+ )
776+ df = df .rename ({"gen_type" : "Source" }, axis = 1 )
777+
778+ discharge = (
779+ df [(df ["Source" ] == "Storage" ) & (df ["Type" ] == "Dispatch" )]
780+ .drop (["Source" , "Type" ], axis = 1 )
781+ .rename ({"value" : "discharge" }, axis = 1 )
782+ )
783+
784+ # Get load dataframe
785+ load = tools .get_dataframe ("load_balance.csv" , drop_scenario_info = False )
786+ load = load .drop ("normalized_energy_balance_duals_dollar_per_mwh" , axis = 1 )
787+
788+ # Sum load across all the load zones
789+ key_columns = ["timestamp" , "scenario_name" , "scenario_index" ]
790+ load = load .groupby (key_columns , as_index = False ).sum ()
791+
792+ # Subtract storage dispatch from generation and add it to the storage charge to get net flow
793+ load = load .merge (discharge , how = "left" , on = key_columns , validate = "one_to_one" )
794+ load ["ZoneTotalCentralDispatch" ] -= load ["discharge" ]
795+ load ["StorageNetCharge" ] += load ["discharge" ]
796+ load = load .drop ("discharge" , axis = 1 )
797+
798+ # Rename and convert from wide to long format
799+ load = load .rename (
800+ {
801+ "ZoneTotalCentralDispatch" : "Total Generation (excl. storage discharge)" ,
802+ "TXPowerNet" : "Transmission Losses" ,
803+ "StorageNetCharge" : "Storage Net Flow" ,
804+ "zone_demand_mw" : "Demand" ,
805+ },
806+ axis = 1 ,
807+ ).sort_index (axis = 1 )
808+ load = load .melt (id_vars = key_columns , var_name = "Source" )
809+ load ["Type" ] = "Dispatch"
810+
811+ # Merge dispatch contributions with load contributions
812+ df = pd .concat ([load , df ])
813+
814+ # Add the timestamp information and make period string to ensure it doesn't mess up the graphing
815+ df = tools .transform .timestamp (df ).astype ({"period" : str })
816+
817+ # Convert to TWh (incl. multiply by timepoint duration)
818+ df ["value" ] *= df ["tp_duration" ] / 1e6
819+
820+ FREQUENCY = "1W"
821+
822+ def groupby_time (df ):
823+ return df .groupby (
824+ [
825+ "scenario_name" ,
826+ "period" ,
827+ "Source" ,
828+ "Type" ,
829+ tools .pd .Grouper (key = "datetime" , freq = FREQUENCY , origin = "start" ),
830+ ]
831+ )["value" ]
832+
833+ df = groupby_time (df ).sum ().reset_index ()
834+
835+ # Get the state of charge data
836+ soc = tools .get_dataframe (
837+ "StateOfCharge.csv" , dtype = {"STORAGE_GEN_TPS_1" : str }, drop_scenario_info = False
838+ )
839+ soc = soc .rename (
840+ {"STORAGE_GEN_TPS_2" : "timepoint" , "StateOfCharge" : "value" }, axis = 1
841+ )
842+ # Sum over all the projects that are in the same scenario with the same timepoint
843+ soc = soc .groupby (["timepoint" , "scenario_name" ], as_index = False ).sum ()
844+ soc ["Source" ] = "State Of Charge"
845+ soc ["value" ] /= 1e6 # Convert to TWh
846+
847+ # Group by time
848+ soc = tools .transform .timestamp (
849+ soc , use_timepoint = True , key_col = "timepoint"
850+ ).astype ({"period" : str })
851+ soc ["Type" ] = "Dispatch"
852+ soc = groupby_time (soc ).mean ().reset_index ()
853+
854+ # Add state of charge to dataframe
855+ df = pd .concat ([df , soc ])
856+ # Add column for day since that's what we really care about
857+ df ["day" ] = df ["datetime" ].dt .dayofyear
858+
859+ # Plot
860+ # Get the colors for the lines
861+ colors = tools .get_colors ()
862+ colors .update (
863+ {
864+ "Transmission Losses" : "brown" ,
865+ "Storage Net Flow" : "cadetblue" ,
866+ "Demand" : "black" ,
867+ "Total Generation (excl. storage discharge)" : "black" ,
868+ "State Of Charge" : "green" ,
869+ }
870+ )
871+
872+ # plot
873+ num_periods = df ["period" ].nunique ()
874+ pn = tools .pn
875+ plot = (
876+ pn .ggplot (df )
877+ + pn .geom_line (pn .aes (x = "day" , y = "value" , color = "Source" , linetype = "Type" ))
878+ + pn .facet_grid ("period ~ scenario_name" )
879+ + pn .labs (y = "Contribution to Energy Balance (TWh)" )
880+ + pn .scales .scale_color_manual (
881+ values = colors , aesthetics = "color" , na_value = colors ["Other" ]
882+ )
883+ + pn .scales .scale_x_continuous (
884+ name = "Month" ,
885+ labels = ["J" , "F" , "M" , "A" , "M" , "J" , "J" , "A" , "S" , "O" , "N" , "D" ],
886+ breaks = (15 , 46 , 76 , 106 , 137 , 167 , 198 , 228 , 259 , 289 , 319 , 350 ),
887+ limits = (0 , 366 ),
888+ )
889+ + pn .scales .scale_linetype_manual (
890+ values = {"Dispatch Limit" : "dotted" , "Dispatch" : "solid" }
891+ )
892+ + pn .theme (
893+ figure_size = (
894+ pn .options .figure_size [0 ] * tools .num_scenarios ,
895+ pn .options .figure_size [1 ] * num_periods ,
896+ )
897+ )
898+ )
899+
900+ tools .save_figure (plot .draw ())
901+
744902
745903@graph (
746904 "curtailment_per_period" ,
0 commit comments