files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Analyze YANK output file.
  
  """
  
  # =============================================================================================
  # MODULE IMPORTS
  # =============================================================================================
  
  # Module imports handled in individual functions since CLI should be faster to boot up
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  
  usage = """
  YANK analyze
  
  Usage: 
    yank analyze ((-s STORE | --store=STORE) | (-y YAML | --yaml=YAML)) [-e SERIAL | --serial=SERIAL] [--skipunbiasing] [--distcutoff=DISTANCE] [--energycutoff=ENERGY] [-v | --verbose] [--fulltraj]
    yank analyze report ((-s STORE | --store=STORE) | (-y YAML | --yaml=YAML)) (-o OUTPUT | --output=OUTPUT) [--format=FORMAT] [-e SERIAL | --serial=SERIAL] [--skipunbiasing] [--distcutoff=DISTANCE] [--energycutoff=ENERGY] [-v | --verbose] [--fulltraj]
    yank analyze extract-trajectory --netcdf=FILEPATH [--checkpoint=FILEPATH ] (--state=STATE | --replica=REPLICA) --trajectory=FILEPATH [--start=START_FRAME] [--skip=SKIP_FRAME] [--end=END_FRAME] [--nosolvent] [--discardequil] [--imagemol] [-v | --verbose]
  
  Description:
    Analyze the data to compute Free Energies OR extract the trajectory from the NetCDF file into a common format.
    yank analyze report generates a Jupyter (Ipython) notebook of the report instead of writing it to standard output
  
  Free Energy Required Arguments:
    -s STORE, --store=STORE       Storage directory for NetCDF data files. 
                                  EXCLUSIVE with -y and --yaml
    -y YAML, --yaml=YAML          Target YAML file which setup and ran the experiment(s) being analyzed. 
                                  This slightly changes the optional -o|--output flag.
                                  EXCLUSIVE with -s and --store
    
  YANK Analysis Output Arguments:
    -e SERIAL, --serial=SERIAL    Save data in Pickle serialized output. This behaves differently in report mode. 
                                  In normal mode, this is a SINGULAR output file in Pickle format
                                  In report mode, this is the base name of the individual serial files. If not provided, 
                                  then the name is inferred from the storage (-s) or the yaml (-y) file
    report                        Toggles output to be of the Jupyter Notebook analysis as a rendered notebook or as 
                                  a static file. Can use a path + name as well. File format is set by the --format flag
                                  
    -o=REPORT, --output=REPORT    Name of the health report Jupyter notebook or static file, can use a path + name as well
                                  If the filename ends in .pdf or .html, the notebook is auto run and converted to a
                                  static PDF or HTML file respectively
                                  PDF requires xelatex binary in OS path, often provided by LaTeX packages
                                  MODIFIED BY -y|--yaml: This becomes the DIRECTORY of the output. The names are inferred 
                                  from the input YAML file
    --format=FORMAT               File format of the notebook. If the filename ends in .pdf or .html, the notebook is run 
                                  and converted to a static PDF or HTML file respectively. If --format is NOT set, it 
                                  defaults to '.ipynb'                                
  
  Free Energy Optional Arguments:
    --skipunbiasing               Skip the radially-symmetric restraint unbiasing. This can be an expensive step.
                                  If this flag is not specified, and no cutoff is given, a distance cutoff is
                                  automatically determined as the 99.9-percentile of the restraint distance distribution
                                  in the bound state.
    --distcutoff=DISTANCE         The restraint distance cutoff (in angstroms) to be used to unbias the restraint.
                                  When the restraint is unbiased, the analyzer discards all the samples for which the
                                  distance between the restrained atoms is above this cutoff. Effectively, this is
                                  equivalent to placing a hard wall potential at a restraint distance "distcutoff".
    --energycutoff=ENERGY         The restraint unitless potential energy cutoff (i.e. in kT) to be used to unbias the
                                  restraint. When the restraint is unbiased, the analyzer discards all the samples for
                                  which the restrain potential energy (in kT) is above this cutoff. Effectively, this is
                                  equivalent to placing a hard wall potential at a restraint distance such that the
                                  restraint potential energy is equal to "energycutoff".
  
  Extract Trajectory Required Arguments:
    --netcdf=FILEPATH             Path to the NetCDF file.
    --checkpoint=FILEPATH         Path to the NetCDF checkpoint file if not the default name inferned from "netcdf" option
    --state=STATE_IDX             Index of the alchemical state for which to extract the trajectory
    --replica=REPLICA_IDX         Index of the replica for which to extract the trajectory
    --trajectory=FILEPATH         Path to the trajectory file to create (extension determines the format)
  
  Extract Trajectory Options:
    --start=START_FRAME           Index of the first frame to keep
    --end=END_FRAME               Index of the last frame to keep
    --skip=SKIP_FRAME             Extract one frame every SKIP_FRAME
    --nosolvent                   Do not extract solvent
    --discardequil                Detect and discard equilibration frames
    --imagemol                    Reprocess trajectory to enforce periodic boundary conditions to molecules positions
  
  General Options:
    -v, --verbose                 Print verbose output
    --fulltraj                    Force ALL analysis run from this command to rely on the full trajectory and not do any 
                                  automatic equilibration detection or decorrelation subsampling. Although the 
                                  equilibration and correlation times will still be computed, no calculation depending on 
                                  them will use this information.
  
  """
  
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  
  def dispatch(args):
  
      import os
      import pickle
      from .. import utils, analyze, mpi
  
      utils.config_root_logger(args['--verbose'])
  
      if args['report']:
          if not args['--format']:
              args['--format'] = '.ipynb'
          elif args['--format'][0] != '.':
              # Ensure format is not double dotted
              args['--format'] = '.' + args['--format']
          if args['--yaml'] is not None and args['--output']:
              # Ensure the last output is treated as a directory in all cases
              if not os.path.isdir(args['--output']):
                  raise ValueError("{} is not a directory, which is required when specifying a YAML file as a source for "
                                   "analysis".format(args['--output']))
              base, last_item = os.path.split(args['--output'])
              if last_item != '':
                  args['--output'] = os.path.join(base, last_item, '')
          return dispatch_report(args)
  
      if args['extract-trajectory']:
          return dispatch_extract_trajectory(args)
  
      # Configure analyzer keyword arguments.
      analyzer_kwargs = extract_analyzer_kwargs(args)
      do_serialize = True if args['--serial'] is not None else False
      if args['--yaml']:
          multi_analyzer = analyze.MultiExperimentAnalyzer(args['--yaml'])
          output = multi_analyzer.run_all_analysis(serial_data_path=args['--serial'], serialize_data=do_serialize,
                                                   **analyzer_kwargs)
          for exp_name, data in output.items():
              analyze.print_analysis_data(data, header="######## EXPERIMENT: {} ########".format(exp_name))
  
      else:
          @mpi.on_single_node(0)
          def single_run():
              # Helper to ensure case someone does MPI on a single diretory
              output = analyze.analyze_directory(args['--store'], **analyzer_kwargs)
              if do_serialize:
                  with open(args['--serial'], 'wb') as f:
                      pickle.dump(output, f)
                  print("Results have been serialized to {}".format(args['--serial']))
          single_run()
      return True
  
  
  def extract_analyzer_kwargs(args, quantities_as_strings=False):
  
      import simtk.unit as unit
  
      """Return a dictionary with the keyword arguments to pass to the analyzer."""
      analyzer_kwargs = {}
      if args['--skipunbiasing']:
          analyzer_kwargs['unbias_restraint'] = False
      if args['--energycutoff']:
          analyzer_kwargs['restraint_energy_cutoff'] = float(args['--energycutoff'])
      if args['--distcutoff']:
          if quantities_as_strings:
              distcutoff = args['--distcutoff'] + '*angstroms'
          else:
              distcutoff = float(args['--distcutoff']) * unit.angstroms
          analyzer_kwargs['restraint_distance_cutoff'] = distcutoff
      if args['--fulltraj']:
          analyzer_kwargs['use_full_trajectory'] = True
      return analyzer_kwargs
  
  
  def dispatch_extract_trajectory(args):
  
      import os
      from .. import analyze
  
      # Paths
      output_path = args['--trajectory']
      nc_path = args['--netcdf']
  
      # Get keyword arguments to pass to extract_trajectory()
      kwargs = {}
  
      if args['--state']:
          kwargs['state_index'] = int(args['--state'])
      else:
          kwargs['replica_index'] = int(args['--replica'])
  
      if args['--start']:
          kwargs['start_frame'] = int(args['--start'])
      if args['--skip']:
          kwargs['skip_frame'] = int(args['--skip'])
      if args['--end']:
          kwargs['end_frame'] = int(args['--end'])
  
      if args['--nosolvent']:
          kwargs['keep_solvent'] = False
      if args['--discardequil']:
          kwargs['discard_equilibration'] = True
      if args['--imagemol']:
          kwargs['image_molecules'] = True
      if args['--checkpoint']:
          kwargs["nc_checkpoint_file"] = args['--checkpoint']
  
      # Extract trajectory
      trajectory = analyze.extract_trajectory(nc_path, **kwargs)
  
      # Create output directory and save trajectory
      output_dir = os.path.dirname(output_path)
      if output_dir != '' and not os.path.isdir(output_dir):
          os.makedirs(output_dir)
      trajectory.save(output_path)
  
      return True
  
  
  def dispatch_report(args):
  
      import io
      import os
      import re
      import pkg_resources
      from .. import analyze
  
      # Check modules for render
      store = args['--store']
      yaml_input = args['--yaml']
      output = args['--output']
      do_serialize = True if args['--serial'] is not None else False
      analyzer_kwargs = extract_analyzer_kwargs(args, quantities_as_strings=True)
      file_extension = args['--format']
      # requires_prerendering = file_extension.lower() in {'.pdf', '.html', '.ipynb'}
  
      try:
          import seaborn
          import matplotlib
          import jupyter
      except ImportError:
          error_msg = ("Rendering this notebook requires the following packages:\n"
                       " - seaborn\n"
                       " - matplotlib\n"
                       " - jupyter\n"
                       "Rendering as {} is not possible without the packages!".format(file_extension))
          raise ImportError(error_msg)
  
      def run_notebook(source_path, output_file, serial_file, **analyzer_kwargs):
          template_path = pkg_resources.resource_filename('yank', 'reports/YANK_Health_Report_Template.ipynb')
          with open(template_path, 'r') as template:
              notebook_text = re.sub('STOREDIRBLANK', source_path, template.read())
              notebook_text = re.sub('ANALYZERKWARGSBLANK', str(analyzer_kwargs), notebook_text)
              if serial_file is not None:
                  # Uncomment the line. Traps '#' and the rest, reports only the rest
                  notebook_text = re.sub(r"(#)(report\.dump_serial_data\('SERIALOUTPUT'\))", r'\2', notebook_text)
                  notebook_text = re.sub('SERIALOUTPUT', serial_file, notebook_text)
  
          # Cast to static output
          print("Rendering notebook as a {} file...".format(file_extension))
          import nbformat
          from nbconvert.preprocessors import ExecutePreprocessor
          import nbconvert.exporters
          # Categorize exporters based on extension, requires exporter object and data type output
          # 'b' = byte types output, e.g. PDF
          # 't' = text based output, e.g. HTML or even raw notebook, human-readable-like
          exporters = {
              ".pdf": {'exporter': nbconvert.exporters.PDFExporter, 'write_type': 'b'},
              ".html": {'exporter': nbconvert.exporters.HTMLExporter, 'write_type': 't'},
              ".ipynb": {'exporter': nbconvert.exporters.NotebookExporter, 'write_type': 't'}
          }
  
          # Load the notebook through Jupyter.
          loaded_notebook = nbformat.read(io.StringIO(notebook_text), as_version=4)
          # Process the notebook.
          ep = ExecutePreprocessor(timeout=None)
          # Sometimes the default startup timeout exceed the default of 60 seconds.
          ep.startup_timeout = 180
          # Set the title name, does not appear in all exporters
          _, output_file_name = os.path.split(output_file)
          resource_data = {'metadata': {'name': 'YANK Simulation Report: {}'.format(output_file_name)}}
          print("Processing notebook now, this may take a while...")
          processed_notebook, resources = ep.preprocess(loaded_notebook, resource_data)
  
          # Retrieve exporter
          exporter_data = exporters[file_extension.lower()]
          # Determine exporter and data output type
          exporter = exporter_data['exporter']
          write_type = exporter_data['write_type']
          with open(output_file, 'w{}'.format(write_type)) as notebook:
              exported_notebook, _ = nbconvert.exporters.export(exporter, processed_notebook, resources=resources)
              notebook.write(exported_notebook)
  
      def cast_notebook_serial_path(relative_notebook_path):
          if args['--serial'] is None:
              serial_file = None
          else:
              serial_file = os.path.splitext(relative_notebook_path)[0] + '_' + args['--serial']
          return serial_file
  
      class NotebookMultiExperimentAnalyzer(analyze.MultiExperimentAnalyzer):
          """Custom Multi Experiment Analyzer for notebooks"""
  
          @staticmethod
          def _run_specific_analysis(path, **analyzer_kwargs):
              _, exp_name = os.path.split(path)
              single_output_file = os.path.join(output, exp_name + args['--format'])
              single_serial_file = cast_notebook_serial_path(single_output_file)
              run_notebook(path, single_output_file, single_serial_file, **analyzer_kwargs)
              return
  
          @staticmethod
          def _serialize(serial_path, payload):
              """The notebooks do not have a general serial dump"""
              pass
  
      if yaml_input is not None:
          multi_notebook = NotebookMultiExperimentAnalyzer(yaml_input)
          _ = multi_notebook.run_all_analysis(serialize_data=do_serialize,
                                              serial_data_path=args['--serial'],
                                              **analyzer_kwargs)
      else:
          notebook_serial_file = cast_notebook_serial_path(output)
          run_notebook(store, output, notebook_serial_file, **analyzer_kwargs)
  
      return True
  
files: Yank/commands/analyze.py
files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Analyze YANK output file.
  
  """
  
  # =============================================================================================
  # MODULE IMPORTS
  # =============================================================================================
  
  import io
  import re
  import os
  import pickle
  
  from simtk import unit
  
  import pkg_resources
  from .. import utils, analyze, mpi
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  
  usage = """
  YANK analyze
  
  Usage: 
    yank analyze ((-s STORE | --store=STORE) | (-y YAML | --yaml=YAML)) [-e SERIAL | --serial=SERIAL] [--skipunbiasing] [--distcutoff=DISTANCE] [--energycutoff=ENERGY] [-v | --verbose] [--fulltraj]
    yank analyze report ((-s STORE | --store=STORE) | (-y YAML | --yaml=YAML)) (-o OUTPUT | --output=OUTPUT) [--format=FORMAT] [-e SERIAL | --serial=SERIAL] [--skipunbiasing] [--distcutoff=DISTANCE] [--energycutoff=ENERGY] [-v | --verbose] [--fulltraj]
    yank analyze extract-trajectory --netcdf=FILEPATH [--checkpoint=FILEPATH ] (--state=STATE | --replica=REPLICA) --trajectory=FILEPATH [--start=START_FRAME] [--skip=SKIP_FRAME] [--end=END_FRAME] [--nosolvent] [--discardequil] [--imagemol] [-v | --verbose]
  
  Description:
    Analyze the data to compute Free Energies OR extract the trajectory from the NetCDF file into a common format.
    yank analyze report generates a Jupyter (Ipython) notebook of the report instead of writing it to standard output
  
  Free Energy Required Arguments:
    -s STORE, --store=STORE       Storage directory for NetCDF data files. 
                                  EXCLUSIVE with -y and --yaml
    -y YAML, --yaml=YAML          Target YAML file which setup and ran the experiment(s) being analyzed. 
                                  This slightly changes the optional -o|--output flag.
                                  EXCLUSIVE with -s and --store
    
  YANK Analysis Output Arguments:
    -e SERIAL, --serial=SERIAL    Save data in Pickle serialized output. This behaves differently in report mode. 
                                  In normal mode, this is a SINGULAR output file in Pickle format
                                  In report mode, this is the base name of the individual serial files. If not provided, 
                                  then the name is inferred from the storage (-s) or the yaml (-y) file
    report                        Toggles output to be of the Jupyter Notebook analysis as a rendered notebook or as 
                                  a static file. Can use a path + name as well. File format is set by the --format flag
                                  
    -o=REPORT, --output=REPORT    Name of the health report Jupyter notebook or static file, can use a path + name as well
                                  If the filename ends in .pdf or .html, the notebook is auto run and converted to a
                                  static PDF or HTML file respectively
                                  PDF requires xelatex binary in OS path, often provided by LaTeX packages
                                  MODIFIED BY -y|--yaml: This becomes the DIRECTORY of the output. The names are inferred 
                                  from the input YAML file
    --format=FORMAT               File format of the notebook. If the filename ends in .pdf or .html, the notebook is run 
                                  and converted to a static PDF or HTML file respectively. If --format is NOT set, it 
                                  defaults to '.ipynb'                                
  
  Free Energy Optional Arguments:
    --skipunbiasing               Skip the radially-symmetric restraint unbiasing. This can be an expensive step.
                                  If this flag is not specified, and no cutoff is given, a distance cutoff is
                                  automatically determined as the 99.9-percentile of the restraint distance distribution
                                  in the bound state.
    --distcutoff=DISTANCE         The restraint distance cutoff (in angstroms) to be used to unbias the restraint.
                                  When the restraint is unbiased, the analyzer discards all the samples for which the
                                  distance between the restrained atoms is above this cutoff. Effectively, this is
                                  equivalent to placing a hard wall potential at a restraint distance "distcutoff".
    --energycutoff=ENERGY         The restraint unitless potential energy cutoff (i.e. in kT) to be used to unbias the
                                  restraint. When the restraint is unbiased, the analyzer discards all the samples for
                                  which the restrain potential energy (in kT) is above this cutoff. Effectively, this is
                                  equivalent to placing a hard wall potential at a restraint distance such that the
                                  restraint potential energy is equal to "energycutoff".
  
  Extract Trajectory Required Arguments:
    --netcdf=FILEPATH             Path to the NetCDF file.
    --checkpoint=FILEPATH         Path to the NetCDF checkpoint file if not the default name inferned from "netcdf" option
    --state=STATE_IDX             Index of the alchemical state for which to extract the trajectory
    --replica=REPLICA_IDX         Index of the replica for which to extract the trajectory
    --trajectory=FILEPATH         Path to the trajectory file to create (extension determines the format)
  
  Extract Trajectory Options:
    --start=START_FRAME           Index of the first frame to keep
    --end=END_FRAME               Index of the last frame to keep
    --skip=SKIP_FRAME             Extract one frame every SKIP_FRAME
    --nosolvent                   Do not extract solvent
    --discardequil                Detect and discard equilibration frames
    --imagemol                    Reprocess trajectory to enforce periodic boundary conditions to molecules positions
  
  General Options:
    -v, --verbose                 Print verbose output
    --fulltraj                    Force ALL analysis run from this command to rely on the full trajectory and not do any 
                                  automatic equilibration detection or decorrelation subsampling. Although the 
                                  equilibration and correlation times will still be computed, no calculation depending on 
                                  them will use this information.
  
  """
  
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  
  def dispatch(args):
      utils.config_root_logger(args['--verbose'])
  
      if args['report']:
          if not args['--format']:
              args['--format'] = '.ipynb'
          elif args['--format'][0] != '.':
              # Ensure format is not double dotted
              args['--format'] = '.' + args['--format']
          if args['--yaml'] is not None and args['--output']:
              # Ensure the last output is treated as a directory in all cases
              if not os.path.isdir(args['--output']):
                  raise ValueError("{} is not a directory, which is required when specifying a YAML file as a source for "
                                   "analysis".format(args['--output']))
              base, last_item = os.path.split(args['--output'])
              if last_item != '':
                  args['--output'] = os.path.join(base, last_item, '')
          return dispatch_report(args)
  
      if args['extract-trajectory']:
          return dispatch_extract_trajectory(args)
  
      # Configure analyzer keyword arguments.
      analyzer_kwargs = extract_analyzer_kwargs(args)
      do_serialize = True if args['--serial'] is not None else False
      if args['--yaml']:
          multi_analyzer = analyze.MultiExperimentAnalyzer(args['--yaml'])
          output = multi_analyzer.run_all_analysis(serial_data_path=args['--serial'], serialize_data=do_serialize,
                                                   **analyzer_kwargs)
          for exp_name, data in output.items():
              analyze.print_analysis_data(data, header="######## EXPERIMENT: {} ########".format(exp_name))
  
      else:
          @mpi.on_single_node(0)
          def single_run():
              # Helper to ensure case someone does MPI on a single diretory
              output = analyze.analyze_directory(args['--store'], **analyzer_kwargs)
              if do_serialize:
                  with open(args['--serial'], 'wb') as f:
                      pickle.dump(output, f)
                  print("Results have been serialized to {}".format(args['--serial']))
          single_run()
      return True
  
  
  def extract_analyzer_kwargs(args, quantities_as_strings=False):
      """Return a dictionary with the keyword arguments to pass to the analyzer."""
      analyzer_kwargs = {}
      if args['--skipunbiasing']:
          analyzer_kwargs['unbias_restraint'] = False
      if args['--energycutoff']:
          analyzer_kwargs['restraint_energy_cutoff'] = float(args['--energycutoff'])
      if args['--distcutoff']:
          if quantities_as_strings:
              distcutoff = args['--distcutoff'] + '*angstroms'
          else:
              distcutoff = float(args['--distcutoff']) * unit.angstroms
          analyzer_kwargs['restraint_distance_cutoff'] = distcutoff
      if args['--fulltraj']:
          analyzer_kwargs['use_full_trajectory'] = True
      return analyzer_kwargs
  
  
  def dispatch_extract_trajectory(args):
      # Paths
      output_path = args['--trajectory']
      nc_path = args['--netcdf']
  
      # Get keyword arguments to pass to extract_trajectory()
      kwargs = {}
  
      if args['--state']:
          kwargs['state_index'] = int(args['--state'])
      else:
          kwargs['replica_index'] = int(args['--replica'])
  
      if args['--start']:
          kwargs['start_frame'] = int(args['--start'])
      if args['--skip']:
          kwargs['skip_frame'] = int(args['--skip'])
      if args['--end']:
          kwargs['end_frame'] = int(args['--end'])
  
      if args['--nosolvent']:
          kwargs['keep_solvent'] = False
      if args['--discardequil']:
          kwargs['discard_equilibration'] = True
      if args['--imagemol']:
          kwargs['image_molecules'] = True
      if args['--checkpoint']:
          kwargs["nc_checkpoint_file"] = args['--checkpoint']
  
      # Extract trajectory
      trajectory = analyze.extract_trajectory(nc_path, **kwargs)
  
      # Create output directory and save trajectory
      output_dir = os.path.dirname(output_path)
      if output_dir != '' and not os.path.isdir(output_dir):
          os.makedirs(output_dir)
      trajectory.save(output_path)
  
      return True
  
  
  def dispatch_report(args):
  
      # Check modules for render
      store = args['--store']
      yaml_input = args['--yaml']
      output = args['--output']
      do_serialize = True if args['--serial'] is not None else False
      analyzer_kwargs = extract_analyzer_kwargs(args, quantities_as_strings=True)
      file_extension = args['--format']
      # requires_prerendering = file_extension.lower() in {'.pdf', '.html', '.ipynb'}
  
      try:
          import seaborn
          import matplotlib
          import jupyter
      except ImportError:
          error_msg = ("Rendering this notebook requires the following packages:\n"
                       " - seaborn\n"
                       " - matplotlib\n"
                       " - jupyter\n"
                       "Rendering as {} is not possible without the packages!".format(file_extension))
          raise ImportError(error_msg)
  
      def run_notebook(source_path, output_file, serial_file, **analyzer_kwargs):
          template_path = pkg_resources.resource_filename('yank', 'reports/YANK_Health_Report_Template.ipynb')
          with open(template_path, 'r') as template:
              notebook_text = re.sub('STOREDIRBLANK', source_path, template.read())
              notebook_text = re.sub('ANALYZERKWARGSBLANK', str(analyzer_kwargs), notebook_text)
              if serial_file is not None:
                  # Uncomment the line. Traps '#' and the rest, reports only the rest
                  notebook_text = re.sub(r"(#)(report\.dump_serial_data\('SERIALOUTPUT'\))", r'\2', notebook_text)
                  notebook_text = re.sub('SERIALOUTPUT', serial_file, notebook_text)
  
          # Cast to static output
          print("Rendering notebook as a {} file...".format(file_extension))
          import nbformat
          from nbconvert.preprocessors import ExecutePreprocessor
          import nbconvert.exporters
          # Categorize exporters based on extension, requires exporter object and data type output
          # 'b' = byte types output, e.g. PDF
          # 't' = text based output, e.g. HTML or even raw notebook, human-readable-like
          exporters = {
              ".pdf": {'exporter': nbconvert.exporters.PDFExporter, 'write_type': 'b'},
              ".html": {'exporter': nbconvert.exporters.HTMLExporter, 'write_type': 't'},
              ".ipynb": {'exporter': nbconvert.exporters.NotebookExporter, 'write_type': 't'}
          }
  
          # Load the notebook through Jupyter.
          loaded_notebook = nbformat.read(io.StringIO(notebook_text), as_version=4)
          # Process the notebook.
          ep = ExecutePreprocessor(timeout=None)
          # Sometimes the default startup timeout exceed the default of 60 seconds.
          ep.startup_timeout = 180
          # Set the title name, does not appear in all exporters
          _, output_file_name = os.path.split(output_file)
          resource_data = {'metadata': {'name': 'YANK Simulation Report: {}'.format(output_file_name)}}
          print("Processing notebook now, this may take a while...")
          processed_notebook, resources = ep.preprocess(loaded_notebook, resource_data)
  
          # Retrieve exporter
          exporter_data = exporters[file_extension.lower()]
          # Determine exporter and data output type
          exporter = exporter_data['exporter']
          write_type = exporter_data['write_type']
          with open(output_file, 'w{}'.format(write_type)) as notebook:
              exported_notebook, _ = nbconvert.exporters.export(exporter, processed_notebook, resources=resources)
              notebook.write(exported_notebook)
  
      def cast_notebook_serial_path(relative_notebook_path):
          if args['--serial'] is None:
              serial_file = None
          else:
              serial_file = os.path.splitext(relative_notebook_path)[0] + '_' + args['--serial']
          return serial_file
  
      class NotebookMultiExperimentAnalyzer(analyze.MultiExperimentAnalyzer):
          """Custom Multi Experiment Analyzer for notebooks"""
  
          @staticmethod
          def _run_specific_analysis(path, **analyzer_kwargs):
              _, exp_name = os.path.split(path)
              single_output_file = os.path.join(output, exp_name + args['--format'])
              single_serial_file = cast_notebook_serial_path(single_output_file)
              run_notebook(path, single_output_file, single_serial_file, **analyzer_kwargs)
              return
  
          @staticmethod
          def _serialize(serial_path, payload):
              """The notebooks do not have a general serial dump"""
              pass
  
      if yaml_input is not None:
          multi_notebook = NotebookMultiExperimentAnalyzer(yaml_input)
          _ = multi_notebook.run_all_analysis(serialize_data=do_serialize,
                                              serial_data_path=args['--serial'],
                                              **analyzer_kwargs)
      else:
          notebook_serial_file = cast_notebook_serial_path(output)
          run_notebook(store, output, notebook_serial_file, **analyzer_kwargs)
  
      return True
  
