# -*- coding: utf-8 -*-
import copy
import itertools
from collections import namedtuple, Counter
from common.utils import PicklableObject
from interactions.four_particle import FourParticleM
Helper class that takes care of creating all necessary Integral
s for the actual interaction.
class Interaction(PicklableObject):
Human-friendly interaction identifier
name = "Particle interaction"
integrals = None
particles = None # All particles involved
Temperature when the typical interaction time exceeds the Hubble expansion time
decoupling_temperature = 0.
Create an Integral
object for all particle species involved in the interaction.
Precise expressions for all integrals can be derived by permuting all particle-related functions in the distribution functions, matrix elements, momenta, energy and mass arrays.
To avoid double-counting, one should create an integral for each particle specie only once.
Additionally, one has to generate the integrals for all the crossing processes by\
def __init__(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
self.integrals = []
for reaction in self.reactions_map():
lhs = [item for item in reaction if item.side == -1]
item = lhs[0]
Skip collision integrals for anti-particles
if item.antiparticle:
continue
self.init_integrals(reaction, item)
String-like representation of the interaction and all its integral
def __str__(self):
return self.name + "\n\t" + "\n\t".join([str(integral) for integral in self.integrals])
@classmethod
def cross_particle(cls, item):
return item._replace(
crossed=not item.crossed,
antiparticle=not item.antiparticle if not item.specie.majorana else False,
side=-item.side
)
def permutations(self, particles, antiparticles):
return itertools.permutations(
[
IntegralItem(side=-1,
specie=particle,
antiparticle=antiparticle,
index=i,
crossed=False)
for i, (particle, antiparticle)
in enumerate(zip(particles[0], antiparticles[0]))
] + [
IntegralItem(side=1,
specie=particle,
antiparticle=antiparticle,
index=i,
crossed=False)
for i, (particle, antiparticle)
in enumerate(zip(particles[1], antiparticles[1]), len(particles[0]))
]
)
Return all relevant collision integrals involving count
particles
def reactions_map(self):
reactions = {}
perms = self.permutations(self.particles, self.antiparticles)
perms = itertools.chain( self.permutations(self.particles, self.antiparticles), self.permutations(tuple(reversed(self.particles)), tuple(reversed(self.antiparticles))) )
if any(specie.majorana for specie in itertools.chain.from_iterable(self.particles)): print "There is Majorana in da house!" conjugated_antiparticles = ( tuple(not antip if not specie.majorana else False for specie, antip in zip(self.particles[0], self.antiparticles[0])), tuple(not antip if not specie.majorana else False for specie, antip in zip(self.particles[1], self.antiparticles[1])) ) perms = itertools.chain(perms, self.permutations(self.particles, conjugated_antiparticles))
Generate possible reactions by inserting an arrow at every position
for i in range(sum(map(len, self.particles)) - 1):
Cross particles according to their position relative to the reaction arrow
reaction = tuple(
Interaction.cross_particle(item)
if (item.side > 0 and j <= i) or (item.side < 0 and j > i)
else item
for j, item in enumerate(perm)
)
lhs = [item for item in reaction if item.side == -1]
rhs = [item for item in reaction if item.side == 1]
lhs = lhs[0:1] + sorted(lhs[1:])
rhs = sorted(rhs)
if lhs[0].antiparticle:
continue
Skip kinematically impossible reactions
if (
(len(lhs) == 1 and lhs[0].specie.mass <= sum(item.specie.mass for item in rhs))
or
(len(rhs) == 1 and rhs[0].specie.mass <= sum(item.specie.mass for item in lhs))
):
continue
k = tuple(map(tuple, lhs + rhs))
if k not in reactions:
reactions[k] = tuple(lhs + rhs)
Starting from the single representation of the interaction, create integrals objects to be computed for every particle specie involved
def init_integrals(self, reaction, item):
particle = item.specie
particle_Ms = copy.deepcopy(self.Ms)
for M in particle_Ms:
if isinstance(M, FourParticleM):
map = {item.index: i for i, item in enumerate(reaction)}
M.apply_order(tuple(map[val] for val in M.order), reaction)
Add interaction integrals by putting each incoming particle as the first one
self.integrals.append(self.integral(
particle=particle,
reaction=reaction,
decoupling_temperature=self.decoupling_temperature,
Ms=particle_Ms,
))
class IntegralSet(PicklableObject):
integrals = None
def __init__(self, new_integrals):
self.integrals = []
for new_integral in new_integrals:
self.append(new_integral)
def species(self, integral):
return (
Counter(item.specie for item in integral.reaction if item.side == -1),
Counter(item.specie for item in integral.reaction if item.side == 1)
)
Check if there is already a similar integral
for integral in self.integrals:
if new_integral.particle == integral.particle and new_species == self.species(integral):
old_integral = integral
break
If there is no, just append a new one
if not old_integral:
self.integrals.append(new_integral)
return