def sphericalCoordinates(x, y, z): """Returns the r, theta and phi spherical coordinates corresponding to x, y z cartesian coordinates. @param x: the cartesian x. @type x: float @param y: the cartesian y. @type y: float @param z: the cartesian z. @type z: float @return: the r, theta and phi spherical coordinates.. @rtype: a list of three floats """ # The spherical radius is computed r = N.sqrt(x**2 + y**2 + z**2) # The spherical theta is computed theta = N.arccos(z / r) # The spherical phi is computed phi = N.arctan2(y, x) return r, theta, phi
def __init__(self, *parameters): """ :param parameters: one of 1) three lattice vectors or 2) six numbers: the lengths of the three lattice vectors (a, b, c) followed by the three angles (alpha, beta, gamma). """ if len(parameters) == 6: self.a, self.b, self.c, self.alpha, self.beta, self.gamma = \ parameters e1 = Vector(self.a, 0, 0) e2 = self.b * Vector(N.cos(self.gamma), N.sin(self.gamma), 0.) e3_x = N.cos(self.beta) e3_y = (N.cos(self.alpha)-N.cos(self.beta)*N.cos(self.gamma)) \ / N.sin(self.gamma) e3_z = N.sqrt(1. - e3_x**2 - e3_y**2) e3 = self.c * Vector(e3_x, e3_y, e3_z) self.basis = (e1, e2, e3) elif len(parameters) == 3: assert isVector(parameters[0]) assert isVector(parameters[1]) assert isVector(parameters[2]) self.basis = list(parameters) e1, e2, e3 = self.basis self.a = e1.length() self.b = e2.length() self.c = e3.length() self.alpha = N.arccos(e2 * e3 / (self.b * self.c)) self.beta = N.arccos(e1 * e3 / (self.a * self.c)) self.gamma = N.arccos(e1 * e2 / (self.a * self.b)) else: raise ValueError("Parameter list incorrect") r = LA.inverse(N.transpose([e1, e2, e3])) self.reciprocal_basis = [Vector(r[0]), Vector(r[1]), Vector(r[2])]
def shouldPass(self, Vector, isVector): # Create vector objects v1 = Vector(1., -2., 3.) v2 = Vector([-2., 1., 0.]) # Check that vectors are not copied v1_copy = copy.copy(v1) self.assertTrue(v1 is v1_copy) v1_copy = copy.deepcopy(v1) self.assertTrue(v1 is v1_copy) # check len and element access self.assertEqual(len(v1), 3) self.assertEqual(v1[0], 1.) self.assertEqual(v1[1], -2.) self.assertEqual(v1[2], 3.) self.assertEqual(v1[-3], 1.) self.assertEqual(v1[-2], -2.) self.assertEqual(v1[-1], 3.) self.assertEqual(v1.x(), 1.) self.assertEqual(v1.y(), -2.) self.assertEqual(v1.z(), 3.) # Check arithmetic self.assertEqual(v1 + v2, Vector(-1., -1., 3.)) self.assertEqual(v1 - v2, Vector(3., -3., 3.)) self.assertEqual(-v1, Vector(-1., 2., -3.)) self.assertEqual(v1 * v2, -4.) self.assertEqual(2. * v1, Vector(2., -4., 6.)) self.assertEqual(v1 / 0.5, Vector(2., -4., 6.)) # Check comparisons self.assertTrue(v1 == v1) self.assertFalse(v1 == v2) self.assertFalse(v1 == None) # Check methods self.assertAlmostEqual(v1.length(), N.sqrt(14.), 12) self.assertAlmostEqual(v1.normal()[0], v1[0] / N.sqrt(14.), 12) self.assertAlmostEqual(v1.normal()[1], v1[1] / N.sqrt(14.), 12) self.assertAlmostEqual(v1.normal()[2], v1[2] / N.sqrt(14.), 12) self.assertAlmostEqual(v1.cross(v1).length(), 0., 12) self.assertEqual(v1.cross(v2), Vector(-3., -6., -3.)) self.assertAlmostEqual(v1.angle(v1), 0., 12) self.assertAlmostEqual(v1.angle(v2), N.arccos(v1.normal() * v2.normal()), 12) dp = v1.dyadicProduct(v2) for i in range(3): for j in range(3): self.assertEqual(dp[i, j], v1[i] * v2[j]) self.assertTrue(N.logical_and.reduce(v1.asTensor().array == v1.array)) # Check isVector self.assertTrue(isVector(v1)) self.assertTrue(isVector(v2)) self.assertFalse(isVector(0.)) self.assertFalse(isVector("string"))
def _intersectCirclePlane(circle, plane): if abs(abs(circle.normal*plane.normal)-1.) < eps: if plane.hasPoint(circle.center): return circle else: return None else: line = plane.intersectWith(Plane(circle.center, circle.normal)) x = line.distanceFrom(circle.center) if x > circle.radius: return None else: angle = N.arccos(x/circle.radius) along_line = N.sin(angle)*circle.radius normal = circle.normal.cross(line.direction) if line.distanceFrom(circle.center+normal) > x: normal = -normal return (circle.center+x*normal-along_line*line.direction, circle.center+x*normal+along_line*line.direction)
def _intersectCirclePlane(circle, plane): if abs(abs(circle.normal * plane.normal) - 1.) < eps: if plane.hasPoint(circle.center): return circle else: return None else: line = plane.intersectWith(Plane(circle.center, circle.normal)) x = line.distanceFrom(circle.center) if x > circle.radius: return None else: angle = N.arccos(x / circle.radius) along_line = N.sin(angle) * circle.radius normal = circle.normal.cross(line.direction) if line.distanceFrom(circle.center + normal) > x: normal = -normal return (circle.center + x * normal - along_line * line.direction, circle.center + x * normal + along_line * line.direction)
def _tetrahedralH(self, atom, known, unknown, bond): r = atom.position() n = (known[0].position() - r).normal() cone = Objects3D.Cone(r, n, Numeric.arccos(-1. / 3.)) sphere = Objects3D.Sphere(r, bond) circle = sphere.intersectWith(cone) others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) others.remove(atom) other = others[0] ref = (Objects3D.Plane(circle.center, circle.normal) \ .projectionOf(other.position())-circle.center).normal() p0 = circle.center + ref * circle.radius p0 = Objects3D.rotatePoint( p0, Objects3D.Line(circle.center, circle.normal), 60. * Units.deg) p1 = Objects3D.rotatePoint( p0, Objects3D.Line(circle.center, circle.normal), 120. * Units.deg) p2 = Objects3D.rotatePoint( p1, Objects3D.Line(circle.center, circle.normal), 120. * Units.deg) unknown[0].setPosition(p0) unknown[1].setPosition(p1) unknown[2].setPosition(p2)
def _tetrahedralH(self, atom, known, unknown, bond): r = atom.position() n = (known[0].position()-r).normal() cone = Objects3D.Cone(r, n, N.arccos(-1./3.)) sphere = Objects3D.Sphere(r, bond) circle = sphere.intersectWith(cone) others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) others.remove(atom) other = others[0] ref = (Objects3D.Plane(circle.center, circle.normal) \ .projectionOf(other.position())-circle.center).normal() p0 = circle.center + ref*circle.radius p0 = Objects3D.rotatePoint(p0, Objects3D.Line(circle.center, circle.normal), 60.*Units.deg) p1 = Objects3D.rotatePoint(p0, Objects3D.Line(circle.center, circle.normal), 120.*Units.deg) p2 = Objects3D.rotatePoint(p1, Objects3D.Line(circle.center, circle.normal), 120.*Units.deg) unknown[0].setPosition(p0) unknown[1].setPosition(p1) unknown[2].setPosition(p2)
class Molecule(CompositeChemicalObject, ChemicalObject): """Molecule A Glossary:Subclass of Class:MMTK.ChemicalObjects.ChemicalObject. Molecules consist of atoms and groups linked by bonds. Constructor: Molecule(|species|, **|properties|) Arguments: |species| -- a string (not case sensitive) that specifies the molecule name in the chemical database |properties| -- optional keyword properties: * position: the center-of-mass position (a vector) * configuration: the name of a configuration listed in the database definition of the molecule, which is used to initialize the atom positions. If no configuration is specified, the configuration named "default" will be used, if it exists. Otherwise the atom positions are undefined. * name: the atom name (a string) """ def __init__(self, blueprint, _memo=None, **properties): if blueprint is not None: # blueprint is None when called from MoleculeFactory ChemicalObject.__init__(self, blueprint, _memo) properties = copy.copy(properties) CompositeChemicalObject.__init__(self, properties) self.bonds = Bonds.BondList(self.bonds) blueprintclass = Database.BlueprintMolecule def bondedTo(self, atom): return self.bonds.bondedTo(atom) def setBondAttributes(self): self.bonds.setBondAttributes() def clearBondAttributes(self): for a in self.atoms: a.clearBondAttribute() def _subunits(self): return self.groups def _descriptionSpec(self): return "M", None def addGroup(self, group, bond_atom_pairs): for a1, a2 in bond_atom_pairs: o1 = a1.topLevelChemicalObject() o2 = a2.topLevelChemicalObject() if not (o1 == self and o2 == group) \ and not(o2 == self and o1 == group): raise ValueError("bond %s-%s outside object" % (str(a1), str(a2))) self.groups.append(group) self.atoms = self.atoms + group.atoms group.parent = self self.clearBondAttributes() for a1, a2 in bond_atom_pairs: self.bonds.append(Bonds.Bond((a1, a2))) for b in group.bonds: self.bonds.append(b) # construct positions of missing hydrogens def findHydrogenPositions(self): """Find reasonable positions for hydrogen atoms that have no position assigned. This method uses a heuristic approach based on standard geometry data. It was developed for proteins and DNA and may not give good results for other molecules. It raises an exception if presented with a topology it cannot handle.""" self.setBondAttributes() try: unknown = DictWithDefault([]) for a in self.atoms: if a.position() is None: if a.symbol != 'H': raise ValueError('position of ' + a.fullName() + \ ' is undefined') bonded = a.bondedTo()[0] unknown[bonded].append(a) for a, list in unknown.items(): bonded = a.bondedTo() n = len(bonded) known = [] for b in bonded: if b.position() is not None: known.append(b) nb = len(list) if a.symbol == 'C': if n == 4: if nb == 1: self._C4oneH(a, known, list) elif nb == 2: self._C4twoH(a, known, list) elif nb == 3: self._C4threeH(a, known, list) elif n == 3: if nb == 1: self._C3oneH(a, known, list) else: self._C3twoH(a, known, list) elif n == 2: self._C2oneH(a, known, list) else: print a raise ValueError("Can't handle C with " + ` n ` + " bonds") elif a.symbol == 'N': if n == 4: if nb == 3: self._N4threeH(a, known, list) elif nb == 2: self._N4twoH(a, known, list) elif nb == 1: self._N4oneH(a, known, list) elif n == 3: if nb == 1: self._N3oneH(a, known, list) elif nb == 2: self._N3twoH(a, known, list) elif n == 2: self._N2oneH(a, known, list) else: print a raise ValueError("Can't handle N with " + ` n ` + " bonds") elif a.symbol == 'O' and n == 2: self._O2(a, known, list) elif a.symbol == 'S' and n == 2: self._S2(a, known, list) else: print a raise ValueError("Can't handle this yet: " + a.symbol + ' with ' + ` n ` + ' bonds (' + a.fullName() + ').') finally: self.clearBondAttributes() # default C-H bond length and X-C-H angle _ch_bond = 1.09 * Units.Ang _hch_angle = Numeric.arccos(-1. / 3.) * Units.rad _nh_bond = 1.03 * Units.Ang _hnh_angle = 120. * Units.deg _oh_bond = 0.95 * Units.Ang _coh_angle = 114.9 * Units.deg _sh_bond = 1.007 * Units.Ang _csh_angle = 96.5 * Units.deg def _C4oneH(self, atom, known, unknown): r = atom.position() n0 = (known[0].position() - r).normal() n1 = (known[1].position() - r).normal() n2 = (known[2].position() - r).normal() n3 = (n0 + n1 + n2).normal() unknown[0].setPosition(r - self._ch_bond * n3) def _C4twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() r2 = known[1].position() plane = Objects3D.Plane(r, r1, r2) axis = -((r1 - r) + (r2 - r)).normal() plane = plane.rotate(Objects3D.Line(r, axis), 90. * Units.deg) cone = Objects3D.Cone(r, axis, 0.5 * self._hch_angle) sphere = Objects3D.Sphere(r, self._ch_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _C4threeH(self, atom, known, unknown): self._tetrahedralH(atom, known, unknown, self._ch_bond) def _C3oneH(self, atom, known, unknown): r = atom.position() n1 = (known[0].position() - r).normal() n2 = (known[1].position() - r).normal() n3 = -(n1 + n2).normal() unknown[0].setPosition(r + self._ch_bond * n3) def _C3twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) r2 = others[0].position() try: plane = Objects3D.Plane(r, r1, r2) except ZeroDivisionError: # We get here if all three points are colinear. # Add a small random displacement as a fix. from MMTK.Random import randomPointInSphere plane = Objects3D.Plane(r, r1, r2 + randomPointInSphere(0.001)) axis = (r - r1).normal() cone = Objects3D.Cone(r, axis, 0.5 * self._hch_angle) sphere = Objects3D.Sphere(r, self._ch_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _C2oneH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() x = r + self._ch_bond * (r - r1).normal() unknown[0].setPosition(x) def _N2oneH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) r2 = others[0].position() try: plane = Objects3D.Plane(r, r1, r2) except ZeroDivisionError: # We get here when all three points are colinear. # Add a small random displacement as a fix. from MMTK.Random import randomPointInSphere plane = Objects3D.Plane(r, r1, r2 + randomPointInSphere(0.001)) axis = (r - r1).normal() cone = Objects3D.Cone(r, axis, 0.5 * self._hch_angle) sphere = Objects3D.Sphere(r, self._nh_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) def _N3oneH(self, atom, known, unknown): r = atom.position() n1 = (known[0].position() - r).normal() n2 = (known[1].position() - r).normal() n3 = -(n1 + n2).normal() unknown[0].setPosition(r + self._nh_bond * n3) def _N3twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) r2 = others[0].position() plane = Objects3D.Plane(r, r1, r2) axis = (r - r1).normal() cone = Objects3D.Cone(r, axis, 0.5 * self._hnh_angle) sphere = Objects3D.Sphere(r, self._nh_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _N4threeH(self, atom, known, unknown): self._tetrahedralH(atom, known, unknown, self._nh_bond) def _N4twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() r2 = known[1].position() plane = Objects3D.Plane(r, r1, r2) axis = -((r1 - r) + (r2 - r)).normal() plane = plane.rotate(Objects3D.Line(r, axis), 90. * Units.deg) cone = Objects3D.Cone(r, axis, 0.5 * self._hnh_angle) sphere = Objects3D.Sphere(r, self._nh_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _N4oneH(self, atom, known, unknown): r = atom.position() n0 = (known[0].position() - r).normal() n1 = (known[1].position() - r).normal() n2 = (known[2].position() - r).normal() n3 = (n0 + n1 + n2).normal() unknown[0].setPosition(r - self._nh_bond * n3) def _O2(self, atom, known, unknown): others = known[0].bondedTo() for a in others: r = a.position() if a != atom and r is not None: break dihedral = 180. * Units.deg self._findPosition(unknown[0], atom.position(), known[0].position(), r, self._oh_bond, self._coh_angle, dihedral) def _S2(self, atom, known, unknown): c2 = filter(lambda a: a.symbol == 'C', known[0].bondedTo())[0] self._findPosition(unknown[0], atom.position(), known[0].position(), c2.position(), self._sh_bond, self._csh_angle, 180. * Units.deg) def _tetrahedralH(self, atom, known, unknown, bond): r = atom.position() n = (known[0].position() - r).normal() cone = Objects3D.Cone(r, n, Numeric.arccos(-1. / 3.)) sphere = Objects3D.Sphere(r, bond) circle = sphere.intersectWith(cone) others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) others.remove(atom) other = others[0] ref = (Objects3D.Plane(circle.center, circle.normal) \ .projectionOf(other.position())-circle.center).normal() p0 = circle.center + ref * circle.radius p0 = Objects3D.rotatePoint( p0, Objects3D.Line(circle.center, circle.normal), 60. * Units.deg) p1 = Objects3D.rotatePoint( p0, Objects3D.Line(circle.center, circle.normal), 120. * Units.deg) p2 = Objects3D.rotatePoint( p1, Objects3D.Line(circle.center, circle.normal), 120. * Units.deg) unknown[0].setPosition(p0) unknown[1].setPosition(p1) unknown[2].setPosition(p2) def _findPosition(self, unknown, a1, a2, a3, bond, angle, dihedral): sphere = Objects3D.Sphere(a1, bond) cone = Objects3D.Cone(a1, a2 - a1, angle) plane = Objects3D.Plane(a3, a2, a1) plane = plane.rotate(Objects3D.Line(a1, a2 - a1), dihedral) points = sphere.intersectWith(cone).intersectWith(plane) for p in points: if (a1 - a2).cross(p - a1) * (plane.normal) > 0: unknown.setPosition(p) break
def threeAngles(self, e1, e2, e3, tolerance=1e-7): """ Find three angles a1, a2, a3 such that Rotation(a1*e1)*Rotation(a2*e2)*Rotation(a3*e3) is equal to the rotation object. e1, e2, and e3 are non-zero vectors. There are two solutions, both of which are computed. @param e1: a rotation axis @type e1: L{Scientific.Geometry.Vector} @param e2: a rotation axis @type e2: L{Scientific.Geometry.Vector} @param e3: a rotation axis @type e3: L{Scientific.Geometry.Vector} @returns: a list containing two arrays of shape (3,), each containing the three angles of one solution @rtype: C{list} of C{N.array} @raise ValueError: if two consecutive axes are parallel """ # Written by Pierre Legrand ([email protected]) # # Basically this is a reimplementation of the David # Thomas's algorithm [1] described by Gerard Bricogne in [2]: # # [1] "Modern Equations of Diffractometry. Goniometry." D.J. Thomas # Acta Cryst. (1990) A46 Page 321-343. # # [2] "The ECC Cooperative Programming Workshop on Position-Sensitive # Detector Software." G. Bricogne, # Computational aspect of Protein Crystal Data Analysis, # Proceedings of the Daresbury Study Weekend (23-24/01/1987) # Page 122-126 e1 = e1.normal() e2 = e2.normal() e3 = e3.normal() # We are searching for the three angles a1, a2, a3 # If 2 consecutive axes are parallel: decomposition is not meaningful if (e1.cross(e2)).length() < tolerance or \ (e2.cross(e3)).length() < tolerance : raise ValueError('Consecutive parallel axes. Too many solutions') w = self(e3) # Solve the equation : _a.cosx + _b.sinx = _c _a = e1 * e3 - (e1 * e2) * (e2 * e3) _b = e1 * (e2.cross(e3)) _c = e1 * w - (e1 * e2) * (e2 * e3) _norm = (_a**2 + _b**2)**0.5 # Checking for possible errors in initial Rot matrix if _norm == 0: raise ValueError('FAILURE 1, norm = 0') if abs(_c / _norm) > 1 + tolerance: raise ValueError( 'FAILURE 2' + 'malformed rotation Tensor (non orthogonal?) %.8f' % (_c / _norm)) #if _c/_norm > 1: raise ValueError('Step1: No solution') _th = angleFromSineAndCosine(_b / _norm, _a / _norm) _xmth = N.arccos(_c / _norm) # a2a and a2b are the two possible solutions to the equation. a2a = mod_angle((_th + _xmth), 2 * N.pi) a2b = mod_angle((_th - _xmth), 2 * N.pi) solutions = [] # for each solution, find the two other angles (a1, a3). for a2 in (a2a, a2b): R2 = Rotation(e2, a2) v = R2(e3) v1 = v - (v * e1) * e1 w1 = w - (w * e1) * e1 norm = ((v1 * v1) * (w1 * w1))**0.5 if norm == 0: # in that case rotation 1 and 3 are about the same axis # so any solution for rotation 1 is OK a1 = 0. else: cosa1 = (v1 * w1) / norm sina1 = v1 * (w1.cross(e1)) / norm a1 = mod_angle(angleFromSineAndCosine(sina1, cosa1), 2 * N.pi) R3 = Rotation(e2, -1 * a2) * Rotation(e1, -1 * a1) * self # u = normalized test vector perpendicular to e3 # if e2 and e3 are // we have an exception before. # if we take u = e1^e3 then it will not work for # Euler and Kappa axes. u = (e2.cross(e3)).normal() cosa3 = u * R3(u) sina3 = u * (R3(u).cross(e3)) a3 = mod_angle(angleFromSineAndCosine(sina3, cosa3), 2 * N.pi) solutions.append(N.array([a1, a2, a3])) # Gives the closest solution to 0,0,0 first if N.add.reduce(solutions[0]**2) > \ N.add.reduce(solutions[1]**2): solutions = [solutions[1], solutions[0]] return solutions