Skip to content

Commit 07c77dc

Browse files
committed
Add feature for interactively exploring outputs from a previous model run.
Both the input and output datasets from a previous model run are loaded into a Switch model instance to allow interactive exploration. This enables using model Expressions, Sets and Variables to quickly produce new outputs or for quality control of solutions. This feature was discussed in Pull Request #91 (#91). Also, updates Copperplate0 example's outputs. Added missing snippet to 2.0.0b1 upgrade script.
1 parent 8b50539 commit 07c77dc

File tree

8 files changed

+94
-42
lines changed

8 files changed

+94
-42
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DispatchBaseloadByPeriod_index_1 DispatchBaseloadByPeriod_index_2 DispatchBaseloadByPeriod
2+
S-Geothermal 2020 0.5
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
GEN_TPS_1 GEN_TPS_2 DispatchGen
2+
S-Geothermal 2 0.5
3+
S-Geothermal 1 0.5
4+
S-NG_CC 1 6.9022
5+
S-NG_CC 2 0.0
6+
S-Central_PV-1 2 0.0
7+
S-Central_PV-1 1 0.5978
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
GEN_TP_FUELS_1 GEN_TP_FUELS_2 GEN_TP_FUELS_3 GenFuelUseRate
2+
S-NG_CC 1 NaturalGas 46.279251
3+
S-NG_CC 2 NaturalGas 0.0
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
timestamp S-Geothermal S-NG_CC S-Central_PV-1
2-
2025011512 0.96858075 6.43361925 0.5978
3-
2025011600 0.96858075 0.0 0.0
2+
2025011512 0.5 6.9022 0.5978
3+
2025011600 0.5 0 0
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
GENERATION_PROJECT PERIOD GenCapacity GenCapitalCosts GenFixedOMCosts
2+
S-Central_PV-1 2020 1 227410 41850
3+
S-NG_CC 2020 7.34277 832743 43089.6
4+
S-Geothermal 2020 1 455992 0
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
load_zone timestamp LZ_NetDispatch lz_demand_mw DumpPower
2-
South 2025011512 8.0 8 0.0
3-
South 2025011600 0.96858075 0.5 0.46858075
1+
load_zone timestamp ZoneTotalCentralDispatch ZoneTotalDistributedDispatch zone_demand_mw
2+
South 2025011512 8 0 8
3+
South 2025011600 0.5 0 0.5

switch_model/solve.py

