Skip to content

Commit a577b36

Browse files
committed
Merge remote-tracking branch 'rael/wecc' into make-mps
2 parents 8230f9b + 03aa2e0 commit a577b36

26 files changed

+776
-234
lines changed

REAM Model Changelog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ Changes are listed from oldest (first line) to newest (last line of table).
1515
| #36 | May 2021 | Correct inputs to only list transmission lines in one direction. |
1616
| #56 | June 2021 | Convert 2020 predetermined build years to 2019 in `get_inputs.py` to avoid conflicts with 2020 period. |
1717
| #57 | June 2021 | Specify predetermined storage energy capacity in inputs (previously left unspecified). |
18-
| #68 | June 2021 | Change financial params to 2018 dollars & 5% interest rate. Start using terrain multipliers (which now include the economic multiplier). |
18+
| #68 | June 2021 | Change financial params to 2018 dollars & 5% interest rate. Start using terrain multipliers (which now include the economic multiplier). |
19+
| #72 | July 2021 | Drop build and O&M costs of existing transmission lines. |
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
####################
3+
Add generation plants groups
4+
5+
Date applied:
6+
Description:
7+
This script adds the option to specify generation plant groups.
8+
The generation groups are specified in the table generation_plant_group.
9+
Plants are assigned to a group by adding them to the many-to-many table generation_plant_group_member.
10+
Groups are assigned to a generation_plant_scenario_id by specifying them in generation_plant_scenario_group_member
11+
#################
12+
*/
13+
14+
CREATE TABLE switch.generation_plant_group
15+
(
16+
generation_plant_group_id serial NOT NULL,
17+
description text NOT NULL,
18+
name character varying(30) NOT NULL,
19+
PRIMARY KEY (generation_plant_group_id)
20+
);
21+
22+
COMMENT ON TABLE switch.generation_plant_group
23+
IS 'This table specifies all the generation plant groups. Every group has a set of generation plants (see generation_plant_group_member). Groups can be assigned to a generation_plant_scenario (see generation_plant_scenario_group_member).';
24+
25+
CREATE TABLE switch.generation_plant_group_member
26+
(
27+
generation_plant_group_id integer,
28+
generation_plant_id integer,
29+
PRIMARY KEY (generation_plant_group_id, generation_plant_id)
30+
);
31+
32+
ALTER TABLE switch.generation_plant_group_member
33+
ADD CONSTRAINT generation_plant_group_member_group_id_fkey
34+
FOREIGN KEY (generation_plant_group_id)
35+
REFERENCES switch.generation_plant_group (generation_plant_group_id);
36+
37+
ALTER TABLE switch.generation_plant_group_member
38+
ADD CONSTRAINT generation_plant_group_member_generation_plant_id_fkey
39+
FOREIGN KEY (generation_plant_id)
40+
REFERENCES switch.generation_plant (generation_plant_id);
41+
42+
COMMENT ON TABLE switch.generation_plant_group_member
43+
IS 'This table is a many-to-many table that specifies the generation plants that are associated with a generation group.';
44+
45+
CREATE TABLE switch.generation_plant_scenario_group_member
46+
(
47+
generation_plant_scenario_id integer,
48+
generation_plant_group_id integer,
49+
PRIMARY KEY (generation_plant_scenario_id, generation_plant_group_id)
50+
);
51+
52+
ALTER TABLE switch.generation_plant_scenario_group_member
53+
ADD CONSTRAINT generation_plant_scenario_group_member_scenario_id_fkey
54+
FOREIGN KEY (generation_plant_scenario_id)
55+
REFERENCES switch.generation_plant_scenario (generation_plant_scenario_id);
56+
57+
ALTER TABLE switch.generation_plant_scenario_group_member
58+
ADD CONSTRAINT generation_plant_scenario_group_member_group_id_fkey
59+
FOREIGN KEY (generation_plant_group_id)
60+
REFERENCES switch.generation_plant_group (generation_plant_group_id);
61+
62+
COMMENT ON TABLE switch.generation_plant_scenario_group_member
63+
IS 'This table is a many-to-many table that specifies which generation plant groups belong to which generation plant scenarios';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
####################
3+
Add column gen_store_energy_to_power_ratio
4+
5+
Date applied: 2021-06-18
6+
Description:
7+
This script adds a column to the generation_plant
8+
table called gen_storage_energy_to_power_ratio specifying
9+
the storage duration
10+
#################
11+
*/
12+
13+
ALTER TABLE switch.generation_plant ADD COLUMN gen_storage_energy_to_power_ratio real;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
####################
3+
Add transmission options
4+
5+
Date applied: 2021-06-23
6+
Description:
7+
Adds two rows to table transmission_base_capital_cost_scenario_id
8+
1. A scenario where transmission costs are zero.
9+
2. A scenario where transmission costs are infinity (building not allowed).
10+
#################
11+
*/
12+
13+
INSERT INTO switch.transmission_base_capital_cost (transmission_base_capital_cost_scenario_id,
14+
trans_capital_cost_per_mw_km, description)
15+
VALUES (3, 'Infinity', 'For scenarios where building transmission is forbidden.');
16+
17+
INSERT INTO switch.transmission_base_capital_cost (transmission_base_capital_cost_scenario_id,
18+
trans_capital_cost_per_mw_km, description)
19+
VALUES (4, 0, 'For scenarios where transmission is unlimited.');
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
####################
3+
Add transmission options
4+
5+
Date applied: 2021-06-29
6+
Description:
7+
Adds an extra scenario to the database for a 10x increase in transmission costs.
8+
#################
9+
*/
10+
11+
INSERT INTO switch.transmission_base_capital_cost (transmission_base_capital_cost_scenario_id,
12+
trans_capital_cost_per_mw_km, description)
13+
VALUES (5, 9600, '10x the costs of scenario #2. Approximates the no TX case.');

