Exemple #1
0
def _canDonate(donor, acceptor, noProtonsOK, protons, lonePairs):
	# donor is fixed; can it donate to acceptor?
	if not lonePairs:
		return True
	if not protons:
		if noProtonsOK:
			if debug:
				print "can't still donate; no protons"
			return False
		raise ValueError, "No protons for %s to accept from" % (
							acceptor.oslIdent())
	accPos = acceptor.xformCoord()
	hDist, h = min(map(lambda xyz: ((xyz - accPos).sqlength(), xyz),
								protons))
	lpDist = min(map(lambda xyz: (xyz - accPos).sqlength(), lonePairs))
	# besides a proton being closest, it must be sufficiently pointed
	# towards the acceptor
	if hDist >= lpDist:
		from math import sqrt
		if debug:
			print "can't still donate; h dist (%g) >= lp dist (%g)" % ( sqrt(hDist), sqrt(lpDist))
	elif chimera.angle(h, donor.xformCoord(), accPos) >= _testAngles[
			typeInfo4Atom[donor].geometry]:
		if debug:
			print "can't still donate; angle (%g) >= test angle (%g)" % ( chimera.angle(h, donor.xformCoord(), accPos), _testAngles[ typeInfo4Atom[donor].geometry])
	return hDist < lpDist and chimera.angle(h,
			donor.xformCoord(), accPos) < _testAngles[
			typeInfo4Atom[donor].geometry]
Exemple #2
0
	def angle(self, items):
		from Axes import Axis
		from Planes import Plane
		numAxes = len([i for i in items if isinstance(i, Axis)])
		numPlanes = len([i for i in items if isinstance(i, Plane)])
		if numAxes == 2:
			axis1, axis2 = items
			angle = chimera.angle(axis1.xformDirection(),
									axis2.xformDirection())
			if angle > 90.0:
				angle = 180.0 - angle
		elif numAxes == numPlanes == 1:
			if isinstance(items[0], Axis):
				axis, plane = items
			else:
				plane, axis = items
			angle = chimera.angle(axis.xformDirection(), plane.xformNormal())
			angle = abs(90.0 - angle)
		elif numPlanes == 2:
			plane1, plane2 = items
			angle = chimera.angle(plane1.xformNormal(), plane2.xformNormal())
			if angle > 90.0:
				angle = 180.0 - angle
		else:
			raise ValueError("angle calculation not implemented")
		return angle
def accGeneric(donor, donorHyds, acceptor, r2, minAngle):
	if base.verbose:
		print "generic acceptor"
	dp = donor.xformCoord()
	ap = acceptor.xformCoord()
	if donor.element.name == "S":
		r2 = sulphurCompensate(r2)
	if base.verbose:
		print "distance: %g, cut off: %g" % (distance(dp, ap), sqrt(r2))
	if sqdistance(dp, ap) > r2:
		if base.verbose:
			print "dist criteria failed"
		return 0
	if base.verbose:
		print "dist criteria okay"
	 
	ap = acceptor.xformCoord()
	dp = donor.xformCoord()
	for bonded in acceptor.primaryNeighbors():
		bp = bonded.xformCoord()
		if base.verbose:
			print "angle: %g" % angle(bp, ap, dp)
		ang = angle(bp, ap, dp)
		if ang < minAngle:
			if base.verbose:
				print "angle too sharp (%g < %g)" % (ang,
								minAngle)
			return 0
	if base.verbose:
		print "angle(s) okay (all > %g)" % minAngle
	return 1
Exemple #4
0
def _tet2check(tetPos, toward, toward2, partnerPos):
	if debug:
		print "2 position tetCheck",
	for pos in bondPositions(tetPos, 4, 1.0, [], toward=toward,
							toward2=toward2):
		if debug:
			print chimera.angle(partnerPos, tetPos, pos),
		if chimera.angle(partnerPos, tetPos, pos) < _testAngles[4]:
			if debug:
				print "true"
			return True
	if debug:
		print "false"
	return False
Exemple #5
0
	def _angleLabel(self, atoms):
		pts = tuple([a.xformCoord() for a in atoms])
		if len(pts) == 3:
			val = chimera.angle(*pts)
		else:
			val = chimera.dihedral(*pts)
		return "%.*f" % (prefs[ANGLE_PRECISION], val)
    def order_retriever(self, dummies_xyz):
        """
        Base class method to order dummies
        for types.

        Logic:

        - For each dummy we look for the
          one being at the opposite side
          of the metal by looking for a
          DZ-MET_DZ angle of 180.
          Then we appned both together on
          the list.
        """
        new_order = []
        for dummy in dummies_xyz:
            if dummy not in new_order:
                new_order.append(dummy)
                for i in range(0, len(dummies_xyz)):
                    angle = chimera.angle(dummy, self.metal.labelCoord(),
                                          dummies_xyz[i])
                    if abs(angle -
                           180) < 0.01 and dummies_xyz[i] not in new_order:
                        new_order.append(dummies_xyz[i])
                        break
        return new_order
 def _axisDistance(self, axis, infinite=False):
     from chimera import angle, cross, Plane
     # shortest distance between lines is perpendicular to both...
     sDir = self.xformDirection()
     aDir = axis.xformDirection()
     if angle(sDir, aDir) in [0.0, 180.0]:
         # parallel
         return self._axisEndsDist(axis)
     shortDir = cross(sDir, aDir)
     # can use analytically shortest dist only if each axis
     # penetrates the plane formed by the other axis and the
     # perpendicular
     if not infinite:
         for a1, a2 in [(axis, self), (self, axis)]:
             normal = cross(a1.xformDirection(), shortDir)
             plane = Plane(a1.xformCenter(), normal)
             d1 = plane.distance(a2.xformCenter() +
                                 a2.xformDirection() * a2.extents[0])
             d2 = plane.distance(a2.xformCenter() +
                                 a2.xformDirection() * a2.extents[1])
             if cmp(d1, 0.0) == cmp(d2, 0.0):
                 # both ends on same side of plane
                 return self._axisEndsDist(axis)
     # D is perpendicular distance to origin
     d1 = Plane(self.xformCenter(), shortDir).equation()[3]
     d2 = Plane(axis.xformCenter(), shortDir).equation()[3]
     return abs(d1 - d2)
Exemple #8
0
def rotate(molecule, at, alpha):
    if len(at) == 3:
        try:
            a1, a2, a3 = [a.coord() for a in at]
        except AttributeError:
            a1, a2, a3 = at
        axis_a = a1 - a2
        axis_b = a3 - a2
        delta = chimera.angle(a1, a2, a3) - alpha
        axis = chimera.cross(axis_a, axis_b)
        if axis.data() == (0.0, 0.0, 0.0):
            axis = chimera.cross(axis_a, axis_b + chimera.Vector(1, 0, 0))
            logger.warning("Had to choose arbitrary normal vector")
        pivot = a2
    elif len(at) == 4:
        try:
            a1, a2, a3, a4 = [a.coord() for a in at]
        except AttributeError:
            a1, a2, a3, a4 = at
        axis = a3 - a2
        delta = chimera.dihedral(a1, a2, a3, a4) - alpha
        pivot = a3
    else:
        raise ValueError(
            "Atom list must contain 3 (angle) or 4 (dihedral) atoms only")

    r = X.translation(pivot - ZERO)  # move to origin
    r.multiply(X.rotation(axis, -delta))  # rotate
    r.multiply(X.translation(ZERO - pivot))  # return to orig pos
    for a in molecule.atoms:
        a.setCoord(r.apply(a.coord()))
Exemple #9
0
    def evaluate(self, ind):
        atoms_coords = [a.xformCoord() for a in self.probes(ind)]
        try:
            angle = chimera.angle(*atoms_coords)
        except TypeError:  # four atoms, means dihedral
            angle = chimera.dihedral(*atoms_coords)

        if self.threshold == 'planar':
            return abs(math.sin(math.radians(angle)))
        return abs(self.threshold - angle.real)
Exemple #10
0
def _angleCheck(d, a, hbondInfo):
	addAtoD = addDtoA = True
	# are the protons/lps already added to the
	# donor pointed toward the acceptor?
	if d in hbondInfo:
		geom = _typeInfo(d).geometry
		for isAcc, da in hbondInfo[d]:
			angle = chimera.angle(da.xformCoord(),
						d.xformCoord(), a.xformCoord())
			if angle > _testAngles[geom]:
				continue
			if isAcc:
				# lone pair pointing toward acceptor;
				# won't work
				addAtoD = addDtoA = False
				if debug:
					print "can't donate; lone pair (to %s) pointing toward acceptor (angle %g)" % (da.oslIdent(), angle)
				break
			addAtoD = False
			if debug:
				print "donor already pointing (angle %g) towards acceptor (due to %s)" % (angle, da.oslIdent())
	if not addAtoD and not addDtoA:
		return addAtoD, addDtoA
	if a in hbondInfo:
		geom = _typeInfo(a).geometry
		for isAcc, da in hbondInfo[a]:
			angle = chimera.angle(da.xformCoord(),
						a.xformCoord(), d.xformCoord())
			if angle > _testAngles[geom]:
				continue
			if not isAcc:
				# proton pointing toward donor; won't work
				if debug:
					print "can't accept; proton (to %s) pointing too much toward donor (angle %g)" % (da.oslIdent(), angle)
				addAtoD = addDtoA = False
				break
			addDtoA = False
			if debug:
				print "acceptor already pointing too much (angle %g) towards donor (due to %s)" % (angle, da.oslIdent())
	return addAtoD, addDtoA
