Skip to content

Commit c11826c

Browse files
committed
Use greater than or less than instead of ==
1 parent ea2e33a commit c11826c

File tree

4 files changed

+93
-42
lines changed

4 files changed

+93
-42
lines changed

.gitignore

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,python,venv,data,visualstudiocode,pycharm,jupyternotebooks,dotenv,virtualenv
33

44
### Data ###
5-
*.csv
6-
# Don't ignore data in switch_model.csv
7-
!switch_model/**/*.csv
85
*.dat
96
*.efx
107
*.gbr
@@ -18,8 +15,11 @@
1815
*.vcf
1916
*.xml
2017
*.pickle
18+
examples/**/temp/**
19+
examples/**/outputs/*.csv
20+
examples/**/outputs/*.txt
21+
examples/**/graphs/*.png
2122
examples/**/info.txt
22-
examples/**/*.png
2323

2424
### dotenv ###
2525
.env

switch_model/policies/wind_to_solar_ratio.py

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
44
It takes in wind_to_solar_ratio.csv that has the following format
55
6-
PERIOD,wind_to_solar_ratio
7-
2020,.
8-
2030,0.5
9-
2040,1
10-
2050,1.5
6+
PERIOD,wind_to_solar_ratio,wind_to_solar_ratio_const_gt
7+
2020,.,.
8+
2030,0.5,0
9+
2040,1,0
10+
2050,1.5,1
1111
1212
Here when wind_to_solar_ratio is specified (i.e. not '.') a constraint is activated that enforces that
1313
14-
Online wind capacity = Online solar capacity * wind_to_solar_ratio
14+
Online wind capacity >=/<= Online solar capacity * wind_to_solar_ratio
1515
1616
for the entire period.
17+
18+
When wind_to_solar_ratio_const_gt is true (1) the constraint is a >= constraint.
19+
When wind_to_solar_ratio_const_gt is False (0) the constraint is a <= constraint.
1720
"""
1821
import os
1922

@@ -30,51 +33,77 @@ def define_components(mod):
3033
mod.WindCapacity = Expression(
3134
mod.PERIODS,
3235
rule=lambda m, p: sum(
33-
m.GenCapacity[g, p] for g in m.VARIABLE_GENS if m.gen_energy_source[g] == _WIND_ENERGY_TYPE
34-
)
36+
m.GenCapacity[g, p]
37+
for g in m.VARIABLE_GENS
38+
if m.gen_energy_source[g] == _WIND_ENERGY_TYPE
39+
),
3540
)
3641

3742
mod.SolarCapacity = Expression(
3843
mod.PERIODS,
3944
rule=lambda m, p: sum(
40-
m.GenCapacity[g, p] for g in m.VARIABLE_GENS if m.gen_energy_source[g] == _SOLAR_ENERGY_TYPE
41-
)
45+
m.GenCapacity[g, p]
46+
for g in m.VARIABLE_GENS
47+
if m.gen_energy_source[g] == _SOLAR_ENERGY_TYPE
48+
),
4249
)
4350

4451
mod.wind_to_solar_ratio = Param(
4552
mod.PERIODS,
4653
default=0, # 0 means the constraint is inactive
47-
within=NonNegativeReals
54+
within=NonNegativeReals,
4855
)
4956

57+
mod.wind_to_solar_ratio_const_gt = Param(mod.PERIODS, default=True, within=Boolean)
58+
5059
# We use a scaling factor to improve the numerical properties
5160
# of the model.
5261
# Learn more by reading the documentation on Numerical Issues.
5362
# 1e-3 was picked since this value is normally on the order of GW instead of MW
5463
scaling_factor = 1e-3
64+
65+
def wind_to_solar_ratio_const_rule(m, p):
66+
if m.wind_to_solar_ratio[p] == 0: # 0 means Constraint is inactive
67+
return Constraint.Skip
68+
69+
lhs = m.WindCapacity[p] * scaling_factor
70+
rhs = m.SolarCapacity[p] * m.wind_to_solar_ratio[p] * scaling_factor
71+
if m.wind_to_solar_ratio_const_gt[p]:
72+
return lhs >= rhs
73+
else:
74+
return lhs <= rhs
75+
5576
mod.wind_to_solar_ratio_const = Constraint(
56-
mod.PERIODS,
57-
rule=lambda m, p: Constraint.skip if m.wind_to_solar_ratio == 0 else (
58-
m.WindCapacity[p] * scaling_factor == m.SolarCapacity[p] * m.wind_to_solar_ratio[p] * scaling_factor
59-
)
77+
mod.PERIODS, rule=wind_to_solar_ratio_const_rule
6078
)
6179

6280

6381
def load_inputs(mod, switch_data, inputs_dir):
6482
switch_data.load_aug(
65-
index=mod.PERIODS,
66-
filename=os.path.join(inputs_dir, 'wind_to_solar_ratio.csv'),
83+
filename=os.path.join(inputs_dir, "wind_to_solar_ratio.csv"),
6784
auto_select=True,
68-
param=(mod.wind_to_solar_ratio,),
69-
optional=True
85+
param=(mod.wind_to_solar_ratio, mod.wind_to_solar_ratio_const_gt),
86+
optional=True, # We want to allow including this module even if the file isn't there
7087
)
7188

7289

7390
def post_solve(m, outdir):
74-
df = pd.DataFrame({
75-
"WindCapacity (GW)": value(m.WindCapacity[p]) / 1000,
76-
"SolarCapacity (GW)": value(m.SolarCapacity[p]) / 1000,
77-
"ComputedRatio": value(m.WindCapacity[p] / m.SolarCapacity[p]),
78-
"ExpectedRatio": value(m.wind_to_solar_ratio[p])
79-
} for p in m.PERIODS if m.wind_to_solar_ratio[p] != 0)
80-
write_table(m, output_file=os.path.join(outdir, "wind_to_solar_ratio.csv"), df=df, index=False)
91+
df = pd.DataFrame(
92+
{
93+
"WindCapacity (GW)": value(m.WindCapacity[p]) / 1000,
94+
"SolarCapacity (GW)": value(m.SolarCapacity[p]) / 1000,
95+
"ComputedRatio": value(m.WindCapacity[p] / m.SolarCapacity[p])
96+
if value(m.SolarCapacity[p]) != 0
97+
else ".",
98+
"ExpectedRatio": value(m.wind_to_solar_ratio[p])
99+
if m.wind_to_solar_ratio[p] != 0
100+
else ".",
101+
}
102+
for p in m.PERIODS
103+
)
104+
write_table(
105+
m,
106+
output_file=os.path.join(outdir, "wind_to_solar_ratio.csv"),
107+
df=df,
108+
index=False,
109+
)

switch_model/utilities/__init__.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -636,14 +636,21 @@ def load_aug(
636636
switch_data.load(**kwds)
637637
return
638638

639+
# Use our custom DataManager to allow 'inf' in csvs.
640+
if extension == ".csv":
641+
kwds["using"] = "switch_csv"
642+
639643
# copy the optional_params to avoid side-effects when the list is altered below
640644
optional_params = list(optional_params)
641645
# Parse header and first row
642646
with open(path) as infile:
643647
headers_line = infile.readline()
644648
second_line = infile.readline()
645-
file_is_empty = headers_line == ""
646-
file_has_no_data_rows = second_line == ""
649+
650+
# Skip if the file is empty.
651+
if optional and headers_line == "":
652+
return
653+
647654
suffix = path.split(".")[-1]
648655
if suffix in {"tab", "tsv"}:
649656
separator = "\t"
@@ -653,9 +660,7 @@ def load_aug(
653660
raise InputError(f"Unrecognized file type for input file {path}")
654661
# TODO: parse this more formally, e.g. using csv module
655662
headers = headers_line.strip().split(separator)
656-
# Skip if the file is empty.
657-
if optional and file_is_empty:
658-
return
663+
659664
# Try to get a list of parameters. If param was given as a
660665
# singleton or a tuple, make it into a list that can be edited.
661666
params = []
@@ -740,14 +745,11 @@ def load_aug(
740745
del kwds["select"][i]
741746
del kwds["param"][p_i]
742747

743-
if optional and file_has_no_data_rows:
744-
# Skip the file. Note that we are only doing this after having
748+
if optional and second_line == "":
749+
# Skip the file if it has no data. Note that we are only doing this after having
745750
# validated the file's column headings.
746751
return
747752

748-
# Use our custom DataManager to allow 'inf' in csvs.
749-
if kwds["filename"][-4:] == ".csv":
750-
kwds["using"] = "switch_csv"
751753
# All done with cleaning optional bits. Pass the updated arguments
752754
# into the DataPortal.load() function.
753755
switch_data.load(**kwds)

switch_model/wecc/get_inputs.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import argparse
1313
import os
1414
import shutil
15+
import warnings
1516
from typing import Iterable, List
1617

1718
# Switch packages
@@ -69,6 +70,8 @@ def write_csv(data: Iterable[List], fname, headers: List[str], log=True):
6970
"switch_model.policies.carbon_policies",
7071
"switch_model.policies.rps_unbundled",
7172
# "switch_model.reporting.basic_exports_wecc",
73+
# Always include since by default it does nothing except output useful data
74+
"switch_model.policies.wind_to_solar_ratio",
7275
]
7376

7477

@@ -901,14 +904,31 @@ def query_db(full_config, skip_cf):
901904

902905

903906
def write_wind_to_solar_ratio(wind_to_solar_ratio):
907+
# TODO ideally we'd have a table where we can specify the wind_to_solar_ratios per period.
908+
# At the moment only the wind_to_solar_ratio is specified and which doesn't allow different values per period
904909
if wind_to_solar_ratio is None:
905910
return
906911

907-
print("wind_to_solar_ratio.csv")
912+
print("wind_to_solar_ratio.csv...")
908913
df = pd.read_csv("periods.csv")[["INVESTMENT_PERIOD"]]
909914
df["wind_to_solar_ratio"] = wind_to_solar_ratio
915+
916+
# wind_to_solar_ratio.csv requires a column called wind_to_solar_ratio_const_gt that is True (1) or False (0)
917+
# This column specifies whether the constraint is a greater than constraint or a less than constraint.
918+
# In our case we want it to be a greater than constraint if we're trying to force wind-to-solar ratio above its default
919+
# and we want it to be a less than constraint if we're trying to force the ratio below its default.
920+
# Here the default is the ratio if we didn't have the constraint.
921+
cutoff_ratio = 0.28
922+
warnings.warn(
923+
"To determine the sign of the wind-to-solar ratio constraint we have "
924+
f"assumed that without the constraint, the wind-to-solar ratio is {cutoff_ratio}. "
925+
f"This value was accurate for Martin's LDES runs however it may not be accurate for you. "
926+
f"You should update this value in get_inputs or manually specify whether you want a greater than "
927+
f"or a less than constraint."
928+
)
929+
df["wind_to_solar_ratio_const_gt"] = 1 if wind_to_solar_ratio > cutoff_ratio else 0
930+
910931
df.to_csv("wind_to_solar_ratio.csv", index=False)
911-
modules.append("switch_model.policies.wind_to_solar_ratio")
912932

913933

914934
def ca_policies(db_cursor, ca_policies_scenario_id, study_timeframe_id):

0 commit comments

Comments
 (0)