|
19 | 19 | from sympy import NotInvertible |
20 | 20 |
|
21 | 21 | from syngular import Field |
22 | | -from pyadic import PAdic |
| 22 | +from pyadic import PAdic, ModP |
23 | 23 |
|
24 | | -from .tools import MinkowskiMetric, flatten, subs_dict, pNB, myException, indexing_decorator, pAu, pAd, pSu, pSd, pMVar |
| 24 | +from .tools import MinkowskiMetric, flatten, subs_dict, pNB, myException, indexing_decorator, pAu, pAd, pSu, pSd, pMVar, LeviCivita |
25 | 25 | from .particle import Particle |
26 | 26 | from .particles_compute import Particles_Compute |
27 | 27 | from .particles_eval import Particles_Eval |
28 | 28 | from .hardcoded_limits.particles_set import Particles_Set |
29 | 29 | from .hardcoded_limits.particles_set_pair import Particles_SetPair |
30 | 30 | from .algebraic_geometry.particles_singular_variety import Particles_SingularVariety |
31 | 31 | from .particles_variety import Particles_Variety |
| 32 | +from .particles_slices import Particles_Slices |
32 | 33 |
|
33 | 34 |
|
34 | 35 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # |
35 | 36 |
|
36 | 37 |
|
37 | | -class Particles(Particles_Compute, Particles_Eval, Particles_Set, Particles_SetPair, Particles_SingularVariety, Particles_Variety, list): |
| 38 | +class Particles(Particles_Compute, Particles_Eval, Particles_Set, Particles_SetPair, Particles_SingularVariety, |
| 39 | + Particles_Variety, Particles_Slices, list): |
38 | 40 | """Describes the kinematics of n particles. Base one list of Particle objects.""" |
39 | 41 |
|
40 | 42 | # MAGIC METHODS |
@@ -101,9 +103,14 @@ def total_mom(self): |
101 | 103 | return sum([oParticle.r2_sp for oParticle in self]) |
102 | 104 |
|
103 | 105 | @property |
104 | | - def masses(self): |
| 106 | + def m2s(self): |
| 107 | + """Masses squared of all particles in phase space.""" |
| 108 | + return [oParticle.m2 for oParticle in self] |
| 109 | + |
| 110 | + @property |
| 111 | + def ms(self): |
105 | 112 | """Masses of all particles in phase space.""" |
106 | | - return [oParticle.mass for oParticle in self] |
| 113 | + return [oParticle.m for oParticle in self] |
107 | 114 |
|
108 | 115 | @property |
109 | 116 | def internal_masses_dict(self): |
@@ -152,14 +159,35 @@ def copy(self): |
152 | 159 | from .symmetries import identity |
153 | 160 | return self.image(identity(len(self))) |
154 | 161 |
|
155 | | - def cluster(self, llIntegers): |
156 | | - """Returns clustered particle objects according to lists of lists of integers (e.g. corners of one loop diagram).""" |
| 162 | + def cluster(self, llIntegers, massive_fermions=None): |
| 163 | + """Returns clustered particle objects according to lists of lists of integers (e.g. corners of one loop diagram). |
| 164 | + Massive legs are by default massive scalars. |
| 165 | + Massive fermions can be specificed as e.g.: massive_fermions=((3, 'u', all), (4, 'd', all))) |
| 166 | + """ |
157 | 167 | drule1 = dict(zip(["s" + "".join(map(str, entry)) for entry in llIntegers], [f"s{i}" for i in range(1, len(llIntegers) + 1)])) |
158 | 168 | drule2 = dict(zip(["s_" + "".join(map(str, entry)) for entry in llIntegers], [f"s_{i}" for i in range(1, len(llIntegers) + 1)])) |
159 | 169 | clustered_internal_masses = {key: (subs_dict(val, drule1 | drule2) if isinstance(val, str) else val) |
160 | 170 | for key, val in self.internal_masses_dict.items()} |
161 | | - return Particles([sum([self[i] for i in corner_as_integers]) for corner_as_integers in llIntegers], |
162 | | - field=self.field, fix_mom_cons=False, internal_masses=clustered_internal_masses) |
| 171 | + selfClustered = Particles([sum([self[i] for i in corner_as_integers]) for corner_as_integers in llIntegers], |
| 172 | + field=self.field, fix_mom_cons=False, internal_masses=clustered_internal_masses) |
| 173 | + if massive_fermions is not None: |
| 174 | + for leg, index_position, index_value in massive_fermions: |
| 175 | + assert len(llIntegers[leg - 1]) == 2 |
| 176 | + a, b = llIntegers[leg - 1] |
| 177 | + selfClustered[leg]._r_sp_d = numpy.block([self(f"|{a}⟩"), self(f"|{b}⟩")]) # |bold leg> = |leg^I> = (lambda_leg)_alpha^I |
| 178 | + selfClustered[leg]._l_sp_d = numpy.block([[self(f"[{a}|")], [self(f"[{b}|")]]) # [bold leg| = [leg_I| = (tilde-lambda_leg)_I_alpha |
| 179 | + if index_position == "u": |
| 180 | + selfClustered[leg]._l_sp_d = -LeviCivita @ selfClustered[leg]._l_sp_d # [bold leg| = [leg^I|| |
| 181 | + elif index_position == "d": |
| 182 | + selfClustered[leg]._r_sp_d = selfClustered[leg]._r_sp_d @ -LeviCivita # |bold leg> = |leg_I> |
| 183 | + else: |
| 184 | + raise Exception("Massive fermion spin index position must be either 'u' or 'd'.") |
| 185 | + selfClustered[leg].spin_index = index_position |
| 186 | + if isinstance(index_value, int): |
| 187 | + selfClustered[leg]._r_sp_d = selfClustered[leg]._r_sp_d[:, index_value - 1:index_value] |
| 188 | + selfClustered[leg]._l_sp_d = selfClustered[leg]._l_sp_d[index_value - 1:index_value, :] |
| 189 | + selfClustered[leg]._sps_d_to_sps_u() |
| 190 | + return selfClustered |
163 | 191 |
|
164 | 192 | def make_analytical_d(self, indepVars=None, symbols=('a', 'b', 'c', 'd')): |
165 | 193 | """ """ |
@@ -202,6 +230,44 @@ def analytical_subs_d(self): |
202 | 230 | subs_dict.update({lc[i]: iParticle.l_sp_d[0, 0], ld[i]: iParticle.l_sp_d[0, 1]}) |
203 | 231 | return subs_dict |
204 | 232 |
|
| 233 | + def subs(self, myDict): |
| 234 | + """For all rank-1 spinor components, substitutes symbols with values from myDict.""" |
| 235 | + for oP in self: |
| 236 | + if isinstance(oP.r_sp_d[0, 0], (sympy.Add, sympy.Mul, sympy.Symbol)): |
| 237 | + oP.r_sp_d[0, 0] = oP.r_sp_d[0, 0].subs(myDict) |
| 238 | + if isinstance(oP.r_sp_d[0, 0], sympy.Integer) and self.field.name == "finite field": |
| 239 | + oP.r_sp_d[0, 0] = ModP(oP.r_sp_d[0, 0], self.field.characteristic) |
| 240 | + elif isinstance(oP.r_sp_d[0, 0], sympy.Integer) and self.field.name == "padic": |
| 241 | + oP.r_sp_d[0, 0] = PAdic(oP.r_sp_d[0, 0], self.field.characteristic, self.field.digits) |
| 242 | + else: |
| 243 | + oP.r_sp_d[0, 0] = sympy.poly(oP.r_sp_d[0, 0], modulus=self.field.characteristic ** self.field.digits).as_expr() |
| 244 | + if isinstance(oP.r_sp_d[1, 0], (sympy.Add, sympy.Mul, sympy.Symbol)): |
| 245 | + oP.r_sp_d[1, 0] = oP.r_sp_d[1, 0].subs(myDict) |
| 246 | + if isinstance(oP.r_sp_d[1, 0], sympy.Integer) and self.field.name == "finite field": |
| 247 | + oP.r_sp_d[1, 0] = ModP(oP.r_sp_d[1, 0], self.field.characteristic) |
| 248 | + elif isinstance(oP.r_sp_d[1, 0], sympy.Integer) and self.field.name == "padic": |
| 249 | + oP.r_sp_d[1, 0] = PAdic(oP.r_sp_d[1, 0], self.field.characteristic, self.field.digits) |
| 250 | + else: |
| 251 | + oP.r_sp_d[1, 0] = sympy.poly(oP.r_sp_d[1, 0], modulus=self.field.characteristic ** self.field.digits).as_expr() |
| 252 | + oP.r_sp_d = oP.r_sp_d # trigger setter |
| 253 | + if isinstance(oP.l_sp_d[0, 0], (sympy.Add, sympy.Mul, sympy.Symbol)): |
| 254 | + oP.l_sp_d[0, 0] = oP.l_sp_d[0, 0].subs(myDict) |
| 255 | + if isinstance(oP.l_sp_d[0, 0], sympy.Integer) and self.field.name == "finite field": |
| 256 | + oP.l_sp_d[0, 0] = ModP(oP.l_sp_d[0, 0], self.field.characteristic) |
| 257 | + elif isinstance(oP.l_sp_d[0, 0], sympy.Integer) and self.field.name == "padic": |
| 258 | + oP.l_sp_d[0, 0] = PAdic(oP.l_sp_d[0, 0], self.field.characteristic, self.field.digits) |
| 259 | + else: |
| 260 | + oP.l_sp_d[0, 0] = sympy.poly(oP.l_sp_d[0, 0], modulus=self.field.characteristic ** self.field.digits).as_expr() |
| 261 | + if isinstance(oP.l_sp_d[0, 1], (sympy.Add, sympy.Mul, sympy.Symbol)): |
| 262 | + oP.l_sp_d[0, 1] = oP.l_sp_d[0, 1].subs(myDict) |
| 263 | + if isinstance(oP.l_sp_d[0, 1], sympy.Integer) and self.field.name == "finite field": |
| 264 | + oP.l_sp_d[0, 1] = ModP(oP.l_sp_d[0, 1], self.field.characteristic) |
| 265 | + elif isinstance(oP.l_sp_d[0, 1], sympy.Integer) and self.field.name == "padic": |
| 266 | + oP.l_sp_d[0, 1] = PAdic(oP.l_sp_d[0, 1], self.field.characteristic, self.field.digits) |
| 267 | + else: |
| 268 | + oP.l_sp_d[0, 1] = sympy.poly(oP.l_sp_d[0, 1], modulus=self.field.characteristic ** self.field.digits).as_expr() |
| 269 | + oP.l_sp_d = oP.l_sp_d # trigger setter |
| 270 | + |
205 | 271 | def fix_mom_cons(self, A=0, B=0, real_momenta=False, axis=1): # using real momenta changes both |⟩ and |] of A & B |
206 | 272 | """Fixes momentum conservation using particles A and B.""" |
207 | 273 | if A == 0 and B == 0: # defaults to random particles to fix mom cons |
@@ -234,7 +300,7 @@ def momentum_conservation_check(self, silent=True): |
234 | 300 |
|
235 | 301 | def onshell_relation_check(self, silent=True): |
236 | 302 | """Returns true if all on-shell relations are satisfied.""" |
237 | | - onshell_violation = max(map(abs, flatten(self.masses))) |
| 303 | + onshell_violation = max(map(abs, flatten(self.m2s))) |
238 | 304 | if silent is False: |
239 | 305 | print("The largest on shell violation is {}".format(float(onshell_violation) if type(onshell_violation) is mpmath.mpf else onshell_violation)) |
240 | 306 | if onshell_violation > self.field.tollerance: |
|
0 commit comments