Skip to content

Commit 945189c

Browse files
authored
Merge pull request #87 from staadecker/add_storage
add_storage post process
2 parents 1575847 + 61c7a5c commit 945189c

File tree

2 files changed

+151
-1
lines changed

2 files changed

+151
-1
lines changed

switch_model/tools/templates/config.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,12 @@ get_inputs:
3939
# ca_policies_scenario_id:
4040
# enable_planning_reserves:
4141
# generation_plant_technologies_scenario_id:
42-
# variable_o_m_cost_scenario_id:
42+
# variable_o_m_cost_scenario_id:
43+
# add_storage was used by Martin when studying LDES
44+
# you likely don't need to use these parameters
45+
# they won't impact your runs
46+
# add_storage:
47+
# costs_scenario: 0
48+
# plants_scenario: 0
49+
# constant_scenario: 0
50+
# minimums_scenario: 0
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
"""
2+
This post-process step was created by Martin Staadecker
3+
when studying long duration energy storage. It
4+
allows adding storage technologies from a Google Sheet to
5+
the csvs in the inputs folder.
6+
"""
7+
import pandas as pd
8+
9+
from switch_model.wecc.get_inputs.register_post_process import register_post_process
10+
11+
12+
def fetch_df(tab_name, key, config):
13+
"""
14+
Returns a dataframe from the google sheet and filters it by the key
15+
"""
16+
TAB_NAME_GID = {
17+
"constants": 0,
18+
"plants": 889129113,
19+
"costs": 1401952285,
20+
"minimums": 1049456965
21+
}
22+
SHEET_ID = "1SJrj039T1T95NLTs964VQnsfZgo2QWCo29x2ireVYcU"
23+
24+
gid = TAB_NAME_GID[tab_name]
25+
url = f"https://docs.google.com/spreadsheet/ccc?key={SHEET_ID}&output=csv&gid={gid}"
26+
27+
df: pd.DataFrame = pd.read_csv(url, index_col=False) \
28+
.replace("FALSE", 0) \
29+
.replace("TRUE", 1)
30+
31+
if "description" in df.columns:
32+
df = df.drop("description", axis=1)
33+
34+
if key is not None:
35+
df = filer_by_scenario(df, key, config)
36+
return df
37+
38+
39+
def filer_by_scenario(df, scenario_column, config):
40+
"""
41+
Filters a dataframe by a scenario param
42+
"""
43+
if scenario_column in config:
44+
scenario = config[scenario_column]
45+
else:
46+
scenario = input(f"Which scenario do you want for '{scenario_column}' (default 0) : ")
47+
scenario = int(scenario) if scenario != "" else 0
48+
df = df[df[scenario_column] == scenario]
49+
return df.drop(scenario_column, axis=1)
50+
51+
52+
def cross_join(df1, df2):
53+
return df1.assign(key=1).merge(
54+
df2.assign(key=1),
55+
on="key"
56+
).drop("key", axis=1)
57+
58+
59+
def add_to_csv(filename, to_add, primary_key=None, append=True):
60+
"""
61+
Used to append a dataframe to an input .csv file
62+
"""
63+
if append:
64+
try:
65+
df = pd.read_csv(filename, index_col=False)
66+
df = pd.concat([df, to_add], ignore_index=True)[df.columns]
67+
except FileNotFoundError:
68+
df = to_add
69+
else:
70+
df = to_add
71+
# Confirm that primary_key is unique
72+
if primary_key is not None:
73+
assert len(df[primary_key]) == len(df[primary_key].drop_duplicates())
74+
df.to_csv(filename, index=False)
75+
76+
77+
def drop_previous_candidate_storage():
78+
"""
79+
Drops all candidate storage from the model
80+
"""
81+
# Get the generation projects
82+
STORAGE_TECH = "Battery_Storage"
83+
84+
gen = pd.read_csv("generation_projects_info.csv", index_col=False)
85+
# Find generation projects that are both storage and not predetermined (i.e. candidate)
86+
predetermined_gen = pd.read_csv("gen_build_predetermined.csv", index_col=False)["GENERATION_PROJECT"]
87+
should_drop = (gen["gen_tech"] == STORAGE_TECH) & ~gen["GENERATION_PROJECT"].isin(predetermined_gen)
88+
# Find projects that we should drop (candidate storage)
89+
gen_to_drop = gen[should_drop]["GENERATION_PROJECT"]
90+
# Verify we're dropping the right amount
91+
assert len(gen_to_drop) == 50 # 50 is the number of load zones. we expect one candidate per load zone
92+
93+
# Drop and write output
94+
gen = gen[~should_drop]
95+
gen.to_csv("generation_projects_info.csv", index=False)
96+
97+
# Drop the dropped generation projects from gen_build_costs.csv
98+
costs = pd.read_csv("gen_build_costs.csv", index_col=False)
99+
costs = costs[~costs["GENERATION_PROJECT"].isin(gen_to_drop)]
100+
costs.to_csv("gen_build_costs.csv", index=False)
101+
102+
103+
@register_post_process(
104+
name="add_storage",
105+
msg="Adding storage from Google Sheets",
106+
only_with_config=True,
107+
priority=1 # Increased priority (default is 2) so that it always runs before replace_plants_in_zone_all.py
108+
)
109+
def main(config):
110+
# Drop previous candidate storage from inputs
111+
drop_previous_candidate_storage()
112+
113+
# Get the generation storage plants from Google Sheet
114+
gen_projects = fetch_df("constants", "constant_scenario", config).set_index("param_name").transpose()
115+
gen_projects = cross_join(gen_projects, fetch_df("plants", "plants_scenario", config))
116+
117+
# Append the storage plants to the inputs
118+
add_to_csv("generation_projects_info.csv", gen_projects, primary_key="GENERATION_PROJECT")
119+
120+
# Create min_per_tech.csv
121+
min_projects = fetch_df("minimums", "minimums_scenario", config)
122+
add_to_csv("min_per_tech.csv", min_projects, primary_key=["gen_tech", "period"], append=False)
123+
124+
# Get the plant costs from GSheets and append to costs
125+
storage_costs = fetch_df("costs", "costs_scenario", config)
126+
add_to_csv("gen_build_costs.csv", storage_costs, primary_key=["GENERATION_PROJECT", "build_year"])
127+
128+
# Create add_storage_info.csv
129+
pd.DataFrame([config]).transpose().to_csv("add_storage_info.csv", header=False)
130+
131+
# Add the storage types to the graphs
132+
gen_type = gen_projects[["gen_tech", "gen_energy_source"]].drop_duplicates()
133+
gen_type.columns = ["gen_tech", "energy_source"]
134+
gen_type["map_name"] = "default"
135+
gen_type["gen_type"] = "Storage"
136+
pd.concat([
137+
pd.read_csv("graph_tech_types.csv", index_col=False), gen_type
138+
]).to_csv("graph_tech_types.csv", index=False)
139+
140+
141+
if __name__ == "__main__":
142+
main({})

0 commit comments

Comments
 (0)