Source code for schrodinger.application.mopac.mopac_startup
"""
Startup driver for running semiempirical calculations (i.e. MOPAC)
"""
# Contributors: Mark A. Watson
import os
import re
from schrodinger.application.mopac import mopac_backend
from schrodinger.application.mopac import mopac_parser
from schrodinger.application.mopac import utils
from schrodinger.application.mopac.exceptions import MopacIOError
from schrodinger.infra import mm
from schrodinger.job import jobcontrol
from schrodinger.job import launchapi
from schrodinger.job import launchparams
_external_re = re.compile(r"EXTERNAL\s*=\s*(\S+)", re.I)
def _get_EXTERNAL_files(infiles, keywords):
"""
Look for EXTERNAL specification in MOPAC input files and cmdline flags to
indicate files which need to be registered as additional input files.
:type infiles: list
:param infiles: known input file paths
:type keywords: str
:param keywords: string of additional MOPAC keywords from cmdline
:return: set of input file names
"""
def add_file(f, efiles):
if not os.path.isfile(f):
raise RuntimeError(f"EXTERNAL file '{f}' not found.")
efiles.add(f)
efiles = set()
# Handle EXTERNAL specification in .mop files
for infile in infiles:
if utils.is_mopac_file(infile):
with open(infile, 'r') as fh:
last_line_continued = True
for ix, line in enumerate(fh):
match = _external_re.search(line)
if match:
add_file(match.group(1), efiles)
if ix > 0 and not last_line_continued:
break
last_line_continued = re.search("[&+]", line)
# Handle EXTERNAL=filename from cmdline flags
if keywords:
match = _external_re.search(keywords)
if match:
add_file(match.group(1), efiles)
return efiles
def _get_input_files(parsed_args):
"""
Return set of input files that need to be registered with jobcontrol
and/or copied to the work directory.
:type parsed_args: ParsedArgs object
:param parsed_args: parsed cmdline arguments
:return: set of input file names
"""
infiles = set(parsed_args.infiles)
for infile in infiles:
if not os.path.isfile(infile):
msg = f'Cannot find input file {infile} in {os.getcwd()}'
raise MopacIOError(msg)
# Include infiles obtained from the EXTERNAL specification
infiles.update(_get_EXTERNAL_files(infiles, parsed_args.keywords))
return infiles
def _get_output_files(parsed_args):
"""
Return set of output files expected from this workflow.
Currently all output files are registered on-the-fly in the
backend scripts. Hopefully this can be improved in the future
to make the API more clearly defined.
:type parsed_args: ParsedArgs object
:param parsed_args: parsed cmdline arguments
:return: set of output file names
"""
outfiles = set()
return outfiles
def _register_input_files(jsb, infiles):
"""
Register input files with job specification.
:type jsb: `schrodinger.job.launchapi.JobSpecificationArgsBuilder`
:param jsb: jobcontrol job specification builder instance
:type infiles: set of strings
:param infiles: input file names
"""
for infile in infiles:
if os.path.exists(infile):
jsb.setInputFile(infile)
else:
msg = f'Failed to find expected file {infile}'
raise MopacIOError(msg)
def _register_output_files(jsb, outfiles):
"""
Register output files with job specification.
:type jsb: `schrodinger.job.launchapi.JobSpecificationArgsBuilder`
:param jsb: jobcontrol job specification builder instance
:type outfiles: list of strings
:param outfiles: output file names
"""
for outfile in outfiles:
jsb.setOutputFile(outfile, stream=True)
def _get_job_params():
"""
Return the jobcontrol launch parameters
:type parsed_args: ParsedArgs object
:param parsed_args: parsed cmdline arguments
"""
params = launchparams.LaunchParameters()
# Set jobcontrol parallel resources
params.setNumberOfProcessorsManyNodes(1) # no MPI
params.setNumberOfProcessorsOneNode(1) # launch 1-CPU driver
return params
def _get_job_spec(cmdline, infiles, outfiles, jobname):
"""
Return the jobcontrol job specification
:type cmdline: str
:param cmdline: cmdline to launch under jobcontrol
:type infiles: list
:param infiles: list of input files to register
:type outfiles: list
:param outfiles: list of (known) output files to register
:type jobname: str
:param jobname: name of job
:return `schrodinger.job.launchapi.JobSpecification`
"""
# Create job_spec_builder instance
jsb = launchapi.JobSpecificationArgsBuilder(cmdline,
use_schrodinger_run=True,
use_jobname_log=True)
# Set jobname and .log filename
jsb.setJobname(jobname)
# Set Program name so Maestro knows how to incorporate
jsb.setProgramName("SemiEmp")
# Register input and output files
_register_input_files(jsb, infiles)
_register_output_files(jsb, outfiles)
return jsb.getJobSpec()
def _run_backend(args):
"""
Launch backend process under jobcontrol
:type args: list
:param args: cmdline arguments
"""
status = 0
parsed_args = mopac_parser.parse_args(args)
print("Launching MOPAC under jobcontrol.")
# Get jobcontrol jobspec and launch parameters
cmdline = ['semi_emp_backend.py'] + args
infiles = _get_input_files(parsed_args)
outfiles = _get_output_files(parsed_args)
job_spec = _get_job_spec(cmdline, infiles, outfiles, parsed_args.jobname)
# Get jobcontrol launch parameters
job_params = _get_job_params()
# Launch jobcontrol job
job = jobcontrol.launch_from_job_spec(job_spec, job_params)
print("Release:", mm.mmfile_get_release_name())
print("Exec:", os.getenv('MMSHARE_EXEC'))
print(f"JobId: {job.JobId}")
if parsed_args.wait:
# Barrier until jobcontrol job is finished
job.wait()
return status
[docs]def main(args):
"""
Startup driver for running MOPAC
:type args: list
:param args: cmdline arguments
"""
parsed_args = mopac_parser.parse_args(args)
if parsed_args.nojobid:
# Run script directly
status = mopac_backend.main(args)
else:
# Run script under jobcontrol
status = _run_backend(args)
return status