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_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 _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 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 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_atom_serialization(self): """ Tests the serialization of Atom """ atom = pmd.Atom( atomic_number=random.randint(1, 100), name=random.choice(uppercase) + random.choice(uppercase), type=random.choice(uppercase) + random.choice(uppercase), charge=random.random() * 2 - 1, mass=random.random() * 30 + 1, nb_idx=random.randint(1, 20), solvent_radius=random.random() * 2, screen=random.random() * 2, tree='M', join=random.random() * 2, irotat=random.random(), occupancy=random.random(), bfactor=random.random() * 10, altloc=random.choice(uppercase), rmin=random.random() * 2, epsilon=random.random() / 2, rmin14=random.random() * 2, epsilon14=random.random() / 2) atom.xx, atom.xy, atom.xz = (random.random() * 100 - 50 for i in range(3)) atom.number = random.randint(1, 100) atom.vx, atom.vy, atom.vz = (random.random() * 100 - 50 for i in range(3)) atom.multipoles = np.random.rand(10) * 10 fobj = BytesIO() pickle.dump(atom, fobj) fobj.seek(0) unpickled = pickle.load(fobj) self.assertIsInstance(unpickled, pmd.Atom) self._equal_atoms(unpickled, atom)
def _add_particles(self, topol): err_msg = 'Unknown spin label type {{}}. Allowed values are: {}' err_msg = err_msg.format(', '.join(self.ALLOWED_TYPES)) # we use the same radius and screen as for oxygen if not self.explicit: radius, screen = self._find_radius_and_screen(topol) # find all the unique types of spin labels types = set(self.params.values()) for key in self.params: if self.params[key] not in self.ALLOWED_TYPES: raise ValueError(err_msg.format(self.params[key])) # create the particle atom = pmd.Atom(None, 8, 'OND', 'OND', 0.0, 16.00) if not self.explicit: atom.solvent_radius = radius atom.screen = screen # add to system topol.add_atom_to_residue(atom, topol.residues[key - 1]) # find the other atoms ca = topol.view[':{},@CA'.format(key)].atoms[0] cb = topol.view[':{},@CB'.format(key)].atoms[0] n = topol.view[':{},@N'.format(key)].atoms[0] # set position ca_pos = np.array((ca.xx, ca.xy, ca.xz)) n_pos = np.array((n.xx, n.xy, n.xz)) cb_pos = np.array((cb.xx, cb.xy, cb.xz)) direction = (ca_pos - cb_pos) / np.linalg.norm(ca_pos - cb_pos) new_pos = (ca_pos - self.bond_params[self.params[key]][1].value_in_unit(u.angstrom) * direction + ca_pos - n_pos) atom.xx = new_pos[0] atom.xy = new_pos[1] atom.xz = new_pos[2] topol.remake_parm() # setup the new non-bonded parameters for t in types: indices = [index for index in self.params if self.params[index] == t] selection_string = '(:{residue_mask})&(@{atom_name})'.format( residue_mask=','.join(str(i) for i in indices), atom_name=t) action = pmd.tools.addLJType( topol, selection_string, radius=self.lj_params[t][0].value_in_unit(u.angstrom), epsilon=self.lj_params[t][1].value_in_unit(u.kilocalorie_per_mole) ) action.execute()
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 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 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 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 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 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 _new_atom(parm, carbon, hydrogen, name): """ Creates a new hydrogen atom and add the necessary topology Parameters ---------- parm : parmed.Structure the topology carbon : parmed.Atom the carbon atom to which the hydrogen should be bonded hydrogen : parmed.Atom the other hydrogen, already in the topology name : string the name of the hydrogen atom Returns ------- parmed.Atom the newly created atom """ new = parmed.Atom(list=parm.atoms, name=name, type="HAL2", charge=0.0, mass=1.008000) new.residue = parm.residues[0] parm.residues[0].atoms.insert(hydrogen.idx + 1, new) parm.atoms.insert(hydrogen.idx + 1, new) # Add topology parm.bonds.append(parmed.Bond(carbon, new)) for a in parm.adjusts: if a.atom1 == hydrogen: parm.adjusts.append(parmed.NonbondedException(new, a.atom2)) elif a.atom2 == hydrogen: parm.adjusts.append(parmed.NonbondedException(a.atom1, new)) for angle in hydrogen.angles: parm.angles.append(_new_angle(angle, hydrogen, new)) parm.angles.append(parmed.Angle(hydrogen, carbon, new)) parm.angles[-1].funct = 5 for dihedral in hydrogen.dihedrals: parm.dihedrals.append(_new_dihedral(dihedral, hydrogen, new)) return new
def add_dummy_atoms_to_structure( structure: pmd.Structure, dummy_atom_offsets: List[np.ndarray], offset_coordinates: Optional[np.ndarray] = None, ): """A convenience method to add a number of dummy atoms to an existing ParmEd structure, and to position those atoms at a specified set of positions. Parameters ---------- structure The structure to add the dummy atoms to. dummy_atom_offsets The list of positions (defined by a 3-d numpy array) of the dummy atoms to add. offset_coordinates An optional amount to offset each of the dummy atom positions by with shape=(3,). """ if offset_coordinates is None: offset_coordinates = np.zeros(3) full_coordinates = np.vstack( [ structure.coordinates, *[ offset_coordinates + dummy_atom_offset for dummy_atom_offset in dummy_atom_offsets ], ] ) for index in range(len(dummy_atom_offsets)): structure.add_atom(pmd.Atom(name="DUM"), f"DM{index + 1}", 1) structure.positions = full_coordinates
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 _add_particles(self, topol): err_msg = 'Unknown spin label type {{}}. Allowed values are: {}' err_msg = err_msg.format(', '.join(self.ALLOWED_TYPES)) # we use the same radius and screen as for oxygen if not self.explicit: radius, screen = self._find_radius_and_screen(topol) # find all the unique types of spin labels types = set(self.params.values()) # create the bond types bond_types = {} for t in types: bond_k, bond_r = self.bond_params[t] topol.bond_types.append( pmd.BondType(bond_k, bond_r, list=topol.bond_types)) bt = topol.bond_types[-1] bond_types[t] = bt # create the angle types angle_types = {} for t in types: angle_k, angle_theta = self.angle_params[t] topol.angle_types.append( pmd.AngleType(angle_k, angle_theta, list=topol.angle_types)) at = topol.angle_types[-1] angle_types[t] = at # create the torsion types tors_types = {} for t in types: tors_k, tors_per, tors_phase = self.tors_params[t] topol.dihedral_types.append( pmd.DihedralType(tors_k, tors_per, tors_phase, list=topol.dihedral_types)) tt = topol.dihedral_types[-1] tors_types[t] = tt for key in self.params: if self.params[key] not in self.ALLOWED_TYPES: raise ValueError(err_msg.format(self.params[key])) # create the particle atom = pmd.Atom(None, 8, 'OND', 'OND', 0.0, 16.00) if not self.explicit: atom.radii = radius atom.screen = screen # add to system topol.add_atom_to_residue(atom, topol.residues[key]) # find the other atoms ca = topol.view[':{},@CA'.format(key + 1)].atoms[0] cb = topol.view[':{},@CB'.format(key + 1)].atoms[0] n = topol.view[':{},@N'.format(key + 1)].atoms[0] # add bond topol.bonds.append(pmd.Bond(atom, ca, bond_types[self.params[key]])) # add angle topol.angles.append( pmd.Angle(cb, ca, atom, angle_types[self.params[key]])) # add torsion topol.dihedrals.append( pmd.Dihedral(n, ca, cb, atom, type=tors_types[self.params[key]])) # set position ca_pos = np.array((ca.xx, ca.xy, ca.xz)) n_pos = np.array((n.xx, n.xy, n.xz)) cb_pos = np.array((cb.xx, cb.xy, cb.xz)) direction = np.linalg.norm(ca_pos - n_pos) new_pos = cb_pos - self.bond_params[ self.params[key]][1].value_in_unit(u.angstrom) * direction atom.xx = new_pos[0] atom.xy = new_pos[1] atom.xz = new_pos[2] topol.remake_parm() # setup the new non-bonded parameters for t in types: indices = [ index + 1 for index in self.params if self.params[index] == t ] selection_string = '(:{residue_mask})&(@{atom_name})'.format( residue_mask=','.join(str(i) for i in indices), atom_name=t) print topol.LJ_radius action = pmd.tools.addLJType( topol, selection_string, radius=self.lj_params[t][0].value_in_unit(u.angstrom), epsilon=self.lj_params[t][1].value_in_unit( u.kilocalorie_per_mole)) action.execute() print topol.LJ_radius
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 _create_atoms(struct, residue, template, hnames) : """ Adds the necessary atoms to a parmed.Structure object Parameters ---------- struct : parmed.Structure the structure residue : string the residue name template : string the name prefix of the carbon atoms hnames: string the name postfox of the hydrogen atoms """ try : h9 = struct.view["@H9%s&:%s"%(hnames[0],residue)].atoms[0] except : h9 = struct.view["@H9%s&:%s"%("1",residue)].atoms[0] c8 = struct.view["@%s8&:%s"%(template,residue)].atoms[0] c9 = struct.view["@%s9&:%s"%(template,residue)].atoms[0] c10 = struct.view["@%s10&:%s"%(template,residue)].atoms[0] term_i = _find_chain_end(struct.view[":%s"%residue], 30, template) terminal = struct.view["@%s%d&:%s"%(template,term_i,residue)].atoms[0] term_res_idx = _get_atom_index(terminal.residue, terminal.name) v1 = geo.vnorm(np.asarray(_get_coords_from_atom_or_tuple(c9)) - np.asarray(_get_coords_from_atom_or_tuple(c10))) v2 = geo.vnorm(np.asarray(_get_coords_from_atom_or_tuple(c9)) - np.asarray(_get_coords_from_atom_or_tuple(h9))) v1_v2 = geo.vnorm(np.cross(v1,v2)) xyz = 0.5*(np.asarray(_get_coords_from_atom_or_tuple(c9)) + np.asarray(_get_coords_from_atom_or_tuple(c10))) + 1.3*v1_v2 catom = parmed.Atom(list=struct.atoms, name="%s%d"%(template, term_i+1)) catom.residue = c10.residue catom.xx = xyz[0] catom.xy = xyz[1] catom.xz = xyz[2] c10.residue.atoms.insert(term_res_idx+4, catom) struct.atoms.insert(terminal.idx+4, catom) struct.bonds.append(parmed.Bond(c9,catom)) struct.bonds.append(parmed.Bond(c10,catom)) xyz = geo.build_xyz(np.asarray(_get_coords_from_atom_or_tuple(catom)), np.asarray(_get_coords_from_atom_or_tuple(c9)), np.asarray(_get_coords_from_atom_or_tuple(c8)), 0.95, 118, 150) hatom1 = parmed.Atom(list=struct.atoms, name="H%d%s"%(term_i+1, hnames[0])) hatom1.residue = catom.residue hatom1.xx = xyz[0] hatom1.xy = xyz[1] hatom1.xz = xyz[2] catom.residue.atoms.insert(term_res_idx+5, hatom1) struct.atoms.insert(terminal.idx+5, hatom1) struct.bonds.append(parmed.Bond(catom, hatom1)) xyz = geo.build_xyz(np.asarray(_get_coords_from_atom_or_tuple(catom)), np.asarray(_get_coords_from_atom_or_tuple(c9)), np.asarray(_get_coords_from_atom_or_tuple(c8)), 0.95, 118, 350) hatom2 = parmed.Atom(list=struct.atoms, name="H%d%s"%(term_i+1, hnames[1])) hatom2.residue = catom.residue hatom2.xx = xyz[0] hatom2.xy = xyz[1] hatom2.xz = xyz[2] catom.residue.atoms.insert(term_res_idx+6, hatom2) struct.atoms.insert(terminal.idx+6, hatom2) struct.bonds.append(parmed.Bond(catom, hatom2))
def insertHbyList(ase_struct, pmd_top, implicitHbondingPartners, bond_length=1.0): # make copies of passed structures as not to alter originals: new_pmd_top = pmd_top.copy(pmd.Structure) new_ase_struct = ase_struct.copy() # names stores the String IDs of all atoms as to facilitate later ASE ID -> Atom name mapping names = [a.name for a in pmd_top.atoms] # make copied atoms accessible by unchangable indices (standard list) originalAtoms = [a for a in new_pmd_top.atoms] implicitHbondingPartnersIdxHnoTuples = [ (a.idx, implicitHbondingPartners[k]) for a in pmd_top.atoms for k in implicitHbondingPartners.keys() if a.name == k ] implicitHbondingPartnersIdxHnoDict = dict( implicitHbondingPartnersIdxHnoTuples) # build numbered neighbour list "manually" #i: list of atoms e.g. [0,0,0,1,1,2,2,...] i = np.array([b.atom1.idx for b in new_pmd_top.bonds]) #j: list of bonding partners corresponding to i [1,2,3,...] ==> 0 has bondpartners 1,2 and 3; etc. j = np.array([b.atom2.idx for b in new_pmd_top.bonds]) r = new_ase_struct.positions for k, Hno in implicitHbondingPartnersIdxHnoDict.items( ): # for all atoms to append hydrogen to print('Adding {} H-atoms to {} (#{})...'.format( Hno, originalAtoms[k].name, k)) for h in range(0, Hno): r = new_ase_struct.positions bondingPartners = j[i == k] print('bondingPartners', bondingPartners) partnerStr = '' for p in bondingPartners: if partnerStr == '': partnerStr = originalAtoms[p].name else: partnerStr += ', ' + originalAtoms[p].name print('Atom {} already has bonding partners {}'.format( originalAtoms[k].name, partnerStr)) dr = (r[j[i == k]] - r[k]).mean(axis=0) # my understanding: dr is vector # from atom k's position towards the geometrical center of mass # it forms with its defined neighbours # r0 is a vector offset into the opposit direction: dr = dr / np.linalg.norm(dr) #normalized vector in direction dr #calculate an orthogonal vector 'dr_ortho' on dr #and push the H atoms in dr+dr_ortho and dr-dr_ortho #if one has to add more than two H atoms introduce dr_ortho_2 = dr x dr_ortho dr_ortho = np.cross(dr, np.array([1, 0, 0])) if np.linalg.norm( dr_ortho ) < 0.1: #if dr and (1,0,0) have almost the same direction dr_ortho = np.cross(dr, np.array([0, 1, 0])) # (1-2*h) = {1,-1} for h={0,1} h_pos_vec = (dr + (1 - 2 * h) * dr_ortho ) / np.linalg.norm(dr + (1 - 2 * h) * dr_ortho) r0 = r[k] - bond_length * h_pos_vec new_ase_struct += ase.Atom('H', r0) # add atom in ase structure n_atoms = len(new_ase_struct) #introduce a corrector step for a added atom which is too close to others #do as many corrector stepps until all atoms are more than 1\AA appart c_step = 0 while True: nl = NeighborList(cutoffs=[.5] * len(new_ase_struct), skin=0.09, self_interaction=False, bothways=True) nl.update(new_ase_struct) indices, offsets = nl.get_neighbors(-1) indices = np.delete(indices, np.where(indices == k)) if len(indices) == 0: break elif c_step > 15: print( 'programm needs more than 15 corrector steps for H atom {} at atom {}' .format(n_atoms, k)) sys.exit(15) break print('too close atoms', indices) c_step += 1 print('correcter step {} for H {} at atom {}'.format( c_step, n_atoms - 1, k)) # if indices not empty -> the atom(-1)=r_H is to close together # with atom a_close=indices[0], it is a H-atom belonging to atom 'k'=r_k . #correctorstep: corr_step = (r_H-a_close)/|(r_H-a_close)| #corrected_pos: corr_pos = ((r_H-r_k) + corr_step)/|((r_H-r_k) + corr_step)| #new H position: new_r_H = r_k + corr_pos r_H, r_k, a_close = np.take(new_ase_struct.get_positions(), [-1, k, indices[0]], axis=0) #print('r_H, r_k, a_close', r_H, r_k, a_close) corr_step = (r_H - a_close) / np.linalg.norm( (r_H - a_close) ) #maybe introduce here a skaling Faktor s=0.3 or somthing like that to make tiny corrections and don't overshoot. corr_pos = ((r_H - r_k) + corr_step) / np.linalg.norm( (r_H - r_k) + corr_step) new_r_H = r_k + bond_length * corr_pos #correct the H position to new_r_H in new_ase_struct trans = np.zeros([n_atoms, 3]) trans[-1] = new_r_H - r_H new_ase_struct.translate(trans) #view(new_ase_struct) #sys.exit() i = np.append(i, k) # manually update numbered neighbour lists j = np.append(j, len(new_ase_struct) - 1) # update pmd topology bondingPartner = originalAtoms[ k] # here we need the original numbering, # as ParmEd alters indices when adding atoms to the structure nameH = '{}{}'.format( h + 1, bondingPartner.name) # atom needs a unique name print('Adding H-atom {} at position [ {}, {}, {} ]'.format( nameH, r0[0], r0[1], r0[2])) new_H = pmd.Atom(name=nameH, type='H', atomic_number=1) new_H.xx = r0[ 0] # ParmEd documentation not very helpful, did not find any more compact assignment new_H.xy = r0[1] new_H.xz = r0[2] # do not understand ParmEd that well, apparently we need the Bond object in order to update topology new_Bond = pmd.Bond(bondingPartner, new_H) new_H.bond_to( bondingPartner) # not sure, whether this is necessary new_pmd_top.bonds.append(new_Bond) new_pmd_top.add_atom_to_residue(new_H, bondingPartner.residue) originalAtoms.append( new_H) # add atom to the bottom of "index-stiff" list names.append(nameH) # append name of H-atom return new_ase_struct, new_pmd_top, names
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
def _add_particles(self, topol): err_msg = "Unknown spin label type {{}}. Allowed values are: {}" err_msg = err_msg.format(", ".join(self.ALLOWED_TYPES)) # we use the same radius and screen as for oxygen if not self.explicit: radius, screen = self._find_radius_and_screen(topol) # find all the unique types of spin labels types = set(self.params.values()) for key in self.params: if self.params[key] not in self.ALLOWED_TYPES: raise ValueError(err_msg.format(self.params[key])) # create the particle atom = pmd.Atom(None, 8, "OND", "OND", 0.0, 16.00) if not self.explicit: atom.solvent_radius = radius atom.screen = screen # add to system topol.add_atom_to_residue(atom, topol.residues[int(key)]) # find the other atoms ca = topol.view[f":{int(key)+1},@CA"].atoms[0] cb = topol.view[f":{int(key)+1},@CB"].atoms[0] n = topol.view[f":{int(key)+1},@N"].atoms[0] # Mark that the spin label and CA are connected. # This will not actually add a bond to the potential, # but will mark the connectivity between the atoms. atom.bond_to(ca) # set position ca_pos = np.array((ca.xx, ca.xy, ca.xz)) n_pos = np.array((n.xx, n.xy, n.xz)) cb_pos = np.array((cb.xx, cb.xy, cb.xz)) direction = (ca_pos - cb_pos) / np.linalg.norm(ca_pos - cb_pos) new_pos = (ca_pos - self.bond_params[self.params[key]][1].value_in_unit( u.nanometer) * direction + ca_pos - n_pos) atom.xx = new_pos[0] atom.xy = new_pos[1] atom.xz = new_pos[2] topol.remake_parm() # setup the new non-bonded parameters for t in types: indices = [ index for index in self.params if self.params[index] == t ] selection_string = "(:{residue_mask})&(@{atom_name})".format( residue_mask=",".join(str(i) for i in indices), atom_name=t) action = pmd.tools.addLJType( topol, selection_string, radius=self.lj_params[t][0].value_in_unit(u.angstrom), epsilon=self.lj_params[t][1].value_in_unit( u.kilocalorie_per_mole), ) action.execute()
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 _add_atom(parm, last_idx, template, htemplate): """ Change the terminal hydrogen to a carbon and add three hydrogen atoms, also add bonded information Parameters ---------- parm : parmed.Structure the topology last_idx : integer the index of the terminal carbon template : string the name prefix of the carbon atoms htemplate: string the name postfox of the hydrogen atoms """ # Find all carbons preceeding the terminal carbon and the hydrogens # bonded to them carbons = [] hydrogens = [] for idx in range(last_idx - 2, last_idx + 1): carbons.append(_get_atom(parm, "%s%d" % (template, idx))) hydrogens.append( [_get_atom(parm, "H%d%s" % (idx, hstr)) for hstr in htemplate[:2]]) # Change the charges of the carbon and hydrogens next to the terminal carbon carbons[1].charge = 0.0 hydrogens[1][0].charge = 0.0 hydrogens[1][1].charge = 0.0 # Change the type and charge of the terminal carbon and its hydrogens carbons[2].charge = 0.0470 carbons[2].type = "CTL2" for h in hydrogens[2]: h.charge = -0.0070 h.type = "HAL2" # Find the final hydrogen and change it to a carbon hatom = _get_atom(parm, "H%d%s" % (last_idx, htemplate[2])) hatom.type = "CTL3" hatom.charge = -0.0810 hatom.name = "%s%d" % (template, last_idx + 1) hatom.mass = 12.0110 # Create 3 new hydrogen atoms and the topology information newatoms = [] for i, hstr in enumerate(htemplate): newatom = parmed.Atom(list=parm.atoms, name="H%d%s" % (last_idx + 1, hstr), type="HAL3", charge=0.0160, mass=1.008000) newatom.residue = parm.residues[0] parm.residues[0].atoms.insert(hatom.idx + 1 + i, newatom) parm.atoms.insert(hatom.idx + 1 + i, newatom) parm.bonds.append(parmed.Bond(hatom, newatom)) parm.adjusts.append(parmed.NonbondedException(carbons[1], newatom)) parm.adjusts.append(parmed.NonbondedException(hydrogens[2][0], newatom)) parm.adjusts.append(parmed.NonbondedException(hydrogens[2][1], newatom)) parm.angles.append(parmed.Angle(carbons[2], hatom, newatom)) parm.dihedrals.append( parmed.Dihedral(carbons[1], carbons[2], hatom, newatom)) parm.dihedrals.append( parmed.Dihedral(hydrogens[2][0], carbons[2], hatom, newatom)) parm.dihedrals.append( parmed.Dihedral(hydrogens[2][1], carbons[2], hatom, newatom)) newatoms.append(newatom) # Add angles between thre three added hydrogens parm.angles.append(parmed.Angle(newatoms[0], hatom, newatoms[1])) parm.angles.append(parmed.Angle(newatoms[0], hatom, newatoms[2])) parm.angles.append(parmed.Angle(newatoms[1], hatom, newatoms[2])) # Correct the angle and dihedral types for angle in parm.angles[-12:]: angle.funct = 5 for dihedral in parm.dihedrals[-9:]: dihedral.funct = 9