def read_modes(self, filename): try: fd = open("/usr/share/xorg/" + filename, 'r') except IOError: fd = open("/usr/share/rhpxl/" + filename, 'r') lines = fd.readlines() fd.close() for line in lines: if line[0] != "#" and line[0] != '/': line = string.strip(line) elements = string.split(line) if line == "": continue if len(elements) < 11 or string.lower( elements[0]) != "modeline": raise exceptions.StandardError( "Invalid modeline in file: %s" % (line)) name = elements[1][1:-1] if self.modelines.has_key(name): self.modelines[name].append(ModeLine(elements)) else: self.modelines[name] = [ModeLine(elements)]
def __init__(self, filename=DEFAULT_AMINO_LIB, logging=True, reorder=True, unique=False, verbose=False, topologyFormat=_DEFAULT_TOPOLOGY_FORMAT, cutType=True): """Constructor.""" self.filename = filename if topologyFormat == "Molaris": self._Parse(reorder=reorder, unique=unique, logging=logging, verbose=verbose) elif topologyFormat == "CHARMM": self._ParseCHARMM(cutType=cutType, logging=logging, verbose=verbose) else: raise exceptions.StandardError("Unknown topology format.") if logging: print("# . %s> Found %d component%s" % (_MODULE_LABEL, self.ncomponents, "s" if self.ncomponents != 1 else "")) if not verbose: self.WriteLabels() self._i = 0
def __getitem__(self, key): """Find and return a component from the library.""" component = self._FindComponent(key) if not component: raise exceptions.StandardError( "Component %s not found in the library." % key) return component
def _get_ssh_connection(self): """Returns an ssh connection to the specified host""" _timeout = True ssh = SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) _start_time = time.time() saved_exception = exceptions.StandardError() #doing this because the log file fills up with these messages #this way it only logs it once log_attempted = False socket_error_logged = False auth_error_logged = False ssh_error_logged = False while not self._is_timed_out(self.timeout, _start_time): try: if not log_attempted: self._log.debug('Attempting to SSH connect to: ') self._log.debug('host: %s, username: %s, password: %s' % (self.host, self.username, self.password)) log_attempted = True ssh.connect(hostname=self.host, username=self.username, password=self.password, timeout=20, key_filename=[], look_for_keys=False, allow_agent=False) _timeout = False break except socket.error as e: if not socket_error_logged: self._log.error('Socket Error: %s' % str(e)) socket_error_logged = True saved_exception = e continue except paramiko.AuthenticationException as e: if not auth_error_logged: self._log.error('Auth Exception: %s' % str(e)) auth_error_logged = True saved_exception = e time.sleep(2) continue except paramiko.SSHException as e: if not ssh_error_logged: self._log.error('SSH Exception: %s' % str(e)) ssh_error_logged = True saved_exception = e time.sleep(2) continue #Wait 2 seconds otherwise time.sleep(2) if _timeout: self._log.error('SSHConnector timed out while trying to establish a connection') raise saved_exception #This MUST be done because the transport gets garbage collected if it #is not done here, which causes the connection to close on invoke_shell #which is needed for exec_shell_command ResourceManager.register(self, ssh.get_transport()) return ssh
def Run(self): """Run the calculation.""" fileOutput = open(self.fileTeraChemOutput, "w") subprocess.check_call([self.pathTeraChem, self.fileTeraChemInput], stdout=fileOutput, stderr=fileOutput) fileOutput.close() # . Parse the output file terachem = TeraChemOutputFile(filename=self.fileTeraChemOutput) self.Efinal = terachem.Efinal # . Include forces on QM atoms self.forces = terachem.forces # . Include forces on point charges (NOT AVAILABLE IN TERACHEM?) if (self.qmmm): if (hasattr(terachem, "pointCharges")): mmforces = [] for pc in terachem.pointCharges: pass self.mmforces = mmforces else: raise exceptions.StandardError( "QMMM=True, but pointcharge data is missing.") # . Include charges scheme = { CS_MULLIKEN: terachem.charges if hasattr(terachem, "charges") else [], CS_MERZKOLLMAN: terachem.espcharges if hasattr(terachem, "espcharges") else [], } self.charges = scheme[self.chargeScheme] # . Include timing information self.jobtime = terachem.jobtime # . Finish up self._Finalize()
def TokenizeLine (line, converters=None, separator=None, reverse=False): """Tokenize a line with optional converters and separator.""" tokens = None if line is not None: if separator is None: tokens = line.split () else: tokens = line.split (separator) # . If the line is empty, return nothing if len (tokens) < 1: return None # . The last token becomes first if reverse: tokens.reverse () if converters is not None: ntokens = len (tokens) nconverters = len (converters) # . Truncate tokens if there is more of them than converters if ntokens > nconverters: tokens = tokens[:nconverters] # . Add "empty" tokens if there is fewer of them than converters if ntokens < nconverters: tokens.extend ([None] * (nconverters - ntokens)) # . Do the conversion for (i, (token, converter)) in enumerate (zip (tokens, converters)): if (token is None) or (converter is None): new = token else: try: new = converter (token) except: raise exceptions.StandardError ("Unable to convert token " + repr (i) + ".", True) tokens[i] = new return tokens
def Run(self): # . Run the calculation orcaInput = os.path.join(self.scratch, self.job + ".inp") orcaOutput = os.path.join(self.scratch, self.job + ".log") # . In the debug mode, reuse the already existing log file calculate = True if self.debug: if os.path.exists(orcaOutput): calculate = False if calculate: fileOutput = open(orcaOutput, "w") subprocess.check_call([self.pathORCA, orcaInput], stdout=fileOutput, stderr=fileOutput) fileOutput.close() # . Parse the output file orca = ORCAOutputFile(orcaOutput, reverse=True) # . In ORCA, the final QM energy does not seem to include the self interaction energy of point charges self.Efinal = orca.Efinal # . Include forces on QM atoms engrad = EngradFile(os.path.join(self.scratch, self.job + ".engrad"), reverse=True) self.forces = engrad.forces # . Include forces on point charges if self.qmmm: pcgrad = PCgradFile(os.path.join(self.scratch, self.job + ".pcgrad"), reverse=True) self.mmforces = pcgrad.forces # . Include charges if self.chargeScheme == CS_MULLIKEN: charges = [] for atom in orca.atoms: charges.append(atom.charge) self.charges = charges elif self.chargeScheme == CS_MERZKOLLMAN: raise exceptions.StandardError( "Merz-Kollman charges are not (yet) implemented in QMCallerORCA." ) elif self.chargeScheme == CS_CHELPG: raise exceptions.StandardError( "CHELPG charges are not (yet) implemented in QMCallerORCA.") # . Finish up self._Finalize()
def __init__(self, **keywordArguments): """Constructor.""" super(QMCallerMopac, self).__init__(**keywordArguments) if self.qmmm: if self.fileAtoms != "mol.in": raise exceptions.StandardError( "With qmmm option enabled, fileAtoms can only be mol.in.") # . Prepare a MOPAC input file self._WriteInput()
def _WriteInput(self): """Write ORCA input files.""" # . Check for the scratch directory if not os.path.exists(self.scratch): os.makedirs(self.scratch) # . Header lines = [ "# . ORCA job", ] # . Include solvent or protein if self.qmmm: pcFile = os.path.abspath( os.path.join(self.scratch, self.job + ".pc")) lines.append("%%pointcharges \"%s\"" % pcFile) elif self.cosmo: raise exceptions.StandardError( "COSMO model is not (yet) implemented in QMCallerORCA.") # . Number of processors if self.ncpu < 2: cpus = "" else: cpus = " PAL%d" % self.ncpu # . Level of theory method, basis = self.method.split("/") lines.append("! ENGRAD %s %s SCFCONV10%s" % (method, basis, cpus)) # . Electronic state lines.append("* xyz %d %d" % (self.charge, self.multiplicity)) # . Geometry atoms = self.molaris.qatoms + self.molaris.latoms for atom in atoms: lines.append("%2s %16.10f %16.10f %16.10f" % (atom.label, atom.x, atom.y, atom.z)) # . End of file lines.append("*") # . Write everything to a file fo = open(os.path.join(self.scratch, (self.job + ".inp")), "w") for line in lines: fo.write(line + "\n") fo.close() # . Now prepare PC data if self.qmmm: pointCharges = self.molaris.patoms + self.molaris.watoms ncharges = len(pointCharges) lines = [ " %d" % ncharges, ] for atom in pointCharges: lines.append("%12.4f %16.10f %16.10f %16.10f" % (atom.charge, atom.x, atom.y, atom.z)) # . Write point charges to a file fo = open(os.path.join(self.scratch, (self.job + ".pc")), "w") for line in lines: fo.write(line + "\n") fo.close()
def WriteScanTrajectory(self, filename="traj.xyz", relative=True, reverse=False, append=False): if not hasattr(self, "scan"): raise exceptions.StandardError("No trajectory found.") self._WriteTrajectory(self.scan, filename=filename, relative=relative, reverse=reverse, append=append)
def __init__(self, **keywordArguments): """Constructor.""" # . Set default attributes attributes = self.__class__.defaultAttributes for (key, value) in attributes.iteritems(): setattr(self, key, value) # . Set user attributes for (key, value) in keywordArguments.iteritems(): if attributes.has_key(key): setattr(self, key, value) else: raise exceptions.StandardError("Unknown option %s." % key) # . Check for conflicting attributes if self.cosmo and self.qmmm: raise exceptions.StandardError( "Both cosmo and qmmm options cannot be enabled.") # . Read mol.in file from Molaris self.molaris = MolarisAtomsFile(filename=self.fileAtoms, replaceSymbols=self.replaceSymbols)
def WriteOptTrajectory(self, filename="opt_traj.xyz", relative=True, reverse=False, append=False): if not hasattr(self, "opt"): raise exceptions.StandardError("No optimization trajectory found.") self._WriteTrajectory(self.opt, filename=filename, relative=relative, reverse=reverse, append=append)
def _Finalize(self): # . Check if the QM calculation is OK checks = [ hasattr(self, "Efinal"), hasattr(self, "forces"), hasattr(self, "charges"), ] if not all(checks): raise exceptions.StandardError("Something went wrong.") # . Write a file for Molaris containing QM forces and charges self._WriteForcesCharges() # . Write a file containing the QM trajectory self._WriteTrajectory()
def _FindComponent(self, key): if isinstance(key, int): # . Search by serial for component in self.components: if component.serial == key: return component elif isinstance(key, str): # . Search by name for component in self.components: if component.name == key: return component else: raise exceptions.StandardError("Unknown type of key.") # . Component not found return None
def _FindAtom(self, resName, resSerial, atomName): found = False if resSerial > 0: for atom in self.atoms: if atom.resSerial == resSerial and atom.resName == resName and atom.atomName == atomName: found = True break else: # . If the serial number is lower than zero, the residue has to have a unique name for atom in self.atoms: if atom.resName == resName and atom.atomName == atomName: found = True break if not found: raise exceptions.StandardError("Cannot find atom %s %d %s" % (resName, resSerial, atomName)) return atom
def __init__(self, **keywordArguments): """Constructor.""" super(QMCallerGaussian, self).__init__(**keywordArguments) # . Determine if a semiempirical potential is used method = self.method[:3] if method in ( "AM1", "PM3", ) and self.qmmm: raise exceptions.StandardError( "Point charges cannot be used with semiempirical methods.") # . Reuse the wavefunction if the checkpoint file exists if self.fileGaussianCheckpoint: self.restart = os.path.exists( self.fileGaussianCheckpoint) and self.restart else: self.restart = False # . Prepare a Gaussian input file self._WriteInput()
def _WriteInput(self): """Write a Mopac input file.""" # . Set up a charge scheme schemes = { CS_MULLIKEN: "MULLIK", CS_MERZKOLLMAN: "ESP", } if not schemes.has_key(self.chargeScheme): raise exceptions.StandardError("Charge scheme %s is undefined." % self.chargeScheme) chargeScheme = schemes[self.chargeScheme] # . Set up a spin state multp = { 1: "", 2: "DOUBLET", 3: "TRIPLET", } spinState = multp[self.multiplicity] # . Set up a solvation model solvationModel = ("EPS=%.2f" % self.dielectric) if self.cosmo else "" # . Set up a QM/MM model qmmmModel = "DEBUG MOL_QMMM" if self.qmmm else "" # . Write header data = [] data.append("%s 1SCF CHARGE=%-2d %s %s GRAD XYZ AUX %s %s\n" % (self.method, self.charge, spinState, solvationModel, chargeScheme, qmmmModel)) mdstep = "" if hasattr(self.molaris, "mdstep"): mdstep = "MD step: %d" % self.molaris.mdstep data.append("%s\n" % mdstep) data.append("\n") # . Write geometry atoms = self.molaris.qatoms + self.molaris.latoms for atom in atoms: data.append("%2s %16.10f 1 %16.10f 1 %16.10f 1\n" % (atom.label, atom.x, atom.y, atom.z)) # . Finish up WriteData(data, self.fileMopacInput)
def convert(blog_kind): markdown = request.json.get('markdown') #app.log.debug("parser = %s, converter = %s, prefer_h1 = %s" % (sparser, sconverter, prefer_h1)) if not markdown: import exceptions raise exceptions.StandardError("No markdown") #s = source.decode('utf-8') app.log.debug("===== markdown =====\n" + markdown) #blog_kind = request.json.blog_kind if blog_kind == 'ameblo': md2html = Markdown2Ameblo(app.log) else: md2html = Markdown2Blogger(app.log) html = md2html.convert(markdown) response.content_type = 'application/json' return json.dumps({ 'html': html.strip(), 'blog_kind': blog_kind, })
def _Parse(self): lines = open(self.inputfile) scan = [] opt = [] # . Assume the job is failed until finding a "Normal termination" statement jobOK = False try: while True: line = next(lines) # . Get the version and revision of Gaussian if line.startswith(" Gaussian"): if line.count("Revision"): tokens = TokenizeLine( line, converters=[None, None, None, None]) self.version = tokens[1][:-1] self.revision = tokens[3][:-1] # . Get the number of atoms and their coordinates elif line.count("Input orientation:") or line.count( "Z-Matrix orientation:"): for skip in range(4): next(lines) natoms = 0 atoms = [] while True: line = next(lines) if line.count("----"): break tokens = TokenizeLine( line, converters=[int, int, int, float, float, float]) atomicNumber, x, y, z = tokens[1], tokens[3], tokens[ 4], tokens[5] atom = Atom(symbol=atomicNumberToSymbol[atomicNumber], x=x, y=y, z=z, charge=0.) atoms.append(atom) natoms += 1 self.atoms = atoms # . Get the final energy (for semiempirical calculations) elif line.count("NIter="): tokens = TokenizeLine(line, converters=[None, float, None, None]) self.Efinal = tokens[1] * HARTREE_TO_KCAL_MOL # . Get the final energy (for ab initio/DFT calculations) # SCF Done: E(RB+HF-LYP) = -882.208703983 A.U. after 28 cycles elif line.count("SCF Done"): tokens = TokenizeLine(line, converters=[ None, None, None, None, float, None, None, int, None ]) self.Efinal = tokens[4] * HARTREE_TO_KCAL_MOL # . Get the final, PCM-corrected energy (replace the regular energy) # # After PCM corrections, the SCF energy is -2571.87944471 a.u. elif line.count("After PCM corrections, the SCF energy is"): tokens = TokenizeLine(line, converters=([ None, ] * 7 + [ float, ])) Efinal = tokens[-1] * HARTREE_TO_KCAL_MOL if hasattr(self, "Efinal"): self.PCMcorr = Efinal - self.Efinal self.Efinal = Efinal # . Get the thermochemistry # # Zero-point correction= 0.381354 (Hartree/Particle) # Thermal correction to Energy= 0.400762 # Thermal correction to Enthalpy= 0.401706 # Thermal correction to Gibbs Free Energy= 0.334577 # Sum of electronic and zero-point Energies= -965.928309 # Sum of electronic and thermal Energies= -965.908901 # Sum of electronic and thermal Enthalpies= -965.907957 # Sum of electronic and thermal Free Energies= -965.975086 elif line.startswith( " Sum of electronic and zero-point Energies"): tokens = TokenizeLine(line, converters=[ float, ], reverse=True) thermoZPE = tokens[-1] * HARTREE_TO_KCAL_MOL elif line.startswith( " Sum of electronic and thermal Energies"): tokens = TokenizeLine(line, converters=[ float, ], reverse=True) thermoU = tokens[-1] * HARTREE_TO_KCAL_MOL elif line.startswith( " Sum of electronic and thermal Enthalpies"): tokens = TokenizeLine(line, converters=[ float, ], reverse=True) thermoH = tokens[-1] * HARTREE_TO_KCAL_MOL elif line.startswith( " Sum of electronic and thermal Free Energies"): tokens = TokenizeLine(line, converters=[ float, ], reverse=True) thermoG = tokens[-1] * HARTREE_TO_KCAL_MOL thermo = Thermo( Ezpe=thermoZPE, U=thermoU, H=thermoH, G=thermoG, ) self.thermo = thermo # . Get the self energy of the charges # . In g03, there is no "a.u." at the end # Self energy of the charges = -252.7809376522 a.u. elif line.count("Self energy of the charges"): tokens = TokenizeLine(line, converters=[None] * 6 + [float]) self.Echrg = tokens[6] * HARTREE_TO_KCAL_MOL # . Get ESP charges (can be Merz-Kollman or CHELPG) elif line.count("Charges from ESP fit"): for i in range(2): next(lines) self.espcharges = [] for i in range(natoms): tokens = TokenizeLine(next(lines), converters=[int, None, float]) charge = tokens[2] self.espcharges.append(charge) # . Get Mulliken charges # . The second condition is for Gaussian 09 elif line.startswith( " Mulliken atomic charges:") or line.startswith( " Mulliken charges:"): next(lines) self.charges = [] for i in range(natoms): tokens = TokenizeLine(next(lines), converters=[int, None, float]) charge = tokens[2] self.charges.append(charge) # . Get Mulliken charges summed into heavy atoms elif line.startswith( " Mulliken charges with hydrogens summed into heavy atoms" ): nheavy = 0 for atom in self.atoms: if atom.symbol[0] != "H": nheavy += 1 next(lines) self.sumcharges = [] #while True: for i in range(nheavy): line = next(lines) #if line.startswith (" Electronic spatial extent (au):"): # break tokens = TokenizeLine(line, converters=[int, None, float]) charge = tokens[2] self.sumcharges.append(charge) # . Get forces # . http://www.gaussian.com/g_tech/g_ur/k_force.htm # . Gaussian prints gradients, not forces, despite the misleading label "Forces" (?) # . There is not need to multiply the gradients by -1, since Molaris does it after reading the d.o file. # . In Plotnikov's script, there was no multiplication by -1. # elif line.count ("***** Axes restored to original set *****"): # for skip in range (4): # next (lines) elif line.count( "Center Atomic Forces (Hartrees/Bohr)" ): for i in range(2): next(lines) self.forces = [] for i in range(natoms): tokens = TokenizeLine( next(lines), converters=[int, int, float, float, float]) force = Force( x=tokens[2] * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, y=tokens[3] * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, z=tokens[4] * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM) self.forces.append(force) # . Read coordinates and values of point charges # Point Charges: # XYZ= 2.0006 1.0001 0.0000 Q= 0.1110 A= 0.0000 R= 0.0000 C= 0.0000 # XYZ= 2.0009 2.0911 0.0000 Q= -0.3675 A= 0.0000 R= 0.0000 C= 0.0000 # XYZ= 1.4863 2.4537 0.8897 Q= 0.1110 A= 0.0000 R= 0.0000 C= 0.0000 # (...) # Sum of input charges= 0.000000 elif line.startswith(" Point Charges:"): points = [] line = next(lines) while line.startswith(" XYZ="): tokens = TokenizeLine(line, converters=[ None, float, float, float, None, float ]) (x, y, z), charge = tokens[1:4], tokens[5] point = (x, y, z, charge) points.append(point) line = next(lines) # . Read in positions of points in space, other than nuclei, where electrostatic # . properties are evaluated. # # ********************************************************************** # # Electrostatic Properties Using The SCF Density # # ********************************************************************** # # Atomic Center 1 is at 3.665580 6.467202 12.974383 # Atomic Center 2 is at 4.909670 6.386763 13.616169 # (...) # Read-in Center 2400 is at 5.504554 14.162232 26.811879 # Read-in Center 2401 is at 5.086579 15.682876 27.049785 elif line.count( "Electrostatic Properties Using The SCF Density"): positions = [] for i in range(3): next(lines) line = next(lines) while (line.count("Atomic Center") or line.count("Read-in Center")): # . Fixed format! x = float(line[32:42]) y = float(line[42:52]) z = float(line[52:62]) position = (x, y, z) if line.count("Read-in Center"): positions.append(position) line = next(lines) # Electrostatic Properties (Atomic Units) # # ----------------------------------------------------------------- # Center Electric -------- Electric Field -------- # Potential X Y Z # ----------------------------------------------------------------- # 1 Atom -14.711204 -0.022648 0.000626 -0.009472 # 2 Atom -22.331530 0.084739 0.046163 -0.012921 # (...) elif line.count("Electrostatic Properties (Atomic Units)"): pointsElectric = [] atomsElectric = [] for i in range(6): line = next(lines) while not line.count("----"): onNucleus = True if line.count("Atom") else False line = line.replace("Atom", "") tokens = TokenizeLine( line, converters=[int, float, float, float, float]) if len(tokens) != 5: # . Electric field components may not always be there. In such cases, set all to zero. potential, (ex, ey, ez) = tokens[1], (0., 0., 0.) else: potential, (ex, ey, ez) = tokens[1], tokens[2:] field = (ex, ey, ez, potential) if onNucleus: # . Electrostatic potential and field on a nucleus atomsElectric.append(field) else: # . Electrostatic potential and field on a point charge pointsElectric.append(field) line = next(lines) self.atomsElectric = atomsElectric # . Save point charges try: pointCharges = [] for (x, y, z, charge), (ex, ey, ez, potential) in zip( points, pointsElectric): pc = PointCharge( x=x, y=y, z=z, charge=charge, # . Convert from Eh/bohr to kcal/(mol*A) ex=ex * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, ey=ey * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, ez=ez * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, # . Convert from Eh/e to kcal/(mol*e) potential=potential * HARTREE_TO_KCAL_MOL, ) pointCharges.append(pc) self.pointCharges = pointCharges except: pass # . Save electric (=electrostatic) properties properties = [] for (x, y, z), (ex, ey, ez, potential) in zip(positions, pointsElectric): prop = ElectricProperty( x=x, y=y, z=z, # . Convert from Eh/bohr to kcal/(mol*A) ex=ex * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, ey=ey * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, ez=ez * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, # . Convert from Eh/e to kcal/(mol*e) potential=potential * HARTREE_TO_KCAL_MOL, ) properties.append(prop) self.properties = properties # . Get atoms from the input file # Symbolic Z-matrix: # Charge = 1 Multiplicity = 1 # LI -0.112 0. -0.104 # XX -0.796 -1.788 -0.682 # O 0.093 0. 1.723 # (...) elif line.count("Symbolic Z-matrix"): next(lines) atomsInput = [] while True: tokens = TokenizeLine( next(lines), converters=[None, float, float, float]) if not tokens: break symbol, x, y, z = tokens atom = Atom( symbol=symbol, x=x, y=y, z=z, charge=0., ) atomsInput.append(atom) self.atomsInput = atomsInput # . Get job time in seconds elif line.count("Job cpu time"): tokens = TokenizeLine(line, converters=[ None, None, None, int, None, int, None, int, None, float, None ]) days, hours, minutes, seconds = tokens[3], tokens[ 5], tokens[7], tokens[9] self.jobtime = (days * 24 * 3600) + (hours * 3600) + ( minutes * 60) + seconds # . Quit here, since composite jobs are not supported (?) # break # . Check for a failed job elif line.startswith(" Normal termination of Gaussian"): jobOK = True # . Determine if we have reached the end of an IRC step elif line.count("-- Optimized point #"): newStep = ScanStep(Efinal=self.Efinal, atoms=self.atoms[:], forces=self.forces[:], charges=self.charges[:], espcharges=[]) # <--FIX ME scan.append(newStep) # . Determine if we have reached the end of a geometry optimization step elif line.startswith(" Berny optimization."): if hasattr(self, "Efinal"): optStep = ScanStep(Efinal=self.Efinal, atoms=self.atoms[:], forces=self.forces[:], charges=self.charges[:], espcharges=[]) # <--FIX ME opt.append(optStep) except StopIteration: pass # . Close the file lines.close() # . Check for a failed job if not jobOK: raise exceptions.StandardError("Job %s did not end normally." % self.inputfile) # . Does the job involve a scan (IRC or PES)? if scan: self.scan = scan # . Does the job involve a geometry optimization? if opt: self.opt = opt
def _Parse(self): lines = open(self.filename) fileOK = False try: while True: line = next(lines) if line.startswith(" NORMAL TERMINATION OF MOLARIS"): fileOK = True elif line.startswith(" Energies for the system at step"): while True: line = next(lines) if line.startswith( " EVB Total Energies -- Hamiltonian Breakdown" ): # . State I for i in range(4): line = next(lines) tokens = TokenizeLine(line, converters=([ None, ] + [ float, ] * 9)) Eevb = tokens[1] if not hasattr(self, "Eevba"): self.Eevba = [] self.Eevba.append(Eevb) # . State II line = next(lines) tokens = TokenizeLine(line, converters=([ None, ] + [ float, ] * 9)) Eevb = tokens[1] if not hasattr(self, "Eevbb"): self.Eevbb = [] self.Eevbb.append(Eevb) elif line.startswith( " Now running quantum program ..."): tokens = TokenizeLine(line, converters=[ int, ], reverse=True) state = tokens[0] while True: line = next(lines) if line.startswith(" E_evb(eminus)="): pass elif line.startswith(" E_classical"): pass elif line.startswith(" Equantum"): pass elif line.startswith(" e_qmmm"): tokens = TokenizeLine(line, converters=[ float, ], reverse=True) Eqmmm = tokens[0] break if state < 2: if not hasattr(self, "Eqmmma"): self.Eqmmma = [] self.Eqmmma.append(Eqmmm) else: if not hasattr(self, "Eqmmmb"): self.Eqmmmb = [] self.Eqmmmb.append(Eqmmm) # . Exit MD step break elif line.startswith(" Average energies"): # . Last step does not report QMMM energies (bug in Molaris?) break except StopIteration: pass # . Finalize lines.close() if not fileOK: raise exceptions.StandardError("Abnormal termination of file %s" % self.filename)
def _WriteInput(self): """Write a TeraChem input file.""" # . Write a coordinates file atoms = self.molaris.qatoms + self.molaris.latoms natoms = len(atoms) output = open(self.fileCoordinates, "w") output.write("%d\nTeraChem job.\n" % natoms) for atom in atoms: output.write("%2s %16.10f %16.10f %16.10f\n" % (atom.label, atom.x, atom.y, atom.z)) output.write("\n") output.close() # . Write an input file output = open(self.fileTeraChemInput, "w") (method, basis) = self.method.split("/") output.write("method %s\n" % method) output.write("basis %s\n" % basis) output.write("charge %d\n" % self.charge) output.write("spinmult %d\n" % self.multiplicity) output.write("coordinates %s\n" % self.fileCoordinates) output.write("dftd %s\n" % "no") output.write("run %s\n" % "gradient") # . Choose a charge scheme schemes = { CS_MULLIKEN: "", CS_MERZKOLLMAN: "resp yes", } if (not schemes.has_key(self.chargeScheme)): raise exceptions.StandardError("Charge scheme %s is undefined." % self.chargeScheme) selectScheme = schemes[self.chargeScheme] if (selectScheme != ""): output.write("%s\n" % selectScheme) if (self.qmmm): # . Include point charges (electrostatic embedding) output.write("pointcharges %s\n" % self.filePointCharges) pcfile = open(self.filePointCharges, "w") pointCharges = self.molaris.patoms + self.molaris.watoms for atom in pointCharges: pcfile.write("%10.4f %14.8f %14.8f %14.8f\n" % (atom.charge, atom.x, atom.y, atom.z)) pcfile.close() else: # . Do not include point charges output.write("pointcharges %s\n" % "no") if (self.fileTeraChemCheckpoint): # . Write a checkpoint file output.write("chkfile %s\n" % self.fileTeraChemCheckpoint) guess = "generate" if (self.restart): fileGuess = os.path.join("scr", "c0") if (os.path.exists(fileGuess)): guess = fileGuess else: filea = os.path.join("scr", "ca") fileb = os.path.join("scr", "cb") if (os.path.exists(filea) and os.path.exists(fileb)): guess = "%s %s" % (filea, fileb) output.write("guess %s\n" % guess) if (self.SCFConvergence != _DEFAULT_CONVERGENCE): output.write("convthre %e\n" % math.pow(10, -self.SCFConvergence)) output.write("end\n\n") output.close()
def GenerateList(self, selectResidues, library, nstates=2, shift=2, modify=_DEFAULT_MODIFY): """Generate a list of EVB atoms to use in a Molaris input file.""" if not isinstance(library, AminoLibrary): raise exceptions.StandardError("Argument is not an amino library.") # . Iterate residues residues = [] for selectLabel, selectSerial, selectGroups in selectResidues: # . Perform initial checks if not selectLabel in library: raise exceptions.StandardError( "Residue %s not found in the library." % selectLabel) # . Iterate residues from the determine_atoms.out file found = False for selfResidue in self.residues: checks = ( selfResidue.label == selectLabel, selfResidue.serial == selectSerial, ) if all(checks): found = True break if not found: raise exceptions.StandardError( "Residue %s not found in file %s." % (selectLabel, self.filename)) # . Select AminoComponent aminoComponent = library[selectLabel] # . Select atoms belonging to selected groups selectedAtoms = [] for aminoGroup in aminoComponent.groups: if aminoGroup.symbol in selectGroups: selectedAtoms.extend(aminoGroup.labels) aminoAtoms = [] for aminoAtom in aminoComponent.atoms: if aminoAtom.atomLabel in selectedAtoms: aminoAtoms.append(aminoAtom) # . Print a list of EVB atoms # evb_atm 7196 0.7000 P0 0.7000 P0 # 0.7000 P4 PG # evb_atm 7197 -0.9000 O- -0.9000 O- # -0.9000 O3 O1G # evb_atm 7198 -0.9000 O- -0.9000 O- # -0.9000 O3 O2G for selfAtom in selfResidue.atoms: for aminoAtom in aminoAtoms: if selfAtom.label == aminoAtom.atomLabel: # . Modify atom type if modify.has_key(aminoAtom.atomType): modifiedType = modify[aminoAtom.atomType] else: modifiedType = "%s0" % aminoAtom.atomType[0] entry = " %7.4f %2s" % (aminoAtom.atomCharge, modifiedType) states = "" for i in range(nstates): states = "%s%s" % (states, entry) print( "%sevb_atm %5d%s # %7.4f %2s %-4s" % (" " * shift, selfAtom.serial, states, aminoAtom.atomCharge, aminoAtom.atomType, aminoAtom.atomLabel)) break
def _ParseCHARMM(self, cutType, logging, verbose): data = open(self.filename) if logging: print("# . %s> Parsing file \"%s\"" % (_MODULE_LABEL, self.filename)) # . Initialize bonds = [] group = [] groups = [] internal = [] components = [] try: while True: line = self._GetCleanLine(data) # RESI ASP -1.00 # GROUP # ATOM N NH1 -0.47 # ATOM H H 0.31 # ATOM CA CT1 0.07 # ATOM HA HB 0.09 # GROUP # ATOM CB CT2 -0.28 # (...) # BOND CB CA CG CB OD2 CG # (...) # DOUBLE O C CG OD1 # # . Treat patches as components if line[:4] in ( "RESI", "PRES", ): if group: groups.append(group) # . Create a temporary component component = (componentLabel, componentCharge, groups, bonds, internal) components.append(component) # . Component begins tokens = TokenizeLine(line, converters=[None, None, float]) (componentLabel, componentCharge) = tokens[1:] # . Reinitialize bonds = [] group = [] groups = [] internal = [] elif line.startswith("GROUP"): if group: groups.append(group) group = [] elif line.startswith("ATOM"): tokens = TokenizeLine(line, converters=[None, None, None, float]) newAtom = AminoAtom( # . Molaris only uses 2-character atom types atomType=tokens[2][:2] if cutType else tokens[2], atomLabel=tokens[1], atomCharge=tokens[3], ) group.append(newAtom) elif line.startswith("BOND") or line.startswith("DOUBLE"): tokens = line.split() labels = tokens[1:] nlabels = len(labels) if (nlabels % 2) != 0: raise exceptions.StandardError( "Incorrect BOND entry in component %s." % componentLabel) for i in range(0, nlabels, 2): labela, labelb = labels[i], labels[i + 1] # . Ignore bonds involving atoms from other residues checks = [] for label in (labela, labelb): checks.extend([ label[0] != "+", label[-1] != "-", not label[0].isdigit() ]) if all(checks): pair = (labela, labelb) bonds.append(pair) elif line.startswith("IC"): # IC -C CA *N H 1.3551 126.4900 180.0000 115.4200 0.9996 # IC -C N CA C 1.3551 126.4900 180.0000 114.4400 1.5390 # IC N CA C +N 1.4592 114.4400 180.0000 116.8400 1.3558 # IC +N CA *C O 1.3558 116.8400 180.0000 122.5200 1.2297 # IC CA C +N +CA 1.5390 116.8400 180.0000 126.7700 1.4613 # (...) while (line.startswith("IC")): tokens = TokenizeLine(line, converters=[ None, None, None, None, None, float, float, float, float, float ]) (a, b, c, d) = tokens[1:5] ic = InternalCoordinate(i=a, j=b, k=c.replace("*", ""), l=d, distanceLeft=tokens[5], angleLeft=tokens[6], torsion=tokens[7], angleRight=tokens[8], distanceRight=tokens[9], improper=(c[0] == "*")) internal.append(ic) line = self._GetCleanLine(data) except StopIteration: pass # . Finish up data.close() if group: groups.append(group) component = (componentLabel, componentCharge, groups, bonds, internal) components.append(component) # . Set up actual amino components from temporary components aminoComponents = [] for componentSerial, (componentLabel, componentCharge, groups, bonds, internalCoordinates) in enumerate(components, 1): # . Merge atoms aminoAtoms = [] for group in groups: for atom in group: aminoAtoms.append(atom) aminoGroups = [] # . Iterate temporary groups for igroup, group in enumerate(groups): # . Merge atom labels labels = [] for atom in group: labels.append(atom.atomLabel) # . Create a group natoms = len(group) aminoGroup = AminoGroup( radius=5., natoms=natoms, labels=labels, symbol=chr(ord(_GROUP_START) + igroup), centralAtom=group[natoms / 2].atomLabel, ) aminoGroups.append(aminoGroup) # . Create a component component = AminoComponent( serial=componentSerial, label=componentLabel, groups=aminoGroups, atoms=aminoAtoms, bonds=bonds, internal=internalCoordinates, connect=("", ""), logging=True if (logging and verbose) else False, title="Generated from CHARMM topology", ) aminoComponents.append(component) self.components = aminoComponents
def _Parse(self, logging, reorder, unique, verbose): components = [] names = [] data = open(self.filename) if logging: print("# . %s> Parsing file \"%s\"" % (_MODULE_LABEL, self.filename)) try: while True: line = self._GetCleanLine(data) # . Check if a new residue starts if line.startswith("---"): # . Get serial and name line, title = self._GetLineWithComment(data) entry = TokenizeLine(line, converters=[ None, ])[0] # . Remove spaces entry = entry.replace(" ", "") # . Check if last residue found if entry == "0": break for i, char in enumerate(entry): if not char.isdigit(): break componentSerial, name = int(entry[:i]), entry[i:] # . Check if the component label is unique if unique: if name in names: raise exceptions.StandardError( "Component label %s is not unique." % name) names.append(name) # . Get number of atoms line = self._GetCleanLine(data) natoms = int(line) # . Initiate conversion table serial->label convert = {} # . Read atoms atoms = [] labels = [] for i in range(natoms): line = self._GetCleanLine(data) atomNumber, atomLabel, atomType, atomCharge = TokenizeLine( line, converters=[int, None, None, float]) if unique: # . Check if the atom label is unique if atomLabel in labels: raise exceptions.StandardError( "Component %s %d: Atom label %s is not unique." % (name, serial, atomLabel)) labels.append(atomLabel) # . Create atom atom = AminoAtom(atomLabel=atomLabel, atomType=atomType, atomCharge=atomCharge) atoms.append(atom) # . Update conversion table serial->label convert[atomNumber] = atomLabel # . Get number of bonds line = self._GetCleanLine(data) nbonds = int(line) # . Read bonds bonds = [] for i in range(nbonds): line = self._GetCleanLine(data) atoma, atomb = TokenizeLine(line, converters=[int, int]) if reorder: # . Keep the lower number first if atoma > atomb: atoma, atomb = atomb, atoma bonds.append((atoma, atomb)) if reorder: # . Sort bonds bonds.sort(key=lambda bond: bond[0]) # . Convert numerical bonds to labeled bonds labeledBonds = [] for atoma, atomb in bonds: # . FIXME: Workaround for invalid entries in the amino file try: pair = (convert[atoma], convert[atomb]) labeledBonds.append(pair) except: pass bonds = labeledBonds # . Read connecting atoms line = self._GetCleanLine(data) seriala, serialb = TokenizeLine(line, converters=[int, int]) # . Convert serials of connecting atoms to labels connecta, connectb = "", "" if seriala > 0: connecta = convert[seriala] if serialb > 0: connectb = convert[serialb] # . Read number of electroneutral groups line = self._GetCleanLine(data) ngroups = int(line) # . Read groups groups = [] for i in range(ngroups): line = self._GetCleanLine(data) nat, central, radius = TokenizeLine( line, converters=[int, int, float]) line = self._GetCleanLine(data) serials = TokenizeLine(line, converters=[int] * nat) # . Convert central atom's serial to a label # . FIXME: Workaround for invalid entries in the amino file try: central = convert[central] # . Convert serials to labels labels = [] for serial in serials: labels.append(convert[serial]) symbol = chr(ord(_GROUP_START) + i) group = AminoGroup(natoms=nat, centralAtom=central, radius=radius, labels=labels, symbol=symbol) groups.append(group) except: pass # . Create a component and add it to the list logFlag = False if logging: if verbose: logFlag = True component = AminoComponent(serial=componentSerial, name=name, atoms=atoms, bonds=bonds, groups=groups, connect=(connecta, connectb), logging=logFlag, title=title) components.append(component) except StopIteration: pass # . Finish up data.close() self.components = components
def _WriteInput(self): """Write a Gaussian input file.""" # . Write job control data = [] if self.ncpu > 1: data.append("%%NProcShared=%d\n" % self.ncpu) if self.memory: data.append("%%mem=%dgb\n" % self.memory) if self.fileGaussianCheckpoint: data.append("%%chk=%s\n" % self.fileGaussianCheckpoint) # . Set up a charge scheme schemes = { CS_MULLIKEN: "", CS_CHELPG: "POP=CHELPG", CS_MERZKOLLMAN: "POP=MK", } if not schemes.has_key(self.chargeScheme): raise exceptions.StandardError("Charge scheme %s is undefined." % self.chargeScheme) chargeScheme = schemes[self.chargeScheme] # . Include extra options, if any present if self.extraOptions: if isinstance(self.extraOptions, tuple): extraOptions = " ".join(self.extraOptions) else: extraOptions = self.extraOptions else: extraOptions = "" # . Write header if self.qmmm: background = "Charge Prop=(Field,Read)" elif self.cosmo: background = "SCRF=(Solvent=Water,Read)" else: background = "" if self.restart: restart = "Guess=Read" else: restart = "" if self.SCFConvergence != 6: scfConvergence = "SCF=(Conver=%d)" % self.SCFConvergence else: scfConvergence = "" keywords = ( self.method, "NoSymm", "Force", background, restart, chargeScheme, scfConvergence, extraOptions, ) header = " ".join(keywords) data.append("#P " + header + "\n\n") mdstep = "" if hasattr(self.molaris, "mdstep"): mdstep = " (MD step: %d)" % self.molaris.mdstep data.append("Input file generated by MolarisTools%s.\n\n" % mdstep) data.append("%d %d\n" % (self.charge, self.multiplicity)) # . Write geometry atoms = self.molaris.qatoms + self.molaris.latoms for atom in atoms: data.append("%2s %16.10f %16.10f %16.10f\n" % (atom.label, atom.x, atom.y, atom.z)) data.append("\n") # . If cosmo=True, write epsilon if self.cosmo: data.append("eps=%f\n\n" % self.dielectric) # . Write point charges if self.qmmm: pointCharges = self.molaris.patoms + self.molaris.watoms for atom in pointCharges: data.append("%16.10f %16.10f %16.10f %16.10f\n" % (atom.x, atom.y, atom.z, atom.charge)) data.append("\n") # . Write points where the electric field is be calculated for atom in pointCharges: data.append("%16.10f %16.10f %16.10f\n" % (atom.x, atom.y, atom.z)) data.append("\n") # . Finish up WriteData(data, self.fileGaussianInput)
def Run(self): """Run the calculation.""" qchemInput = os.path.join(self.scratch, self.job + ".inp") qchemOutput = os.path.join(self.scratch, self.job + ".out") qchemError = os.path.join(self.scratch, self.job + ".err") qchemEField = os.path.join(self.scratch, "efield.dat") # . Call Q-Chem fileError = open(qchemError, "w") if self.ncpu < 2: command = [ os.path.join(self.pathQChem, "bin", "qchem"), "-save", qchemInput, qchemOutput, _DEFAULT_SAV_FOLDER ] else: command = [ os.path.join(self.pathQChem, "bin", "qchem"), "-save", "-nt", "%d" % self.ncpu, qchemInput, qchemOutput, _DEFAULT_SAV_FOLDER ] subprocess.check_call(command, stdout=fileError, stderr=fileError) fileError.close() # . Parse output files qchem = QChemOutputFile(qchemOutput) efield = EfieldFile(qchemEField) if self.qmmm: # . Calculate electrostatic forces acting on MM atoms mmforces = [] pointCharges = self.molaris.patoms + self.molaris.watoms nvectors = len(pointCharges) for point, (ex, ey, ez) in zip(pointCharges, efield.field[:nvectors]): force = Force( x=ex * point.charge, y=ey * point.charge, z=ez * point.charge, ) mmforces.append(force) self.mmforces = mmforces # . Include forces on QM atoms forces = [] for (fx, fy, fz) in efield.field[nvectors:]: force = Force( x=-fx, y=-fy, z=-fz, ) forces.append(force) self.forces = forces # . If there are point charges, remove their self interaction energy from the final QM energy self.Efinal = (qchem.Efinal - qchem.Echrg) if self.qmmm else qchem.Efinal # . Include charges if self.chargeScheme == CS_MULLIKEN: self.charges = qchem.charges elif self.chargeScheme == CS_MERZKOLLMAN: raise exceptions.StandardError( "Merz-Kollman charges are not (yet) implemented in QMCallerQChem." ) elif self.chargeScheme == CS_CHELPG: raise exceptions.StandardError( "CHELPG charges are not (yet) implemented in QMCallerQChem.") # . Finish up self._Finalize()
def _Parse(self): lines = open(self.inputfile) jobOK = True try: while True: line = next(lines) # . Get the number of atoms # . This line does not exists in log files generated by some versions of Mopac if line.count("TOTAL NO. OF ATOMS:"): tokens = TokenizeLine(line, converters=[ int, ], reverse=True) self.natoms = tokens[0] # . Read gradients elif line.count("FINAL POINT AND DERIVATIVES"): # . Skip the next two lines next(lines) next(lines) self.forces = [] for i in range(self.natoms): force = Force( x=self._GetGradientLine(lines) * GRADIENT_TO_FORCE, y=self._GetGradientLine(lines) * GRADIENT_TO_FORCE, z=self._GetGradientLine(lines) * GRADIENT_TO_FORCE) self.forces.append(force) # . Get the final total energy (electronic + nuclear repulsion) elif line.count("TOTAL ENERGY"): tokens = TokenizeLine( line, converters=[None, None, None, float, None]) self.Etotal = tokens[3] * EV_TO_KCAL_MOL # . Read the final heat in formation # . Comment: For some reason (numeric?), heat of formation was used as a final form of energy # . in old Plotnikov's scripts, instead of the total energy. elif line.count("FINAL HEAT OF FORMATION"): tokens = TokenizeLine(line, converters=[ None, None, None, None, None, float, None, None, float, None ]) self.Efinal = tokens[5] # . Read ESP (= Merz-Kollman) charges # . This line does not exists in log files generated by some versions of Mopac (relevant?) elif line.count("ELECTROSTATIC POTENTIAL CHARGES"): # . Skip the next two lines next(lines) next(lines) self.mkcharges = [] for i in range(self.natoms): tokens = TokenizeLine(next(lines), converters=[int, None, float]) charge = tokens[2] self.mkcharges.append(charge) # . Read Mulliken charges elif line.count("MULLIKEN POPULATIONS AND CHARGES"): # . Skip the next two lines next(lines) next(lines) self.charges = [] for i in range(self.natoms): tokens = TokenizeLine( next(lines), converters=[int, None, float, float]) charge = tokens[3] self.charges.append(charge) # . Get the most recent coordinates of atoms # CARTESIAN COORDINATES # # NO. ATOM X Y Z # # 1 C 3.6656 6.4672 12.9744 # 2 O 4.9097 6.3868 13.6162 # (...) elif line.count("CARTESIAN COORDINATES"): for i in range(3): next(lines) atoms = [] while True: line = next(lines) templ = line.split() if len(templ) != 5: break tokens = TokenizeLine( line, converters=[int, None, float, float, float]) serial, symbol, x, y, z = tokens atom = Atom( symbol=symbol, x=x, y=y, z=z, charge=0., ) atoms.append(atom) self.atoms = atoms # . A workaround for some versions of Mopac that does not provide "TOTAL NO. OF ATOMS" if not hasattr(self, "natoms"): self.natoms = len(atoms) # . Check for a failed job elif self._CheckLine(line, _ERROR_LINES): jobOK = False except StopIteration: pass # . Close the file lines.close() # . Check for a failed job if not jobOK: raise exceptions.StandardError("Job %s did not end normally." % self.inputfile)
def _Parse (self, reverse, convert): lines = open (self.inputfile) # . Assume the job is failed until finding a "TERMINATED NORMALLY" statement jobOK = False try: while True: line = next (lines) # . Get coordinates of QM atoms # --------------------------------- # CARTESIAN COORDINATES (ANGSTROEM) # --------------------------------- # C 5.663910 4.221157 -1.234141 # H 5.808442 3.140412 -1.242145 # (...) if line.startswith ("CARTESIAN COORDINATES (ANGSTROEM)"): next (lines) line = next (lines) geometry = [] while line != "\n": tokens = TokenizeLine (line, converters=[None, float, float, float]) label, x, y, z = tokens atom = (label, x, y, z) geometry.append (atom) line = next (lines) # . Get charges on QM atoms # ----------------------- # MULLIKEN ATOMIC CHARGES # ----------------------- # 0 C : -0.520010 # 1 H : 0.271953 # (...) # Sum of atomic charges: -0.0000000 if line.startswith ("MULLIKEN ATOMIC CHARGES"): next (lines) line = next (lines) charges = [] while not line.startswith ("Sum of atomic charges:"): tokens = TokenizeLine (line, separator=":", converters=[None, float]) charge = tokens[1] charges.append (charge) line = next (lines) self.charges = charges # . Construct the final list of atoms with charges atoms = [] for (label, x, y, z), charge in zip (geometry, charges): atom = Atom ( symbol = label , x = x , y = y , z = z , charge = charge , ) atoms.append (atom) self.atoms = atoms # . Get gradients on QM atoms # ------------------ # CARTESIAN GRADIENT # ------------------ # # 1 C : -0.017273415 0.000431161 0.011902545 # 2 H : 0.011246801 -0.004065387 -0.003492146 # (...) elif line.startswith ("CARTESIAN GRADIENT"): for i in range (2): next (lines) line = next (lines) forces = [] while line != "\n": tokens = TokenizeLine (line, converters=[int, None, None, float, float, float]) gx, gy, gz = tokens[3:6] if reverse: gx, gy, gz = (-gx, -gy, -gz) if convert: gx, gy, gz = (gx * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, gy * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM, gz * HARTREE_BOHR_TO_KCAL_MOL_ANGSTROM) force = Force ( x = gx , y = gy , z = gz , ) forces.append (force) line = next (lines) self.forces = forces # . Get the final energy # FINAL SINGLE POINT ENERGY -263.834308915009 elif line.startswith ("FINAL SINGLE POINT ENERGY"): tokens = TokenizeLine (line, converters=[float, ], reverse=True) if convert: self.Efinal = tokens[-1] * HARTREE_TO_KCAL_MOL else: self.Efinal = tokens[-1] # . Check for a failed job elif line.count ("****ORCA TERMINATED NORMALLY****"): jobOK = True except StopIteration: pass # . Close the file lines.close () # . Check for a failed job if not jobOK: raise exceptions.StandardError ("Job %s did not end normally." % self.inputfile)