def mol_to_parmed(mol): """ Convert MDT Molecule to parmed Structure Args: mol (moldesign.Molecule): Returns: parmed.Structure """ struc = parmed.Structure() struc.title = mol.name pmedatoms = [] for atom in mol.atoms: pmedatm = parmed.Atom(atomic_number=atom.atomic_number, name=atom.name, mass=atom.mass.value_in(u.dalton), number=utils.if_not_none(atom.pdbindex, -1)) pmedatm.xx, pmedatm.xy, pmedatm.xz = atom.position.value_in(u.angstrom) pmedatoms.append(pmedatm) struc.add_atom(pmedatm, resname=utils.if_not_none(atom.residue.resname, 'UNL'), resnum=utils.if_not_none(atom.residue.pdbindex, -1), chain=utils.if_not_none(atom.chain.name, '')) for bond in mol.bonds: struc.bonds.append( parmed.Bond(pmedatoms[bond.a1.index], pmedatoms[bond.a2.index], order=bond.order)) return struc
def test_assign_histidine(): fn = get_fn('4lzt/4lzt_h.pdb') parm = pmd.load_file(fn) his_residues = [ res.name for res in parm.residues if res.name in {'HIS', 'HIE', 'HID', 'HIP'} ] assert his_residues == ['HIS'] pdbfixer = AmberPDBFixer(parm) pdbfixer.assign_histidine() his_residues = [ res.name for res in pdbfixer.parm.residues if res.name in {'HIS', 'HIE', 'HID', 'HIP'} ] assert his_residues == ['HID'] # Do not rename to HIS if this residue does not # have hydrogen. parm2 = pmd.Structure() for resnum, resname in enumerate(['HIE', 'HID', 'HIS']): atom = pmd.Atom(name='C', atomic_number=6) parm2.add_atom(atom, resname, resnum=resnum, chain='A') fixer2 = AmberPDBFixer(parm2) fixer2.assign_histidine() assert ['HIE', 'HID', 'HIS'] == [res.name for res in fixer2.parm.residues]
def test_two_particle_vsite(self): """ Tests assignment of 2-particle virtual site """ struct = pmd.Structure() struct.add_atom(pmd.Atom(name='C', atomic_number=6), 'RES', 1) struct.add_atom(pmd.Atom(name='C', atomic_number=6), 'RES', 1) struct.add_atom(pmd.ExtraPoint(name='EP', atomic_number=0), 'RES', 1) struct.bond_types.append(pmd.BondType(10, 1.0)) struct.bond_types.append(pmd.BondType(10, 0.5)) struct.bond_types.claim() struct.bonds.append(pmd.Bond(struct[0], struct[1], type=struct.bond_types[0]) ) struct.bonds.append(pmd.Bond(struct[1], struct[2], type=struct.bond_types[1]) ) # This should be a two-particle virtual site struct.coordinates = [[0, 0, 0], [0, 0, 1], [0, 0, 1.5]] system = mm.System() system.addParticle(struct[0].mass) system.addParticle(struct[1].mass) system.addParticle(struct[2].mass) struct.omm_set_virtual_sites(system) # Make sure the third atom is a virtual site self.assertTrue(system.isVirtualSite(2)) self.assertIsInstance(system.getVirtualSite(2), mm.TwoParticleAverageSite)
def test_duplicate_angle_type(self): """ Tests handling of duplicate angle type in ParameterSet """ struct = pmd.Structure() struct.add_atom(pmd.Atom('CA', type='CX'), 'ALA', 1) struct.add_atom(pmd.Atom('CB', type='CY'), 'ALA', 1) struct.add_atom(pmd.Atom('CC', type='CZ'), 'ALA', 1) struct.add_atom(pmd.Atom('CD', type='CX'), 'GLY', 2) struct.add_atom(pmd.Atom('CE', type='CY'), 'GLY', 2) struct.add_atom(pmd.Atom('CF', type='CZ'), 'GLY', 2) struct.angle_types.append(pmd.AngleType(10.0, 120.0)) struct.angle_types.append(pmd.AngleType(11.0, 109.0)) struct.angle_types.claim() struct.angles.append( pmd.Angle(struct[0], struct[1], struct[2], type=struct.angle_types[0])) struct.angles.append( pmd.Angle(struct[3], struct[4], struct[5], type=struct.angle_types[1])) self.assertRaises( pmd.exceptions.ParameterError, lambda: pmd.ParameterSet. from_structure(struct, allow_unequal_duplicates=False))
def _write_pdb(xyzname, resname): bname = os.path.basename(xyzname) lines = [] with open(xyzname, "r") as f: lines = f.readlines()[2:] s = parmed.Structure() r = parmed.Residue(name=resname, number=1, list=s.residues) s.residues.append(r) for i, atom_line in enumerate(lines, 1): data = atom_line.strip().split() a = parmed.Atom(list=s.atoms, atomic_number=parmed.periodic_table.AtomicNum[data[0]], name=data[0] + "%d" % i) a.number = i a.xx = float(data[1]) a.xy = float(data[2]) a.xz = float(data[3]) r.add_atom(a) s.atoms.append(a) s.write_pdb(os.path.splitext(bname)[0] + ".pdb")
def __init__(self, parm=None): # TODO: make a copy? # Why not now? parm[:] will not correctly assign TER residue # self.parm = parm[:] if isinstance(parm, string_types): self.parm = parmed.load_file(parm) elif parm is None: self.parm = parmed.Structure() else: self.parm = parm
def get_structure_string(self): """Require `parmed` package. """ import parmed as pmd c = self._schrodinger_structure s = pmd.Structure() fsys = c.fsys_ct if hasattr(c, 'fsys_ct') else c for atom in fsys.atom: parm_atom = pmd.Atom(name=atom.pdbname.strip(), atomic_number=atom.atomic_number) s.add_atom(parm_atom, atom.pdbres.strip(), atom.resnum, chain=atom.chain) s.coordinates = fsys.getXYZ() return ParmEdStructure(s).get_structure_string()
def ethane_ua(self): ethane = parmed.Structure() a1 = parmed.topologyobjects.Atom(name="C", atomic_number=6) a2 = parmed.topologyobjects.Atom(name="C", atomic_number=6) ethane.add_atom(a1, resname="RES", resnum=1) ethane.add_atom(a2, resname="RES", resnum=1) bond_type = parmed.topologyobjects.BondType(1.0, 1.0) bond = parmed.topologyobjects.Bond(a1, a2, type=bond_type) ethane.bonds.append(bond) ethane.bond_types.append(bond_type) ethane.coordinates = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]]) return ethane
def test_urey_bradley_type(self): """ Tests handling getting urey-bradley types from Structure """ warnings.filterwarnings('error', category=pmd.exceptions.ParameterWarning) struct = pmd.Structure() struct.add_atom(pmd.Atom('CA', type='CX'), 'ALA', 1) struct.add_atom(pmd.Atom('CB', type='CY'), 'ALA', 1) struct.add_atom(pmd.Atom('CC', type='CZ'), 'ALA', 1) struct.add_atom(pmd.Atom('CD', type='CX'), 'GLY', 2) struct.add_atom(pmd.Atom('CE', type='CY'), 'GLY', 2) struct.add_atom(pmd.Atom('CF', type='CZ'), 'GLY', 2) struct.bond_types.append(pmd.BondType(100.0, 1.0)) struct.bond_types.claim() struct.bonds.extend([ pmd.Bond(struct[0], struct[1], type=struct.bond_types[0]), pmd.Bond(struct[1], struct[2], type=struct.bond_types[0]), pmd.Bond(struct[3], struct[4], type=struct.bond_types[0]), pmd.Bond(struct[4], struct[5], type=struct.bond_types[0]), ]) struct.angle_types.append(pmd.AngleType(10.0, 120.0)) struct.angle_types.append(pmd.AngleType(11.0, 109.0)) struct.angle_types.claim() struct.angles.append( pmd.Angle(struct[0], struct[1], struct[2], type=struct.angle_types[0])) struct.angles.append( pmd.Angle(struct[3], struct[4], struct[5], type=struct.angle_types[0])) struct.urey_bradley_types.append(pmd.BondType(150.0, 2.0)) struct.urey_bradley_types.claim() struct.urey_bradleys.extend([ pmd.UreyBradley(struct[0], struct[2], type=struct.urey_bradley_types[0]), pmd.UreyBradley(struct[3], struct[5], type=struct.urey_bradley_types[0]), ]) params = pmd.ParameterSet.from_structure(struct) self.assertEqual(len(params.urey_bradley_types), 2) for key, ubt in iteritems(params.urey_bradley_types): self.assertEqual(len(key), 3) self.assertEqual(ubt.req, 2.0) self.assertEqual(ubt.k, 150.0) warnings.filterwarnings('default', category=pmd.exceptions.ParameterWarning)
def createStructureFromResidue(residue): # Create ParmEd structure for residue. structure = parmed.Structure() for a in residue.atoms(): if a.element is None: atom = parmed.ExtraPoint(name=a.name) else: atom = parmed.Atom(atomic_number=a.element.atomic_number, name=a.name, mass=a.element.mass) structure.add_atom(atom, residue.name, residue.index, 'A') atommap[a] = atom for a1, a2 in topology.bonds(): structure.bonds.append(Bond(atommap[a1], atommap[a2])) return structure
def test_duplicate_bond_type(self): """ Tests handling of duplicate bond type in ParameterSet """ struct = pmd.Structure() struct.add_atom(pmd.Atom('CA', type='CX'), 'ALA', 1) struct.add_atom(pmd.Atom('CB', type='CY'), 'ALA', 1) struct.add_atom(pmd.Atom('CD', type='CX'), 'GLY', 2) struct.add_atom(pmd.Atom('CE', type='CY'), 'GLY', 2) struct.bond_types.append(pmd.BondType(10.0, 1.0)) struct.bond_types.append(pmd.BondType(11.0, 1.1)) struct.bond_types.claim() struct.bonds.append(pmd.Bond(struct[0], struct[1], type=struct.bond_types[0])) struct.bonds.append(pmd.Bond(struct[2], struct[3], type=struct.bond_types[1])) with self.assertRaises(pmd.exceptions.ParameterError): pmd.ParameterSet.from_structure(struct, allow_unequal_duplicates=False)
def _structure_from_residue(residue, parent_structure): """Convert a ParmEd Residue to an equivalent Structure.""" structure = pmd.Structure() for atom in residue.atoms: structure.add_atom(atom, resname=residue.name, resnum=residue.number) for bond in parent_structure.bonds: if bond.atom1 in residue.atoms and bond.atom2 in residue.atoms: structure.bonds.append(bond) idx_offset = min([a.idx for a in structure]) for atom in structure.atoms: atom._idx -= idx_offset return structure
def add_ghost_atom_to_pqr_from_atoms_center_of_mass(pqr_filename, atom_index_list, new_pqr_filename=None): """ Add a ghost atom to a PQR file at the location of the center of mass of the atoms listed. Parameters: ----------- pqr_filename : str The name of the PQR file where the ghost atom will be added into the file itself. atom_index_list : list A list of integers which are atom indices. The center of mass will be found for these atoms, and the ghost atom will be located at that center of mass. new_pqr_filename : str or None If None, the pqr_filename will be overwritten. Otherwise, the new PQR with the ghost atom will be written to this file. """ if new_pqr_filename is None: new_pqr_filename = pqr_filename pqr_struct = parmed.load_file(pqr_filename, skip_bonds=True) center_of_mass = np.array([[0., 0., 0.]]) total_mass = 0.0 for atom_index in atom_index_list: atom_pos = pqr_struct.coordinates[atom_index, :] atom_mass = pqr_struct.atoms[atom_index].mass center_of_mass += atom_mass * atom_pos total_mass += atom_mass center_of_mass = center_of_mass / total_mass ghost_atom = parmed.Atom(name="GHO", mass=0.0, charge=0.0, solvent_radius=0.0) ghost_structure = parmed.Structure() ghost_structure.add_atom(ghost_atom, "GHO", 1) ghost_structure.coordinates = np.array(center_of_mass) complex = pqr_struct + ghost_structure for residue in complex.residues: residue.chain = "" complex.save(new_pqr_filename, overwrite=True) ghost_index = len(complex.atoms) return ghost_index
def to_parmed(traj, all_coords=False): import parmed as pmd parm = pmd.Structure() for atom in traj.top.simplify().atoms: p_atom = pmd.Atom(name=atom.name, type=atom.type, atomic_number=atom.atomic_number, charge=atom.charge, mass=atom.mass) parm.add_atom(p_atom, resname=atom.resname, resnum=atom.resid) # chain=str(atom.molnum)) if all_coords: parm.coordinates = traj.xyz else: parm.coordinates = traj.xyz[0] parm.box = traj.top.box return parm
def to_parmed(self, title='', **kwargs): """Create a ParmEd Structure from a Compound. """ structure = pmd.Structure() structure.title = title if title else self.name atom_mapping = {} # For creating bonds below guessed_elements = set() for atom in self.particles(): atomic_number = None name = ''.join(char for char in atom.name if not char.isdigit()) try: atomic_number = AtomicNum[atom.name] except KeyError: element = element_by_name(atom.name) if name not in guessed_elements: warn('Guessing that "{}" is element: "{}"'.format( atom, element)) guessed_elements.add(name) else: element = atom.name atomic_number = atomic_number or AtomicNum[element] mass = Mass[element] pmd_atom = pmd.Atom(atomic_number=atomic_number, name=atom.name, mass=mass) pmd_atom.xx, pmd_atom.xy, pmd_atom.xz = atom.pos * 10 # Angstroms structure.add_atom(pmd_atom, resname='RES', resnum=1) atom_mapping[atom] = pmd_atom for atom1, atom2 in self.bonds(): bond = pmd.Bond(atom_mapping[atom1], atom_mapping[atom2]) structure.bonds.append(bond) box = self.boundingbox box_vector = np.empty(6) box_vector[3] = box_vector[4] = box_vector[5] = 90.0 for dim, val in enumerate(self.periodicity): if val: box_vector[dim] = val * 10 else: box_vector[dim] = box.lengths[dim] * 10 + 5 structure.box = box_vector return structure
def propane_ua(self): propane = parmed.Structure() a1 = parmed.topologyobjects.Atom(name="C", atomic_number=6) a2 = parmed.topologyobjects.Atom(name="C", atomic_number=6) a3 = parmed.topologyobjects.Atom(name="C", atomic_number=6) propane.add_atom(a1, resname="RES", resnum=1) propane.add_atom(a2, resname="RES", resnum=1) propane.add_atom(a3, resname="RES", resnum=1) bond_type = parmed.topologyobjects.BondType(1.5, 1.5) b1 = parmed.topologyobjects.Bond(a1, a2, type=bond_type) b2 = parmed.topologyobjects.Bond(a2, a3, type=bond_type) propane.bonds.append(b1) propane.bonds.append(b2) propane.bond_types.append(bond_type) propane.coordinates = np.array( [ [0.0, 0.1, 0.2], [1.0, 0.3, 0.1], [2.0, 0.4, 0.2] ] ) return propane
def process(self, mol, port): try: # Split the complex in components in order to apply the FF protein, ligand, water, excipients = oeommutils.split( mol, ligand_res_name=self.opt['ligand_res_name']) self.log.info( "\nComplex name: {}\nProtein atom numbers = {}\nLigand atom numbers = {}\n" "Water atom numbers = {}\nExcipients atom numbers = {}".format( mol.GetTitle(), protein.NumAtoms(), ligand.NumAtoms(), water.NumAtoms(), excipients.NumAtoms())) # Unique prefix name used to output parametrization files self.opt['prefix_name'] = mol.GetTitle() oe_mol_list = [] par_mol_list = [] # Apply FF to the Protein if protein.NumAtoms(): oe_mol_list.append(protein) protein_structure = utils.applyffProtein(protein, self.opt) par_mol_list.append(protein_structure) # Apply FF to the ligand if ligand.NumAtoms(): oe_mol_list.append(ligand) ligand_structure = utils.applyffLigand(ligand, self.opt) par_mol_list.append(ligand_structure) # Apply FF to water molecules if water.NumAtoms(): oe_mol_list.append(water) water_structure = utils.applyffWater(water, self.opt) par_mol_list.append(water_structure) # Apply FF to the excipients if excipients.NumAtoms(): excipient_structure = utils.applyffExcipients( excipients, self.opt) par_mol_list.append(excipient_structure) # The excipient order is set equal to the order in related # parmed structure to avoid possible atom index mismatching excipients = oeommutils.openmmTop_to_oemol( excipient_structure.topology, excipient_structure.positions, verbose=False) oechem.OEPerceiveBondOrders(excipients) oe_mol_list.append(excipients) # Build the overall Parmed structure complex_structure = parmed.Structure() for struc in par_mol_list: complex_structure = complex_structure + struc complx = oe_mol_list[0].CreateCopy() num_atom_system = complx.NumAtoms() for idx in range(1, len(oe_mol_list)): oechem.OEAddMols(complx, oe_mol_list[idx]) num_atom_system += oe_mol_list[idx].NumAtoms() if not num_atom_system == complex_structure.topology.getNumAtoms(): oechem.OEThrow.Fatal( "Parmed and OE topologies mismatch atom number error") complx.SetTitle(mol.GetTitle()) # Set Parmed structure box_vectors is_periodic = True try: vec_data = pack_utils.PackageOEMol.getData(complx, tag='box_vectors') vec = pack_utils.PackageOEMol.decodePyObj(vec_data) complex_structure.box_vectors = vec except: is_periodic = False self.log.warn( "System has been parametrize without periodic box vectors for vacuum simulation" ) # Attach the Parmed structure to the complex packed_complex = pack_utils.PackageOEMol.pack( complx, complex_structure) # Attach the reference positions to the complex ref_positions = complex_structure.positions packedpos = pack_utils.PackageOEMol.encodePyObj(ref_positions) packed_complex.SetData(oechem.OEGetTag('OEMDDataRefPositions'), packedpos) # Set atom serial numbers, Ligand name and HETATM flag # oechem.OEPerceiveResidues(packed_complex, oechem.OEPreserveResInfo_SerialNumber) for at in packed_complex.GetAtoms(): thisRes = oechem.OEAtomGetResidue(at) thisRes.SetSerialNumber(at.GetIdx()) if thisRes.GetName() == 'UNL': # thisRes.SetName("LIG") thisRes.SetHetAtom(True) oechem.OEAtomSetResidue(at, thisRes) if packed_complex.GetMaxAtomIdx( ) != complex_structure.topology.getNumAtoms(): raise ValueError( "OEMol complex and Parmed structure mismatch atom numbers") # Check if it is possible to create the OpenMM System if is_periodic: complex_structure.createSystem( nonbondedMethod=app.CutoffPeriodic, nonbondedCutoff=10.0 * unit.angstroms, constraints=app.HBonds, removeCMMotion=False) else: complex_structure.createSystem(nonbondedMethod=app.NoCutoff, constraints=app.HBonds, removeCMMotion=False) self.success.emit(packed_complex) except Exception as e: # Attach error message to the molecule that failed self.log.error(traceback.format_exc()) mol.SetData('error', str(e)) # Return failed mol self.failure.emit(mol) return
def specific_ff_to_residue( structure, forcefield_selection=None, residues=None, reorder_res_in_pdb_psf=False, boxes_for_simulation=1, ): """ Takes the mbuild Compound or mbuild Box structure and applies the selected force field to the corresponding residue via foyer. Note: a residue is defined as a molecule in this case, so it is not designed for applying a force field to a protein. Parameters ---------- structure: mbuild Compound object or mbuild Box object; The mBuild Compound object or mbuild Box object, which contains the molecules (or empty box) that will have the force field applied to them. forcefield_selection: str or dictionary, default=None Apply a forcefield to the output file by selecting a force field xml file with its path or by using the standard force field name provided the `foyer` package. Example dict for FF file: {'ETH' : 'oplsaa.xml', 'OCT': 'path_to file/trappe-ua.xml'} Example str for FF file: 'path_to file/trappe-ua.xml' Example dict for standard FF names : {'ETH' : 'oplsaa', 'OCT': 'trappe-ua'} Example str for standard FF names: 'trappe-ua' Example of a mixed dict with both : {'ETH' : 'oplsaa', 'OCT': 'path_to file/'trappe-ua.xml'} residues: list, [str, ..., str], default=None Labels of unique residues in the Compound. Residues are assigned by checking against Compound.name. Only supply residue names as 4 characters strings, as the residue names are truncated to 4 characters to fit in the psf and pdb file. reorder_res_in_pdb_psf: bool, default=False This option provides the ability to reorder the residues/molecules from the original structure's order. If True, the residues will be reordered as they appear in the residues variable. If False, the order will be the same as entered in the original structure. boxes_for_simulation: int [1, 2], default = 1 Gibbs (GEMC) or grand canonical (GCMC) ensembles are examples of where the boxes_for_simulation would be 2. Canonical (NVT) or isothermal–isobaric (NPT) ensembles are example with the boxes_for_simulation equal to 1. Note: the only valid options are 1 or 2. Returns ------- list, [structure, coulomb14scalar_dict, lj14_scalar_dict, residues_applied_list] structure: parmed.Structure parmed structure with applied force field coulomb14scalar_dict: dict a dictionary with the 1,4-colombic scalars for each residue (i.e., a different force field could on each residue) lj14_scalar_dict: dict a dictionary with the 1,4-LJ scalars for each residue (i.e., a different force field could on each residue) residues_applied_list: list list of residues (i.e., list of stings). These are all the residues in which the force field actually applied Notes ----- To write the NAMD/GOMC force field, pdb, psf, and force field (.inp) files, the residues and forcefields must be provided in a str or dictionary. If a dictionary is provided all residues must be specified to a force field if the boxes_for_simulation is equal to 1. Generating an empty box (i.e., pdb and psf files): Enter residues = [], but the accompanying structure must be an empty mb.Box. However, when doing this, the forcefield_selection must be supplied, or it will provide an error (i.e., forcefield_selection can not be equal to None). In this current FF/psf/pdb writer, a residue type is essentially a molecule type. Therefore, it can only correctly write systems where every bead/atom in the molecule has the same residue name, and the residue name is specific to that molecule type. For example: a protein molecule with many residue names is not currently supported, but is planned to be supported in the future. """ if has_foyer: from foyer import Forcefield from foyer.forcefields import forcefields else: print_error_message = ( "Package foyer is not installed. " "Please install it using conda install -c conda-forge foyer") raise ImportError(print_error_message) if not isinstance(structure, (Compound, mb.Box)): print_error_message = ("ERROR: The structure expected to be of type: " "{} or {}, received: {}".format( type(Compound()), type(mb.Box(lengths=[1, 1, 1])), type(structure), )) raise TypeError(print_error_message) print("forcefield_selection = " + str(forcefield_selection)) if forcefield_selection is None: print_error_message = ( "Please the force field selection (forcefield_selection) as a dictionary " "with all the residues specified to a force field " '-> Ex: {"Water" : "oplsaa", "OCT": "path/trappe-ua.xml"}, ' "Note: the file path must be specified the force field file " "or by using the standard force field name provided the `foyer` package." ) raise TypeError(print_error_message) elif forcefield_selection is not None and not isinstance( forcefield_selection, dict): print_error_message = ( "The force field selection (forcefield_selection) " "is not a dictionary. Please enter a dictionary " "with all the residues specified to a force field " '-> Ex: {"Water" : "oplsaa", "OCT": "path/trappe-ua.xml"}, ' "Note: the file path must be specified the force field file " "or by using the standard force field name provided the `foyer` package." ) raise TypeError(print_error_message) if residues is None or not isinstance(residues, list): print_error_message = ( "Please enter the residues in the Specific_FF_to_residue function." ) raise TypeError(print_error_message) if not isinstance(reorder_res_in_pdb_psf, bool): print_error_message = ( "Please enter the reorder_res_in_pdb_psf " "in the Specific_FF_to_residue function (i.e., True or False).") raise TypeError(print_error_message) print_error_message_for_boxes_for_simulatiion = ( "ERROR: Please enter boxes_for_simulation equal " "the integer 1 or 2.") if not isinstance(boxes_for_simulation, int): raise TypeError(print_error_message_for_boxes_for_simulatiion) elif isinstance(boxes_for_simulation, int) and boxes_for_simulation not in [ 1, 2, ]: raise ValueError(print_error_message_for_boxes_for_simulatiion) forcefield_keys_list = [] if forcefield_selection is not None: for res in forcefield_selection.keys(): forcefield_keys_list.append(res) ff_data = forcefield_selection if forcefield_keys_list == [] and len(residues) != 0: print_error_message = "The forcefield_selection variable are not provided, but there are residues provided." raise ValueError(print_error_message) elif forcefield_keys_list != [] and len(residues) == 0: print_error_message = ( "The residues variable is an empty list but there are " "forcefield_selection variables provided.") raise ValueError(print_error_message) user_entered_ff_with_path_dict = ( {} ) # True means user entered the path, False is a standard foyer FF with no path for z in range(0, len(forcefield_keys_list)): for res_i in range(0, len(residues)): if residues[res_i] == forcefield_keys_list[z]: if (os.path.splitext(ff_data[forcefield_keys_list[z]])[1] == ".xml" and len(residues) != 0): user_entered_ff_with_path_dict.update( {residues[res_i]: True}) elif (os.path.splitext(ff_data[forcefield_keys_list[z]])[1] == "" and len(residues) != 0): user_entered_ff_with_path_dict.update( {residues[res_i]: False}) else: print_error_message = ( r"Please make sure you are entering the correct " "foyer FF name and not a path to a FF file. " "If you are entering a path to a FF file, " "please use the forcefield_files variable with the " "proper XML extension (.xml).") raise ValueError(print_error_message) coulomb14scalar_dict = {} lj14_scalar_dict = {} for j in range(0, len(forcefield_keys_list)): residue_iteration = forcefield_keys_list[j] if user_entered_ff_with_path_dict[residue_iteration]: ff_for_residue_iteration = ff_data[residue_iteration] try: read_xlm_iteration = minidom.parse(ff_for_residue_iteration) except: print_error_message = ( "Please make sure you are entering the correct foyer FF path, " "including the FF file name.xml " "If you are using the pre-build FF files in foyer, " "only use the string name without any extension.") raise ValueError(print_error_message) elif not user_entered_ff_with_path_dict[residue_iteration]: ff_for_residue_iteration = ff_data[residue_iteration] ff_names_path_iteration = (forcefields.get_ff_path()[0] + "/xml/" + ff_for_residue_iteration + ".xml") try: read_xlm_iteration = minidom.parse(ff_names_path_iteration) except: print_error_message = ( "Please make sure you are entering the correct foyer FF name, or the " "correct file extension (i.e., .xml, if required).") raise ValueError(print_error_message) lj_coul_1_4_values = read_xlm_iteration.getElementsByTagName( "NonbondedForce") for Scalar in lj_coul_1_4_values: coulomb14scalar_dict.update({ residue_iteration: float(Scalar.getAttribute("coulomb14scale")) }) lj14_scalar_dict.update( {residue_iteration: float(Scalar.getAttribute("lj14scale"))}) # Check to see if it is an empty mbuild.Compound and set intial atoms to 0 # note empty mbuild.Compound will read 1 atoms but there is really noting there if isinstance(structure, Compound): if len(structure.children) == 0: # there are no real atoms in the Compound so the test fails. User should use mbuild.Box print_error_message = ( "ERROR: If you are not providing an empty box, " "you need to specify the atoms/beads as children in the mb.Compound. " "If you are providing and empty box, please do so by specifying and " "mbuild Box ({})".format(type(mb.Box(lengths=[1, 1, 1])))) raise TypeError(print_error_message) else: initial_no_atoms = len(structure.to_parmed().atoms) # calculate the initial number of atoms for later comparison if isinstance(structure, mb.Box): lengths = structure.lengths angles = structure.angles structure = mb.Compound() structure.box = mb.Box(lengths=lengths, angles=angles) initial_no_atoms = 0 # add the FF to the residues compound_box_infor = structure.to_parmed(residues=residues) new_structure = pmd.Structure() new_structure.box = compound_box_infor.box # prepare all compound and remove nested compounds no_layers_to_check_for_residues = 3 print_error_message_all_res_not_specified = ( "ERROR: All the residues are not specified, or " "the residues entered does not match the residues that " "were found and built for structure.") for j in range(0, no_layers_to_check_for_residues): new_compound_iter = mb.Compound() new_compound_iter.periodicity = structure.periodicity if structure.name in residues: if len(structure.children) == 0: warn( "Warning: This residue is the atom, and is a single atom., " + str(structure.name)) new_compound_iter.add(mb.compound.clone(structure)) elif len(structure.children) > 0: new_compound_iter.add(mb.compound.clone(structure)) else: for child in structure.children: if len(child.children) == 0: if child.name not in residues: raise ValueError( print_error_message_all_res_not_specified) else: new_compound_iter.add(mb.compound.clone(child)) elif len(child.children) > 0: if child.name in residues: new_compound_iter.add(mb.compound.clone(child)) else: for sub_child in child.children: if sub_child.name in residues: new_compound_iter.add( mb.compound.clone(sub_child)) else: if len(sub_child.children) == 0 and ( child.name not in residues): raise ValueError( print_error_message_all_res_not_specified ) structure = new_compound_iter residues_applied_list = [] residue_orig_order_list = [] for child in structure.children: if child.name not in residue_orig_order_list: residue_orig_order_list.append(child.name) for res_reorder_iter in range(0, len(residues)): if residues[res_reorder_iter] not in residue_orig_order_list: text_to_print_1 = ( "All the residues were not used from the forcefield_selection " "string or dictionary. There may be residues below other " "specified residues in the mbuild.Compound hierarchy. " "If so, all the highest listed residues pass down the force " "fields through the hierarchy. Alternatively, residues that " "are not in the structure may have been specified. ") text_to_print_2 = ( "Note: This warning will appear if you are using the CHARMM pdb and psf writers " + "2 boxes, and the boxes do not contain all the residues in each box." ) if boxes_for_simulation == 1: warn(text_to_print_1) raise ValueError(text_to_print_1) if boxes_for_simulation == 2: warn(text_to_print_1 + text_to_print_2) if not reorder_res_in_pdb_psf: residues = residue_orig_order_list elif reorder_res_in_pdb_psf: print( "INFO: the output file are being reordered in via the residues list's sequence." ) for i in range(0, len(residues)): children_in_iteration = False new_compound_iteration = mb.Compound() new_compound_iter.periodicity = structure.periodicity new_structure_iteration = pmd.Structure() new_structure_iteration.box = compound_box_infor.box for child in structure.children: if ff_data.get(child.name) is None: print_error_message = "ERROR: All residues are not specified in the force_field dictionary" raise ValueError(print_error_message) if child.name == residues[i]: children_in_iteration = True new_compound_iteration.add(mb.compound.clone(child)) if children_in_iteration: if user_entered_ff_with_path_dict[residues[i]]: ff_iteration = Forcefield(ff_data[residues[i]]) residues_applied_list.append(residues[i]) elif not user_entered_ff_with_path_dict[residues[i]]: ff_iteration = Forcefield(name=ff_data[residues[i]]) residues_applied_list.append(residues[i]) new_compound_iteration.box = None new_structure_iteration = ff_iteration.apply( new_compound_iteration, residues=[residues[i]]) new_structure = new_structure + new_structure_iteration structure = new_structure # calculate the final number of atoms final_no_atoms = len(structure.atoms) if final_no_atoms != initial_no_atoms: print_error_message = ( "ERROR: The initial number of atoms sent to the force field analysis is " "not the same as the final number of atoms analyzed. " "The initial number of atoms was {} and the final number of atoms was {}. " "Please ensure that all the residues names that are in the initial " "Compound are listed in the residues list " "(i.e., the residues variable).".format(initial_no_atoms, final_no_atoms)) raise ValueError(print_error_message) return [ structure, coulomb14scalar_dict, lj14_scalar_dict, residues_applied_list, ]
def test_no_bonds(self): with pytest.raises(ValueError): ConstrainedMolecule(parmed.Structure())
def to_parmed(off_system: "System") -> pmd.Structure: """Convert an OpenFF System to a ParmEd Structure""" structure = pmd.Structure() _convert_box(off_system.box, structure) if "Electrostatics" in off_system.handlers.keys(): has_electrostatics = True electrostatics_handler = off_system.handlers["Electrostatics"] else: has_electrostatics = False for topology_molecule in off_system.topology.topology_molecules: # type: ignore[union-attr] for atom in topology_molecule.atoms: atomic_number = atom.atomic_number element = pmd.periodic_table.Element[atomic_number] mass = pmd.periodic_table.Mass[element] structure.add_atom( pmd.Atom( atomic_number=atomic_number, mass=mass, ), resname="FOO", resnum=0, ) if "Bonds" in off_system.handlers.keys(): bond_handler = off_system.handlers["Bonds"] bond_type_map: Dict = dict() for pot_key, pot in bond_handler.potentials.items(): k = pot.parameters["k"].to(kcal_mol_a2).magnitude / 2 length = pot.parameters["length"].to(unit.angstrom).magnitude bond_type = pmd.BondType(k=k, req=length) bond_type_map[pot_key] = bond_type structure.bond_types.append(bond_type) for top_key, pot_key in bond_handler.slot_map.items(): idx_1, idx_2 = top_key.atom_indices bond_type = bond_type_map[pot_key] bond = pmd.Bond( atom1=structure.atoms[idx_1], atom2=structure.atoms[idx_2], type=bond_type, ) structure.bonds.append(bond) structure.bond_types.claim() if "Angles" in off_system.handlers.keys(): angle_handler = off_system.handlers["Angles"] angle_type_map: Dict = dict() for pot_key, pot in angle_handler.potentials.items(): k = pot.parameters["k"].to(kcal_mol_rad2).magnitude / 2 theta = pot.parameters["angle"].to(unit.degree).magnitude # TODO: Look up if AngleType already exists in struct angle_type = pmd.AngleType(k=k, theteq=theta) angle_type_map[pot_key] = angle_type structure.angle_types.append(angle_type) for top_key, pot_key in angle_handler.slot_map.items(): idx_1, idx_2, idx_3 = top_key.atom_indices angle_type = angle_type_map[pot_key] structure.angles.append( pmd.Angle( atom1=structure.atoms[idx_1], atom2=structure.atoms[idx_2], atom3=structure.atoms[idx_3], type=angle_type, )) structure.angle_types.append(angle_type) structure.angle_types.claim() # ParmEd treats 1-4 scaling factors at the level of each DihedralType, # whereas SMIRNOFF captures them at the level of the non-bonded handler, # so they need to be stored here for processing dihedrals vdw_14 = off_system.handlers["vdW"].scale_14 # type: ignore[attr-defined] if has_electrostatics: coul_14 = off_system.handlers[ "Electrostatics"].scale_14 # type: ignore[attr-defined] else: coul_14 = 1.0 vdw_handler = off_system.handlers["vdW"] if "ProperTorsions" in off_system.handlers.keys(): proper_torsion_handler = off_system.handlers["ProperTorsions"] proper_type_map: Dict = dict() for pot_key, pot in proper_torsion_handler.potentials.items(): k = pot.parameters["k"].to(kcal_mol).magnitude periodicity = pot.parameters["periodicity"] phase = pot.parameters["phase"].magnitude proper_type = pmd.DihedralType( phi_k=k, per=periodicity, phase=phase, scnb=1 / vdw_14, scee=1 / coul_14, ) proper_type_map[pot_key] = proper_type structure.dihedral_types.append(proper_type) for top_key, pot_key in proper_torsion_handler.slot_map.items(): idx_1, idx_2, idx_3, idx_4 = top_key.atom_indices dihedral_type = proper_type_map[pot_key] structure.dihedrals.append( pmd.Dihedral( atom1=structure.atoms[idx_1], atom2=structure.atoms[idx_2], atom3=structure.atoms[idx_3], atom4=structure.atoms[idx_4], type=dihedral_type, )) structure.dihedral_types.append(dihedral_type) key1 = TopologyKey(atom_indices=(idx_1, )) key4 = TopologyKey(atom_indices=(idx_4, )) vdw1 = vdw_handler.potentials[vdw_handler.slot_map[key1]] vdw4 = vdw_handler.potentials[vdw_handler.slot_map[key4]] sig1, eps1 = _lj_params_from_potential(vdw1) sig4, eps4 = _lj_params_from_potential(vdw4) sig = (sig1 + sig4) * 0.5 eps = (eps1 * eps4)**0.5 nbtype = pmd.NonbondedExceptionType(rmin=sig * 2**(1 / 6), epsilon=eps * vdw_14, chgscale=coul_14) structure.adjusts.append( pmd.NonbondedException(structure.atoms[idx_1], structure.atoms[idx_4], type=nbtype)) structure.adjust_types.append(nbtype) structure.dihedral_types.claim() structure.adjust_types.claim() # if False: # "ImroperTorsions" in off_system.term_collection.terms: # improper_term = off_system.term_collection.terms["ImproperTorsions"] # for improper, smirks in improper_term.smirks_map.items(): # idx_1, idx_2, idx_3, idx_4 = improper # pot = improper_term.potentials[improper_term.smirks_map[improper]] # # TODO: Better way of storing periodic data in generally, probably need to improve Potential # n = re.search(r"\d", "".join(pot.parameters.keys())).group() # k = pot.parameters["k" + n].m # kcal/mol # periodicity = pot.parameters["periodicity" + n].m # dimless # phase = pot.parameters["phase" + n].m # degree # # dihedral_type = pmd.DihedralType(per=periodicity, phi_k=k, phase=phase) # structure.dihedrals.append( # pmd.Dihedral( # atom1=structure.atoms[idx_1], # atom2=structure.atoms[idx_2], # atom3=structure.atoms[idx_3], # atom4=structure.atoms[idx_4], # type=dihedral_type, # ) # ) vdw_handler = off_system.handlers["vdW"] for pmd_idx, pmd_atom in enumerate(structure.atoms): top_key = TopologyKey(atom_indices=(pmd_idx, )) smirks = vdw_handler.slot_map[top_key] potential = vdw_handler.potentials[smirks] element = pmd.periodic_table.Element[pmd_atom.element] sigma, epsilon = _lj_params_from_potential(potential) atom_type = pmd.AtomType( name=element + str(pmd_idx + 1), number=pmd_idx, atomic_number=pmd_atom.atomic_number, mass=pmd.periodic_table.Mass[element], ) atom_type.set_lj_params(eps=epsilon, rmin=sigma * 2**(1 / 6) / 2) pmd_atom.atom_type = atom_type pmd_atom.type = atom_type.name pmd_atom.name = pmd_atom.type for pmd_idx, pmd_atom in enumerate(structure.atoms): if has_electrostatics: top_key = TopologyKey(atom_indices=(pmd_idx, )) partial_charge = electrostatics_handler.charges[ top_key] # type: ignore[attr-defined] unitless_ = partial_charge.to(unit.elementary_charge).magnitude pmd_atom.charge = float(unitless_) pmd_atom.atom_type.charge = float(unitless_) else: pmd_atom.charge = 0 # Assign dummy residue names, GROMACS will not accept empty strings for res in structure.residues: res.name = "FOO" structure.positions = off_system.positions.to( unit.angstrom).magnitude # type: ignore[attr-defined] for idx, pos in enumerate(structure.positions): structure.atoms[idx].xx = pos._value[0] structure.atoms[idx].xy = pos._value[1] structure.atoms[idx].xz = pos._value[2] return structure
def from_itp_mol2(path, mol_name): """ Fix mol2 files that have atom type put in both the atom name and atom type fields Parameters --------- path : str mol_name : str Notes ----- Will generate a new mol2 file with of the form {}_new.mol2 """ #itp_file = path + 'tip3p.itp' #itp_file = path + 'dspc.itp' itp_file = path + mol_name + '.itp' mol2_file = path + mol_name + '.mol2' itplines = open(itp_file,'r').readlines() itplines = itp_utils.remove_comments(itplines) mol2_file_out = path + mol_name + "_new.mol2" cmpd2 = mb.load(mol2_file) cmpd2.name=mol_name #parmed won't read this mol2 file in properly directly temp_parmed = pmd.Structure() temp_parmed = cmpd2.to_parmed(residues=[cmpd2.name]) atom_name_list = [] atom_type_list = [] #search for relavant info atoms_directive = itp_utils.find_directive('atoms', itplines) index = atoms_directive keep_iterating = True while keep_iterating == True: index += 1 if itplines[index].find('bonds') == -1: atom_info = itplines[index].split() if len(atom_info) >= 7: atom_name_list.append(atom_info[4]) atom_type_list.append(atom_info[1]) else: keep_iterating = False #set the properties in the parmed structure for i, atom in enumerate(temp_parmed): atom.name = atom_name_list[i] atom.type = atom_type_list[i] temp_parmed.save(mol2_file_out, overwrite=True)
def applyffExcipients(excipients, opt): """ This function applies the selected force field to the excipients Parameters: ----------- excipients: OEMol molecule The excipients molecules to parametrize opt: python dictionary The options used to parametrize the excipients Return: ------- excipient_structure: Parmed structure instance The parametrized excipient parmed structure """ # OpenMM topology and positions from OEMol topology, positions = oeommutils.oemol_to_openmmTop(excipients) # Try to apply the selected FF on the excipients forcefield = app.ForceField(opt['protein_forcefield']) # List of the unrecognized excipients unmatched_res_list = forcefield.getUnmatchedResidues(topology) # Unique unrecognized excipient names templates = set() for res in unmatched_res_list: templates.add(res.name) if templates: # Some excipients are not recognized oechem.OEThrow.Info("The following excipients are not recognized " "by the protein FF: {}" "\nThey will be parametrized by using the FF: {}".format(templates, opt['other_forcefield'])) # Create a bit vector mask used to split recognized from un-recognize excipients bv = oechem.OEBitVector(excipients.GetMaxAtomIdx()) bv.NegateBits() # Dictionary containing the name and the parmed structures of the unrecognized excipients unrc_excipient_structures = {} # Dictionary used to skip already selected unrecognized excipients and count them unmatched_excp = {} # Ordered list of the unrecognized excipients unmatched_res_order = [] for r_name in templates: unmatched_excp[r_name] = 0 hv = oechem.OEHierView(excipients) for chain in hv.GetChains(): for frag in chain.GetFragments(): for hres in frag.GetResidues(): r_name = hres.GetOEResidue().GetName() if r_name not in unmatched_excp: continue else: unmatched_res_order.append(r_name) if unmatched_excp[r_name]: # Test if we have selected the unknown excipient # Set Bit mask atms = hres.GetAtoms() for at in atms: bv.SetBitOff(at.GetIdx()) unmatched_excp[r_name] += 1 else: unmatched_excp[r_name] = 1 # Create AtomBondSet to extract from the whole excipient system # the current selected FF unknown excipient atms = hres.GetAtoms() bond_set = set() for at in atms: bv.SetBitOff(at.GetIdx()) bonds = at.GetBonds() for bond in bonds: bond_set.add(bond) atom_bond_set = oechem.OEAtomBondSet(atms) for bond in bond_set: atom_bond_set.AddBond(bond) # Create the unrecognized excipient OEMol unrc_excp = oechem.OEMol() if not oechem.OESubsetMol(unrc_excp, excipients, atom_bond_set): oechem.OEThrow.Fatal("Is was not possible extract the residue: {}".format(r_name)) # Charge the unrecognized excipient if not oequacpac.OEAssignCharges(unrc_excp, oequacpac.OEAM1BCCCharges(symmetrize=True)): oechem.OEThrow.Fatal("Is was not possible to " "charge the extract residue: {}".format(r_name)) # If GAFF or GAFF2 is selected as FF check for tleap command if opt['other_forcefield'] in ['GAFF', 'GAFF2']: ff_utils.ParamLigStructure(oechem.OEMol(), opt['other_forcefield']).checkTleap if opt['other_forcefield'] == 'SMIRNOFF': unrc_excp = oeommutils.sanitizeOEMolecule(unrc_excp) # Parametrize the unrecognized excipient by using the selected FF pmd = ff_utils.ParamLigStructure(unrc_excp, opt['other_forcefield'], prefix_name=opt['prefix_name']+'_'+r_name) unrc_excp_struc = pmd.parameterize() unrc_excp_struc.residues[0].name = r_name unrc_excipient_structures[r_name] = unrc_excp_struc # Recognized FF excipients pred_rec = oechem.OEAtomIdxSelected(bv) rec_excp = oechem.OEMol() oechem.OESubsetMol(rec_excp, excipients, pred_rec) if rec_excp.NumAtoms() > 0: top_known, pos_known = oeommutils.oemol_to_openmmTop(rec_excp) ff_rec = app.ForceField(opt['protein_forcefield']) try: omm_system = ff_rec.createSystem(top_known, rigidWater=False) rec_struc = parmed.openmm.load_topology(top_known, omm_system, xyz=pos_known) except: oechem.OEThrow.Fatal("Error in the recognised excipient parametrization") # Unrecognized FF excipients bv.NegateBits() pred_unrc = oechem.OEAtomIdxSelected(bv) unrc_excp = oechem.OEMol() oechem.OESubsetMol(unrc_excp, excipients, pred_unrc) # Unrecognized FF excipients coordinates oe_coord_dic = unrc_excp.GetCoords() unrc_coords = np.ndarray(shape=(unrc_excp.NumAtoms(), 3)) for at_idx in oe_coord_dic: unrc_coords[at_idx] = oe_coord_dic[at_idx] # It is important the order used to assemble the structures. In order to # avoid mismatch between the coordinates and the structures, it is convenient # to use the unrecognized residue order unmatched_res_order_count = [] i = 0 while i < len(unmatched_res_order): res_name = unmatched_res_order[i] for j in range(i+1, len(unmatched_res_order)): if unmatched_res_order[j] == res_name: continue else: break if i == (len(unmatched_res_order) - 1): num = 1 unmatched_res_order_count.append((res_name, num)) break else: num = j - i unmatched_res_order_count.append((res_name, num)) i = j # Merge all the unrecognized Parmed structure unrc_struc = parmed.Structure() for pair in unmatched_res_order_count: res_name = pair[0] nums = pair[1] unrc_struc = unrc_struc + nums*unrc_excipient_structures[res_name] # Set the unrecognized coordinates unrc_struc.coordinates = unrc_coords # Set the parmed excipient structure merging # the unrecognized and recognized parmed # structures together if rec_excp.NumAtoms() > 0: excipients_structure = unrc_struc + rec_struc else: excipients_structure = unrc_struc return excipients_structure else: # All the excipients are recognized by the selected FF omm_system = forcefield.createSystem(topology, rigidWater=False) excipients_structure = parmed.openmm.load_topology(topology, omm_system, xyz=positions) return excipients_structure
def test_ep_exceptions(self): """ Test Nonbonded exception handling with virtual sites """ # Analyze the exception parameters for bonding pattern # # E1 -- A1 -- A2 -- A3 -- A4 -- A5 -- E5 # | | | # E2 E3 E4 struct = pmd.Structure() ep1 = ExtraPoint(name='E1', type='EP', atomic_number=0, weights=[1, 2]) ep2 = ExtraPoint(name='E2', type='EP', atomic_number=0) ep3 = ExtraPoint(name='E3', type='EP', atomic_number=0) ep4 = ExtraPoint(name='E4', type='EP', atomic_number=0) ep5 = ExtraPoint(name='E5', type='EP', atomic_number=0) self.assertIs(ep1.parent, None) self.assertEqual(ep1.bond_partners, []) self.assertEqual(ep1.angle_partners, []) self.assertEqual(ep1.dihedral_partners, []) self.assertEqual(ep1.tortor_partners, []) self.assertEqual(ep1.exclusion_partners, []) a1 = pmd.Atom(name='A1', type='AX', charge=0.1, atomic_number=6) a2 = pmd.Atom(name='A2', type='AY', charge=0.1, atomic_number=6) a3 = pmd.Atom(name='A3', type='AZ', charge=0.1, atomic_number=7) a4 = pmd.Atom(name='A4', type='AX', charge=0.1, atomic_number=6) a5 = pmd.Atom(name='A5', type='AY', charge=0.1, atomic_number=6) a1.rmin = a2.rmin = a3.rmin = a4.rmin = a5.rmin = 0.5 a1.epsilon = a2.epsilon = a3.epsilon = a4.epsilon = a5.epsilon = 1.0 bond_type = pmd.BondType(10.0, 1.0) bond_type2 = pmd.BondType(10.0, 2.0) bond_type3 = pmd.BondType(10.0, 0.5) bond_type4 = pmd.BondType(10.0, math.sqrt(2)) angle_type = pmd.AngleType(10.0, 90) dihedral_type = pmd.DihedralType(10.0, 2, 0) struct.add_atom(a1, 'RES', 1) struct.add_atom(a2, 'RES', 1) struct.add_atom(a3, 'RES', 1) struct.add_atom(a4, 'RES', 1) struct.add_atom(a5, 'RES', 1) struct.add_atom(ep1, 'RES', 1) struct.add_atom(ep2, 'RES', 1) struct.add_atom(ep3, 'RES', 1) struct.add_atom(ep4, 'RES', 1) struct.add_atom(ep5, 'RES', 1) struct.bonds.extend([ pmd.Bond(a1, ep1, type=bond_type), pmd.Bond(ep2, a2, type=bond_type), pmd.Bond(a3, ep3, type=bond_type3), pmd.Bond(a4, ep4, type=bond_type) ]) struct.bonds.extend([ pmd.Bond(a1, a2, type=bond_type), pmd.Bond(a4, a3, type=bond_type4), pmd.Bond(a3, a2, type=bond_type4), pmd.Bond(a4, a5, type=bond_type2), pmd.Bond(a5, ep5, type=bond_type) ]) struct.angles.extend([ pmd.Angle(a1, a2, a3, type=angle_type), pmd.Angle(a2, a3, a4, type=angle_type), pmd.Angle(a3, a4, a5, type=angle_type) ]) struct.dihedrals.extend([ pmd.Dihedral(a1, a2, a3, a4, type=dihedral_type), pmd.Dihedral(a2, a3, a4, a5, type=dihedral_type) ]) struct.bond_types.extend( [bond_type, bond_type3, bond_type2, bond_type4]) struct.angle_types.append(angle_type) struct.dihedral_types.append(dihedral_type) # Test exclusions now a1.exclude(a5) system = struct.createSystem()
def reparm(ligands, base): print( '**Running reparameterization of ligand(s) using open force fields\'s SMIRNOFF with openff 2.0.0**' ) # Load already parm'd system in_prmtop = base + '.prmtop' in_crd = base + '.inpcrd' # Create parmed strucuture orig_structure = parmed.amber.AmberParm(in_prmtop, in_crd) # Split orig_stucuture into unique structure instances e.g. protein, water, ligand, etc. pieces = orig_structure.split() for piece in pieces: # TODO: Figure out how to know which piece is which print(f"There are {len(piece[1])} instance(s) of {piece[0]}") # Generate an openff topology for the ligand # Openff Molecule does not support mol2 so conversion is needed ligs_w_sdf = [] for ligand in ligands: obabel[ligand[0], '-O', util.get_base(ligand[0]) + '.sdf']() ligs_w_sdf.append( (ligand[0], ligand[1], util.get_base(ligand[0]) + '.sdf')) # Keep track of ligands that were successfully reparmed so we know to skip them when putting the pieces back together reparmed_pieces = [] complex_structure = parmed.Structure() force_field = ForceField("openff_unconstrained-2.0.0.offxml") for lig in ligs_w_sdf: # Set up openff topology ligand_off_molecule = Molecule(lig[2]) ligand_pdbfile = PDBFile(lig[0]) ligand_off_topology = Topology.from_openmm( ligand_pdbfile.topology, unique_molecules=[ligand_off_molecule], ) # Parameterizing the ligand # Find ligand "piece", reparm, add to the new structure for piece in pieces: new_ligand_structure = None # TODO: Figure out how to know which piece is which if (ligand_off_molecule.n_atoms == len(piece[0].atoms)): if (ligand_off_molecule.n_bonds == len(piece[0].bonds)): if ([ atom.atomic_number for atom in ligand_off_molecule.atoms ] == [atom.element for atom in piece[0].atoms]): print('Found ligand piece', piece) try: # Since the method of matching the piece to ligand is imperfect, ligands that are isomers could mess things up. # So try any piece that matches and see if we get an error print('Reparameterizing ligand using SMIRNOFF') ligand_system = force_field.create_openmm_system( ligand_off_topology) new_ligand_structure = parmed.openmm.load_topology( ligand_off_topology.to_openmm(), ligand_system, xyz=piece[0].positions, ) # A quick check to make sure things were not messed up during param if check_discrepencies(new_ligand_structure, piece): # Add the newly parameterized ligand the complex structure reparmed_pieces.append(piece) new_ligand_structure *= len(piece[1]) complex_structure += parmed.amber.AmberParm.from_structure( new_ligand_structure) break except: pass # Stick all the pieces back together for piece in pieces: if (piece not in reparmed_pieces): curr_structure = parmed.Structure() curr_structure += piece[0] curr_structure *= len(piece[1]) complex_structure += parmed.amber.AmberParm.from_structure( curr_structure) # print("Unique atom names:",sorted(list({atom.atom_type.name for atom in complex_structure})),) # print("Number of unique atom types:", len({atom.atom_type for atom in complex_structure})) # print("Number of unique epsilons:", len({atom.epsilon for atom in complex_structure})) # print("Number of unique sigmas:", len({atom.sigma for atom in complex_structure})) # # Copy over the original coordinates and box vectors complex_structure.coordinates = orig_structure.coordinates complex_structure.box_vectors = orig_structure.box_vectors # Save the newly parameterized system complex_structure.save(base + ".prmtop", overwrite=True) complex_structure.save(base + ".inpcrd", overwrite=True)
def convert(self, obj): """Write selection at current trajectory frame to :class:`~parmed.structure.Structure`. Parameters ----------- obj : AtomGroup or Universe or :class:`Timestep` """ try: import parmed as pmd except ImportError: raise ImportError('ParmEd is required for ParmEdConverter but ' 'is not installed. Try installing it with \n' 'pip install parmed') try: # make sure to use atoms (Issue 46) ag_or_ts = obj.atoms except AttributeError: if isinstance(obj, base.Timestep): raise ValueError("Writing Timesteps to ParmEd " "objects is not supported") else: raise_from(TypeError("No atoms found in obj argument"), None) # Check for topology information missing_topology = [] try: names = ag_or_ts.names except (AttributeError, NoDataError): names = itertools.cycle(('X', )) missing_topology.append('names') try: resnames = ag_or_ts.resnames except (AttributeError, NoDataError): resnames = itertools.cycle(('UNK', )) missing_topology.append('resnames') if missing_topology: warnings.warn( "Supplied AtomGroup was missing the following attributes: " "{miss}. These will be written with default values. " "Alternatively these can be supplied as keyword arguments." "".format(miss=', '.join(missing_topology))) try: positions = ag_or_ts.positions except: positions = [None] * ag_or_ts.n_atoms try: velocities = ag_or_ts.velocities except: velocities = [None] * ag_or_ts.n_atoms atom_kwargs = [] for atom, name, resname, xyz, vel in zip(ag_or_ts, names, resnames, positions, velocities): akwargs = {'name': name} chain_seg = {'segid': atom.segid} for attrname in ('mass', 'charge', 'type', 'altLoc', 'tempfactor', 'occupancy', 'gbscreen', 'solventradius', 'nbindex', 'rmin', 'epsilon', 'rmin14', 'epsilon14', 'id'): try: akwargs[MDA2PMD.get(attrname, attrname)] = getattr(atom, attrname) except AttributeError: pass try: el = atom.element.lower().capitalize() akwargs['atomic_number'] = SYMB2Z[el] except (KeyError, AttributeError): try: tp = atom.type.lower().capitalize() akwargs['atomic_number'] = SYMB2Z[tp] except (KeyError, AttributeError): pass try: chain_seg['chain'] = atom.chainID except AttributeError: pass try: chain_seg['inscode'] = atom.icode except AttributeError: pass atom_kwargs.append( (akwargs, resname, atom.resid, chain_seg, xyz, vel)) struct = pmd.Structure() for akwarg, resname, resid, kw, xyz, vel in atom_kwargs: atom = pmd.Atom(**akwarg) if xyz is not None: atom.xx, atom.xy, atom.xz = xyz if vel is not None: atom.vx, atom.vy, atom.vz = vel atom.atom_type = pmd.AtomType( akwarg['name'], None, akwarg['mass'], atomic_number=akwargs.get('atomic_number')) struct.add_atom(atom, resname, resid, **kw) try: struct.box = ag_or_ts.dimensions except AttributeError: struct.box = None if hasattr(ag_or_ts, 'universe'): atomgroup = { atom: index for index, atom in enumerate(list(ag_or_ts)) } get_atom_indices = functools.partial(get_indices_from_subset, atomgroup=atomgroup, universe=ag_or_ts.universe) else: get_atom_indices = lambda x: x # bonds try: params = ag_or_ts.bonds.atomgroup_intersection(ag_or_ts, strict=True) except AttributeError: pass else: for p in params: atoms = [ struct.atoms[i] for i in map(get_atom_indices, p.indices) ] try: for obj in p.type: bond = pmd.Bond(*atoms, type=obj.type, order=obj.order) struct.bonds.append(bond) if isinstance(obj.type, pmd.BondType): struct.bond_types.append(bond.type) bond.type.list = struct.bond_types except (TypeError, AttributeError): order = p.order if p.order is not None else 1 btype = getattr(p.type, 'type', None) bond = pmd.Bond(*atoms, type=btype, order=order) struct.bonds.append(bond) if isinstance(bond.type, pmd.BondType): struct.bond_types.append(bond.type) bond.type.list = struct.bond_types # dihedrals try: params = ag_or_ts.dihedrals.atomgroup_intersection(ag_or_ts, strict=True) except AttributeError: pass else: for p in params: atoms = [ struct.atoms[i] for i in map(get_atom_indices, p.indices) ] try: for obj in p.type: imp = getattr(obj, 'improper', False) ign = getattr(obj, 'ignore_end', False) dih = pmd.Dihedral(*atoms, type=obj.type, ignore_end=ign, improper=imp) struct.dihedrals.append(dih) if isinstance(dih.type, pmd.DihedralType): struct.dihedral_types.append(dih.type) dih.type.list = struct.dihedral_types except (TypeError, AttributeError): btype = getattr(p.type, 'type', None) imp = getattr(p.type, 'improper', False) ign = getattr(p.type, 'ignore_end', False) dih = pmd.Dihedral(*atoms, type=btype, improper=imp, ignore_end=ign) struct.dihedrals.append(dih) if isinstance(dih.type, pmd.DihedralType): struct.dihedral_types.append(dih.type) dih.type.list = struct.dihedral_types for param, pmdtype, trackedlist, typelist, clstype in ( ('ureybradleys', pmd.UreyBradley, struct.urey_bradleys, struct.urey_bradley_types, pmd.BondType), ('angles', pmd.Angle, struct.angles, struct.angle_types, pmd.AngleType), ('impropers', pmd.Improper, struct.impropers, struct.improper_types, pmd.ImproperType), ('cmaps', pmd.Cmap, struct.cmaps, struct.cmap_types, pmd.CmapType)): try: params = getattr(ag_or_ts, param) values = params.atomgroup_intersection(ag_or_ts, strict=True) except AttributeError: pass else: for v in values: atoms = [ struct.atoms[i] for i in map(get_atom_indices, v.indices) ] try: for parmed_obj in v.type: p = pmdtype(*atoms, type=parmed_obj.type) trackedlist.append(p) if isinstance(p.type, clstype): typelist.append(p.type) p.type.list = typelist except (TypeError, AttributeError): vtype = getattr(v.type, 'type', None) p = pmdtype(*atoms, type=vtype) trackedlist.append(p) if isinstance(p.type, clstype): typelist.append(p.type) p.type.list = typelist return struct
def patch(self, top_string, crd_string): INTOP = "in.top" INRST = "in.rst" OUTTOP = "out.top" OUTRST = "out.rst" with util.in_temp_dir(): with open(INTOP, "wt") as outfile: outfile.write(top_string) with open(INRST, "wt") as outfile: outfile.write(crd_string) base = pmd.load_file(INTOP) crd = pmd.load_file(INRST) base.coordinates = crd.coordinates # create a new structure to add our dummy atoms to parm = pmd.Structure() # add in atom type for our dummy particles atype = pmd.AtomType("SDUM", 0, mass=12.0, charge=0.0) atype.set_lj_params(0.0, 0.0) for i in range(self.n_tensors): a1 = pmd.Atom( name="S1", atomic_number=atype.atomic_number, type=str(atype), charge=atype.charge, mass=atype.mass, solvent_radius=1.0, screen=0.5, ) a1.atom_type = atype a2 = pmd.Atom( name="S2", atomic_number=atype.atomic_number, type=str(atype), charge=atype.charge, mass=atype.mass, solvent_radius=1.0, screen=0.5, ) a2.atom_type = atype parm.add_atom(a1, resname="SDM", resnum=i) parm.add_atom(a2, resname="SDM", resnum=i) # we add noise here because we'll get NaN if the particles ever # end up exactly on top of each other parm.positions = np.zeros((2 * self.n_tensors, 3)) # combine the old system with the new dummy atoms comb = base + parm last_index = comb.residues[-1].idx self.resids = list(range(last_index - self.n_tensors + 2, last_index + 2)) comb.write_parm(OUTTOP) comb.write_rst7(OUTRST) with open(OUTTOP, "rt") as infile: top_string = infile.read() with open(OUTRST, "rt") as infile: crd_string = infile.read() return top_string, crd_string