Exemple #11
0
def interpInternal(c0map, c1map, f, cs, a0, a1, a2, a3):
	"""Computer coordinate of atom a0 by interpolating dihedral angle
	defined by atoms (a0, a1, a2, a3)"""
	import chimera
	c00 = c0map[a0]
	c01 = c0map[a1]
	c02 = c0map[a2]
	c03 = c0map[a3]
	length0 = c00.distance(c01)
	angle0 = chimera.angle(c00, c01, c02)
	dihed0 = chimera.dihedral(c00, c01, c02, c03)
	c10 = c1map[a0]
	c11 = c1map[a1]
	c12 = c1map[a2]
	c13 = c1map[a3]
	length1 = c10.distance(c11)
	angle1 = chimera.angle(c10, c11, c12)
	dihed1 = chimera.dihedral(c10, c11, c12, c13)
	length = length0 + (length1 - length0) * f
	angle = angle0 + (angle1 - angle0) * f
	ddihed = dihed1 - dihed0
	if ddihed > 180:
		ddihed -= 360
	elif ddihed < -180:
		ddihed += 360
	dihed = dihed0 + ddihed * f
	c1 = a1.coord(cs)
	c2 = a2.coord(cs)
	c3 = a3.coord(cs)
	from chimera import molEdit
	try:
		c0 = molEdit.findPt(c1, c2, c3, length, angle, dihed)
	except ValueError:
		print `a1`, a1
		print `a2`, a2
		print `a3`, a3
		raise
	a0.setCoord(c0, cs)
Exemple #12
0
def _lenAngle(new, n1, n2, tmplMap, bondCache, angleCache):
    from chimera import distance, angle
    bondKey = (n1, new)
    angleKey = (n2, n1, new)
    try:
        bl = bondCache[bondKey]
        ang = angleCache[angleKey]
    except KeyError:
        n2pos = tmplMap[n2].coord()
        n1pos = tmplMap[n1].coord()
        newpos = tmplMap[new].coord()
        bondCache[bondKey] = bl = distance(newpos, n1pos)
        angleCache[angleKey] = ang = angle(newpos, n1pos, n2pos)
    return bl, ang
def testTheta(dp, donorHyds, ap, theta):
	if len(donorHyds) == 0:
		if base.verbose:
			print "no hydrogens for theta test; default accept"
		return 1
	for hydPos in donorHyds:
		ang =  angle(ap, hydPos, dp)
		if ang >= theta:
			if base.verbose:
				print "theta okay (%g >= %g)" % (ang, theta)
			return 1
		if base.verbose:
			print "theta failure (%g < %g)" % (ang, theta)

	return 0
def testTheta(dp, donorHyds, ap, theta):
    if len(donorHyds) == 0:
        if base.verbose:
            print "no hydrogens for theta test; default accept"
        return 1
    for hydPos in donorHyds:
        ang = angle(ap, hydPos, dp)
        if ang >= theta:
            if base.verbose:
                print "theta okay (%g >= %g)" % (ang, theta)
            return 1
        if base.verbose:
            print "theta failure (%g < %g)" % (ang, theta)

    return 0
Exemple #15
0
def _canAccept(donor, acceptor, protons, lonePairs):
	# acceptor is fixed; can it accept from donor?
	if not protons:
		return True
	if not lonePairs:
		raise ValueError("No lone pairs on %s for %s to donate to" % (
							acceptor, donor))
	donPos = donor.xformCoord()
	hDist = min(map(lambda xyz: (xyz - donPos).sqlength(), protons))
	lpDist, lp = min(map(lambda xyz: ((xyz - donPos).sqlength(), xyz),
								lonePairs))
	# besides a lone pair being closest, it must be sufficiently pointed
	# towards the donor
	if lpDist >= hDist:
		from math import sqrt
		if debug:
			print "can't still accept; lp dist (%g) >= h dist (%g)" % (sqrt(lpDist), sqrt(hDist))
	elif chimera.angle(lp, acceptor.xformCoord(), donPos) >= _testAngles[
			typeInfo4Atom[acceptor].geometry]:
		if debug:
			print "can't still accept; angle (%g) >= test angle (%g)" % ( chimera.angle(lp, acceptor.xformCoord(), donPos), _testAngles[ typeInfo4Atom[acceptor].geometry])
	return lpDist < hDist and chimera.angle(lp,
			acceptor.xformCoord(), donPos) < _testAngles[
			typeInfo4Atom[acceptor].geometry]
def testThetaTau(dp, donorHyds, ap, pp, upsilonLow, upsilonHigh, theta):
	if pp:
		upsilonHigh = 0 - upsilonHigh
		upsilon = angle(pp, ap, dp)
		if upsilon < upsilonLow or upsilon > upsilonHigh:
			if base.verbose:
				print "upsilon (%g) failed (%g-%g)" % (
					upsilon, upsilonLow, upsilonHigh)
			return 0
	else:
		if base.verbose:
			print "can't determine upsilon; default okay"
	if base.verbose:
		print "upsilon okay"
	return testTheta(dp, donorHyds, ap, theta)
Exemple #17
0
def _tetCheck(tet, partner, hbondInfo, tetAcc):
	"""Check if tet can still work"""
	tetPos = tet.xformCoord()
	partnerPos = partner.xformCoord()
	bonded = tet.primaryNeighbors()
	tetInfo = hbondInfo.get(tet, [])
	towards = []
	# if there is a real bond to the tet, we want to check the
	# dihedral to the new position (using a 120 angle) rather than
	# the angle (109.5) since the vector from the tet to the not-
	# yet-added positions is probably not directly along the future bond
	if bonded:
		if debug:
			print "checking dihedral"
		chkFunc = lambda op, pp=partnerPos, tp=tetPos, \
			bp=bonded[0].xformCoord(): chimera.dihedral(
			pp, tp, bp, op) / 30.0
	else:
		if debug:
			print "checking angle"
		chkFunc = lambda op, pp=partnerPos, tp=tetPos: chimera.angle(
			pp, tp, op) / 27.375
	for isAcc, other in tetInfo:
		if isAcc == tetAcc:
			# same "polarity"; pointing towards is good
			result = True
		else:
			# opposite "polarity"; pointing towards is bad
			result = False
		angleGroup = int(chkFunc(other.xformCoord()))
		if angleGroup in [1,2,5,6]:
			# in the tetrahedral "dead zones"
			if debug:
				print "tetCheck for", tet.oslIdent(),
				print "; dead zone for", other.oslIdent()
			return False
		if angleGroup == 0:
			if debug:
				print "tetCheck for", tet.oslIdent(),
				print "; pointing towards", other.oslIdent(),
				print "returning", result
			return result
		towards.append(other.xformCoord())
	# further tests only for 2 of 4 filled...
	if bonded or len(towards) != 2:
		return True

	return _tet2check(tetPos, towards[0], towards[1], partnerPos)
Exemple #18
0
def formDihedral(resBud, real1, tmplRes, a, b):
    res = resBud.residue
    inres = filter(lambda a, rb=resBud, r=res: a.residue == r and a != rb,
                   real1.neighbors)
    if real1.residue != res or len(inres) < 1:
        raise AssertionError, "Can't form in-residue dihedral for" \
          " %s of residue %s" % (bud, res.oslIdent())
    real2 = inres[0]
    xyz0, xyz1, xyz2 = map(lambda a, tr=tmplRes: tr.atomsMap[a.name].coord(),
                           (resBud, real1, real2))

    xyz = a.coord()
    blen = b.length()
    ang = angle(xyz, xyz0, xyz1)
    dihed = dihedral(xyz, xyz0, xyz1, xyz2)
    return addDihedralAtom(a.name, a.element, resBud, real1, real2, blen, ang,
                           dihed)
Exemple #19
0
def formDihedral(resBud, real1, tmplRes, a, b):
	res = resBud.residue
	inres = filter(lambda a, rb=resBud, r=res: a.residue == r and a != rb,
							real1.neighbors)
	if real1.residue != res or len(inres) < 1:
		raise AssertionError, "Can't form in-residue dihedral for" \
				" %s of residue %s" % (bud, res.oslIdent())
	real2 = inres[0]
	xyz0, xyz1, xyz2 = map(lambda a, tr=tmplRes:
			tr.atomsMap[a.name].coord(), (resBud, real1, real2))

	xyz = a.coord()
	blen = b.length()
	ang = angle(xyz, xyz0, xyz1)
	dihed = dihedral(xyz, xyz0, xyz1, xyz2)
	return addDihedralAtom(a.name, a.element, resBud,
						real1, real2, blen, ang, dihed)
