source: coopr.pysp/trunk/coopr/pysp/phinit.py @ 3120

Last change on this file since 3120 was 3120, checked in by jwatson, 11 years ago

Adding PySP options for linearizing expressions.

  • Property svn:executable set to *
File size: 29.8 KB
Line 
1#  _________________________________________________________________________
2#
3#  Coopr: A COmmon Optimization Python Repository
4#  Copyright (c) 2009 Sandia Corporation.
5#  This software is distributed under the BSD License.
6#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7#  the U.S. Government retains certain rights in this software.
8#  For more information, see the Coopr README.txt file.
9#  _________________________________________________________________________
10
11
12import os
13import sys
14
15from optparse import OptionParser, OptionGroup
16
17import pyutilib.services
18import pyutilib.misc
19
20# garbage collection control.
21import gc
22
23# for profiling
24try:
25    import cProfile as profile
26except ImportError:
27    import profile
28import pstats
29
30# for serializing
31import pickle
32
33from coopr.pysp.convergence import *
34from coopr.pysp.scenariotree import *
35from coopr.pysp.ph import *
36from coopr.pysp.ef import *
37from coopr.opt.base import SolverFactory
38from coopr.opt.parallel import SolverManagerFactory
39
40from pyutilib.component.core import ExtensionPoint
41from coopr.pysp.solutionwriter import ISolutionWriterExtension
42
43#
44# utility method to construct an option parser for ph arguments,
45# to be supplied as an argument to the runph method.
46#
47
48def construct_ph_options_parser(usage_string):
49
50   parser = OptionParser()
51   parser.usage = usage_string
52
53   inputOpts       = OptionGroup( parser, 'Input Options' )
54   phOpts          = OptionGroup( parser, 'PH Options' )
55   solverOpts      = OptionGroup( parser, 'Solver Options' )
56   postprocessOpts = OptionGroup( parser, 'Postprocessing Options' )   
57   outputOpts      = OptionGroup( parser, 'Output Options' )
58   otherOpts       = OptionGroup( parser, 'Other Options' )
59   parser.add_option_group( inputOpts )
60   parser.add_option_group( phOpts )
61   parser.add_option_group( solverOpts )
62   parser.add_option_group( postprocessOpts )   
63   parser.add_option_group( outputOpts )
64   parser.add_option_group( otherOpts )
65
66   inputOpts.add_option('-m','--model-directory',
67     help="The directory in which all model (reference and scenario) definitions are stored. Default is \".\".",
68     action="store",
69     dest="model_directory",
70     type="string",
71     default=".")
72   inputOpts.add_option('-i','--instance-directory',
73     help="The directory in which all instance (reference and scenario) definitions are stored. Default is '.'.",
74     action="store",
75     dest="instance_directory",
76     type="string",
77     default=".")
78   inputOpts.add_option('--bounds-cfgfile',
79     help="The name of a configuration script to set variable bound values. Default is None.",
80     action="store",
81     dest="bounds_cfgfile",
82     default=None)
83
84   phOpts.add_option('-r','--default-rho',
85     help="The default (global) rho for all blended variables. Default is 1.",
86     action="store",
87     dest="default_rho",
88     type="float",
89     default=1.0)
90   phOpts.add_option('--rho-cfgfile',
91     help="The name of a configuration script to compute PH rho values. Default is None.",
92     action="store",
93     dest="rho_cfgfile",
94     type="string",
95     default=None)
96   phOpts.add_option('--max-iterations',
97     help="The maximal number of PH iterations. Default is 100.",
98     action="store",
99     dest="max_iterations",
100     type="int",
101     default=100)
102   phOpts.add_option('--termdiff-threshold',
103     help="The convergence threshold used in the term-diff and normalized term-diff convergence criteria. Default is 0.01.",
104     action="store",
105     dest="termdiff_threshold",
106     type="float",
107     default=0.01)
108   phOpts.add_option('--enable-free-discrete-count-convergence',
109     help="Terminate PH based on the free discrete variable count convergence metric. Default is False.",
110     action="store_true",
111     dest="enable_free_discrete_count_convergence",
112     default=False)
113   phOpts.add_option('--enable-normalized-termdiff-convergence',
114     help="Terminate PH based on the normalized termdiff convergence metric. Default is True.",
115     action="store_true",
116     dest="enable_normalized_termdiff_convergence",
117     default=False)
118   phOpts.add_option('--enable-termdiff-convergence',
119     help="Terminate PH based on the termdiff convergence metric. Default is True.",
120     action="store_true",
121     dest="enable_termdiff_convergence",
122     default=True)
123   phOpts.add_option('--free-discrete-count-threshold',
124     help="The convergence threshold used in the criterion based on when the free discrete variable count convergence criterion. Default is 20.",
125     action="store",
126     dest="free_discrete_count_threshold",
127     type="float",
128     default=20)
129   phOpts.add_option('--linearize-nonbinary-penalty-terms',
130     help="Approximate the PH quadratic term for non-binary variables with a piece-wise linear function, using the supplied number of equal-length pieces from each bound to the average",
131     action="store",
132     dest="linearize_nonbinary_penalty_terms",
133     type="int",
134     default=0)
135   phOpts.add_option('--breakpoint-strategy',
136     help="Specify the strategy to distribute breakpoints on the [lb, ub] interval of each variable when linearizing. 0 indicates uniform distribution. 1 indicates breakpoints at the node min and max, uniformly in-between. 2 indicates more aggressive concentration of breakpoints near the observed node min/max.",
137     action="store",
138     dest="breakpoint_strategy",
139     type="int",
140     default=0)   
141   phOpts.add_option('--retain-quadratic-binary-terms',
142     help="Do not linearize PH objective terms involving binary decision variables",
143     action="store_true",
144     dest="retain_quadratic_binary_terms",
145     default=False)
146   phOpts.add_option('--drop-proximal-terms',
147     help="Eliminate proximal terms (i.e., the quadratic penalty terms) from the weighted PH objective. Default is False.",
148     action="store_true",
149     dest="drop_proximal_terms",
150     default=False)
151   phOpts.add_option('--enable-ww-extensions',
152     help="Enable the Watson-Woodruff PH extensions plugin. Default is False.",
153     action="store_true",
154     dest="enable_ww_extensions",
155     default=False)
156   phOpts.add_option('--ww-extension-cfgfile',
157     help="The name of a configuration file for the Watson-Woodruff PH extensions plugin. Default is wwph.cfg.",
158     action="store",
159     dest="ww_extension_cfgfile",
160     type="string",
161     default="")
162   phOpts.add_option('--ww-extension-suffixfile',
163     help="The name of a variable suffix file for the Watson-Woodruff PH extensions plugin. Default is wwph.suffixes.",
164     action="store",
165     dest="ww_extension_suffixfile",
166     type="string",
167     default="")
168   phOpts.add_option('--user-defined-extension',
169     help="The name of a python module specifying a user-defined PH extension plugin.",
170     action="store",
171     dest="user_defined_extension",
172     type="string",
173     default=None)
174   
175   solverOpts.add_option('--scenario-mipgap',
176     help="Specifies the mipgap for all PH scenario sub-problems",
177     action="store",
178     dest="scenario_mipgap",
179     type="float",
180     default=None)
181   solverOpts.add_option('--scenario-solver-options',
182     help="Solver options for all PH scenario sub-problems",
183     action="append",
184     dest="scenario_solver_options",
185     type="string",
186     default=[])
187   solverOpts.add_option('--solver',
188     help="The type of solver used to solve scenario sub-problems. Default is cplex.",
189     action="store",
190     dest="solver_type",
191     type="string",
192     default="cplex")
193   solverOpts.add_option('--solver-manager',
194     help="The type of solver manager used to coordinate scenario sub-problem solves. Default is serial.",
195     action="store",
196     dest="solver_manager_type",
197     type="string",
198     default="serial")
199   solverOpts.add_option('--disable-warmstarts',
200     help="Disable warm-start of scenario sub-problem solves in PH iterations >= 1. Default is False.",
201     action="store_true",
202     dest="disable_warmstarts",
203     default=False)
204   solverOpts.add_option("--linearize-expressions",
205     help="EXPERIMENTAL: An option intended for use on linear or mixed-integer models " \
206          "in which expression trees in a model (constraints or objectives) are compacted " \
207          "into a more memory-efficient and concise form. The trees themselves are eliminated. ",
208     action="store_true",
209     dest="linearize_expressions",
210     default=False)
211
212   postprocessOpts.add_option('--ef-output-file',
213     help="The name of the extensive form output file (currently only LP format is supported), if writing of the extensive form is enabled. Default is efout.lp.",
214     action="store",
215     dest="ef_output_file",
216     type="string",
217     default="efout.lp")
218   postprocessOpts.add_option('--solve-ef',
219     help="Following write of the extensive form model, solve it.",
220     action="store_true",
221     dest="solve_ef",
222     default=False)
223   postprocessOpts.add_option('--ef-mipgap',
224     help="Specifies the mipgap for the EF solve",
225     action="store",
226     dest="ef_mipgap",
227     type="float",
228     default=None)
229   postprocessOpts.add_option('--ef-solver-options',
230     help="Solver options for the extension form problem",
231     action="append",
232     dest="ef_solver_options",
233     type="string",
234     default=[])   
235   postprocessOpts.add_option('--output-ef-solver-log',
236     help="Output solver log during the extensive form solve",
237     action="store_true",
238     dest="output_ef_solver_log",
239     default=False)
240   
241   outputOpts.add_option('--output-scenario-tree-solution',
242     help="Report the full solution (even leaves) in scenario tree format upon termination. Values represent averages, so convergence is not an issue. Default is False.",
243     action="store_true",
244     dest="output_scenario_tree_solution",
245     default=False)
246   outputOpts.add_option('--output-solver-logs',
247     help="Output solver logs during scenario sub-problem solves",
248     action="store_true",
249     dest="output_solver_logs",
250     default=False)
251   outputOpts.add_option('--output-solver-results',
252     help="Output solutions obtained after each scenario sub-problem solve",
253     action="store_true",
254     dest="output_solver_results",
255     default=False)
256   outputOpts.add_option('--output-times',
257     help="Output timing statistics for various PH components",
258     action="store_true",
259     dest="output_times",
260     default=False)
261   outputOpts.add_option('--report-only-statistics',
262     help="When reporting solutions (if enabled), only output per-variable statistics - not the individual scenario values. Default is False.",
263     action="store_true",
264     dest="report_only_statistics",
265     default=False)
266   outputOpts.add_option('--report-solutions',
267     help="Always report PH solutions after each iteration. Enabled if --verbose is enabled. Default is False.",
268     action="store_true",
269     dest="report_solutions",
270     default=False)
271   outputOpts.add_option('--report-weights',
272     help="Always report PH weights prior to each iteration. Enabled if --verbose is enabled. Default is False.",
273     action="store_true",
274     dest="report_weights",
275     default=False)
276   outputOpts.add_option('--restore-from-checkpoint',
277     help="The name of the checkpoint file from which PH should be initialized. Default is \"\", indicating no checkpoint restoration",
278     action="store",
279     dest="restore_from_checkpoint",
280     type="string",
281     default="")
282   outputOpts.add_option('--solution-writer',
283     help="The plugin invoked to write the scenario tree solution. Defaults to the empty list.",
284     action="append",
285     dest="solution_writer",
286     type="string",
287     default = [])
288   outputOpts.add_option('--suppress-continuous-variable-output',
289     help="Eliminate PH-related output involving continuous variables.",
290     action="store_true",
291     dest="suppress_continuous_variable_output",
292     default=False)
293   outputOpts.add_option('--verbose',
294     help="Generate verbose output for both initialization and execution. Default is False.",
295     action="store_true",
296     dest="verbose",
297     default=False)
298   outputOpts.add_option('--write-ef',
299     help="Upon termination, write the extensive form of the model - accounting for all fixed variables.",
300     action="store_true",
301     dest="write_ef",
302     default=False)
303
304   otherOpts.add_option('--disable-gc',
305     help="Disable the python garbage collecter. Default is False.",
306     action="store_true",
307     dest="disable_gc",
308     default=False)
309   otherOpts.add_option('--keep-solver-files',
310     help="Retain temporary input and output files for scenario sub-problem solves",
311     action="store_true",
312     dest="keep_solver_files",
313     default=False)
314   otherOpts.add_option('--profile',
315     help="Enable profiling of Python code.  The value of this option is the number of functions that are summarized.",
316     action="store",
317     dest="profile",
318     type="int",
319     default=0)
320   otherOpts.add_option('--checkpoint-interval',
321     help="The number of iterations between writing of a checkpoint file. Default is 0, indicating never.",
322     action="store",
323     dest="checkpoint_interval",
324     type="int",
325     default=0)
326   otherOpts.add_option('--traceback',
327     help="When an exception is thrown, show the entire call stack. Ignored if profiling is enabled. Default is False.",
328     action="store_true",
329     dest="traceback",
330     default=False)   
331
332   return parser
333
334#
335# Create the reference model / instance and scenario tree instance for PH.
336# IMPT: This method should be moved into a more generic module - it has nothing
337#       to do with PH, and is used elsewhere (by routines that shouldn't have
338#       to know about PH).
339#
340
341def load_reference_and_scenario_models(options):
342
343   #
344   # create and populate the reference model/instance pair.
345   #
346
347   reference_model = None
348   reference_instance = None
349
350   try:
351      reference_model_filename = os.path.expanduser(options.model_directory)+os.sep+"ReferenceModel.py"
352      if options.verbose is True:
353         print "Scenario reference model filename="+reference_model_filename
354      model_import = pyutilib.misc.import_file(reference_model_filename)
355      if "model" not in dir(model_import):
356         print ""
357         print "***ERROR: Exiting test driver: No 'model' object created in module "+reference_model_filename
358         return None, None, None, None
359
360      if model_import.model is None:
361         print ""
362         print "***ERROR: Exiting test driver: 'model' object equals 'None' in module "+reference_model_filename
363         return None, None, None, None
364
365      reference_model = model_import.model
366   except IOError:
367      print "***ERROR: Failed to load scenario reference model from file="+reference_model_filename
368      return None, None, None, None
369
370   try:
371      reference_instance_filename = os.path.expanduser(options.instance_directory)+os.sep+"ReferenceModel.dat"
372      if options.verbose is True:
373         print "Scenario reference instance filename="+reference_instance_filename
374      reference_instance = reference_model.create(reference_instance_filename, preprocess=False)
375      # IMPT: disable canonical representation construction for ASL solvers.
376      #       this is a hack, in that we need to address encodings and
377      #       the like at a more general level.
378      if options.solver_type == "asl":
379         reference_instance.skip_canonical_repn = True
380      else:
381         reference_instance.preprocess()
382     
383   except IOError:
384      print "***ERROR: Failed to load scenario reference instance data from file="+reference_instance_filename
385      return None, None, None, None
386
387   #
388   # create and populate the scenario tree model
389   #
390
391   from coopr.pysp.util.scenariomodels import scenario_tree_model
392   scenario_tree_instance = None
393
394   try:
395      scenario_tree_instance_filename = os.path.expanduser(options.instance_directory)+os.sep+"ScenarioStructure.dat"
396      if options.verbose is True:
397         print "Scenario tree instance filename="+scenario_tree_instance_filename
398      scenario_tree_instance = scenario_tree_model.create(scenario_tree_instance_filename)
399   except IOError:
400      print "***ERROR: Failed to load scenario tree reference instance data from file="+scenario_tree_instance_filename
401      return None, None, None, None
402
403   #
404   # construct the scenario tree
405   #
406   scenario_tree = ScenarioTree(scenarioinstance=reference_instance,
407                                scenariotreeinstance=scenario_tree_instance)
408
409   return reference_model, reference_instance, scenario_tree, scenario_tree_instance
410
411#
412# Create a PH object from a (pickl) checkpoint. Experimental at the moment.
413#
414def create_ph_from_checkpoint(options):
415
416   # we need to load the reference model, as pickle doesn't save contents of .py files!
417   try:
418      reference_model_filename = os.path.expanduser(options.model_directory)+os.sep+"ReferenceModel.py"
419      if options.verbose is True:
420         print "Scenario reference model filename="+reference_model_filename
421      model_import = pyutilib.misc.import_file(reference_model_filename)
422      if "model" not in dir(model_import):
423         print "***ERROR: Exiting test driver: No 'model' object created in module "+reference_model_filename
424         return
425
426      if model_import.model is None:
427         print "***ERROR: Exiting test driver: 'model' object equals 'None' in module "+reference_model_filename
428         return None
429
430      reference_model = model_import.model
431   except IOError:
432      print "***ERROR: Failed to load scenario reference model from file="+reference_model_filename
433      return None
434
435   # import the saved state
436
437   try:
438      checkpoint_file = open(options.restore_from_checkpoint,"r")
439      ph = pickle.load(checkpoint_file)
440      checkpoint_file.close()
441
442   except IOError, msg:
443      raise RuntimeError, msg
444
445   # tell PH to build the right solver manager and solver TBD - AND PLUGINS, BUT LATER
446
447   raise RuntimeError, "Checkpoint restoration is not fully supported/tested yet!"
448
449   return ph
450
451#
452# Create a PH object from scratch.
453#
454
455def create_ph_from_scratch(options, reference_model, reference_instance, scenario_tree):
456
457   #
458   # print the input tree for validation/information purposes.
459   #
460   if options.verbose is True:
461      scenario_tree.pprint()
462
463   #
464   # validate the tree prior to doing anything serious
465   #
466   if scenario_tree.validate() is False:
467      print "***ERROR: Scenario tree is invalid****"
468      return None
469   else:
470      if options.verbose is True:
471         print "Scenario tree is valid!"
472
473   #
474   # if any of the ww extension configuration options are specified without the
475   # ww extension itself being enabled, halt and warn the user - this has led
476   # to confusion in the past, and will save user support time.
477   #
478   if len(options.ww_extension_cfgfile) > 0 and options.enable_ww_extensions is False:
479      print "***ERROR: A configuration file was specified for the WW extension module, but the WW extensions are not enabled!"
480      return None
481
482   if len(options.ww_extension_suffixfile) > 0 and options.enable_ww_extensions is False:
483      print "***ERROR: A suffix file was specified for the WW extension module, but the WW extensions are not enabled!"
484      return None
485
486   #
487   # if a breakpoint strategy is specified without linearization eanbled, halt and warn the user.
488   #
489   if (options.breakpoint_strategy > 0) and (options.linearize_nonbinary_penalty_terms == 0):
490      print "***ERROR: A breakpoint distribution strategy was specified, but linearization is not enabled!"
491      return None
492
493   #
494   # deal with any plugins. ww extension comes first currently, followed by an option user-defined plugin.
495   # order only matters if both are specified.
496   #
497   if options.enable_ww_extensions is True:
498
499      from coopr.pysp import wwphextension
500
501      plugin = ExtensionPoint(IPHExtension)
502      if len(options.ww_extension_cfgfile) > 0:
503         plugin.service()._configuration_filename = options.ww_extension_cfgfile
504      if len(options.ww_extension_suffixfile) > 0:
505         plugin.service()._suffix_filename = options.ww_extension_suffixfile
506
507   if options.user_defined_extension is not None:
508      print "Trying to import user-defined PH extension module="+options.user_defined_extension
509      # JPW removed the exception handling logic, as the module importer
510      # can raise a broad array of exceptions.
511      __import__(options.user_defined_extension)
512      print "Module successfully loaded"
513
514   #
515   # construct the convergence "computer" class.
516   #
517   converger = None
518   # go with the non-defaults first, and then with the default.
519   if options.enable_free_discrete_count_convergence is True:
520      converger = NumFixedDiscreteVarConvergence(convergence_threshold=options.free_discrete_count_threshold)
521   elif options.enable_normalized_termdiff_convergence is True:
522      converger = NormalizedTermDiffConvergence(convergence_threshold=options.termdiff_threshold)
523   else:
524      converger = TermDiffConvergence(convergence_threshold=options.termdiff_threshold)
525
526
527   #
528   # construct and initialize PH
529   #
530   ph = ProgressiveHedging(max_iterations=options.max_iterations, \
531                           rho=options.default_rho, \
532                           rho_setter=options.rho_cfgfile, \
533                           bounds_setter=options.bounds_cfgfile, \
534                           solver=options.solver_type, \
535                           solver_manager=options.solver_manager_type, \
536                           output_scenario_tree_solution=options.output_scenario_tree_solution, \
537                           scenario_solver_options=options.scenario_solver_options, \
538                           scenario_mipgap=options.scenario_mipgap, \
539                           keep_solver_files=options.keep_solver_files, \
540                           output_solver_log=options.output_solver_logs, \
541                           output_solver_results=options.output_solver_results, \
542                           verbose=options.verbose, \
543                           report_solutions=options.report_solutions, \
544                           report_weights=options.report_weights, \
545                           report_only_statistics=options.report_only_statistics, \
546                           output_times=options.output_times, \
547                           disable_warmstarts=options.disable_warmstarts,
548                           drop_proximal_terms=options.drop_proximal_terms,
549                           retain_quadratic_binary_terms=options.retain_quadratic_binary_terms, \
550                           linearize_nonbinary_penalty_terms=options.linearize_nonbinary_penalty_terms, \
551                           breakpoint_strategy=options.breakpoint_strategy, \
552                           checkpoint_interval=options.checkpoint_interval)
553
554   ph.initialize(scenario_data_directory_name=os.path.expanduser(options.instance_directory), \
555                 model=reference_model, \
556                 model_instance=reference_instance, \
557                 scenario_tree=scenario_tree, \
558                 converger=converger, \
559                 linearize=options.linearize_expressions)
560
561   if options.suppress_continuous_variable_output is True:
562      ph._output_continuous_variable_stats = False # clutters up the screen, when we really only care about the binaries.
563
564   return ph
565
566
567
568
569#
570# Given a PH object, execute it and optionally solve the EF at the end.
571#
572
573def run_ph(options, ph):
574
575   #
576   # at this point, we have an initialized PH object by some means.
577   #
578   start_time = time.time()
579
580   #
581   # kick off the solve
582   #
583   ph.solve()
584
585   end_time = time.time()
586
587   print ""
588   print "Total PH execution time=%8.2f seconds" %(end_time - start_time)
589   print ""
590   if options.output_times is True:
591      ph.print_time_stats()
592
593   solution_writer_plugins = ExtensionPoint(ISolutionWriterExtension)
594   for plugin in solution_writer_plugins:
595      plugin.write(ph._scenario_tree, "ph")
596
597   # store the binding instance, if created, in order to load
598   # the solution back into the scenario tree.
599   binding_instance = None
600
601   #
602   # write the extensive form, accounting (implicitly) for any fixed variables.
603   #
604   if (options.write_ef is True) or (options.solve_ef is True):
605      print ""
606      print "Writing EF for remainder problem"
607      print ""
608      binding_instance = create_and_write_ef(ph._scenario_tree, ph._instances, os.path.expanduser(options.ef_output_file))
609
610   #
611   # solve the extensive form and load the solution back into the PH scenario tree.
612   # contents from the PH solve will obviously be over-written!
613   #
614   if options.solve_ef is True:
615      print ""
616      print "Solving extensive form written to file="+os.path.expanduser(options.ef_output_file)
617      print ""
618
619      ef_solver = SolverFactory(options.solver_type)
620      if ef_solver is None:
621         raise ValueError, "Failed to create solver of type="+options.solver_type+" for use in extensive form solve"
622      if len(options.ef_solver_options) > 0:
623         print "Initializing ef solver with options="+str(options.ef_solver_options)
624         ef_solver.set_options("".join(options.ef_solver_options))
625      if options.ef_mipgap is not None:
626         if (options.ef_mipgap < 0.0) or (options.ef_mipgap > 1.0):
627            raise ValueError, "Value of the mipgap parameter for the EF solve must be on the unit interval; value specified=" + `options.ef_mipgap`
628         else:
629            ef_solver.mipgap = options.ef_mipgap
630
631      ef_solver_manager = SolverManagerFactory(options.solver_manager_type)
632      if ef_solver is None:
633         raise ValueError, "Failed to create solver manager of type="+options.solver_type+" for use in extensive form solve"
634
635      print "Queuing extensive form solve"
636      ef_action_handle = ef_solver_manager.queue(os.path.expanduser(options.ef_output_file), opt=ef_solver, tee=options.output_ef_solver_log)
637      print "Waiting for extensive form solve"
638      ef_results = ef_solver_manager.wait_for(ef_action_handle)
639
640      load_ef_solution(ef_results, binding_instance, ph._instances)
641      ph._scenario_tree.snapshotSolutionFromInstances(ph._instances)
642
643      print ""
644      print "Extensive form solution:"
645      ph._scenario_tree.pprintSolution()
646      print ""
647      print "Extensive form costs:"
648      ph._scenario_tree.pprintCosts(ph._instances)
649
650      solution_writer_plugins = ExtensionPoint(ISolutionWriterExtension)
651      for plugin in solution_writer_plugins:
652         plugin.write(ph._scenario_tree, "postphef")
653
654#
655# The main PH initialization / runner routine. Really only branches based on
656# the construction source - a checkpoint or from scratch.
657#
658
659def exec_ph(options):
660
661   ph = None
662
663   # validate the solution writer plugin exists, to avoid a lot of wasted work.
664   for solution_writer_name in options.solution_writer:
665      print "Trying to import solution writer="+solution_writer_name
666      __import__(solution_writer_name)
667      print "Module successfully loaded"
668
669   # if we are restoring from a checkpoint file, do so - otherwise, construct PH from scratch.
670   if len(options.restore_from_checkpoint) > 0:
671      ph = create_ph_from_checkpoint(options)
672
673   else:
674      reference_model, reference_instance, scenario_tree, scenario_tree_instance = load_reference_and_scenario_models(options)
675      if reference_model is None or reference_instance is None or scenario_tree is None:
676         return
677      ph = create_ph_from_scratch(options, reference_model, reference_instance, scenario_tree)
678
679   if ph is None:
680      print "***FAILED TO CREATE PH OBJECT"
681      return
682
683   run_ph(options, ph)
684
685#
686# the main driver routine for the runph script.
687#
688
689def run(args=None):
690
691    #
692    # Top-level command that executes the extensive form writer.
693    # This is segregated from run_ef_writer to enable profiling.
694    #
695
696    #
697    # Parse command-line options.
698    #
699    try:
700       ph_options_parser = construct_ph_options_parser("runph [options]")
701       (options, args) = ph_options_parser.parse_args(args=args)
702    except SystemExit:
703       # the parser throws a system exit if "-h" is specified - catch
704       # it to exit gracefully.
705       return
706    #
707    # Control the garbage collector - more critical than I would like at the moment.
708    #
709
710    if options.disable_gc is True:
711       gc.disable()
712    else:
713       gc.enable()
714
715    #
716    # Run PH - precise invocation depends on whether we want profiling output.
717    #
718
719    # if an exception is triggered and traceback is enabled, 'ans' won't
720    # have a value and the return statement from this function will flag
721    # an error, masking the stack trace that you really want to see.
722    ans = None       
723
724    if options.profile > 0:
725        #
726        # Call the main PH routine with profiling.
727        #
728        tfile = pyutilib.services.TempfileManager.create_tempfile(suffix=".profile")
729        tmp = profile.runctx('exec_ph(options)',globals(),locals(),tfile)
730        p = pstats.Stats(tfile).strip_dirs()
731        p.sort_stats('time', 'cum')
732        p = p.print_stats(options.profile)
733        p.print_callers(options.profile)
734        p.print_callees(options.profile)
735        p = p.sort_stats('cum','calls')
736        p.print_stats(options.profile)
737        p.print_callers(options.profile)
738        p.print_callees(options.profile)
739        p = p.sort_stats('calls')
740        p.print_stats(options.profile)
741        p.print_callers(options.profile)
742        p.print_callees(options.profile)
743        pyutilib.services.TempfileManager.clear_tempfiles()
744        ans = [tmp, None]
745    else:
746        #
747        # Call the main PH routine without profiling.
748        #
749
750        if options.traceback is True:
751           ans = exec_ph(options)
752        else:
753           try:
754              ans = exec_ph(options)
755           except ValueError, str:
756              print "VALUE ERROR:"
757              print str
758           except TypeError, str:
759              print "TYPE ERROR:"
760              print str
761           except NameError, str:
762              print "NAME ERROR:"
763              print str                           
764           except IOError, str:
765              print "IO ERROR:"
766              print str
767           except pyutilib.common.ApplicationError, str:
768              print "APPLICATION ERROR:"
769              print str
770           except RuntimeError, str:
771              print "RUN-TIME ERROR:"   
772              print str       
773           except:
774              print "Encountered unhandled exception"
775              traceback.print_exc()
776
777    gc.enable()
778
779    return ans
Note: See TracBrowser for help on using the repository browser.