switch_model/__main__.py

Lines changed: 70 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,75 +4,81 @@
44
"""Script to handle switch <cmd> calls from the command line."""
55
from __future__ import print_function
66

7-
import sys, os
7+
import argparse
8+
import importlib
9+
import sys
810
import switch_model
11+
from switch_model.utilities import get_git_branch
12+
13+
14+
def version():
15+
print("Switch model version " + switch_model.__version__)
16+
branch = get_git_branch()
17+
if branch is not None:
18+
print(f"Switch Git branch: {branch}")
19+
return 0
20+
21+
22+
def help_text():
23+
print(
24+
f"Must specifiy one of the following commands: {list(cmds.keys())}.\nE.g. Run 'switch solve' or 'switch get_inputs'."
25+
)
26+
27+
28+
def get_module_runner(module):
29+
def runner():
30+
importlib.import_module(module).main()
31+
32+
return runner
33+
34+
35+
cmds = {
36+
"solve": get_module_runner("switch_model.solve"),
37+
"solve-scenarios": get_module_runner("switch_model.solve_scenarios"),
38+
"test": get_module_runner("switch_model.test"),
39+
"upgrade": get_module_runner("switch_model.upgrade"),
40+
"get_inputs": get_module_runner("switch_model.wecc.get_inputs"),
41+
"drop": get_module_runner("switch_model.tools.drop"),
42+
"new": get_module_runner("switch_model.tools.new"),
43+
"graph": get_module_runner("switch_model.tools.graph.cli_graph"),
44+
"compare": get_module_runner("switch_model.tools.graph.cli_compare"),
45+
"db": get_module_runner("switch_model.wecc.__main__"),
46+
"help": help_text,
47+
}
948

1049