def test_include_dummies(element,
                         file,
                         charge,
                         geom,
                         dummies_xyz,
                         Model=Model):
    # Right Values
    ANGLE = {
        'tetrahedral': [
            60,
        ],
        'square planar': [90, 45],
        'square pyramid': [90, 45],
        'octahedron': [90, 60, 45],
    }
    # Create metal instance
    metal, metal_class = create_metal_class(element=element,
                                            charge=charge,
                                            geom=geom,
                                            file=file)
    # Initialize variables
    metal_class.dummies_xyz = dummies_xyz
    Model.geometry = geom
    # Func to test
    Model.include_dummies(metal_class)
    # Selecting the included atoms
    number_of_dummies = len(dummies_xyz)
    atoms_to_select = ','.join(
        ['D' + str(i) for i in range(1, number_of_dummies + 1)])
    rc("sel ::" + str(metal.residue.type) + '@' + atoms_to_select)
    dummies = chimera.selection.currentAtoms()

    # Eval Angles between them depending geometry
    for i in range(0, number_of_dummies - 2):
        angle = chimera.angle(dummies[i].labelCoord(),
                              dummies[i + 1].labelCoord(),
                              dummies[i + 2].labelCoord())
        assert (int(round(angle)) in ANGLE[geom])
    # Eval Bonds between Dummy&Metal center
    for i in range(0, number_of_dummies):
        bond = chimera.distance(dummies[i].labelCoord(), metal.labelCoord())
        assert (abs(bond - 0.9) < 0.001)
def testPhi(dp, ap, bp, phiPlane, phi):
    if phiPlane:
        normal = cross(phiPlane[1] - phiPlane[0], phiPlane[2] - phiPlane[1])
        normal.normalize()
        D = normal * phiPlane[1].toVector()
        bproj = project(bp, normal, D)
        aproj = project(ap, normal, D)
        dproj = project(dp, normal, D)

        ang = angle(bproj, aproj, dproj)
        if ang < phi:
            if base.verbose:
                print "phi criteria failed (%g < %g)" % (ang, phi)
            return 0
        if base.verbose:
            print "phi criteria OK (%g >= %g)" % (ang, phi)
    else:
        if base.verbose:
            print "phi criteria irrelevant"
    return 1
def testPhi(dp, ap, bp, phiPlane, phi):
	if phiPlane:
		normal = cross(phiPlane[1] - phiPlane[0],
						phiPlane[2] - phiPlane[1])
		normal.normalize()
		D = normal * phiPlane[1].toVector()
		bproj = project(bp, normal, D)
		aproj = project(ap, normal, D)
		dproj = project(dp, normal, D)
		
		ang = angle(bproj, aproj, dproj)
		if ang < phi:
			if base.verbose:
				print "phi criteria failed (%g < %g)" % (ang, phi)
			return 0
		if base.verbose:
			print "phi criteria OK (%g >= %g)" % (ang, phi)
	else:
		if base.verbose:
			print "phi criteria irrelevant"
	return 1
Exemple #23
0
def _pruneCheck(pivotAtom, goldHBond, testHBond):
	geom = _typeInfo(pivotAtom).geometry
	if geom < 2:
		return False
	if geom < 4 and _numBonds(pivotAtom) > 0:
		return False
	if goldHBond[0] == pivotAtom:
		ga = goldHBond[1]
	else:
		ga = goldHBond[0]
	if testHBond[0] == pivotAtom:
		ta = testHBond[1]
	else:
		ta = testHBond[0]
	angle = chimera.angle(ga.xformCoord(), pivotAtom.xformCoord(),
							ta.xformCoord())
	fullAngle = _testAngles[geom] * 3
	while angle > fullAngle / 2.0:
		angle -= fullAngle
	angle = abs(angle)
	return angle > fullAngle / 4.0
def addDihedralBond(a1, a2, length, angleInfo, dihedInfo):
	"""Make bond between two models.

	   The models will be combined and the originals closed.
	   The new model will be opened in the same id/subid as the
	       non-moving model.

	   a1/a2 are atoms in different models.
	   a2 and atoms in its model will be moved to form the bond.
		'length' is the bond length.
		'angleInfo' is a two-tuple of sequence of three atoms and
			an angle that the three atoms should form.
		'dihedInfo' is like 'angleInfo', but 4 atoms.
		angleInfo/dihedInfo can be None if insufficient atoms
	"""

	if a1.molecule == a2.molecule:
		raise ValueError("Atoms to be bonded must be in different models")

	# first, get the distance correct
	from chimera import Xform, cross, angle, Point
	dvector = a1.xformCoord() - a2.xformCoord()
	dvector.length = dvector.length + length
	openState = a2.molecule.openState
	openState.globalXform(Xform.translation(dvector))

	# then angle
	if angleInfo:
		atoms, angleVal = angleInfo
		p1, p2, p3 = [a.xformCoord() for a in atoms]
		axis = cross(p1-p2, p2-p3)
		curAngle = angle(p1, p2, p3)
		delta = angleVal - curAngle
		v2 = p2 - Point(0.0, 0.0, 0.0)
		trans1 = Xform.translation(v2)
		v2.negate()
		trans2 = Xform.translation(v2)
		trans1.multiply(Xform.rotation(axis, delta))
		trans1.multiply(trans2)
		openState.globalXform(trans1)
Exemple #25
0
def addDihedralBond(a1, a2, length, angleInfo, dihedInfo):
    """Make bond between two models.

	   The models will be combined and the originals closed.
	   The new model will be opened in the same id/subid as the
	       non-moving model.

	   a1/a2 are atoms in different models.
	   a2 and atoms in its model will be moved to form the bond.
		'length' is the bond length.
		'angleInfo' is a two-tuple of sequence of three atoms and
			an angle that the three atoms should form.
		'dihedInfo' is like 'angleInfo', but 4 atoms.
		angleInfo/dihedInfo can be None if insufficient atoms
	"""

    if a1.molecule == a2.molecule:
        raise ValueError("Atoms to be bonded must be in different models")

    # first, get the distance correct
    from chimera import Xform, cross, angle, Point
    dvector = a1.xformCoord() - a2.xformCoord()
    dvector.length = dvector.length + length
    openState = a2.molecule.openState
    openState.globalXform(Xform.translation(dvector))

    # then angle
    if angleInfo:
        atoms, angleVal = angleInfo
        p1, p2, p3 = [a.xformCoord() for a in atoms]
        axis = cross(p1 - p2, p2 - p3)
        curAngle = angle(p1, p2, p3)
        delta = angleVal - curAngle
        v2 = p2 - Point(0.0, 0.0, 0.0)
        trans1 = Xform.translation(v2)
        v2.negate()
        trans2 = Xform.translation(v2)
        trans1.multiply(Xform.rotation(axis, delta))
        trans1.multiply(trans2)
        openState.globalXform(trans1)
Exemple #26
0
def measure_curvature(mset):

    atoms = [m.atom for m in mset.markers()]
    a2 = [a for a in atoms if len(a.bonds) == 2]

    kmax = kmin = None
    ksum = 0
    for a in a2:
        b1, b2 = a.bonds
        s = 0.5 * (b1.length() + b2.length())
        from chimera import angle
        t = angle(a.coord() - b1.otherAtom(a).coord(),
                  b2.otherAtom(a).coord() - a.coord())
        from math import pi
        trad = t * pi / 180
        k = trad / s
        if kmin is None or k < kmin:
            kmin = k
        if kmax is None or k > kmax:
            kmax = k
        ksum += k
    kave = ksum / len(a2) if len(a2) > 0 else None
    return kave, kmin, kmax
    def search_for_ligands(metal):
        """
        Search for ligands near by the metal center
        excluding candidates through the next steps:
            1-How many electrons on the outter shell
            2-Metal-Ligand distance
            3-Geometry angles
            4-Exclude Hydrogens and other atoms

        Parameters:
        -----------
        metal: chimera metal object

        Output:
        -------
        chimera ligands object
        """
        # Extracted directly from:
        # Metal Geom Chimera
        data = []
        coordLim = 4.0
        from numpy import array
        atoms = array(metal.molecule.atoms)
        from _multiscale import get_atom_coordinates as gac
        from _closepoints import find_close_points, BOXES_METHOD
        ignore, close = find_close_points(BOXES_METHOD, gac(array([metal])),
                                          gac(atoms), coordLim)
        candidates = list(set(atoms[close]))
        mcrd = metal.coord()
        candidates.sort(lambda a1, a2: cmp(a1.coord().sqdistance(mcrd),
                                           a2.coord().sqdistance(mcrd)))
        exclude = []
        userIncluded = []
        for candidate in candidates:
            if candidate == metal:
                continue
            if candidate in exclude:
                continue
            if candidate not in userIncluded:
                valence = (candidate.element.number - 2) % 8
                if valence < 5 or candidate.element.number == 1:
                    continue
                if candidate.coord().distance(mcrd) > coordLim:
                    break
                if candidate not in metal.bondsMap:
                    from chimera import angle
                    from chimera.idatm import typeInfo
                    angleOK = True
                    try:
                        cnGeom = typeInfo[candidate.idatmType].geometry
                    except KeyError:
                        cnGeom = 0
                    else:
                        if len(candidate.primaryNeighbors()) == cnGeom:
                            # no lone pairs, no possibility of deprotonation
                            continue
                    angleCutoff = [0.0, 72.98, 120.0, 80.0, 72.98][cnGeom]
                    for cnb in candidate.neighbors:
                        if cnb == metal:
                            continue
                        if angle(cnb.coord(), candidate.coord(),
                                 metal.coord()) < angleCutoff:
                            angleOK = False
                            break
                    if not angleOK:
                        continue
            data.append(candidate)
        return data
