From 297e9e088ed46124dc7e8c6b947be2f96a0cac2d Mon Sep 17 00:00:00 2001 From: "josiah.johnston" Date: Wed, 21 Aug 2019 17:17:33 -0700 Subject: [PATCH 1/4] Implement bidirectional transmission lines. Include upgrade code and a wish-list of future updates. --- switch_model/transmission/transport/build.py | 212 ++++++++++-------- .../transmission/transport/dispatch.py | 17 +- switch_model/upgrade/manager.py | 4 +- switch_model/upgrade/upgrade_2_0_5.py | 5 +- switch_model/upgrade/upgrade_2_0_6dev1.py | 64 ++++++ switch_model/version.py | 2 +- 6 files changed, 197 insertions(+), 107 deletions(-) create mode 100644 switch_model/upgrade/upgrade_2_0_6dev1.py diff --git a/switch_model/transmission/transport/build.py b/switch_model/transmission/transport/build.py index af35edebe..c9ba7d1bd 100644 --- a/switch_model/transmission/transport/build.py +++ b/switch_model/transmission/transport/build.py @@ -2,9 +2,20 @@ # Licensed under the Apache License, Version 2.0, which is in the LICENSE file. """ -Defines transmission build-outs. +Defines transmission build-outs for a transport model with separate builds in +each direction. + +To do: +* Consolidate the cost component inputs into a single capital cost per +transmission line direction ($/MW-capacity). Have users estimate cost of +upgrading corridors using whatever method makes the most sense for them. +Provide suggestions based on distance and a default value of $1000/MW-km. +* Rename parameters to use the prefix of tx instead of trans for brevity, and +normalize existing_trans_cap to tx_existing_cap +* Investigate shifting TRANSMISSION_LINES to a 2-dimensional set of zone_from, +zone_to. If Pyomo gives us the choice of using tx or (z_from,z_to) when +iterating & indexing, that would be a win-win for readability and simplicity. """ - import logging import os @@ -13,8 +24,34 @@ from switch_model.financials import capital_recovery_factor as crf -dependencies = 'switch_model.timescales', 'switch_model.balancing.load_zones',\ +dependencies = ( + 'switch_model.timescales', + 'switch_model.balancing.load_zones', 'switch_model.financials' +) +post_requisite = ( + 'switch_model.transmission.transport.dispatch', +) + +def define_arguments(argparser): + """Skip this until we figure out a reasonable way of splitting costs for + assymetric build-outs. The only use case I know for assymetric ratings is + reliability constraints for existing transmission pathways (not individual + lines). However, for new builds, we lack information about what would + bring about asymmetrical rating, or how to divide costs into a power + ratings for one direction vs the other. + + For the moment, just stick with all new builds must be symmetric, and + divide costs of builds evenly in each direction.""" + pass +# group = argparser.add_argument_group(__name__) +# group.add_argument('--allow-new-tx-asymmetrical-builds', default=False, +# dest='tx_new_builds_asymmetric', action='store_true', +# help=("By default, new transmission builds must be symmetrical in " +# "each direction of a line; this option drops that constraint.") +# ) +# if m.options.tx_new_builds_asymmetric: +# # do something... def define_components(mod): """ @@ -22,20 +59,13 @@ def define_components(mod): stated, all power capacity is specified in units of MW and all sets and parameters are mandatory. + TRANSMISSION_LINES is the complete set of transmission pathways connecting + load zones. Each member of this set is a one dimensional identifier such + as "A-B" or "B-A". Transmission is usually abbreviated as trans & tx in + parameter names & indexes. - TRANSMISSION_LINES is the complete set of transmission pathways - connecting load zones. Each member of this set is a one dimensional - identifier such as "A-B". This set has no regard for directionality - of transmission lines and will generate an error if you specify two - lines that move in opposite directions such as (A to B) and (B to - A). Another derived set - TRANS_LINES_DIRECTIONAL - stores - directional information. Transmission may be abbreviated as trans or - tx in parameter names or indexes. - - trans_lz1[tx] and trans_lz2[tx] specify the load zones at either end - of a transmission line. The order of 1 and 2 is unimportant, but you - are encouraged to be consistent to simplify merging information back - into external databases. + trans_lz_send[tx] and trans_lz_receive[tx] specify the load zones at + either end of a transmission line. trans_dbid[tx in TRANSMISSION_LINES] is an external database identifier for each transmission line. This is an optional parameter @@ -62,10 +92,10 @@ def define_components(mod): describes how many MW of capacity was been installed before the start of the study. - BuildTx[(tx, bld_yr) in TRANS_BLD_YRS] is a decision variable - that describes the transfer capacity in MW installed on a corridor - in a given build year. For existing builds, this variable is locked - to the existing capacity. + BuildTx[(tx, bld_yr) in TRANS_BLD_YRS] is a decision variable that + describes the transfer capacity in MW installed on a corridor in a given + build year. For pre-determined builds, this variable is locked to the + specified capacity. TxCapacityNameplate[(tx, bld_yr) in TRANS_BLD_YRS] is an expression that returns the total nameplate transfer capacity of a transmission @@ -105,23 +135,10 @@ def define_components(mod): parameter defaults to 0.03 based on 2009 WREZ transmission model transmission data costs for existing transmission maintenance. - trans_cost_hourly[tx TRANSMISSION_LINES] is the cost of building + trans_cost_annual[tx TRANSMISSION_LINES] is the cost of building transmission lines in units of $BASE_YEAR / MW- transfer-capacity / - hour. This derived parameter is based on the total annualized - capital and fixed O&M costs, then divides that by hours per year to - determine the portion of costs incurred hourly. - - DIRECTIONAL_TX is a derived set of directional paths that - electricity can flow along transmission lines. Each element of this - set is a two-dimensional entry that describes the origin and - destination of the flow: (load_zone_from, load_zone_to). Every - transmission line will generate two entries in this set. Members of - this set are abbreviated as trans_d where possible, but may be - abbreviated as tx in situations where brevity is important and it is - unlikely to be confused with the overall transmission line. - - trans_d_line[trans_d] is the transmission line associated with this - directional path. + year. This derived parameter is based on the total annualized + capital and fixed O&M costs. --- NOTES --- @@ -154,33 +171,48 @@ def define_components(mod): """ mod.TRANSMISSION_LINES = Set() - mod.trans_lz1 = Param(mod.TRANSMISSION_LINES, within=mod.LOAD_ZONES) - mod.trans_lz2 = Param(mod.TRANSMISSION_LINES, within=mod.LOAD_ZONES) - # we don't do a min_data_check for TRANSMISSION_LINES, because it may be empty for model - # configurations that are sometimes run with interzonal transmission and sometimes not - # (e.g., island interconnect scenarios). However, presence of this column will still be - # checked by load_data_aug. - mod.min_data_check('trans_lz1', 'trans_lz2') - - def _check_tx_duplicate_paths(m): - forward_paths = set([ - (m.trans_lz1[tx], m.trans_lz2[tx]) for tx in m.TRANSMISSION_LINES - ]) - reverse_paths = set([ - (m.trans_lz2[tx], m.trans_lz1[tx]) for tx in m.TRANSMISSION_LINES - ]) - overlap = forward_paths.intersection(reverse_paths) - if overlap: - logging.error( - "Transmission lines have bi-directional paths specified " - "in input files. They are expected to specify a single path " - "per pair of connected load zones. " - "(Ex: either A->B or B->A, but not both). " - "Over-specified lines: {}".format(overlap)) - return(False) - else: - return(True) - mod.check_tx_duplicate_paths = BuildCheck(rule=_check_tx_duplicate_paths) + mod.trans_lz_send = Param(mod.TRANSMISSION_LINES, within=mod.LOAD_ZONES) + mod.trans_lz_receive = Param(mod.TRANSMISSION_LINES, within=mod.LOAD_ZONES) + # we don't do a min_data_check for TRANSMISSION_LINES, because it may be + # empty for model configurations that are sometimes run with interzonal + # transmission and sometimes not (e.g., island interconnect scenarios). + # However, presence of this column will still be checked by load_data_aug. + # Counterpoint: It seems cleaner to exclude this module from those + # scenarios, and require TRANSMISSION_LINES to have data for this module. + mod.min_data_check('trans_lz_send', 'trans_lz_receive') + + # Use BuildAction to populate a set's default values. + def _TX_CONNECTED_ZONES_init(m): + all_paths = set() + m._zones_to_tx_dat = {} + for tx in m.TRANSMISSION_LINES: + zones = (m.trans_lz_send[tx], m.trans_lz_receive[tx]) + all_paths.add(zones) + m._zones_to_tx_dat[zones] = tx + opposite_paths = set([(z2, z1) for (z1, z2) in all_paths]) + missing = opposite_paths - all_paths + assert not missing, ( + "Transmission lines do not have pairs in each direction in input " + "files. Missing expected lines: {}".format(missing) + ) + m.TX_CONNECTED_ZONES_set = all_paths + mod.TX_CONNECTED_ZONES_init = BuildAction(rule=_TX_CONNECTED_ZONES_init) + mod.TX_CONNECTED_ZONES = Set( + dimen=2, + within=mod.LOAD_ZONES * mod.LOAD_ZONES, + initialize=lambda m: m.TX_CONNECTED_ZONES_set) + mod.zones_to_tx = Param( + mod.TX_CONNECTED_ZONES, + within=mod.TRANSMISSION_LINES, + initialize=lambda m, z1, z2: m._zones_to_tx_dat[z1, z2]) + + mod.trans_reverse = Param( + mod.TRANSMISSION_LINES, + doc="The transmission line in the opposite direction.", + initialize=lambda m, tx: ( + m.zones_to_tx[m.trans_lz_receive[tx], m.trans_lz_send[tx]] + ) + ) mod.trans_dbid = Param(mod.TRANSMISSION_LINES, default=lambda m, tx: tx) mod.trans_length_km = Param(mod.TRANSMISSION_LINES, within=NonNegativeReals) @@ -199,6 +231,12 @@ def _check_tx_duplicate_paths(m): initialize=mod.TRANSMISSION_LINES * mod.PERIODS, filter=lambda m, tx, p: m.trans_new_build_allowed[tx]) mod.BuildTx = Var(mod.TRANS_BLD_YRS, within=NonNegativeReals) + mod.Tx_New_Builds_Symmetric = Constraint( + mod.TRANS_BLD_YRS, + rule=lambda m, tx, bld_yr: ( + m.BuildTx[tx, bld_yr] == m.BuildTx[m.trans_reverse[tx], bld_yr] + ) + ) mod.TxCapacityNameplate = Expression( mod.TRANSMISSION_LINES, mod.PERIODS, rule=lambda m, tx, period: sum( @@ -231,12 +269,15 @@ def _check_tx_duplicate_paths(m): # Multiply capital costs by capital recover factor to get annual # payments. Add annual fixed O&M that are expressed as a fraction of # overnight costs. + # Divide costs by 2 to reflect symmetrical bi-directional builds. mod.trans_cost_annual = Param( mod.TRANSMISSION_LINES, within=NonNegativeReals, initialize=lambda m, tx: ( - m.trans_capital_cost_per_mw_km * m.trans_terrain_multiplier[tx] * - m.trans_length_km[tx] * (crf(m.interest_rate, m.trans_lifetime_yrs) + + m.trans_capital_cost_per_mw_km / 2.0 * + m.trans_terrain_multiplier[tx] * + m.trans_length_km[tx] * + (crf(m.interest_rate, m.trans_lifetime_yrs) + m.trans_fixed_om_fraction))) # An expression to summarize annual costs for the objective # function. Units should be total annual future costs in $base_year @@ -251,29 +292,13 @@ def _check_tx_duplicate_paths(m): ) mod.Cost_Components_Per_Period.append('TxFixedCosts') - def init_DIRECTIONAL_TX(model): - tx_dir = set() - for tx in model.TRANSMISSION_LINES: - tx_dir.add((model.trans_lz1[tx], model.trans_lz2[tx])) - tx_dir.add((model.trans_lz2[tx], model.trans_lz1[tx])) - return tx_dir - mod.DIRECTIONAL_TX = Set( - dimen=2, - initialize=init_DIRECTIONAL_TX) mod.TX_CONNECTIONS_TO_ZONE = Set( mod.LOAD_ZONES, - initialize=lambda m, lz: set( - z for z in m.LOAD_ZONES if (z,lz) in m.DIRECTIONAL_TX)) - - def init_trans_d_line(m, zone_from, zone_to): - for tx in m.TRANSMISSION_LINES: - if((m.trans_lz1[tx] == zone_from and m.trans_lz2[tx] == zone_to) or - (m.trans_lz2[tx] == zone_from and m.trans_lz1[tx] == zone_to)): - return tx - mod.trans_d_line = Param( - mod.DIRECTIONAL_TX, - within=mod.TRANSMISSION_LINES, - initialize=init_trans_d_line) + initialize=lambda m, z_receive: set([ + z_send for z_send in m.LOAD_ZONES + if (z_send,z_receive) in m.TX_CONNECTED_ZONES + ]) + ) def load_inputs(mod, switch_data, inputs_dir): @@ -283,7 +308,7 @@ def load_inputs(mod, switch_data, inputs_dir): a *. transmission_lines.csv - TRANSMISSION_LINE, trans_lz1, trans_lz2, trans_length_km, + TRANSMISSION_LINE, trans_lz_send, trans_lz_receive, trans_length_km, trans_efficiency, existing_trans_cap, trans_dbid*, trans_derating_factor*, trans_terrain_multiplier*, trans_new_build_allowed* @@ -300,19 +325,14 @@ def load_inputs(mod, switch_data, inputs_dir): # no rows after header (fix bugs in pyomo.core.plugins.data.text) switch_data.load_aug( filename=os.path.join(inputs_dir, 'transmission_lines.csv'), - select=( - 'TRANSMISSION_LINE', 'trans_lz1', 'trans_lz2', - 'trans_length_km', 'trans_efficiency', 'existing_trans_cap', - 'trans_dbid', 'trans_derating_factor', - 'trans_terrain_multiplier', 'trans_new_build_allowed' - ), + auto_select=True, index=mod.TRANSMISSION_LINES, optional_params=( 'trans_dbid', 'trans_derating_factor', 'trans_terrain_multiplier', 'trans_new_build_allowed' ), param=( - mod.trans_lz1, mod.trans_lz2, + mod.trans_lz_send, mod.trans_lz_receive, mod.trans_length_km, mod.trans_efficiency, mod.existing_trans_cap, mod.trans_dbid, mod.trans_derating_factor, mod.trans_terrain_multiplier, mod.trans_new_build_allowed @@ -340,8 +360,8 @@ def post_solve(instance, outdir): { "TRANSMISSION_LINE": tx, "PERIOD": p, - "trans_lz1": mod.trans_lz1[tx], - "trans_lz2": mod.trans_lz2[tx], + "trans_lz_send": mod.trans_lz_send[tx], + "trans_lz_receive": mod.trans_lz_receive[tx], "trans_dbid": mod.trans_dbid[tx], "trans_length_km": mod.trans_length_km[tx], "trans_efficiency": mod.trans_efficiency[tx], diff --git a/switch_model/transmission/transport/dispatch.py b/switch_model/transmission/transport/dispatch.py index 031afb24c..37c2377c6 100644 --- a/switch_model/transmission/transport/dispatch.py +++ b/switch_model/transmission/transport/dispatch.py @@ -8,8 +8,12 @@ from pyomo.environ import * -dependencies = 'switch_model.timescales', 'switch_model.balancing.load_zones',\ - 'switch_model.financials', 'switch_model.transmission.transport.build' +dependencies = ( + 'switch_model.timescales', + 'switch_model.balancing.load_zones', + 'switch_model.financials', + 'switch_model.transmission.transport.build' +) def define_components(mod): """ @@ -23,7 +27,7 @@ def define_components(mod): TRANS_TIMEPOINTS describes the scope that transmission dispatch decisions must be made over. It is defined as the set of - DIRECTIONAL_TX crossed with TIMEPOINTS. It is indexed as + TX_CONNECTED_ZONES crossed with TIMEPOINTS. It is indexed as (load_zone_from, load_zone_to, timepoint) and may be abbreviated as [z_from, zone_to, tp] for brevity. @@ -50,7 +54,7 @@ def define_components(mod): mod.TRANS_TIMEPOINTS = Set( dimen=3, - initialize=lambda m: m.DIRECTIONAL_TX * m.TIMEPOINTS + initialize=lambda m: m.TX_CONNECTED_ZONES * m.TIMEPOINTS ) mod.DispatchTx = Var(mod.TRANS_TIMEPOINTS, within=NonNegativeReals) @@ -58,7 +62,8 @@ def define_components(mod): mod.TRANS_TIMEPOINTS, rule=lambda m, zone_from, zone_to, tp: ( m.DispatchTx[zone_from, zone_to, tp] <= - m.TxCapacityNameplateAvailable[m.trans_d_line[zone_from, zone_to], + + m.TxCapacityNameplateAvailable[m.zones_to_tx[zone_from, zone_to], m.tp_period[tp]])) mod.TxPowerSent = Expression( @@ -69,7 +74,7 @@ def define_components(mod): mod.TRANS_TIMEPOINTS, rule=lambda m, zone_from, zone_to, tp: ( m.DispatchTx[zone_from, zone_to, tp] * - m.trans_efficiency[m.trans_d_line[zone_from, zone_to]])) + m.trans_efficiency[m.zones_to_tx[zone_from, zone_to]])) def TXPowerNet_calculation(m, z, tp): return ( diff --git a/switch_model/upgrade/manager.py b/switch_model/upgrade/manager.py index 24372aaad..93ab9e35b 100644 --- a/switch_model/upgrade/manager.py +++ b/switch_model/upgrade/manager.py @@ -17,6 +17,7 @@ from . import upgrade_2_0_1 from . import upgrade_2_0_4 from . import upgrade_2_0_5 +from . import upgrade_2_0_6dev1 # Available upgrade code. This needs to be in consecutive order so # upgrade_inputs can incrementally apply the upgrades. @@ -28,7 +29,8 @@ upgrade_2_0_0b4, upgrade_2_0_1, upgrade_2_0_4, - upgrade_2_0_5 + upgrade_2_0_5, + upgrade_2_0_6dev1 ] ] diff --git a/switch_model/upgrade/upgrade_2_0_5.py b/switch_model/upgrade/upgrade_2_0_5.py index dd48829ef..a86450292 100644 --- a/switch_model/upgrade/upgrade_2_0_5.py +++ b/switch_model/upgrade/upgrade_2_0_5.py @@ -2,9 +2,8 @@ # Licensed under the Apache License, Version 2.0, which is in the LICENSE file. """ -Upgrade input directories from 2.0.1 to 2.0.4. (There were no changes for 2.0.2 -or 2.0.3.) This doesn't actually do anything except update the data version -number and show the module-change messages. +Upgrade input directories from 2.0.4 to 2.0.5: convert all standard inputs to +csv files. """ import os, shutil, argparse, glob diff --git a/switch_model/upgrade/upgrade_2_0_6dev1.py b/switch_model/upgrade/upgrade_2_0_6dev1.py new file mode 100644 index 000000000..4b4cd6740 --- /dev/null +++ b/switch_model/upgrade/upgrade_2_0_6dev1.py @@ -0,0 +1,64 @@ +# Copyright (c) 2015-2019 The Switch Authors. All rights reserved. +# Licensed under the Apache License, Version 2.0, which is in the LICENSE file. + +""" +Upgrade input directories from 2.0.5 to 2.0.6dev1: +transmission lines now require bi-directional specifications. +""" + +import argparse +import glob +import os +import shutil + +import pandas +from pyomo.environ import DataPortal + +import switch_model.upgrade + +upgrades_from = '2.0.5' +upgrades_to = '2.0.6dev1' + +replace_modules = { + # no renames in this version +} + +module_messages = { + # description of significant changes to particular modules (other than moving) + # module: message + 'switch_model.transmission.transport.build': + 'Transmission lines now must be specified in each direction.', +} + +def upgrade_input_dir(inputs_dir): + # Write a new version text file. + switch_model.upgrade._write_input_version(inputs_dir, upgrades_to) + + path = os.path.join(inputs_dir, 'transmission_lines.csv') + try: + df = pandas.read_csv(path) + except FileNotFoundError: + return + + df.rename( + inplace=True, + columns={ + 'trans_lz1': 'trans_lz_send', + 'trans_lz2': 'trans_lz_receive', + }) + # Database key is not the same for non-directional vs directional lines. + # Safest to drop it. + if 'trans_dbid' in df: + df.drop(columns='trans_dbid', inplace=True) + df2 = df.copy() + df2.rename( + inplace=True, + columns={ + 'trans_lz_send': 'trans_lz_receive', + 'trans_lz_receive': 'trans_lz_send' + }) + df2['TRANSMISSION_LINE'] = ( + df2['trans_lz_send'] + '-' + df2['trans_lz_receive'] + ) + df_merged = pandas.concat([df, df2], ignore_index=True, sort=False) + df_merged.to_csv(path, index=False) diff --git a/switch_model/version.py b/switch_model/version.py index 813233d64..3ebd70302 100644 --- a/switch_model/version.py +++ b/switch_model/version.py @@ -5,4 +5,4 @@ distribution because it needs to be executed before Switch (and its dependencies) are installed. """ -__version__='2.0.6-dev' +__version__='2.0.6-dev1' From cf65a08c47319179e002f09aec226338fe859e16 Mon Sep 17 00:00:00 2001 From: "josiah.johnston" Date: Fri, 30 Aug 2019 17:08:12 -0700 Subject: [PATCH 2/4] Update inputs for bi-directional transmission --- .../inputs/switch_inputs_version.txt | 2 +- .../3zone_toy/inputs/transmission_lines.csv | 4 +- examples/3zone_toy_stochastic_PySP/README.md | 6 +-- .../inputs/pysp_inputs/RootNode.dat | 54 +++++++++++-------- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/transmission_lines.csv | 4 +- .../inputs/switch_inputs_version.txt | 2 +- .../carbon_cap/inputs/transmission_lines.csv | 4 +- examples/ccs/inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/transmission_lines.csv | 4 +- .../1plant/inputs/switch_inputs_version.txt | 2 +- .../3plants/inputs/switch_inputs_version.txt | 2 +- .../4plants/inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../inputs/switch_inputs_version.txt | 2 +- .../rps_simple/inputs/transmission_lines.csv | 4 +- .../storage/inputs/switch_inputs_version.txt | 2 +- 31 files changed, 74 insertions(+), 54 deletions(-) diff --git a/examples/3zone_toy/inputs/switch_inputs_version.txt b/examples/3zone_toy/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/3zone_toy/inputs/switch_inputs_version.txt +++ b/examples/3zone_toy/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/3zone_toy/inputs/transmission_lines.csv b/examples/3zone_toy/inputs/transmission_lines.csv index b7fb0cc60..88ccfc1c7 100644 --- a/examples/3zone_toy/inputs/transmission_lines.csv +++ b/examples/3zone_toy/inputs/transmission_lines.csv @@ -1,3 +1,5 @@ -TRANSMISSION_LINE,trans_lz1,trans_lz2,trans_length_km,trans_efficiency,existing_trans_cap +TRANSMISSION_LINE,trans_lz_send,trans_lz_receive,trans_length_km,trans_efficiency,existing_trans_cap N-C,North,Central,100,0.96,3 C-S,Central,South,200,0.94,6 +Central-North,Central,North,100,0.96,3 +South-Central,South,Central,200,0.94,6 diff --git a/examples/3zone_toy_stochastic_PySP/README.md b/examples/3zone_toy_stochastic_PySP/README.md index 52c70d35e..38e7c73f8 100644 --- a/examples/3zone_toy_stochastic_PySP/README.md +++ b/examples/3zone_toy_stochastic_PySP/README.md @@ -165,7 +165,7 @@ lower bounds set in the core mathematical model, bounds can be added in a configuration file which is named pha_bounds_cfg.py in this example. To run progressive hedging with a linear solver such as glpk, use the following command: - >>>runph --model-location=. --instance-directory=inputs/pysp_inputs \ + >>>runph --model-directory . --instance-directory=inputs/pysp_inputs \ --solver=glpk --default-rho=1000.0 --traceback \ --rho-cfgfile=rhosetter.py \ --solution-writer=pyomo.pysp.plugins.csvsolutionwriter \ @@ -178,9 +178,9 @@ decisions are similar, but slightly different. Another equivalent way of solving this example is to run the command: - >>>runph --model-location=. --instance-directory=inputs/pysp_inputs \ + >>>runph --model-directory . --instance-directory=inputs/pysp_inputs \ --solver=gurobi --default-rho=1000.0 --traceback \ - --rho-cfgfile=rhosetter-FS-only.py \ + --rho-cfgfile=rhosetter_FS_only.py \ --solution-writer=pyomo.pysp.plugins.csvsolutionwriter \ --output-scenario-tree-solution diff --git a/examples/3zone_toy_stochastic_PySP/inputs/pysp_inputs/RootNode.dat b/examples/3zone_toy_stochastic_PySP/inputs/pysp_inputs/RootNode.dat index e26f8631b..c2a9abedc 100644 --- a/examples/3zone_toy_stochastic_PySP/inputs/pysp_inputs/RootNode.dat +++ b/examples/3zone_toy_stochastic_PySP/inputs/pysp_inputs/RootNode.dat @@ -52,26 +52,26 @@ param interest_rate := 0.07; param discount_rate := 0.05; set LOAD_ZONES := North Central South; param zone_demand_mw := - North 1 5 - North 2 4 + North 1 5.0 + North 2 4.0 North 3 4.5 North 4 4.2 - North 5 4 - North 6 6 - North 7 6 + North 5 4.0 + North 6 6.0 + North 7 6.0 Central 1 3.7 - Central 2 3 + Central 2 3.0 Central 3 3.6 Central 4 3.3 - Central 5 3 - Central 6 4 + Central 5 3.0 + Central 6 4.0 Central 7 4.6 - South 1 6 - South 2 7 + South 1 6.0 + South 2 7.0 South 3 6.5 South 4 7.2 - South 5 8 - South 6 10 + South 5 8.0 + South 6 10.0 South 7 10.5 ; set EXTERNAL_COINCIDENT_PEAK_DEMAND_ZONE_PERIODS := ('North', 2020) ('Central', 2020) ('South', 2020) ('North', 2030) ('Central', 2030) ('South', 2030); @@ -86,18 +86,18 @@ param zone_expected_coincident_peak_demand := set NON_FUEL_ENERGY_SOURCES := Wind Solar Geothermal Water Electricity; set FUELS := Coal ResidualFuelOil DistillateFuelOil NaturalGas Uranium BioSolid; param f_co2_intensity := - Coal 0.09552 + Coal 0.09552000000000001 ResidualFuelOil 0.0788 DistillateFuelOil 0.07315 NaturalGas 0.05306 - Uranium 0 + Uranium 0.0 BioSolid 0.09435 ; param f_upstream_co2_intensity := - Coal 0 - ResidualFuelOil 0 - DistillateFuelOil 0 - NaturalGas 0 + Coal 0.0 + ResidualFuelOil 0.0 + DistillateFuelOil 0.0 + NaturalGas 0.0 BioSolid -0.09435 ; set GENERATION_PROJECTS := N-Geothermal N-Coal_IGCC N-Coal_IGCC_CCS N-Coal_ST N-NG_CC N-NG_CC_CCS N-NG_GT N-Nuclear N-Biomass_IGCC N-Biomass_IGCC_CCS N-Residential_PV N-Commercial_PV N-Central_PV-1 N-Central_PV-2 N-Wind-1 N-Wind-2 C-Coal_IGCC C-Coal_ST C-NG_CC C-NG_GT C-Nuclear C-Biomass_IGCC C-Residential_PV C-Commercial_PV C-Central_PV-1 C-Central_PV-2 C-Wind-1 C-Wind-2 S-Geothermal S-NG_CC S-NG_CC_CCS S-NG_GT S-Biomass_IGCC S-Biomass_IGCC_CCS S-Residential_PV S-Commercial_PV S-Central_PV-1 S-Central_PV-2; @@ -471,7 +471,7 @@ param gen_full_load_heat_rate := param gen_variable_om := N-Geothermal 28.83 N-Coal_IGCC 6.0822 - N-Coal_IGCC_CCS 9.858 + N-Coal_IGCC_CCS 9.857999999999999 N-Coal_ST 3.4 N-NG_CC 3.4131 N-NG_CC_CCS 9.3 @@ -951,24 +951,34 @@ param fuel_cost := Central BioSolid 2030 7.5 South BioSolid 2030 7.5 ; -set TRANSMISSION_LINES := N-C C-S; -param trans_lz1 := +set TRANSMISSION_LINES := N-C C-S Central-North South-Central; +param trans_lz_send := N-C "North" C-S "Central" + Central-North "Central" + South-Central "South" ; -param trans_lz2 := +param trans_lz_receive := N-C "Central" C-S "South" + Central-North "North" + South-Central "Central" ; param trans_length_km := N-C 100 C-S 200 + Central-North 100 + South-Central 200 ; param trans_efficiency := N-C 0.96 C-S 0.94 + Central-North 0.96 + South-Central 0.94 ; param existing_trans_cap := N-C 3 C-S 6 + Central-North 3 + South-Central 6 ; diff --git a/examples/3zone_toy_stochastic_PySP/inputs/switch_inputs_version.txt b/examples/3zone_toy_stochastic_PySP/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/3zone_toy_stochastic_PySP/inputs/switch_inputs_version.txt +++ b/examples/3zone_toy_stochastic_PySP/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/3zone_toy_stochastic_PySP/inputs/transmission_lines.csv b/examples/3zone_toy_stochastic_PySP/inputs/transmission_lines.csv index b7fb0cc60..88ccfc1c7 100644 --- a/examples/3zone_toy_stochastic_PySP/inputs/transmission_lines.csv +++ b/examples/3zone_toy_stochastic_PySP/inputs/transmission_lines.csv @@ -1,3 +1,5 @@ -TRANSMISSION_LINE,trans_lz1,trans_lz2,trans_length_km,trans_efficiency,existing_trans_cap +TRANSMISSION_LINE,trans_lz_send,trans_lz_receive,trans_length_km,trans_efficiency,existing_trans_cap N-C,North,Central,100,0.96,3 C-S,Central,South,200,0.94,6 +Central-North,Central,North,100,0.96,3 +South-Central,South,Central,200,0.94,6 diff --git a/examples/carbon_cap/inputs/switch_inputs_version.txt b/examples/carbon_cap/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/carbon_cap/inputs/switch_inputs_version.txt +++ b/examples/carbon_cap/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/carbon_cap/inputs/transmission_lines.csv b/examples/carbon_cap/inputs/transmission_lines.csv index b7fb0cc60..88ccfc1c7 100644 --- a/examples/carbon_cap/inputs/transmission_lines.csv +++ b/examples/carbon_cap/inputs/transmission_lines.csv @@ -1,3 +1,5 @@ -TRANSMISSION_LINE,trans_lz1,trans_lz2,trans_length_km,trans_efficiency,existing_trans_cap +TRANSMISSION_LINE,trans_lz_send,trans_lz_receive,trans_length_km,trans_efficiency,existing_trans_cap N-C,North,Central,100,0.96,3 C-S,Central,South,200,0.94,6 +Central-North,Central,North,100,0.96,3 +South-Central,South,Central,200,0.94,6 diff --git a/examples/ccs/inputs/switch_inputs_version.txt b/examples/ccs/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/ccs/inputs/switch_inputs_version.txt +++ b/examples/ccs/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/copperplate0/inputs/switch_inputs_version.txt b/examples/copperplate0/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/copperplate0/inputs/switch_inputs_version.txt +++ b/examples/copperplate0/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/copperplate1/inputs/switch_inputs_version.txt b/examples/copperplate1/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/copperplate1/inputs/switch_inputs_version.txt +++ b/examples/copperplate1/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/custom_extension/inputs/switch_inputs_version.txt b/examples/custom_extension/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/custom_extension/inputs/switch_inputs_version.txt +++ b/examples/custom_extension/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/discrete_and_min_build/inputs/switch_inputs_version.txt b/examples/discrete_and_min_build/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/discrete_and_min_build/inputs/switch_inputs_version.txt +++ b/examples/discrete_and_min_build/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/discrete_build/inputs/switch_inputs_version.txt b/examples/discrete_build/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/discrete_build/inputs/switch_inputs_version.txt +++ b/examples/discrete_build/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/dr_simple/inputs/switch_inputs_version.txt b/examples/dr_simple/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/dr_simple/inputs/switch_inputs_version.txt +++ b/examples/dr_simple/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/hydro_simple/inputs/switch_inputs_version.txt b/examples/hydro_simple/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/hydro_simple/inputs/switch_inputs_version.txt +++ b/examples/hydro_simple/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/hydro_system/inputs/switch_inputs_version.txt b/examples/hydro_system/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/hydro_system/inputs/switch_inputs_version.txt +++ b/examples/hydro_system/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/new_builds_only/inputs/switch_inputs_version.txt b/examples/new_builds_only/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/new_builds_only/inputs/switch_inputs_version.txt +++ b/examples/new_builds_only/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/planning_reserves/inputs/switch_inputs_version.txt b/examples/planning_reserves/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/planning_reserves/inputs/switch_inputs_version.txt +++ b/examples/planning_reserves/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/planning_reserves/inputs/transmission_lines.csv b/examples/planning_reserves/inputs/transmission_lines.csv index b7fb0cc60..88ccfc1c7 100644 --- a/examples/planning_reserves/inputs/transmission_lines.csv +++ b/examples/planning_reserves/inputs/transmission_lines.csv @@ -1,3 +1,5 @@ -TRANSMISSION_LINE,trans_lz1,trans_lz2,trans_length_km,trans_efficiency,existing_trans_cap +TRANSMISSION_LINE,trans_lz_send,trans_lz_receive,trans_length_km,trans_efficiency,existing_trans_cap N-C,North,Central,100,0.96,3 C-S,Central,South,200,0.94,6 +Central-North,Central,North,100,0.96,3 +South-Central,South,Central,200,0.94,6 diff --git a/examples/production_cost_models/1plant/inputs/switch_inputs_version.txt b/examples/production_cost_models/1plant/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/1plant/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/1plant/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/production_cost_models/3plants/inputs/switch_inputs_version.txt b/examples/production_cost_models/3plants/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/3plants/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/3plants/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/production_cost_models/4plants/inputs/switch_inputs_version.txt b/examples/production_cost_models/4plants/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/4plants/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/4plants/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/production_cost_models/4plants_with_unserved_load/inputs/switch_inputs_version.txt b/examples/production_cost_models/4plants_with_unserved_load/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/4plants_with_unserved_load/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/4plants_with_unserved_load/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/production_cost_models/discrete_unit_commit/inputs/switch_inputs_version.txt b/examples/production_cost_models/discrete_unit_commit/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/discrete_unit_commit/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/discrete_unit_commit/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/production_cost_models/spinning_reserves/inputs/switch_inputs_version.txt b/examples/production_cost_models/spinning_reserves/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/spinning_reserves/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/spinning_reserves/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/production_cost_models/spinning_reserves_advanced/inputs/switch_inputs_version.txt b/examples/production_cost_models/spinning_reserves_advanced/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/spinning_reserves_advanced/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/spinning_reserves_advanced/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/production_cost_models/unit_commit/inputs/switch_inputs_version.txt b/examples/production_cost_models/unit_commit/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/production_cost_models/unit_commit/inputs/switch_inputs_version.txt +++ b/examples/production_cost_models/unit_commit/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/rps_simple/inputs/switch_inputs_version.txt b/examples/rps_simple/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/rps_simple/inputs/switch_inputs_version.txt +++ b/examples/rps_simple/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 diff --git a/examples/rps_simple/inputs/transmission_lines.csv b/examples/rps_simple/inputs/transmission_lines.csv index b7fb0cc60..88ccfc1c7 100644 --- a/examples/rps_simple/inputs/transmission_lines.csv +++ b/examples/rps_simple/inputs/transmission_lines.csv @@ -1,3 +1,5 @@ -TRANSMISSION_LINE,trans_lz1,trans_lz2,trans_length_km,trans_efficiency,existing_trans_cap +TRANSMISSION_LINE,trans_lz_send,trans_lz_receive,trans_length_km,trans_efficiency,existing_trans_cap N-C,North,Central,100,0.96,3 C-S,Central,South,200,0.94,6 +Central-North,Central,North,100,0.96,3 +South-Central,South,Central,200,0.94,6 diff --git a/examples/storage/inputs/switch_inputs_version.txt b/examples/storage/inputs/switch_inputs_version.txt index e01025862..233f0bbba 100644 --- a/examples/storage/inputs/switch_inputs_version.txt +++ b/examples/storage/inputs/switch_inputs_version.txt @@ -1 +1 @@ -2.0.5 +2.0.6dev1 From 11f8daad3e646de75a8fdc9f0ef81a8236acade3 Mon Sep 17 00:00:00 2001 From: "josiah.johnston" Date: Fri, 30 Aug 2019 17:08:52 -0700 Subject: [PATCH 3/4] Avoid printing excessive digits in total_cost.txt that generally reflect minute numerical instabilities between solvers. --- switch_model/reporting/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/switch_model/reporting/__init__.py b/switch_model/reporting/__init__.py index f9017ae6a..bbd5fb96d 100644 --- a/switch_model/reporting/__init__.py +++ b/switch_model/reporting/__init__.py @@ -193,7 +193,7 @@ def get_value(obj): def save_total_cost_value(instance, outdir): with open(os.path.join(outdir, 'total_cost.txt'), 'w') as fh: - fh.write('{}\n'.format(value(instance.SystemCost))) + fh.write('{:.3f}\n'.format(value(instance.SystemCost))) def save_cost_components(m, outdir): From 0fd7b075bc0fb1f62d6920b7d5c2b0b880ced217 Mon Sep 17 00:00:00 2001 From: "josiah.johnston" Date: Tue, 10 Sep 2019 16:51:45 -0700 Subject: [PATCH 4/4] Add optional pre-determined transmission builds (for future transmission lines), and update an example to include that functionality. --- .../inputs/trans_build_predetermined.csv | 2 ++ examples/3zone_toy/outputs/total_cost.txt | 2 +- switch_model/transmission/transport/build.py | 34 ++++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 examples/3zone_toy/inputs/trans_build_predetermined.csv diff --git a/examples/3zone_toy/inputs/trans_build_predetermined.csv b/examples/3zone_toy/inputs/trans_build_predetermined.csv new file mode 100644 index 000000000..9a1f623e9 --- /dev/null +++ b/examples/3zone_toy/inputs/trans_build_predetermined.csv @@ -0,0 +1,2 @@ +TRANSMISSION_LINE,PERIOD,trans_predetermined_cap +C-S,2030,2.5 \ No newline at end of file diff --git a/examples/3zone_toy/outputs/total_cost.txt b/examples/3zone_toy/outputs/total_cost.txt index 7db8d1c71..fbe36e67f 100644 --- a/examples/3zone_toy/outputs/total_cost.txt +++ b/examples/3zone_toy/outputs/total_cost.txt @@ -1 +1 @@ -134733088.429 +134736088.426 diff --git a/switch_model/transmission/transport/build.py b/switch_model/transmission/transport/build.py index c9ba7d1bd..66b4a3eab 100644 --- a/switch_model/transmission/transport/build.py +++ b/switch_model/transmission/transport/build.py @@ -230,7 +230,23 @@ def _TX_CONNECTED_ZONES_init(m): dimen=2, initialize=mod.TRANSMISSION_LINES * mod.PERIODS, filter=lambda m, tx, p: m.trans_new_build_allowed[tx]) - mod.BuildTx = Var(mod.TRANS_BLD_YRS, within=NonNegativeReals) + mod.PREDETERMINED_TX_BLD_YRS = Set( + dimen=2, + within=mod.TRANS_BLD_YRS) + mod.trans_predetermined_cap = Param( + mod.PREDETERMINED_TX_BLD_YRS, + within=NonNegativeReals) + def bounds_BuildTx(m, tx, bld_yr): + try: + cap = m.trans_predetermined_cap[tx, bld_yr] + bounds = (cap, cap) + except KeyError: + bounds = (0, None) + return bounds + mod.BuildTx = Var( + mod.TRANS_BLD_YRS, + within=NonNegativeReals, + bounds=bounds_BuildTx) mod.Tx_New_Builds_Symmetric = Constraint( mod.TRANS_BLD_YRS, rule=lambda m, tx, bld_yr: ( @@ -313,6 +329,13 @@ def load_inputs(mod, switch_data, inputs_dir): trans_derating_factor*, trans_terrain_multiplier*, trans_new_build_allowed* + Predetermined transmission builds should only list builds during study + periods. Existing transmission needs to be listed in transmission_lines.csv + This is a different style than predetermined generation builds. + + trans_build_predetermined.csv* + TRANSMISSION_LINE, PERIOD, trans_predetermined_cap + Note that in the next file, parameter names are written on the first row (as usual), and the single value for each parameter is written in the second row. @@ -338,6 +361,15 @@ def load_inputs(mod, switch_data, inputs_dir): mod.trans_terrain_multiplier, mod.trans_new_build_allowed ) ) + switch_data.load_aug( + filename=os.path.join(inputs_dir, 'trans_build_predetermined.csv'), + optional=True, + auto_select=True, + index=mod.PREDETERMINED_TX_BLD_YRS, + param=( + mod.trans_predetermined_cap, + ) + ) switch_data.load_aug( filename=os.path.join(inputs_dir, 'trans_params.csv'), optional=True, auto_select=True,