def QCD_transition(self, hadrons=None, quarkic_interactions=None, hadronic_interactions=None, secondary_interactions=None): self.data.truncate() dof_before_qcd = Params.entropic_dof_eq(self) self.particles = utils.particle_filter([ 'Up quark', 'Down quark', 'Charm quark', 'Strange quark', 'Top quark', 'Bottom quark', 'Gluon' ], self.particles) self.add_particles(hadrons) dof_after_qcd = Params.entropic_dof_eq(self) self.params.aT = self.data['aT'][-1] * (dof_before_qcd / dof_after_qcd)**(1 / 3) # self.params.x = self.data['x'][-1] * (dof_before_qcd/dof_after_qcd)**(1/3) # self.params.a = self.data['a'][-1] * (dof_before_qcd/dof_after_qcd)**(1/3) self.params.update(self.total_energy_density(), self.total_entropy()) # T will be updated here... # self.params.T = CONST.lambda_QCD # That's why we change it here again self.update_particles() if quarkic_interactions and self.interactions: for inter in quarkic_interactions: self.interactions.remove(inter) if hadronic_interactions: self.interactions += (hadronic_interactions) if secondary_interactions: self.interactions += (secondary_interactions)
def __init__(self, folder='logs', plotting=True, params=None, grid=None): """ :param particles: Set of `particle.Particle` to model :param interactions: Set of `interaction.Interaction` - quantum interactions \ between particle species :param folder: Log file path (current `datetime` by default) """ self.particles = [] self.interactions = [] self.clock_start = time.time() self.params = Params() if not params else params # self.graphics = None # if utils.getboolenv("PLOT", plotting): # from plotting import Plotting # self.graphics = Plotting() self.init_log(folder=folder) # Controls parallelization of the collision integrals calculations self.PARALLELIZE = utils.getboolenv("PARALLELIZE", True) if self.PARALLELIZE: parallelization.init_pool() self.fraction = 0 self.step = 1
def main(): from sys import argv dir_path = os.path.dirname(os.path.realpath(__file__)) if not os.path.exists(dir_path + "/output"): print "No output detected. Exiting." return config = Params(argv[1]) writeDecision(config, dir_path, "output/decision.file") print "Tuning file written to output/decision.file"
def decoupling_test(): params = Params(T=SMP.leptons.neutrino_e['decoupling_temperature'] * 2, dy=0.025) neutrino = Particle(params=params, **SMP.leptons.neutrino_e) assert neutrino.in_equilibrium eq_distribution = neutrino._distribution params.T /= 2 params.a = params.m / params.T params.infer() assert neutrino.in_equilibrium, "Neutrino should not depend on global temperature change" neutrino.update() assert not neutrino.in_equilibrium, "Neutrino should have decoupled" noneq_distribution = neutrino._distribution assert all(eq_distribution == noneq_distribution), \ "Free massless particle distribution should be conserved in conformal coordinates"
def __init__(self, folder=None, params=None, max_log_rate=2): """ :param folder: Log file path (current `datetime` by default) """ self.particles = [] self.interactions = [] self.clock_start = time.time() self.log_throttler = utils.Throttler(max_log_rate) self.params = params if not self.params: self.params = Params() self.folder = folder if self.folder: if os.path.exists(folder): shutil.rmtree(folder) self.init_log(folder=folder) self.fraction = 0 self.step = 1
""" import os from particles import Particle from library.SM import particles as SMP, interactions as SMI from evolution import Universe from common import UNITS, Params folder = os.path.join(os.path.split(__file__)[0], 'output') T_kawano = 10 * UNITS.MeV T_simple = 0.05 * UNITS.MeV T_final = 0.0008 * UNITS.MeV params = Params(T=T_kawano, dy=0.0125) universe = Universe(params=params, folder=folder) photon = Particle(**SMP.photon) electron = Particle(**SMP.leptons.electron) neutrino_e = Particle(**SMP.leptons.neutrino_e) neutrino_mu_tau = Particle(**SMP.leptons.neutrino_mu) neutrino_mu_tau.dof = 4 neutron = Particle(**SMP.hadrons.neutron) proton = Particle(**SMP.hadrons.proton) universe.add_particles( [photon, electron, neutrino_e, neutrino_mu_tau, neutron, proton])
def setup(): params = Params(T=SMP.leptons.neutrino_e['decoupling_temperature'], dy=0.025) return [params], {}
def main(): from os import system from sys import argv from common import Params import sys dir_path = os.path.dirname(os.path.realpath(__file__)) config = Params(argv[1]) scheduler = argv[2] collective_list = config.getStrlst("collectives") omb_path = config.getStr("omb_collective_directory") imb_bin = config.getStr("imb_binary") num_rank_list = config.getIntlst("number_of_ranks") max_num_node = config.getInt("max_num_node") num_core_per_node = config.getInt("number_of_cores_per_node") num_run = config.getInt("number_of_runs_per_test") job_directory = dir_path + "/collective_jobs" for collective in collective_list: params = Params(job_directory + "/" + collective + ".job") if not os.path.exists(dir_path + "/output"): os.makedirs(dir_path + "/output") if not os.path.exists(dir_path + "/output/" + collective): os.makedirs(dir_path + "/output/" + collective) num_alg = params.getInt("number_of_algorithms") exclude_alg = params.getIntlst("exclude_algorithms") two_proc_alg = -1 try: two_proc_alg = params.getInt("two_proc_alg") except Exception as e: print "No two proc algorithm for " + collective f = open( dir_path + "/output/" + collective + "/" + collective + "_coltune.sh", "w") print >> f, "#!/bin/sh" print >> f, "#" if scheduler == "slurm": print >> f, "#SBATCH --job-name=" + collective print >> f, "#SBATCH --output=res.txt" print >> f, "#" print >> f, "#SBATCH --ntasks-per-node=" + str(num_core_per_node) print >> f, "#SBATCH --time=1000:00:00" print >> f, "#SBATCH --nodes=" + str(max_num_node) elif scheduler == "sge": print >> f, "#$ -j y" print >> f, "#$ -pe mpi %d" % (max_num_node * num_core_per_node) print >> f, "#" print >> f, "#$ -cwd" print >> f, "#" print >> f, "echo Got $NSOLTS processors." else: print "Unknown scheduler. Aborting.." sys.exit() print >> f, "" for num_rank in num_rank_list: for alg in range(num_alg + 1): if alg in exclude_alg or (alg == two_proc_alg and num_rank > 2): continue print >> f, "# ", alg, num_rank, "ranks" for run_id in xrange(num_run): if collective in imb_collectives: prg_name = imb_bin + " -npmin %d %s " % (num_rank, collective) else: prg_name = omb_path + "/osu_" + collective cmd = "mpirun --np %d " % (num_rank) cmd += "--mca coll_tuned_use_dynamic_rules 1 --mca coll_tuned_" + collective + "_algorithm " + str( alg) cmd += " " + prg_name cmd += " >& " + dir_path + "/output/" + collective + "/" + str( alg) + "_" + str(num_rank) + "ranks" + "_run" + str( run_id) + ".out" print >> f, cmd print >> f, "" f.close() print "SGE script wrote to " + collective + "_coltune.sh successfully!"
class Universe(object): """ ## Universe The master object that governs the calculation. """ # System state is rendered to the log file each `log_freq` steps log_freq = 1 clock_start = None particles = None interactions = None kawano = None kawano_log = None oscillations = None step_monitor = None data = pandas.DataFrame(columns=('aT', 'T', 'a', 'x', 't', 'rho', 'fraction')) def __init__(self, folder='logs', plotting=True, params=None, grid=None): """ :param particles: Set of `particle.Particle` to model :param interactions: Set of `interaction.Interaction` - quantum interactions \ between particle species :param folder: Log file path (current `datetime` by default) """ self.particles = [] self.interactions = [] self.clock_start = time.time() self.params = Params() if not params else params # self.graphics = None # if utils.getboolenv("PLOT", plotting): # from plotting import Plotting # self.graphics = Plotting() self.init_log(folder=folder) # Controls parallelization of the collision integrals calculations self.PARALLELIZE = utils.getboolenv("PARALLELIZE", True) if self.PARALLELIZE: parallelization.init_pool() self.fraction = 0 self.step = 1 def init_kawano(self, datafile='s4.dat', **kwargs): kawano.init_kawano(**kwargs) self.kawano_log = open(os.path.join(self.folder, datafile), 'w') self.kawano_log.write("\t".join(kawano.heading) + "\n") self.kawano = kawano self.kawano_data = pandas.DataFrame(columns=self.kawano.heading) def init_oscillations(self, pattern, particles): self.oscillations = (pattern, particles) def evolve(self, T_final, export=True): """ ## Main computing routine Modeling is carried in steps of scale factor, starting from the initial conditions defined\ by a single parameter: the initial temperature. Initial temperature (e.g. 10 MeV) corresponds to a point in the history of the Universe \ long before then BBN. Then most particle species are in the thermodynamical equilibrium. """ for particle in self.particles: print particle for interaction in self.interactions: print interaction if self.params.rho is None: self.update_particles() self.params.update(self.total_energy_density()) self.save_params() while self.params.T > T_final: try: self.log() self.make_step() self.save() self.step += 1 self.data.to_pickle(os.path.join(self.folder, "evolution.pickle")) except KeyboardInterrupt: print "Keyboard interrupt!" break self.log() if export: self.export() return self.data def export(self): for particle in self.particles: print particle # if self.graphics: # self.graphics.save(self.logfile) if self.kawano: # if self.graphics: # self.kawano.plot(self.kawano_data, save=self.kawano_log.name) self.kawano_log.close() print kawano.run(self.folder) self.kawano_data.to_pickle(os.path.join(self.folder, "kawano.pickle")) print "Data saved to file {}".format(self.logfile) self.data.to_pickle(os.path.join(self.folder, "evolution.pickle")) def make_step(self): self.integrand(self.params.x, self.params.aT) order = min(self.step + 1, 5) fs = self.data['fraction'].tail(order-1).values.tolist() fs.append(self.fraction) self.params.aT +=\ integrators.adams_bashforth_correction(fs=fs, h=self.params.dy, order=order) self.params.x += self.params.dx self.params.update(self.total_energy_density()) if self.step_monitor: self.step_monitor(self) def add_particles(self, particles): for particle in particles: particle.set_params(self.params) self.particles += particles def update_particles(self): """ ### 1. Update particles state Update particle species distribution functions, check for regime switching,\ update precalculated variables like energy density and pressure. """ for particle in self.particles: particle.update() def init_interactions(self): """ ### 2. Initialize non-equilibrium interactions Non-equilibrium interactions of different particle species are treated by a\ numerical integration of the Boltzmann equation for distribution functions in\ the expanding space-time. Depending on the regime of the particle species involved and cosmological parameters, \ each `Interaction` object populates `Particle.collision_integrals` array with \ currently active `Integral` objects. """ for interaction in self.interactions: interaction.initialize() def calculate_collisions(self): """ ### 3. Calculate collision integrals """ particles = [particle for particle in self.particles if particle.collision_integrals] with utils.printoptions(precision=2): if self.PARALLELIZE: for particle in particles: parallelization.orders = [ (particle, parallelization.poolmap(particle, 'calculate_collision_integral', particle.grid.TEMPLATE)) ] for particle, result in parallelization.orders: with utils.benchmark(lambda: "I(" + particle.symbol + ") = " + repr(particle.collision_integral)): particle.collision_integral = numpy.array(result.get(1000)) else: for particle in particles: with utils.benchmark(lambda: "I(" + particle.symbol + ") = " + repr(particle.collision_integral)): particle.collision_integral = particle.integrate_collisions() def update_distributions(self): """ ### 4. Update particles distributions """ if self.oscillations: pattern, particles = self.oscillations integrals = {A.flavour: A.collision_integral for A in particles} for A in particles: A.collision_integral = sum(pattern[(A.flavour, B.flavour)] * integrals[B.flavour] for B in particles) for particle in self.particles: particle.update_distribution() def calculate_temperature_terms(self): """ ### 5. Calculate temperature equation terms """ numerator = 0 denominator = 0 for particle in self.particles: numerator += particle.numerator denominator += particle.denominator return numerator, denominator def integrand(self, t, y): """ ## Temperature equation integrand Master equation for the temperature looks like \begin{equation} \frac{d (aT)}{dx} = \frac{\sum_i{N_i}}{\sum_i{D_i}} \end{equation} Where $N_i$ and $D_i$ represent contributions from different particle species. See definitions for different regimes: * [[Radiation|particles/RadiationParticle.py#master-equation-terms]] * [[Intermediate|particles/IntermediateParticle.py#master-equation-terms]] * [[Dust|particles/DustParticle.py#master-equation-terms]] * [[Non-equilibrium|particles/NonEqParticle.py#master-equation-terms]] """ # 1\. Update particles states self.update_particles() # 2\. Initialize non-equilibrium interactions self.init_interactions() # 3\. Calculate collision integrals self.calculate_collisions() # 4\. Update particles distributions self.update_distributions() # 5\. Calculate temperature equation terms numerator, denominator = self.calculate_temperature_terms() self.fraction = self.params.x * numerator / denominator return self.fraction def save_params(self): self.data = self.data.append({ 'aT': self.params.aT, 'T': self.params.T, 'a': self.params.a, 'x': self.params.x, 'rho': self.params.rho, 'N_eff': self.params.N_eff, 't': self.params.t, 'fraction': self.fraction }, ignore_index=True) def save(self): """ Save current Universe parameters into the data arrays or output files """ self.save_params() if self.kawano and self.params.T <= self.kawano.T_kawano: # t[s] x Tg[10^9K] dTg/dt[10^9K/s] rho_tot[g cm^-3] H[s^-1] # n nue->p e p e->n nue n->p e nue p e nue->n n e->p nue p nue->n e rates = self.kawano.baryonic_rates(self.params.a) row = { self.kawano.heading[0]: self.params.t / UNITS.s, self.kawano.heading[1]: self.params.x / UNITS.MeV, self.kawano.heading[2]: self.params.T / UNITS.K9, self.kawano.heading[3]: (self.params.T - self.data['T'].iloc[-2]) / (self.params.t - self.data['t'].iloc[-2]) * UNITS.s / UNITS.K9, self.kawano.heading[4]: self.params.rho / UNITS.g_cm3, self.kawano.heading[5]: self.params.H * UNITS.s } row.update({self.kawano.heading[i]: rate / UNITS.MeV**5 for i, rate in enumerate(rates, 6)}) self.kawano_data = self.kawano_data.append(row, ignore_index=True) log_entry = "\t".join("{:e}".format(item) for item in self.kawano_data.iloc[-1]) print "KAWANO", log_entry self.kawano_log.write(log_entry + "\n") def init_log(self, folder=''): self.folder = folder self.logfile = utils.ensure_path(os.path.join(self.folder, 'log.txt')) sys.stdout = utils.Logger(self.logfile) def log(self): """ Runtime log output """ # Print parameters every now and then if self.step % self.log_freq == 0: print ('[{clock}] #{step}\tt = {t:e}\taT = {aT:e}\tT = {T:e}\ta = {a:e}\tdx = {dx:e}' .format(clock=str(timedelta(seconds=int(time.time() - self.clock_start))), step=self.step, t=self.params.t / UNITS.s, aT=self.params.aT / UNITS.MeV, T=self.params.T / UNITS.MeV, a=self.params.a, dx=self.params.dx / UNITS.MeV)) # if self.graphics: # self.graphics.plot(self.data) def total_energy_density(self): return sum(particle.energy_density for particle in self.particles)
[Log file](log.txt) """ import os from particles import Particle from evolution import Universe from common import Params, UNITS from semianalytic import step_monitor from library.SM import particles as SMP folder = os.path.split(__file__)[0] params = Params(T=10 * UNITS.MeV, dy=0.025) T_final = 0.0008 * UNITS.MeV Particles = [] photon = Particle(**SMP.photon) neutron = Particle(**SMP.hadrons.neutron) proton = Particle(**SMP.hadrons.proton) neutrino_e = Particle(**SMP.leptons.neutrino_e) neutrino_mu = Particle(**SMP.leptons.neutrino_mu) neutrino_tau = Particle(**SMP.leptons.neutrino_tau) electron = Particle(**SMP.leptons.electron) muon = Particle(**SMP.leptons.muon) tau = Particle(**SMP.leptons.tau) Particles += [
[Log file](log.txt) """ import os import numpy from particles import Particle from evolution import Universe from library.SM import particles as SMP from common import Params, UNITS folder = os.path.join(os.path.split(__file__)[0], 'output') T_final = 100 * UNITS.keV params = Params(T=100 * UNITS.MeV, dy=0.05) universe = Universe(params=params, folder=folder) photon = Particle(**SMP.photon) electron = Particle(**SMP.leptons.electron) universe.add_particles([photon, electron]) universe.evolve(T_final) initial_aT = universe.data['aT'][0] print("a * T is not conserved: {}".format( any([initial_aT != value for value in universe.data['aT']]))) initial_a = universe.data['a'][int(len(universe.data) / 2)] initial_t = universe.data['t'][int(len(universe.data) / 2)] / UNITS.s last_a = universe.data['a'][-1]
def writeDecision(config, dir_path, outfil): collective_list = config.getStrlst("collectives") num_rank_list = config.getIntlst("number_of_ranks") num_run = config.getInt("number_of_runs_per_test") num_coll = len(collective_list) output_dir = dir_path + "/output" job_dir = dir_path + "/collective_jobs" f = open(outfil, "w") print >> f, "%-10s" % num_coll, "# Number of collectives" for collective in collective_list: if not os.path.exists(dir_path + "/output/" + collective): print "Collective " + collective + " output not detected. Exiting." return params = Params(job_dir + "/" + collective + ".job") num_alg = params.getInt("number_of_algorithms") exclude_alg = params.getIntlst("exclude_algorithms") two_proc_alg = -1 try: two_proc_alg = params.getInt("two_proc_alg") except Exception as e: print "No two proc algorithm for " + collective raw_dir = dir_path + "/output/" + collective coll_result = {} for num_rank in num_rank_list: coll_result[num_rank] = NumRankResult(config, num_alg, exclude_alg, two_proc_alg, raw_dir, num_rank, collective) writeResult(num_rank_list, coll_result, raw_dir + "/best.out") print "Result wrote for " + collective + " to " + collective + "/best.out" print >> f, "%-10s" % coll_id_from_name( collective), "# Collective ID for", collective com_sizes = len(num_rank_list) print >> f, "%-10s" % com_sizes, "# Number of com sizes" for num_rank in num_rank_list: nod_result = coll_result[num_rank] print >> f, "%-10s" % num_rank, "# Com size" best = Params(output_dir + "/" + collective + "/best.out") best_alg = 0 # Open MPI requires that all data should start from msg size 0. # The default one is `0 0 0 0\n` # For collective data starts from msg size 0 (barrier or # collectives benchmarked by IMB) this line could be updated. if nod_result.msgsizlst()[0] == 0: num_sizes = 0 size_output = "" else: num_sizes = 1 size_output = "0 0 0 0\n" for i, msg_siz in enumerate(nod_result.msgsizlst()): new_alg = nod_result.selectAlg()[i] if new_alg == best_alg: continue best_alg = new_alg num_sizes += 1 size_output += str(msg_siz) size_output += " " + str(best_alg) size_output += " 0" size_output += " 0\n" print >> f, "%-10s" % num_sizes, "# Number of msg sizes" print >> f, size_output, writeDetail(params, coll_result, raw_dir + "/detail.out", num_alg, exclude_alg, two_proc_alg, num_run, num_rank_list)
description='Run simulation for given mass and mixing angle') parser.add_argument('--mass', default=300) parser.add_argument('--theta', default=0.001) parser.add_argument('--comment', default='') args = parser.parse_args() mass = float(args.mass) * UNITS.MeV theta = float(args.theta) folder = utils.ensure_dir( os.path.split(__file__)[0], 'output', "mass={:e}_theta={:e}".format(mass / UNITS.MeV, theta) + args.comment) T_initial = 50. * UNITS.MeV T_final = 0.0008 * UNITS.MeV params = Params(T=T_initial, dy=0.0003125) universe = Universe(params=params, folder=folder) linear_grid_s = LinearSpacedGrid(MOMENTUM_SAMPLES=51, MAX_MOMENTUM=20 * UNITS.MeV) linear_grid = LinearSpacedGrid(MOMENTUM_SAMPLES=51, MAX_MOMENTUM=50 * UNITS.MeV) photon = Particle(**SMP.photon) #electron = Particle(**SMP.leptons.electron) #muon = Particle(**SMP.leptons.muon) #tau = Particle(**SMP.leptons.tau) neutrino_e = Particle(**SMP.leptons.neutrino_e, **{'grid': linear_grid}) #neutrino_mu = Particle(**SMP.leptons.neutrino_mu)
import os from collections import defaultdict from particles import Particle from library.SM import particles as SMP, interactions as SMI from library.NuMSM import particles as NuP, interactions as NuI from evolution import Universe from common import UNITS, Params folder = os.path.join(os.path.split(__file__)[0], 'output') T_initial = 200 * UNITS.MeV T_final = 50 * UNITS.MeV params = Params(T=T_initial, dy=0.025) universe = Universe(params=params, logfile=os.path.join(folder, 'log.txt')) photon = Particle(**SMP.photon) electron = Particle(**SMP.leptons.electron) neutrino_e = Particle(**SMP.leptons.neutrino_e) neutral_pion = Particle(**SMP.hadrons.neutral_pion) sterile = Particle(**NuP.sterile_neutrino(300 * UNITS.MeV)) sterile.decoupling_temperature = T_initial universe.add_particles([
class Universe(object): """ ## Universe The master object that governs the calculation. """ # System state is rendered to the evolution.txt file each `export_freq` steps export_freq = 100 log_throttler = None clock_start = None particles = None interactions = None kawano = None kawano_log = None oscillations = None step_monitor = None data = utils.DynamicRecArray([ ['aT', 'MeV', UNITS.MeV], ['T', 'MeV', UNITS.MeV], ['a', None, 1], ['x', 'MeV', UNITS.MeV], ['t', 's', UNITS.s], ['rho', 'MeV^4', UNITS.MeV**4], ['N_eff', None, 1], ['fraction', None, 1], ['S', 'MeV^3', UNITS.MeV**3] ]) def __init__(self, folder=None, params=None, max_log_rate=2): """ :param folder: Log file path (current `datetime` by default) """ self.particles = [] self.interactions = [] self.clock_start = time.time() self.log_throttler = utils.Throttler(max_log_rate) self.params = params if not self.params: self.params = Params() self.folder = folder if self.folder: if os.path.exists(folder): shutil.rmtree(folder) self.init_log(folder=folder) self.fraction = 0 self.step = 1 def init_kawano(self, datafile='s4.dat', **kwargs): kawano.init_kawano(**kwargs) if self.folder: self.kawano_log = open(os.path.join(self.folder, datafile), 'w') self.kawano_log.write("\t".join([col[0] for col in kawano.heading]) + "\n") self.kawano = kawano self.kawano_data = utils.DynamicRecArray(self.kawano.heading) def init_oscillations(self, pattern, particles): self.oscillations = (pattern, particles) def evolve(self, T_final, export=True, init_time=True): """ ## Main computing routine Modeling is carried in steps of scale factor, starting from the initial conditions defined\ by a single parameter: the initial temperature. Initial temperature (e.g. 10 MeV) corresponds to a point in the history of the Universe \ long before then BBN. Then most particle species are in the thermodynamical equilibrium. """ T_initial = self.params.T print("\n\n" + "#"*32 + " Initial states " + "#"*32 + "\n") for particle in self.particles: print(particle) print("\n\n" + "#"*34 + " Log output " + "#"*34 + "\n") for interaction in self.interactions: print(interaction) print("\n") # TODO: test if changing updating particles beforehand changes the computed time if init_time: self.params.init_time(self.total_energy_density()) if self.params.rho is None: # self.update_particles() self.params.update(self.total_energy_density(), self.total_entropy()) self.save_params() while self.params.T > T_final: try: self.log() self.make_step() self.save() self.step += 1 if self.folder and self.step % self.export_freq == 0: with open(os.path.join(self.folder, "evolution.txt"), "wb") as f: self.data.savetxt(f) except KeyboardInterrupt: print("\nKeyboard interrupt!") sys.exit(1) break if not (T_initial > self.params.T > 0): print("\n(T < 0) or (T > T_initial): suspect numerical instability") sys.exit(1) self.log() if export: self.export() return self.data def export(self): print("\n\n" + "#"*33 + " Final states " + "#"*33 + "\n") for particle in self.particles: print(particle) print("\n") if self.folder: if self.kawano: self.kawano_log.close() print(kawano.run(self.folder)) with open(os.path.join(self.folder, "kawano.txt"), "wb") as f: self.kawano_data.savetxt(f) print("Execution log saved to file {}".format(self.logfile)) with open(os.path.join(self.folder, "evolution.txt"), "wb") as f: self.data.savetxt(f) def make_step(self): self.integrand(self.params.x, self.params.aT) if self.step_monitor: self.step_monitor(self) if environment.get('ADAMS_BASHFORTH_TEMPERATURE_CORRECTION'): fs = (list(self.data['fraction'][-MAX_ADAMS_BASHFORTH_ORDER:]) + [self.fraction]) self.params.aT += adams_bashforth_correction(fs=fs, h=self.params.h) else: self.params.aT += self.fraction * self.params.h self.params.x += self.params.dx self.params.update(self.total_energy_density(), self.total_entropy()) self.log_throttler.update() def add_particles(self, particles): for particle in particles: particle.set_params(self.params) self.particles += particles def update_particles(self): """ ### 1. Update particles state Update particle species distribution functions, check for regime switching,\ update precalculated variables like energy density and pressure. """ for particle in self.particles: particle.update() def init_interactions(self): """ ### 2. Initialize non-equilibrium interactions Non-equilibrium interactions of different particle species are treated by a\ numerical integration of the Boltzmann equation for distribution functions in\ the expanding space-time. Depending on the regime of the particle species involved and cosmological parameters, \ each `Interaction` object populates `Particle.collision_integrals` array with \ currently active `Integral` objects. """ for interaction in self.interactions: interaction.initialize() def calculate_collisions(self): """ ### 3. Calculate collision integrals """ particles = [particle for particle in self.particles if particle.collision_integrals] with utils.printoptions(precision=3, linewidth=100): for particle in particles: with (utils.benchmark(lambda: "δf/f ({}) = {}".format(particle.symbol, particle.collision_integral / particle._distribution * self.params.h), self.log_throttler.output)): particle.collision_integral = particle.integrate_collisions() def update_distributions(self): """ ### 4. Update particles distributions """ if self.oscillations: pattern, particles = self.oscillations integrals = {A.flavour: A.collision_integral for A in particles} for A in particles: A.collision_integral = sum(pattern[(A.flavour, B.flavour)] * integrals[B.flavour] for B in particles) for particle in self.particles: particle.update_distribution() def calculate_temperature_terms(self): """ ### 5. Calculate temperature equation terms """ numerator = 0 denominator = 0 for particle in self.particles: numerator += particle.numerator() denominator += particle.denominator() return numerator, denominator def integrand(self, t, y): """ ## Temperature equation integrand Master equation for the temperature looks like \begin{equation} \frac{d (aT)}{dx} = \frac{\sum_i{N_i}}{\sum_i{D_i}} \end{equation} Where $N_i$ and $D_i$ represent contributions from different particle species. See definitions for different regimes: * [[Radiation|particles/RadiationParticle.py#master-equation-terms]] * [[Intermediate|particles/IntermediateParticle.py#master-equation-terms]] * [[Dust|particles/DustParticle.py#master-equation-terms]] * [[Non-equilibrium|particles/NonEqParticle.py#master-equation-terms]] """ # 1\. Update particles states self.update_particles() # 2\. Initialize non-equilibrium interactions self.init_interactions() # 3\. Calculate collision integrals self.calculate_collisions() # 4\. Update particles distributions self.update_distributions() # 5\. Calculate temperature equation terms numerator, denominator = self.calculate_temperature_terms() if environment.get('LOGARITHMIC_TIMESTEP'): self.fraction = self.params.x * numerator / denominator else: self.fraction = numerator / denominator return self.fraction def save_params(self): self.data.append({ 'aT': self.params.aT, 'T': self.params.T, 'a': self.params.a, 'x': self.params.x, 'rho': self.params.rho, 'N_eff': self.params.N_eff, 't': self.params.t, 'fraction': self.fraction, 'S': self.params.S }) def save(self): """ Save current Universe parameters into the data arrays or output files """ self.save_params() if self.kawano and self.params.T <= self.kawano.T_kawano: # t[s] x Tg[10^9K] dTg/dt[10^9K/s] rho_tot[g cm^-3] H[s^-1] # n nue->p e p e->n nue n->p e nue p e nue->n n e->p nue p nue->n e rates = self.kawano.baryonic_rates(self.params.a) row = { self.kawano_data.columns[0]: self.params.t, self.kawano_data.columns[1]: self.params.x, self.kawano_data.columns[2]: self.params.T, self.kawano_data.columns[3]: (self.data['T'][-1] - self.data['T'][-2]) / (self.data['t'][-1] - self.data['t'][-2]), self.kawano_data.columns[4]: self.params.rho, self.kawano_data.columns[5]: self.params.H } row.update({self.kawano_data.columns[i]: rate for i, rate in enumerate(rates, 6)}) self.kawano_data.append(row) if self.log_throttler.output: print("KAWANO", self.kawano_data.row_repr(-1, names=True)) self.kawano_log.write(self.kawano_data.row_repr(-1) + "\n") def init_log(self, folder=''): self.logfile = utils.ensure_path(os.path.join(self.folder, 'log.txt')) sys.stdout = utils.Logger(self.logfile) def log(self): """ Runtime log output """ # Print parameters every now and then if self.log_throttler.output: print('[{clock}] #{step}\tt = {t:e} s\taT = {aT:e} MeV\tT = {T:e} MeV' '\tδaT/aT = {daT:e}\tS = {S:e} MeV^3' .format(clock=timedelta(seconds=int(time.time() - self.clock_start)), step=self.step, t=self.params.t / UNITS.s, aT=self.params.aT / UNITS.MeV, T=self.params.T / UNITS.MeV, daT=self.fraction * self.params.h / self.params.aT, S=self.params.S / UNITS.MeV**3)) def total_entropy(self): return sum(particle.entropy for particle in self.particles) * self.params.a**3 def total_energy_density(self): return sum(particle.energy_density for particle in self.particles)
from library.SM import particles as SMP from interactions import CrossGeneratingInteraction from interactions.four_particle import FourParticleM, FourParticleIntegral from particles import Particle from evolution import Universe from common import CONST, UNITS, Params params = Params(T=2 * UNITS.MeV, dy=0.025) photon = Particle(**SMP.photon) neutrino = Particle(**SMP.leptons.neutrino_e) neutrino_scattering = CrossGeneratingInteraction( particles=((neutrino, neutrino), (neutrino, neutrino)), antiparticles=((False, True), (False, True)), Ms=(FourParticleM(K1=64 * CONST.G_F**2, order=(0, 1, 2, 3)), ), integral_type=FourParticleIntegral) universe = Universe(params=params) universe.PARALLELIZE = False universe.add_particles([photon, neutrino]) universe.interactions += [neutrino_scattering] import numpy addition = numpy.vectorize(lambda x: 0.1 * numpy.exp(-(x / UNITS.MeV - 3)**2), otypes=[numpy.float_]) neutrino._distribution += addition(neutrino.grid.TEMPLATE) # def check(p=[]): # return neutrino_scattering.F_B(in_p=p[:2], out_p=p[2:]) * (1 - neutrino.distribution(p[0])) \ # - neutrino_scattering.F_A(in_p=p[:2], out_p=p[2:]) * neutrino.distribution(p[0])
[Log file](log.txt) """ import os from particles import Particle from evolution import Universe from common import Params, UNITS from library.SM import particles as SMP folder = os.path.join(os.path.split(__file__)[0], 'output') params = Params(T=10 * UNITS.MeV, dy=0.003125) T_final = 0.008 * UNITS.MeV Particles = [] photon = Particle(**SMP.photon) neutrino_e = Particle(**SMP.leptons.neutrino_e) neutrino_mu = Particle(**SMP.leptons.neutrino_mu) neutrino_tau = Particle(**SMP.leptons.neutrino_tau) electron = Particle(**SMP.leptons.electron) Particles += [ photon, neutrino_e, neutrino_mu, neutrino_tau,
class Universe(object): """ ## Universe The master object that governs the calculation. """ # System state is rendered to the evolution.txt file each `export_freq` steps export_freq = 100 log_throttler = None clock_start = None particles = None interactions = None kawano = None kawano_log = None oscillations = None step_monitor = None data = utils.DynamicRecArray([['aT', 'MeV', UNITS.MeV], ['T', 'MeV', UNITS.MeV], ['a', None, 1], ['x', 'MeV', UNITS.MeV], ['t', 's', UNITS.s], ['rho', 'MeV^4', UNITS.MeV**4], ['N_eff', None, 1], ['fraction', None, 1], ['S', 'MeV^3', UNITS.MeV**3]]) def __init__(self, folder=None, params=None, max_log_rate=2): """ :param folder: Log file path (current `datetime` by default) """ self.particles = [] self.interactions = [] self.clock_start = time.time() self.log_throttler = utils.Throttler(max_log_rate) self.params = params if not self.params: self.params = Params() self.folder = folder if self.folder: if os.path.exists(folder): shutil.rmtree(folder) self.init_log(folder=folder) self.fraction = 0 self.step = 1 def init_kawano(self, datafile='s4.dat', **kwargs): kawano.init_kawano(**kwargs) if self.folder: self.kawano_log = open(os.path.join(self.folder, datafile), 'w') self.kawano_log.write("\t".join([col[0] for col in kawano.heading]) + "\n") self.kawano = kawano self.kawano_data = utils.DynamicRecArray(self.kawano.heading) def oscillation_parameters(self): if self.oscillation_matter: MSW_12 = CONST.MSW_constant * self.oscillation_particles[ 0].grid.TEMPLATE**2 * self.params.T**4 / CONST.delta_m12_sq / self.params.a**2 MSW_13 = CONST.MSW_constant * self.oscillation_particles[ 0].grid.TEMPLATE**2 * self.params.T**4 / CONST.delta_m13_sq / self.params.a**2 if not environment.get("NORMAL_HIERARCHY_NEUTRINOS"): MSW_13 *= -1 else: MSW_12 = 0. MSW_13 = 0. pattern = self.pattern_function(MSW_12, MSW_13, self.oscillation_matter) self.oscillations = (pattern, self.oscillation_particles) def init_oscillations(self, pattern_function, particles, matter_effects=True): self.pattern_function = pattern_function self.oscillation_particles = particles self.oscillation_matter = matter_effects self.oscillation_parameters() def evolve(self, T_final, export=True, init_time=True): """ ## Main computing routine Modeling is carried in steps of scale factor, starting from the initial conditions defined\ by a single parameter: the initial temperature. Initial temperature (e.g. 10 MeV) corresponds to a point in the history of the Universe \ long before then BBN. Then most particle species are in the thermodynamical equilibrium. """ T_initial = self.params.T print("\n\n" + "#" * 32 + " Initial states " + "#" * 32 + "\n") for particle in self.particles: print(particle) print("\n\n" + "#" * 34 + " Log output " + "#" * 34 + "\n") for interaction in self.interactions: if interaction.integrals: print(interaction) print("\n") # TODO: test if changing updating particles beforehand changes the computed time if init_time: self.params.init_time(self.total_energy_density()) if self.params.rho is None: # self.update_particles() self.params.update(self.total_energy_density(), self.total_entropy()) self.save_params() while self.params.T > T_final: try: self.log() self.make_step() self.save() self.step += 1 if self.folder and self.step % self.export_freq == 0: with open(os.path.join(self.folder, "evolution.txt"), "wb") as f: self.data.savetxt(f) if self.kawano: with open(os.path.join(self.folder, "kawano.txt"), "wb") as f: self.kawano_data.savetxt(f) except KeyboardInterrupt: print("\nKeyboard interrupt!") sys.exit(1) break if not (self.params.T > 0): print("\n(T < 0): suspect numerical instability") sys.exit(1) self.log() if export: self.export() return self.data def export(self): print("\n\n" + "#" * 33 + " Final states " + "#" * 33 + "\n") for particle in self.particles: print(particle) print("\n") if self.folder: if self.kawano: self.kawano_log.close() print(kawano.run(self.folder)) with open(os.path.join(self.folder, "kawano.txt"), "wb") as f: self.kawano_data.savetxt(f) print("Execution log saved to file {}".format(self.logfile)) with open(os.path.join(self.folder, "evolution.txt"), "wb") as f: self.data.savetxt(f) def make_step(self): self.integrand(self.params.x, self.params.aT) if self.step_monitor: self.step_monitor(self) if environment.get('ADAMS_BASHFORTH_TEMPERATURE_CORRECTION'): fs = (list(self.data['fraction'][-MAX_ADAMS_BASHFORTH_ORDER:]) + [self.fraction]) self.params.aT += adams_bashforth_correction(fs=fs, h=self.params.h) else: self.params.aT += self.fraction * self.params.h self.params.x += self.params.dx self.params.update(self.total_energy_density(), self.total_entropy()) self.log_throttler.update() def add_particles(self, particles): for particle in particles: particle.set_params(self.params) self.particles += particles self.particles = utils.particle_orderer(self.particles) def update_particles(self): """ ### 1. Update particles state Update particle species distribution functions, check for regime switching,\ update precalculated variables like energy density and pressure. """ for particle in self.particles: particle.update() def init_interactions(self): """ ### 2. Initialize non-equilibrium interactions Non-equilibrium interactions of different particle species are treated by a\ numerical integration of the Boltzmann equation for distribution functions in\ the expanding space-time. Depending on the regime of the particle species involved and cosmological parameters, \ each `Interaction` object populates `Particle.collision_integrals` array with \ currently active `Integral` objects. """ for interaction in self.interactions: interaction.initialize() def calculate_collisions(self): """ ### 3. Calculate collision integrals """ particles = [ particle for particle in self.particles if particle.collision_integrals ] with utils.printoptions(precision=3, linewidth=100): for particle in particles: # with (utils.benchmark(lambda: "δf/f ({}) = {}".format(particle.symbol, particle.collision_integral / particle._distribution * self.params.h), # self.log_throttler.output)): particle.collision_integral = particle.integrate_collisions() def update_distributions(self): """ ### 4. Update particles distributions """ if self.oscillations: self.oscillation_parameters() pattern, particles = self.oscillations if any(self.params.T < A.decoupling_temperature for A in particles): integrals = { A.flavour: A.collision_integral for A in particles } for A in particles: A.collision_integral = sum( pattern[(A.flavour, B.flavour)] * integrals[B.flavour] for B in particles) for particle in self.particles: particle.update_distribution() def calculate_temperature_terms(self): """ ### 5. Calculate temperature equation terms """ numerator = 0 denominator = 0 for particle in self.particles: numerator += particle.numerator() denominator += particle.denominator() return numerator, denominator def integrand(self, t, y): """ ## Temperature equation integrand Master equation for the temperature looks like \begin{equation} \frac{d (aT)}{dx} = \frac{\sum_i{N_i}}{\sum_i{D_i}} \end{equation} Where $N_i$ and $D_i$ represent contributions from different particle species. See definitions for different regimes: * [[Radiation|particles/RadiationParticle.py#master-equation-terms]] * [[Intermediate|particles/IntermediateParticle.py#master-equation-terms]] * [[Dust|particles/DustParticle.py#master-equation-terms]] * [[Non-equilibrium|particles/NonEqParticle.py#master-equation-terms]] """ # 1\. Update particles states self.update_particles() # 2\. Initialize non-equilibrium interactions self.init_interactions() # 3\. Calculate collision integrals self.calculate_collisions() # 4\. Update particles distributions self.update_distributions() # 5\. Calculate temperature equation terms numerator, denominator = self.calculate_temperature_terms() if environment.get('LOGARITHMIC_TIMESTEP'): self.fraction = self.params.x * numerator / denominator else: self.fraction = numerator / denominator return self.fraction def save_params(self): self.data.append({ 'aT': self.params.aT, 'T': self.params.T, 'a': self.params.a, 'x': self.params.x, 'rho': self.params.rho, 'N_eff': self.params.N_eff, 't': self.params.t, 'fraction': self.fraction, 'S': self.params.S }) def save(self): """ Save current Universe parameters into the data arrays or output files """ self.save_params() if self.kawano and self.params.T <= self.kawano.T_kawano: # t[s] x Tg[10^9K] dTg/dt[10^9K/s] rho_tot[g cm^-3] H[s^-1] # n nue->p e p e->n nue n->p e nue p e nue->n n e->p nue p nue->n e rates = self.kawano.baryonic_rates(self.params.a) if environment.get('LOGARITHMIC_TIMESTEP'): dTdt = (self.fraction - self.params.aT) * self.params.H / self.params.a else: dTdt = (self.fraction - self.params.T / self.params.m) * self.params.H * self.params.m row = { self.kawano_data.columns[0]: self.params.t, self.kawano_data.columns[1]: self.params.x, self.kawano_data.columns[2]: self.params.T, self.kawano_data.columns[3]: dTdt, self.kawano_data.columns[4]: self.params.rho, self.kawano_data.columns[5]: self.params.H } row.update({ self.kawano_data.columns[i]: rate for i, rate in enumerate(rates, 6) }) self.kawano_data.append(row) if self.log_throttler.output: print("KAWANO", self.kawano_data.row_repr(-1, names=True)) self.kawano_log.write(self.kawano_data.row_repr(-1) + "\n") def init_log(self, folder=''): self.logfile = utils.ensure_path(os.path.join(self.folder, 'log.txt')) sys.stdout = utils.Logger(self.logfile) def log(self): """ Runtime log output """ # Print parameters every now and then if self.log_throttler.output: print( '[{clock}] #{step}\tt = {t:e} s\taT = {aT:e} MeV\tT = {T:e} MeV' '\tδaT/aT = {daT:e}\tS = {S:e} MeV^3'.format( clock=timedelta(seconds=int(time.time() - self.clock_start)), step=self.step, t=self.params.t / UNITS.s, aT=self.params.aT / UNITS.MeV, T=self.params.T / UNITS.MeV, daT=self.fraction * self.params.h / self.params.aT, S=self.params.S / UNITS.MeV**3)) def total_entropy(self): return sum(particle.entropy for particle in self.particles) #* (self.params.a_ini/self.params.a)**3 def total_energy_density(self): return sum(particle.energy_density for particle in self.particles) def QCD_transition(self, hadrons=None, quarkic_interactions=None, hadronic_interactions=None, secondary_interactions=None): self.data.truncate() dof_before_qcd = Params.entropic_dof_eq(self) self.particles = utils.particle_filter([ 'Up quark', 'Down quark', 'Charm quark', 'Strange quark', 'Top quark', 'Bottom quark', 'Gluon' ], self.particles) self.add_particles(hadrons) dof_after_qcd = Params.entropic_dof_eq(self) self.params.aT = self.data['aT'][-1] * (dof_before_qcd / dof_after_qcd)**(1 / 3) # self.params.x = self.data['x'][-1] * (dof_before_qcd/dof_after_qcd)**(1/3) # self.params.a = self.data['a'][-1] * (dof_before_qcd/dof_after_qcd)**(1/3) self.params.update(self.total_energy_density(), self.total_entropy()) # T will be updated here... # self.params.T = CONST.lambda_QCD # That's why we change it here again self.update_particles() if quarkic_interactions and self.interactions: for inter in quarkic_interactions: self.interactions.remove(inter) if hadronic_interactions: self.interactions += (hadronic_interactions) if secondary_interactions: self.interactions += (secondary_interactions)