Skip to content

Commit c2995bc

Browse files
committed
Use greater than or less than instead of ==
1 parent 548c9d8 commit c2995bc

File tree

4 files changed

+68
-30
lines changed

4 files changed

+68
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*.vcf
1616
*.xml
1717
*.pickle
18+
examples/**/temp/**
1819
examples/**/outputs/*.csv
1920
examples/**/outputs/*.txt
2021
examples/**/graphs/*.png

switch_model/policies/wind_to_solar_ratio.py

Lines changed: 33 additions & 18 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

@@ -47,34 +50,46 @@ def define_components(mod):
4750
within=NonNegativeReals
4851
)
4952

53+
mod.wind_to_solar_ratio_const_gt = Param(
54+
mod.PERIODS,
55+
default=True,
56+
within=Boolean
57+
)
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
55-
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-
)
60-
)
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+
76+
mod.wind_to_solar_ratio_const = Constraint(mod.PERIODS, rule=wind_to_solar_ratio_const_rule)
6177

6278

6379
def load_inputs(mod, switch_data, inputs_dir):
6480
switch_data.load_aug(
65-
index=mod.PERIODS,
6681
filename=os.path.join(inputs_dir, 'wind_to_solar_ratio.csv'),
6782
auto_select=True,
68-
param=(mod.wind_to_solar_ratio,),
69-
optional=True
83+
param=(mod.wind_to_solar_ratio, mod.wind_to_solar_ratio_const_gt),
84+
optional=True # We want to allow including this module even if the file isn't there
7085
)
7186

7287

7388
def post_solve(m, outdir):
7489
df = pd.DataFrame({
7590
"WindCapacity (GW)": value(m.WindCapacity[p]) / 1000,
7691
"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)
92+
"ComputedRatio": value(m.WindCapacity[p] / m.SolarCapacity[p]) if value(m.SolarCapacity[p]) != 0 else ".",
93+
"ExpectedRatio": value(m.wind_to_solar_ratio[p]) if m.wind_to_solar_ratio[p] != 0 else "."
94+
} for p in m.PERIODS)
8095
write_table(m, output_file=os.path.join(outdir, "wind_to_solar_ratio.csv"), df=df, index=False)

switch_model/utilities/__init__.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -588,14 +588,21 @@ def load_aug(switch_data, optional=False, auto_select=False,
588588
switch_data.load(**kwds)
589589
return
590590

591+
# Use our custom DataManager to allow 'inf' in csvs.
592+
if extension == ".csv":
593+
kwds['using'] = "switch_csv"
594+
591595
# copy the optional_params to avoid side-effects when the list is altered below
592596
optional_params=list(optional_params)
593597
# Parse header and first row
594598
with open(path) as infile:
595599
headers_line = infile.readline()
596600
second_line = infile.readline()
597-
file_is_empty = (headers_line == '')
598-
file_has_no_data_rows = (second_line == '')
601+
602+
# Skip if the file is empty.
603+
if optional and headers_line == '':
604+
return
605+
599606
suffix = path.split('.')[-1]
600607
if suffix in {'tab', 'tsv'}:
601608
separator = '\t'
@@ -605,9 +612,7 @@ def load_aug(switch_data, optional=False, auto_select=False,
605612
raise InputError(f'Unrecognized file type for input file {path}')
606613
# TODO: parse this more formally, e.g. using csv module
607614
headers = headers_line.strip().split(separator)
608-
# Skip if the file is empty.
609-
if optional and file_is_empty:
610-
return
615+
611616
# Try to get a list of parameters. If param was given as a
612617
# singleton or a tuple, make it into a list that can be edited.
613618
params = []
@@ -687,14 +692,11 @@ def load_aug(switch_data, optional=False, auto_select=False,
687692
del kwds['select'][i]
688693
del kwds['param'][p_i]
689694

690-
if optional and file_has_no_data_rows:
691-
# Skip the file. Note that we are only doing this after having
695+
if optional and second_line == '':
696+
# Skip the file if it has no data. Note that we are only doing this after having
692697
# validated the file's column headings.
693698
return
694699

695-
# Use our custom DataManager to allow 'inf' in csvs.
696-
if kwds["filename"][-4:] == ".csv":
697-
kwds['using'] = "switch_csv"
698700
# All done with cleaning optional bits. Pass the updated arguments
699701
# into the DataPortal.load() function.
700702
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

@@ -886,14 +889,31 @@ def query_db(full_config, skip_cf):
886889
create_modules_txt()
887890

888891
def write_wind_to_solar_ratio(wind_to_solar_ratio):
892+
# TODO ideally we'd have a table where we can specify the wind_to_solar_ratios per period.
893+
# At the moment only the wind_to_solar_ratio is specified and which doesn't allow different values per period
889894
if wind_to_solar_ratio is None:
890895
return
891896

892-
print("wind_to_solar_ratio.csv")
897+
print("wind_to_solar_ratio.csv...")
893898
df = pd.read_csv("periods.csv")[["INVESTMENT_PERIOD"]]
894899
df["wind_to_solar_ratio"] = wind_to_solar_ratio
900+
901+
# wind_to_solar_ratio.csv requires a column called wind_to_solar_ratio_const_gt that is True (1) or False (0)
902+
# This column specifies whether the constraint is a greater than constraint or a less than constraint.
903+
# 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
904+
# and we want it to be a less than constraint if we're trying to force the ratio below its default.
905+
# Here the default is the ratio if we didn't have the constraint.
906+
cutoff_ratio = 0.28
907+
warnings.warn(
908+
"To determine the sign of the wind-to-solar ratio constraint we have "
909+
f"assumed that without the constraint, the wind-to-solar ratio is {cutoff_ratio}. "
910+
f"This value was accurate for Martin's LDES runs however it may not be accurate for you. "
911+
f"You should update this value in get_inputs or manually specify whether you want a greater than "
912+
f"or a less than constraint."
913+
)
914+
df["wind_to_solar_ratio_const_gt"] = 1 if wind_to_solar_ratio > cutoff_ratio else 0
915+
895916
df.to_csv("wind_to_solar_ratio.csv", index=False)
896-
modules.append("switch_model.policies.wind_to_solar_ratio")
897917

898918
def ca_policies(db_cursor, ca_policies_scenario_id, study_timeframe_id):
899919
if ca_policies_scenario_id is None:

0 commit comments

Comments
 (0)