Esempio n. 1
0
 def iter_vectors(self, with_indices=False):
     it = super(Tensor, self).iter_vectors(True)
     for val, idxs in it:
         yieldval = Vector(val, units=self.units)
         if with_indices:
             yield yieldval, idxs
         else:
             yield yieldval
Esempio n. 2
0
    def b_vector_for_positions(cls, *args):
        def R(i, j):
            # This is ugly...
            # TODO figure out a better way to do this
            mol = Molecule([Atom('H', args[i - 1]), Atom('H', args[j - 1])])
            return BondLength(mol[0], mol[1])

        Rab, Rad = R(1, 2), R(1, 4)
        BRab, BRad = Rab.get_b_tensor(max_order=2), Rad.get_b_tensor(
            max_order=2)
        Bb = Rad.value * Vector(
            [BRab[3 + alpha, 3:].dot(BRad[3:]) for alpha in [X, Y, Z]])
        Bd = Rad.value * Vector(
            [BRad[3 + alpha, 3:].dot(BRab[3:]) for alpha in [X, Y, Z]])
        Bd += BRab[3:].dot(BRad[3:]) * BRad[3:]
        Ba = -Bb - Bd
        return Ba, Bb, Vector(0, 0, 0), Bd
Esempio n. 3
0
 def __init__(self, symbol, position, **kwargs):
     self._init_common(**kwargs)
     self.symbol = symbol
     if hasunits(position):
         if 'units' not in kwargs:
             self._cartesian_units = position.units
         elif self._cartesian_units != kwargs['units']:
             position = position.in_units(self._cartesian_units)
     self.position = Vector(position, copy=True, units=self.cartesian_units)
Esempio n. 4
0
 def eigensystem(self, sort=False):
     """
     Wrapper to `numpy.eigh` and `numpy.eig` for symmetric and non-symmetric
     matrices, respectively.  Note that the eigenvectors are column vectors
     in the returned matrix.
     """
     if self.is_hermitian():
         evals, evecs = np.linalg.eigh(self)
     else:
         evals, evecs = np.linalg.eig(self)
         evals = np.real_if_close(evals)
         evecs = np.real_if_close(evecs)
     if sort:
         evals, evecs = zip(*sorted((val, tuple(vec)) for val, vec in zip(evals.ravel(), evecs.col_iter)))
         evecs = Matrix(evecs).T
         evals = Vector(evals)
     else:
         evals = evals.view(Vector)
         evecs = evecs.view(Matrix)
     return evals, evecs
Esempio n. 5
0
 def eigensystem(self, sort=False):
     """
     Wrapper to `numpy.eigh` and `numpy.eig` for symmetric and non-symmetric
     matrices, respectively.  Note that the eigenvectors are column vectors
     in the returned matrix.
     """
     if self.is_hermitian():
         evals, evecs = np.linalg.eigh(self)
     else:
         evals, evecs = np.linalg.eig(self)
         evals = np.real_if_close(evals)
         evecs = np.real_if_close(evecs)
     if sort:
         evals, evecs = zip(*sorted(
             (val, tuple(vec))
             for val, vec in zip(evals.ravel(), evecs.col_iter)))
         evecs = Matrix(evecs).T
         evals = Vector(evals)
     else:
         evals = evals.view(Vector)
         evecs = evecs.view(Matrix)
     return evals, evecs
Esempio n. 6
0
 def analytic_b_tensor_for_order(self, order):
     """
     Compute the analytic B tensor of a given order and return a tensor indexed by the
     coordinate's own indexing scheme.
     """
     if order == 1:
         # We can't use the `b_vector` attribute since that vector is indexed in the parent
         #   molecule's indexing scheme
         my_b = [self.__class__.b_vector_for_positions(*[a.pos for a in self.atoms])]
         if NotImplemented in my_b:
             return NotImplemented
         # Return now, to avoid double-caching
         return Vector(my_b)
     else:
         return NotImplemented
Esempio n. 7
0
    def __init__(self, representation, disps, increments=None, deltas=None):
        base = representation.molecule
        self._base_molecule = base
        self.representation = representation

        self.disp_vect = []
        for d in disps:
            self.disp_vect.append(d)
            # TODO Decide if I should handle units here or in displaced_by (probably there is better)
            #if hasunits(d):
            #    if isinstance(representation, InternalRepresentation):
            #        units = representation.units[d.units.genre]
            #    elif isinstance(representation, CartesianRepresentation):
            #        units = representation.units
            #    else:
            #        raise NotImplementedError
            #    self.disp_vect.append(strip_units(d, ) if hasunits(d) else d)
            #else:
            #    self.disp_vect.append(strip_units(d))
        self.disp_vect = Vector(self.disp_vect)
        self._increments = increments
        self._deltas = []