files: Yank/commands/analyze.py
files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Set up and run YANK calculation from script.
  
  """
  
  # =============================================================================================
  # GLOBAL IMPORTS
  # =============================================================================================
  
  # Module imports handled in individual functions since CLI should be faster to boot up
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  
  usage = """
  YANK script
  
  Usage:
    yank script (-y FILEPATH | --yaml=FILEPATH) [--jobid=INTEGER] [--njobs=INTEGER] [-o OVERRIDE]...
  
  Description:
    Set up and run free energy calculations from a YAML script. All options can be specified in the YAML script.
  
  Required Arguments:
    -y, --yaml=FILEPATH           Path to the YAML script specifying options and/or how to set up and run the experiment.
  
  Optional Arguments:
    --jobid=INTEGER               You can run only a subset of the experiments by specifying jobid and njobs, where
                                  1 <= job_id <= n_jobs. In this case, njobs must be specified as well and YANK will
                                  run only 1/n_jobs of the experiments. This can be used to run several separate YANK
                                  executions in parallel starting from the same script.
                                  Alternatively, a zero-indexed environment variable name can be specified, in which
                                  case the jobid will be taken from this variable (0 <= environment variable < n_jobs).
    --njobs=INTEGER               Specify the total number of parallel executions. jobid has to be specified too.
                                  Alternatively, an environment variable name can be specified, in which
                                  case the number of jobs jobid will be taken from this variable.
    -o, --override=OVERRIDE       Override a single option in the script file. May be specified multiple times.
                                  Specified as a nested dictionary of the form:
                                  top_option:sub_option:value
                                  of any depth level where the last entry is always the value, do not use quotes.
                                  Please see script file documentation for valid options.
                                  Specifying the same option multiple times results in an error.
                                  This method is not recommended for complex options such as lists or combinations
  """
  
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  
  def dispatch(args):
      """
      Set up and run YANK calculation from a script.
  
      Parameters
      ----------
      args : dict
         Command-line arguments from docopt.
  
      """
  
      import os
      from ..experiment import ExperimentBuilder
  
      override = None
      if args['--override']:  # Is False for None and [] (empty list)
          over_opts = args['--override']
          # Check for duplicates
          if len(over_opts) != len(set(over_opts)):
              raise ValueError("There were duplicate override options, result will be ambiguous!")
          # Create a dict of strings
          override_dict = {}
          for opt in over_opts:
              split_opt = opt.split(':')
              try:
                  key, value = split_opt[-2:]
              except IndexError:
                  raise IndexError("Override option {} is not of the form top_dict:key:value. Minimal accepted case is "
                                   "key:value".format(opt))
              top_dict = override_dict
              for opt_level in split_opt[:-2]:
                  try:
                      # Dict already exists
                      top_dict[opt_level]
                  except KeyError:
                      top_dict[opt_level] = {}
                  finally:
                      top_dict = top_dict[opt_level]
              top_dict[key] = value
          # Create a string that looks like a dictionary, removing quotes
          # This is done to avoid input type ambiguity and instead let the parser handle it as though it were a file
          override = str(override_dict).replace("'", "").replace('"', '')
  
      if args['--jobid']:
          job_id = args['--jobid']
          try:
              # Try processing it as an integer
              job_id = int(job_id)
          except ValueError:
              if job_id in os.environ:
                  # Take the value of the environment variable
                  job_id = int(os.environ[job_id]) + 1
              else:
                  raise ValueError("--jobid '%s' unknown" % job_id)
      else:
          job_id = None
  
      if args['--njobs']:
          n_jobs = args['--njobs']
          try:
              # Try processing it as an integer
              n_jobs = int(n_jobs)
          except ValueError:
              if n_jobs in os.environ:
                  # Take the value of the environment variable
                  n_jobs = int(os.environ[n_jobs])
              else:
                  raise ValueError("--njobs '%s' unknown" % n_jobs)
      else:
          n_jobs = None
  
      if args['--yaml']:
          yaml_path = args['--yaml']
  
          if not os.path.isfile(yaml_path):
              raise ValueError('Cannot find YAML script "{}"'.format(yaml_path))
  
          yaml_builder = ExperimentBuilder(script=yaml_path, job_id=job_id, n_jobs=n_jobs)
          if override:  # Parse the string present.
              yaml_builder.update_yaml(override)
          yaml_builder.run_experiments()
          return True
  
      return False
  
files: Yank/commands/script.py
files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Set up and run YANK calculation from script.
  
  """
  
  # =============================================================================================
  # GLOBAL IMPORTS
  # =============================================================================================
  
  import os
  from ..experiment import ExperimentBuilder
  
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  
  usage = """
  YANK script
  
  Usage:
    yank script (-y FILEPATH | --yaml=FILEPATH) [--jobid=INTEGER] [--njobs=INTEGER] [-o OVERRIDE]...
  
  Description:
    Set up and run free energy calculations from a YAML script. All options can be specified in the YAML script.
  
  Required Arguments:
    -y, --yaml=FILEPATH           Path to the YAML script specifying options and/or how to set up and run the experiment.
  
  Optional Arguments:
    --jobid=INTEGER               You can run only a subset of the experiments by specifying jobid and njobs, where
                                  1 <= job_id <= n_jobs. In this case, njobs must be specified as well and YANK will
                                  run only 1/n_jobs of the experiments. This can be used to run several separate YANK
                                  executions in parallel starting from the same script.
                                  Alternatively, a zero-indexed environment variable name can be specified, in which
                                  case the jobid will be taken from this variable (0 <= environment variable < n_jobs).
    --njobs=INTEGER               Specify the total number of parallel executions. jobid has to be specified too.
                                  Alternatively, an environment variable name can be specified, in which
                                  case the number of jobs jobid will be taken from this variable.
    -o, --override=OVERRIDE       Override a single option in the script file. May be specified multiple times.
                                  Specified as a nested dictionary of the form:
                                  top_option:sub_option:value
                                  of any depth level where the last entry is always the value, do not use quotes.
                                  Please see script file documentation for valid options.
                                  Specifying the same option multiple times results in an error.
                                  This method is not recommended for complex options such as lists or combinations
  """
  
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  
  def dispatch(args):
      """
      Set up and run YANK calculation from a script.
  
      Parameters
      ----------
      args : dict
         Command-line arguments from docopt.
  
      """
      override = None
      if args['--override']:  # Is False for None and [] (empty list)
          over_opts = args['--override']
          # Check for duplicates
          if len(over_opts) != len(set(over_opts)):
              raise ValueError("There were duplicate override options, result will be ambiguous!")
          # Create a dict of strings
          override_dict = {}
          for opt in over_opts:
              split_opt = opt.split(':')
              try:
                  key, value = split_opt[-2:]
              except IndexError:
                  raise IndexError("Override option {} is not of the form top_dict:key:value. Minimal accepted case is "
                                   "key:value".format(opt))
              top_dict = override_dict
              for opt_level in split_opt[:-2]:
                  try:
                      # Dict already exists
                      top_dict[opt_level]
                  except KeyError:
                      top_dict[opt_level] = {}
                  finally:
                      top_dict = top_dict[opt_level]
              top_dict[key] = value
          # Create a string that looks like a dictionary, removing quotes
          # This is done to avoid input type ambiguity and instead let the parser handle it as though it were a file
          override = str(override_dict).replace("'", "").replace('"', '')
  
      if args['--jobid']:
          job_id = args['--jobid']
          try:
              # Try processing it as an integer
              job_id = int(job_id)
          except ValueError:
              if job_id in os.environ:
                  # Take the value of the environment variable
                  job_id = int(os.environ[job_id]) + 1
              else:
                  raise ValueError("--jobid '%s' unknown" % job_id)
      else:
          job_id = None
  
      if args['--njobs']:
          n_jobs = args['--njobs']
          try:
              # Try processing it as an integer
              n_jobs = int(n_jobs)
          except ValueError:
              if n_jobs in os.environ:
                  # Take the value of the environment variable
                  n_jobs = int(os.environ[n_jobs])
              else:
                  raise ValueError("--njobs '%s' unknown" % n_jobs)
      else:
          n_jobs = None
  
      if args['--yaml']:
          yaml_path = args['--yaml']
  
          if not os.path.isfile(yaml_path):
              raise ValueError('Cannot find YAML script "{}"'.format(yaml_path))
  
          yaml_builder = ExperimentBuilder(script=yaml_path, job_id=job_id, n_jobs=n_jobs)
          if override:  # Parse the string present.
              yaml_builder.update_yaml(override)
          yaml_builder.run_experiments()
          return True
  
      return False
  
