def __init__(self, filename=None, name=None, rtf=None, prm=None, netcharge=None, method=FFTypeMethod.CGenFF_2b6, basis=BasisSet._6_31G_star, solvent=True, theory=Theory.B3LYP, execution=Execution.Inline, qmcode=Code.PSI4, outdir="./"): # filename -- a mol2 format input geometry # rtf, prm -- rtf, prm files # method -- if rtf, prm == None, guess atom types according to this method ( of enum FFTypeMethod ) self.basis = basis self.theory = theory self.solvent = solvent self.solvent_name = "vacuum" if solvent: self.solvent_name = "water" if theory == Theory.RHF: self.theory_name = "rhf" if theory == Theory.B3LYP: self.theory_name = "b3lyp" if basis == BasisSet._6_31G_star: self.basis_name = "6-31g-star" elif basis == BasisSet._cc_pVDZ: self.basis_name = "cc-pVDZ" else: raise ValueError("Unknown Basis Set") self.execution = execution self.qmcode = qmcode self.method = method self.outdir = outdir if not (filename.endswith(".mol2")): raise ValueError("Input file must be mol2 format") super().__init__(filename=filename, name=name) if (len(self.bonds) == 0): print("No bonds found. Guessing them") self.bonds = self._guessBonds() (a, b) = guessAnglesAndDihedrals(self.bonds, cyclicdih=True) self.natoms = self.serial.shape[0] self.angles = a self.dihedrals = b ee = detectEquivalents(self) self._soft_dihedrals = detectSoftDihedrals(self, ee) self._equivalent_atom_groups = ee[ 0] # list of groups of equivalent atoms self._equivalent_atoms = ee[ 1] # list of equivalent atoms, indexed by atom self._equivalent_group_by_atom = ee[ 2] # mapping from atom index to equivalent atom group if netcharge is None: self.netcharge = int(round(np.sum(self.charge))) else: self.netcharge = int(round(netcharge)) # Canonicalise the atom naming. self._rename_mol() if rtf and prm: # If the user has specified explicit RTF and PRM files go ahead and load those self._rtf = RTF(rtf) self._prm = PRM(prm) else: # Otherwise make atom types using the specified method # (Right now only MATCH) fftype = FFType(self, method=self.method) self._rtf = fftype._rtf self._prm = fftype._prm if not self._rtf or not self._prm: raise ValueError("RTF and PRM not defined") self.impropers = np.array(self._rtf.impropers) self.report()
def __init__(self, mol, method=FFTypeMethod.CGenFF_2b6, acCharges=None, tmpDir=None): # Find the executables if method == FFTypeMethod.GAFF or method == FFTypeMethod.GAFF2: antechamber_binary = shutil.which("antechamber") if not antechamber_binary: raise RuntimeError("antechamber executable not found") parmchk2_binary = shutil.which("parmchk2") if not parmchk2_binary: raise RuntimeError("parmchk2 executable not found") elif method == FFTypeMethod.CGenFF_2b6: match_binary = shutil.which("match-typer") if not match_binary: raise RuntimeError("match-typer executable not found") else: raise ValueError('method') # Create a temporary directory with TemporaryDirectory() as tmpdir: # HACK to keep the files tmpdir = tmpdir if tmpDir is None else tmpDir if method == FFTypeMethod.GAFF or method == FFTypeMethod.GAFF2: # Write the molecule to a file mol.write(os.path.join(tmpdir, 'mol.mol2')) # Run antechamber if method == FFTypeMethod.GAFF: atomtype = "gaff" elif method == FFTypeMethod.GAFF2: atomtype = "gaff2" else: raise ValueError('method') cmd = [ antechamber_binary, '-at', atomtype, '-nc', str(mol.netcharge), '-fi', 'mol2', '-i', 'mol.mol2', '-fo', 'prepi', '-o', 'mol.prepi' ] if acCharges is not None: cmd += ['-c', acCharges] returncode = subprocess.call(cmd, cwd=tmpdir) if returncode != 0: raise RuntimeError('"antechamber" failed') # Run parmchk2 returncode = subprocess.call([ parmchk2_binary, '-f', 'prepi', '-i', 'mol.prepi', '-o', 'mol.frcmod', '-a', 'Y' ], cwd=tmpdir) if returncode != 0: raise RuntimeError('"parmchk2" failed') # Read the results self._rtf = AmberRTF(mol, os.path.join(tmpdir, 'mol.prepi'), os.path.join(tmpdir, 'mol.frcmod')) self._prm = AmberPRM(os.path.join(tmpdir, 'mol.prepi'), os.path.join(tmpdir, 'mol.frcmod')) elif method == FFTypeMethod.CGenFF_2b6: # Write the molecule to a file mol.write(os.path.join(tmpdir, 'mol.pdb')) # Run match-type returncode = subprocess.call([ match_binary, '-charge', str(mol.netcharge), '-forcefield', 'top_all36_cgenff_new', 'mol.pdb' ], cwd=tmpdir) if returncode != 0: raise RuntimeError('"match-typer" failed') # Read the results self._rtf = RTF(os.path.join(tmpdir, 'mol.rtf')) self._prm = PRM(os.path.join(tmpdir, 'mol.prm')) else: raise ValueError('method')
def __init__(self, filename=None, name=None, rtf=None, prm=None, netcharge=None, method=FFTypeMethod.CGenFF_2b6, qm=None, outdir="./", mol=None, acCharges=None): if filename is not None and not filename.endswith('.mol2'): raise ValueError('Input file must be mol2 format') if mol is None: super().__init__(filename=filename, name=name) else: for v in mol.__dict__: self.__dict__[v] = deepcopy(mol.__dict__[v]) # Guess bonds if len(self.bonds) == 0: logger.warning('No bonds found! Guessing them...') self.bonds = self._guessBonds() # Guess angles and dihedrals self.angles, self.dihedrals = guessAnglesAndDihedrals(self.bonds, cyclicdih=True) # Detect equivalent atoms equivalents = detectEquivalents(self) self._equivalent_atom_groups = equivalents[ 0] # List of groups of equivalent atoms self._equivalent_atoms = equivalents[ 1] # List of equivalent atoms, indexed by atom self._equivalent_group_by_atom = equivalents[ 2] # Mapping from atom index to equivalent atom group # Detect rotatable dihedrals self._rotatable_dihedrals = detectSoftDihedrals(self, equivalents) # Set total charge if netcharge is None: self.netcharge = int(round(np.sum(self.charge))) else: self.netcharge = int(round(netcharge)) # Canonicalise the names self._rename() # Assign atom types, charges, and initial parameters self.method = method if rtf and prm: # If the user has specified explicit RTF and PRM files go ahead and load those self._rtf = RTF(rtf) self._prm = PRM(prm) logger.info('Reading FF parameters from %s and %s' % (rtf, prm)) elif method == FFTypeMethod.NONE: pass # Don't assign any atom types else: # Otherwise make atom types using the specified method fftype = FFType(self, method=self.method, acCharges=acCharges) logger.info('Assigned atom types with %s' % self.method.name) self._rtf = fftype._rtf self._prm = fftype._prm if hasattr(self, '_rtf'): self.atomtype[:] = [ self._rtf.type_by_name[name] for name in self.name ] self.charge[:] = [ self._rtf.charge_by_name[name] for name in self.name ] self.impropers = np.array(self._rtf.impropers) # Check if atom type names are compatible for type_ in self._rtf.types: if re.match(FFMolecule._ATOM_TYPE_REG_EX, type_): raise ValueError( 'Atom type %s is incompatable. It cannot finish with "x" + number!' % type_) # Set atom masses # TODO: maybe move to molecule if self.masses.size == 0: if hasattr(self, '_rtf'): self.masses[:] = [ self._rtf.mass_by_type[self._rtf.type_by_index[i]] for i in range(self.numAtoms) ] else: self.masses[:] = [ vdw.massByElement(element) for element in self.element ] self.qm = qm if qm else Psi4() self.outdir = outdir