Esempio n. 8
0
 def __lt__(self, other):
     """ Determines a sort order based on Cotton ordering (or as close as possible to it...)
     (i.e. the ordering used by Cotton in his character tables)
     Note:  Cotton isn't really that consistent with his ordering, particularly for larger point groups.  This
     represents my best guess, basically just to get something consistant out on the table.
     """
     if self is other:
         return False
     if not isinstance(other, SymmetryOperation):
         raise TypeError("Comparison of SymmetryOperation with " +
                         classname(other) + " is not allowed.")
     elif not self.point_group is other.point_group:
         raise ValueError(
             "Cannot compare operations from different point groups.")
     if isinstance(self, g.IdentityOperation):
         return True
     if isinstance(other, g.IdentityOperation):
         return False
     elif isinstance(self, g.Rotation):
         if not isinstance(other, g.Rotation):
             return True
         elif (self.n, self.n - self.exponent) != (other.n, other.n -
                                                   other.exponent):
             # Reverse ordering by order, forward ordering by exponent
             return (self.n, self.n - self.exponent) > (other.n, other.n -
                                                        other.exponent)
         # Then put in the order z, y, x
         elif SymmetryOperation.is_same_axis(self.axis, Vector(0, 0, 1)):
             return True
         elif SymmetryOperation.is_same_axis(other.axis, Vector(0, 0, 1)):
             return False
         elif SymmetryOperation.is_same_axis(self.axis, Vector(0, 1, 0)):
             return True
         elif SymmetryOperation.is_same_axis(other.axis, Vector(0, 1, 0)):
             return False
         elif SymmetryOperation.is_same_axis(self.axis, Vector(1, 0, 0)):
             return True
         elif SymmetryOperation.is_same_axis(other.axis, Vector(1, 0, 0)):
             return False
         # Finally, order by proximity of axis to the z-axis (not an official part of Cotton ordering, but doesn't come up too often
         else:
             return (Vector(0, 0, 1) - self.axis).magnitude() < (
                 Vector(0, 0, 1) - other.axis).magnitude()
     elif isinstance(other, g.Rotation):
         return False
     elif isinstance(self, g.Inversion):
         return True
     elif isinstance(other, g.Inversion):
         return False
     elif isinstance(self, g.ImproperRotation):
         if not isinstance(other, g.ImproperRotation):
             return True
         elif (self.n, self.n - self.exponent) != (other.n, other.n -
                                                   other.exponent):
             # Reverse ordering by order, forward ordering by exponent
             return (self.n, self.n - self.exponent) > (other.n, other.n -
                                                        other.exponent)
         # Then put in the order z, y, x
         elif SymmetryOperation.is_same_axis(self.axis, Vector(0, 0, 1)):
             return True
         elif SymmetryOperation.is_same_axis(other.axis, Vector(0, 0, 1)):
             return False
         elif SymmetryOperation.is_same_axis(self.axis, Vector(0, 1, 0)):
             return True
         elif SymmetryOperation.is_same_axis(other.axis, Vector(0, 1, 0)):
             return False
         elif SymmetryOperation.is_same_axis(self.axis, Vector(1, 0, 0)):
             return True
         elif SymmetryOperation.is_same_axis(other.axis, Vector(1, 0, 0)):
             return False
         # Finally, order by proximity of axis to the z-axis (not an official part of Cotton ordering, but doesn't come up too often
         else:
             return (Vector(0, 0, 1) - self.axis).magnitude() < (
                 Vector(0, 0, 1) - other.axis).magnitude()
     elif isinstance(other, g.ImproperRotation):
         return False
     else:  # Both are Reflections
         if self.is_principal_reflection():
             return True
         elif other.is_principal_reflection():
             return False
         elif self.is_dihedral():
             if not other.is_dihedral():
                 return True
             # Otherwise same axis ordering as rotations
             elif SymmetryOperation.is_same_axis(self.axis, Vector(0, 0,
                                                                   1)):
                 return True
             elif SymmetryOperation.is_same_axis(other.axis,
                                                 Vector(0, 0, 1)):
                 return False
             elif SymmetryOperation.is_same_axis(self.axis, Vector(0, 1,
                                                                   0)):
                 return True
             elif SymmetryOperation.is_same_axis(other.axis,
                                                 Vector(0, 1, 0)):
                 return False
             elif SymmetryOperation.is_same_axis(self.axis, Vector(1, 0,
                                                                   0)):
                 return True
             elif SymmetryOperation.is_same_axis(other.axis,
                                                 Vector(1, 0, 0)):
                 return False
             else:
                 return (Vector(0, 0, 1) - self.axis).magnitude() < (
                     Vector(0, 0, 1) - other.axis).magnitude()
         elif other.is_dihedral():
             return False
         else:  # Both are sigma_v's
             # Same axis ordering as rotations
             if SymmetryOperation.is_same_axis(self.axis, Vector(0, 0, 1)):
                 return True
             elif SymmetryOperation.is_same_axis(other.axis,
                                                 Vector(0, 0, 1)):
                 return False
             elif SymmetryOperation.is_same_axis(self.axis, Vector(0, 1,
                                                                   0)):
                 return True
             elif SymmetryOperation.is_same_axis(other.axis,
                                                 Vector(0, 1, 0)):
                 return False
             elif SymmetryOperation.is_same_axis(self.axis, Vector(1, 0,
                                                                   0)):
                 return True
             elif SymmetryOperation.is_same_axis(other.axis,
                                                 Vector(1, 0, 0)):
                 return False
             else:
                 return (Vector(0, 0, 1) - self.axis).magnitude() < (
                     Vector(0, 0, 1) - other.axis).magnitude()