files: Yank/commands/script.py
files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Run YANK self tests after installation.
  
  """
  
  # =============================================================================================
  # MODULE IMPORTS
  # =============================================================================================
  
  # Module imports handled in individual functions since CLI should be faster to boot up
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  usage = """
  YANK selftest
  
  Usage:
    yank selftest  [-d | --doctests] [-n | --nosetests] [--verbosity=#] [(--skip (platforms | openeye))]...
  
  Description:
    Run the YANK selftests to check that functions are behaving as expected and if external licenses are available
  
  General Options:
    -d, --doctests                Run module doctests
    -n, --nosetests               Run the nosetests (slow) on YANK
    --verbosity=#                 Optional verbosity level of nosetests OR verbose doctests [default: 1]
                                  Does nothing without -n or -d
    --skip                        Skips a named selftests (platforms OR openeye) for speed
                                  May be specified multiple times, once per TEST
  
  """
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  
  class LicenseError(Exception):
      """Error raised by a missing License."""
      pass
  
  
  def dispatch(args):
  
      import re
      import doctest
      import pkgutil
      import subprocess
      import simtk.openmm as mm
      from .. import version
      from . import platforms
  
      # Determine verbosity in advance
      # TODO: Figure out how to get -v back in to command and allow -vv and -vvv
      # nosetests: -v == --verbosity=2
      # Assuming that verbosity = 1 (or no -v) is no verbosity for doctests
      # verbosity = max(args['-v'] + 1, int(args['--verbosity']))
      verbosity = int(args['--verbosity'])
      # Header
      print("\n")
      print("YANK Selftest")
      print("-------------")
  
      # Yank Version
      print("Yank Version %s \n" % version.version)
  
      # OpenMM Platforms
      if not (args['platforms'] > 0):  # Don't need to check for --skip since invalid without argument
          platforms.dispatch(None)
          # Errors
          platform_errors = mm.Platform.getPluginLoadFailures()
          if len(platform_errors) > 0:  # This check only required to make header
              print("************************************************")
              print("\nWarning! There were OpenMM Platform Load Errors!")
              print("************************************************")
              for e in platform_errors:
                  print(e)
              print("************************************************")
              print("************************************************")
      else:
          print("Skipped OpenMM Platform Test")
  
      # Space out tests
      print("\n")
  
      # OpenEye checks
      if not (args['openeye'] > 0):
          try:
              import openeye
              import openeye.examples.openeye_tests as OETests
              print("OpenEye version {} Found! Checking install...".format(openeye.__version__))
              # OETests.run_test_suite([])  # Disabled for now as its slow
              # Check that the required tools work
              from ..utils import is_openeye_installed
              complete_tool_set = set('oechem', 'oequacpac', 'oeiupac', 'oeomega')
              unlicensed_tools = complete_tool_set.copy()
              for tool in complete_tool_set:
                  if is_openeye_installed(oetools=(tool,)):
                      unlicensed_tools.remove(tool)
              # If not the empty set, raise a unique error
              if unlicensed_tools != set():
                  raise LicenseError
          except LicenseError:
              if unlicensed_tools == complete_tool_set:
                  message = "No valid OpenEye licenses were found for the tools YANK uses, despite an OpenEye install " \
                            "being found.\nPlease check you have at least one " \
                            "of the following tools to use some of YANK's OpenEye features:\n    " \
                            "{}"
              else:
                  message = "OpenEye is missing licenses to some of the tools YANK can use.\n" \
                            "Some features of the OpenEye features YANK can use will be available, but not all.\n" \
                            "    Missing Licenses: {}"
              print(message.format(unlicensed_tools))
          except:
              # Broad except to trap any thing else that went wrong in the OpenEye test
              print("Valid OpenEye install not found")
              print("Not required, but please check install if you expected it")
      else:
          print("Skipped OpenEye Tests")
      print("\n")
  
      # NVIDIA-SMI calls
      print("Checking GPU Computed Mode (if present)...")
      try:
          nvidia_output = subprocess.check_output('nvidia-smi -q -d COMPUTE', shell=True)
      except subprocess.CalledProcessError as e:
          print("nvidia-smi had an issue, could not find CUDA cards, however this may be expected on your system.")
      else:
          n_cards = 0
          card_modes = []
          # Cast subprocess output to str
          try:
              # Try decoding byte string
              nvidia_output = nvidia_output.decode()
          except AttributeError:
              # Already a string
              pass
          finally:
              # Ensure its a string in case decode didn't work
              # Only a problem in case of odd encoding but all it should do is add odd character to the start of string
              # which is not really a problem since its not what we are searching for here
              nvidia_output = str(nvidia_output)
  
          split_nvidia_output = nvidia_output.split('\n')
          for line in split_nvidia_output:
              match = re.search('(?:Compute[^:]*:\s+)(\w+)', line)
              if match:
                  n_cards += 1
                  card_modes.append(match.group(1))
          if n_cards == 0:
              print("nvidia-smi returned 'Compute' search, but no cards matched query pattern.\n"
                    "Please run `nvidia-smi` yourself to confirm the Compute Mode is in shared/Default")
          else:
              print("Found {} NVIDIA GPUs in the following modes: [".format(n_cards) + ', '.join(card_modes) + "]\n"
                    "These should all be in shared/Default mode for YANK to use them")
  
      # Run nosetests
      # Note: These will not run during standard nosetests because they must be explicitly called
      # i.e. no infinite nosetests loop
      if args['--nosetests']:
          # Clear some lines
          print("\n")
          # Alert User
          print("******************************************")
          print("Nosetests invoked! This will take a while!")
          print("******************************************")
          import nose
          try:  # Check for timer install
              result = nose.run(argv=['yank', '--nocapture', '--verbosity={}'.format(verbosity),
                                      '--with-timer', '-a', '!slow'])
          except:
              result = nose.run(argv=['yank', '--nocapture', '--verbosity={}'.format(verbosity), '-a', '!slow'])
          print("\n")
  
      # Doctests
      if args['--doctests']:
          # Alert User
          print("*****************************************")
          print("Doctests invoked! This will take a while!")
          print("*****************************************")
          # Run tests on main module.
          import yank  # NOT "from .. import yank" since we want to run on the whole module
          if verbosity > 1:
              verbose = True
          else:
              verbose = False
          (failure_count, test_count) = doctest.testmod(yank, verbose=verbose)
          # Run tests on all submodules.
          package = yank
          prefix = package.__name__ + "."
          for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
              module = __import__(modname, fromlist="dummy")
              (module_failure_count, module_test_count) = doctest.testmod(module, verbose=verbose)
              failure_count += module_failure_count
              test_count += module_test_count
  
          # Report results.
          if failure_count == 0:
              print("All doctests pass.")
          else:
              print("WARNING: There were {} doctest failures.".format(failure_count))
          print("\n")
  
      # Helpful end test
      print("YANK Selftest complete.\nThank you for using YANK!\n")
      return True
  
files: Yank/commands/selftest.py
files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Run YANK self tests after installation.
  
  """
  
  # =============================================================================================
  # MODULE IMPORTS
  # =============================================================================================
  
  import doctest
  import pkgutil
  import subprocess
  import re
  
  from .. import version
  from . import platforms
  import simtk.openmm as mm
  
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  usage = """
  YANK selftest
  
  Usage:
    yank selftest  [-d | --doctests] [-n | --nosetests] [--verbosity=#] [(--skip (platforms | openeye))]...
  
  Description:
    Run the YANK selftests to check that functions are behaving as expected and if external licenses are available
  
  General Options:
    -d, --doctests                Run module doctests
    -n, --nosetests               Run the nosetests (slow) on YANK
    --verbosity=#                 Optional verbosity level of nosetests OR verbose doctests [default: 1]
                                  Does nothing without -n or -d
    --skip                        Skips a named selftests (platforms OR openeye) for speed
                                  May be specified multiple times, once per TEST
  
  """
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  class LicenseError(Exception):
      """Error raised by a missing License."""
      pass
  
  
  def dispatch(args):
  
      # Determine verbosity in advance
      # TODO: Figure out how to get -v back in to command and allow -vv and -vvv
      # nosetests: -v == --verbosity=2
      # Assuming that verbosity = 1 (or no -v) is no verbosity for doctests
      # verbosity = max(args['-v'] + 1, int(args['--verbosity']))
      verbosity = int(args['--verbosity'])
      # Header
      print("\n")
      print("YANK Selftest")
      print("-------------")
  
      # Yank Version
      print("Yank Version %s \n" % version.version)
  
      # OpenMM Platforms
      if not (args['platforms'] > 0):  # Don't need to check for --skip since invalid without argument
          platforms.dispatch(None)
          # Errors
          platform_errors = mm.Platform.getPluginLoadFailures()
          if len(platform_errors) > 0:  # This check only required to make header
              print("************************************************")
              print("\nWarning! There were OpenMM Platform Load Errors!")
              print("************************************************")
              for e in platform_errors:
                  print(e)
              print("************************************************")
              print("************************************************")
      else:
          print("Skipped OpenMM Platform Test")
  
      # Space out tests
      print("\n")
  
      # OpenEye checks
      if not (args['openeye'] > 0):
          try:
              import openeye
              import openeye.examples.openeye_tests as OETests
              print("OpenEye version {} Found! Checking install...".format(openeye.__version__))
              # OETests.run_test_suite([])  # Disabled for now as its slow
              # Check that the required tools work
              from ..utils import is_openeye_installed
              complete_tool_set = set('oechem', 'oequacpac', 'oeiupac', 'oeomega')
              unlicensed_tools = complete_tool_set.copy()
              for tool in complete_tool_set:
                  if is_openeye_installed(oetools=(tool,)):
                      unlicensed_tools.remove(tool)
              # If not the empty set, raise a unique error
              if unlicensed_tools != set():
                  raise LicenseError
          except LicenseError:
              if unlicensed_tools == complete_tool_set:
                  message = "No valid OpenEye licenses were found for the tools YANK uses, despite an OpenEye install " \
                            "being found.\nPlease check you have at least one " \
                            "of the following tools to use some of YANK's OpenEye features:\n    " \
                            "{}"
              else:
                  message = "OpenEye is missing licenses to some of the tools YANK can use.\n" \
                            "Some features of the OpenEye features YANK can use will be available, but not all.\n" \
                            "    Missing Licenses: {}"
              print(message.format(unlicensed_tools))
          except:
              # Broad except to trap any thing else that went wrong in the OpenEye test
              print("Valid OpenEye install not found")
              print("Not required, but please check install if you expected it")
      else:
          print("Skipped OpenEye Tests")
      print("\n")
  
      # NVIDIA-SMI calls
      print("Checking GPU Computed Mode (if present)...")
      try:
          nvidia_output = subprocess.check_output('nvidia-smi -q -d COMPUTE', shell=True)
      except subprocess.CalledProcessError as e:
          print("nvidia-smi had an issue, could not find CUDA cards, however this may be expected on your system.")
      else:
          n_cards = 0
          card_modes = []
          # Cast subprocess output to str
          try:
              # Try decoding byte string
              nvidia_output = nvidia_output.decode()
          except AttributeError:
              # Already a string
              pass
          finally:
              # Ensure its a string in case decode didn't work
              # Only a problem in case of odd encoding but all it should do is add odd character to the start of string
              # which is not really a problem since its not what we are searching for here
              nvidia_output = str(nvidia_output)
  
          split_nvidia_output = nvidia_output.split('\n')
          for line in split_nvidia_output:
              match = re.search('(?:Compute[^:]*:\s+)(\w+)', line)
              if match:
                  n_cards += 1
                  card_modes.append(match.group(1))
          if n_cards == 0:
              print("nvidia-smi returned 'Compute' search, but no cards matched query pattern.\n"
                    "Please run `nvidia-smi` yourself to confirm the Compute Mode is in shared/Default")
          else:
              print("Found {} NVIDIA GPUs in the following modes: [".format(n_cards) + ', '.join(card_modes) + "]\n"
                    "These should all be in shared/Default mode for YANK to use them")
  
      # Run nosetests
      # Note: These will not run during standard nosetests because they must be explicitly called
      # i.e. no infinite nosetests loop
      if args['--nosetests']:
          # Clear some lines
          print("\n")
          # Alert User
          print("******************************************")
          print("Nosetests invoked! This will take a while!")
          print("******************************************")
          import nose
          try:  # Check for timer install
              result = nose.run(argv=['yank', '--nocapture', '--verbosity={}'.format(verbosity),
                                      '--with-timer', '-a', '!slow'])
          except:
              result = nose.run(argv=['yank', '--nocapture', '--verbosity={}'.format(verbosity), '-a', '!slow'])
          print("\n")
  
      # Doctests
      if args['--doctests']:
          # Alert User
          print("*****************************************")
          print("Doctests invoked! This will take a while!")
          print("*****************************************")
          # Run tests on main module.
          import yank  # NOT "from .. import yank" since we want to run on the whole module
          if verbosity > 1:
              verbose = True
          else:
              verbose = False
          (failure_count, test_count) = doctest.testmod(yank, verbose=verbose)
          # Run tests on all submodules.
          package = yank
          prefix = package.__name__ + "."
          for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
              module = __import__(modname, fromlist="dummy")
              (module_failure_count, module_test_count) = doctest.testmod(module, verbose=verbose)
              failure_count += module_failure_count
              test_count += module_test_count
  
          # Report results.
          if failure_count == 0:
              print("All doctests pass.")
          else:
              print("WARNING: There were {} doctest failures.".format(failure_count))
          print("\n")
  
      # Helpful end test
      print("YANK Selftest complete.\nThank you for using YANK!\n")
      return True
  
files: Yank/commands/selftest.py
files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Query output files for quick status.
  
  """
  
  # =============================================================================================
  # MODULE IMPORTS
  # =============================================================================================
  
  # Module imports handled in individual functions since CLI should be faster to boot up
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  
  usage = """
  YANK status
  
  Usage:
    yank status (-y FILEPATH | --yaml=FILEPATH) [--status=STRING] [--njobs=INTEGER] [-v | --verbose]
  
  Description:
    Print the current status of the experiments.
  
  Required Arguments:
    -y, --yaml=FILEPATH           Path to the YAML script specifying options and/or how to
                                  set up and run the experiment.
  
  General Options:
    --status=STRING               Print only the jobs in a particular status. Accepted values
                                  are "completed", "ongoing", and "pending". This works only
                                  if verbose is set.
    --njobs=INTEGER               Print the job id associated to each experiment assuming
                                  njobs to be the one specified here.
    -v, --verbose                 Print status of each experiment individually. If this is
                                  not set, only a summary of the status of all experiments
                                  is printed.
  
  """
  
  
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  def find_contiguous_ids(job_ids):
      """Return the contiguous job ids in the given list.
  
      Returns
      -------
      contiguous_job_ids : str
          The job ids organized in contiguous sets.
  
      """
  
      import operator
      import itertools
  
      contiguous_job_ids = []
      for k, g in itertools.groupby(enumerate(job_ids), lambda x: x[0]-x[1]):
          group = list(map(operator.itemgetter(1), g))
          if len(group) == 1:
              contiguous_job_ids.append(str(group[0]))
          else:
              contiguous_job_ids.append('{}-{}'.format(group[0], group[-1]))
      return ','.join(contiguous_job_ids)
  
  
  def dispatch(args):
  
      import collections
      from .. import experiment
  
      # Handle optional arguments.
      if args['--njobs']:
          n_jobs = int(args['--njobs'])
      else:
          n_jobs = None
  
      # Turn off verbosity.
      exp_builder = experiment.ExperimentBuilder(args['--yaml'], n_jobs=n_jobs)
      exp_builder.verbose = False
  
      # Count all experiment status.
      job_ids_by_status = {
          'completed': [],
          'ongoing': [],
          'pending': []
      }
      counter = collections.Counter()
      for exp_status in exp_builder.status():
          counter[exp_status.status] += 1
  
          # Store job id.
          job_ids_by_status[exp_status.status].append(exp_status.job_id)
  
          # Print experiment and phases details.
          if args['--verbose']:
              # Filter by status if requested.
              if args['--status'] and args['--status'] != exp_status.status:
                  continue
  
              # Print experiment information.
              exp_description = '{}: status={}'.format(exp_status.name,
                                                       exp_status.status)
              if n_jobs is not None:
                  exp_description += ', job_id={}'.format(exp_status.job_id)
              print(exp_description)
  
              # Print phases information.
              for phase_name, phase_status in exp_status.phases.items():
                  if phase_status.iteration is not None:
                      iteration = ', iteration={}/{}'.format(phase_status.iteration,
                                                             exp_status.number_of_iterations)
                  else:
                      iteration = ''
                  print('\t{}: status={}{}'.format(phase_name, phase_status.status, iteration))
  
      # Print summary.
      tot_n_experiments = sum(count for count in counter.values())
      summary_msg = ', '.join('{} {}'.format(count, status) for status, count in counter.items())
      print('Total number of experiments: {} ({})'.format(tot_n_experiments, summary_msg))
  
      if n_jobs is not None:
          for status, job_ids in job_ids_by_status.items():
              print('{} job ids: {}'.format(status, find_contiguous_ids(job_ids)))
  
      return True
  
files: Yank/commands/status.py
files: #!/usr/local/bin/env python
  
  # =============================================================================================
  # MODULE DOCSTRING
  # =============================================================================================
  
  """
  Query output files for quick status.
  
  """
  
  # =============================================================================================
  # MODULE IMPORTS
  # =============================================================================================
  
  import operator
  import itertools
  import collections
  
  from .. import experiment
  
  # =============================================================================================
  # COMMAND-LINE INTERFACE
  # =============================================================================================
  
  usage = """
  YANK status
  
  Usage:
    yank status (-y FILEPATH | --yaml=FILEPATH) [--status=STRING] [--njobs=INTEGER] [-v | --verbose]
  
  Description:
    Print the current status of the experiments.
  
  Required Arguments:
    -y, --yaml=FILEPATH           Path to the YAML script specifying options and/or how to
                                  set up and run the experiment.
  
  General Options:
    --status=STRING               Print only the jobs in a particular status. Accepted values
                                  are "completed", "ongoing", and "pending". This works only
                                  if verbose is set.
    --njobs=INTEGER               Print the job id associated to each experiment assuming
                                  njobs to be the one specified here.
    -v, --verbose                 Print status of each experiment individually. If this is
                                  not set, only a summary of the status of all experiments
                                  is printed.
  
  """
  
  
  # =============================================================================================
  # COMMAND DISPATCH
  # =============================================================================================
  
  def find_contiguous_ids(job_ids):
      """Return the contiguous job ids in the given list.
  
      Returns
      -------
      contiguous_job_ids : str
          The job ids organized in contiguous sets.
  
      """
      contiguous_job_ids = []
      for k, g in itertools.groupby(enumerate(job_ids), lambda x:x[0]-x[1]):
          group = list(map(operator.itemgetter(1), g))
          if len(group) == 1:
              contiguous_job_ids.append(str(group[0]))
          else:
              contiguous_job_ids.append('{}-{}'.format(group[0], group[-1]))
      return ','.join(contiguous_job_ids)
  
  
  def dispatch(args):
      # Handle optional arguments.
      if args['--njobs']:
          n_jobs = int(args['--njobs'])
      else:
          n_jobs = None
  
      # Turn off verbosity.
      exp_builder = experiment.ExperimentBuilder(args['--yaml'], n_jobs=n_jobs)
      exp_builder.verbose = False
  
      # Count all experiment status.
      job_ids_by_status = {
          'completed': [],
          'ongoing': [],
          'pending': []
      }
      counter = collections.Counter()
      for exp_status in exp_builder.status():
          counter[exp_status.status] += 1
  
          # Store job id.
          job_ids_by_status[exp_status.status].append(exp_status.job_id)
  
          # Print experiment and phases details.
          if args['--verbose']:
              # Filter by status if requested.
              if args['--status'] and args['--status'] != exp_status.status:
                  continue
  
              # Print experiment information.
              exp_description = '{}: status={}'.format(exp_status.name,
                                                       exp_status.status)
              if n_jobs is not None:
                  exp_description += ', job_id={}'.format(exp_status.job_id)
              print(exp_description)
  
              # Print phases information.
              for phase_name, phase_status in exp_status.phases.items():
                  if phase_status.iteration is not None:
                      iteration = ', iteration={}/{}'.format(phase_status.iteration,
                                                             exp_status.number_of_iterations)
                  else:
                      iteration = ''
                  print('\t{}: status={}{}'.format(phase_name, phase_status.status, iteration))
  
      # Print summary.
      tot_n_experiments = sum(count for count in counter.values())
      summary_msg = ', '.join('{} {}'.format(count, status) for status, count in counter.items())
      print('Total number of experiments: {} ({})'.format(tot_n_experiments, summary_msg))
  
      if n_jobs is not None:
          for status, job_ids in job_ids_by_status.items():
              print('{} job ids: {}'.format(status, find_contiguous_ids(job_ids)))
  
      return True
  
files: Yank/commands/status.py