def setUp(self): if os.environ.get('TRAVIS_OS_NAME') == 'osx': self.skipTest('Psi4 does not work on Mac') # TODO fix! self.qm = Psi4() super().setUp()
def _run(self, molName): self.molName = molName logger.info('Molecule: {}'.format(self.molName)) molFile = os.path.join(home('test-charge'), self.molName + '.mol2') self.mol = Molecule(molFile) self.new_mols = {} self.extras = {} self.new_mols['Gasteiger'] = fitGasteigerCharges(self.mol) try: self.new_mols['AM1-BCC'] = fitChargesWithAntechamber(self.mol, type='bcc') except: pass qm = Psi4() qm.theory = 'B3LYP' qm.basis = '6-311++G**' workDir = os.path.join('tmp', self.molName) os.makedirs(workDir, exist_ok=True) for factor in [-10, -5, -4, -3, -2, -1]: logger.info('Factor: {}'.format(factor)) key = 'ESP b {}'.format(factor) np.random.seed(20181114) # Make ESP grid generation deterministic self.new_mols[key], self.extras[key] = fitESPCharges(self.mol, qm, workDir, restraint_factor=10**factor)
def setUp(self): # TODO finish self.skipTest('No AceCloud tests') self.qm = Psi4() self.qm.queue = AceCloudQueue() super().setUp()
def setUp(self): self.skipTest('No Slurm tests') if 'TRAVIS' in os.environ: self.skipTest('No Psi4 Slurm tests on Travis') self.qm = Psi4() self.qm.queue = SlurmQueue() self.qm.queue.partition = 'normalGPU' # TODO use CPU partition when available self.qm.queue.jobname = 'Psi4_test' super().setUp()
def _get_qm_calculator(args, queue): from htmd.qm import Psi4, Gaussian, FakeQM2 logger.info('=== QM configuration ===') # Create a QM object logger.info('Code: {}'.format(args.code)) if args.code == 'Psi4': qm = Psi4() elif args.code == 'Gaussian': qm = Gaussian() else: raise AssertionError() # Override with a FakeQM object if args.fake_qm: qm = FakeQM2() logger.warning('Using FakeQM') # Configure the QM object qm.theory = args.theory logger.info('Theory: {}'.format(qm.theory)) qm.basis = args.basis logger.info('Basis sets: {}'.format(qm.basis)) qm.solvent = args.environment logger.info('Environment: {}'.format(qm.solvent)) qm.queue = queue qm.charge = args.charge # Update B3LYP to B3LYP-D3 # TODO: this is silent and not documented stuff if qm.theory == 'B3LYP': qm.correction = 'D3' logger.warning('The dispersion correction is set to {}'.format( qm.correction)) # Update basis sets # TODO: this is silent and not documented stuff if args.charge < 0 and qm.solvent == 'vacuum': if qm.basis == '6-31G*': qm.basis = '6-31+G*' if qm.basis == '6-311G**': qm.basis = '6-311++G**' if qm.basis == 'cc-pVDZ': qm.basis = 'aug-cc-pVDZ' logger.warning('Basis sets are changed to {}'.format(qm.basis)) return qm
def setUp(self): self.skipTest('No Slurm tests') if 'TRAVIS' in os.environ: self.skipTest('No Psi4 Slurm tests on Travis') # For Slurm, the test directory has to be on a shared filesystem self.testDir = os.getcwd() self.qm = Psi4() self.qm.queue = SlurmQueue() self.qm.queue.partition = 'normalGPU' # TODO use CPU partition when available self.qm.queue.jobname = 'Psi4_test' super().setUp()
def main_parameterize(arguments=None): args = getArgumentParser().parse_args(args=arguments) if not os.path.exists(args.filename): raise ValueError('File %s cannot be found' % args.filename) method_map = { 'GAFF': FFTypeMethod.GAFF, 'GAFF2': FFTypeMethod.GAFF2, 'CGENFF': FFTypeMethod.CGenFF_2b6 } methods = [method_map[method] for method in args.forcefield] # Get RTF and PRM file names rtfFile, prmFile = None, None if args.rtf_prm: rtfFile, prmFile = args.rtf_prm # Create a queue for QM if args.queue == 'local': queue = LocalCPUQueue() elif args.queue == 'Slurm': queue = SlurmQueue(_configapp=args.code.lower()) elif args.queue == 'LSF': queue = LsfQueue(_configapp=args.code.lower()) elif args.queue == 'PBS': queue = PBSQueue() # TODO: configure elif args.queue == 'AceCloud': queue = AceCloudQueue() # TODO: configure queue.groupname = args.groupname queue.hashnames = True else: raise NotImplementedError # Override default ncpus if args.ncpus: logger.info('Overriding ncpus to {}'.format(args.ncpus)) queue.ncpu = args.ncpus if args.memory: logger.info('Overriding memory to {}'.format(args.memory)) queue.memory = args.memory # Create a QM object if args.code == 'Psi4': qm = Psi4() elif args.code == 'Gaussian': qm = Gaussian() else: raise NotImplementedError # This is for debugging only! if args.fake_qm: qm = FakeQM2() logger.warning('Using FakeQM') # Get rotatable dihedral angles mol = Molecule(args.filename) mol = canonicalizeAtomNames(mol) mol, equivalents, all_dihedrals = getEquivalentsAndDihedrals(mol) netcharge = args.charge if args.charge is not None else int( round(np.sum(mol.charge))) if args.list: print('\n === Parameterizable dihedral angles of {} ===\n'.format( args.filename)) with open('torsions.txt', 'w') as fh: for dihedral in all_dihedrals: dihedral_name = '-'.join(mol.name[list(dihedral[0])]) print(' {}'.format(dihedral_name)) fh.write(dihedral_name + '\n') print() sys.exit(0) # Set up the QM object qm.theory = args.theory qm.basis = args.basis qm.solvent = args.environment qm.queue = queue qm.charge = netcharge # Select which dihedrals to fit parameterizable_dihedrals = [list(dih[0]) for dih in all_dihedrals] if len(args.dihedral) > 0: all_dihedral_names = [ '-'.join(mol.name[list(dihedral[0])]) for dihedral in all_dihedrals ] parameterizable_dihedrals = [] for dihedral_name in args.dihedral: if dihedral_name not in all_dihedral_names: raise ValueError( '%s is not recognized as a rotatable dihedral angle' % dihedral_name) parameterizable_dihedrals.append( list( all_dihedrals[all_dihedral_names.index(dihedral_name)][0])) # Print arguments print('\n === Arguments ===\n') for key, value in vars(args).items(): print('{:>12s}: {:s}'.format(key, str(value))) print('\n === Parameterizing %s ===\n' % args.filename) for method in methods: print(" === Fitting for %s ===\n" % method.name) printReport(mol, netcharge, equivalents, all_dihedrals) parameters, mol = fftype(mol, method=method, rtfFile=rtfFile, prmFile=prmFile, netcharge=args.charge) if isinstance(qm, FakeQM2): qm._parameters = parameters # Copy the molecule to preserve initial coordinates orig_coor = mol.coords.copy() # Update B3LYP to B3LYP-D3 # TODO: this is silent and not documented stuff if qm.theory == 'B3LYP': qm.correction = 'D3' # Update basis sets # TODO: this is silent and not documented stuff if netcharge < 0 and qm.solvent == 'vacuum': if qm.basis == '6-31G*': qm.basis = '6-31+G*' if qm.basis == 'cc-pVDZ': qm.basis = 'aug-cc-pVDZ' logger.info('Changing basis sets to %s' % qm.basis) # Minimize molecule if args.minimize: print('\n == Minimizing ==\n') mol = minimize(mol, qm, args.outdir) # Fit ESP charges if args.fit_charges: print('\n == Fitting ESP charges ==\n') # Set random number generator seed if args.seed: np.random.seed(args.seed) # Select the atoms with fixed charges fixed_atom_indices = getFixedChargeAtomIndices( mol, args.fix_charge) # Fit ESP charges mol, _, esp_charges, qm_dipole = fitCharges( mol, qm, args.outdir, fixed=fixed_atom_indices) # Print dipoles logger.info('QM dipole: %f %f %f; %f' % tuple(qm_dipole)) mm_dipole = getDipole(mol) if np.all(np.isfinite(mm_dipole)): logger.info('MM dipole: %f %f %f; %f' % tuple(mm_dipole)) else: logger.warning( 'MM dipole cannot be computed. Check if elements are detected correctly.' ) # Fit dihedral angle parameters if args.fit_dihedral: print('\n == Fitting dihedral angle parameters ==\n') # Set random number generator seed if args.seed: np.random.seed(args.seed) # Invent new atom types for dihedral atoms mol, originaltypes = inventAtomTypes(mol, parameterizable_dihedrals, equivalents) parameters = recreateParameters(mol, originaltypes, parameters) parameters = createMultitermDihedralTypes(parameters) if isinstance(qm, FakeQM2): qm._parameters = parameters # Fit the parameters fitDihedrals(mol, qm, method, parameters, all_dihedrals, parameterizable_dihedrals, args.outdir, geomopt=args.optimize_dihedral) # Output the FF parameters print('\n == Writing results ==\n') writeParameters(mol, parameters, qm, method, netcharge, args.outdir, original_coords=orig_coor) # Write energy file energyFile = os.path.join(args.outdir, 'parameters', method.name, _qm_method_name(qm), 'energies.txt') printEnergies(mol, parameters, energyFile) logger.info('Write energy file: %s' % energyFile)
def main_parameterize(arguments=None): args = getArgumentParser().parse_args(args=arguments) if not os.path.exists(args.filename): raise ValueError('File %s cannot be found' % args.filename) method_map = { 'GAFF': FFTypeMethod.GAFF, 'GAFF2': FFTypeMethod.GAFF2, 'CGENFF': FFTypeMethod.CGenFF_2b6 } methods = [method_map[method] for method in args.forcefield] # TODO: move into FFMolecule # Get RTF and PRM file names rtf, prm = None, None if args.rtf_prm: rtf, prm = args.rtf_prm # Create a queue for QM if args.queue == 'local': queue = LocalCPUQueue() elif args.queue == 'Slurm': queue = SlurmQueue(_configapp=args.code.lower()) elif args.queue == 'LSF': queue = LsfQueue(_configapp=args.code.lower()) elif args.queue == 'PBS': queue = PBSQueue() # TODO: configure elif args.queue == 'AceCloud': queue = AceCloudQueue() # TODO: configure queue.groupname = args.groupname queue.hashnames = True else: raise NotImplementedError # Override default ncpus if args.ncpus: logger.info('Overriding ncpus to {}'.format(args.ncpus)) queue.ncpu = args.ncpus if args.memory: logger.info('Overriding memory to {}'.format(args.memory)) queue.memory = args.memory # Create a QM object if args.code == 'Psi4': qm = Psi4() elif args.code == 'Gaussian': qm = Gaussian() else: raise NotImplementedError # This is for debugging only! if args.fake_qm: qm = FakeQM2() logger.warning('Using FakeQM') # Set up the QM object qm.theory = args.theory qm.basis = args.basis qm.solvent = args.environment qm.queue = queue # List rotatable dihedral angles if args.list: mol = FFMolecule(args.filename, method=methods[0], netcharge=args.charge, rtf=rtf, prm=prm, qm=qm, outdir=args.outdir) print('\n === Parameterizable dihedral angles of %s ===\n' % args.filename) with open('torsions.txt', 'w') as fh: for dihedral in mol.getRotatableDihedrals(): dihedral_name = '%s-%s-%s-%s' % tuple(mol.name[dihedral]) print(' ' + dihedral_name) fh.write(dihedral_name + '\n') print() sys.exit(0) # Print arguments print('\n === Arguments ===\n') for key, value in vars(args).items(): print('{:>12s}: {:s}'.format(key, str(value))) print('\n === Parameterizing %s ===\n' % args.filename) for method in methods: print(" === Fitting for %s ===\n" % method.name) # Create the molecule mol = FFMolecule(args.filename, method=method, netcharge=args.charge, rtf=rtf, prm=prm, qm=qm, outdir=args.outdir) mol.printReport() # Copy the molecule to preserve initial coordinates mol_orig = mol.copy() # Update B3LYP to B3LYP-D3 # TODO: this is silent and not documented stuff if qm.theory == 'B3LYP': qm.correction = 'D3' # Update basis sets # TODO: this is silent and not documented stuff if mol.netcharge < 0 and qm.solvent == 'vacuum': if qm.basis == '6-31G*': qm.basis = '6-31+G*' if qm.basis == 'cc-pVDZ': qm.basis = 'aug-cc-pVDZ' logger.info('Changing basis sets to %s' % qm.basis) # Minimize molecule if args.minimize: print('\n == Minimizing ==\n') mol.minimize() # Fit ESP charges if args.fit_charges: print('\n == Fitting ESP charges ==\n') # Set random number generator seed if args.seed: np.random.seed(args.seed) # Select the atoms with fixed charges fixed_atom_indices = [] for fixed_atom_name in args.fix_charge: if fixed_atom_name not in mol.name: raise ValueError( 'Atom %s is not found. Check --fix-charge arguments' % fixed_atom_name) for aton_index in range(mol.numAtoms): if mol.name[aton_index] == fixed_atom_name: fixed_atom_indices.append(aton_index) logger.info('Charge of atom %s is fixed to %f' % (fixed_atom_name, mol.charge[aton_index])) # Fit ESP charges _, qm_dipole = mol.fitCharges(fixed=fixed_atom_indices) # Copy the new charges to the original molecule mol_orig.charge[:] = mol.charge # Print dipoles logger.info('QM dipole: %f %f %f; %f' % tuple(qm_dipole)) mm_dipole = mol.getDipole() if np.all(np.isfinite(mm_dipole)): logger.info('MM dipole: %f %f %f; %f' % tuple(mm_dipole)) else: logger.warning( 'MM dipole cannot be computed. Check if elements are detected correctly.' ) # Fit dihedral angle parameters if args.fit_dihedral: print('\n == Fitting dihedral angle parameters ==\n') # Set random number generator seed if args.seed: np.random.seed(args.seed) # Get all rotatable dihedrals all_dihedrals = mol.getRotatableDihedrals() # Choose which dihedrals to fit dihedrals = [] all_dihedral_names = [ '-'.join(mol.name[dihedral]) for dihedral in all_dihedrals ] for dihedral_name in args.dihedral: if dihedral_name not in all_dihedral_names: raise ValueError( '%s is not recognized as a rotatable dihedral angle' % dihedral_name) dihedrals.append( all_dihedrals[all_dihedral_names.index(dihedral_name)]) dihedrals = dihedrals if len( dihedrals ) > 0 else all_dihedrals # Set default to all dihedral angles # Fit the parameters mol.fitDihedrals(dihedrals, args.optimize_dihedral) # Output the FF parameters print('\n == Writing results ==\n') mol.writeParameters(mol_orig) # Write energy file energyFile = os.path.join(mol.outdir, 'parameters', method.name, mol.output_directory_name(), 'energies.txt') printEnergies(mol, energyFile) logger.info('Write energy file: %s' % energyFile)
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