def submit(self, script, cwd=None): """Submit a job to the queue.x | Args: | script (str): content of the submission script | cwd (Optional[str]): path to the desired working directory | | Returns: | job_id (str): the job ID assigned by the queue system and parsed | with sub_outre """ if self._rTarg is None: subproc = sp.Popen(self.sub_cmd.split(), stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE, cwd=cwd) stdout, stderr = safe_communicate(subproc, script) else: with self._rTarg.context as rTarg: stdout, stderr = rTarg.run_cmd(self.sub_cmd, cwd=cwd, stdin=script) # Parse out the job id! match = self.sub_outre.search(stdout) if match is None: raise RuntimeError('Submission of job has failed with output:\n' '\tSTDOUT: {0}\n\tSTDERR: {1}'.format( stdout, stderr)) else: return match.groupdict()['job_id']
def list(): list_proc = sp.Popen(['ps', 'aux'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = safe_communicate(list_proc) # Parse stdout all_subms = [] for l in stdout.split('\n'): if 'soprano.hpc.submitter._spawn' in l: lspl = l.split() # File, name, time, PID subm = lspl[-3], lspl[-2], lspl[-7], int(lspl[1]) all_subms.append(subm) return all_subms
def kill(self, job_id): """Kill the job with the given ID | Args: | job_id (str): ID of the job to kill | """ if self._rTarg is None: subproc = sp.Popen(self.kill_cmd.split() + [job_id], stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = safe_communicate(subproc) else: with self._rTarg.context as rTarg: stdout, stderr = rTarg.run_cmd(self.kill_cmd + ' {0}'.format(job_id))
def setup_job(self, name, args, folder): """Copy files to temporary folder to prepare for execution""" for f in args['files']: shutil.move(f, folder) # Also copy the pseudopotentials for pp in self.pspots: shutil.copy(pp, folder) success = True self.log('Copying files for job {0}\n'.format(name)) # Perform dryrun test if required if self.drun: self.log('Performing DRYRUN\n') dry_proc = sp.Popen([self.castep_command, name, '--dryrun'], cwd=folder, stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = safe_communicate(dry_proc) # When it's finished... try: castfile = open(os.path.join(folder, name + '.castep')) except IOError: return False # Does the file contain the required lines? drline1 = "| DRYRUN finished ... |" drline2 = "| No problems found with input files. |" castlines = castfile.readlines() success = False for i, l in enumerate(castlines): if drline1 in l: if drline2 in castlines[i+1]: success = True break # Ok, now remove the CASTEP file castfile.close() os.remove(castfile.name) return success
def list(self, user='******'): """List all jobs found in the queue | Returns: | jobs (dict): a dict of jobs classified by ID containing all info | that can be matched through list_outre | user (Optional[str]): user for whom jobs should be listed. Will | not have any effect if list_user_opt has not | been specified. Default is $USER. | """ cmd = self.list_cmd if self.list_user_opt is not None: cmd += ' {0} {1}'.format(self.list_user_opt, user) if self._rTarg is None: subproc = sp.Popen(cmd.split(), stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = safe_communicate(subproc) else: with self._rTarg.context as rTarg: stdout, stderr = rTarg.run_cmd(cmd) # Parse out everything! jobs = {} for line in stdout.split('\n'): match = self.list_outre.search(line) if match is None: continue else: jobdict = match.groupdict() jobs[jobdict['job_id']] = jobdict return jobs
def airssGen(input_file, n=100, buildcell_command='buildcell', buildcell_path=None, clone_calc=True): """Generator function binding to AIRSS' Buildcell. This function searches for a buildcell executable and uses it to generate multiple new Atoms structures for a collection. | Args: | input_file (str or file): the .cell file with appropriate comments | specifying the details of buildcell's | construction work. | n (int): number of structures to generate. If set to None the | generator goes on indefinitely. | buildcell_command (str): command required to call the buildcell | executable. | buildcell_path (str): path where the buildcell executable can be | found. If not present, the buildcell command | will be invoked directly (assuming the | executable is in the system PATH). | clone_calc (bool): if True, the CASTEP calculator in the input file | will be copied and attached to the new structures. | This means that for example any additional CASTEP | keywords/blocks in the input file will be carried | on to the new structures. Default is True. | Returns: | airssGenerator (generator): an iterable object that yields structures | created by buildcell. """ # First: check that AIRSS is even installed if buildcell_path is None: buildcell_path = '' airss_cmd = [os.path.join(buildcell_path, buildcell_command)] try: stdout, stderr = sp.Popen(airss_cmd + ['-h'], stdout=sp.PIPE, stderr=sp.PIPE).communicate() except OSError: # Not even installed! raise RuntimeError('No instance of Buildcell found on this system') # Now open the given input file try: input_file = open(input_file) # If it's a string except TypeError: pass # If it's already a file template = input_file.read() # Now get the file name basename = seedname(input_file.name) input_file.close() # Calculator (if needed) calc = None if clone_calc: calc = ase_io.read(input_file.name).calc # And keep track of the count! # (at least if it's not infinite) i = 0 while True: if n is not None: if i >= n: return i += 1 # Generate a structure subproc = sp.Popen(airss_cmd, universal_newlines=True, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = safe_communicate(subproc, template) # Now turn it into a proper Atoms object # To do this we need to make it look like a file to ASE's io.read newcell = ase_io.read(StringIO(stdout), format='castep-cell') if clone_calc: newcell.set_calculator(copy.deepcopy(calc)) # Generate it a name, function of its properties postfix = hashlib.md5(str(newcell.get_positions() ).encode()).hexdigest() newcell.info['name'] = '{0}_{1}'.format(basename, postfix) yield newcell
def get_gulp_charges(s, charge_method="eem", save_charges=True, gulp_command='gulp', gulp_path=None): """Calculate the atomic partial charges using GULP. | Parameters: | s (ase.Atoms): the structure to calculate the energy of | charge_method (Optional[str]): which method to use for atomic partial | charge calculation. Can be any of | 'eem', 'qeq' and 'pacha'. | Default is 'eem'. | save_charges (Optional[bool]): whether to save or not the charges in | the given ase.Atoms object. Default is | True. | gulp_command (Optional[str]): command required to call the GULP | executable. | gulp_path (Optional[str]): path where the GULP executable can be | found. If not present, the GULP command | will be invoked directly (assuming the | executable is in the system PATH). | Returns: | charges(np.array(float)): per-atom partial charges """ # Sanity check if charge_method not in ['eem', 'qeq', 'pacha']: raise ValueError('Invalid charge_method passed to get_gulp_charges') # Now define the input gin = "{0}\n".format(charge_method) gin += _gulp_cell_definition(s) # AND GO! if gulp_path is None: gulp_path = '' gulp_cmd = [os.path.join(gulp_path, gulp_command)] # Run the thing... try: gulp_proc = sp.Popen(gulp_cmd, universal_newlines=True, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = safe_communicate(gulp_proc, gin) except OSError: raise RuntimeError('GULP not found on this system with the given ' 'command') # Necessary for compatibility in Python2 try: stdout = unicode(stdout) except NameError: pass # And parse the output gulp_lines = stdout.split('\n') charges = _gulp_parse_charges(gulp_lines) if charges is None: raise RuntimeError('ERROR - GULP run failed to return charges') # Run a security check if not np.all(s.get_atomic_numbers() == charges['Z']): raise RuntimeError('ERROR - Invalid charges parsed from GULP output') charges = charges['q'] if save_charges: s.set_initial_charges(charges) return charges
def get_w99_energy(s, charge_method='eem', Etol=1e-6, gulp_command='gulp', gulp_path=None, save_charges=False): """Calculate the W99 force field energy using GULP. | Parameters: | s (ase.Atoms): the structure to calculate the energy of | charge_method (Optional[str]): which method to use for atomic partial | charge calculation. Can be any of | 'eem', 'qeq' and 'pacha'. | Default is 'eem'. | Etol (Optional[float]): tolerance on energy for intermolecular | potential cutoffs (relative to single | interaction energy). Default is 1e-6 eV. | gulp_command (Optional[str]): command required to call the GULP | executable. | gulp_path (Optional[str]): path where the GULP executable can be | found. If not present, the GULP command | will be invoked directly (assuming the | executable is in the system PATH). | save_charges (Optional[bool]): whether to retrieve also the charges | and save them in the Atoms object. | False by default. | Returns: | energy (float): the calculated energy """ # Sanity check if charge_method not in ['eem', 'qeq', 'pacha']: raise ValueError('Invalid charge_method passed to get_w99_energy') # First, atom types find_w99_atomtypes(s) # Now define the input gin = "molq {0} dipole\n".format(charge_method) gin += _gulp_cell_definition(s, syms=s.get_array('w99_types')) # Finally, the potential definition gin += _w99_field_definition(s, Etol) # AND GO! if gulp_path is None: gulp_path = '' gulp_cmd = [os.path.join(gulp_path, gulp_command)] try: gulp_proc = sp.Popen(gulp_cmd, universal_newlines=True, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = safe_communicate(gulp_proc, gin) except OSError: raise RuntimeError('GULP not found on this system with the given ' 'command') # Necessary for compatibility in Python2 try: stdout = unicode(stdout) except NameError: pass # Now parse the energy gulp_lines = stdout.split('\n') E = _gulp_parse_energy(gulp_lines) if E is None: raise RuntimeError('ERROR - GULP run failed to return energy') # Remember it with a mock ASE calculator calc = SinglePointCalculator(s, energy=E) s.set_calculator(calc) if save_charges: qs = _gulp_parse_charges(gulp_lines) s.set_initial_charges(qs['q']) return E