def test_write_xml_parameters_methanol_ions_energy(self): """ Test writing XML parameter files from Charmm parameter files, reading them back into OpenMM ForceField, and computing energy of methanol and NaCl """ params = openmm.OpenMMParameterSet.from_parameterset( pmd.charmm.CharmmParameterSet(get_fn('par_all36_cgenff.prm'), get_fn('top_all36_cgenff.rtf'), get_fn('toppar_water_ions.str')) # WARNING: contains duplicate water templates ) del params.residues['TP3M'] # Delete to avoid duplicate water template topologies ffxml_filename = get_fn('charmm_conv.xml', written=True) params.write(ffxml_filename, provenance=dict( OriginalFile='par_all36_cgenff.prm, top_all36_cgenff.rtf, toppar_water_ions.str', Reference='MacKerrell' ) ) forcefield = app.ForceField(ffxml_filename) # Parameterize methanol and ions in vacuum pdbfile = app.PDBFile(get_fn('methanol_ions.pdb')) system = forcefield.createSystem(pdbfile.topology, nonbondedMethod=app.NoCutoff) integrator = mm.VerletIntegrator(1.0*u.femtoseconds) context = mm.Context(system, integrator, CPU) context.setPositions(pdbfile.positions) ffxml_potential = context.getState(getEnergy=True).getPotentialEnergy() / u.kilocalories_per_mole del context, integrator # Compute energy via ParmEd reader psf = CharmmPsfFile(get_fn('methanol_ions.psf')) system = psf.createSystem(params) integrator = mm.VerletIntegrator(1.0*u.femtoseconds) context = mm.Context(system, integrator, CPU) context.setPositions(pdbfile.positions) parmed_potential = context.getState(getEnergy=True).getPotentialEnergy() / u.kilocalories_per_mole del context, integrator # Ensure potentials are almost equal self.assertAlmostEqual(ffxml_potential, parmed_potential)
def test_add_charmm_psf(self): """ Test adding CHARMM PSF file to ParmList """ parms = ParmList() ala3 = CharmmPsfFile(get_fn('ala3_solv.psf')) aaa = CharmmPsfFile(get_fn('ala_ala_ala.psf')) xaaa = CharmmPsfFile(get_fn('ala_ala_ala.psf.xplor')) aaaa = CharmmPsfFile(get_fn('ala_ala_ala_autopsf.psf')) parms.add_parm(ala3) parms.add_parm(aaa) parms.add_parm(xaaa) parms.add_parm(aaaa) # Check indexing self.assertIs(parms[0], ala3) self.assertIs(parms[1], aaa) self.assertIs(parms[2], xaaa) self.assertIs(parms[3], aaaa) # Test name indexing self.assertIs(parms[get_fn('ala3_solv.psf')], ala3) self.assertIs(parms[get_fn('ala_ala_ala.psf')], aaa) self.assertIs(parms[get_fn('ala_ala_ala.psf.xplor')], xaaa) self.assertIs(parms[get_fn('ala_ala_ala_autopsf.psf')], aaaa) # Check that the last parm added is active self.assertIs(parms.parm, parms[-1]) # Set a new active topology parms.set_new_active(0) self.assertIs(parms.parm, ala3) parms.set_new_active(get_fn('ala_ala_ala.psf')) self.assertIs(parms.parm, aaa)
def __init__(self, true_value=None, initial_value=None, n_increments=18, rj=True, sample_phase=False, continuous=False): self._param = CharmmParameterSet(get_fun('toy.str')) self._struct = CharmmPsfFile(get_fun('toy.psf')) self._pdb = app.PDBFile(get_fun('toy.pdb')) self._topology = md.load_psf(get_fun('toy.psf')) self.synthetic_energy = units.Quantity() self._positions = units.Quantity() self._platform = mm.Platform.getPlatformByName('Reference') # Replace ('CG331', 'CG321', 'CG321', 'CG331') torsion with true_value self._dih_type = ('CG331', 'CG321', 'CG321', 'CG331') original_torsion = self._param.dihedral_types[self._dih_type] if true_value is not None: if type(true_value) == DihedralTypeList: dih_tlist = true_value elif type(true_value) == DihedralType: dih_tlist = DihedralTypeList() dih_tlist.append(true_value) else: dih_tlist = self._randomize_dih_param(return_dih=True) self.true_value = copy.deepcopy(dih_tlist) self._param.dihedral_types[self._dih_type] = dih_tlist # parametrize toy self._struct.load_parameters(self._param, copy_parameters=False) self._struct.positions = self._pdb.positions # generate synthetic torsion scan self._torsion_scan(n_increments=n_increments) # initialize parameter if initial_value is not None: if type(initial_value) == DihedralTypeList: dih_tlist = initial_value if type(initial_value) == DihedralType: dih_tlist = DihedralTypeList() dih_tlist.append(initial_value) elif initial_value == 'cgenff': dih_tlist = original_torsion else: dih_tlist = self._randomize_dih_param(return_dih=True) self.initial_value = copy.deepcopy(dih_tlist) self._param.dihedral_types[self._dih_type] = dih_tlist # create torsionfit.TorsionScanSet torsions = np.zeros((len(self._positions), 4)) torsions[:] = [1, 2, 3, 4] direction = None steps = None self.scan_set = ScanSet.QMDataBase(positions=self._positions.value_in_unit(units.nanometers), topology=self._topology, structure=self._struct, torsions=torsions, steps=steps, directions=direction, qm_energies=self.synthetic_energy.value_in_unit(units.kilojoules_per_mole)) self.model = model.TorsionFitModel(param=self._param, frags=self.scan_set, platform=self._platform, param_to_opt=[self._dih_type], rj=rj, continuous_phase=continuous, sample_phase=sample_phase)
def test_create_simple_system(self): psf = CharmmPsfFile(get_fn("ala3_solv_drude.psf")) crd = CharmmCrdFile(get_fn("ala3_solv_drude.crd")) params = CharmmParameterSet( get_fn("toppar_drude_master_protein_2013e.str")) system = psf.createSystem(params)
def test_no_parameters(self): """ Test proper error handling when parameters not present """ parm = CharmmPsfFile(get_fn('ala_ala_ala.psf')) parm.rb_torsions.append(parm.dihedrals.pop()) parm.urey_bradleys.append(UreyBradley(parm.angles[0].atom1, parm.angles[0].atom3)) self.assertRaises(ParameterError, parm.omm_bond_force) self.assertRaises(ParameterError, parm.omm_angle_force) self.assertRaises(ParameterError, parm.omm_dihedral_force) self.assertRaises(ParameterError, parm.omm_rb_torsion_force) self.assertRaises(ParameterError, parm.omm_urey_bradley_force) self.assertRaises(ParameterError, parm.omm_improper_force) self.assertRaises(ParameterError, parm.omm_cmap_force)
def parse(filename): """ Read a CHARMM- or XPLOR-style PSF file Parameters ---------- filename : str Name of the file to parse Returns ------- psf_file : :class:`CharmmPsfFile` The PSF file instance with all information loaded """ return CharmmPsfFile(filename)
def test_turn_off_param(self): """ Test turning off parameters """ structure = CharmmPsfFile(get_fn('butane_charge_off.psf')) param = CharmmParameterSet(get_fn('par_all36_cgenff.prm'), get_fn('top_all36_cgenff.rtf')) par.turn_off_params(structure, param, bonds=True, angles=True, dihedral=True, urey_bradley=True, lj=True, copy=False) scan = get_fn('MP2_torsion_scan/') butane_scan = qmdb.parse_psi4_out(scan, get_fn('butane_charge_off.psf')) butane_scan.compute_energy(param) self.assertTrue((butane_scan.mm_energy.value_in_unit( unit.kilojoule_per_mole) == 0).all())
def parse_psi4_log(logfiles, structure): """ Parses output of psi4 torsion scan script :param logfiles: list of str logfiles of psi4 script :param structure: str Charmm psf file of structure :return: TorsionScanSet """ topology = md.load_psf(structure) structure = CharmmPsfFile(structure) positions = np.ndarray((0, topology.n_atoms, 3)) qm_energies = np.ndarray(0) torsions = np.ndarray((0, 4), dtype=int) directions = np.ndarray(0, dtype=int) angles = np.ndarray(0, dtype=float) if type(logfiles) != list: logfiles = [logfiles] for file in logfiles: qm = np.ndarray(0) fi = open(file, 'r') # check if log file is complete complete = False # complete flag for line in fi: if line.startswith('Relative'): complete = True fi.seek(0) section = None torsion = np.ndarray((1, 4), dtype=int) angle = np.ndarray(0, dtype=float) for line in fi: # Flag if structure is optimized optimized = False if line.startswith('Optimizer'): optimized = True # Store Dihedral and position of optimized structures fi.next() l = filter(None, fi.next().strip().split(' ')) dih = round(float(l[-2])) try: t = l[-6:-2] for i in range(len(t)): torsion[0][i] = int(t[i]) - 1 torsions = np.append(torsions, torsion, axis=0) except ValueError: pass angle = np.append(angle, dih) fi.next() pos = filter(None, re.split("[, \[\]]", fi.next().strip())) pos = [float(i) for i in pos] pos = np.asarray(pos).reshape((-1, 3)) # convert angstroms to nanometers positions = np.append(positions, pos[np.newaxis] * 0.1, axis=0) if not complete and optimized: # Find line that starts with energy for line in fi: if line.startswith('Energy'): energy = filter(None, line.strip().split(' '))[-1] # Convert to KJ/mol energy = float(energy) * 2625.5 qm = np.append(qm, energy) break if line.startswith('Relative'): section = 'Energy' fi.next() continue if section == 'Energy': line = filter(None, line.strip().split(' ')) if line != []: dih = round(float(line[0])) if dih in angle: # Only save energies of optimized structures qm_energies = np.append(qm_energies, float(line[-1])) if qm.size is not 0: qm = qm - min(qm) qm_energies = np.append(qm_energies, qm) fi.close() angles = np.append(angles, angle, axis=0) return QMDataBase(positions, topology, structure, torsions, directions, angles, qm_energies)
def parse_gauss(logfiles, structure): """ parses Guassian09 torsion-scan log file parameters ---------- logfiles: str of list of str Name of Guassian 09 torsion scan log file structure: charmm psf file returns ------- TorsionScanSet """ topology = md.load_psf(structure) structure = CharmmPsfFile(structure) positions = np.ndarray((0, topology.n_atoms, 3)) qm_energies = np.ndarray(0) torsions = np.ndarray((0, 4), dtype=int) directions = np.ndarray(0, dtype=int) steps = np.ndarray((0, 3), dtype=int) if type(logfiles) != list: logfiles = [logfiles] for file in (logfiles): direction = np.ndarray(1) torsion = np.ndarray((1, 4), dtype=int) step = np.ndarray((0, 3), dtype=int) index = (2, 12, -1) log = Gaussian(file) data = log.parse() # convert angstroms to nanometers positions = np.append(positions, data.atomcoords * 0.1, axis=0) # Only add qm energies for structures that converged (because cclib throws out those coords but not other info) qm_energies = np.append(qm_energies, (convertor( data.scfenergies[:len(data.atomcoords)], "eV", "kJmol-1") - min( convertor(data.scfenergies[:len(data.atomcoords)], "eV", "kJmol-1"))), axis=0) fi = open(file, 'r') for line in fi: if re.search(' Scan ', line): t = line.split()[2].split(',') t[0] = t[0][-1] t[-1] = t[-1][0] for i in range(len(t)): torsion[0][i] = (int(t[i]) - 1) if re.search('^ D ', line): d = line.split()[-1] if d[0] == '-': direction[0] = 0 elif d[0] == '1': direction[0] = 1 if re.search('Step', line): try: point = np.array(([int(line.rsplit()[j]) for j in index])) point = point[np.newaxis, :] step = np.append(step, point, axis=0) except: pass fi.close() # only add scan points from converged structures steps = np.append(steps, step[:len(data.atomcoords)], axis=0) for i in range(len(data.atomcoords)): torsions = np.append(torsions, torsion, axis=0) directions = np.append(directions, direction, axis=0) del log del data return QMDataBase(positions=positions, topology=topology, structure=structure, torsions=torsions, steps=steps, qm_energies=qm_energies, directions=directions)
def parse_psi4_out(oufiles_dir, structure, pattern="*.out"): """ Parse psi4 out files from distributed torsion scan (there are many output files, one for each structure) :param oufiles_dir: str path to directory where the psi4 output files are :param structure: str path to psf, mol2 or pbd file of structure :param pattern: str pattern for psi4 output file. Default is *.out :return: TorsionScanSet """ # Check extension of structure file if structure.endswith('psf'): topology = md.load_psf(structure) structure = CharmmPsfFile(structure) else: topology = md.load(structure).topology structure = parmed.load_file(structure) positions = np.ndarray((0, topology.n_atoms, 3)) qm_energies = np.ndarray(0) torsions = np.ndarray((0, 4), dtype=int) angles = np.ndarray(0, dtype=float) optimized = np.ndarray(0, dtype=bool) out_files = {} for path, subdir, files in os.walk(oufiles_dir): for name in files: if fnmatch(name, pattern): if name.startswith('timer'): continue name_split = name.split('_') try: torsion_angle = (name_split[1] + '_' + name_split[2] + '_' + name_split[3] + '_' + name_split[4]) except IndexError: warnings.warn( "Do you only have one torsion scan? The output files will be treated as one scan" ) torsion_angle = 'only_one_scan' try: out_files[torsion_angle] except KeyError: out_files[torsion_angle] = [] path = os.path.join(os.getcwd(), path, name) out_files[torsion_angle].append(path) # Sort files in increasing angles order for each torsion sorted_files = [] dih_angles = [] for tor in out_files: dih_angle = [] for file in out_files[tor]: dih_angle.append(int(file.split('_')[-1].split('.')[0])) sorted_files.append([ out_file for (angle, out_file) in sorted(zip(dih_angle, out_files[tor])) ]) dih_angle.sort() dih_angles.append(dih_angle) if not out_files: raise Exception( "There are no psi4 output files. Did you choose the right directory?" ) # Parse files for f in itertools.chain.from_iterable(sorted_files): torsion = np.ndarray((1, 4), dtype=int) fi = open(f, 'r') for line in fi: if line.startswith('dih_string'): t = line.strip().split('"')[1].split(' ')[:4] for i in range(len(t)): torsion[0][i] = int(t[i]) - 1 torsions = np.append(torsions, torsion, axis=0) fi.close() optimizer = True log = Psi(f) data = log.parse() try: data.optdone except AttributeError: optimizer = False warnings.warn("Warning: Optimizer failed for {}".format(f)) optimized = np.append(optimized, optimizer) positions = np.append(positions, data.atomcoords[-1][np.newaxis] * 0.1, axis=0) # Try MP2 energies. Otherwise take SCFenergies try: qm_energy = convertor(data.mpenergies[-1], "eV", "kJmol-1") except AttributeError: try: qm_energy = convertor(np.array([data.scfenergies[-1]]), "eV", "kJmol-1") except AttributeError: warnings.warn( "Warning: Check if the file terminated before completing SCF" ) qm_energy = np.array([np.nan]) qm_energies = np.append(qm_energies, qm_energy, axis=0) # Subtract lowest energy to find relative energies qm_energies = qm_energies - min(qm_energies) angles = np.asarray(list(itertools.chain.from_iterable(dih_angles))) return QMDataBase(positions=positions, topology=topology, structure=structure, torsions=torsions, angles=angles, qm_energies=qm_energies, optimized=optimized)
class ToyModel(object): """ Toy model of 4 carbons to test torsionfit Attributes ---------- synthetic_energy: simtk.units.Quantity((n_increments), unit=kilojoule/mole) true_value: parmed.topologyobjects.DihedralTypeList (the dihedral parameters used to calculate synthetic energy) initital_value: parmed.topologyobjects.DihedralTypeList (dihedral parameters used to initialize pymc model) torsion_scan: torsionfit.TorsionScanSet model: torsionfit.TorsionFitModel The default toy model has randomized true and initial dihedral parameters. This can be changed by passing a parmed.topologyobjects.DihedralTypeList to true_value and/or initial_value when initializing a model instance. """ def __init__(self, true_value=None, initial_value=None, n_increments=18, rj=True, sample_phase=False, continuous=False): self._param = CharmmParameterSet(get_fun('toy.str')) self._struct = CharmmPsfFile(get_fun('toy.psf')) self._pdb = app.PDBFile(get_fun('toy.pdb')) self._topology = md.load_psf(get_fun('toy.psf')) self.synthetic_energy = units.Quantity() self._positions = units.Quantity() self._platform = mm.Platform.getPlatformByName('Reference') # Replace ('CG331', 'CG321', 'CG321', 'CG331') torsion with true_value self._dih_type = ('CG331', 'CG321', 'CG321', 'CG331') original_torsion = self._param.dihedral_types[self._dih_type] if true_value is not None: if type(true_value) == DihedralTypeList: dih_tlist = true_value elif type(true_value) == DihedralType: dih_tlist = DihedralTypeList() dih_tlist.append(true_value) else: dih_tlist = self._randomize_dih_param(return_dih=True) self.true_value = copy.deepcopy(dih_tlist) self._param.dihedral_types[self._dih_type] = dih_tlist # parametrize toy self._struct.load_parameters(self._param, copy_parameters=False) self._struct.positions = self._pdb.positions # generate synthetic torsion scan self._torsion_scan(n_increments=n_increments) # initialize parameter if initial_value is not None: if type(initial_value) == DihedralTypeList: dih_tlist = initial_value if type(initial_value) == DihedralType: dih_tlist = DihedralTypeList() dih_tlist.append(initial_value) elif initial_value == 'cgenff': dih_tlist = original_torsion else: dih_tlist = self._randomize_dih_param(return_dih=True) self.initial_value = copy.deepcopy(dih_tlist) self._param.dihedral_types[self._dih_type] = dih_tlist # create torsionfit.TorsionScanSet torsions = np.zeros((len(self._positions), 4)) torsions[:] = [1, 2, 3, 4] direction = None steps = None self.scan_set = ScanSet.QMDataBase(positions=self._positions.value_in_unit(units.nanometers), topology=self._topology, structure=self._struct, torsions=torsions, steps=steps, directions=direction, qm_energies=self.synthetic_energy.value_in_unit(units.kilojoules_per_mole)) self.model = model.TorsionFitModel(param=self._param, frags=self.scan_set, platform=self._platform, param_to_opt=[self._dih_type], rj=rj, continuous_phase=continuous, sample_phase=sample_phase) def _spher2cart(self, r, theta, phi): """convert spherical to cartesian coordinates Paramters: r: bond length theta: bond angle phi: dihedral angle returns: cartesian coordinates """ x = r * np.sin(theta) * np.cos(phi) y = r * np.sin(theta) * np.sin(phi) z = r * np.cos(theta) return [x, y, z] def _torsion_scan(self, n_increments): """ Generate positions and energy for torsion scan Parameters: n_increments: int. how many points to scan """ n_increments = n_increments n_atoms = 4 phis = np.arange(-np.pi, +np.pi, np.pi/n_increments) positions = np.zeros((len(phis), n_atoms, 3)) # Get bond length in nm for bond in self._struct.bonds: if bond.atom1.type == bond.atom2.type: length = units.Quantity(value=bond.type.req * 0.1, unit=units.nanometers) # convert to nm # Get angle in radians for angle in self._struct.angles: if angle.atom1.type == angle.atom2.type: theta = units.Quantity(value=angle.type.theteq * (np.pi/180.0), unit=units.radians) atom1_coords = self._spher2cart(length.value_in_unit(units.nanometer), theta.value_in_unit(units.radian), phis[0]) for i, phi in enumerate(phis): atom3_coords = self._spher2cart(length.value_in_unit(units.nanometer), theta.value_in_unit(units.radian), phi) atom3_coords[-1] = abs(atom3_coords[-1]) + length._value positions[i] = [atom1_coords, [0.000, 0.000, 0.000], [0.000, 0.000, length._value], atom3_coords] self._positions = units.Quantity(value=positions, unit=units.nanometer) # put units back in # calculate energy self.synthetic_energy = units.Quantity(value=np.zeros((len(positions))), unit=units.kilojoules_per_mole) platform = mm.Platform.getPlatformByName('Reference') integrator = mm.VerletIntegrator(0.004*units.picoseconds) system = self._struct.createSystem() context = mm.Context(system, integrator, platform) for i, conf in enumerate(self._positions): context.setPositions(conf) state = context.getState(getEnergy=True) energy = state.getPotentialEnergy() self.synthetic_energy[i] = energy + units.Quantity(value=np.random.normal(0, 1.0), unit=units.kilojoules_per_mole) def _randomize_dih_param(self, return_dih=False): """ generates random dihedral parameters """ dih_tlist = DihedralTypeList() multiplicities = [1, 2, 3, 4, 6] terms = np.random.randint(1, 5+1) np.random.shuffle(multiplicities) for i in range(terms): k = np.random.uniform(0.0, 20.0) n = multiplicities[i] phase = np.random.randint(0, 1+1) if phase == 1: phase = 180 dih_tlist.append(DihedralType(k, n, phase, 1.00, 1.00)) self._param.dihedral_types[self._dih_type] = dih_tlist if return_dih: _dih_tlist = copy.deepcopy(dih_tlist) return _dih_tlist def save_torsion_values(self, filename=None): """ Parameters ---------- filename : str file to save torsion values. Default is None. If None, return np.array Returns ------- dih_param: np.array array containing torison parameters """ dih_param = np.ones(shape=(2, 6, 3)) * np.nan for i, dih in enumerate(self.true_value): dih_param[0, i, 0] = dih.per dih_param[0, i, 1] = dih.phi_k dih_param[0, i, 2] = dih.phase for j, dih in enumerate(self.initial_value): dih_param[1, j, 0] = dih.per dih_param[1, j, 1] = dih.phi_k dih_param[1, j, 2] = dih.phase if filename is not None: np.save(filename, dih_param) else: return dih_param @staticmethod def from_dih_params(filename=None, dih_params=None, rj=False, continuous=False, n_increments=13, sample_phase=False): """ Parameters ---------- filename : str name of file with serialized dihedral parameters rj : bool Flag if using reversible jump. Default it True continuous : bool Flag if sampling continuous phase. Default is False n_increments : int incermentation of torsion drive sample_phase : bool Flag if sampling phase. Default is False (K is allowed to go negative when sample_phase is False) Returns ------- ToyModel with true and initial value from saved file. """ if filename is None and dih_params is None: msg = 'You must provide either an npy file or a numpy array with true and initial values for the toy model' raise Exception(msg) if filename is not None: dih_params = np.load(filename) dih_tlist_true = DihedralTypeList() dih_tlist_init = DihedralTypeList() true = dih_params[0] init = dih_params[1] for dih in true: if not np.isnan(dih[0]): dih_tlist_true.append(DihedralType(per=dih[0], phi_k=dih[1], phase=dih[2])) for dih in init: if not np.isnan(dih[0]): dih_tlist_init.append(DihedralType(per=dih[0], phi_k=dih[1], phase=dih[2])) return ToyModel(true_value=dih_tlist_true, initial_value=dih_tlist_init, rj=rj, continuous=continuous, n_increments=n_increments, sample_phase=sample_phase)
pdb_solvate = app.PDBFile('../structure/butane_solvated.pdb') coords = pdb_solvate.positions min_crds = [coords[0][0], coords[0][1], coords[0][2]] max_crds = [coords[0][0], coords[0][1], coords[0][2]] for coord in coords: min_crds[0] = min(min_crds[0], coord[0]) min_crds[1] = min(min_crds[1], coord[1]) min_crds[2] = min(min_crds[2], coord[2]) max_crds[0] = max(max_crds[0], coord[0]) max_crds[1] = max(max_crds[1], coord[1]) max_crds[2] = max(max_crds[2], coord[2]) psf_solvate_1 = CharmmPsfFile('../structure/butane_solvated.psf') psf_solvate_1.box = (max_crds[0] - min_crds[0], max_crds[1] - min_crds[1], max_crds[2] - min_crds[2], 90.0, 90.0, 90.0) database['solvated']['structure_1'] = psf_solvate_1 psf_solvate_1.load_parameters(param_1, copy_parameters=False) system_solvate_1 = psf_solvate_1.createSystem( nonbondedMethod=app.PME, constraints=app.HBonds, nonbondedCutoff=12.0 * u.angstroms, switchDistance=10.0 * u.angstroms) database['solvated']['system_1'] = system_solvate_1 psf_solvate_2 = CharmmPsfFile('../structure/butane_solvated.psf') psf_solvate_2.box = (max_crds[0] - min_crds[0], max_crds[1] - min_crds[1], max_crds[2] - min_crds[2], 90.0, 90.0, 90.0) database['solvated']['structure_2'] = psf_solvate_2
def compare_energies(system_name, pdb_filename, psf_filename, ffxml_filenames, toppar_filenames, box_vectors_filename=None, system_kwargs=None, tolerance=1e-5, units=u.kilojoules_per_mole, write_serialized_xml=False): """ Compare energies between (pdb, psf, toppar) loaded via ParmEd and (pdb, ffxml) loaded by OpenMM ForceField Parameters ---------- system_name : str Name of the test system pdb_filename : str Name of PDB file that should contain CRYST entry and PDB format compliant CONECT records for HETATM residues. psf_filename : str CHARMM PSF file ffxml_filenames : list of str List of OpenMM ffxml files toppar_filenames : list of CHARMM toppar filenames to load into CharmmParameterSet List of CHARMM toppar files box_vectors_filename : str, optional, default=None If specified, read box vectors from a file like step2.1_waterbox.prm system_kwargs : dict, optional, default=None Keyword arguments to pass to CharmmPsfFile.createSystem() and ForceField.CreateSystem() when constructing System objects for energy comparison tolerance : float, optional, default=1e-5 Relative energy discrepancy tolerance units : simtk.unit.Unit Unit to use for energy comparison write_serialized_xml : bool, optional, default=False If True, will write out serialized System XML files for OpenMM systems to aid debugging. """ is_periodic = False if (system_kwargs is not None) and ('nonbondedMethod' in system_kwargs) and ( system_kwargs['nonbondedMethod'] in [app.CutoffPeriodic, app.PME]): is_periodic = True # Load PDB file pdbfile = app.PDBFile(pdb_filename) # Read box vectors if box_vectors_filename: box_vectors = read_box_vectors(box_vectors_filename) pdbfile.topology.setPeriodicBoxVectors(box_vectors) else: box_vectors = pdbfile.topology.getPeriodicBoxVectors() # Load CHARMM system through OpenMM openmm_toppar = app.CharmmParameterSet(*toppar_filenames) openmm_psf = app.CharmmPsfFile(psf_filename) # Set box vectors if is_periodic: openmm_psf.setBox(box_vectors[0][0], box_vectors[1][1], box_vectors[2][2]) openmm_system = openmm_psf.createSystem(openmm_toppar, **system_kwargs) openmm_structure = openmm.load_topology(openmm_psf.topology, openmm_system, xyz=pdbfile.positions) #openmm_energies = openmm.energy_decomposition_system(openmm_structure, openmm_system, nrg=units) #print('OpenMM CHARMM loader energy components : %s' % str(openmm_energies)) openmm_total_energy = compute_potential(openmm_system, pdbfile.positions) / units # Load CHARMM system through ParmEd parmed_toppar = CharmmParameterSet(*toppar_filenames) parmed_structure = CharmmPsfFile(psf_filename) #structure.load_parameters(toppar) parmed_structure.positions = pdbfile.positions # Set box vectors if is_periodic: parmed_structure.box = (box_vectors[0][0] / u.angstroms, box_vectors[1][1] / u.angstroms, box_vectors[2][2] / u.angstroms, 90, 90, 90) parmed_system = parmed_structure.createSystem(parmed_toppar, **system_kwargs) #parmed_energies = openmm.energy_decomposition_system(parmed_structure, parmed_system, nrg=units) #print('ParmEd CHARMM loader energy components : %s' % str(parmed_energies)) parmed_total_energy = compute_potential(parmed_system, pdbfile.positions) / units # Delete H-H bonds from waters and retreive updated topology and positions modeller = app.Modeller(openmm_psf.topology, pdbfile.positions) hhbonds = [ b for b in modeller.topology.bonds() if b[0].element == app.element.hydrogen and b[1].element == app.element.hydrogen ] modeller.delete(hhbonds) ffxml_topology = modeller.topology # OpenMM system with ffxml ff = app.ForceField(*ffxml_filenames) ffxml_system = ff.createSystem(ffxml_topology, **system_kwargs) ffxml_structure = openmm.load_topology(ffxml_topology, ffxml_system, xyz=pdbfile.positions) #ffxml_energies = openmm.energy_decomposition_system(ffxml_structure, ffxml_system, nrg=units) #print('ffxml energy components : %s' % str(ffxml_energies)) ffxml_total_energy = compute_potential(ffxml_system, pdbfile.positions) / units write_serialized_xml = True # DEBUG if write_serialized_xml: print('Writing serialized XML files...') write_serialized_system(system_name + '.charmm.system.xml', openmm_system) write_serialized_system(system_name + '.parmed.system.xml', parmed_system) write_serialized_system(system_name + '.openmm.system.xml', ffxml_system) print('-' * 100) print('') print('OpenMM CHARMM reader total energy: %14.3f' % openmm_total_energy) print('ParmEd CHARMM reader total energy: %14.3f' % parmed_total_energy) print('OPENMM ffxml total energy: %14.3f' % ffxml_total_energy) print('TOTAL ERROR: %14.3f' % (ffxml_total_energy - openmm_total_energy)) print('') print('-' * 100) # TODO : Automate comparison return # calc rel energies and assert rel_energies = [] for i, j in zip(ffxml_energies, parmed_energies): if i[0] != j[0]: raise Exception('Mismatch in energy tuples naming.') if abs(i[1]) > NEARLYZERO: rel_energies.append((i[0], abs((i[1] - j[1]) / i[1]))) else: if abs(j[1]) > NEARLYZERO: raise AssertionError( 'One of the CHARMM %s energies (%s) for %s is zero, ' 'while the corresponding OpenMM energy is non-zero' % (system_name, i[0], ffxml)) rel_energies.append((i[0], 0)) dihedrals_done = False for i in rel_energies: if i[0] != 'PeriodicTorsionForce': if i[1] > tolerance: raise AssertionError( '%s energies (%s, %f) outside of allowed tolerance (%f) for %s' % (system_name, i[0], i[1], tolerance, ffxml)) else: if not dihedrals_done: if i[1] > tolerance: raise AssertionError( '%s energies (%s, %f) outside of allowed tolerance (%f) for %s' % (system_name, i[0], i[1], tolerance, ffxml)) dihedrals_done = True else: #impropers if i[1] > improper_tolerance: raise AssertionError( '%s energies (%s-impropers, %f) outside of allowed tolerance (%f) for %s' % (system_name, i[0], i[1], improper_tolerance, ffxml)) # logging if not no_log: charmm_energies_log = dict() omm_energies_log = dict() rel_energies_log = dict() charmm_energies_log['ffxml_name'] = ffxml charmm_energies_log['test_system'] = system_name charmm_energies_log['data_type'] = 'CHARMM' charmm_energies_log['units'] = units omm_energies_log['ffxml_name'] = ffxml omm_energies_log['test_system'] = system_name omm_energies_log['data_type'] = 'OpenMM' omm_energies_log['units'] = units rel_energies_log['ffxml_name'] = ffxml rel_energies_log['test_system'] = system_name rel_energies_log['data_type'] = 'abs((CHARMM-OpenMM)/CHARMM)' dihedrals_done = False for item in amber_energies: if item[0] == 'PeriodicTorsionForce' and not dihedrals_done: charmm_energies_log['PeriodicTorsionForce_dihedrals'] = item[1] dihedrals_done = True elif item[0] == 'PeriodicTorsionForce' and dihedrals_done: charmm_energies_log['PeriodicTorsionForce_impropers'] = item[1] elif item[0] == 'CMMotionRemover': continue else: charmm_energies_log[item[0]] = item[1] dihedrals_done = False for item in omm_energies: if item[0] == 'PeriodicTorsionForce' and not dihedrals_done: omm_energies_log['PeriodicTorsionForce_dihedrals'] = item[1] dihedrals_done = True elif item[0] == 'PeriodicTorsionForce' and dihedrals_done: omm_energies_log['PeriodicTorsionForce_impropers'] = item[1] elif item[0] == 'CMMotionRemover': continue else: omm_energies_log[item[0]] = item[1] dihedrals_done = False for item in rel_energies: if item[0] == 'PeriodicTorsionForce' and not dihedrals_done: rel_energies_log['PeriodicTorsionForce_dihedrals'] = item[1] dihedrals_done = True elif item[0] == 'PeriodicTorsionForce' and dihedrals_done: rel_energies_log['PeriodicTorsionForce_impropers'] = item[1] elif item[0] == 'CMMotionRemover': continue else: rel_energies_log[item[0]] = item[1] logger.log(charmm_energies_log) logger.log(omm_energies_log) logger.log(rel_energies_log)
from __future__ import division, print_function import sys # OpenMM Imports import simtk.openmm as mm import simtk.openmm.app as app # ParmEd Imports from parmed.charmm import CharmmPsfFile, CharmmCrdFile, CharmmParameterSet from parmed.openmm import StateDataReporter from parmed import unit as u # Load the CHARMM files print('Loading CHARMM files...') params = CharmmParameterSet('BNZ_2_PRD.prm') ala5_gas = CharmmPsfFile('zero.psf') ala5_crds = app.PDBFile('A2B_gas.pdb')
CharmmParameterSet) from parmed.exceptions import CharmmWarning, ParameterError from parmed.openmm.utils import energy_decomposition from parmed import unit as u, openmm, load_file, UreyBradley from parmed.utils.six.moves import range from copy import copy from math import sqrt import unittest from utils import get_fn, TestCaseRelative, mm, app, has_openmm, CPU, Reference import warnings # Suppress warning output from bad psf file... sigh. warnings.filterwarnings('ignore', category=CharmmWarning) # System charmm_gas = CharmmPsfFile(get_fn('ala_ala_ala.psf')) charmm_gas_crds = load_file(get_fn('ala_ala_ala.pdb')) charmm_nbfix = CharmmPsfFile(get_fn('ala3_solv.psf')) charmm_nbfix_crds = CharmmCrdFile(get_fn('ala3_solv.crd')) charmm_nbfix.box = [3.271195e1, 3.299596e1, 3.300715e1, 90, 90, 90] # Parameter sets param22 = CharmmParameterSet(get_fn('top_all22_prot.inp'), get_fn('par_all22_prot.inp')) param36 = CharmmParameterSet(get_fn('par_all36_prot.prm'), get_fn('toppar_water_ions.str')) @unittest.skipUnless(has_openmm, "Cannot test without OpenMM") class TestCharmmFiles(TestCaseRelative): def test_gas_energy(self):
from parmed.openmm.utils import energy_decomposition from parmed import unit as u, openmm, load_file from parmed.utils.six.moves import range from copy import copy from math import sqrt import unittest from utils import get_fn, TestCaseRelative, mm, app, has_openmm, CPU, Reference import warnings # Suppress warning output from bad psf file... sigh. warnings.filterwarnings('ignore', category=CharmmWarning) # System charmm_gas = CharmmPsfFile(get_fn('ala_ala_ala.psf')) charmm_gas_crds = load_file(get_fn('ala_ala_ala.pdb')) charmm_nbfix = CharmmPsfFile(get_fn('ala3_solv.psf')) charmm_nbfix_crds = CharmmCrdFile(get_fn('ala3_solv.crd')) charmm_nbfix.box = [3.271195e1, 3.299596e1, 3.300715e1, 90, 90, 90] # Parameter sets param22 = CharmmParameterSet(get_fn('top_all22_prot.inp'), get_fn('par_all22_prot.inp')) param36 = CharmmParameterSet(get_fn('par_all36_prot.prm'), get_fn('toppar_water_ions.str')) @unittest.skipUnless(has_openmm, "Cannot test without OpenMM") class TestCharmmFiles(TestCaseRelative): def testGasEnergy(self): """ Compare OpenMM and CHARMM gas phase energies """ parm = charmm_gas
import parmed as pm from parmed.charmm import CharmmPsfFile, CharmmCrdFile, CharmmParameterSet print('Loading CHARMM files...') params = CharmmParameterSet('toppar/par_all36m_prot.prm', 'toppar/par_all36_lipid.prm') gram = CharmmPsfFile('../../simulations/themis_data/1jnoetaohh3o2.psf') for atom in gram.atoms: print(atom.name, atom.idx, atom.charge, atom.rmin, atom.epsilon)