def donUpsilonTau(donor, donorHyds, acceptor,
  sp2Or2, sp2OupsilonLow, sp2OupsilonHigh, sp2Otheta, sp2Otau,
  sp3Or2, sp3OupsilonLow, sp3OupsilonHigh, sp3Otheta, sp3Otau, sp3Ophi,
  sp3Nr2, sp3NupsilonLow, sp3NupsilonHigh, sp3Ntheta, sp3Ntau, sp3NupsilonN,
  genR2, genUpsilonLow, genUpsilonHigh, genTheta, tauSym):

	if base.verbose:
		print "donUpsilonTau"

	accType = acceptor.idatmType
	if not typeInfo.has_key(accType):
		return 0
	
	geom = typeInfo[accType].geometry
	element = acceptor.element.name
	if element == 'O' and geom == planar:
		if base.verbose:
			print "planar O"
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			sp2Or2, sp2OupsilonLow, sp2OupsilonHigh, sp2Otheta,
			sp2Otau, tauSym)
	elif element == 'O' and geom == tetrahedral \
	or element == 'N' and geom == planar:
		if base.verbose:
			print "planar N or tet O"
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			sp3Or2, sp3OupsilonLow, sp3OupsilonHigh, sp3Otheta,
			sp3Otau, tauSym)
	elif element == 'N' and geom == tetrahedral:
		if base.verbose:
			print "tet N"
		# test upsilon at the N
		# see if lone pairs point at the donor
		bondedPos = []
		for bonded in acceptor.primaryNeighbors():
			bondedPos.append(bonded.xformCoord())
		if len(bondedPos) > 1:
			ap = acceptor.xformCoord()
			dp = donor.xformCoord()
			lonePairs = bondPositions(ap, tetrahedral, 1.0,
								bondedPos)
			for lp in lonePairs:
				upPos = ap - (lp - ap)
				ang = angle(upPos, ap, dp)
				if ang >= sp3NupsilonN:
					if base.verbose:
						print "upsilon(N) okay" \
							" (%g >= %g)" % (ang,
							sp3NupsilonN)
					break
			else:
				if base.verbose:
					print "all upsilon(N) failed (< %g)" % (
								sp3NupsilonN)
				return 0
		elif base.verbose:
			print "lone pair positions indeterminate at N;" \
						"upsilon(N) default okay"
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			sp3Nr2, sp3NupsilonLow, sp3NupsilonHigh, sp3Ntheta,
			sp3Ntau, tauSym)
	else:
		if base.verbose:
			print "generic acceptor"
		if acceptor.element.name == "S":
			genR2 = sulphurCompensate(genR2)
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			genR2, genUpsilonLow, genUpsilonHigh, genTheta,
			None, None)
	if base.verbose:
		print "failed criteria"
	return 0
def testUpsilonTauAcceptor(donor, donorHyds, acceptor, r2, upsilonLow,
					upsilonHigh, theta, tau, tauSym):
	dc = donor.xformCoord()
	ac = acceptor.xformCoord()

	D2 = dc.sqdistance(ac)
	if D2 > r2:
		if base.verbose:
			print "dist criteria failed (%g > %g)" % (sqrt(D2),
								sqrt(r2))
		return 0

	upsilonHigh = 0 - upsilonHigh
	heavys = filter(lambda a: a.element.number > 1,
						donor.primaryNeighbors())
	if len(heavys) != 1:
		raise AtomTypeError("upsilon tau donor (%s) not bonded to"
			" exactly one heavy atom" % donor.oslIdent())
	ang = angle(heavys[0].xformCoord(), dc, ac)
	if ang < upsilonLow or ang > upsilonHigh:
		if base.verbose:
			print "upsilon criteria failed (%g < %g or %g > %g)" % (
				ang, upsilonLow, ang, upsilonHigh)
		return 0
	if base.verbose:
		print "upsilon criteria OK (%g < %g < %g)" % (upsilonLow, ang,
								upsilonHigh)

	dp = dc
	ap = ac

	if not testTheta(dp, donorHyds, ap, theta):
		return 0
	
	if tau is None:
		if base.verbose:
			print "tau test irrelevant"
		return 1

	# sulfonamides and phosphonamides can have bonded NH2 groups that
	# are planar enough to be declared Npl, so use the hydrogen
	# positions to determine planarity if possible
	if tauSym == 4:
		bondedPos = hydPositions(donor)
	else:
		# since we expect tetrahedral hydrogens to be oppositely
		# aligned from the attached tetrahedral center, 
		# we can't use their positions for tau testing
		bondedPos = []
	if 2 * len(bondedPos) != tauSym:
		bondedPos = hydPositions(heavys[0], includeLonePairs=True)
		donorEquiv = donor.allLocations()
		for b in heavys[0].primaryNeighbors():
			if b in donorEquiv or b.element.number < 2:
				continue
			bondedPos.append(b.xformCoord())
		if not bondedPos:
			if base.verbose:
				print "tau indeterminate; default okay"
			return 1

	if 2 * len(bondedPos) != tauSym:
		raise AtomTypeError("Unexpected tau symmetry (%d,"
				" should be %d) for donor %s" % (
				2 * len(bondedPos), tauSym, donor.oslIdent()))

	normal = heavys[0].xformCoord() - dp
	normal.normalize()

	if tau < 0.0:
		test = lambda ang, t=tau: ang <= 0.0 - t
	else:
		test = lambda ang, t=tau: ang >= t
	
	projAccPos = project(ap, normal, 0.0)
	projDonPos = project(dp, normal, 0.0)
	for bpos in bondedPos:
		projBpos = project(bpos, normal, 0.0)
		ang = angle(projAccPos, projDonPos, projBpos)
		if test(ang):
			if tau < 0.0:
				if base.verbose:
					print "tau okay (%g < %g)" % (ang, -tau)
				return 1
		else:
			if tau > 0.0:
				if base.verbose:
					print "tau too small (%g < %g)" % (ang,
									tau)
				return 0
	if tau < 0.0:
		if base.verbose:
			print "all taus too big (> %g)" % -tau
		return 0
	
	if base.verbose:
		print "all taus acceptable (> %g)" % tau
	return 1
Exemple #30
0
import chimera
from chimera import Plane, Xform, cross, Vector, Point

m = chimera.openModels.list()[0]
b = chimera.selection.currentBonds()[0]
bondVec = b.atoms[0].coord() - b.atoms[1].coord()
bondVec.normalize()
axis = Vector(1.0, 0.0, 0.0)
crossProd = cross(axis, bondVec)
if crossProd.sqlength() > 0:
	from math import acos, degrees
	xform = Xform.rotation(crossProd, degrees(acos(axis * bondVec)))
	xform.invert()
else:
	xform = Xform.identity()

m.openState.xform = xform

# okay, that puts the bond parallel to the X axis, now swing the plane of
# the rest of the molecule into the xy plane...
molPlane = Plane([a.xformCoord() for a in m.atoms[:3]])
angle = chimera.angle(molPlane.normal, Vector(0, 0, -1))
xform2 = Xform.rotation(b.atoms[0].xformCoord()-b.atoms[1].xformCoord(), angle)
xform2.multiply(xform)

