def _setup_angle_tools(self, atoms): a1, a2, a3 = atoms self.viewer.shapes = [] # creates a temp cylinder that will be overwritten in self.set_distance angle_normal = np.cross(a1.position - a2.position, a3.position - a2.position) self._widgetshapes = { 'plane': self.viewer.draw_circle(a2.position, normal=angle_normal, radius=max(a1.distance(a2), a3.distance(a2)), opacity=0.55, color='blue'), 'origin': self.viewer.draw_sphere(a2.position, radius=0.5, opacity=0.85, color='green'), 'b1': self.viewer.draw_cylinder(a1.position, a2.position, radius=0.3, opacity=0.65, color='red'), 'b2': self.viewer.draw_cylinder(a3.position, a2.position, radius=0.3, opacity=0.65, color='red') } self.angle_slider.value = mdt.angle(a1, a2, a3).value_in(u.degrees) self.angle_slider.description = ANGLEDESCRIPTION.format( a1=a1, a2=a2, a3=a3, c1=self.viewer.HIGHLIGHT_COLOR, c2=self.NBR2HIGHLIGHT) b1 = mdt.Bond(a1, a2) b2 = mdt.Bond(a3, a2) if b1.is_cyclic: b1, b2 = b2, b1 # try to find a b1 that is not cyclic if b1.exists and b2.exists and not b1.is_cyclic: self.tool_holder.children = ( self.rigid_mol_selector, self.angle_slider, ) else: self.tool_holder.children = (self.angle_slider, ) self.rigid_mol_selector.value = False
def _setup_distance_tools(self, atoms): a1, a2 = atoms self.viewer.shapes = [] # creates a temp cylinder that will be overwritten in self.set_distance self._widgetshapes = { 'bond': self.viewer.draw_cylinder(a1.position, a2.position, radius=0.3, opacity=0.65, color='red') } self.length_slider.value = a1.distance(a2).value_in(u.angstrom) self.length_slider.description = BONDDESCRIPTION.format( a1=a1, a2=a2, c1=self.viewer.HIGHLIGHT_COLOR) bond = mdt.Bond(a1, a2) if bond.exists and not bond.is_cyclic: self.tool_holder.children = ( self.rigid_mol_selector, self.length_slider, ) else: self.tool_holder.children = (self.length_slider, ) self.rigid_mol_selector.value = False
def test_bond_alignment_on_axis(benzene): mol = benzene.copy() directions = [ 'x', 'y', 'z', [1, 2, 3.0], [0, 1, 0], [0.1, 0.1, 0.1] * u.angstrom ] for i, dir in enumerate(directions): bond = mdt.Bond(*random.sample(mol.atoms, 2)) center = (i % 2) == 0.0 bond.align(dir, centered=center) if center: np.testing.assert_allclose(bond.midpoint, np.zeros(3), atol=1.e-12) np.testing.assert_allclose(bond.a1.position.defunits_value(), -bond.a2.position.defunits_value(), atol=1e-10) if isinstance(dir, str): if dir == 'x': d = np.array([1.0, 0, 0]) elif dir == 'y': d = np.array([0.0, 1.0, 0.0]) elif dir == 'z': d = np.array([0.0, 0.0, 1.0]) else: raise WtfError() else: d = normalized(u.array(dir)) newvec = (bond.a2.position - bond.a1.position).normalized() assert abs(1.0 - d.dot(newvec)) < 1e-10
def bonds(self): """ Iterable[moldesign.Bond]: iterator over bonds from this object's atoms """ bg = self.bond_graph for atom, nbrs in bg.iteritems(): for nbr, order in nbrs.iteritems(): if atom.index < nbr.index or nbr not in bg: yield mdt.Bond(atom, nbr, order)
def internal_bonds(self): """ Iterable[moldesign.Bond]: iterator over bonds that connect two atoms in this object """ bg = self.bond_graph for atom, nbrs in bg.iteritems(): for nbr, order in nbrs.iteritems(): if atom.index < nbr.index and nbr in bg: yield mdt.Bond(atom, nbr, order)
def test_set_dihedral_bond_no_adjust(four_particle_45_twist): mol = four_particle_45_twist bond = mdt.Bond(mol.atoms[1], mol.atoms[2]) mdt.set_dihedral(bond, 10.0 * u.degrees, adjustmol=False) np.testing.assert_almost_equal(mdt.dihedral(*mol.atoms).value_in( u.degrees), 10.0, decimal=8)
def test_bond_object_for_unbonded_atoms(pdb3aid): mol = pdb3aid a1 = mol.atoms[1] a2 = mol.atoms[-10] bond = mdt.Bond(a1, a2) assert bond.order is None bond.order = 2 assert_consistent_bond(mol, a1, a2, 2)
def external_bonds(self): """ Iterable[moldesign.Bond]: iterator over bonds that bond these atoms to other atoms """ bg = self.bond_graph for atom, nbrs in bg.iteritems(): for nbr, order in nbrs.iteritems(): if nbr not in bg: yield mdt.Bond(atom, nbr, order)
def _setup_dihedral_tools(self, atoms): a1, a2, a3, a4 = atoms bc = mdt.Bond(a2, a3) b_0 = mdt.Bond(a1, a2) b_f = mdt.Bond(a3, a4) self._widgetshapes = { 'b1': self.viewer.draw_cylinder(a1.position, a2.position, radius=0.3, opacity=0.65, color='red'), 'b3': self.viewer.draw_cylinder(a3.position, a4.position, radius=0.3, opacity=0.65, color='red'), 'plane': self.viewer.draw_circle(center=bc.midpoint, normal=a3.position - a2.position, radius=1.5, color='blue', opacity=0.55) } self.dihedral_slider.description = DIHEDRALDESCRIPTION.format( a1=a1, a2=a2, a3=a3, a4=a4, c0=self.NBR1HIGHLIGHT, c1=self.viewer.HIGHLIGHT_COLOR, c2=self.NBR2HIGHLIGHT) if bc.exists and b_0.exists and b_f.exists and not b_0.is_cyclic: self.tool_holder.children = ( self.rigid_mol_selector, self.dihedral_slider, ) else: self.rigid_mol_selector = False self.tool_holder.children = (self.dihedral_slider, )
def test_delete_bond(pdb3aid): mol = pdb3aid a1 = mol.atoms[89] # arbitrary atom mol.ff = 'arglebargle' # changing topology should reset this nbonds = mol.num_bonds a2 = a1.bonded_atoms[-1] bond = mdt.Bond(a1, a2) mol.delete_bond(bond) assert mol.num_bonds == nbonds - 1 assert_not_bonded(mol, a1, a2)
def test_bond_object_automatically_associates_with_molecule(pdb3aid): mol = pdb3aid a1 = mol.atoms[511] # arbitrary atom bond_from_atom = a1.bonds[-1] a2 = bond_from_atom.partner(a1) created_bond = mdt.Bond(a2, a1) assert created_bond == bond_from_atom assert created_bond.order == mol.bond_graph[a1][a2] assert bond_from_atom.order == mol.bond_graph[a1][a2] assert created_bond.molecule is mol assert bond_from_atom.molecule is mol
def add_orbitals(mol, wfn, orbdata, orbtype): orbs = [] for i in range(len(orbdata.coeffs)): bond = None atoms = [mol.atoms[orbdata.iatom[i] - 1]] if orbdata.bond_names[i] == 'RY': bname = '%s Ryd*' % atoms[0].name nbotype = 'rydberg' utf_name = bname elif orbdata.bond_names[i] == 'LP': bname = '%s lone pair' % atoms[0].name nbotype = 'lone pair' utf_name = bname elif orbdata.bond_names[i] == 'LV': bname = '%s lone vacancy' % atoms[0].name nbotype = 'lone vacancy' utf_name = bname elif orbdata.num_bonded_atoms[i] == 1: bname = '%s Core' % atoms[0].name nbotype = 'core' utf_name = bname else: atoms.append(mol.atoms[orbdata.jatom[i] - 1]) bond = mdt.Bond(*atoms) if orbdata.bondnums[i] == 1: # THIS IS NOT CORRECT nbotype = 'sigma' utf_type = SIGMA_UTF else: nbotype = 'pi' utf_type = PI_UTF bname = '%s%s (%s - %s)' % (nbotype, orbdata.stars[i], atoms[0].name, atoms[1].name) utf_name = '%s%s (%s - %s)' % (utf_type, orbdata.stars[i], atoms[0].name, atoms[1].name) name = '%s %s' % (bname, orbtype) orbs.append( mdt.Orbital(orbdata.coeffs[i], wfn=wfn, occupation=orbdata.occupations[i], atoms=atoms, name=name, nbotype=nbotype, bond=bond, unicode_name=utf_name, _data=orbdata)) return wfn.add_orbitals(orbs, orbtype=orbtype)
def test_change_bond_order_with_bond_object(pdb3aid): mol = pdb3aid a1 = mol.atoms[511] # arbitrary atom bond = a1.bonds[-1] a2 = bond.partner(a1) other_bond_object = mdt.Bond(a1, a2) oldorder = bond.order assert oldorder == other_bond_object.order assert a1.bond_graph[a2] == oldorder # First raise the order bond.order += 1 assert_consistent_bond(mol, a1, a2, oldorder + 1) # Now delete the bond by setting it to None bond.order = None assert_not_bonded(mol, a1, a2)
def get_bond_term(self, bond_or_atom1, atom2=None): if atom2 is None: bond = bond_or_atom1 else: bond = mdt.Bond(bond_or_atom1, atom2) pmdatom = self.parmed_obj.atoms[bond.a1.index] indices = [bond.a1.index, bond.a2.index] for pmdbond in pmdatom.bonds: pmdindices = sorted((pmdbond.atom1.idx, pmdbond.atom2.idx)) if pmdindices == indices: assert self.parmed_obj.atoms[ pmdindices[0]].element == bond.a1.atnum assert self.parmed_obj.atoms[ pmdindices[1]].element == bond.a2.atnum return BondTerm(bond, pmdbond) else: raise ValueError("No ForceField term found for bond: %s" % bond)
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 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 get_bond(self, a1, a2): return mdt.Bond(a1, a2)