def h2(): atom1 = mdt.Atom('H') atom1.x = 0.5 * u.angstrom atom2 = mdt.Atom(atnum=1) atom2.position = [-0.5, 0.0, 0.0] * u.angstrom h2 = mdt.Molecule([atom1, atom2], name='h2') atom1.bond_to(atom2, 1) return h2
def test_create_atom_with_atomic_number(): h = mdt.Atom('ca', atnum=1) assert h.atnum == 1 assert h.element == 'H' assert h.name == 'ca' with pytest.raises(AssertionError): mdt.Atom('ca', atnum=1, element='He')
def linear(): expected = {'C1': 1, 'Cinf_v': 1} a1 = mdt.Atom(1) a1.position = [-1, 0, 0] * u.angstrom a2 = mdt.Atom(2) a2.position = [1, 0, 0] * u.angstrom return mdt.Molecule([a1, a2], name='linear_h2'), expected, True
def planar(): expected = {'C1': 1, 'Cs': 1} a1 = mdt.Atom(1) a1.position = [-1, 0, 0] * u.angstrom a2 = mdt.Atom(2) a2.position = [0.9, 0.2, 0] * u.angstrom a3 = mdt.Atom(3) a3.position = [-0.9, 3.2, 0] * u.angstrom return mdt.Molecule([a1, a2, a3], name='planar_h3'), expected, True
def test_set_hybridization_and_saturate(): # Creates just the carbons of ethylene, expects the routine to figure out the rest atom1 = mdt.Atom(6) atom2 = mdt.Atom(6) atom2.x = 1.35 * u.angstrom atom1.bond_to(atom2, 1) mol = mdt.Molecule([atom1, atom2]) newmol = mdt.set_hybridization_and_saturate(mol) pytest.xfail('This is apparently broken') assert newmol.num_atoms == 6 assert newmol.atoms[0].bond_graph[atom1] == 2 assert len(newmol.get_atoms(atnum=1)) == 4
def create_link_atoms(mol, qmatoms): """ Create hydrogen caps for bonds between QM and MM regions. Each link atom will have ``metadata.mmatom``, ``metadata.mmpartner`` attributes to identify the atom it replaces and the atom it's bonded to in the MM system. Raises: ValueError: if any MM/QM atom is bonded to more than one QM/MM atom, or the bond order is not one Returns: List[mdt.Atom]: list of link atoms """ linkatoms = [] qmset = set(qmatoms) for qmatom in qmatoms: mmatom = _get_mm_nbr(mol, qmatom, qmset) if mmatom is None: continue la = mdt.Atom(atnum=1, name='HL%d' % len(linkatoms), metadata={'mmatom': mmatom, 'mmpartner': qmatom}) linkatoms.append(la) set_link_atom_positions(linkatoms) return linkatoms
def test_initialization_charges(): a1 = mdt.Atom('Na', formal_charge=-1) mol = mdt.Molecule([a1]) assert mol.charge == -1 * u.q_e with pytest.raises(TypeError): mdt.Atom( 'H', charge=3 ) # it needs to be "formal_charge" to distinguish from partial charge m2 = mdt.Molecule([a1], charge=-1) assert m2.charge == -1 * u.q_e m2 = mdt.Molecule([a1], charge=-3 * u.q_e) # TODO: test for warning assert m2.charge == -3 * u.q_e
def biopy_to_mol(struc): """Convert a biopython PDB structure to an MDT molecule. Because Biopython doesn't assign bonds, assign connectivity using templates. Args: struc (Bio.PDB.Structure.Structure): Biopython PDB structure to convert Returns: moldesign.Molecule: converted molecule """ # TODO: assign bonds using 1) CONECT records, 2) residue templates, 3) distance newatoms = [] for chain in struc.get_chains(): tmp, pdbidx, pdbid = chain.get_full_id() newchain = mdt.Chain(pdbname=pdbid.strip()) for residue in chain.get_residues(): newresidue = mdt.Residue(pdbname=residue.resname.strip(), pdbindex=residue.id[1]) newchain.add(newresidue) for atom in residue.get_atom(): newatom = mdt.Atom(element=atom.element, name=atom.get_name(), pdbname=atom.get_name(), pdbindex=atom.get_serial_number()) newatom.position = atom.coord * u.angstrom newresidue.add(newatom) newatoms.append(newatom) return mdt.Molecule(newatoms, name=struc.get_full_id()[0])
def test_add_atom(h2): newatom = mdt.Atom('Xe') h2.add_atom(newatom) assert newatom in h2.atoms assert h2.num_atoms == 3 assert h2.num_residues == 1 assert h2.num_chains == 1 assert newatom.residue is h2.residues[0] assert newatom.chain is h2.chains[0]
def test_create_atom_with_element_as_name(): he_plus = mdt.Atom("He", position=np.ones(3) * u.nm, formal_charge=1 * u.q_e) assert he_plus.atomic_number == 2 helpers.assert_almost_equal(he_plus.position, 10 * np.ones(3) * u.angstrom) assert he_plus.name == 'He' assert he_plus.element == 'He' assert he_plus.formal_charge == 1 * u.q_e
def precanned_trajectory(): a1 = mdt.Atom(6) a2 = mdt.Atom(1) a3 = mdt.Atom(7) mol = mdt.Molecule([a1, a2, a3]) traj = mdt.Trajectory(mol) a1.x = 1.0 * u.angstrom a3.y = 1.0 * u.angstrom traj.new_frame(somenumber=1, someletter='a') mol.time = 1.0 * u.fs a1.x = 2.0 * u.angstrom traj.new_frame(somenumber=2, someletter='b') mol.time = 2.0 * u.fs a2.x = -1.0 * u.angstrom a3.x = -1.0 * u.angstrom traj.new_frame(somenumber=3, someletter='c') return traj
def test_constrained_distance_minimization(minkey): minimizer = MINIMIZERS[minkey] mol = mdt.Molecule([mdt.Atom(1), mdt.Atom(2)]) mol.atoms[0].x = 2.0 * u.angstrom mol.atoms[1].x = 3.0 * u.angstrom mol.atoms[1].y = 2.0 * u.angstrom mol.set_energy_model(mdt.models.HarmonicOscillator, k=2.0 * u.kcalpermol / u.angstrom**2) e0 = mol.calculate_potential_energy() p0 = mol.positions.copy() mol.constrain_distance(mol.atoms[0], mol.atoms[1]) if minkey == 'bfgs': # BFGS expected to fail here with pytest.raises(mdt.exceptions.NotSupportedError): minimizer(mol) return traj = minimizer(mol) assert_something_resembling_minimization_happened(p0, e0, traj, mol)
def parmed_to_mdt(pmdmol): """ Convert parmed Structure to MDT Structure Args: pmdmol (parmed.Structure): parmed structure to convert Returns: mdt.Molecule: converted molecule """ atoms = collections.OrderedDict() residues = {} chains = {} masses = [pa.mass for pa in pmdmol.atoms] * u.dalton positions = [[pa.xx, pa.xy, pa.xz] for pa in pmdmol.atoms] * u.angstrom for iatom, patm in enumerate(pmdmol.atoms): if patm.residue.chain not in chains: chains[patm.residue.chain] = mdt.Chain(pdbname=patm.residue.chain) chain = chains[patm.residue.chain] if patm.residue not in residues: residues[patm.residue] = mdt.Residue(resname=patm.residue.name, pdbindex=patm.residue.number) residues[patm.residue].chain = chain chain.add(residues[patm.residue]) residue = residues[patm.residue] atom = mdt.Atom(name=patm.name, atnum=patm.atomic_number, pdbindex=patm.number, mass=masses[iatom]) atom.position = positions[iatom] atom.residue = residue residue.add(atom) assert patm not in atoms atoms[patm] = atom for pbnd in pmdmol.bonds: atoms[pbnd.atom1].bond_to(atoms[pbnd.atom2], int(pbnd.order)) mol = mdt.Molecule(list(atoms.values()), metadata=_get_pdb_metadata(pmdmol)) return mol
def biopy_to_mol(struc): """Convert a biopython PDB structure to an MDT molecule. Note: Biopython doesn't deal with bond data, so no bonds will be present in the Molecule Args: struc (Bio.PDB.Structure.Structure): Biopython PDB structure to convert Returns: moldesign.Molecule: converted molecule """ # TODO: assign bonds using 1) CONECT records, 2) residue templates, 3) distance newatoms = [] backup_chain_names = list(string.ascii_uppercase) for chain in struc.get_chains(): tmp, pdbidx, pdbid = chain.get_full_id() if not pdbid.strip(): pdbid = backup_chain_names.pop() newchain = mdt.Chain(pdbname=pdbid.strip()) for residue in chain.get_residues(): newresidue = mdt.Residue(pdbname=residue.resname.strip(), pdbindex=residue.id[1]) newchain.add(newresidue) for atom in residue.get_atom(): elem = atom.element if len(elem) == 2: elem = elem[0] + elem[1].lower() newatom = mdt.Atom(element=elem, name=atom.get_name(), pdbname=atom.get_name(), pdbindex=atom.get_serial_number()) newatom.position = atom.coord * u.angstrom newresidue.add(newatom) newatoms.append(newatom) return mdt.Molecule(newatoms, name=struc.get_full_id()[0])
def h2(): mol = mdt.Molecule([mdt.Atom('H'), mdt.Atom('H')]) mol.atoms[1].z = 0.75 * u.angstrom return mol
def topology_to_mol(topo, name=None, positions=None, velocities=None, assign_bond_orders=True): """ Convert an OpenMM topology object into an MDT molecule. Args: topo (simtk.openmm.app.topology.Topology): topology to convert name (str): name to assign to molecule positions (list): simtk list of atomic positions velocities (list): simtk list of atomic velocities assign_bond_orders (bool): assign bond orders from templates (simtk topologies do not store bond orders) """ from simtk import unit as stku # Atoms atommap = {} newatoms = [] masses = u.amu * [ atom.element.mass.value_in_unit(stku.amu) for atom in topo.atoms() ] for atom, mass in zip(topo.atoms(), masses): newatom = mdt.Atom(atnum=atom.element.atomic_number, name=atom.name, mass=mass) atommap[atom] = newatom newatoms.append(newatom) # Coordinates if positions is not None: poslist = np.array( [p.value_in_unit(stku.nanometer) for p in positions]) * u.nm poslist.ito(u.default.length) for newatom, position in zip(newatoms, poslist): newatom.position = position if velocities is not None: velolist = np.array([ v.value_in_unit(stku.nanometer / stku.femtosecond) for v in velocities ]) * u.nm / u.fs velolist = u.default.convert(velolist) for newatom, velocity in zip(newatoms, velolist): newatom.momentum = newatom.mass * simtk2pint(velocity) # Biounits chains = {} for chain in topo.chains(): if chain.id not in chains: chains[chain.id] = mdt.Chain(name=chain.id, index=chain.index) newchain = chains[chain.id] for residue in chain.residues(): newresidue = mdt.Residue(name='%s%d' % (residue.name, residue.index), chain=newchain, pdbindex=int(residue.id), pdbname=residue.name) newchain.add(newresidue) for atom in residue.atoms(): newatom = atommap[atom] newatom.residue = newresidue newresidue.add(newatom) # Bonds bonds = {} for bond in topo.bonds(): a1, a2 = bond na1, na2 = atommap[a1], atommap[a2] if na1 not in bonds: bonds[na1] = {} if na2 not in bonds: bonds[na2] = {} b = mdt.Bond(na1, na2) b.order = 1 if name is None: name = 'Unnamed molecule from OpenMM' newmol = mdt.Molecule(newatoms, name=name) if assign_bond_orders: for residue in newmol.residues: try: residue.assign_template_bonds() except (KeyError, ValueError): pass return newmol
def pybel_to_mol(pbmol, reorder_atoms_by_residue=False, primary_structure=True, **kwargs): """ Translate a pybel molecule object into a moldesign object. Note: The focus is on translating topology and biomolecular structure - we don't translate any metadata. Args: pbmol (pybel.Molecule): molecule to translate reorder_atoms_by_residue (bool): change atom order so that all atoms in a residue are stored contiguously primary_structure (bool): translate primary structure data as well as atomic data **kwargs (dict): keyword arguments to moldesign.Molecule __init__ method Returns: moldesign.Molecule: translated molecule """ newatom_map = {} newresidues = {} newchains = {} newatoms = mdt.AtomList([]) backup_chain_names = list(string.ascii_uppercase) for pybatom in pbmol.atoms: obres = pybatom.OBAtom.GetResidue() name = obres.GetAtomID(pybatom.OBAtom).strip() if pybatom.atomicnum == 67: print(( "WARNING: openbabel parsed atom serial %d (name:%s) as Holmium; " "correcting to hydrogen. ") % (pybatom.OBAtom.GetIdx(), name)) atnum = 1 elif pybatom.atomicnum == 0: print( "WARNING: openbabel failed to parse atom serial %d (name:%s); guessing %s. " % (pybatom.OBAtom.GetIdx(), name, name[0])) atnum = mdt.data.ATOMIC_NUMBERS[name[0]] else: atnum = pybatom.atomicnum mdtatom = mdt.Atom(atnum=atnum, name=name, formal_charge=pybatom.formalcharge * u.q_e, pdbname=name, pdbindex=pybatom.OBAtom.GetIdx()) newatom_map[pybatom.OBAtom.GetIdx()] = mdtatom mdtatom.position = pybatom.coords * u.angstrom if primary_structure: obres = pybatom.OBAtom.GetResidue() resname = obres.GetName() residx = obres.GetIdx() chain_id = obres.GetChain() chain_id_num = obres.GetChainNum() if chain_id_num not in newchains: # create new chain if not mdt.utils.is_printable( chain_id.strip()) or not chain_id.strip(): chain_id = backup_chain_names.pop() print( 'WARNING: assigned name %s to unnamed chain object @ %s' % (chain_id, hex(chain_id_num))) chn = mdt.Chain(pdbname=str(chain_id)) newchains[chain_id_num] = chn else: chn = newchains[chain_id_num] if residx not in newresidues: # Create new residue pdb_idx = obres.GetNum() res = mdt.Residue(pdbname=resname, pdbindex=pdb_idx) newresidues[residx] = res chn.add(res) res.chain = chn else: res = newresidues[residx] res.add(mdtatom) newatoms.append(mdtatom) for ibond in range(pbmol.OBMol.NumBonds()): obbond = pbmol.OBMol.GetBond(ibond) a1 = newatom_map[obbond.GetBeginAtomIdx()] a2 = newatom_map[obbond.GetEndAtomIdx()] order = obbond.GetBondOrder() bond = mdt.Bond(a1, a2) bond.order = order if reorder_atoms_by_residue and primary_structure: resorder = {} for atom in newatoms: resorder.setdefault(atom.residue, len(resorder)) newatoms.sort(key=lambda a: resorder[a.residue]) return mdt.Molecule(newatoms, **kwargs)
def harmonic_atom(): mol = mdt.Molecule([mdt.Atom(1)]) mol.atoms[0].x = 2.0 * u.angstrom mol.set_energy_model(mdt.models.HarmonicOscillator, k=2.0 * u.kcalpermol / u.angstrom**2) return mol
def _make_mol_with_n_hydrogens(n): return mdt.Molecule([mdt.Atom('H') for i in range(n)])
def heh_plus(): mol = mdt.Molecule([mdt.Atom('H'), mdt.Atom('He')]) mol.atoms[1].z = 1.0 * u.angstrom mol.charge = 1 * u.q_e mol.atoms[0].bond_to(mol.atoms[1], 1) return mol
def h2(): mol = mdt.Molecule([mdt.Atom('H1'), mdt.Atom('H2')]) mol.atoms[0].x = 0.5 * u.angstrom mol.atoms[1].x = -0.25 * u.angstrom mol.atoms[0].bond_to(mol.atoms[1], 1) return mol
def almost_planar(planar): atoms = planar[0].atoms atoms.append(mdt.Atom(4)) atoms[-1].position = [0, 0, 0.005] * u.angstrom return mdt.Molecule(atoms, name='almost_planar')
def carbon_atom(): atom1 = mdt.Atom('C') return atom1
def test_create_atom_with_uppercase_element(): cl_minus = mdt.Atom("CL2", element='CL', formal_charge=-1) assert cl_minus.formal_charge == -1 * u.q_e assert cl_minus.atnum == 17 assert cl_minus.name == 'CL2' assert cl_minus.element == 'Cl'
def test_create_atom_with_unrelated_name(): carbon_named_bob = mdt.Atom("bob", element='c') assert carbon_named_bob.name == 'bob' assert carbon_named_bob.atnum == 6 assert carbon_named_bob.element == 'C'
def test_create_atom_with_element_name_as_first_char(): carbon_alpha = mdt.Atom('ca') assert carbon_alpha.name == 'ca' assert carbon_alpha.atnum == 6 assert carbon_alpha.element == 'C'