Esempio n. 9
0
    def analytic_b_tensor_for_order(self, order):
        #--------------------------------------------------------------------------------#
        # Now check the cache
        cache_key = (self.__class__, order) + tuple(a.pos for a in self.atoms)
        cache_resp = SimpleInternalCoordinate._check_b_tensor_cache(*cache_key)
        if cache_resp is not None:
            return cache_resp
        #--------------------------------------------------------------------------------#
        B = partial(SimpleInternalCoordinate.b_tensor_element_reindexed, self)
        #--------------------------------------------------------------------------------#
        # First order is already done elsewhere...
        if order == 1:
            # We can't use the `b_vector` attribute since that vector is indexed in the parent
            #   molecule's indexing scheme
            my_b = Vector(
                self.__class__.b_vector_for_positions(
                    *[a.pos for a in self.atoms]))
            # Return now, to avoid double-caching
            return my_b
        #--------------------------------------------------------------------------------#
        # Second order terms
        if order == 2:
            # BondAngles are composed of 3 atoms (3 CartesianCoordinates each), and the
            #   order is 2, so the output Tensor will be a 9x9 Tensor
            my_b = np.ndarray(shape=(9, ) * 2)
            # some precomputed values
            phi = self.value * self.units.to(Radians)
            cotphi = 1.0 / tan(phi)
            cscphi = 1.0 / sin(phi)
            #========================================#
            # see comments in BondLength version
            # First, handle the terminal atom entries
            for (a_idx, a), (c_idx, c) in product(zip([0, 2],
                                                      self.terminal_atoms),
                                                  repeat=2):
                # Generate the temporary coordinates
                if a_idx == 0:
                    Rab = BondLength(self.atoms[0], self.atoms[1])
                    Rbc = BondLength(self.atoms[1], self.atoms[2])
                else:  # a_idx == 2
                    Rab = BondLength(self.atoms[2], self.atoms[1])
                    Rbc = BondLength(self.atoms[1], self.atoms[0])
                #----------------------------------------#
                # Now iterate over the possible sets of cartesian coordinates
                for alpha, beta in product([X, Y, Z], repeat=2):
                    a_alpha, c_beta = a_idx * 3 + alpha, c_idx * 3 + beta
                    if a_idx == c_idx:
                        # From equation 19 in Allen, et al. Mol. Phys. 89 (1996), 1213-1221
                        a_beta = c_beta
                        other_terminal_index = 2 if a_idx == 0 else 0
                        my_b[a_alpha, a_beta] = (
                            -cotphi * B(self, a_alpha) * B(self, a_beta) -
                            cscphi * sum(
                                B(Rab, a_idx * 3 + sigma, a_alpha, a_beta) *
                                B(Rbc, other_terminal_index * 3 + sigma)
                                for sigma in [X, Y, Z]))
                    else:
                        # From equation 20 in Allen, et al. Mol. Phys. 89 (1996), 1213-1221
                        my_b[a_alpha, c_beta] = (
                            -cotphi * B(self, a_alpha) * B(self, c_beta) -
                            cscphi * sum(
                                B(Rab, a_idx * 3 + sigma, a_alpha) *
                                B(Rbc, c_idx * 3 + sigma, c_beta)
                                for sigma in [X, Y, Z]))
            # Now fill in the middle atom entries utilizing translational invariance
            # From equation 32 in Allen, et al. Mol. Phys. 89 (1996), 1213-1221
            for alpha, beta in product([X, Y, Z], repeat=2):
                b_beta = 3 + beta
                for a_idx, a in zip([0, 2], self.terminal_atoms):
                    a_alpha = 3 * a_idx + alpha
                    my_b[b_beta, a_alpha] = my_b[a_alpha, b_beta] = \
                        -sum(my_b[a_alpha, t + beta]
                            for t in [0, 6] # 0 and 6 are the offsets for the terminal atoms
                        )
                # Now the b_alpha, b_beta entry:
                my_b[3 + alpha, 3 + beta] = sum(
                    my_b[atom1 * 3 + alpha, atom2 * 3 + beta]
                    for atom1, atom2 in product([0, 2], repeat=2))
        #--------------------------------------------------------------------------------#
        else:
            # behold, the general formula!
            # BondAngles are composed of 3 atoms (3 CartesianCoordinates each),
            #   so the output will be a 9x9x...x9 (`order`-dimensional) tensor
            my_b = Tensor(indices=','.join('v' * order),
                          index_range_set=self.__class__._btens_idx_range_set)
            #Bphi = self._btensor
            Rab = self.get_coord(BondLength, self.atoms[0], self.atoms[1])
            Rbc = self.get_coord(BondLength, self.atoms[1], self.atoms[2])
            #Brab = Rab._btensor
            #Brbc = Rbc._btensor
            #for o in xrange(1, order):
            #    t = Bphi.for_order(o)
            #    rab = Brab.for_order(o)
            #    rbc = Rbc._btensor.for_order(o)
            #    if isinstance(t, ComputableTensor): t.fill()
            #    if isinstance(rab, ComputableTensor): rab.fill()
            #    if isinstance(rbc, ComputableTensor): rab.fill()
            #Brab.for_order(order).fill()
            #Brbc.for_order(order).fill()
            #remap_set = IndexRangeSet()
            #DeclareIndexRange('v', 6, index_range_set=remap_set).with_subranges(
            #    IndexRange('b', 3, 6, index_range_set=remap_set),
            #    IndexRange('c', 0, 3, index_range_set=remap_set)
            #)
            ##Brbc = DerivativeCollection(coordinate=Rbc, einsum_index='v', index_range_set=remap_set)
            #ba = Vector([Brab[(0,) + (Ellipsis,)*order]])
            #for o in xrange(1, order+1):
            #    Brbc.for_order(o).index_range_set = remap_set
            ## some precomputed values
            phi = self.value * self.units.to(Radians)
            cotphi = 1.0 / tan(phi)
            cscphi = 1.0 / sin(phi)

            #========================================#
            # First take care of the terminal atoms...
            def f_K(k):
                if k % 2 == 1:
                    if (k + 1) / 2 % 2 == 0:
                        return 1
                    else:  # (k+1)/2 % 2 == 1
                        return -1
                elif k / 2 % 2 == 0:
                    return cotphi
                else:  # k/2 % 2 == 1
                    return -cotphi

            #----------------------------------------#
            a_idx, c_idx = 0, 2
            for num_a_coords in xrange(0, order + 1):
                # outer loop over possible alphas
                for alphas in product([X, Y, Z], repeat=order):
                    a_alphas = tuple(3 * a_idx + alpha
                                     for alpha in alphas[:num_a_coords])
                    c_alphas = tuple(3 * c_idx + alpha
                                     for alpha in alphas[num_a_coords:])
                    # Now we have all of the specifics for the left-hand side, so compute the
                    #   right-hand side to go with it...
                    cum_sum = 0.0
                    for sigma in [X, Y, Z]:
                        cum_sum += B(Rab, 3 * a_idx + sigma, *a_alphas) * B(
                            Rbc, 3 * c_idx + sigma, *c_alphas)
                    cum_sum *= -cscphi
                    for k in range(2, order + 1):
                        inner_sum = 0.0
                        for s in I_lubar(order, k, a_alphas + c_alphas):
                            prod = 1.0
                            for i in range(k):
                                prod *= B(self, *s[i])
                            inner_sum += prod
                        cum_sum += f_K(k) * inner_sum
                    # Spread over permutations
                    for idxs in permutations(a_alphas + c_alphas):
                        my_b[idxs] = cum_sum
            #========================================#
            # now compute the terms involving the middle atom
            a1_idx, a2_idx, a3_idx = 0, 1, 2
            for num_a2s in xrange(1, order + 1):
                # Now fill in the middle atom entries utilizing translational invariance
                # From equation 32 in Allen, et al. Mol. Phys. 89 (1996), 1213-1221
                for first_a2_position in xrange(0, order - num_a2s + 1):
                    for alphas in product([X, Y, Z], repeat=order):
                        a1_alphas = tuple(
                            3 * a1_idx + alpha
                            for alpha in alphas[:first_a2_position])
                        middle_alphas = alphas[
                            first_a2_position:first_a2_position + num_a2s]
                        a2_alphas = tuple(3 * a2_idx + alpha
                                          for alpha in middle_alphas)
                        a3_alphas = tuple(
                            3 * a3_idx + alpha
                            for alpha in alphas[first_a2_position + num_a2s:])
                        val_to_spread = 0.0
                        for midatoms in product([a1_idx, a3_idx],
                                                repeat=num_a2s):
                            idxs = a1_alphas
                            idxs += tuple(ai * 3 + middle_alphas[i]
                                          for i, ai in enumerate(midatoms))
                            idxs += a3_alphas
                            val_to_spread += my_b[idxs]
                        if num_a2s % 2 == 1:
                            val_to_spread *= -1.0
                        for perm in permutations(a1_alphas + a2_alphas +
                                                 a3_alphas):
                            my_b[perm] = val_to_spread
        #--------------------------------------------------------------------------------#
        # Cache the value we got
        SimpleInternalCoordinate._set_b_tensor_cache_entry(my_b, *cache_key)
        #--------------------------------------------------------------------------------#
        return my_b