m.openState.xform = xform2
def donThetaTau(donor, donorHyds, acceptor,
					sp2Orp2, sp2Otheta,
					sp3Orp2, sp3Otheta, sp3Ophi,
					sp3Nrp2, sp3Ntheta, sp3Nupsilon,
					genRp2, genTheta,
					isWater=0):
					# 'isWater' only for hydrogenless water
	if base.verbose:
		print "donThetaTau"
	if len(donorHyds) == 0 and not isWater:
		if base.verbose:
			print "No hydrogens; default failure"
		return 0
	ap = acceptor.xformCoord()
	dp = donor.xformCoord()

	accType = acceptor.idatmType
	if not typeInfo.has_key(accType):
		if base.verbose:
			print "Unknown acceptor type failure"
		return 0
	
	geom = typeInfo[accType].geometry
	element = acceptor.element.name
	if element == 'O' and geom == planar:
		if base.verbose:
			print "planar O"
		for hydPos in donorHyds:
			if sqdistance(hydPos, ap) <= sp2Orp2:
				break
		else:
			if not isWater:
				if base.verbose:
					print "dist criteria failed (all > %g)"\
						% sqrt(sp2Orp2)
				return 0
		theta = sp2Otheta
	elif element == 'O' and geom == tetrahedral \
	or element == 'N' and geom == planar:
		if base.verbose:
			print "planar N or tet O"
		for hydPos in donorHyds:
			if sqdistance(hydPos, ap) <= sp3Orp2:
				break
		else:
			if not isWater:
				if base.verbose:
					print "dist criteria failed (all > %g)"\
						% sqrt(sp3Orp2)
				return 0
		theta = sp3Otheta
		
		# only test phi for acceptors with two bonded atoms
		if len(acceptor.primaryBonds()) == 2:
			if base.verbose:
				print "testing donor phi"
			bonded = acceptor.primaryNeighbors()
			phiPlane, basePos = getPhiPlaneParams(acceptor,
							bonded[0], bonded[1])
			if not testPhi(donor.xformCoord(), ap, basePos,
							phiPlane, sp3Ophi):
				return 0

	elif element == 'N' and geom == tetrahedral:
		if base.verbose:
			print "tet N"
		for hydPos in donorHyds:
			if sqdistance(hydPos, ap) <= sp3Nrp2:
				break
		else:
			if not isWater:
				if base.verbose:
					print "dist criteria failed (all > %g)"\
						% sqrt(sp3Nrp2)
				return 0
		theta = sp3Ntheta

		# test upsilon against lone pair directions
		bondedPos = []
		for bonded in acceptor.primaryNeighbors():
			bondedPos.append(bonded.xformCoord())
		lpPos = bondPositions(ap, geom, 1.0, bondedPos)
		if lpPos:
			# fixed lone pair positions
			for lp in bondPositions(ap, geom, 1.0, bondedPos):
				# invert position so that we are
				# measuring angles correctly
				ang = angle(dp, ap, ap - (lp - ap))
				if ang > sp3Nupsilon:
					if base.verbose:
						print "acceptor upsilon okay"\
							" (%g > %g)" % (
							ang, sp3Nupsilon)
					break
			else:
				if base.verbose:
					print "all acceptor upsilons failed"\
						" (< %g)" % sp3Nupsilon
				return 0
		# else: indefinite lone pair positions; default okay
	else:
		if base.verbose:
			print "generic acceptor"
		if acceptor.element.name == "S":
			genRp2 = sulphurCompensate(genRp2)
		for hydPos in donorHyds:
			if sqdistance(hydPos, ap) <= genRp2:
				break
		else:
			if base.verbose:
				print "dist criteria failed (all > %g)" % sqrt(
								genRp2)
			return 0
		theta = genTheta
	if base.verbose:
		print "dist criteria OK"

	return testTheta(dp, donorHyds, ap, theta)
Exemple #32
0
import chimera
from chimera import Plane, Xform, cross, Vector, Point

m = chimera.openModels.list()[0]
b = chimera.selection.currentBonds()[0]
bondVec = b.atoms[0].coord() - b.atoms[1].coord()
bondVec.normalize()
axis = Vector(1.0, 0.0, 0.0)
crossProd = cross(axis, bondVec)
if crossProd.sqlength() > 0:
    from math import acos, degrees
    xform = Xform.rotation(crossProd, degrees(acos(axis * bondVec)))
    xform.invert()
else:
    xform = Xform.identity()

m.openState.xform = xform

# okay, that puts the bond parallel to the X axis, now swing the plane of
# the rest of the molecule into the xy plane...
molPlane = Plane([a.xformCoord() for a in m.atoms[:3]])
angle = chimera.angle(molPlane.normal, Vector(0, 0, -1))
xform2 = Xform.rotation(b.atoms[0].xformCoord() - b.atoms[1].xformCoord(),
                        angle)
xform2.multiply(xform)

m.openState.xform = xform2
def donGeneric(donor, donorHyds, acceptor, sp2Orp2, sp3Orp2, sp3Nrp2,
	sp2Or2, sp3Or2, sp3Nr2, genRp2, genR2, minHydAngle, minBondedAngle):
	if base.verbose:
		print "donGeneric"
	dc = donor.xformCoord()
	ac = acceptor.xformCoord()

	accType = acceptor.idatmType
	if not typeInfo.has_key(accType):
		return 0

	geom = typeInfo[accType].geometry
	element = acceptor.element.name
	if element == 'O' and geom == planar:
		if base.verbose:
			print "planar O"
		r2 = sp2Or2
		rp2 = sp2Orp2
	elif element == 'O' and geom == tetrahedral \
	or element == 'N' and geom == planar:
		if base.verbose:
			print "planar N or tet O"
		r2 = sp3Or2
		rp2 = sp3Orp2
	elif element == 'N' and geom == tetrahedral:
		if base.verbose:
			print "tet N"
		r2 = sp3Nr2
		rp2 = sp3Nrp2
	else:
		if base.verbose:
			print "generic acceptor"
		if acceptor.element.name == "S":
			r2 = sulphurCompensate(genR2)
			minBondedAngle = minBondedAngle - 9
		r2 = genR2
		rp2 = genRp2

	ap = acceptor.xformCoord()
	dp = donor.xformCoord()
	if len(donorHyds) == 0:
		D2 = dc.sqdistance(ac)
		if D2 > r2:
			if base.verbose:
				print "dist criteria failed (%g > %g)" % (
							sqrt(D2), sqrt(r2))
			return 0
	else:
		for hydPos in donorHyds:
			if sqdistance(hydPos, ap) < rp2:
				break
		else:
			if base.verbose:
				print "hyd dist criteria failed (all >= %g)" % (
								sqrt(rp2))
			return 0
		
	if base.verbose:
		print "dist criteria OK"

	for bonded in donor.primaryNeighbors():
		if bonded.element.number <= 1:
			continue
		bp = bonded.xformCoord()
		ang = angle(bp, dp, ap)
		if ang < minBondedAngle:
			if base.verbose:
				print "bonded angle too sharp (%g < %g)" % (
						ang, minBondedAngle)
			return 0
		
	if len(donorHyds) == 0:
		if base.verbose:
			print "No specific hydrogen positions; default accept"
		return 1

	for hydPos in donorHyds:
		ang = angle(dp, hydPos, ap)
		if ang >= minHydAngle:
			if base.verbose:
				print "hydrogen angle okay (%g >= %g)" % (
						ang, minHydAngle)
			return 1
	if base.verbose:
		print "hydrogen angle(s) too sharp (< %g)" % minHydAngle
	return 0