Lines changed: 70 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ def debug(type, value, tb):
4646
parser = _ArgumentParser(allow_abbrev=False, add_help=False)
4747
add_module_args(parser)
4848
module_options = parser.parse_known_args(args=args)[0]
49-
if do_inputs_need_upgrade(module_options.inputs_dir):
49+
if(os.path.exists(module_options.inputs_dir) and
50+
do_inputs_need_upgrade(module_options.inputs_dir)):
5051
do_upgrade = query_yes_no(
5152
("Warning! Your inputs directory needs to be upgraded. "
5253
"Do you want to auto-upgrade now? We'll keep a backup of "
@@ -76,6 +77,10 @@ def debug(type, value, tb):
7677
if return_model and not return_instance:
7778
return model
7879

80+
if model.options.reload_prior_solution:
81+
if not os.path.isdir(model.options.outputs_dir):
82+
raise IOError("Specified outputs directory for solution exploration does not exist.")
83+
7984
# get a list of modules to iterate through
8085
iterate_modules = get_iteration_list(model)
8186

@@ -93,9 +98,9 @@ def debug(type, value, tb):
9398
# create an instance
9499
instance = model.load_inputs()
95100
instance.pre_solve()
101+
instantiation_time = time.time()
96102
if model.options.verbose:
97-
instantiation_time = time.time()
98-
print "Inputs loaded in {:.2f} s.".format(instantiation_time - creation_time)
103+
print "Inputs loaded in {:.2f} s.\n".format(instantiation_time - creation_time)
99104

100105
# return the instance as-is if requested
101106
if return_instance:
@@ -104,49 +109,73 @@ def debug(type, value, tb):
104109
else:
105110
return instance
106111

107-
# make sure the outputs_dir exists (used by some modules during iterate)
108-
# use a race-safe approach in case this code is run in parallel
109-
try:
110-
os.makedirs(instance.options.outputs_dir)
111-
except OSError:
112-
# directory probably exists already, but double-check
113-
if not os.path.isdir(instance.options.outputs_dir):
114-
raise
115-
116-
# solve the model
117-
if iterate_modules:
118-
if instance.options.verbose:
119-
print "iterating model..."
120-
iterate(instance, iterate_modules)
112+
if model.options.reload_prior_solution:
113+
# read variable values from previously solved model
114+
import csv
115+
var_objects = [c for c in instance.component_objects()
116+
if isinstance(c,pyomo.core.base.Var)]
117+
def _convert_if_numeric(s):
118+
try:
119+
return float(s)
120+
except ValueError:
121+
return s
122+
for var in var_objects:
123+
if '{}.tab'.format(var.name) not in os.listdir(model.options.outputs_dir):
124+
raise RuntimeError("Tab output file for variable {} cannot be found in outputs directory. Exiting.".format(var.name))
125+
with open(os.path.join(model.options.outputs_dir, '{}.tab'.format(var.name)),'r') as f:
126+
reader = csv.reader(f, delimiter='\t')
127+
# skip headers
128+
next(reader)
129+
for row in reader:
130+
index = (_convert_if_numeric(i) for i in row[:-1])
131+
var[index].value = float(row[-1])
132+
print 'Loaded variable {} values into instance.'.format(var.name)
133+
output_loading_time = time.time()
134+
print 'Finished loading previous results into model instance in {:.2f} s.'.format(output_loading_time - instantiation_time)
121135
else:
122-
results = solve(instance)
123-
if instance.options.verbose:
124-
print "Optimization termination condition was {}.\n".format(
125-
results.solver.termination_condition)
126-
127-
# report/save results
128-
if instance.options.verbose:
129-
post_solve_start_time = time.time()
130-
print "Executing post solve functions..."
131-
instance.post_solve()
132-
if instance.options.verbose:
133-
post_solve_end_time = time.time()
134-
print "Post solve processing completed in {:.2f} s.".format(
135-
post_solve_end_time - post_solve_start_time)
136+
# make sure the outputs_dir exists (used by some modules during iterate)
137+
# use a race-safe approach in case this code is run in parallel
138+
try:
139+
os.makedirs(model.options.outputs_dir)
140+
except OSError:
141+
# directory probably exists already, but double-check
142+
if not os.path.isdir(model.options.outputs_dir):
143+
raise
144+
145+
# solve the model
146+
if iterate_modules:
147+
if model.options.verbose:
148+
print "Iterating model..."
149+
iterate(instance, iterate_modules)
150+
else:
151+
results = solve(instance)
152+
if model.options.verbose:
153+
print "Optimization termination condition was {}.\n".format(
154+
results.solver.termination_condition)
155+
156+
# report/save results
157+
if model.options.verbose:
158+
post_solve_start_time = time.time()
159+
print "Executing post solve functions..."
160+
instance.post_solve()
161+
if model.options.verbose:
162+
post_solve_end_time = time.time()
163+
print "Post solve processing completed in {:.2f} s.".format(
164+
post_solve_end_time - post_solve_start_time)
136165

137166
# return stdout to original
138167
sys.stdout = stdout_copy
139168

140-
if pre_module_options.interact:
169+
if model.options.interact or model.options.reload_prior_solution:
141170
m = instance # present the solved model as 'm' for convenience
142171
banner = (
143172
"\n"
144-
"=================================================================================\n"
173+
"=======================================================================\n"
145174
"Entering interactive Python shell.\n"
146175
"Abstract model is in 'model' variable; \n"
147176
"Solved instance is in 'instance' and 'm' variables.\n"
148177
"Type ctrl-d or exit() to exit shell.\n"
149-
"=================================================================================\n"
178+
"=======================================================================\n"
150179
)
151180
import code
152181
code.interact(banner=banner, local=dict(globals().items() + locals().items()))
@@ -331,6 +360,12 @@ def define_arguments(argparser):
331360
argparser.add_argument(
332361
'--verbose', '-v', default=False, action='store_true',
333362
help='Show information about model preparation and solution')
363+
argparser.add_argument(
364+
'--interact', default=False, action='store_true',
365+
help='Enter interactive shell after solving the instance to enable inspection of the solved model.')
366+
argparser.add_argument(
367+
'--reload-prior-solution', default=False, action='store_true',
368+
help='Load outputs from a previously solved instance into variable values to allow interactive exploration of model components without having to solve the instance again.')
334369

335370

336371
def add_module_args(parser):
@@ -362,8 +397,7 @@ def add_pre_module_args(parser):
362397
help='Directory containing log files (default is "logs"')
363398
parser.add_argument("--debug", action="store_true", default=False,
364399
help='Automatically start pdb debugger on exceptions')
365-
parser.add_argument("--interact", action="store_true", default=False,
366-
help='Enter interactive shell after solving model (to inspect finished model).')
400+
367401

368402
def parse_pre_module_options(args):
369403
"""
@@ -612,7 +646,6 @@ def query_yes_no(question, default="yes"):
612646
"(or 'y' or 'n').\n")
613647

614648

615-
616649
###############
617650

618651
if __name__ == "__main__":

switch_model/upgrade/upgrade_2_0_0b1.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,9 @@ def update_cols_with_defaults(df, col_list):
420420
],
421421
'zone_coincident_peak_demand.tab':[
422422
('peak_demand_mw','zone_expected_coincident_peak_demand')
423+
],
424+
'variable_capacity_factors.tab':[
425+
('proj_max_capacity_factor','gen_max_capacity_factor')
423426
]
424427
}
425428

0 commit comments

Comments
 (0)