Esempio n. 10
0
 def values(self):
     vals = [c.value for c in self.coords]
     return Vector(vals)
Esempio n. 11
0
 def values_for_matrix(self, mat):
     return Vector([c.value_for_molecule_matrix(mat) for c in self.coords])
Esempio n. 12
0
 def __init__(self, symbol, x, y, z, **kwargs):
     self._init_common(**kwargs)
     self.symbol = symbol
     self.position = Vector([x, y, z], units=self.cartesian_units)
Esempio n. 13
0
 def analytic_b_tensor_for_order(self, order):
     # First check the cache
     cache_key = (self.__class__, order) + tuple(a.pos for a in self.atoms)
     cache_resp = SimpleInternalCoordinate._check_b_tensor_cache(*cache_key)
     if cache_resp is not None:
         return cache_resp
     #--------------------------------------------------------------------------------#
     B = partial(SimpleInternalCoordinate.b_tensor_element_reindexed, self)
     #--------------------------------------------------------------------------------#
     # First order is already done elsewhere...
     if order == 1:
         # We can't use the `b_vector` attribute since that vector is indexed in the parent
         #   molecule's indexing scheme
         my_b = Vector(self.__class__.b_vector_for_positions(*[a.pos for a in self.atoms]))
         # Return now, to avoid double-caching
         return my_b
     #--------------------------------------------------------------------------------#
     # TODO Explicit (matrix/tensor based) implementations of 2nd, 3rd, and 4th order to speed things up substantially
     else:
         # We only have general formulas for terminal atoms as of now...
         # So we can save a little bit of time by computing these terms and
         #   then doing only finite difference for everything else
         # Torsions are composed of 4 atoms (3 CartesianCoordinates each),
         #   so the output will be a 12x12x...x12 (`order`-dimensional) tensor
         my_b = np.ndarray(shape=(12,)*order)
         if sanity_checking_enabled:
             my_b = np.ones(shape=(12,)*order) * float('inf')
         # some precomputed values
         #========================================#
         # Helper function needed for derivative:
         def Bsin2phi(phi, *idx_alphas):
             # some precomputed values
             twophi = 2.0*phi.value*phi.units.to(Radians)
             sin2phi = sin(twophi)
             cos2phi = cos(twophi)
             def h_K(k):
                 if k % 2 == 1:
                     if ((k-1) / 2) % 2 == 0:
                         return cos2phi
                     else:
                         return -cos2phi
                 else: # k is even
                     if k/2 % 2 == 0:
                         return sin2phi
                     else:
                         return -sin2phi
             cumsum = 0.0
             n = len(idx_alphas)
             for k in xrange(1, n + 1):
                 inner_sum = 0.0
                 for s in I_lubar(n, k, idx_alphas):
                     inner_prod = 1.0
                     for i in range(k):
                         inner_prod *= B(phi, *s[i])
                     inner_sum += inner_prod
                 cumsum += 2.0**k * h_K(k) * inner_sum
             return cumsum
         #========================================#
         # The aaaa...bbbbb...cccc... terms
         phis = []
         rbcs = []
         for a_idx, a in zip([0, 3], self.terminal_atoms):
             # Determine whihc terminal atom we're handling
             if a_idx == 0:
                 b_idx, c_idx, d_idx = 1, 2, 3
             else: # a_idx == 3
                 b_idx, c_idx, d_idx = 2, 1, 0
             #----------------------------------------#
             phi_abc = self.get_coord(BondAngle,
                 self.atoms[a_idx],
                 self.atoms[b_idx],
                 self.atoms[c_idx],
             )
             # units of Radians
             ang_conv = phi_abc.units.to(Radians)
             # TODO figure out what 'units' should be here
             rbc = self.get_coord(BondLength, self.atoms[b_idx], self.atoms[c_idx])
             # Keep them for later
             phis.append(phi_abc)
             rbcs.append(rbc)
             #----------------------------------------#
             # some precomputed values
             sin2phi = sin(2.0 * phi_abc.value * ang_conv)
             #----------------------------------------#
             for num_a, num_b, num_c in unlabeled_balls_in_labeled_boxes(order-1, [order-1]*3):
                 num_a += 1
                 alph_iter = product(*map(lambda n: symmetric_product([X,Y,Z], n), (num_a, num_b, num_c)))
                 for alphas_a, alphas_b, alphas_c in alph_iter:
                     a_alphas = tuple(3*a_idx + alpha for alpha in alphas_a)
                     b_alphas = tuple(3*b_idx + alpha for alpha in alphas_b)
                     c_alphas = tuple(3*c_idx + alpha for alpha in alphas_c)
                     all_alphas = a_alphas + b_alphas + c_alphas
                     cum_sum = 0.0
                     for t1, t2 in I_ll(num_b + num_c, 2, b_alphas + c_alphas):
                         bphi = LightVector([
                             B(phi_abc, 3*a_idx + sigma, *(a_alphas[1:] + t1))
                                 for sigma in [X, Y, Z]
                         ])
                         brbc = LightVector([
                             B(rbc, 3*c_idx + sigma, *t2) for sigma in [X, Y, Z]]
                         )
                         cum_sum += cross(bphi, brbc)[alphas_a[0]]
                     cum_sum *= 2.0
                     cum_sum -= B(self, a_alphas[0]) * Bsin2phi(phi_abc, *all_alphas[1:])
                     for s1, s2 in I_llbar(num_a - 1 + num_b + num_c, 2, all_alphas[1:]):
                         cum_sum -= Bsin2phi(phi_abc, *s1) * B(self, *((a_alphas[0],) + s2))
                     cum_sum /= sin2phi
                     for perm in permutations(all_alphas):
                         my_b[perm] = cum_sum
         #========================================#
         # Note that the terminal-atom cross-derivatives are 0
         if sanity_checking_enabled:
             # Fill in the explicity zeros now, since we had infinity there to make sure
             #   uncomputed values weren't being used for something else.
             for a_idx, d_idx in permutations([0, 3]):
                 for remaining_idxs in product([0, 1, 2, 3], repeat=order-2):
                     for alphas in product([X, Y, Z], repeat=order):
                         idx_alphas = tuple(3*atom_idx + alpha
                                 for atom_idx, alpha in zip((a_idx, d_idx) + remaining_idxs, alphas))
                         for perm in permutations(idx_alphas):
                             my_b[perm] = 0.0
         #========================================#
         # Arbitrary order bbbbb.... terms
         a_idx, b_idx, c_idx, d_idx = 0, 1, 2, 3
         phi_abc = phis[0]
         phi_bcd = self.get_coord(BondAngle,
             self.atoms[b_idx],
             self.atoms[c_idx],
             self.atoms[d_idx],
         )
         ang_conv = phi_bcd.units.to(Radians)
         # TODO figure out what 'units' should be here
         r_ba = self.get_coord(BondLength,
             self.atoms[b_idx],
             self.atoms[a_idx]
         )
         r_cd = self.get_coord(BondLength,
             self.atoms[c_idx],
             self.atoms[d_idx]
         )
         #----------------------------------------#
         def Bcscphi(phi, *b_alphas):
             phi_val = phi.value * phi.units.to(Radians)
             sinphi = sin(phi_val)
             cscphi = 1.0 / sinphi
             if len(b_alphas) == 0:
                 return cscphi
             cotphi = cos(phi_val) / sinphi
             #------------------------------------#
             def dcsc_n(n):
                 def t(n_t, k):
                     if k == 0:
                         return 1
                     elif k <= n_t//2:
                         return (2*k + 1) * t(n_t-1, k) + (n_t - 2*k + 1) * t(n_t-1, k-1)
                     else:
                         return 0
                 #--------------------------------#
                 ret_val = 0.0
                 for kk in xrange(n//2 + 1):
                     ret_val += t(n, kk) * cotphi**(n - 2*kk) * cscphi**(2*kk + 1)
                 if n % 2 == 1:
                     return -ret_val
                 else:
                     return ret_val
             #------------------------------------#
             outer_sum = 0.0
             for k in xrange(1, len(b_alphas) + 1):
                 inner_sum = 0.0
                 for idx_sets in labeled_balls_in_unlabeled_boxes(len(b_alphas), [len(b_alphas)]*k):
                     if any(len(st) == 0 for st in idx_sets):
                         continue
                     b_idx_sets = tuple(tuple(b_alphas[i] for i in idxset) for idxset in idx_sets)
                     product = 1.0
                     for b_idxs in b_idx_sets:
                         product *= B(phi, *b_idxs)
                     inner_sum += product
                 outer_sum += dcsc_n(k) * inner_sum
             return outer_sum
         #----------------------------------------#
         for alphas in symmetric_product([X, Y, Z], order):
             # here we go...
             term_sum = first_term = second_term = third_term = 0.0
             iter_alphas = alphas[1:]
             b_alphas = tuple(3*b_idx + alpha for alpha in alphas)
             #----------------------------------------#
             for b_alphas1, b_alphas2, b_alphas3 in I_ll(order-1, 3, b_alphas[1:]):
                 #----------------------------------------#
                 # preconstruct the vectors we need for the cross products
                 b_a_phi_abc = LightVector([
                     B(phi_abc, 3*a_idx + sigma, *b_alphas2) for sigma in [X, Y, Z]
                 ])
                 b_c_phi_abc = LightVector([
                     B(phi_abc, 3*c_idx + sigma, *b_alphas2) for sigma in [X, Y, Z]
                 ])
                 b_a_r_ba = LightVector([
                     B(r_ba, 3*a_idx + sigma, *b_alphas3) for sigma in [X, Y, Z]
                 ])
                 #----------------------------------------#
                 # save a little bit of time by only computing this part once per iteration
                 Bcscphi_abc = Bcscphi(phi_abc, *b_alphas1)
                 #----------------------------------------#
                 # now add the contribution from this set of indices
                 first_term += Bcscphi_abc * cross(b_a_phi_abc, b_a_r_ba)[alphas[0]]
                 second_term += Bcscphi_abc * cross(b_c_phi_abc, b_a_r_ba)[alphas[0]]
             #----------------------------------------#
             term_sum -= first_term + second_term
             b_d_r_cd = LightVector([
                 B(r_cd, 3*d_idx + sigma) for sigma in [X, Y, Z]
             ])
             for b_alphas1, b_alphas2 in I_ll(order-1, 2, b_alphas[1:]):
                 #----------------------------------------#
                 b_b_phi_bcd = LightVector([
                     B(phi_bcd, 3*b_idx + sigma, *b_alphas2) for sigma in [X, Y, Z]
                 ])
                 #----------------------------------------#
                 third_term += Bcscphi(phi_bcd, *b_alphas1) * cross(b_b_phi_bcd, b_d_r_cd)[alphas[0]]
             term_sum += third_term
             #----------------------------------------#
             # and spread it across permutations
             for perm in permutations(tuple(3*b_idx + alpha for alpha in alphas)):
                 my_b[perm] = term_sum
         #========================================#
         # and fill in the bbbb...cccc... derivatives by translational invariance
         # Range from one c index to all
         for num_c in xrange(1, order+1):
             num_b = order - num_c
             alph_iter = product(*map(lambda n: symmetric_product([X,Y,Z], n), (num_b, num_c)))
             for alphas_b, alphas_c in alph_iter:
                 b_alphas = tuple(3*b_idx + alph for alph in alphas_b)
                 c_alphas = tuple(3*c_idx + alph for alph in alphas_c)
                 alphas_all = alphas_b + alphas_c
                 currsum = 0.0
                 for repl_atoms in product([a_idx, b_idx, d_idx], repeat=num_c):
                     repl_alphas = tuple(3*repl_atom + alph
                         for repl_atom, alph in zip(repl_atoms, alphas_c))
                     currsum += my_b[repl_alphas + b_alphas]
                     if sanity_checking_enabled and math.isinf(my_b[b_alphas + repl_alphas]):
                         raise IndexError("indices not filled in: {}, needed for {}".format(
                             b_alphas + repl_alphas,
                             b_alphas + c_alphas
                         ))
                 if num_c % 2 == 1:
                     currsum *= -1.0
                 #----------------------------------------#
                 # and spread this value over all permutations...
                 for perm in permutations(b_alphas + c_alphas):
                     my_b[perm] = currsum
     #--------------------------------------------------------------------------------#
     # Cache the value we got
     SimpleInternalCoordinate._set_b_tensor_cache_entry(my_b, *cache_key)
     #--------------------------------------------------------------------------------#
     return my_b
Esempio n. 14
0
 def analytic_b_tensor_for_order(self, order):
     # First check the cache
     cache_key = (self.__class__, order) + tuple(a.pos for a in self.atoms)
     cache_resp = SimpleInternalCoordinate._check_b_tensor_cache(*cache_key)
     if cache_resp is not None:
         return cache_resp
     #--------------------------------------------------------------------------------#
     # Define a function that acts like the B tensor for getting lower-order terms
     B = partial(SimpleInternalCoordinate.b_tensor_element_reindexed, self)
     #--------------------------------------------------------------------------------#
     # First order is trivial...
     if order == 1:
         # We can't use the `b_vector` attribute since that vector is indexed in the parent
         #   molecule's indexing scheme
         my_b = Vector(
             self.__class__.b_vector_for_positions(
                 *[a.pos for a in self.atoms]))
         # return early to avoid double-caching, since b_vector_for_positions caches
         #   the result in the B tensor cache already
         return my_b
     #--------------------------------------------------------------------------------#
     # Second order terms
     elif order == 2:
         # BondLengths are composed of 2 atoms (3 CartesianCoordinates each), and the
         #   order is 2, so the output Tensor will be a 6x6 Tensor
         my_b = np.ndarray(shape=(6, ) * 2)
         Rabinv = 1.0 / self.value
         # This uses the itertools.product function, which acts like
         #   a nested loop (with depth given by the 'repeat' parameter).
         #   Mostly, I used this here just to keep the code indentation
         #   from getting out of hand (particularly in the case of the
         #   Torsion coordinate).  Also, though, this is useful for the
         #   general coordinate formulas.
         # First do the "same atom" derivatives
         for a_idx, a in enumerate(self.atoms):
             # Now iterate over the possible sets of cartesian coordinates
             for alpha, beta in symmetric_product([X, Y, Z], 2):
                 a_alpha, a_beta = 3 * a_idx + alpha, 3 * a_idx + beta
                 if alpha != beta:
                     my_b[a_alpha, a_beta] = -Rabinv * (B(self, a_alpha) *
                                                        B(self, a_beta))
                     my_b[a_beta, a_alpha] = -Rabinv * (B(self, a_alpha) *
                                                        B(self, a_beta))
                 else:
                     my_b[a_alpha, a_beta] = -Rabinv * (
                         B(self, a_alpha) * B(self, a_beta) - 1)
         # Now get the "different atom" derivatives from the "same atom ones"
         for alpha in [X, Y, Z]:
             for beta in [X, Y, Z]:
                 a_alpha = alpha
                 b_beta = 3 + beta
                 my_b[a_alpha,
                      b_beta] = my_b[b_beta,
                                     a_alpha] = (-1) * my_b[alpha, beta]
     #--------------------------------------------------------------------------------#
     else:
         # Behold, the general formula!
         # BondLengths are composed of 2 atoms (3 CartesianCoordinates each),
         #   so the output Tensor will be a 6x6x...x6 (`order`-dimensional) Tensor
         my_b = Tensor(indices=','.join('v' * order),
                       index_range_set=self.__class__._btens_idx_range_set)
         # New, fancy way with einstein summation (if I can get it to work)
         #B = self._btensor
         #for o in xrange(1, order):
         #    t = B.for_order(o)
         #    if isinstance(t, ComputableTensor):
         #        t.fill()
         # First do the "same atom" derivatives
         #def idxs(letter, num): return tuple(letter + '_' + str(x) for x in xrange(num))
         #aa = idxs('a', order)
         #bb = idxs('b', order)
         #for s in I_lubar(order, 2, aa):
         #    my_b[aa] += B[s[0]] * B[s[1]]
         #for s in I_lubar(order, 2, bb):
         #    my_b[bb] += B[s[0]] * B[s[1]]
         #Rabinv = 1.0 / self.value
         #my_b[aa] *= -Rabinv
         #my_b[bb] *= -Rabinv
         ## Now get the "different atom" derivatives from the "same atom ones"
         #for nacarts in xrange(1, order):
         #    aprefactor =  -1.0 if (order - nacarts) % 2 == 1 else 1.0
         #    for a_b_set in set(permutations("a"*nacarts + "b"*(order-nacarts))):
         #        ab = tuple(map(lambda x: x[0] + "_" + str(x[1]), zip(a_b_set, xrange(order))))
         #        my_b[ab] = aprefactor * my_b[aa]
         # old way...
         Rabinv = 1.0 / self.value
         for a_idx, a in enumerate(self.atoms):
             # Now iterate over the possible sets of cartesian coordinates
             for alphas in symmetric_product([X, Y, Z], order):
                 a_alphas = tuple(3 * a_idx + alpha for alpha in alphas)
                 cumval = 0.0
                 for a_alps, a_bets in I_lubar(order, 2, a_alphas):
                     cumval += B(self, *a_alps) * B(self, *a_bets)
                 val_to_spread = -Rabinv * cumval
                 # and spread over permutations
                 for idxs in permutations(a_alphas):
                     my_b[idxs] = val_to_spread
         # Now get the "different atom" derivatives from the "same atom ones"
         #   (From eq. 31 in Allen, et al. Mol. Phys. 89 (1996), 1213-1221)
         for alphas in product([X, Y, Z], repeat=order):
             for nacarts in xrange(1, order):
                 acoords = tuple(acart for acart in alphas[:nacarts])
                 bcoords = tuple(3 + bcart for bcart in alphas[nacarts:])
                 aprefactor = -1.0 if (order - nacarts) % 2 == 1 else 1.0
                 val_to_spread = my_b[alphas]
                 for perm in permutations(acoords + bcoords):
                     my_b[perm] = aprefactor * val_to_spread
     #--------------------------------------------------------------------------------#
     # Whew...that was tough.  Let's cache the value we got
     SimpleInternalCoordinate._set_b_tensor_cache_entry(my_b, *cache_key)
     #--------------------------------------------------------------------------------#
     return my_b
Esempio n. 15
0
 def diagonal(self):
     if not self.is_square():
         raise NotImplementedError
     ndim = len(self.shape)
     return Vector([self[(i, ) * ndim] for i in xrange(self.shape[0])])
Esempio n. 16
0
 def values_for_molecule(self, mol):
     return Vector([c.value_for_molecule(mol) for c in self.coords])
Esempio n. 17
0
    def _name_operations(self):
        """ Determines the most-likely-to-be human-readable names for the operations.
        """

        # Figure out the principal reflections (sigma_h's)
        principal_rotations = []
        for k, g in itertools.groupby(sorted(self.rotations),
                                      attrgetter('n', 'exponent')):
            principal_rotations = list(g)
            break
        if len(principal_rotations) > 0:
            if principal_rotations[0].n > 2:
                # Mark them all as sigma_h's
                for rot in principal_rotations:
                    for op in self.reflections:
                        if SymmetryOperation.is_same_axis(op.axis, rot.axis):
                            op.principal_reflection = True
            else:
                # Otherwise, just choose the Z axis
                for op in self.reflections:
                    if SymmetryOperation.is_same_axis(
                            op.axis, principal_rotations[0].axis):
                        op.principal_reflection = True
                        break
        self.operations.sort()
        self._rotations = None
        self._improper_rotations = None
        self._reflections = None
        self.identity_element.name = "E"
        if not self.inversion is None:
            self.inversion.name = 'i'
        for key, rotiter in itertools.groupby(self.rotations, attrgetter('n')):
            rotgrp = list(rotiter)
            prime_count = 0
            labels = []
            order = rotgrp[0].n
            if len(rotgrp) == rotgrp[0].n - 1:
                # only one axis, label it simply
                rotgrp[0].name = "C_" + str(order)
                for i in xrange(1, len(rotgrp)):
                    rotgrp[i].name = "C_" + str(order) + "^" + str(
                        rotgrp[i].exponent)
            else:
                naxes = len([x for x in rotgrp if x.exponent == 1])
                has_paren_label = False
                for i, rot in enumerate(rotgrp):
                    if i < naxes:
                        if SymmetryOperation.is_same_axis(
                                rot.axis, Vector(0, 0, 1)):
                            labels.append("(z)")
                            has_paren_label = True
                            rot.name = "C_" + str(order) + "(z)"
                        elif SymmetryOperation.is_same_axis(
                                rot.axis, Vector(0, 1, 1)):
                            labels.append("(y)")
                            has_paren_label = True
                            rot.name = "C_" + str(order) + "(y)"
                        elif SymmetryOperation.is_same_axis(
                                rot.axis, Vector(1, 0, 0)):
                            labels.append("(x)")
                            has_paren_label = True
                            rot.name = "C_" + str(order) + "(x)"
                        else:
                            label = "'" * (prime_count +
                                           (1 if has_paren_label else 0))
                            labels.append(label)
                            prime_count += 1
                            rot.name = "C_" + str(order) + label
                    else:
                        rot.name = "C_" + str(order) + labels[
                            i % naxes] + "^" + str(rot.exponent)
        for key, rotiter in itertools.groupby(self.improper_rotations,
                                              attrgetter('n')):
            rotgrp = list(rotiter)
            prime_count = 0
            labels = []
            order = rotgrp[0].n
            if len(rotgrp) == rotgrp[0].n - 1:
                # only one axis, label it simply
                rotgrp[0].name = "S_" + str(order)
                for i in xrange(1, len(rotgrp)):
                    rotgrp[i].name = "S_" + str(order) + "^" + str(
                        rotgrp[i].exponent)
            else:
                has_paren_label = False
                naxes = len([x for x in rotgrp if x.exponent == 1])
                for i, rot in enumerate(rotgrp):
                    if i < naxes:
                        if SymmetryOperation.is_same_axis(
                                rot.axis, Vector(0, 0, 1)):
                            labels.append("(z)")
                            has_paren_label = True
                            rot.name = "S_" + str(order) + "(z)"
                        elif SymmetryOperation.is_same_axis(
                                rot.axis, Vector(0, 1, 0)):
                            labels.append("(y)")
                            has_paren_label = True
                            rot.name = "S_" + str(order) + "(y)"
                        elif SymmetryOperation.is_same_axis(
                                rot.axis, Vector(1, 0, 0)):
                            labels.append("(x)")
                            has_paren_label = True
                            rot.name = "S_" + str(order) + "(x)"
                        else:
                            label = "'" * (prime_count +
                                           (1 if has_paren_label else 0))
                            labels.append(label)
                            prime_count += 1
                            rot.name = "S_" + str(order) + label
                    else:
                        rot.name = "S_" + str(order) + labels[
                            i % naxes] + "^" + str(rot.exponent)
        prime_count = {'v': 0, 'd': 0, 'h': 0}
        has_paren_label = False
        for ref in self.reflections:
            subscript = ''
            if ref.is_principal_reflection():
                subscript = 'h'
            elif ref.is_dihedral():
                subscript = 'd'
            else:
                subscript = 'v'
            if SymmetryOperation.is_same_axis(ref.axis, Vector(0, 0, 1)):
                ref.name = "sigma_" + subscript + "(xy)"
                has_paren_label = True
            elif SymmetryOperation.is_same_axis(ref.axis, Vector(0, 1, 0)):
                ref.name = "sigma_" + subscript + "(xz)"
                has_paren_label = True
            elif SymmetryOperation.is_same_axis(ref.axis, Vector(1, 0, 0)):
                ref.name = "sigma_" + subscript + "(yz)"
                has_paren_label = True
            else:
                label = "'" * (prime_count[subscript] +
                               (1 if has_paren_label else 0))
                prime_count[subscript] += 1
                ref.name = "sigma_" + subscript + label
        self.classes.sort(key=attrgetter('first_element'))
Esempio n. 18
0
 def xyz(self):
     return Vector([atom.pos for atom in self.atoms])