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
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
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)
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
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
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
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 = []
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()
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
def values(self): vals = [c.value for c in self.coords] return Vector(vals)
def values_for_matrix(self, mat): return Vector([c.value_for_molecule_matrix(mat) for c in self.coords])
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)
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
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
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])])
def values_for_molecule(self, mol): return Vector([c.value_for_molecule(mol) for c in self.coords])
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'))
def xyz(self): return Vector([atom.pos for atom in self.atoms])