def __init__(self, wd): self.wd = wd # Load the default settings from 'default.settings' file settings = self.loadDefaultSettings() # Load the settings from user command runSettings = self.initParser(settings) # Combine the two dicts for k,v in runSettings.items(): settings[k] = v if os.path.abspath(settings['Results']) != settings['Results']: settings['Results'] = os.path.abspath(os.path.join(wd, settings['Results'])) if not (os.path.exists(settings['Results'])): os.makedirs(settings['Results']) if settings['ForceLog']: settings['LogFolder'] = 'log/' if os.path.abspath(settings['LogFolder']) != settings['LogFolder']: settings['LogFolder'] = os.path.abspath(os.path.join(wd, settings['LogFolder'])) if not (os.path.exists(settings['LogFolder'])): os.makedirs(settings['LogFolder']) self.initLogging(settings) # A useful check before going on : no export was selected if not any((settings['LatexOutput'], settings['MathematicaOutput'], settings['PythonOutput'], settings['UFOfolder'])): loggingCritical("Error : No ouput would be produced after the computation. Please choose at least one export option (Latex, Mathematica, Python, UFO).") exit() if settings['UFOfolder'] is not None: if os.path.abspath(settings['UFOfolder']) != settings['UFOfolder']: settings['UFOfolder'] = os.path.abspath(os.path.join(wd, settings['UFOfolder'])) if not (os.path.exists(settings['UFOfolder'])): os.makedirs(settings['UFOfolder']) if 'RealBasis' in settings and type(settings['RealBasis']) == str: settings['RealBasis'] = settings['RealBasis'].lower() if settings['RealBasis'] in ('none', 'true', 'false'): settings['RealBasis'] = eval(settings['RealBasis'].capitalize()) elif settings['RealBasis'] not in ('adjoint', 'all'): loggingInfo("Warning : RealBasis argument not understood. Setting it to 'adjoint'.") settings['RealBasis'] = 'adjoint' if 'MoreGroupTheoryInfo' in settings: if settings['MoreGroupTheoryInfo'] is True: settings['MoreGroupTheoryInfo'] = 10 elif settings['MoreGroupTheoryInfo'] is False: settings['MoreGroupTheoryInfo'] = 0 elif type(settings['MoreGroupTheoryInfo']) != int: loggingInfo("Warning : 'MoreGroupTheoryInfo' setting must be a boolean or a positive integer. Setting it to False.") settings['MoreGroupTheoryInfo'] = 0 else: settings['MoreGroupTheoryInfo'] = 0 self.yamlSettings = self.readModelFile(settings) self.settings = settings
def Beta(self, *args, nLoops=1): # Possibly store the result of the computation. This is useful to # speed up the computation in presence of kinetic mixing storeAfter = False if self.store: key = tuple([*args, nLoops]) if key in self.storeDic: return self.storeDic[key] storeAfter = True ret = 0 for j, coeff in enumerate(self.coefficients[nLoops]): if coeff != 0: try: tmp = self.functions[nLoops][j](*args) if tmp != 0: if ret == 0: ret = coeff * tmp else: ret += coeff * tmp except BaseException as e: loggingCritical( f"## Error while computing {self.functions[nLoops][j].__name__}. ##" ) loggingCritical('>> ' + str(e)) exit() if storeAfter: self.storeDic[key] = ret return ret
def couplingsDefinition(self, model): s = "" substitutedCouplings = [ str(k) for subDic in model.substitutions.values() for k in subDic ] for cType in model.toCalculate: if 'Anomalous' in cType: continue self.cDic[cType] = {} for k, v in model.allCouplings.items(): if v[0] == cType and k not in substitutedCouplings: # The conjugated couplings are removed, and must be replaced by Conjugate[ ... ] if not k[-2:] == '^*' and not k[-4:] == '^{*}' and not k[ -4:] == 'star': self.cDic[cType][v[1]] = pycode(v[1]).replace( '{', '').replace('}', '') self.allCouplings[k] = pycode(v[1]).replace( '{', '').replace('}', '') else: candidates = [ el for el in model.allCouplings if el in k and el != k ] if len(candidates) == 1: self.conjugatedCouplings[k] = candidates[0] else: lengths = [len(el) for el in candidates] i, maxLen = lengths.index( max(lengths)), max(lengths) lengths.remove(maxLen) if maxLen not in lengths: self.conjugatedCouplings[k] = candidates[i] else: loggingCritical( f"Warning in Python export: could not determine the conjugate quantity of {k} automatically." + "\n -> The user will have to modify the output Python file manually." ) s += f"\n\n # {self.translation[cType]}" if cType == 'Vevs' and model.gaugeFixing is None: s += "\n # For vevs the gauge must be fixed. Let's use for instance the Landau gauge :\n" s += " self.xiGauge = 0\n" self.gaugeFixing = True for c, cName in self.cDic[cType].items(): s += f"\n self.{cName} = Coupling('{cName}', '{cType}'" if cName in self.latex: s += ", latex='" + self.latex[cName].replace( '\\', '\\\\').replace("'", "\\'") + "'" if isinstance(c, mSymbol): s += ', shape=' + str(c.shape).replace(' ', '') s += ')' return s
def UFOsubstitutions(self, model, inconsistentRGEerror=True): """ Transform the UFO mapping dict into substitutions, and apply them to the RGEs """ subsDic = {} yukMats = {} for k, v in self.mapping.items(): if not '[' in k and not ']' in k: subsDic[k] = v else: # Handle Yukawa-matrix assumptions base = k[:k.find('[')] inds = eval(k.replace(base, '')) if base not in yukMats: if base not in self.couplingStructure: loggingCritical( f"Error : coupling matrix '{base}' is unknown.") continue yukMats[base] = (self.couplingStructure[base], {}) if v != '0': yukMats[base][1][tuple(inds)] = v for k, (shape, dic) in yukMats.items(): matList = [[ dic[(i, j)] if (i, j) in dic else 0 for j in range(shape[1]) ] for i in range(shape[0])] subsDic[k] = str(matList).replace("\\'", "'") subsDic = getSubstitutions(model, subsDic) doSubstitutions(model, subsDic, inconsistentRGEerror=inconsistentRGEerror)
def __new__(cls, *args, symmetric=False, hermitian=False, real=False, unitary=False): if args[1] != args[2]: if symmetric or hermitian or unitary: loggingCritical( "Matrix <" + args[0] + "> cannot be symmetric, hermitian or unitary since it is not a square matrix." ) exit() if not args[1] == args[2] == 1: obj = Symbol.__new__(cls, args[0], commutative=False, hermitian=hermitian) obj.is_symmetric = symmetric obj.is_realMatrix = real obj.is_unitary = unitary else: obj = Symbol.__new__(cls, args[0], real=real) obj.shape = (args[1], args[2]) return obj
def pseudoScalarHandling(self, allScalars, scalarPos): gNabelList = [ gName for gName, g in self.groups.items() if self.Qnb[gName] != 0 and g.dimR(self.Qnb[gName]) > 1 ] self.pseudoRanges = [el[3] for el in self.pseudoRealReps] self.pseudoRepsPos = [ gNabelList.index(el[0]) for el in self.pseudoRealReps ] self.pseudoRealReps = [(el[0], el[2]) for el in self.pseudoRealReps] # First, check that a solution to X = pseudoRealConjugate(X) = C.X exists C = sMat([[1, 0], [0, -1]]) for gName in gNabelList: g = self.groups[gName] rep = self.Qnb[gName] if (gName, rep) in self.pseudoRealReps: m = self.idb.get(g.type, '_pseudoMetric', rep) else: m = eye(g.dimR(rep)) C = C.kroneckerProduct(m) N = C.shape[0] ns = (eye(N) - C).nullSpace() if ns == {}: loggingCritical( f"Error: real scalar '{self._name}' cannot be self-conjugate.") exit() nsMat = sMat(N, N // 2) for r, rowDic in ns.items(): for c, v in rowDic.items(): nsMat[c, r] = v * self.pseudoNorm self.pseudoNS = nsMat self.pseudoNSadj = 1 / (self.pseudoNorm**2 * 2) * nsMat.adjoint() pseudoTransfo = eye(N // 2).append(I * eye(N // 2), axis=1) * nsMat self.pseudoTransfo = {} for k, v in pseudoTransfo.todok().items(): if k[0] not in self.pseudoTransfo: self.pseudoTransfo[k[0]] = {} self.pseudoTransfo[k[0]][k[1]] = v def computeExpr(inds, symb): res = 0 for k, v in self.pseudoTransfo[flattenedTensorPos( self.indexStructure, inds)].items(): res += v * symb[flattenedTensorInds(self.indexStructure, k)] return res self.computeComponents = computeExpr
def expandLagrangian(self, RGmodule): self.lagrangian = Lagrangian(self.saveSettings, self, RGmodule) loggingInfo("Done.") loggingInfo("Expanding the Lagrangian ...") self.lagrangian.expand() self.expandedPotential = self.lagrangian.expandedPotential # Read the substitutions now self.substitutions = {} self.gutNorm = {} if 'Substitutions' in self.saveSettings and self.saveSettings[ 'Substitutions'] != {}: self.substitutions = getSubstitutions( self, self.saveSettings['Substitutions'] if 'Substitutions' in self.saveSettings else {}) # If any matrix substitution is provided, check now that the shapes correspond. # This is to prevent the computation from starting if not. # Also, if the matrix satisfies Y = Diag(y), replace it by y*Identity(nG) if 'yukMat' in self.substitutions: for k, v in self.substitutions['yukMat'].items(): shape = tuple([ el for el in self.couplingStructure[k] if type(el) != bool ]) if not isinstance(v[1], DiagonalMatrix): if v[1].shape != shape: loggingCritical("Error : The shape of the matrix " + k + " given in Substitutions" + " should be " + str(shape)) exit() else: if shape[0] != shape[1]: loggingCritical( "Error in Substitutions : the 'diag' keyword cannot be used for" + " the rectangular matrix '" + k + "'") exit() cType, diagMat = self.substitutions['yukMat'][k] self.substitutions['yukMat'][k] = (cType, diagMat.arg * Identity(shape[0])) # VeVs if self.vevs != {}: for k, v in self.vevs.items(): RGmodule.Vdic[(v[0], )] = v[1] # Anomalous dimensions if self.fermionAnomalous != {}: for k, v in self.fermionAnomalous.items(): RGmodule.gammaFdic[v] = k if self.scalarAnomalous != {}: for k, v in self.scalarAnomalous.items(): RGmodule.gammaSdic[v] = k
def sympyParse(expr): if '^' in expr: loggingCritical( f"\nError in expression '{errorExpr}' : powers must be written using the '**' operator" ) exit() return parse_expr(expr, local_dict=localDict, transformations=standard_transformations[1:] + (implicit_multiplication, ), evaluate=False)
def expand(self): """ Performs a first level of expansion of the Lagrangian. More precisely, replaces all the occurences of user-defined quantities with their expression.""" def isComplex(cType, c, expTerm): return (cType in ('QuarticTerms', 'TrilinearTerms', 'ScalarMasses') and c + 'star' not in self.potential[cType] and not (c[-4:] == 'star' and c[:-4] in self.potential[cType]) and expTerm.find(I) != set()) count = 0 content = () for couplingType, terms in self.potential.items(): if couplingType in self.translateContent: self.dicToFill = self.translateDic(self.RGmodule)[couplingType] content = self.translateContent[couplingType] self.currentPotentialDic = self.potential[couplingType] else: continue for coupling, term in list(terms.items()): TensorObject.globalTensorCount = 1 parsedTerm = [] try: expTerm = self.parseExpression( term, expandedTerm=parsedTerm).dic[()] except BaseException as e: loggingCritical( f"\nError while expanding the term '{coupling}':") loggingCritical(f" -> {str(e)}") exit() self.expandedPotential[couplingType][coupling] = parsedTerm[0] self.fullyExpandedPotential[couplingType][coupling] = expTerm self.fillTensorDic(coupling, expTerm, content) if isComplex(couplingType, coupling, expTerm): self.conjugateScalarTerm(couplingType, coupling, content) count += 1 count += 1 print_progress(count, self.model.nCouplings, prefix=' ' * 4, bar_length=20, printTime=self.model.times, logProgress=True) # Finally, remove all vanishing elements from dict for k, v in list(self.dicToFill.items()): if v == 0: self.dicToFill.pop(k)
def getDefinitions(self, settings): ####################### # Pre-Defined objects # ####################### preDefinedObjects = {} # #Kronecker delta preDefinedObjects['kd'] = TensorObject("kd") # #Eps tensors preDefinedObjects['Eps'] = TensorObject("Eps") self.definitions.update(preDefinedObjects) ############# # Particles # ############# particleTensors = {} for name, p in self.model.Particles.items(): particleTensors[name] = TensorObject(p) # Self-conjugate scalars have a complex conjugate counterpart if p.pseudoRealReps != []: particleTensors[name + 'bar'] = TensorObject(p).getConjugate() self.definitions.update(particleTensors) ######################## # User-defined objects # ######################## if 'Definitions' in settings[ 'Potential'] and settings['Potential']['Definitions'] != {}: for k, v in settings['Potential']['Definitions'].items(): obj = self.parseExpression(v, name=k) if obj is not None: self.definitions[str(obj.symbol)] = obj if str(type(obj.expr)) == 'cgc': self.cgcs[str(obj.symbol)] = obj if str(type(obj.expr)) == 't': self.definitions[str(obj.symbol) + 'bar'] = obj.getConjugate() else: loggingCritical( f"Warning : unable to read the definition '{k}: {v}'. Skipping." )
def getQnb(self, dic, gaugeGroups): """Get the Qnbs of the particle from the dic. The only thing to do is to transform the DimR notation into DynkinLabels""" for k,v in dic.items(): g = gaugeGroups[k] if isinstance(v, str): if not g.abelian: loggingCritical(f"Error while reading particle {self.name} : for non-abelian " + "gauge factors, quantum number must be integers or dynkin labels.") exit() v = parse_expr(v.replace('i','I').replace('Sqrt','sqrt')) if isinstance(v, list): v = tuple(v) if not isinstance(v, tuple) and not g.abelian: dynk = tuple(self.idb.get(g.type, 'dynkinLabels', v, realBasis=GaugeGroup.realBasis)) if type(dynk[0]) == list: loggingCritical(f"Error : more than one representation of the group {g.type} have dimension {dynk} :") loggingCritical(' -> ' + ', '.join([str(el) for el in dynk[:-1]]) + ' and ' + str(dynk[-1])) loggingCritical("Please use the Dynkin-labels notation instead to remove the ambihuity.") exit() v = dynk dic[k] = v return dic
def as_explicit(self, applyFunc=None): if self.shape == (1, 1): return self if isinstance(self.shape[0], Symbol) or isinstance( self.shape[1], Symbol): loggingCritical( "Error : a Matrix with symbolic number of generations as rows and/or columns cannot be given as an explicit matrix." ) exit() mat = SparseMatrix(*self.shape, {}) for i in range(self.shape[0]): for j in range(self.shape[1]): assump = {} if self.is_realMatrix or (self.is_hermitian and i == j): assump['real'] = True else: assump['complex'] = True if i == j and self.is_antisymmetric: mat[i, j] = 0 elif i <= j: mat[i, j] = Symbol( '{' + str(self) + '}_{' + str(i + 1) + str(j + 1) + '}', **assump) else: if self.is_symmetric: mat[i, j] = mat[j, i] elif self.is_antisymmetric: mat[i, j] = -1 * mat[j, i] elif self.is_hermitian: mat[i, j] = conjugate(mat[j, i]) else: mat[i, j] = Symbol( '{' + str(self) + '}_{' + str(i + 1) + str(j + 1) + '}', **assump) if applyFunc is None: return Matrix(mat) # The following case is needed in Python export retList = mat.tolist() for i, row in enumerate(retList): for j, el in enumerate(row): retList[i][j] = applyFunc(el) return retList
def getAssumptions(self): for cType, terms in self.potential.items(): if cType == 'Definitions': continue newKeys = [] for k, v in terms.items(): assumptions = {} invalidAssumptions = [] if cType == 'ScalarMasses': assumptions = {'squared': False} if type(v) != tuple: v = (v, ) if type(v) == tuple: if 'real' in v: assumptions['real'] = True if 'symmetric' in v: assumptions['symmetric'] = True if 'hermitian' in v: assumptions['hermitian'] = True if 'unitary' in v: assumptions['unitary'] = True if cType == 'ScalarMasses': if 'squared' in v: assumptions['squared'] = True invalidAssumptions = [ el for el in v[1:] if el not in assumptions ] if assumptions != {} or invalidAssumptions != []: self.potential[cType][k] = v[0] for el in invalidAssumptions: loggingCritical("Warning : assumption '" + el + "' is not understood. Ignoring it.") self.assumptions[k] = assumptions newKeys.append(k) self.potential[cType] = { k: v for k, v in zip(newKeys, terms.values()) }
def initFromString(self, s): if s.lower() == 'kd': # Kronecker delta self.symbol = IndexedBase('KD') self.dic = None self.range = None self.dim = None self.sym = False elif s.lower() == 'eps': # Kronecker delta self.symbol = IndexedBase('Eps') self.dic = None self.range = None self.dim = None self.sym = False else: loggingCritical("Error : Unkown tensor object '{s}'.") return
def checkDependencies(): from Logging import loggingCritical, loggingInfo dep = checkDependenciesAux(requirements) if dep[0] is False: ex = False if dep[1] != []: mess = 'Error: some dependencies are missing/outdated.\n' + '\n'.join( dep[1]) loggingCritical(mess) ex = True if dep[2] != []: mess = 'Warning: some optional dependencies are missing/outdated.\n' + '\n'.join( dep[2]) loggingInfo(mess) if ex: exit()
def mapBetaFunctions(self): loggingInfo("Re-combining the RGES ...") #This is for progress bar nTot = 0 count = 0 for couplingType, RGlist in self.allRGEs.items(): nTot += len( self.potential[couplingType]) * self.loopDic[couplingType] for couplingType, RGloops in self.allRGEs.items(): mat = self.lagrangianMapping[couplingType] for n, RGlist in RGloops.items(): couplingRGEs = mat * Matrix(RGlist) # Take into account the beta-exponent expFactor = 1 if 'Anomalous' not in couplingType: exponent = self.betaExponent(n + 1) - 2 * (n + 1) if exponent != 0: expFactor = Pow(4 * pi, exponent) for pos, coupling in enumerate( list(self.potential[couplingType])): try: self.couplingRGEs[couplingType][n][coupling] = expand( couplingRGEs[pos] * expFactor) except BaseException as e: loggingCritical( f"Error expanding term at : {couplingType}, {n}, {pos}" ) loggingCritical(e) exit() count += 1 print_progress(count, nTot, prefix=' ', bar_length=20, printTime=self.times)
def getVevs(self, settings): if 'Vevs' in settings and settings['Vevs'] != {}: for k, v in settings['Vevs'].items(): if '[' in v and ']' in v: try: field = v[:v.find('[')] inds = eval('(' + v[v.find('[') + 1:v.find(']')] + ',)') except: loggingCritical("Error while reading the vev '" + k + "'. Skipping.") continue else: field = v inds = tuple() if field not in self.Scalars: loggingCritical("Error while reading the vev '" + k + "' : scalar '" + field + "' is unkown. Skipping") continue field = self.Scalars[field] ni = len(field.indexStructure) if len(inds) != ni or any([ i < 1 or i > field.indexStructure[pos] for pos, i in enumerate(inds) ]): loggingCritical("Error while reading the vev '" + k + "' : " + "scalar '" + str(field) + "' should have exactly " + str(ni) + (" indices with ranges " if ni > 1 else " index with range ") + str(field.indexStructure) + ". Skipping") continue vSymb = Symbol(k, real=True) inds = tuple([i - 1 for i in inds]) scalarStr = str(field) if inds != tuple(): scalarStr += str(inds).replace('(', '[').replace( ')', ']').replace(',]', ']') # Now determine whether the scalar is a real/imaginary part of a cplx field fromCplx = field.fromCplx if fromCplx != False: part = fromCplx.realFields.index(field) self.vevs[k] = (self.allScalars[scalarStr][0], vSymb, scalarStr, fromCplx, part) else: self.vevs[k] = (self.allScalars[scalarStr][0], vSymb, scalarStr)
def write(self, path): tmpDir = os.getcwd() if not os.path.exists(os.path.join(path, 'PythonOuput')): os.makedirs(os.path.join(path, 'PythonOuput')) # First : write the Python solver module fileName = os.path.join(path, 'PythonOuput', self._Name + '.py') try: self.file = open(fileName, 'w') except: loggingCritical( 'ERROR while creating the Python output file. Skipping.') return self.file.write(self.string) self.file.close() # Then, create the file containing the expression of the beta-functions fileName = os.path.join(path, 'PythonOuput', 'RGEs.py') try: self.file = open(fileName, 'w') self.file.write(self.RGEfileString()) except: loggingCritical( 'ERROR while creating the Python RGE file. Skipping.') return self.file.close() # Finally create and write the run.py file os.chdir(os.path.join(path, 'PythonOuput')) self.runString(self.model, os.path.join(path, 'PythonOuput')) os.chdir(tmpDir) fileName = os.path.join(path, 'PythonOuput', 'run.py') try: self.file = open(fileName, 'w') self.file.write(self.stringRun) except: loggingCritical( 'ERROR while creating the Python run file. Skipping.') return self.file.close()
def write(self, path): self.storePath = path if not os.path.exists(os.path.join(path, 'PythonOutput')): os.makedirs(os.path.join(path, 'PythonOutput')) # First : write the C++ solver file fileName = os.path.join(path, 'PythonOutput', self._Name + '.cpp') try: self.file = open(fileName, 'w') except: loggingCritical( 'ERROR while creating the C++ output file. Skipping.') return self.file.write(self.allStr) self.file.close() # Then, create the makefile fileName = os.path.join(path, 'PythonOutput', 'Makefile') try: self.file = open(fileName, 'w') self.file.write(self.makeFileString()) except: loggingCritical('ERROR while creating the Makefile. Skipping.') return self.file.close() # Optional: write the light Cpp output if self.lightSolver: fileName = os.path.join(self.lightSolver, 'RGEsolver.cpp') try: self.file = open(fileName, 'w') except: loggingCritical( 'ERROR while creating the light C++ output file. Skipping.' ) return self.file.write(self.lallStr) self.file.close()
def RGEs(self, model, anomalous): sec = '1.' if not anomalous else '1.A)' s = f"(* ::Subsection:: *)\n(*{sec} RGEs*)\n\n" if self.inconsistentRGset: s += """(* ::Text:: *) (*< WARNING : > The RGE set is inconsistent. Please refer to the .tex output to solve this.*)\n\n""" exponent = mathematica(model.betaExponent(Symbol('i'))) s += f"""(* \\[Beta]-function definition *) \\[Beta][x_] := {mathematica(1/self.betaFactor)}*Sum[If[With[{{j=i}}, ValueQ[\\[Beta][x, j]]] && (!ValueQ[loops[x]] || i <= loops[x]), 1/(4 Pi)^({exponent})*\[Beta][x,i], 0], {{i,{max(model.loopDic.values())}}}]*Log[10];\n""" substitutedCouplings = [str(k) for subDic in model.substitutions.values() for k in subDic] for cType in model.toCalculate: if 'Anomalous' in cType: continue cDic = {} for k,v in model.allCouplings.items(): if v[0] == cType and k not in substitutedCouplings: # The conjugated couplings are removed, and must be replaced by Conjugate[ ... ] if not k[-2:] == '^*' and not k[-4:] == '^{*}' and not k[-4:] == 'star': cDic[k] = mathematica(v[1]) else: candidates = [el for el in model.allCouplings if el in k and el != k] if len(candidates) == 1: self.conjugatedCouplings[k] = candidates[0] else: lengths = [len(el) for el in candidates] i, maxLen = lengths.index(max(lengths)), max(lengths) lengths.remove(maxLen) if maxLen not in lengths: self.conjugatedCouplings[k] = candidates[i] else: loggingCritical(f"Warning in Mathematica export: could not determine the conjugate quantity of {k} automatically." + "\n -> The user will have to modify the output Mathematica file manually.") s += f"\n\n(* {self.translation[cType]} *)\n\n{self.cListNames[cType]} = " s += '{' + ', '.join([c for c in cDic.values()]) + '};\n' if model.allRGEs[cType] == {}: continue if cType == 'Vevs': if model.gaugeFixing is None: s += "\n(* For vevs the gauge must be fixed. Let's use for instance the Landau gauge : *)\n" s += "xiGauge = 0;\n" else: s += "\n(* Gauge fixing *)\n" s += f"xiGauge = {mathematica(model.gaugeFixing)};\n" self.allCouplings[cType] = [] for nLoop, RGEdic in model.couplingRGEs[cType].items(): RGEdic = {v:RGEdic[k] for k,v in cDic.items() if k in RGEdic} for k, RGE in RGEdic.items(): s += '\n\\[Beta][' + k + f', {nLoop+1}] = ' betaFunc = mathematica(RGE, k in self.couplingStructure) # Yukawa singlet workaround : take the trace of the beta-function if needed if k in self.couplingStructure and self.couplingStructure[k] == (1,1) and 'Dot[' in betaFunc: s += 'Tr[' + betaFunc + '];' else: s += betaFunc + ';' if k not in self.allCouplings[cType]: self.allCouplings[cType].append(k) s += '\n' self.string += s
def readModelFile(self, RunSettings): if RunSettings['Model'] == '': loggingCritical( "Error : Please, specify a .model file (using '-m' argument).") exit() else: if os.path.abspath(RunSettings['Model']) != RunSettings['Model']: RunSettings['Model'] = os.path.abspath( os.path.join(self.wd, RunSettings['Model'])) try: # Open the Yaml file and load the settings f = open(RunSettings['Model'], 'r') RunSettings['StoreModelFile'] = f.read() f.close() fString = self.parseFile(RunSettings['StoreModelFile']) if yaml.__version__ > '5.1': yamlSettings = yaml.load(fString, Loader=yaml.FullLoader) else: yamlSettings = yaml.load(fString) except yaml.scanner.ScannerError as err: loggingCritical( f"Check the YAML file {RunSettings['Model']}, impossible to load the settings:\n\n-> {err}." ) exit() except yaml.parser.ParserError as err: loggingCritical( f"Check the YAML file {RunSettings['Model']}, impossible to parse the settings:\n\n->{err}." ) exit() except IOError as errstr: loggingCritical( f"Did not find the YAML file {RunSettings['Model']}, specify the path if not in the current directory.\n\n-> {errstr}." ) exit() loggingInfo(f"Loading the YAML file: {RunSettings['Model']} ...", end=' ') # Now we want to process the settings before creating the model class # Let's first construct the dictionaries if the input is given as a list if 'Fermions' in yamlSettings and yamlSettings['Fermions'] != {}: for k, v in yamlSettings['Fermions'].items(): if type(v) == dict: continue elif type(v) == list: if len(v) == len(yamlSettings['Groups']) + 1: qnb = { grp: Q for (grp, Q) in zip(yamlSettings['Groups'], v[1:]) } yamlSettings['Fermions'][k] = { 'Gen': v[0], 'Qnb': qnb } else: loggingCritical( f"Error : The length of the lists describing fermions should be 1 + {len(yamlSettings['Groups'])}, " + f"corresponding to generation + various quantum numbers. ('{k} : {v}')" ) exit() else: loggingCritical( f"Error : Fermions should either be described by a dictionary or a list. ('{k} : {v}')" ) exit() if 'RealScalars' in yamlSettings and yamlSettings[ 'RealScalars'] != {}: for k, v in yamlSettings['RealScalars'].items(): if type(v) == dict: if len(v) == 1 and 'Qnb' in v: yamlSettings['RealScalars'][k] = v['Qnb'] elif type(v) == list: if len(v) == len(yamlSettings['Groups']): qnb = { grp: Q for (grp, Q) in zip(yamlSettings['Groups'], v) } yamlSettings['RealScalars'][k] = qnb else: loggingCritical( f"Error : The length of the lists describing real scalars should be {len(yamlSettings['Groups'])}, " + f"corresponding to the various quantum numbers. ('{k} : {v}')" ) exit() else: loggingCritical( f"Error : Real scalars should either be described by a dictionary or a list. ('{k} : {v}')" ) exit() # For complex scalars, also check that the pairs [Pi, Sigma] are only used once if 'CplxScalars' in yamlSettings and 'ComplexScalars' not in yamlSettings: yamlSettings['ComplexScalars'] = yamlSettings['CplxScalars'] if 'ComplexScalars' in yamlSettings and yamlSettings[ 'ComplexScalars'] != {}: realFieldsDic = {} for k, v in yamlSettings['ComplexScalars'].items(): if type(v) == dict: pass elif type(v) == list: if len(v) == len(yamlSettings['Groups']) + 3: qnb = { grp: Q for (grp, Q) in zip(yamlSettings['Groups'], v[3:]) } yamlSettings['ComplexScalars'][k] = { 'RealFields': [v[0], v[1]], 'Norm': v[2], 'Qnb': qnb } else: loggingCritical( f"Error : The length of the lists describing complex scalars should be 3 + {len(yamlSettings['Groups'])}, " + f"corresponding to Re + Im + norm + various quantum numbers. ('{k} : {v}')" ) exit() else: loggingCritical( f"Error : Complex scalars should either be described by a dictionary or a list. ('{k} : {v}')" ) exit() rf = tuple(yamlSettings['ComplexScalars'][k]['RealFields']) if rf not in realFieldsDic: realFieldsDic[rf] = k else: loggingCritical( f"Error in complex scalar '{k}' : the real fields couple {rf} is already used in '{realFieldsDic[rf]}'" ) exit() if 'Potential' in yamlSettings and yamlSettings['Potential'] != {}: labels = ('QuarticTerms', 'Yukawas', 'TrilinearTerms', 'ScalarMasses', 'FermionMasses') for cType in labels: if cType in yamlSettings['Potential'] and yamlSettings[ 'Potential'][cType] != {}: for coupling, term in yamlSettings['Potential'][ cType].items(): if type(term) == str: # This is an explicit math expression pass elif type(term) == dict: # This is a dict with no values, containing : # { 'mathExpression', assumption1, assumption2, ... } tup = list(term.keys()) if len(tup) > 1: tup = tuple([tup[0]] + [el.lower() for el in tup[1:]]) else: tup = tup[0] yamlSettings['Potential'][cType][ coupling] = tup else: loggingCritical( f"Could not understand the term : {term}") exit() loggingInfo("Done.") return yamlSettings
def validateSettings(self, settings, runSettings): """Implements the different checks carried out on the input provided by the user""" ######################################## # Check the gauge groups and particles # ######################################## if not 'Groups' in settings: loggingCritical("Error : No gauge groups specified. Exiting.") exit() else: groups = settings['Groups'].keys() allParticles = {} if 'Fermions' in settings and settings['Fermions'] != {}: for k, v in settings['Fermions'].items(): if k not in allParticles: allParticles[k] = v else: loggingCritical( f"Error : Particle '{k}' cannot be defined twice. Please check the model file." ) exit() if 'RealScalars' in settings: for k, v in settings['RealScalars'].items(): if k not in allParticles: allParticles[k] = v else: loggingCritical( f"Error : Particle '{k}' cannot be defined twice. Please check the model file." ) exit() if 'ComplexScalars' in settings: for k, v in settings['ComplexScalars'].items(): twice = [] for f in v['RealFields']: if '*' in f or '+' in f or '-' in f: loggingCritical( f"Error : Invalid field name '{f}' in RealScalars of particle '{k}'. Exiting" ) exit() if f in allParticles: twice.append(f) else: allParticles[f] = None if k in allParticles: twice.append(k) if twice != []: for el in twice: loggingCritical( f"Error : Particle '{el}' cannot be defined twice. Please check the model file." ) exit() allParticles[k] = v # Check that all the gauge groups are defined above for part, val in allParticles.items(): if val is None: continue if 'Qnb' in val: tags = val['Qnb'].keys() else: tags = val.keys() if not all([el in groups for el in tags]): loggingCritical( f"Error : the particle '{part}' is charged under an unknown gauge group." ) exit() if not 'Potential' in settings: settings['Potential'] = {} self.saveSettings['Potential'] = {} ################ # RUN settings # ################ if 'Loops' in runSettings: maxLoops = { 'GaugeCouplings': 3, 'Yukawas': 2, 'QuarticTerms': 2, 'TrilinearTerms': 2, 'ScalarMasses': 2, 'FermionMasses': 2, 'Vevs': 2 } if type(runSettings['Loops'] ) == str and runSettings['Loops'].lower() == 'max': loops = 'max' else: try: loops = eval(str(runSettings['Loops'])) except: loops = str(runSettings['Loops']) if type(loops) == int: self.nLoops = 7 * [loops] self.loopDic['GaugeCouplings'] = self.nLoops[0] self.loopDic['Yukawas'] = self.nLoops[1] self.loopDic['QuarticTerms'] = self.nLoops[2] self.loopDic['TrilinearTerms'] = self.nLoops[3] self.loopDic['ScalarMasses'] = self.nLoops[4] self.loopDic['FermionMasses'] = self.nLoops[5] self.loopDic['Vevs'] = self.nLoops[6] elif type(loops) == list and len(loops) == 3: self.nLoops = loops self.loopDic['GaugeCouplings'] = self.nLoops[0] self.loopDic['Yukawas'] = self.nLoops[1] self.loopDic['QuarticTerms'] = self.nLoops[2] self.loopDic['FermionMasses'] = self.loopDic['Yukawas'] self.loopDic['TrilinearTerms'] = self.loopDic['QuarticTerms'] self.loopDic['ScalarMasses'] = self.loopDic['QuarticTerms'] self.loopDic['Vevs'] = self.loopDic['QuarticTerms'] # elif type(loops) == list and len(loops) == 6: # self.nLoops = loops # self.loopDic['GaugeCouplings'] = self.nLoops[0] # self.loopDic['Yukawas'] = self.nLoops[1] # self.loopDic['QuarticTerms'] = self.nLoops[2] # self.loopDic['TrilinearTerms'] = self.nLoops[3] # self.loopDic['ScalarMasses'] = self.nLoops[4] # self.loopDic['FermionMasses'] = self.nLoops[5] # self.loopDic['Vevs'] = self.loopDic['QuarticTerms'] # elif type(loops) == list and len(loops) == 7: # self.nLoops = loops # self.loopDic['GaugeCouplings'] = self.nLoops[0] # self.loopDic['Yukawas'] = self.nLoops[1] # self.loopDic['QuarticTerms'] = self.nLoops[2] # self.loopDic['TrilinearTerms'] = self.nLoops[3] # self.loopDic['ScalarMasses'] = self.nLoops[4] # self.loopDic['FermionMasses'] = self.nLoops[5] # self.loopDic['Vevs'] = self.nLoops[6] elif type(loops) == str and loops == 'max': self.nLoops = [] for k, v in maxLoops.items(): self.nLoops.append(v) self.loopDic[k] = v else: loggingCritical( "Error : Loops should be in one of the following forms :\n" + "\t- A single integer\n" + # "\t- A list of three, six or seven integers\n" + "\t- A list of three integers\n" + "\t- The keyword 'max'") exit() # Nothing to calculate ? if all([el == 0 for el in self.nLoops]): loggingCritical("Nothing to calculate ! Exiting.") exit() # If loop orders are too high, set them to the max allowed value for k, v in maxLoops.items(): if self.loopDic[k] > v: loggingInfo( f"Warning : Loop level for '{k}' is too high ({self.loopDic[k]}). Setting it to {v}" ) self.loopDic[k] = v # Anomalous self.loopDic['ScalarAnomalous'] = self.loopDic['QuarticTerms'] self.loopDic['FermionAnomalous'] = self.loopDic['Yukawas']
def exports(runSettings, model): loggingInfo("Exporting results...") tmpWD = os.getcwd() # Create a folder with the name of the model if runSettings['CreateFolder'] is True: path = os.path.join(runSettings['Results'], model._Name) if not (os.path.exists(path)): os.makedirs(path) else: path = runSettings['Results'] if runSettings['LatexOutput'] is True: from Latex import LatexExport loggingInfo("\tExporting to Latex...", end=' ') latex = LatexExport(model) latex.write(os.path.join(path, model._Name + '.tex')) loggingInfo("Done.") if runSettings['MathematicaOutput'] is True: from Mathematica import MathematicaExport loggingInfo("\tExporting to Mathematica...", end=' ') mathematica = MathematicaExport(model) mathematica.write(os.path.join(path, model._Name + '.m')) loggingInfo("Done.") if runSettings['PythonOutput'] is True: from Python import PythonExport # If Latex export is disabled, create a Latex object anyway # to get the latex substitutions if runSettings['LatexOutput'] is False: from Latex import LatexExport latex = LatexExport(model, getLatexSubs=True) loggingInfo("\tExporting to Python...", end=' ') try: python = PythonExport(model, latexSubs=latex.latex) python.write(path) except TypeError as e: print('\nError : ' + str(e)) else: loggingInfo("Done.") if runSettings['UFOfolder'] is not None: from UFO import UFOExport loggingInfo("\tExporting to UFO...", end=' ') try: ufo = UFOExport(model) ufo.write(runSettings['UFOfolder']) except TypeError as e: loggingCritical("An error occurred during the UFO export : \n" + str(e)) else: loggingInfo("Done.") # Copy the .model file in the results folder if runSettings['CopyModelFile']: fName = os.path.join(path, os.path.basename(runSettings['Model'])) s = "# This model file was automatically copied by PyR@TE 3 on " + time.ctime() + "\n" s += runSettings['StoreModelFile'] try: f = open(fName, 'w') f.write(s) f.close() except: loggingCritical("Error while copying the model file in the results folder.") # Now apply possible user-defined commands from 'default.settings' commands = [cmd.strip() for cmd in runSettings['EndCommands'].replace('[name]', model._Name).split(',')] loggingInfo("Running user-defined commands : ") os.chdir(path) shell = (sys.platform.startswith('win')) for cmd in commands: loggingInfo("\t-> '" + cmd + "'") try: run(cmd.split(' '), shell=shell, stdout=DEVNULL, stderr=STDOUT, check=True) except CalledProcessError as e: loggingCritical("An error occurred when running the command. Skipping.") loggingCritical(' >> ' + str(e)) os.chdir(tmpWD) # This is for debugging, remove later model.latex = latex # model.python = python
def __init__(self, settings, runSettings, idb, realBasis='all'): ############### # Definitions # ############### self._Name = settings['Name'].replace(' ', '_').replace('/', '_').replace( '\\', '_') self._Author = settings['Author'] self._Date = settings['Date'] self.times = runSettings['PrintComputationTimes'] self.saveSettings = copy.deepcopy(settings) self.runSettings = runSettings # Declare an interactive db access object self.idb = idb self.loopDic = {} self.validateSettings(settings, runSettings) loggingInfo("Loading the model ...", end=' ') #################### # Get gauge groups # #################### self.gaugeGroups = {} self.gaugeGroupsList = [] self.UgaugeGroups = [] self.realBasis = runSettings['RealBasis'] self.getGaugegroups(settings, realBasis=self.realBasis) # Number of U1 gauge factors self.nU = [g.abelian for g in self.gaugeGroupsList].count(True) self.kinMix = (self.nU > 1 and runSettings['NoKinMix'] is False) ################# # Get particles # ################# self.Particles = {} self.Fermions = {} self.Scalars = {} self.ComplexScalars = {} #The following dicts contain all the components of the fields self.allFermions = {} self.allScalars = {} self.symbolicGen = False self.getParticles(settings) ###################### # Read the potential # ###################### self.potential = settings['Potential'] self.assumptions = {} self.getAssumptions() #Read the vevs + possible gauge fixing self.vevs = {} self.getVevs(settings) self.gaugeFixing = None if 'GaugeParameter' in settings: self.gaugeFixing = self.parseMathExpr(settings['GaugeParameter'], real=True) #Read the anomalous dimensions self.scalarAnomalous = {} self.fermionAnomalous = {} self.getAnomalous(settings) #Identify the various couplings of the model self.allCouplings = {} self.couplingsPos = {'GaugeCouplings': {}} self.YukPos = {} self.ExplicitMatrices = [] self.couplingStructure = {} self.gaugeCouplings = [] for i, gp in enumerate(self.gaugeGroupsList): self.allCouplings[str(gp.g)] = ('GaugeCouplings', gp.g) self.couplingsPos['GaugeCouplings'][str(gp.g)] = i self.gaugeCouplings.append(str(gp.g)) self.mixedGaugeCouplings = [] self.upper = True if self.kinMix: self.kinMat = zeros(self.nU) for i in range(self.nU): for j in range(self.nU): if (self.upper and i < j) or (not self.upper and i > j): c = 'g_' + str(i + 1) + str(j + 1) pos = [ self.gaugeGroupsList.index(self.UgaugeGroups[k]) for k in (i, j) ] self.allCouplings[c] = ('GaugeCouplings', Symbol(c, real=True)) self.couplingsPos['GaugeCouplings'][c] = max(pos) + .5 self.kinMat[i, j] = Symbol(c, real=True) self.mixedGaugeCouplings.append(self.kinMat[i, j]) self.gaugeCouplings.append(c) elif i == j: self.kinMat[i, j] = self.UgaugeGroups[i].g self.kinMat2 = self.kinMat * self.kinMat.transpose() # Fill the dics related to the various couplings of the model self.nCouplings = 0 for couplingType, terms in self.potential.items(): if couplingType == 'Definitions': continue tKeys = list(terms.keys()) for coupling in terms: self.nCouplings += 1 # Add the various couplings to the Couplings dictionnary if couplingType == 'Yukawas': self.YukPos[coupling] = tKeys.index(coupling) if couplingType == 'FermionMasses': self.YukPos[coupling] = 100 + tKeys.index(coupling) if couplingType not in self.couplingsPos: self.couplingsPos[couplingType] = {} self.couplingsPos[couplingType][coupling] = tKeys.index( coupling) self.allCouplings[coupling] = couplingType if self.vevs != {}: self.couplingsPos['Vevs'] = {} for i, (k, v) in enumerate(self.vevs.items()): self.allCouplings[k] = ('Vevs', v[1]) self.couplingsPos['Vevs'][k] = i # Read the beta-factor if 'BetaFactor' in settings: if type(settings['BetaFactor']) not in (list, tuple): self.betaFactor = self.parseMathExpr(settings['BetaFactor']) self.betaExponent = lambda n: 2 * n else: self.betaFactor = self.parseMathExpr(settings['BetaFactor'][0]) self.betaExponent = self.parseMathExpr( settings['BetaFactor'][1]) if self.betaExponent.find('n') == set(): if self.betaExponent == 0: self.betaExponent = lambda n: 0 else: loggingCritical( "Error : the beta-exponent must be an integer function of 'n'. Setting it to default (2*n)." ) self.betaExponent = lambda n: 2 * n else: lambdaExponent = lambdify(Symbol('n'), self.betaExponent) self.betaExponent = lambda n: lambdaExponent(n) if self.betaFactor == 0: loggingCritical("Error : beta-factor cannot be 0. Exiting.") exit() else: self.betaFactor = Integer(1) self.betaExponent = lambda n: Integer(2) * n self.translateContent = { 'GaugeCouplings': (0, 0), 'Yukawas': (2, 1), 'QuarticTerms': (0, 4), 'TrilinearTerms': (0, 3), 'ScalarMasses': (0, 2), 'FermionMasses': (2, 0), 'FermionAnomalous': (2, 0), 'ScalarAnomalous': (0, 2), 'Vevs': (0, 1) } self.translateDic = lambda RGmodule: { 'Yukawas': RGmodule.YDic, 'QuarticTerms': RGmodule.LambdaDic, 'TrilinearTerms': RGmodule.Hdic, 'ScalarMasses': RGmodule.MSdic, 'FermionMasses': RGmodule.MFdic, 'FermionAnomalous': RGmodule.gammaFdic, 'ScalarAnomalous': RGmodule.gammaSdic, 'Vevs': RGmodule.Vdic } self.translateBetaFunction = { 'GaugeCouplings': GaugeBetaFunction, 'Yukawas': YukawaBetaFunction, 'QuarticTerms': QuarticBetaFunction, 'TrilinearTerms': TrilinearBetaFunction, 'ScalarMasses': ScalarMassBetaFunction, 'FermionMasses': FermionMassBetaFunction, 'FermionAnomalous': FermionAnomalous, 'ScalarAnomalous': ScalarAnomalous, 'Vevs': VevBetaFunction } self.lagrangianMapping = {} self.toCalculate = {} self.RGclasses = {} self.allRGEs = {} self.couplingRGEs = {} self.NonZeroCouplingRGEs = {} self.NonZeroDiagRGEs = {}
def fillTensorDic(self, coupling, expTerm, content): subTerms = expTerm.as_coeff_add()[1] tensorInds = () coeff = 0 fermions = () scalars = () coupling = Symbol(coupling, complex=True) cType = self.model.allCouplings[str(coupling)] if type(cType) == tuple: cType = cType[0] for subTerm in subTerms: subTerm = list(subTerm.as_coeff_mul()) rationalFactors = [el for el in subTerm[1] if el.is_number] subTerm[1] = tuple( [el for el in subTerm[1] if el not in rationalFactors]) subTerm[0] *= Mul(*rationalFactors) subTerm[1] = splitPow(subTerm[1]) #For fermions, we have to be careful that the order in which the user wrote the terms # is preserved here. Inverting them would transpose the Yukawa / mass matrix fermions = [ self.model.allFermions[str(el)] for el in subTerm[1] if str(el) in self.model.allFermions ] if fermions != []: # Sort fermions according to their pos in the potential term fermionSortKey = {} fermionNames = sorted([str(el[1]) for el in fermions], key=lambda x: len(x), reverse=True) potStr = self.currentPotentialDic[str(coupling)] for el in fermionNames: fermionSortKey[el] = potStr.find(el) potStr = potStr.replace(el, ' ' * len(el)) fermions = sorted(fermions, key=lambda x: fermionSortKey[str(x[1])]) fGen = [f[1].gen for f in fermions] fermions = [f[0] for f in fermions] scalars = [ self.model.allScalars[str(el)][0] for el in subTerm[1] if str(el) in self.model.allScalars ] # sGen = [self.model.allScalars[str(el)][1].gen for el in subTerm[1] if str(el) in self.model.allScalars] if content == (2, 1): #Yukawa if len(fermions) != 2 or len(scalars) != 1: loggingCritical( f"Error in term {str(coupling)} : \n\tYukawa terms must contain exactly 2 fermions and 1 scalar." ) exit() tensorInds = tuple(scalars + fermions) coeff = subTerm[0] * 2 / len( set(itertools.permutations(fermions, 2))) # # Fermion1 = Fermion2 : the matrix is symmetric # if self.allFermionsValues[fermions[0]][1] == self.allFermionsValues[fermions[1]][1]: # self.model.assumptions[str(coupling)]['symmetric'] = True # # Fermion1 = Fermion2bar : the matrix is hermitian # if self.allFermionsValues[fermions[0]][1] == self.allFermionsValues[self.antiFermionPos(fermions[1])][1]: # self.model.assumptions[str(coupling)]['hermitian'] = True assumptionDic = self.model.assumptions[str(coupling)] coupling = mSymbol(str(coupling), fGen[0], fGen[1], **assumptionDic) if coupling not in self.model.couplingStructure: self.model.couplingStructure[str(coupling)] = (fGen[0], fGen[1]) if content == (0, 4): #Quartic if len(fermions) != 0 or len(scalars) != 4: loggingCritical( f"\nError in term {str(coupling)} : \n\tQuartic terms must contain exactly 0 fermion and 4 scalars." ) exit() tensorInds = tuple(sorted(scalars)) coeff = subTerm[0] * 24 / len( set(itertools.permutations( tensorInds, 4))) #/len(set(itertools.permutations(tensorInds, 4))) if content == (0, 3): #Trilinear if len(fermions) != 0 or len(scalars) != 3: loggingCritical( f"\nError in term {str(coupling)} : \n\tTrinilear terms must contain exactly 0 fermion and 3 scalars." ) exit() tensorInds = tuple(sorted(scalars)) coeff = subTerm[0] * 6 / len( set(itertools.permutations(tensorInds, 3))) if content == (0, 2): #Scalar Mass if len(fermions) != 0 or len(scalars) != 2: loggingCritical( f"\nError in term {str(coupling)} : \n\tScalar mass terms must contain exactly 0 fermion and 2 scalars." ) exit() tensorInds = tuple(sorted(scalars)) coeff = subTerm[0] * 2 / len( set(itertools.permutations(tensorInds, 2))) if content == (2, 0): #Fermion Mass if len(fermions) != 2 or len(scalars) != 0: loggingCritical( f"\nError in term {str(coupling)} : \n\tFermion mass terms must contain exactly 2 fermions and 0 scalar." ) exit() tensorInds = tuple(fermions) coeff = subTerm[0] * 2 / len( set(itertools.permutations(tensorInds, 2))) # # Fermion1 = Fermion2 : the matrix is symmetric # if fermions[0] == fermions[1]: # self.model.assumptions[str(coupling)]['symmetric'] = True # # Fermion1 = Fermion2bar : the matrix is hermitian # if fermions[0] == self.antiFermionPos(fermions[1]): # self.model.assumptions[str(coupling)]['hermitian'] = True assumptionDic = self.model.assumptions[str(coupling)] coupling = mSymbol(str(coupling), fGen[0], fGen[1], **assumptionDic) if coupling not in self.model.couplingStructure: self.model.couplingStructure[str(coupling)] = (fGen[0], fGen[1]) if tensorInds not in self.dicToFill: self.dicToFill[tensorInds] = coupling * coeff else: self.dicToFill[tensorInds] += coupling * coeff #Update the 'AllCouplings' dictionary if type(self.model.allCouplings[str(coupling)]) != tuple: tmp = [cType, coupling] if isinstance(coupling, mSymbol): orderedFermions = [ str(list(self.model.allFermions.values())[f][1]) for f in fermions ] tmp.append(tuple(orderedFermions)) self.model.allCouplings[str(coupling)] = tuple(tmp) # If Yukawa / Fermion mass, add the hermitian conjugate to Dic if content == (2, 1) or content == (2, 0): antiFermions = [self.antiFermionPos(f) for f in fermions] antiFermions.reverse() tensorInds = tuple(scalars + antiFermions + [True]) coeff = conjugate(coeff) adjCoupling = Adjoint(coupling).doit() if tensorInds not in self.dicToFill: self.dicToFill[tensorInds] = adjCoupling * coeff else: self.dicToFill[tensorInds] += adjCoupling * coeff
def parseExpression(self, expr, name=None, expandedTerm=None): """ This function handles the convertion from str to TensorObject of lagrangian expressions written by the user in the model file. As much as possible, the user input is validated and error messages are printed if needed.""" originalExpr = expr errorExpr = (name + ' : ' if name is not None else '') + str(originalExpr) ########## # Case 1 : expr is a representation matrix ########## if expr[:2].lower() == 't(': args = expr[2:-1] gp = args.split(',')[0] if gp + ',' not in args: loggingCritical( f"\nError : representation matrix {expr} should have exactly two arguments : group and rep" ) exit() rep = eval(args.replace(gp + ',', '')) if gp in self.model.gaugeGroups: gp = self.model.gaugeGroups[gp] else: for gName, g in self.model.gaugeGroups.items(): if g.type == gp: gp = g break if type(gp) == str: loggingCritical( f"\nError in 'Definitions': gauge group '{gp}' is unknown." ) exit() # DimR -> Dynkin labels if isinstance(rep, int): rep = self.idb.get(gp.type, 'dynkinLabels', rep) repMats = gp.repMat(tuple(rep)) shape = tuple([len(repMats), *repMats[0].shape]) dic = {} for i, mat in enumerate(repMats): for k, v in mat._smat.items(): dic[(i, *k)] = v # This is for latex output expr = Function('t')(Symbol(gp.type), Symbol(str(rep))) return TensorObject(copy=(name, shape, dic), fromDef=name, expr=expr) ########## # Case 2 : expr is a CGC ########## if expr[:4].lower() == 'cgc(': args = expr[4:-1].split(',') # Read the gauge group gp = args[0] args = args[1:] # Collect lists together i = 0 while i < len(args): o = args[i].count('[') + args[i].count('(') + args[i].count( '{') c = args[i].count(']') + args[i].count(')') + args[i].count( '}') if o > c: args[i] = args[i] + ', ' + args[i + 1] args.pop(i + 1) else: i += 1 # Read the fields fields = [] for i, el in enumerate(args): if el.isnumeric() or ('(' in el and ')' in el): # Stop after encountering an int or a tuple i -= 1 break fields.append(el) args = args[i + 1:] # Determine which syntax was used if gp in self.model.gaugeGroups and all( [el in self.model.Particles for el in fields]): fieldNames = fields elif gp in [gp.type for gp in self.model.gaugeGroupsList] and all( [el not in self.model.Particles for el in fields]): fieldNames = [] else: loggingCritical( "\nError : CGC syntax is 'cgc(groupName, field1, field2 [, field3 [, field4, [CGC number]]])' or " + "cgc(group, dynkins1, dynkins2 [, dynkins3 [, dynkins4, [CGC number]]]). The group and particles must be defined above." ) loggingCritical( f"Please rewrite the term '{name}: {expr}' accordingly.") exit() N = 0 # The CGC call contains a pos if args != []: if len(args) == 1: N = int(args[0]) - 1 else: loggingCritical( f"\nError in {name}: {expr} ; too much arguments to cgc() function." ) exit() if N < 0: loggingCritical( f"\nError in {name}: {expr} ; the position argument must be a non-zero positive integer." ) exit() if not isinstance(N, int): loggingCritical( f"\nError in CGC '{name}: {expr}' : position argument must be an integer." ) exit() if fieldNames != []: gpName, gType = gp, self.model.gaugeGroups[gp].type reps = [self.model.Particles[p].Qnb[gpName] for p in fields] else: gType, reps = gp, [eval(labels) for labels in fields] cgc = self.idb.get(gType, 'invariants', reps, pyrateNormalization=True, realBasis=GaugeGroup.realBasis) if len(cgc) == 0: loggingCritical( f"Error: no invariant can be formed from the reps provided in '{name}'." ) exit() if N > len(cgc) - 1: loggingCritical( f"\nError in {name}: {expr} ; the position argument cannot be larger than {len(cgc)} here." ) exit() result = cgc[N] shape = result.dim[:result.rank] dic = {} for k, v in result.dic.items(): dic[k[:result.rank]] = v # This is for latex output expr = Function('cgc')(Symbol(gType), *([Symbol(el) for el in fields] + [N])) return TensorObject(copy=(name, shape, dic), fromDef=name, expr=expr, fields=fieldNames) ########## # Case 3 : an expression involving the already defined quantities ########## localDict = {} count = 0 expr = expr.replace('sqrt', '#').replace('Sqrt', '#') for k, v in sorted(self.definitions.items(), key=lambda x: -len(x[0])): expr = expr.replace(k, f'@_{count}_') localDict[f'symb_{count}_'] = v.symbol count += 1 expr = expr.replace('@', 'symb') expr = expr.replace('#', 'sqrt') def sympyParse(expr): if '^' in expr: loggingCritical( f"\nError in expression '{errorExpr}' : powers must be written using the '**' operator" ) exit() return parse_expr(expr, local_dict=localDict, transformations=standard_transformations[1:] + (implicit_multiplication, ), evaluate=False) # A) Replacements to format the expr string expr = expr.replace(']', '] ').strip() expr = expr.replace(' +', '+').replace(' -', '-').replace(' *', '*').replace(' /', '/') expr = expr.replace(' )', ')') expr = expr.replace('] ', ']*') for k, v in localDict.items(): if isinstance(v, Symbol): expr = expr.replace(k, k + ' ') # B) Parse the string try: expr = sympyParse(expr) except: loggingCritical(f"\nError while parsing the term '{errorExpr}'.") exit() rep = {} if expr.find(Pow) != set(): # Now we want to expand the term, keeping any form (a*b)**2 unexpanded a, b, c = [Wild(x, exclude=(1, )) for x in 'abc'] rep = expr.replace((a * b)**c, lambda a, b, c: (a * b)**Symbol('n_' + str(c)), map=True) if rep == {} or rep[1] == {}: termList = expand(expr).as_coeff_add()[1] else: termList = expand(rep[0], power_base=False).as_coeff_add()[1] # C) Parse the left hand side of the definition (if given) Linds = [] if name is not None: if '[' in name and ']' in name: Lbase = name[:name.find('[')] Linds = name[name.find('[') + 1:name.find(']')].split(',') Linds = [Symbol(el) for el in Linds] # D) Validate and compute the expression rhsResult = 0 commonFreeInds = None for term in termList: split = splitPow(term) rationalFactors = [el for el in split if el.is_number] terms = tuple([el for el in split if el not in rationalFactors]) coeff = Mul(*rationalFactors) # Handle expr**N now newTerms = [] for subTerm in terms: if isinstance(subTerm, Pow): base, exp = subTerm.base, subTerm.exp if isinstance(exp, Symbol): exp = int(exp.name[2:]) indexed = base.find(Indexed) if indexed != set(): indices = flatten([el.indices for el in indexed]) indCopies = {} for i in indices: if i not in indCopies: indCopies[i] = [ Symbol(str(i) + f'_{p}') for p in range(1, exp) ] else: indCopies = {} newTerms.append(base) for p in range(0, exp - 1): sub = {i: copy[p] for i, copy in indCopies.items()} newTerms.append(base.subs(sub)) else: newTerms.append(subTerm) terms = [] for subTerm in newTerms: if isinstance(subTerm, Mul) or isinstance(subTerm, Pow): tmp = splitPow(subTerm) for el in tmp: if not el.is_number: terms.append(el) else: coeff *= el else: if not subTerm.is_number: terms.append(subTerm) else: coeff *= subTerm if expandedTerm is not None: if expandedTerm == []: expandedTerm.append(Mul(coeff, *terms, evaluate=False)) else: expandedTerm[0] += Mul(coeff, *terms, evaluate=False) inds = [] indRanges = {} for i, field in enumerate(terms): if isinstance(field, Symbol): continue try: fieldInds = field.indices except AttributeError: loggingCritical( f"\nError (in term '{expr}') while reading the quantity '{field}'. It seems that indices are missing." ) exit() fieldDef = self.definitions[str(field.base)] if fieldDef.dim is not None and len(fieldInds) != fieldDef.dim: loggingCritical( f"\nError (in term '{expr}'): the quantity {field.base} should carry exactly {fieldDef.dim} indices" ) exit() inds += list(fieldInds) for p, ind in enumerate(field.indices): indRanges[ind] = (fieldDef, p) freeInds = [] for ind in set(inds): count = inds.count(ind) if count == 1: freeInds.append(ind) if count > 2: loggingCritical( f"\nError: in term '{term}', the index '{ind}' appears more than twice." ) exit() if commonFreeInds is None: commonFreeInds = freeInds elif freeInds != commonFreeInds: loggingCritical( f"\nError : each term of the sum '{expr}' must contain the same free indices." ) exit() if name is not None and set(freeInds) != set(Linds): loggingCritical( f"\nError in term {term}: there should be {len(set(Linds))} free indices" + (' -> ' + str(tuple(set(Linds))) if set(Linds) != set() else '')) exit() # Now that the term is validated, construct the resulting tensor object contractArgs = [] for field in terms: if not isinstance(field, Symbol): base, inds = field.base, field.indices else: base, inds = field, [] tens = self.definitions[str(base)] tens.update(len(inds)) inds = [Wild(str(el)) for el in inds] contractArgs.append(tens(*inds)) freeDummies = [Wild(str(el)) for el in Linds] tmp = tensorContract(*contractArgs, value=coeff, freeDummies=freeDummies, doit=True) if not isinstance(tmp, dict): tmp = expand(tmp) if rhsResult == 0: rhsResult = tmp else: if not isinstance(rhsResult, dict): rhsResult += tmp else: for k, v in tmp.items(): v = expand(v) if k in rhsResult: rhsResult[k] += v else: rhsResult = k if rhsResult[k] == 0: del rhsResult[k] if not isinstance(rhsResult, dict): return TensorObject(copy=('' if name is None else name, (), { (): rhsResult }), fromDef=name, expr=expr) ranges = [] for freeInd in Linds: iRange = indRanges[freeInd] iRange = iRange[0].range[iRange[1]] ranges.append(iRange) try: return TensorObject(copy=(Lbase, ranges, rhsResult), fromDef=name, expr=expr) except: loggingCritical( f"\nError while parsing the term '{errorExpr}': please check the consistency of contracted indices." ) exit()
def getParticles(self, settings): def completeTrivialReps(dic): for k, v in self.gaugeGroups.items(): if k not in dic['Qnb']: if v.abelian: dic['Qnb'][k] = 0 else: dic['Qnb'][k] = 1 for key, value in settings.items(): if key == 'Fermions': self.Fermions = value antiFermions = {} # Create the particle and store it in Fermions for part, val in value.items(): completeTrivialReps(val) self.Fermions[part] = Particle(part, val, self.gaugeGroups, self.idb) antiFermions[part + 'bar'] = self.Fermions[part].antiParticle() self.Fermions.update(antiFermions) elif key == 'RealScalars': # Copy the particles in the class for part, qnb in value.items(): norm = None if 'Norm' in qnb: norm = self.parseMathExpr(qnb['Norm']) if 'Qnb' not in qnb: Qnb = {'Gen': 1, 'Qnb': qnb} else: Qnb = qnb completeTrivialReps(Qnb) self.Scalars[part] = Particle(part, Qnb, self.gaugeGroups, self.idb) # Now check that the representations are compatible with a real scalar for gName, g in self.gaugeGroups.items(): if g.abelian and self.Scalars[part].Qnb[gName] != 0: loggingCritical( f"\nError: real scalar '{part}' cannot be charged under the abelian gauge factor {gName}" ) exit() if not g.abelian: rep = self.Scalars[part].Qnb[gName] dimR = g.dimR(rep) if dimR == 1: continue fs = self.idb.get(g.type, 'frobenius', rep) if fs == 1: loggingCritical( f"\nError: real scalar '{part}' cannot transform under the complex representation '{dimR}' of {gName}" ) exit() elif fs == -1: self.Scalars[part].pseudoRealReps.append( (gName, g, rep, dimR)) if self.Scalars[part].pseudoRealReps != []: if norm is None: loggingInfo( f"Warning: self-conjugate scalar '{part}' should be given a norm. Assuming 1/sqrt(2) by default." ) norm = 1 / sqrt(2) self.Scalars[part].pseudoNorm = norm elif norm is not None: loggingInfo( f"Warning: ignoring the unnecessary 'Norm' keyword in real scalar '{part}'." ) elif key == 'Potential': self.potential = value self.Particles.update(self.Fermions) self.Particles.update(self.Scalars) if 'ComplexScalars' in settings: for part, setts in settings['ComplexScalars'].items(): setts['Norm'] = self.parseMathExpr(setts['Norm']) if 'Gen' not in setts: setts['Gen'] = 1 completeTrivialReps(setts) self.ComplexScalars[part] = ComplexScalar( part, setts, self.gaugeGroups, self.idb) self.ComplexScalars[ part + 'bar'] = self.ComplexScalars[part].antiParticle() for r in self.ComplexScalars[part].realFields: self.Scalars[str(r)] = r self.Particles.update(self.ComplexScalars) nF = 0 for fName, f in self.Fermions.items(): ranges = [r for r in f.indicesRange.values()] nonNullRanges = [r for r in ranges if r != 0] if nonNullRanges == []: #Singlet tup = [nF, f, tuple([-1] * len(ranges))] nF += 1 self.allFermions[fName] = tuple(tup) else: for el in itertools.product( *[(list(range(r)) if r != 0 else [-1]) for r in ranges]): tup = [ nF, f, tuple(el), parse_expr(str(fName) + str([n for n in el if n != -1]), local_dict={ str(f._name): IndexedBase(str(f._name)) }) ] nF += 1 self.allFermions[fName + str([n for n in el if n != -1])] = tuple(tup) nS = 0 for sName, s in self.Scalars.items(): ranges = [r for r in s.indicesRange.values()] nonNullRanges = [r for r in ranges if r != 0] if nonNullRanges == []: #Singlet self.allScalars[sName] = (nS, s, tuple([-1] * len(ranges))) nS += 1 else: storeNs = nS for el in itertools.product( *[(list(range(r)) if r != 0 else [-1]) for r in ranges]): tup = [ nS, s, tuple(el), parse_expr(str(sName) + str([n for n in el if n != -1]), local_dict={ str(s._name): IndexedBase(str(s._name)) }) ] self.allScalars[sName + str([n for n in el if n != -1])] = tuple(tup) nS += 1 # If the real scalar transforms under pseudo-real reps, some more work is needed if s.pseudoRealReps != []: s.pseudoScalarHandling(list(self.allScalars.items()), storeNs) self.symbolicGen = any( [isinstance(p.gen, Symbol) for p in self.Particles.values()])
def checkGaugeInvariance(self): loggingInfo("Checking gauge invariance ...", end=' ') t0 = time.time() # fermionGauge = tensorAdd(tensorContract(self.T(A_,i_,j_), # self.T(B_,j_,k_), # freeDummies=[A_,B_,i_,k_], # doit=True) , # tensorMul(-1, tensorContract(self.T(B_,i_,j_), # self.T(A_,j_,k_), # freeDummies=[A_,B_,i_,k_], # doit=True)) , # tensorMul(-I, tensorContract(self.f(A_,B_,C_), # self.T(C_,i_,k_), # freeDummies=[A_,B_,i_,k_], # doit=True))) # scalarGauge = tensorAdd(tensorContract(self.Ts(A_,i_,j_), # self.Ts(B_,j_,k_), # freeDummies=[A_,B_,i_,k_], # doit=True) , # tensorMul(-1, tensorContract(self.Ts(B_,i_,j_), # self.Ts(A_,j_,k_), # freeDummies=[A_,B_,i_,k_], # doit=True)) , # tensorMul(-I, tensorContract(self.f(A_,B_,C_), # self.Ts(C_,i_,k_), # freeDummies=[A_,B_,i_,k_], # doit=True))) # if fermionGauge != {}: # loggingCritical("Basic Lie algebra commutation relations are not satisfied among fermions.\n" # +"Please contact the author.") # exit() # if scalarGauge != {}: # loggingCritical("Basic Lie algebra commutation relations are not satisfied among scalars.\n" # +"Please contact the author.") # exit() yuk = tensorAdd( tensorMul( -1, tensorContract(self.Tt(A_, i_, j_), self.y(a_, j_, k_), freeDummies=[A_, a_, i_, k_], doit=True)), tensorContract(self.y(a_, i_, j_), self.T(A_, j_, k_), freeDummies=[A_, a_, i_, k_], doit=True), tensorContract(self.y(b_, i_, k_), self.Ts(A_, b_, a_), freeDummies=[A_, a_, i_, k_], doit=True)) fermionMass = tensorAdd( tensorMul( -1, tensorContract(self.Tt(A_, i_, j_), self.M(j_, k_), freeDummies=[A_, i_, k_], doit=True)), tensorContract(self.M(i_, j_), self.T(A_, j_, k_), freeDummies=[A_, i_, k_], doit=True)) quartics = tensorAdd( tensorContract(self.Ts(A_, a_, e_), self.l(e_, b_, c_, d_), freeDummies=[A_, a_, b_, c_, d_], doit=True), tensorContract(self.Ts(A_, b_, e_), self.l(a_, e_, c_, d_), freeDummies=[A_, a_, b_, c_, d_], doit=True), tensorContract(self.Ts(A_, c_, e_), self.l(a_, b_, e_, d_), freeDummies=[A_, a_, b_, c_, d_], doit=True), tensorContract(self.Ts(A_, d_, e_), self.l(a_, b_, c_, e_), freeDummies=[A_, a_, b_, c_, d_], doit=True)) trilinears = tensorAdd( tensorContract(self.Ts(A_, a_, e_), self.h(e_, b_, c_), freeDummies=[A_, a_, b_, c_], doit=True), tensorContract(self.Ts(A_, b_, e_), self.h(a_, e_, c_), freeDummies=[A_, a_, b_, c_], doit=True), tensorContract(self.Ts(A_, c_, e_), self.h(a_, b_, e_), freeDummies=[A_, a_, b_, c_], doit=True)) scalarMass = tensorAdd( tensorContract(self.Ts(A_, a_, e_), self.mu(e_, b_), freeDummies=[A_, a_, b_], doit=True), tensorContract(self.Ts(A_, b_, e_), self.mu(a_, e_), freeDummies=[A_, a_, b_], doit=True)) def which(dic): problematicCouplings = {} for k, el in dic.items(): names = [ obj for obj in el.atoms() if not obj.is_number and not (hasattr(obj, 'is_Identity') and obj.is_Identity) ] for c in names: if str(c) not in problematicCouplings: problematicCouplings[str(c)] = set() problematicCouplings[str(c)].add(k[0][0]) return "\n\t" + "\n\t".join([ str(k) + ' (' + ', '.join([ self.model.gaugeGroupsList[g].name for g in sorted(list(v)) ]) + ')' for k, v in problematicCouplings.items() ]) if yuk != {}: loggingCritical( "Gauge invariance is not satisfied by the following Yukawa couplings :" + which(yuk)) if quartics != {}: loggingCritical( "Gauge invariance is not satisfied by the following quartic couplings :" + which(quartics)) if fermionMass != {}: loggingCritical( "Gauge invariance is not satisfied by the following fermion mass couplings :" + which(fermionMass)) if trilinears != {}: loggingCritical( "Gauge invariance is not satisfied by the following trilinear couplings :" + which(trilinears)) if scalarMass != {}: loggingCritical( "Gauge invariance is not satisfied by the following scalar mass couplings :" + which(scalarMass)) if any([ el != {} for el in (yuk, quartics, fermionMass, trilinears, scalarMass) ]): exit() loggingInfo("All OK !" + ( f" ({time.time()-t0:.3f} seconds)" if self.model.times else ''))
def getAnomalous(self, settings): allAnomalous = lambda dic: (len(dic) == 1 and 'All' in dic and dic[ 'All'] is None) if 'ScalarAnomalous' in settings and settings[ 'ScalarAnomalous'] != {} and self.loopDic[ 'ScalarAnomalous'] > 0: sa = settings['ScalarAnomalous'] if not allAnomalous(sa): for k in sa: if not (k[0] == '(' and k[-1] == ')'): loggingCritical( "Warning in ScalarAnomalous : the correct syntax is '(scalar1, scalar2)'.\n\ Please check the term '" + k.replace(';', ', ') + "'. Skipping.") continue fields = k[1:-1].split(';') if len(fields) != 2: loggingCritical( "Warning in ScalarAnomalous : the correct syntax is '(scalar1, scalar2)'.\n\ Please check the term '" + k.replace(';', ', ') + "'. Skipping.") continue skip = False for i, f in enumerate(fields): if '[' not in f: continue base = f[:f.find('[')] inds = f[f.find('['):] try: inds = str([el - 1 for el in eval(inds)]) fields[i] = base + inds except: skip = "unable to read the term '" + k.replace( ';', ', ') + "'" break if fields[i] not in self.allScalars: skip = "in term '" + k.replace( ';', ', ') + "', scalar '" + f + "' is unknown" break if skip: loggingCritical("Warning in ScalarAnomalous : " + skip + ". Skipping.") continue key = [ self.allScalars[s][(3 if len(self.allScalars[s]) > 3 else 1)] for s in fields ] key = tuple([(k if isinstance(k, Indexed) else k._name) for k in key]) val = tuple([self.allScalars[s][0] for s in fields]) self.scalarAnomalous[key] = val else: # All anomalous self.saveSettings['ScalarAnomalous'] = 'All' for i, s1 in enumerate(self.allScalars.values()): for j, s2 in enumerate(self.allScalars.values()): if i > j: continue key = [s[(3 if len(s) > 3 else 1)] for s in (s1, s2)] key = tuple([(k if isinstance(k, Indexed) else k._name) for k in key]) val = tuple([s[0] for s in (s1, s2)]) self.scalarAnomalous[key] = val if 'FermionAnomalous' in settings and settings[ 'FermionAnomalous'] != {} and self.loopDic[ 'FermionAnomalous'] > 0: fa = settings['FermionAnomalous'] if not allAnomalous(fa): for k in fa: if not (k[0] == '(' and k[-1] == ')'): loggingCritical( "Warning in FermionAnomalous : the correct syntax is '(fermion1, fermion2)'.\n\ Please check the term '" + k.replace(';', ', ') + "'. Skipping.") continue fields = k[1:-1].split(';') if len(fields) != 2: loggingCritical( "Warning in FermionAnomalous : the correct syntax is '(fermion1, fermion2)'.\n\ Please check the term '" + k.replace(';', ', ') + "'. Skipping.") continue skip = False for i, f in enumerate(fields): if '[' not in f: continue base = f[:f.find('[')] inds = f[f.find('['):] try: inds = str([el - 1 for el in eval(inds)]) fields[i] = base + inds except: skip = "unable to read the term '" + k.replace( ';', ', ') + "'" break if fields[i] not in self.allFermions: skip = "in term '" + k.replace( ';', ', ') + "', fermion '" + f + "' is unknown" break if skip: loggingCritical("Warning in FermionAnomalous : " + skip + ". Skipping.") continue key = [ self.allFermions[f][(3 if len(self.allFermions[f]) > 3 else 1)] for f in fields ] key = tuple([(k if isinstance(k, Indexed) else k._name) for k in key]) val = tuple([self.allFermions[f][0] for f in fields]) self.fermionAnomalous[key] = val else: # All anomalous self.saveSettings['FermionAnomalous'] = 'All' for i, f1 in enumerate(self.allFermions.values()): if f1[1].conj: continue for j, f2 in enumerate(self.allFermions.values()): if f2[1].conj or i > j: continue key = [f[(3 if len(f) > 3 else 1)] for f in (f1, f2)] key = tuple([(k if isinstance(k, Indexed) else k._name) for k in key]) val = tuple([f[0] for f in (f1, f2)]) self.fermionAnomalous[key] = val
def constructMapping(self, RGmodule): loggingInfo("Mapping the model onto the general Lagrangian ...") #Gauge couplings mapping, taking into account possible kinetic mixing noMix = {} mix = {} alreadyTaken = set() for el in itertools.combinations_with_replacement( range(RGmodule.nGi), 2): A, B = [RGmodule.gi[i] for i in el] c = RGmodule.G_(A, B) if c != 0 and c not in alreadyTaken: dic = noMix if A == B else mix if not self.upper: A, B = B, A dic[(A, B)] = len(dic) alreadyTaken.add(c) newInds = {**noMix, **{k: v + len(noMix) for k, v in mix.items()}} gaugeMatrix = zeros(len(newInds)) def delta(A, B): if A == B: return 1 return 0 def G(A, B): if RGmodule.G_(A, B) == 0: return 0 if not self.kinMix or A not in RGmodule.Ugauge or B not in RGmodule.Ugauge: return sqrt(RGmodule.G_(A, B)).args[0] i, j = RGmodule.Ugauge.index(A), RGmodule.Ugauge.index(B) return self.kinMat[i, j] for (A, B), X in newInds.items(): for (C, D), Y in newInds.items(): gaugeMatrix[X, Y] = G(B, D) * delta(A, C) + G(A, D) * delta(B, C) gaugeMatrix = simplify(gaugeMatrix.inv()) couplingType = 'GaugeCouplings' self.potential[couplingType] = {} for c in self.gaugeCouplings: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = gaugeMatrix * self.betaFactor self.toCalculate[couplingType] = list(newInds.keys()) count = 0 translation = self.translateDic(RGmodule) for couplingType in self.potential: if couplingType == 'Definitions': continue if (couplingType in translation and self.potential[couplingType] != {} and translation[couplingType] != {}): coeffList = [c for c in self.potential[couplingType].keys()] mappingMatrix = SparseMatrix(len(coeffList), len(coeffList), 0) dicList = [] auxDic = {} sortFunc = lambda x: (len(set(x[0])), len(x[1].as_coeff_add()[1]), x[0]) for key, val in translation[couplingType].items(): if key[-1] is True: continue if val not in auxDic: auxDic[val] = key elif sortFunc((key, val)) < sortFunc((auxDic[val], val)): auxDic[val] = key rank = 0 for v, k in auxDic.items(): matTry = self.fillMappingMatrix(mappingMatrix, rank, coeffList, (k, v)) newRank = matTry.rank() if (newRank > rank): mappingMatrix = matTry rank = newRank dicList.append(k) count += 1 print_progress(count, self.nCouplings, prefix=' ' * 4, bar_length=20, printTime=self.times, logProgress=True) if newRank == len(coeffList): self.lagrangianMapping[couplingType] = Matrix( mappingMatrix).inv() * self.betaFactor break else: # The mapping matrix is not invertible ns = mappingMatrix.nullspace() cVec = Matrix([ Symbol('O(' + el + ')') for el in coeffList ]).transpose() errorMess = "\n\nError in Lagrangian mapping: matrix of couplings is not invertible. " errorMess += f"The following operators in '{couplingType}' are linearly dependent:\n\n" for vec in ns: errorMess += f" {(cVec*vec)[0,0]} = 0\n" loggingCritical(errorMess[:-1]) exit() self.toCalculate[couplingType] = dicList # Add vevs and anomalous dimensions by hand (not related to the Lagrangian) if self.vevs != {}: couplingType = 'Vevs' self.potential[couplingType] = {} for c in self.vevs: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye(len( self.vevs)) * self.betaFactor self.toCalculate[couplingType] = list(RGmodule.Vdic.keys()) if self.fermionAnomalous != {}: couplingType = 'FermionAnomalous' self.potential[couplingType] = {} for c in self.fermionAnomalous: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye( len(self.fermionAnomalous)) self.toCalculate[couplingType] = list(RGmodule.gammaFdic.keys()) if self.scalarAnomalous != {}: couplingType = 'ScalarAnomalous' self.potential[couplingType] = {} for c in self.scalarAnomalous: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye( len(self.scalarAnomalous)) self.toCalculate[couplingType] = list(RGmodule.gammaSdic.keys())