1150
def main():
12-
# TODO make a proper command line tool with help information for each option
13-
cmds = [
14-
"solve",
15-
"solve-scenarios",
16-
"test",
17-
"upgrade",
18-
"get_inputs",
19-
"--version",
20-
"drop",
21-
"new",
22-
"graph",
23-
"compare",
24-
"sampling",
25-
]
26-
if len(sys.argv) >= 2 and sys.argv[1] in cmds:
27-
# If users run a script from the command line, the location of the script
28-
# gets added to the start of sys.path; if they call a module from the
29-
# command line then an empty entry gets added to the start of the path,
30-
# indicating the current working directory. This module is often called
31-
# from a command-line script, but we want the current working
32-
# directory in the path because users may try to load local modules via
33-
# the configuration files, so we make sure that's always in the path.
34-
sys.path[0] = ""
35-
36-
# adjust the argument list to make it look like someone ran "python -m <module>" directly
37-
cmd = sys.argv[1]
38-
sys.argv[0] += " " + cmd
51+
parser = argparse.ArgumentParser(add_help=False)
52+
parser.add_argument(
53+
"--version", default=False, action="store_true", help="Get version info"
54+
)
55+
parser.add_argument(
56+
"subcommand",
57+
choices=cmds.keys(),
58+
help="The possible switch subcommands",
59+
nargs="?",
60+
default="help",
61+
)
62+
63+
# If users run a script from the command line, the location of the script
64+
# gets added to the start of sys.path; if they call a module from the
65+
# command line then an empty entry gets added to the start of the path,
66+
# indicating the current working directory. This module is often called
67+
# from a command-line script, but we want the current working
68+
# directory in the path because users may try to load local modules via
69+
# the configuration files, so we make sure that's always in the path.
70+
sys.path[0] = ""
71+
72+
args, remaining_args = parser.parse_known_args()
73+
74+
if args.version:
75+
return version()
76+
77+
# adjust the argument list to make it look like someone ran "python -m <module>" directly
78+
if len(sys.argv) > 1:
79+
sys.argv[0] += " " + sys.argv[1]
3980
del sys.argv[1]
40-
if cmd == "--version":
41-
print("Switch model version " + switch_model.__version__)
42-
from switch_model.utilities import get_git_branch
43-
44-
branch = get_git_branch()
45-
if branch is not None:
46-
print(f"Switch Git branch: {branch}")
47-
return 0
48-
if cmd == "solve":
49-
from switch_model.solve import main
50-
elif cmd == "solve-scenarios":
51-
from switch_model.solve_scenarios import main
52-
elif cmd == "test":
53-
from switch_model.test import main
54-
elif cmd == "upgrade":
55-
from switch_model.upgrade import main
56-
elif cmd == "get_inputs":
57-
from switch_model.wecc.get_inputs import main
58-
elif cmd == "sampling":
59-
from switch_model.wecc.sampling import main
60-
elif cmd == "drop":
61-
from switch_model.tools.drop import main
62-
elif cmd == "new":
63-
from switch_model.tools.new import main
64-
elif cmd == "graph":
65-
from switch_model.tools.graph.cli_graph import main
66-
elif cmd == "compare":
67-
from switch_model.tools.graph.cli_compare import main
68-
main()
69-
else:
70-
print(
71-
"Usage: {} {{{}}} ...".format(
72-
os.path.basename(sys.argv[0]), ", ".join(cmds)
73-
)
74-
)
75-
print("Use one of these commands with --help for more information.")
81+
cmds[args.subcommand]()
7682

7783

7884
if __name__ == "__main__":

switch_model/energy_sources/fuel_costs/markets.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ def define_components(mod):
209209
become non-linear.
210210
211211
"""
212+
# When this variable is True we only allow positive fuel costs
213+
# This simplifies the model since we can set some of our constraints
214+
# as greater than instead of equals.
215+
ONLY_POSITIVE_RFM_COSTS = False
212216

213217
mod.REGIONAL_FUEL_MARKETS = Set(dimen=1)
214218
mod.rfm_fuel = Param(mod.REGIONAL_FUEL_MARKETS, within=mod.FUELS)
@@ -244,7 +248,9 @@ def zone_rfm_init(m, load_zone, fuel):
244248
dimen=3,
245249
validate=lambda m, r, p, st: (r in m.REGIONAL_FUEL_MARKETS and p in m.PERIODS),
246250
)
247-
mod.rfm_supply_tier_cost = Param(mod.RFM_SUPPLY_TIERS, within=Reals)
251+
mod.rfm_supply_tier_cost = Param(
252+
mod.RFM_SUPPLY_TIERS, within=PositiveReals if ONLY_POSITIVE_RFM_COSTS else Reals
253+
)
248254
mod.rfm_supply_tier_limit = Param(
249255
mod.RFM_SUPPLY_TIERS, within=NonNegativeReals, default=float("inf")
250256
)
@@ -365,13 +371,20 @@ def GENS_FOR_RFM_PERIOD_rule(m, rfm, p):
365371
enforce_fuel_consumption_scaling_factor = 1e-2
366372

367373
def Enforce_Fuel_Consumption_rule(m, rfm, p):
368-
return m.FuelConsumptionInMarket[
369-
rfm, p
370-
] * enforce_fuel_consumption_scaling_factor == enforce_fuel_consumption_scaling_factor * sum(
374+
lhs = (
375+
m.FuelConsumptionInMarket[rfm, p] * enforce_fuel_consumption_scaling_factor
376+
)
377+
rhs = enforce_fuel_consumption_scaling_factor * sum(
371378
m.GenFuelUseRate[g, t, m.rfm_fuel[rfm]] * m.tp_weight_in_year[t]
372379
for g in m.GENS_FOR_RFM_PERIOD[rfm, p]
373380
for t in m.TPS_IN_PERIOD[p]
374381
)
382+
# If we have only positive costs, FuelConsumptionInMarket will automatically
383+
# try to be minimized in which case we can use a one-sided constraint
384+
if ONLY_POSITIVE_RFM_COSTS:
385+
return lhs >= rhs
386+
else:
387+
return lhs == rhs
375388

376389
mod.Enforce_Fuel_Consumption = Constraint(
377390
mod.REGIONAL_FUEL_MARKETS, mod.PERIODS, rule=Enforce_Fuel_Consumption_rule

0 commit comments

Comments
 (0)