Skip to content

Commit 95dec23

Browse files
committed
Create add_storage module
1 parent d142a51 commit 95dec23

File tree

3 files changed

+168
-1
lines changed

3 files changed

+168
-1
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"""
2+
This package 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 os
8+
9+
import pandas as pd
10+
from switch_model.wecc.get_inputs.post_process_steps.replace_plants_in_zone_all import replace_plants_in_zone_all
11+
12+
# Parameters picked for Google Sheet
13+
scenario_params = {}
14+
15+
def fetch_df(tab_name, key=None):
16+
"""
17+
Returns a dataframe from the google sheet
18+
"""
19+
tab_name_to_gid = {
20+
"constants": 0,
21+
"plants": 889129113,
22+
"costs": 1401952285
23+
}
24+
gid = tab_name_to_gid[tab_name]
25+
sheet_id = "1SJrj039T1T95NLTs964VQnsfZgo2QWCo29x2ireVYcU"
26+
url = f"https://docs.google.com/spreadsheet/ccc?key={sheet_id}&output=csv&gid={gid}"
27+
df = pd.read_csv(url, index_col=False) \
28+
.replace("FALSE", 0) \
29+
.replace("TRUE", 1)
30+
if key is not None:
31+
df = filer_by_scenario(df, key)
32+
return df
33+
34+
35+
def filer_by_scenario(df, column_name):
36+
"""
37+
Filters a dataframe by a scenario param
38+
"""
39+
if column_name not in scenario_params:
40+
scenario = input(f"Which scenario do you want for '{column_name}' (default 0) : ")
41+
if scenario == "":
42+
scenario = 0
43+
scenario_params[column_name] = int(scenario)
44+
df = df[df[column_name] == scenario_params[column_name]]
45+
return df.drop(column_name, axis=1)
46+
47+
48+
def cross_join(df1, df2):
49+
return df1.assign(key=1).merge(
50+
df2.assign(key=1),
51+
on="key"
52+
).drop("key", axis=1)
53+
54+
55+
def append_to_csv(filename, to_add, primary_key=None):
56+
"""
57+
Used to append a dataframe to an input .csv file
58+
"""
59+
df = pd.read_csv(filename, index_col=False)
60+
col = df.columns
61+
df = pd.concat([df, to_add], ignore_index=True)[col]
62+
# Confirm that primary_key is unique
63+
if primary_key is not None:
64+
assert len(df[primary_key]) == len(df[primary_key].drop_duplicates())
65+
df.to_csv(filename, index=False)
66+
67+
68+
def get_gen_constants():
69+
df = fetch_df("constants", "constant_scenario")
70+
df = df.set_index("param_name")
71+
return df.transpose()
72+
73+
def drop_previous_candidate_storage():
74+
"""
75+
Drops all candidate storage from the model
76+
"""
77+
# Get the generation projects
78+
gen = pd.read_csv("generation_projects_info.csv", index_col=False)
79+
# Find generation projects that are both storage and not predetermined (i.e. candidate)
80+
predetermined_gen = pd.read_csv("gen_build_predetermined.csv", index_col=False)["GENERATION_PROJECT"]
81+
should_drop = (gen["gen_tech"] == "Battery_Storage") & ~gen["GENERATION_PROJECT"].isin(predetermined_gen)
82+
# Find projects that we should drop (candidate storage)
83+
gen_to_drop = gen[should_drop]["GENERATION_PROJECT"]
84+
# Verify we're dropping the right amount
85+
assert len(gen_to_drop) == 50 # 50 is the number of load zones. we expect one candidate per load zone
86+
87+
# Drop and write output
88+
gen = gen[~should_drop]
89+
gen.to_csv("generation_projects_info.csv", index=False)
90+
91+
# Drop the dropped generation projects from gen_build_costs.csv
92+
costs = pd.read_csv("gen_build_costs.csv", index_col=False)
93+
costs = costs[~costs["GENERATION_PROJECT"].isin(gen_to_drop)]
94+
costs.to_csv("gen_build_costs.csv", index=False)
95+
96+
def main(run_post_solve=True, scenario_config=None, change_dir=True):
97+
global scenario_params
98+
# If a config is passed use it when filtering by scenario
99+
if scenario_config is not None:
100+
scenario_params = scenario_config
101+
102+
# Move to input directory
103+
if change_dir:
104+
os.chdir("inputs")
105+
106+
# Drop previous candidate storage from inputs
107+
drop_previous_candidate_storage()
108+
109+
# Get the generation storage plants from Google Sheet
110+
gen_constants = get_gen_constants()
111+
gen_plants = fetch_df("plants", "plants_scenario")
112+
gen_plants = cross_join(gen_plants, gen_constants)
113+
114+
# Append the storage plants to the inputs
115+
append_to_csv("generation_projects_info.csv", gen_plants, primary_key="GENERATION_PROJECT")
116+
117+
# Get the plant costs from GSheets and append to costs
118+
storage_costs = fetch_df("costs", "costs_scenario")
119+
append_to_csv("gen_build_costs.csv", storage_costs, primary_key=["GENERATION_PROJECT", "build_year"])
120+
121+
# Change plants with _ALL_ZONES to a plant in every zone
122+
if run_post_solve:
123+
replace_plants_in_zone_all()
124+
125+
# Create add_storage_info.csv
126+
pd.DataFrame([scenario_params]).transpose().to_csv("add_storage_info.csv", header=False)
127+
128+
# Add the storage types to the graphs
129+
gen_type = gen_plants[["gen_tech", "gen_energy_source"]].drop_duplicates()
130+
gen_type.columns = ["gen_tech", "energy_source"]
131+
gen_type["map_name"] = "default"
132+
gen_type["gen_type"] = "Storage"
133+
pd.concat([
134+
pd.read_csv("graph_tech_types.csv", index_col=False), gen_type
135+
]).to_csv("graph_tech_types.csv", index=False)
136+
137+
138+
if __name__ == "__main__":
139+
main()

switch_model/tools/templates/config.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,11 @@ 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+
# constant_scenario: 0
49+
# plants_scenario: 0
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
This post-process steps was used by Martin when studying LDES.
3+
4+
It adds the storage data to the input files.
5+
"""
6+
from switch_model.wecc.get_inputs.register_post_process import register_post_process
7+
8+
9+
@register_post_process(
10+
name="add_storage",
11+
msg="Adding storage from Google Sheets",
12+
only_with_config=True,
13+
priority=1
14+
)
15+
def add_storage(config):
16+
from switch_model.tools.add_storage import main
17+
main(
18+
run_post_solve=False, # We will run post solve automatically right afterwards
19+
scenario_config=config,
20+
change_dir=False
21+
)

0 commit comments

Comments
 (0)