Skip to content

Commit 424e2e0

Browse files
Expand export from the dispatch module to simplify analysis:
* New file with annual summary per-generation project that includes energy, capacity, costs & capacity factor * New file with tech/annual summary for easily skimming results. * Faster summation of tech/zone annual summary & tech/annual summary. Also include capacity, costs, and cap factor in those summary files. Also, minor updates for python style.
1 parent 7d5056a commit 424e2e0

File tree

1 file changed

+85
-16
lines changed

1 file changed

+85
-16
lines changed

switch_model/generators/core/dispatch.py

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212

1313
import logging
1414
import os, collections
15+
16+
import pandas as pd
1517
from pyomo.environ import *
18+
1619
from switch_model.reporting import write_table
17-
import pandas as pd
1820

1921
dependencies = 'switch_model.timescales', 'switch_model.balancing.load_zones',\
2022
'switch_model.financials', 'switch_model.energy_sources.properties', \
@@ -389,6 +391,7 @@ def post_solve(instance, outdir):
389391

390392
dispatch_normalized_dat = []
391393
for g, t in instance.GEN_TPS:
394+
p = instance.tp_period[t]
392395
record = {
393396
"generation_project": g,
394397
"gen_dbid": instance.gen_dbid[g],
@@ -400,14 +403,19 @@ def post_solve(instance, outdir):
400403
"period": instance.tp_period[t],
401404
"DispatchGen_MW": value(instance.DispatchGen[g, t]),
402405
"Energy_GWh_typical_yr": value(
403-
instance.DispatchGen[g, t] * instance.tp_weight_in_year[t] / 1000),
406+
instance.DispatchGen[g, t] * instance.tp_weight_in_year[t] / 1000
407+
),
404408
"VariableCost_per_yr": value(
405409
instance.DispatchGen[g, t] * instance.gen_variable_om[g] *
406-
instance.tp_weight_in_year[t]),
410+
instance.tp_weight_in_year[t]
411+
),
407412
"DispatchEmissions_tCO2_per_typical_yr": value(sum(
408413
instance.DispatchEmissions[g, t, f] * instance.tp_weight_in_year[t]
409414
for f in instance.FUELS_FOR_GEN[g]
410-
)) if instance.gen_uses_fuel[g] else 0
415+
)) if instance.gen_uses_fuel[g] else 0,
416+
"GenCapacity_MW": value(instance.GenCapacity[g, p]),
417+
"GenCapitalCosts": value(instance.GenCapitalCosts[g, p]),
418+
"GenFixedOMCosts": value(instance.GenFixedOMCosts[g, p]),
411419
}
412420
try:
413421
try:
@@ -419,41 +427,102 @@ def post_solve(instance, outdir):
419427
)
420428
record['Discharge_GWh_typical_yr'] = record['Energy_GWh_typical_yr']
421429
record['Energy_GWh_typical_yr'] -= record['Store_GWh_typical_yr']
430+
record['is_storage'] = True
422431
except KeyError:
423-
#record['ChargeStorage_MW'] = None
424-
pass
432+
record['ChargeStorage_MW'] = float("NaN")
433+
record['Store_GWh_typical_yr'] = float("NaN")
434+
record['Discharge_GWh_typical_yr'] = float("NaN")
435+
record['is_storage'] = False
425436
except AttributeError:
426437
pass
427438
dispatch_normalized_dat.append(record)
428439
dispatch_full_df = pd.DataFrame(dispatch_normalized_dat)
429440
dispatch_full_df.set_index(["generation_project", "timestamp"], inplace=True)
430-
dispatch_full_df.to_csv(os.path.join(outdir, "dispatch.csv"))
441+
dispatch_full_df.to_csv(os.path.join(outdir, "dispatch.csv"),
442+
columns=[])
431443

432444
summary_columns=[
433445
"Energy_GWh_typical_yr",
434446
"VariableCost_per_yr",
435-
"DispatchEmissions_tCO2_per_typical_yr"
447+
"DispatchEmissions_tCO2_per_typical_yr",
448+
"GenCapacity_MW",
449+
"GenCapitalCosts",
450+
"GenFixedOMCosts",
451+
"capacity_factor",
436452
]
437453
if 'ChargeStorage' in dir(instance):
438454
summary_columns.extend([
439455
'Store_GWh_typical_yr',
440456
'Discharge_GWh_typical_yr'
441457
])
442458

443-
annual_summary = dispatch_full_df.groupby(['gen_tech', "gen_energy_source", "period"]).sum()
444-
annual_summary.to_csv(
445-
os.path.join(outdir, "dispatch_annual_summary.csv"),
459+
# Annual summary of each generator
460+
gen_sum = dispatch_full_df.groupby([
461+
"generation_project",
462+
"gen_dbid",
463+
"gen_tech",
464+
"gen_load_zone",
465+
"gen_energy_source",
466+
"period",
467+
"GenCapacity_MW",
468+
"GenCapitalCosts",
469+
"GenFixedOMCosts"]).sum(min_count=1, skipna=False)
470+
gen_sum.reset_index(inplace=True)
471+
gen_sum.set_index(
472+
inplace=True,
473+
keys=[
474+
"generation_project",
475+
"gen_dbid",
476+
"gen_tech",
477+
"gen_load_zone",
478+
"gen_energy_source",
479+
"period",
480+
]
481+
)
482+
gen_sum["Energy_out_avg_MW"] = (
483+
gen_sum["Energy_GWh_typical_yr"] * 1000 / gen_sum["tp_weight_in_year_hrs"]
484+
)
485+
try:
486+
idx = gen_sum["is_storage"].astype(bool)
487+
gen_sum.loc[idx, "Energy_out_avg_MW"] = (
488+
gen_sum.loc[idx, "Discharge_GWh_typical_yr"] * 1000 /
489+
gen_sum.loc[idx, "tp_weight_in_year_hrs"]
490+
)
491+
except KeyError:
492+
pass
493+
494+
def add_cap_factor(df):
495+
df["capacity_factor"] = df["Energy_out_avg_MW"] / df["GenCapacity_MW"]
496+
no_cap = (df["GenCapacity_MW"] == 0)
497+
df.loc[no_cap, "capacity_factor"] = 0
498+
return df
499+
500+
gen_sum = add_cap_factor(gen_sum)
501+
gen_sum.to_csv(
502+
os.path.join(outdir, "gen_project_annual_summary.csv"),
446503
columns=summary_columns)
447504

448-
449-
zonal_annual_summary = dispatch_full_df.groupby(
450-
['gen_tech', "gen_load_zone", "gen_energy_source", "period"]
451-
).sum()
452-
zonal_annual_summary.to_csv(
505+
zone_sum = gen_sum.groupby([
506+
'gen_tech',
507+
"gen_load_zone",
508+
"gen_energy_source",
509+
"period"
510+
]).sum()
511+
zone_sum = add_cap_factor(zone_sum)
512+
zone_sum.to_csv(
453513
os.path.join(outdir, "dispatch_zonal_annual_summary.csv"),
454514
columns=summary_columns
455515
)
456516

517+
annual_summary = zone_sum.groupby([
518+
'gen_tech',
519+
"gen_energy_source",
520+
"period"]).sum()
521+
annual_summary = add_cap_factor(annual_summary)
522+
annual_summary.to_csv(
523+
os.path.join(outdir, "dispatch_annual_summary.csv"),
524+
columns=summary_columns)
525+
457526
try:
458527
import plotnine as p9
459528
# Try to filter out spurious warnings from plotnine

0 commit comments

Comments
 (0)