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 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 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 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 _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