def check_orthogonalities(self): # check row orthogonality check1 = np.zeros((self.nclasses, self.nclasses), dtype=complex) for i in range(self.nclasses): for j in range(self.nclasses): res = 0. for k in range(self.nclasses): res += self.tchar[i, k] * self.tchar[j, k].conj() * self.cdim[k] check1[i, j] = res tmp = self.order * np.identity(self.nclasses) t1 = utils._eq(check1, tmp) # check column orthogonality check2 = np.zeros((self.nclasses, self.nclasses), dtype=complex) for i in range(self.nclasses): for j in range(self.nclasses): res = 0. for k in range(self.nclasses): res += self.tchar[k, i] * self.tchar[k, j].conj() check2[i, j] = res tmp = np.diag( np.ones((self.nclasses, )) * self.order / np.asarray(self.cdim)) t2 = utils._eq(check2, tmp) return t1, t2
def check_coset(self, pref, p, coset): res = [] for elem in coset: rot = _all_rotations[elem] rvec = rot.rot_vector(pref) c1 = utils._eq(rvec, p) c2 = utils._eq(rvec, -p) if c1 or c2: res.append(True) else: res.append(False) return res
def is_faithful(self): # check if complete self.faithful = True checksum = np.ones((self.order, )) * np.sum(self.lelements) u, ind = np.unique(self.tmult, return_index=True) if len(u) != self.order: self.faithful = False tmp = np.sum(self.tmult_global, axis=0) if not utils._eq(tmp, checksum): self.faithful = False tmp = np.sum(self.tmult_global, axis=1) if not utils._eq(tmp, checksum): self.faithful = False
def sort_momenta(self): # check if cosets exists if self.coset1 is None or self.coset2 is None: self.smomenta1 = None self.smomenta2 = None return # search for conjugacy class so that # R*p_ref = p res1 = [] res2 = [] for p, p1, p2 in self.allmomenta: #print("momentum coset search") done = False # check if already in list for r in res1: if utils._eq(r[0], p1): done = True break # if not get coset if not done: #print("%r not in list" % p1) for i, c in enumerate(self.coset1): t = self.check_coset(self.pref1, p1, c) #print(t) if np.all(t): #print(" in coset %d" %i) res1.append((p1, i)) break #else: # print(" not in coset %d" %i) #else: # print("%r already in list" % p1) done = False # check if already in list for r in res2: if utils._eq(r[0], p2): done = True break if not done: for i, c in enumerate(self.coset2): t = self.check_coset(self.pref2, p2, c) if np.all(t): res2.append((p2, i)) break self.smomenta1 = res1 if len(self.smomenta1) != len(self.momenta1): print("some vectors not sorted") self.smomenta2 = res2 if len(self.smomenta2) != len(self.momenta2): print("some vectors not sorted")
def check_all_cosets(self, p, p1, p2): j1, j2 = None, None for m, j in self.smomenta1: if utils._eq(p1, m): j1 = j break if j1 is None: print("j1 is None") for m, j in self.smomenta2: if utils._eq(p2, m): j2 = j break if j2 is None: print("j2 is None") return j1, j2
def gen_momenta(self, pm=4): print("pm=%d" % pm) # pm: maximum component in each direction def _abs(x): return np.vdot(x, x) <= pm def _abs1(x, a): return np.vdot(x, x) == a gen = it.ifilter(_abs, it.product(range(-pm, pm + 1), repeat=3)) lp3 = [np.asarray(y, dtype=int) for y in gen] self.momenta = [ self._U.dot(y) for y in it.ifilter(lambda x: _abs1(x, self.p), lp3) ] self.momenta1 = [ self._U.dot(y) for y in it.ifilter(lambda x: _abs1(x, self.p1), lp3) ] self.momenta2 = [ self._U.dot(y) for y in it.ifilter(lambda x: _abs1(x, self.p2), lp3) ] # check allowed momentum combinations self.allmomenta = [] for p in self.momenta: for p1 in self.momenta1: for p2 in self.momenta2: if utils._eq(p1 + p2 - p): self.allmomenta.append((p, p1, p2)) if not self.allmomenta: raise RuntimeError("no valid momentum combination found")
def get_all_ops(self, jmax): res = [] ind = [] srange = self.s1+self.s2+1 _s1, _s2 = int(2*self.s1+1), int(2*self.s2+1) for j in range(jmax): jmult = int(2*j+1) for mj in range(jmult): for s in range(srange): for l in range(j+srange): c = self.calc_op(j, mj, l, s) if utils._eq(c): continue # orthonormalize to previos vectors for m1, m2 in it.product(range(_s1), range(_s2)): _c = c[:,m1,m2] n = np.sqrt(np.vdot(_c, _c)) if n < self.prec: continue #_c /= n #for old, oldi in zip(res, ind): # #if oldi[0] != j: # # continue # _c = utils.gram_schmidt(_c, old) # n = np.sqrt(np.vdot(_c, _c)) # if n < self.prec: # break #if n < self.prec: # continue res.append(_c.copy()) ind.append([j,mj-j,l,s,m1-self.s1,m2-self.s2]) res = np.asarray(res) ind = np.asarray(ind) return res, ind
def check_all_cosets(self, p, p1, p2): j1, j2 = None, None i1, i2 = None, None for m, j in self.smomenta1: if utils._eq(p1, m): j1 = j break for m, j in self.smomenta2: if utils._eq(p2, m): j2 = j break for m, k1, k2 in self.i1i2: if utils._eq(p, m): i1, i2 = k1, k2 break return j1, j2, i1, i2
def gen_momenta(self): pm = 4 # maximum component in each direction def _abs(x): return np.dot(x, x) <= pm def _abs1(x, a): return np.dot(x, x) == a gen = it.ifilter(_abs, it.product(range(-pm, pm + 1), repeat=3)) lp3 = [np.asarray(y, dtype=int) for y in gen] self.momenta = [y for y in it.ifilter(lambda x: _abs1(x, self.p), lp3)] self.momenta1 = [ y for y in it.ifilter(lambda x: _abs1(x, self.p1), lp3) ] self.momenta2 = [ y for y in it.ifilter(lambda x: _abs1(x, self.p2), lp3) ] self.allmomenta = [] # only save allowed momenta combinations for p in self.momenta: for p1 in self.momenta1: for p2 in self.momenta2: if utils._eq(p1 + p2 - p): self.allmomenta.append((p, p1, p2))
def select_elements(self): # self.elements contains the quaternions # self.lelements contains the "global" (unique) index of the element, # making the elements comparable between different groups self.elements = [] self.lelements = [] # all possible elements for the double cover octahedral group # all elements of O for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(el, 1)) self.lelements.append(i) # all elements of O with inversion if self.withinversion: for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(el, -1)) self.lelements.append(i + 24) # all elements in the double cover of O for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(-el, 1)) self.lelements.append(i + 48) # all elements in the double cover of O with inversion if self.withinversion: for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(-el, -1)) self.lelements.append(i + 72) if self.pref is None: self.p2 = 0 else: self.p2 = np.vdot(self.pref_cart, self.pref_cart) # select elements when pref is given if self.pref is not None and self.p2 > 1e-6: selected = [] elem = [] # change reference momentum to T1u basis bpref = self.U3.dot(self.pref) T1irrep = gg.genT1CMF(self.elements, inv=True, U=self.U3) # Go through all elements of T1 and check whether they leave pref # unchanged for mat, el, num in zip(T1irrep, self.elements, self.lelements): tmp = mat.dot(bpref) c1 = utils._eq(tmp - bpref) # if mat * bpref == bpref, the quaternion belongs to subgroup # invariant under pref and is appended if c1: selected.append(num) elem.append(el) if self.debug > 0: print("The group with P_ref = %r has %d elements:" % (self.pref_cart.__str__(), len(elem))) tmpstr = ["%d" % x for x in selected] tmpstr = ", ".join(tmpstr) print("[%s]" % tmpstr) # replace self.elements with only the relevant subgroup self.elements = elem self.lelements = selected
def Check1(self): for i in range(self.nirreps): for j in range(self.nirreps): res = 0. for k in range(self.nclasses): res += self.tchar[i, k] * self.tchar[ j, k].conj() * self.sclass[k] self.tcheck1[i, j] = res # check against tmp = self.order * np.identity(self.nclasses) return utils._eq(self.tcheck1, tmp)
def Check2(self): for i in range(self.nclasses): for j in range(self.nclasses): res = 0. for k in range(self.nirreps): res += self.tchar[k, i] * self.tchar[k, j].conj() self.tcheck2[i, j] = res # check against tmp = np.diag( np.ones((self.nirreps, )) * self.order / np.asarray(self.sclass)) return utils._eq(self.tcheck2, tmp)
def check_coset(g0, pref, p, coset): res = [] # check needs to be done in basis of T1u for elem in coset: look = g0.lelements.index(elem) rvec = T1irrep.mx[look].dot(pref) c1 = utils._eq(rvec, p) if c1: res.append(True) else: res.append(False) return res
def calc_index(self): def _r1(k): kn = np.vdot(k, k) if kn < self.prec or self.p1 == 0: return 0., 0. theta = np.arccos(k[2]/kn) phi = np.arctan2(k[1], k[0]) return theta, phi #self.rot_index = np.zeros((1, self.elength), dtype=int) self.angles = np.zeros((self.mlength, self.elength, 2)) self.rot_index = np.zeros((self.mlength, self.elength), dtype=int) # use the T1u irrep for the rotations, change to non-symmetric # spherical harmonics base s = 1./np.sqrt(2.) U = np.asarray([[0,-1.j,0],[0,0,1],[-1,0,0]]) Ui = U.conj().T rot = gen.genT1CMF(self.elements, inv=True) lpref = [[0,0,0],[0,0,1],[1,1,0],[1,1,1]] pref = np.asarray(lpref[self.p1]) for i, q in enumerate(rot): #_p1 = Ui.dot(q.dot(U.dot(pref))).real #theta, phi = _r1(_p1) #for l, (_k, k1, k2) in enumerate(self.allmomenta): # if utils._eq(_p1, k1): # self.rot_index[0,i] = l # self.angles[l] = np.asarray([theta,phi]) # break for j, (_p, _p1, _p2) in enumerate(self.allmomenta): p = Ui.dot(q.dot(U.dot(_p))) p1 = Ui.dot(q.dot(U.dot(_p1))).real ang = _r1(p1) p2 = Ui.dot(q.dot(U.dot(_p2))) if not utils._eq(_p1+_p2-_p): raise RuntimeError("some rotation does not conserve momentum!") for l, (_k, _k1, _k2) in enumerate(self.allmomenta): if utils._eq(p1, _k1) and utils._eq(p2, _k2): self.rot_index[j,i] = l self.angles[j,i] = np.asarray(ang) break
def characters(self, representatives): if self.mx is None: return np.nan elif ((self.char is not None) and (self.rep is not None) and (utils._eq(self.rep, representatives))): return self.char else: char = np.zeros((len(representatives), ), dtype=complex) for i, r in enumerate(representatives): char[i] = np.trace(self.mx[r]) self.char = char self.rep = representatives return char
def select_elements(self): # self.elements contains the quaternions # self.lelements contains the "global" (unique) index of the element, # making the elements comparable between different groups self.elements = [] self.lelements = [] # all possible elements for the double cover octahedral group for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(el, 1)) self.lelements.append(i) if self.withinversion: for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(el, -1)) self.lelements.append(i + 24) for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(-el, 1)) self.lelements.append(i + 48) if self.withinversion: for i, el in enumerate(quat.qPar): self.elements.append(quat.QNew.create_from_vector(-el, -1)) self.lelements.append(i + 72) if self.pref is None: self.p2 = 0 else: self.p2 = np.vdot(self.pref_cart, self.pref_cart) # select elements when pref is given if self.pref is not None and self.p2 > 1e-6: selected = [] elem = [] # change reference momentum to T1u basis bpref = self.U3.dot(self.pref) T1irrep = gg.genT1CMF(self.elements, inv=True, U=self.U3) #for el, num in zip(self.elements, self.lelements): for mat, el, num in zip(T1irrep, self.elements, self.lelements): tmp = mat.dot(bpref) #tmp = mat.dot(self.pref) #tmp = el.rotation_matrix(self.withinversion).dot(self.pref) c1 = utils._eq(tmp - bpref) #c1 = utils._eq(tmp - self.pref) if c1: selected.append(num) elem.append(el) if self.debug > 0: print("The group with P_ref = %r has %d elements:" % (self.pref_cart.__str__(), len(elem))) tmpstr = ["%d" % x for x in selected] tmpstr = ", ".join(tmpstr) print("[%s]" % tmpstr) self.elements = elem self.lelements = selected
def check_ortho(self, irrep): if len(self.irreps) == 0: return True char = irrep.characters(self.crep) n = len(self.irreps) # check row orthogonality check1 = np.zeros((n, ), dtype=complex) for i in range(n): res = 0. for k in range(self.nclasses): res += self.tchar[i, k] * char[k].conj() * self.cdim[k] check1[i] = res t1 = utils._eq(check1) return t1
def calc_pion_cg(self, p, p1, p2, irname): """Calculate the elements of the Clebsch-Gordan matrix. Assumes that p=p1+p2, where all three are 3-vectors. """ # get irrep of group g ir = self.g.instances[self.g.lirreps.index(irname)] # j1 and j2 are the conjugacy classes containing # the given momenta p1 and p2 j1, j2, i1, i2 = self.check_all_cosets(p, p1, p2) #print(j1, j2, p1) cg = np.zeros((ir.dim, ir.dim), dtype=complex) for ind, r in enumerate(self.g.lrotations): rep = ir.mx[ind] # hard coded for pi-pi scattering g1 = self.gamma1[r, j1, i1] if utils._eq(g1): continue g2 = self.gamma2[r, j2, i2] if utils._eq(g2): continue cg += rep.conj() * g1 * g2 cg *= float(ir.dim) / self.g.order return cg
def get_cg(self, p1, p2, irrep, change_basis=True): """Pass the 3-momenta of both particles, check for correct order of momenta. """ # change the basis to the one used if change_basis: _p1 = self._U.dot(p1) _p2 = self._U.dot(p2) else: _p1 = np.asarray(p1) _p2 = np.asarray(p2) cg = [] index = None # select correct momentum for ind, (p, k1, k2) in enumerate(self.allmomenta): if utils._eq(k1, _p1) and utils._eq(k2, _p2): index = ind break # select correct momentum #for ind, (p, k1, k2) in enumerate(self.allmomenta): # if utils._eq(k1, p1) and utils._eq(k2, p2): # index = ind # break # if momentum not allowed, return None if index is None: #print("Momentum not present!") return None # get coefficients for i, (name, multi, dim) in enumerate(self.cgnames): if irrep != name: continue #print(self.cg.shape) cg = self.cg[i, :multi, :dim, index] return cg return None
def print_old(self, j): line = "-" * (3*6+5+len(self.allmomenta)*12) print("O_s=%d(p_1) x O_s=%d(p_2) -> O(P)_{S,L}^{J,m_J}"%( self.s1, self.s2)) self.print_head(colsize=3*6+5) print(line) jmult = int(2*j+1) srange = self.s1+self.s2+1 for mj in range(jmult): for s in range(srange): for l in range(j+srange): c = self.calc_op(j, mj, l, s) if utils._eq(c): continue #print("j, mj, l, s = %d, %d, %d, %d" % (j, mj-j, l, s)) self.print_coeffs(c, [j,mj-j,l,s]) print(line)
def is_representation(self, tmult, verbose=False): n = self.mx.shape[0] for i in range(n): mxi = self.mx[i] for j in range(n): mxj = self.mx[j] mxk = self.mx[tmult[i, j]] mxij = mxi.dot(mxj) if not utils._eq(mxij, mxk): if verbose: print("elements %d * %d not the same as %d:" % (i, j, tmult[i, j])) print("multiplied:") print(mxi) print(mxj) print("result:") print(mxij) print("expected:") print(mxk) #print("elements %d * %d (%r * %r) not the same as %d (%r / %r)" % (i, j, mxi, mxj, tmult[i,j], mxij, mxk)) return False return True
def get_pion_cg(self, irname): try: ind = self.irreps.index(irname) return irname, self.cgs[ind], self.allmomenta except: pass result = [] # iterate over momenta for p, p1, p2 in self.allmomenta: res = self.calc_pion_cg(p, p1, p2, irname) if res is None: continue result.append(res) result = np.asarray(result) # check if all coefficients are zero if utils._eq(result): cgs = None else: # orthonormalize the basis cgs = self._norm_cgs(result) self.irreps.append(irname) self.cgs.append(cgs) return irname, cgs, self.allmomenta
def find_flip_vectors(self): irrep = TOh1D(self.elements) flips = [] n = self.nclasses mx_backup = irrep.mx.copy() # flip classes for k in range(1, n): #print("flip %d classes" % k) for ind in it.combinations(range(1, n), k): fvec = np.ones((n, )) for x in ind: fvec[x] *= -1 irrep.flip_classes(fvec, self.lclasses) check1 = np.sum(irrep.mx) check2 = irrep.is_representation(self.tmult, verbose=False) irrep.mx = mx_backup.copy() if utils._eq(check1) and check2: flips.append(fvec.copy()) if self.debug > 1: print("fvec: %s" % fvec.__str__()) print("sum check %r, irrep check %r" % (check1, check2)) flips = np.asarray(flips, dtype=int) self.flip, self.suffixes = self.sort_flip_vectors(flips)
def MkMultTbl(self): mt = np.zeros((self.order, self.order), dtype=int) for i in range(self.order): mxi = self.mx[i] for j in range(self.order): mxj = self.mx[j] prodij = np.dot(mxi, mxj) for k in range(self.order): mxk = self.mx[k] if utils._eq(prodij, mxk, self.prec): mt[i, j] = k self.tmult[i, j] = mt[i, j] # check if faithful if self.dim == 1: # 1D irreps are not faithful self.faithful = False else: self.faithful = True for i in range(self.order): row = np.unique(mt[i]) col = np.unique(mt[:, i]) if row.size != self.order or col.size != self.order: self.faithful = False break
def test_3D_no_inversion(self): res = gg.gen3D(self.elements) for i in res: self.assertFalse(utils._eq(i))
def test_4D_inversion(self): res = gg.gen4D(self.elements, inv=True) for i in res: self.assertFalse(utils._eq(i))
def __init__(self, p, p1, p2, groups=None): """p, p1, and p2 are the magnitudes of the momenta. """ self.prec = 1e-6 # save the norm of the momenta for the combined system # and each particle self.p = p self.p1 = p1 self.p2 = p2 # lookup table for reference momenta lpref = [ np.asarray([0., 0., 0.]), np.asarray([0., 0., 1.]), np.asarray([1., 1., 0.]), np.asarray([1., 1., 1.]) ] # save reference momenta self.pref = lpref[p] self.pref1 = lpref[p1] self.pref2 = lpref[p2] # get the basic groups if groups is None: self.g0 = None self.g = None self.g1 = None self.g2 = None else: self.g0 = groups[0] self.g = groups[p] self.g1 = groups[p1] self.g2 = groups[p2] # get the cosets, always in the maximal group (2O here) # is set to None if at least one group is None self.coset1 = self.gen_coset(self.g1) self.coset2 = self.gen_coset(self.g2) #print(self.coset1) #print(self.coset2) # generate the allowed momentum combinations and sort them into cosets self.gen_momenta() if groups is not None: self.sort_momenta() # calculate induced rep gamma # here for p1 and p2 the A1(A2) irreps are hard-coded # since only these contribute to pi-pi scattering if groups is None: self.gamma1 = None self.gamma2 = None else: irstr = "A1" if p1 < 1e-6 else "A2" self.gamma1 = self.gen_ind_reps(self.g, self.g1, irstr, self.coset1) irstr = "A1" if p2 < 1e-6 else "A2" self.gamma2 = self.gen_ind_reps(self.g, self.g2, irstr, self.coset2) #print(self.gamma1[:5]) self.irreps = [] self.cgs = [] # choose mu1, mu2 and i1, i2, according to Dudek paper # since A1 and A2 are 1D, set mu to 0 self.mu1 = 0 self.mu2 = 0 if self.p == 0: # in this case chose the hightest option self.i1 = -1 self.i2 = -1 self.i1i2 = [(self.pref, -1, -1)] else: self.i1i2 = [] for m in self.momenta: for ((m1, i1), (m2, i2)) in it.product(self.smomenta1, self.smomenta2): if utils._eq(m1 + m2 - m): self.i1i2.append((m, i1, i2)) self.i1 = i1 self.i2 = i2 break
def find_flip_vectors_imaginary(self): irrep = TOh1D(self.elements) flips = [] def check_index(i1, i2): for x in i2: try: i1.index(x) return True except ValueError: continue return False def f_vec(n, ind1, ind2, indm): fvec = np.ones((n, ), dtype=complex) for i in ind1: fvec[i] *= 1.j for i in ind2: fvec[i] *= -1.j for i in indm: fvec[i] *= -1. return fvec n = self.nclasses mx_backup = irrep.mx.copy() count = 0 if self.debug > 0: print("find imaginary flips") print("number of classes %d" % n) # multiply classes with -1, i and -i, # always the same number of classes with +i and -i # total number of classes flipped for kt in range(1, n): if self.debug > 1: print("flip %d classes" % (kt)) # half the number of classes with imaginary flip for ki in range(1, kt // 2 + 1): if self.debug > 1: print("number of imaginary classes: %d" % (2 * ki)) # get indices for classes with +/- i for ind1 in it.combinations(range(1, n), ki): for ind2 in it.combinations(range(1, n), ki): # check if some class is in both index arrays # and skip if it is if check_index(ind1, ind2): if self.debug > 1: print("collision for +/-i:") print("+i: %s" % ind1.__str__()) print("-i: %s" % ind2.__str__()) continue # get indices for classes with -1 for indm in it.combinations(range(1, n), kt - 2 * ki): # check if some class is already taken # and skip if it is if check_index(ind1, indm): if self.debug > 1: print("collision for +i/-1:") print("+i: %s" % ind1.__str__()) print("-i: %s" % indm.__str__()) continue # check if some class is already taken # and skip if it is if check_index(ind2, indm): if self.debug > 1: print("collision for -i/-1:") print("+i: %s" % ind2.__str__()) print("-i: %s" % indm.__str__()) continue fvec = f_vec(n, ind1, ind2, indm) irrep.flip_classes(fvec, self.lclasses) check1 = np.sum(irrep.mx) check2 = irrep.is_representation(self.tmult) irrep.mx = mx_backup.copy() if self.debug > 0: print("fvec: %s" % fvec.__str__()) print("sum check %r, irrep check %r" % (check1, check2)) if utils._eq(check1) and check2: count += 1 flips.append(fvec.copy()) # hard-coded due to long runtime if count == 4: return flips = np.asarray(flips, dtype=complex) self.flip_i, self.suffixes_i = self.sort_flip_vectors(flips, special=True)
def test_different_vectors_complex(self): vec = np.ones((3, ), dtype=complex) + 0.5j vec1 = np.ones((3, ), dtype=complex) * 0.5 + 2.j self.assertFalse(ut._eq(vec, vec1))
def __init__(self, p, p1, p2, groups=None, ir1=None, ir2=None): """p, p1, and p2 are the magnitudes of the momenta. """ self.prec = 1e-6 # save the norm of the momenta for the combined system # and each particle self.p = p self.p1 = p1 self.p2 = p2 indp0, indp1, indp2, indp = None, None, None, None self.U0 = None # transformation from cartesian to symmetrized spherical # harmonics self._U = np.asarray([[0., -1.j, 0.], [0., 0., 1.], [-1., 0., 0.]]) if groups is not None: pindex = [x.p2 for x in groups] try: indp0 = pindex.index(0) indp = pindex.index(p) indp1 = pindex.index(p1) indp2 = pindex.index(p2) except IndexError: raise RuntimeError("no group with P^2 = %d (%d, %d) found" % (p, p1, p2)) self.U0 = groups[indp0].U3 self.T0 = groups[indp0].U2 if (not utils._eq(self.U0, groups[indp].U3)) or (not utils._eq( self.T0, groups[indp].U2)): raise RuntimeError( "The group projecting to has different basis") if (not utils._eq(self.U0, groups[indp1].U3)) or (not utils._eq( self.T0, groups[indp1].U2)): raise RuntimeError("The group of op 1 has different basis") if (not utils._eq(self.U0, groups[indp1].U3)) or (not utils._eq( self.T0, groups[indp1].U2)): raise RuntimeError("The group of op 2 has different basis") self._U = self.U0.dot(self._U) # save reference momenta self.pref = self._U.dot(groups[indp0].pref_cart) self.pref1 = self._U.dot(groups[indp1].pref_cart) self.pref2 = self._U.dot(groups[indp2].pref_cart) # get the cosets, always in the maximal group (2O here) # returns None if groups is None self.coset1 = self.gen_coset(groups, indp0, indp1) self.coset2 = self.gen_coset(groups, indp0, indp2) #print(self.coset1) #print(self.coset2) # generate the allowed momentum combinations and sort them into cosets self.gen_momenta(pm=max(p, p1, p2, 4)) # calculate induced rep gamma # here for p1 and p2 the A1(A2) irreps are hard-coded # since only these contribute to pi-pi scattering if groups is None: self.gamma1 = None self.gamma2 = None self.dim1 = 0 self.dim2 = 0 self.irstr1 = ir1 self.irstr2 = ir2 else: if ir1 is None: self.irstr1 = "A1u" if int(p1) in [0, 3, 5, 6] else "A2u" else: self.irstr1 = ir1 if self.irstr1 not in groups[indp1].irrepsname: raise RuntimeError("irrep %s not in group 1!" % self.irstr1) else: self.dim1 = groups[indp1].irrepsname.index(self.irstr1) self.dim1 = groups[indp1].irrepdim[self.dim1] self.gamma1 = self.gen_ind_reps(groups, indp0, indp1, self.irstr1, self.coset1) #print(self.gamma1[:5]) if ir2 is None: self.irstr2 = "A1u" if int(p2) in [0, 3, 5, 6] else "A2u" else: self.irstr2 = ir2 if self.irstr2 not in groups[indp2].irrepsname: raise RuntimeError("irrep %s not in group 2!" % self.irstr2) else: self.dim2 = groups[indp2].irrepsname.index(self.irstr2) self.dim2 = groups[indp2].irrepdim[self.dim2] self.gamma2 = self.gen_ind_reps(groups, indp0, indp2, self.irstr2, self.coset2) #print(self.gamma2[:5]) self.sort_momenta(groups[indp0]) self.calc_cg_ha(groups, indp)