Exemple #34
0
def addHydrogens(atomList, *args):
	"""Add hydrogens to maximize h-bonding and minimize steric clashes.

	   First, add hydrogens that are fixed and always present.  Then,
	   find H-bonds only to possibly-hydrogen-needing donors.  For
	   aromatic nitogens protonate at least one on a six-membered ring.
	   For remaining rotamers with H-bonds, place hydrogens
	   to maximize H-bond interactions.  Then, for remaining rotamers,
	   minimize steric clashes via torsion-driving.

	   May not work well for atoms that are already partially
	   hydrogenated.
	"""

	if debug:
		print "here (test)"
	if atomList:
		if len(atomList[0].molecule.coordSets) > 1:
			replyobj("Adding H-bond-preserving hydrogens to "
				"trajectories not supported.\n")
			return
	if debug:
		print "here 2"
	global addAtoms, typeInfo4Atom, namingSchemas, hydrogenTotals, \
					idatmType, inversionCache, coordinations
	typeInfo4Atom, namingSchemas, hydrogenTotals, idatmType, hisNs, \
							coordinations = args
	inversionCache = {}
	addAtoms = {}
	for atom in atomList:
		addAtoms[atom] = 1
	from chimera import replyobj
	replyobj.status("Categorizing heavy atoms\n", blankAfter=0)
	problemAtoms = []
	fixed = []
	flat = []
	aroNrings = {}
	global aroAmines
	aroAmines = {}
	unsaturated = []
	saturated = []
	finished = {}
	hbondInfo = {}
	for a, crds in coordinations.items():
		hbondInfo[a] = [(True, crd) for crd in crds]
	for atom in atomList:
		bondingInfo = typeInfo4Atom[atom]
		numBonds = _numBonds(atom)
		substs = bondingInfo.substituents
		if numBonds >= substs:
			continue
		
		geom = bondingInfo.geometry
		if numBonds == 0:
			unsaturated.append(atom)
		elif numBonds == 1:
			if geom == linear:
				fixed.append(atom)
			elif geom == planar:
				if substs == 2:
					unsaturated.append(atom)
				else:
					if idatmType[atom] == 'Npl' \
					and idatmType[
					atom.primaryNeighbors()[0]] == 'Car':
						# primary aromatic amine
						aroAmines[atom] = True
					else:
						flat.append(atom)
			elif geom == tetrahedral:
				if substs < 4:
					unsaturated.append(atom)
				else:
					saturated.append(atom)
			else:
				raise AssertionError, "bad geometry for atom" \
					 " (%s) with one bond partner" \
					 % atom.oslIdent()
		elif numBonds == 2:
			if geom == tetrahedral:
				if substs == 3:
					unsaturated.append(atom)
				else:
					fixed.append(atom)
			elif geom == planar:
				aroNring = None
				if atom.element.number == 7:
					for ring in atom.minimumRings():
						if ring.aromatic():
							aroNring = ring
							break
				if aroNring:
					if aroNrings.has_key(aroNring):
						aroNrings[aroNring].append(atom)
					else:
						aroNrings[aroNring] = [atom]
				else:
					fixed.append(atom)
			else:
				raise AssertionError, "bad geometry for atom" \
					 " (%s) with two bond partners" \
					 % atom.oslIdent()
		elif numBonds == 3:
			if geom != tetrahedral:
				raise AssertionError, "bad geometry for atom" \
					 " (%s) with three bond partners" \
					 % atom.oslIdent()
			fixed.append(atom)
			
	# protonate aromatic nitrogens if only one nitrogen in ring:
	multiNrings = {}
	aroNs = {}
	for ring, ns in aroNrings.items():
		if len(ns) == 1:
			fixed.append(ns[0])
		elif ns[0] in hisNs:
			for n in ns:
				if hisNs[n]:
					fixed.append(n)
		else:
			multiNrings[ring] = ns
			for n in ns:
				aroNs[n] = True
	if debug:
		print len(fixed), "fixed"
		print len(flat), "flat"
		print len(aroAmines), "primary aromatic amines"
		print len(multiNrings), "aromatic multi-nitrogen rings"
		print len(unsaturated), "unsaturated"
		print len(saturated), "saturated rotamers"

	replyobj.status("Building search tree of atom positions\n",
								blankAfter=0)
	# since adaptive search tree is static, it will not include
	# hydrogens added after this; they will have to be found by
	# looking off their heavy atoms

	for needCoplanar in [False, True]:
		if needCoplanar:
			atoms = flat
			replyobj.status("Adding co-planar hydrogens\n",
								blankAfter=0)
		else:
			atoms = fixed
			replyobj.status("Adding simple fixed hydrogens\n",
								blankAfter=0)
			
		for atom in atoms:
			bondingInfo = _typeInfo(atom)
			geom = bondingInfo.geometry
			bondLength = bondWithHLength(atom, geom)
			neighbors = []
			neighborAtoms = []
			for n in atom.primaryNeighbors():
				neighborAtoms.append(n)
				neighbors.append(n.xformCoord())

			coplanar = []
			if needCoplanar:
				for na in neighborAtoms:
					for nna in na.primaryNeighbors():
						if nna != atom:
							coplanar.append(
							  nna.xformCoord())
			if len(coplanar) > 2:
				problemAtoms.append(atom)
				continue
			Hpositions = bondPositions(atom.xformCoord(), geom,
				bondLength, neighbors, coPlanar=coplanar)
			_attachHydrogens(atom, Hpositions)
			finished[atom] = True
			hbondInfo[atom] = (Hpositions, [])
		
	from FindHBond import findHBonds, recDistSlop, recAngleSlop
	from chimera import replyobj

	replyobj.status("Finding hydrogen bonds\n", blankAfter=0)
	donors = {}
	acceptors = {}
	for atom in unsaturated:
		donors[atom] = unsaturated
		acceptors[atom] = unsaturated
	for atom in saturated:
		donors[atom] = saturated
	for ring, ns in multiNrings.items():
		for n in ns:
			donors[n] = ring
			acceptors[n] = ring
	for atom in aroAmines:
		donors[atom] = aroAmines
		acceptors[atom] = aroAmines

	hbonds = findHBonds(chimera.openModels.list(
				modelTypes=[chimera.Molecule]),
				distSlop=recDistSlop, angleSlop=recAngleSlop)
	replyobj.info("%d hydrogen bonds\n" % len(hbonds))
	# want to assign hydrogens to strongest (shortest) hydrogen
	# bonds first, so sort by distance
	replyobj.status("Sorting hydrogen bonds by distance\n", blankAfter=0)
	sortableHbonds = []
	for d, a in hbonds:
		if d in donors:
			sortableHbonds.append((d.xformCoord().sqdistance(
					a.xformCoord()), False, (d, a)))
		if a in acceptors:
			sortableHbonds.append((d.xformCoord().sqdistance(
					a.xformCoord()), True, (d, a)))
	sortableHbonds.sort()
	replyobj.status("Organizing h-bond info\n", blankAfter=0)
	hbonds = {}
	ambiguous = {}
	relBond = {}
	for dsq, isAcc, hbond in sortableHbonds:
		hbonds[hbond] = isAcc
		for da in hbond:
			relBond.setdefault(da, []).append(hbond)
	processed = set()
	breakAmbiguous = False
	breakNring = False
	candidates = {}
	candidates.update(donors)
	candidates.update(acceptors)
	pruned = set()
	prunedBy = {}
	reexamine = {}
	replyobj.status("Adding hydrogens by h-bond strength\n", blankAfter=0)
	while len(processed) < len(hbonds):
		replyobj.status("Adding hydrogens by h-bond strength (%d/%d)\n" % (len(processed), len(hbonds)), blankAfter=0)
		processedOne = False
		seenAmbiguous = {}
		for dsq, isAcc, hbond in sortableHbonds:
			if hbond in processed:
				continue
			d, a = hbond
			if hbond in pruned:
				if hbond in reexamine:
					del reexamine[hbond]
					if debug:
						print "re-examining", hbond[0].oslIdent(), "->", hbond[1].oslIdent()
				elif not breakAmbiguous:
					seenAmbiguous[d] = True
					seenAmbiguous[a] = True
					continue
			if (d not in candidates or d in finished) \
			and (a not in candidates or a in finished):
				# not relevant
				processed.add(hbond)
				continue

			if (a, d) in hbonds \
			and a not in finished \
			and d not in finished \
			and not _resolveAnilene(d, a, aroAmines, hbondInfo) \
			and _angleCheck(d, a, hbondInfo) == (True, True):
				# possibly still ambiguous

				# if one or both ends are aromatic nitrogen,
				# then ambiguity depends on whether other
				# nitrogens in ring are finished and are
				# acceptors...
				nring = False
				try:
					ns = multiNrings[acceptors[a]]
				except (KeyError, TypeError):
					ns = []
				if len(ns) > 1:
					nring = True
					for n in ns:
						if n not in finished:
							break
						protons, lps = hbondInfo[n]
						if protons:
							break
					else:
						# unambiguous, but in the
						# reverse direction!
						processed.add(hbond)
						if debug:
							print "reverse ambiguity resolved"
						continue
				unambiguous = False
				try:
					ns = multiNrings[donors[d]]
				except (KeyError, TypeError):
					ns = []
				if len(ns) > 1:
					nring = True
					for n in ns:
						if n not in finished:
							break
						protons, lps = hbondInfo[n]
						if protons:
							break
					else:
						if debug:
							print "ambiguity resolved due to ring acceptors"
						unambiguous = True

				if not unambiguous:
					if not breakAmbiguous \
					or nring and not breakNring:
						if debug:
							print "postponing", [a.oslIdent() for a in hbond]
						seenAmbiguous[d] = True
						seenAmbiguous[a] = True
						if hbond not in pruned:
							_doPrune(hbond, pruned, relBond, processed, prunedBy)
						continue
					if nring:
						if debug:
							print "breaking ambiguous N-ring hbond"
					else:
						if debug:
							print "breaking ambiguous hbond"
			if (a, d) in hbonds:
				if a in finished:
					if debug:
						print "breaking ambiguity because acceptor finished"
				elif d in finished:
					if debug:
						print "breaking ambiguity because donor finished"
				elif _resolveAnilene(d, a, aroAmines, hbondInfo):
					if debug:
						print "breaking ambiguity by resolving anilene"
				elif _angleCheck(d, a, hbondInfo) != (True, True):
					if debug:
						print "breaking ambiguity due to angle check"
			processed.add(hbond)
			from math import sqrt
			if debug:
				print "processed", d.oslIdent(), "->", a.oslIdent(), sqrt(dsq)
			# if donor or acceptor is tet,
			# see if geometry can work out
			if d not in finished and _typeInfo(d).geometry == 4:
				if not _tetCheck(d, a, hbondInfo, False):
					continue
			if a not in finished and _typeInfo(a).geometry == 4:
				if d in finished:
					checks = []
					for b in d.primaryNeighbors():
						if b.element.number == 1:
							checks.append(b)
				else:
					checks = [d]
				tetOkay = True
				for c in checks:
					if not _tetCheck(a, c, hbondInfo, True):
						tetOkay = False
						break
				if not tetOkay:
					continue
			if a in finished:
				# can d still donate to a?
				if not _canAccept(d, a, *hbondInfo[a]):
					# can't
					continue
				hbondInfo.setdefault(d, []).append((False, a))
			elif d in finished:
				# can d still donate to a?
				noProtonsOK = d in aroNs and _numBonds(d) == 2
				if not _canDonate(d, a, noProtonsOK,
								*hbondInfo[d]):
					# nope
					continue
				hbondInfo.setdefault(a, []).append((True, d))
			else:
				addAtoD, addDtoA = _angleCheck(d, a,
								hbondInfo)
				if not addAtoD and not addDtoA:
					continue
				if addAtoD:
					hbondInfo.setdefault(d, []).append(
								(False, a))
				if addDtoA:
					hbondInfo.setdefault(a, []).append(
								(True, d))
			if (a, d) in hbonds:
				processed.add((a, d))
			processedOne = True
			breakAmbigous = False
			breakNring = False

			if hbond not in pruned:
				_doPrune(hbond, pruned, relBond, processed, prunedBy)
			for end in hbond:
				if end in finished or end not in candidates:
					continue
				if end not in hbondInfo:
					# steric clash from other end
					if debug:
						print "no hbonds left for", end.oslIdent()
					continue
				if debug:
					print "try to finish", end.oslIdent(),
					for isAcc, da in hbondInfo[end]:
						if isAcc:
							print " accepting from", da.oslIdent(),
						else:
							print " donating to", da.oslIdent(),
					print
				didFinish = _tryFinish(end, hbondInfo, finished,
						aroAmines, prunedBy, processed)
				if not didFinish:
					continue
				if debug:
					print "finished", end.oslIdent()

				# if this atom is in candidates 
				# then actually add any hydrogens
				if end in candidates and hbondInfo[end][0]:
					_attachHydrogens(end, hbondInfo[end][0])
					if debug:
						print "protonated", end.oslIdent()
					# if ring nitrogen, eliminate
					# any hbonds where it is an acceptor
					if isinstance(donors[end],chimera.Ring):
						for rb in relBond[end]:
							if rb[1] != end:
								continue
							processed.add(rb)
								
			if a in seenAmbiguous or d in seenAmbiguous:
				# revisit previously ambiguous
				if debug:
					print "revisiting previous ambiguous"
				for da in hbond:
					for rel in relBond[da]:
						if rel in processed:
							continue
						if rel not in pruned:
							continue
						reexamine[rel] = True
				break
		if breakAmbiguous and not processedOne:
			breakNring = True
		breakAmbiguous = not processedOne
	numFinished = 0
	for a in candidates.keys():
		if a in finished:
			numFinished += 1
	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Adding hydrogens to primary aromatic amines\n",
								blankAfter=0)
	if debug:
		print "primary aromatic amines"
	for a in aroAmines:
		if a in finished:
			continue
		if a not in candidates:
			continue
		if debug:
			print "amine", a.oslIdent()
		finished[a] = True
		numFinished += 1
		
		# point protons right toward acceptors;
		# if also accepting, finish as tet, otherwise as planar
		acceptFrom = None
		targets = []
		atPos = a.xformCoord()
		for isAcc, other in hbondInfo.get(a, []):
			if isAcc:
				if not acceptFrom:
					if debug:
						print "accept from", other.oslIdent()
					acceptFrom = other.xformCoord()
				continue
			if debug:
				print "possible donate to", other.oslIdent()
			# find nearest lone pair position on acceptor
			target = _findTarget(a, atPos, other, not isAcc,
							hbondInfo, finished)
			# make sure this proton doesn't form
			# an angle < 90 degrees with any other bonds
			badAngle = False
			for bonded in a.primaryNeighbors():
				if chimera.angle(bonded.xformCoord(),
							atPos, target) < 90.0:
					badAngle = True
					break
			if badAngle:
				if debug:
					print "bad angle"
				continue
			if targets and chimera.angle(targets[0], atPos,
								target) < 90.0:
				if debug:
					print "bad angle"
				continue
			targets.append(target)
			if len(targets) > 1:
				break

		positions = []
		for target in targets:
			vec = target - atPos
			vec.normalize()
			positions.append(atPos +
					vec * bondWithHLength(a, _typeInfo(a).geometry))

		if len(positions) < 2:
			if acceptFrom:
				geom = 4
				knowns = positions + [acceptFrom]
				coPlanar = None
			else:
				geom = 3
				knowns = positions
				coPlanar = []
				for bonded in a.primaryNeighbors():
					for b2 in bonded.primaryNeighbors():
						if a == b2:
							continue
						coPlanar.append(b2.xformCoord())
			sumVec = chimera.Vector()
			for x in a.primaryNeighbors():
				vec = x.xformCoord() - atPos
				vec.normalize()
				sumVec += vec
			for k in knowns:
				vec = k - atPos
				vec.normalize()
				sumVec += vec
			sumVec.negate()
			sumVec.normalize()

			newPos = bondPositions(atPos, geom, bondWithHLength(a, geom),
				[nb.xformCoord() for nb in a.primaryNeighbors()] + knowns,
				coPlanar=coPlanar)
			positions.extend(newPos)
		_attachHydrogens(a, positions)
		if acceptFrom:
			accVec = acceptFrom - atPos
			accVec.length = vdwRadius(a)
			accs = [atPos + accVec]
		else:
			accs = []
		hbondInfo[a] = (positions, accs)

	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Using steric criteria to resolve partial h-bonders\n",
								blankAfter=0)
	for a in candidates.keys():
		if a in finished:
			continue
		if a not in hbondInfo:
			continue
		finished[a] = True
		numFinished += 1

		bondingInfo = _typeInfo(a)
		geom = bondingInfo.geometry

		numBonds = _numBonds(a)
		hydsToPosition = bondingInfo.substituents - numBonds
		openings = geom - numBonds

		hbInfo = hbondInfo[a]
		towardAtom = hbInfo[0][1]
		if debug:
			print a.oslIdent(), "toward", towardAtom.oslIdent(),
		towardPos = towardAtom.xformCoord()
		toward2 = away2 = None
		atPos = a.xformCoord()
		if len(hbInfo) > 1:
			toward2 = hbInfo[1][1].xformCoord()
			if debug:
				print "and toward", hbInfo[1][1].oslIdent()
		elif openings > 3:
			# okay, we need an "away from" atom just to position
			# the rotamer
			away2, dist, nearA = findNearest(atPos, a, [towardAtom],
								_nearDist)
			if debug:
				print "and away from nearest other",
				if away2:
					print nearA.oslIdent(), "[%.2f]" % dist
				else:
					print "(none)"
		else:
			if debug:
				print "with no other positioning determinant"
		bondedPos = []
		for bonded in a.primaryNeighbors():
			bondedPos.append(bonded.xformCoord())
		positions = bondPositions(atPos, geom,
			bondWithHLength(a, geom), bondedPos,
			toward=towardPos, toward2=toward2, away2=away2)
		if len(positions) == hydsToPosition:
			# easy, do them all...
			_attachHydrogens(a, positions)
			continue

		used = {}
		for isAcc, other in hbInfo:
			nearest = None
			otherPos = other.xformCoord()
			for pos in positions:
				dsq = (pos - otherPos).sqlength()
				if not nearest or dsq < nsq:
					nearest = pos
					nsq = dsq
			if nearest in used:
				continue
			used[nearest] = isAcc
			
		remaining = []
		for pos in positions:
			if pos in used:
				continue
			remaining.append(pos)
		# definitely protonate the positions where we donate...
		protonate = []
		for pos, isAcc in used.items():
			if not isAcc:
				protonate.append(pos)
		# ... and the "roomiest" remaining positions.
		rooms = roomiest(remaining, a, _roomDist)
		needed = hydsToPosition - len(protonate)
		protonate.extend(rooms[:needed])
		# then the least sterically challenged...
		_attachHydrogens(a, protonate)
		hbondInfo[a] = (protonate, rooms[needed:])

	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Adding hydrogens to non-h-bonding atoms\n",
								blankAfter=0)
	for a in candidates.keys():
		if a in finished:
			continue
		if a in aroNs:
			continue
		finished[a] = True
		numFinished += 1

		bondingInfo = _typeInfo(a)
		geom = bondingInfo.geometry

		primaryNeighbors = a.primaryNeighbors()
		numBonds = len(primaryNeighbors)
		hydsToPosition = bondingInfo.substituents - numBonds
		openings = geom - numBonds

		away = None
		away2 = None
		toward = None
		atPos = a.xformCoord()
		if debug:
			print "position", a.oslIdent(),
		if openings > 2:
			# okay, we need an "away from" atom for positioning
			#
			# if atom is tet with one bond (i.e. possible positions
			# describe a circle away from the bond), then use the
			# center of the circle as the test position rather 
			# than the atom position itself
			if geom == 4 and openings == 3:
				away, dist, awayAtom = findRotamerNearest(atPos,
						idatmType[a], a,
						primaryNeighbors[0], 3.5)
			else:
				away, dist, awayAtom = findNearest(atPos, a,
								[], _nearDist)

			# actually, if the nearest atom is a metal and we have
			# a free lone pair, we want to position (the lone
			# pair) towards the metal
			if awayAtom and awayAtom.element in metals \
			  and geom - bondingInfo.substituents > 0:
				if debug:
					print "towards metal", awayAtom.oslIdent(), "[%.2f]" % dist,
				toward = away
				away = None
			else:
				if debug:
					print "away from",
					if awayAtom:
						print awayAtom.oslIdent(), "[%.2f]" % dist,
					else:
						print "(none)",
		if openings > 3 and away is not None:
			# need another away from
			away2, dist, nearA = findNearest(atPos, a, [awayAtom],
								_nearDist)
			if debug:
				print "and away from",
				if nearA:
					print nearA.oslIdent(), "[%.2f]" % dist,
				else:
					print "(none)",
		if debug:
			print
		bondedPos = []
		for bonded in primaryNeighbors:
			bondedPos.append(bonded.xformCoord())
		positions = bondPositions(atPos, geom,
			bondWithHLength(a, geom),
			bondedPos, toward=toward, away=away, away2=away2)
		if len(positions) == hydsToPosition:
			# easy, do them all...
			_attachHydrogens(a, positions)
			continue

		# protonate "roomiest" positions
		_attachHydrogens(a, roomiest(positions, a, _roomDist)[:hydsToPosition])

	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Deciding aromatic nitrogen protonation\n",
								blankAfter=0)
	# protonate one N of an aromatic multiple-N ring if none are yet
	# protonated
	for ns in multiNrings.values():
		anyBonded = True
		Npls = []
		for n in ns:
			if _numBonds(n) > 2:
				break
			if n not in coordinations \
					and _typeInfo(n).substituents == 3:
				Npls.append(n)
		else:
			anyBonded = False
		if anyBonded:
			if debug:
				print ns[0].oslIdent(), "ring already protonated"
			continue
		if not Npls:
			Npls = ns

		positions = []
		for n in Npls:
			bondedPos = []
			for bonded in n.primaryNeighbors():
				bondedPos.append(bonded.xformCoord())
			positions.append(bondPositions(n.xformCoord(), planar,
				bondWithHLength(n, planar), bondedPos)[0])
		if len(positions) == 1:
			if debug:
				print "protonate precise ring", Npls[0].oslIdent()
			_attachHydrogens(Npls[0], positions)
		else:
			if debug:
				print "protonate roomiest ring", roomiest(positions, Npls, _roomDist)[0][0].oslIdent()
			_attachHydrogens(*roomiest(positions, Npls, _roomDist)[0])
	# now correct IDATM types of these rings...
	for ns in multiNrings.values():
		for n in ns:
			if len(n.bonds) == 3:
				n.idatmType = "Npl"
			else:
				n.idatmType = "N2"

	replyobj.status("Hydrogens added\n")
	if problemAtoms:
		replyobj.error("Problems adding hydrogens to %d atom(s); see Reply Log for details\n" % len(problemAtoms))
		from chimera.misc import chimeraLabel
		replyobj.info("Did not protonate following atoms:\n%s\n"
			% "\t".join(map(chimeraLabel, problemAtoms)))
	addAtoms = None
	typeInfo4Atom = namingSchemas = hydrogenTotals = idatmType \
			= aroAmines = inversionCache = coordinations = None
Exemple #35
0
def changeAtom(atom, element, geometry, numBonds, autoClose=True, name=None):
    if len(atom.primaryBonds()) > numBonds:
        raise ParamError(
            "Atom already has more bonds than requested.\n"
            "Either delete some bonds or choose a different number"
            " of requested bonds.")
    from chimera.molEdit import addAtom, genAtomName
    changedAtoms = [atom]
    if not name:
        name = genAtomName(element, atom.residue)
    changeAtomName(atom, name)
    atom.element = element
    if hasattr(atom, 'mol2type'):
        delattr(atom, 'mol2type')

    # if we only have one bond, correct its length
    if len(atom.primaryBonds()) == 1:
        neighbor = atom.primaryNeighbors()[0]
        newLength = bondLength(atom,
                               geometry,
                               neighbor.element,
                               a2info=(neighbor, numBonds))
        setBondLength(atom.primaryBonds()[0],
                      newLength,
                      movingSide="smaller side")

    if numBonds == len(atom.primaryBonds()):
        return changedAtoms

    from chimera.bondGeom import bondPositions
    coPlanar = None
    if geometry == 3 and len(atom.primaryBonds()) == 1:
        n = atom.primaryNeighbors()[0]
        if len(n.primaryBonds()) == 3:
            coPlanar = [
                nn.coord() for nn in n.primaryNeighbors() if nn != atom
            ]
    away = None
    if geometry == 4 and len(atom.primaryBonds()) == 1:
        n = atom.primaryNeighbors()[0]
        if len(n.primaryBonds()) > 1:
            nn = n.primaryNeighbors()[0]
            if nn == atom:
                nn = n.primaryNeighbors()[1]
            away = nn.coord()
    hydrogen = Element("H")
    positions = bondPositions(atom.coord(),
                              geometry,
                              bondLength(atom, geometry, hydrogen),
                              [n.coord() for n in atom.primaryNeighbors()],
                              coPlanar=coPlanar,
                              away=away)[:numBonds - len(atom.primaryBonds())]
    if autoClose:
        if len(atom.molecule.atoms) < 100:
            testAtoms = atom.molecule.atoms
        else:
            from CGLutil.AdaptiveTree import AdaptiveTree
            tree = AdaptiveTree(
                [a.coord().data() for a in atom.molecule.atoms],
                a.molecule.atoms, 2.5)
            testAtoms = tree.searchTree(atom.coord().data(), 5.0)
    else:
        testAtoms = []
    for pos in positions:
        for ta in testAtoms:
            if ta == atom:
                continue
            testLen = bondLength(ta, 1, hydrogen)
            testLen2 = testLen * testLen
            if (ta.coord() - pos).sqlength() < testLen2:
                bonder = ta
                # possibly knock off a hydrogen to
                # accomodate the bond...
                for bn in bonder.primaryNeighbors():
                    if bn.element.number > 1:
                        continue
                    if chimera.angle(atom.coord() - ta.coord(),
                                     bn.coord() - ta.coord()) > 45.0:
                        continue
                    if bn in testAtoms:
                        testAtoms.remove(bn)
                    atom.molecule.deleteAtom(bn)
                    break
                break
        else:
            bonder = addAtom(genAtomName(hydrogen, atom.residue),
                             hydrogen,
                             atom.residue,
                             pos,
                             bondedTo=atom)
            changedAtoms.append(bonder)
    return changedAtoms
def metalClash(metalPos, pos, parentPos):
    if metalPos.distance(parentPos) > _metalDist:
        return False
    if chimera.angle(parentPos, pos, metalPos) > 135.0:
        return True
    return False
def changeAtom(atom, element, geometry, numBonds, autoClose=True, name=None):
	if len(atom.primaryBonds()) > numBonds:
		raise ParamError("Atom already has more bonds than requested.\n"
			"Either delete some bonds or choose a different number"
			" of requested bonds.")
	from chimera.molEdit import addAtom, genAtomName
	changedAtoms = [atom]
	if not name:
		name = genAtomName(element, atom.residue)
	changeAtomName(atom, name)
	atom.element = element
	if hasattr(atom, 'mol2type'):
		delattr(atom, 'mol2type')
		
	# if we only have one bond, correct its length
	if len(atom.primaryBonds()) == 1:
		neighbor = atom.primaryNeighbors()[0]
		newLength = bondLength(atom, geometry, neighbor.element,
						a2info=(neighbor, numBonds))
		setBondLength(atom.primaryBonds()[0], newLength,
					movingSide="smaller side")

	if numBonds == len(atom.primaryBonds()):
		return changedAtoms

	from chimera.bondGeom import bondPositions
	coPlanar = None
	if geometry == 3 and len(atom.primaryBonds()) == 1:
		n = atom.primaryNeighbors()[0]
		if len(n.primaryBonds()) == 3:
			coPlanar = [nn.coord() for nn in n.primaryNeighbors()
								if nn != atom]
	away = None
	if geometry == 4 and len(atom.primaryBonds()) == 1:
		n = atom.primaryNeighbors()[0]
		if len(n.primaryBonds()) > 1:
			nn = n.primaryNeighbors()[0]
			if nn == atom:
				nn = n.primaryNeighbors()[1]
			away = nn.coord()
	hydrogen = Element("H")
	positions = bondPositions(atom.coord(), geometry,
		bondLength(atom, geometry, hydrogen),
		[n.coord() for n in atom.primaryNeighbors()], coPlanar=coPlanar,
		away=away)[:numBonds-len(atom.primaryBonds())]
	if autoClose:
		if len(atom.molecule.atoms) < 100:
			testAtoms = atom.molecule.atoms
		else:
			from CGLutil.AdaptiveTree import AdaptiveTree
			tree = AdaptiveTree([a.coord().data()
						for a in atom.molecule.atoms],
						a.molecule.atoms, 2.5)
			testAtoms = tree.searchTree(atom.coord().data(), 5.0)
	else:
		testAtoms = []
	for pos in positions:
		for ta in testAtoms:
			if ta == atom:
				continue
			testLen = bondLength(ta, 1, hydrogen)
			testLen2 = testLen * testLen
			if (ta.coord() - pos).sqlength() < testLen2:
				bonder = ta
				# possibly knock off a hydrogen to
				# accomodate the bond...
				for bn in bonder.primaryNeighbors():
					if bn.element.number > 1:
						continue
					if chimera.angle(atom.coord()
							- ta.coord(), bn.coord()
							- ta.coord()) > 45.0:
						continue
					if bn in testAtoms:
						testAtoms.remove(bn)
					atom.molecule.deleteAtom(bn)
					break
				break
		else:
			bonder = addAtom(genAtomName(hydrogen, atom.residue),
				hydrogen, atom.residue, pos, bondedTo=atom)
			changedAtoms.append(bonder)
	return changedAtoms