MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
cooling_qso_lightup.c
Go to the documentation of this file.
1 
21 /*
22  * 1. Use a BH accretion rate threshold rather than a BH mass threshold.
23  * 2. Bubble size correlates with black hole mass.
24  *
25  * 3. Add a switch to place bubbles at random in massive halos if there are no black holes.
26  * 4. Bubble size correlates with halo mass.
27  */
28 
29 #include <math.h>
30 #include <mpi.h>
31 #include <string.h>
32 #include <omp.h>
33 #include <gsl/gsl_interp.h>
34 #include "physconst.h"
35 #include "slotsmanager.h"
36 #include "partmanager.h"
37 #include "treewalk.h"
38 #include "hydra.h"
39 #include "drift.h"
40 #include "walltime.h"
41 #include "fof.h"
42 #include "utils/endrun.h"
43 #include "utils/paramset.h"
44 #include "utils/mymalloc.h"
45 #include "cooling_qso_lightup.h"
46 
47 #define E0_HeII 54.4 /* HeII ionization potential in eV*/
48 #define HEMASS 4.002602 /* Helium mass in amu*/
49 
50 typedef struct
51 {
55 
56 /*Parameters for the quasar driven helium reionization model.*/
58 {
59  int QSOLightupOn; /* Master flag enabling the helium reioization heating model.*/
60 
61  double qso_candidate_min_mass; /* Minimum mass of a quasar halo candidate.
62  To become a quasar a FOF group should have a mass between min and max. */
63  double qso_candidate_max_mass; /* Minimum mass of a quasar halo candidate.*/
64 
65  double mean_bubble; /* Mean size of the quasar bubble.*/
66  double var_bubble; /* Variance of the quasar bubble size.*/
67 
68  double heIIIreion_finish_frac; /* When the desired ionization fraction exceeds this value,
69  the code will flash-ionize all remaining particles*/
70  double heIIIreion_start; /* Time at which start_reionization is called and helium III reionization begins*/
71 };
72 
74 /* Memory for the helium reionization history. */
75 /* Instantaneous heating from low-energy (short mean free path)
76  * photons to the Quasar to a newly ionized particle.
77  * Computed from parameters stored in the text file.
78  * In ergs.*/
79 static double qso_inst_heating;
80 static int Nreionhist;
81 static double * He_zz;
82 static double * XHeIII;
83 static double * LMFP;
84 static gsl_interp * HeIII_intp;
85 static gsl_interp * LMFP_intp;
86 
87 /*This is a helper for the tests*/
89 {
90  QSOLightupParams = qso;
91 }
92 
93 void
95 {
96  int ThisTask;
97  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
98  if(ThisTask == 0) {
99  QSOLightupParams.QSOLightupOn = param_get_int(ps, "QSOLightupOn");
102  QSOLightupParams.mean_bubble = param_get_double(ps, "QSOMeanBubble");
103  QSOLightupParams.var_bubble = param_get_double(ps, "QSOVarBubble");
104  QSOLightupParams.heIIIreion_finish_frac = param_get_double(ps, "QSOHeIIIReionFinishFrac");
105  }
106  MPI_Bcast(&QSOLightupParams, sizeof(struct qso_lightup_params), MPI_BYTE, 0, MPI_COMM_WORLD);
107 }
108 
109 /* Instantaneous heat injection from HeII reionization
110  * (absorption of photons with E<Emax) per helium atom [ergs]
111  */
112 static double
113 Q_inst(double Emax, double alpha_q)
114 {
115  /* Total ionizing flux for the short mean free path photons*/
116  double intflux = (pow(Emax,-alpha_q+1.)-pow(E0_HeII,-alpha_q+1.))/(pow(Emax,-alpha_q)- pow(E0_HeII,-alpha_q));
117  /* Heating is input per unit mass, so quasars in denser areas will provide more heating.*/
118  double Q_inst = (alpha_q/(alpha_q - 1.0))*intflux -E0_HeII;
119  return eVinergs * Q_inst;
120 }
121 
122 /* Load in reionization history file and build the interpolators between redshift and XHeII.
123  * Format is:
124  * Need to load a text file with the shape of reionization, which gives the overall HeIII fraction,
125  * and the uniform background heating rate. Includes quasar spectral index, etc and most of the physics.
126  * Be careful. Do not double-count uniform long mean free path photons with the homogeneous UVB.
127  *
128  * quasar spectral index
129  * instantaneous absorption threshold energy (in eV)
130  * table of 3 columns, redshift, HeIII fraction, uniform background heating.
131  * The text file specifies the end redshift of reionization.
132  * To generate a helium reionization history file, use tools/HeII_input_file_maker.py
133  * and see the documentation to that file (tools/README_HeII_input_file_maker.py)
134  * An example may be found in examples/HeIIReionizationTable
135  * */
136 static void
137 load_heii_reion_hist(const char * reion_hist_file)
138 {
139  int ThisTask;
140  FILE * fd = NULL;
141  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
142 
143  message(0, "HeII: Loading HeII reionization history from file: %s\n",reion_hist_file);
144  if(ThisTask == 0) {
145  fd = fopen(reion_hist_file, "r");
146  if(!fd)
147  endrun(456, "HeII: Could not open reionization history file at: '%s'\n", reion_hist_file);
148 
149  /*Find size of file*/
150  Nreionhist = 0;
151  while(1)
152  {
153  char buffer[1024];
154  char * retval = fgets(buffer, 1024, fd);
155  /*Happens on end of file*/
156  if(!retval)
157  break;
158  retval = strtok(buffer, " \t\n");
159  /*Discard comments*/
160  if(!retval || retval[0] == '#')
161  continue;
162  Nreionhist++;
163  }
164  rewind(fd);
165  /* Discard first two lines*/
166  Nreionhist -=2;
167  }
168 
169  MPI_Bcast(&Nreionhist, 1, MPI_INT, 0, MPI_COMM_WORLD);
170 
171  if(Nreionhist<= 2)
172  endrun(1, "HeII: Reionization history contains: %d entries, not enough.\n", Nreionhist);
173 
174  /*Allocate memory for the reionization history table.*/
175  He_zz = (double *) mymalloc("ReionizationTable", 3 * Nreionhist * sizeof(double));
176  XHeIII = He_zz + Nreionhist;
177  LMFP = He_zz + 2 * Nreionhist;
178 
179  if(ThisTask == 0)
180  {
181  double qso_spectral_index = 0, photon_threshold_energy = 0;
182  int prei = 0;
183  int i = 0;
184  while(i < Nreionhist)
185  {
186  char buffer[1024];
187  char * line = fgets(buffer, 1024, fd);
188  /*Happens on end of file*/
189  if(!line)
190  break;
191  char * retval = strtok(line, " \t\n");
192  if(!retval || retval[0] == '#')
193  continue;
194  if(prei == 0)
195  {
196  qso_spectral_index = atof(retval);
197  prei++;
198  continue;
199  }
200  else if(prei == 1)
201  {
202  photon_threshold_energy = atof(retval);
203  prei++;
204  continue;
205  }
206  /* First column: redshift. Convert to scale factor so it is increasing.*/
207  He_zz[i] = 1./(1+atof(retval));
208  /* Second column: HeIII fraction.*/
209  retval = strtok(NULL, " \t");
210  if(!retval)
211  endrun(12, "HeII: Line %s of reionization table was incomplete!\n", line);
212  XHeIII[i] = atof(retval);
213  /* Third column: long mean free path photons.*/
214  retval = strtok(NULL, " \t");
215  if(!retval)
216  endrun(12, "HeII: Line %s of reionization table was incomplete!\n", line);
217  LMFP[i] = atof(retval);
218  i++;
219  }
220  fclose(fd);
221  qso_inst_heating = Q_inst(photon_threshold_energy, qso_spectral_index);
222  }
223  /*Broadcast data to other processors*/
224  MPI_Bcast(He_zz, 3 * Nreionhist, MPI_DOUBLE, 0, MPI_COMM_WORLD);
225  MPI_Bcast(&qso_inst_heating, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
226  /* Initialize the interpolators*/
227  HeIII_intp = gsl_interp_alloc(gsl_interp_linear,Nreionhist);
228  LMFP_intp = gsl_interp_alloc(gsl_interp_linear,Nreionhist);
229  gsl_interp_init(HeIII_intp, He_zz, XHeIII, Nreionhist);
230  gsl_interp_init(LMFP_intp, He_zz, LMFP, Nreionhist);
231 
233 
234  message(0, "HeII: Read %d lines z_reion = %g - %g from file %s\n", Nreionhist, 1/He_zz[0] -1, 1/He_zz[Nreionhist-1]-1, reion_hist_file);
235 }
236 
237 void
238 init_qso_lightup(char * reion_hist_file)
239 {
241  load_heii_reion_hist(reion_hist_file);
242 }
243 
244 static double last_zz;
245 static double last_long_mfp_heating;
246 
247 /* Get the long mean free path heating. in erg/s/cm^3 */
248 double
250 {
252  return 0;
253  if(redshift > QSOLightupParams.heIIIreion_start)
254  return 0;
255  if(redshift == last_zz)
256  return last_long_mfp_heating;
257  double atime = 1/(1+redshift);
258 
259  /* Guard against the end of the table*/
260  if(atime > He_zz[Nreionhist-1])
261  return 0;
262 
263  double long_mfp_heating = gsl_interp_eval(LMFP_intp, He_zz, LMFP, atime, NULL);
264 
265  last_zz = redshift;
266  last_long_mfp_heating = long_mfp_heating;
267  return long_mfp_heating;
268 }
269 
270 /* This function gets a random number from a Gaussian distribution using the Box-Muller transform.*/
271 static double gaussian_rng(double mu, double sigma, const int64_t seed)
272 {
273  double u1 = get_random_number(seed);
274  double u2 = get_random_number(seed + 1);
275  double z1 = sqrt(-2 * log(u1) ) * cos(2 * M_PI * u2);
276  return mu + sigma * z1;
277 }
278 
279 /* Build a list of halos which are candidates for becoming a quasar.
280  * We use only halos with the right mass range.*/
281 static int
282 build_qso_candidate_list(int ** qso_cand, FOFGroups * fof)
283 {
284  /*Loop over all halos, building the candidate list.*/
285  int i, ncand=0;
286  *qso_cand = (int *) mymalloc("Quasar_candidates", sizeof(int) * (fof->Ngroups+1));
287  for(i = 0; i < fof->Ngroups; i++)
288  {
289  /* Check that it has the right mass*/
291  continue;
293  continue;
294  /*Add to the candidate list*/
295  (*qso_cand)[ncand] = i;
296  ncand++;
297  }
298  /*Poison value at the end for safety.*/
299  (*qso_cand)[ncand] = -1;
300  *qso_cand = (int *) myrealloc(*qso_cand, (ncand+1) * sizeof(int));
301  return ncand;
302 }
303 
304 /* Count the number of halos present on all tasks, and the number of halos present on tasks
305  * earlier than this one, using an Allgather. Returns the number of halos present on tasks before this one
306  * and sets ncand_tot.
307  */
308 static int
309 count_QSO_halos(int ncand, int64_t * ncand_tot, MPI_Comm Comm)
310 {
311  int64_t ncand_total = 0, ncand_before = 0;
312  int NTask, i, ThisTask;
313  MPI_Comm_size(Comm, &NTask);
314  MPI_Comm_rank(Comm, &ThisTask);
315  int * candcounts = (int*) ta_malloc("qso_cand_counts", int, NTask);
316 
317  /* Get how many candidates are on each processor. TODO: Make a generic MPIU for this.*/
318  MPI_Allgather(&ncand, 1, MPI_INT, candcounts, 1, MPI_INT, Comm);
319 
320  for(i = 0; i < NTask; i++)
321  {
322  if(i < ThisTask)
323  ncand_before += candcounts[i];
324  ncand_total += candcounts[i];
325  }
326 
327  ta_free(candcounts);
328  *ncand_tot = ncand_total;
329  return ncand_before;
330 }
331 
332 /* Choose a FOF halo at random to host a quasar
333  * This function chooses a single quasar from the total candidate list of quasars on *all* processors.
334  * We seed the random number generator off the number of existing quasars.
335  * This is done carefully so that we get the same sequence of quasars irrespective of how many processors we are using.
336  *
337  * Returns: the local index of the halo in FOF if the halo is hosted on this rank, -1 if the halo is not hosted on this rank
338  */
339 static int
340 choose_QSO_halo(int64_t ncand, int64_t * ncand_before, int64_t * ncand_tot, int64_t randseed)
341 {
342  double drand = get_random_number(randseed);
343  int64_t qso = drand * (*ncand_tot);
344  (*ncand_tot)--;
345  /* No quasar on this processor*/
346  if(qso < *ncand_before)
347  (*ncand_before)--;
348  if(qso < *ncand_before || qso >= *ncand_before + ncand)
349  return -1;
350 
351  /* If the quasar is on this processor, return the
352  * index of the quasar in the current candidate array.*/
353  return qso - *ncand_before;
354 }
355 
356 /* Calculates the total ionization fraction of the box.
357  */
358 static double
360 {
361  int64_t n_ionized_tot = 0, n_gas_tot = 0;
362  int i, n_ionized = 0;
363  #pragma omp parallel for reduction(+:n_ionized)
364  for (i = 0; i < PartManager->NumPart; i++){
365  if (P[i].Type == 0 && P[i].HeIIIionized == 1){
366  n_ionized ++;
367  }
368  }
369  /* Get total ionization fraction: note this is only the current gas particles.
370  * Particles that become stars are not counted.*/
371  sumup_large_ints(1, &n_ionized, &n_ionized_tot);
372  MPI_Allreduce(&SlotsManager->info[0].size, &n_gas_tot, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
373  return (double) n_ionized_tot / (double) n_gas_tot;
374 }
375 
376 /* Do the ionization for a single particle, marking it and adding the heat.
377  * No locking is performed so ensure the particle is not being edited in parallel.
378  * Returns 1 if ionization was done, 0 otherwise.*/
379 static int
380 ionize_single_particle(int other, double a3inv, double uu_in_cgs)
381 {
382  /* Mark it ionized if not done so already.*/
383  int done;
384  #pragma omp atomic capture
385  {
386  done = P[other].HeIIIionized;
387  P[other].HeIIIionized = 1;
388  }
389  if(done)
390  return 0;
391 
392  /* Heat the particle*/
393 
394  /* Number of helium atoms per g in the particle*/
395  double nheperg = (1 - HYDROGEN_MASSFRAC) / (PROTONMASS * HEMASS);
396  /* Total heating per unit mass ergs/g for the particle*/
397  double deltau = qso_inst_heating * nheperg;
398 
399  /* Conversion factor between internal energy and entropy.*/
400  double entropytou = pow(SPHP(other).Density * a3inv, GAMMA_MINUS1) / GAMMA_MINUS1;
401  /* Convert to entropy in internal units*/
402  /* Only one thread may get here*/
403  SPHP(other).Entropy += deltau / uu_in_cgs / entropytou;
404  return 1;
405 }
406 
407 struct QSOPriv {
409  int64_t * N_ionized;
410  double a3inv;
411  double uu_in_cgs;
412 };
413 #define QSO_GET_PRIV(tw) ((struct QSOPriv *) (tw->priv))
414 
418 static void
420  TreeWalkResultBase * O,
421  TreeWalkNgbIterBase * iter,
422  LocalTreeWalk * lv)
423 {
424 
425  if(iter->other == -1) {
426  /* Gas only ( 1 == 1 << 0, the bit for type 0)*/
427  iter->mask = 1;
428  /* Bubble size*/
430  iter->Hsml = bubble;
431  /* Don't care about gas HSML */
433  return;
434  }
435 
436  int other = iter->other;
437 
438  /* Only ionize gas*/
439  if(P[other].Type != 0)
440  return;
441 
442  int ionized = ionize_single_particle(other, QSO_GET_PRIV(lv->tw)->a3inv, QSO_GET_PRIV(lv->tw)->uu_in_cgs);
443 
444  if(!ionized)
445  return;
446 
447  int tid = omp_get_thread_num();
448  /* Add to the ionization counter for this thread*/
449  QSO_GET_PRIV(lv->tw)->N_ionized[tid] ++;
450 }
451 
452 static void
454 {
455  int k;
456  FOFGroups * fof = QSO_GET_PRIV(tw)->fof;
457  /* Strictly speaking this is inefficient:
458  * we are also copying the properties of the *particle*
459  * in place in treewalk.c. However, this does not matter unless
460  * there are more local groups than particles!*/
461  for(k = 0; k < 3; k++)
462  {
463  I->base.Pos[k] = fof->Group[place].CM[k];
464  }
465  I->ID = fof->Group[place].base.MinID;
466 }
467 
468 /* Find all particles within the radius of the HeIII bubble,
469  * flag each particle as ionized and add instantaneous heating.
470  * Returns the number of particles ionized
471  */
472 static int64_t
473 ionize_all_part(int qso_ind, int * qso_cand, struct QSOPriv priv, ForceTree * tree)
474 {
475  /* This treewalk finds not yet ionized particles within the radius of the black hole, ionizes them and
476  * adds an instantaneous heating to them. */
477  TreeWalk tw[1] = {{0}};
478 
479  tw->ev_label = "HELIUM";
480  /* This could select the black holes to be made quasars, but we do it below.*/
481  tw->haswork = NULL;
482  tw->tree = tree;
483 
484  /* We set Hsml to a constant in ngbiter, so this
485  * searches a constant distance from the halo.*/
489 
491  tw->reduce = NULL;
492  tw->postprocess = NULL;
495 
496  priv.N_ionized = ta_malloc("n_ionized", int64_t, omp_get_max_threads());
497  memset(priv.N_ionized, 0, sizeof(int64_t) * omp_get_max_threads());
498  tw->priv = &priv;
499 
500  /* This runs only on one BH*/
501  if(qso_ind > 0)
502  treewalk_run(tw, &qso_cand[qso_ind], 1);
503  else
504  treewalk_run(tw, NULL, 0);
505 
506  int64_t N_ionized = 0;
507  int i;
508  for(i = 0; i < omp_get_max_threads(); i++)
509  N_ionized += priv.N_ionized[i];
510 
511  ta_free(priv.N_ionized);
512 
513  return N_ionized;
514 }
515 
516 /* Sequentially turns on quasars.
517  * Keeps adding new quasars until need_more_quasars() returns 0.
518  */
519 static void
520 turn_on_quasars(double atime, FOFGroups * fof, DomainDecomp * ddecomp, Cosmology * CP, double uu_in_cgs, FILE * FdHelium)
521 {
522  int ncand = 0;
523  int * qso_cand = NULL;
524  int64_t n_gas_tot=0, tot_n_ionized=0, ncand_tot=0;
525  MPI_Allreduce(&SlotsManager->info[0].size, &n_gas_tot, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
526  double desired_ion_frac = gsl_interp_eval(HeIII_intp, He_zz, XHeIII, atime, NULL);
527  struct QSOPriv priv;
528  priv.fof = fof;
529  priv.uu_in_cgs = uu_in_cgs;
530  priv.a3inv = 1/pow(atime, 3);
531 
532  /* If the desired ionization fraction is above a threshold (by default 0.95)
533  * ionize all particles*/
534  if(desired_ion_frac > QSOLightupParams.heIIIreion_finish_frac) {
535  int i, nionized=0;
536  int64_t nion_tot=0;
537  #pragma omp parallel for reduction(+: nionized)
538  for (i = 0; i < PartManager->NumPart; i++){
539  if (P[i].Type == 0)
540  nionized += ionize_single_particle(i, priv.a3inv, priv.uu_in_cgs);
541  }
542  sumup_large_ints(1, &nionized, &nion_tot);
543  message(0, "HeII: Helium ionization finished, flash-ionizing %ld particles (%g of total)\n", nion_tot, (double) nion_tot /(double) n_gas_tot);
544  }
545 
546  double rhobar = CP->OmegaBaryon * (3 * HUBBLE * CP->HubbleParam * HUBBLE * CP->HubbleParam)/ (8 * M_PI * GRAVITY) * priv.a3inv;
547  double totbubblegasmass = 4 * M_PI / 3. * pow(QSOLightupParams.mean_bubble, 3) * rhobar;
548  /* Total expected ionizations if the bubbles do not overlap at all
549  * and the bubble is at mean density.*/
550  int64_t non_overlapping_bubble_number = n_gas_tot * totbubblegasmass / CP->OmegaBaryon;
551  double initionfrac = gas_ionization_fraction();
552  double curionfrac = initionfrac;
553 
554  message(0, "HeII: Started helium reionization model with ionization fraction %g\n", initionfrac);
555  if(curionfrac < desired_ion_frac) {
556  ncand = build_qso_candidate_list(&qso_cand, fof);
557  walltime_measure("/HeIII/Find");
558  }
559 
560  int64_t ncand_before = count_QSO_halos(ncand, &ncand_tot, MPI_COMM_WORLD);
561  int iteration;
562 
563  /* If there are no quasars this will be tough*/
564  if(ncand_tot == 0) {
565  if(qso_cand)
566  myfree(qso_cand);
567  return;
568  }
569  message(0, "HeII: Built quasar candidate list from %d quasars\n", ncand_tot);
570  ForceTree gasTree = {0};
571  force_tree_rebuild_mask(&gasTree, ddecomp, GASMASK, 0, NULL);
572  for(iteration = 0; curionfrac < desired_ion_frac; iteration++){
573  /* Get a new quasar*/
574  int new_qso = choose_QSO_halo(ncand, &ncand_before, &ncand_tot, fof->TotNgroups+iteration);
575  if(new_qso >= ncand)
576  endrun(12, "HeII: QSO %d > no. candidates %d! Cannot happen\n", new_qso, ncand);
577  /* Make sure someone has a quasar*/
578  if(ncand_tot <= 0) {
579  if(desired_ion_frac - curionfrac > 0.1)
580  message(0, "HeII: Ionization fraction %g less than desired ionization fraction of %g because not enough quasars\n", curionfrac, desired_ion_frac);
581  break;
582  }
583 
584  int64_t n_ionized = ionize_all_part(new_qso, qso_cand, priv, &gasTree);
585  int64_t tot_qso_ionized = 0;
586  /* Check that the ionization fraction changed*/
587  MPI_Allreduce(&n_ionized, &tot_qso_ionized, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
588  curionfrac += (double) tot_qso_ionized / (double) n_gas_tot;
589  tot_n_ionized += tot_qso_ionized;
590  /* Get the quasar position on rank 0*/
591  double qso_pos[3] = {0};
592  if(new_qso > 0) {
593  int qplace = qso_cand[new_qso];
594  int k;
595  for(k = 0; k < 3; k++) {
596  qso_pos[k] = fof->Group[qplace].CM[k]- PartManager->CurrentParticleOffset[k];
597  while(qso_pos[k] > PartManager->BoxSize) qso_pos[k] -= PartManager->BoxSize;
598  while(qso_pos[k] <= 0) qso_pos[k] += PartManager->BoxSize;
599  }
600  message(1, "HeII: Quasar %d changed the HeIII ionization fraction to %g, ionizing %ld\n", qplace, curionfrac, tot_qso_ionized);
601  }
602  /* Format: Time = current scale factor,
603  * ID of the quasar (the index of the FOF halo)
604  * FOF halo position, x,y,z,
605  * Current ionized fraction
606  * total number of particles ionized by this quasar*/
607  MPI_Allreduce(MPI_IN_PLACE, qso_pos, 3, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
608  if(FdHelium) {
609  fprintf(FdHelium, "%g %g %g %g %g %ld\n", atime, qso_pos[0], qso_pos[1], qso_pos[2], curionfrac, tot_qso_ionized);
610  fflush(FdHelium);
611  }
612 
613  /* Break the loop if we do not ionize enough particles this round.
614  * Try again next timestep when we will hopefully have new BHs.*/
615  if(tot_qso_ionized < 0.01 * non_overlapping_bubble_number && iteration > 10) {
616  message(0, "HeII: Stopping ionization at iteration %d because insufficient ionization happened.\n", iteration);
617  break;
618  }
619 
620  /* Remove this candidate from the list by moving the list down.*/
621  if( new_qso >= 0) {
622  memmove(qso_cand+new_qso, qso_cand+new_qso+1, ncand - new_qso+1);
623  ncand--;
624  }
625  }
626  force_tree_free(&gasTree);
627  if(qso_cand) {
628  myfree(qso_cand);
629  }
630 
631  if(tot_n_ionized > 0)
632  message(0, "HeII: HeIII fraction from %g -> %g, ionizing %ld. Wanted %g.\n", initionfrac, curionfrac, tot_n_ionized, desired_ion_frac);
633  else
634  message(0, "HeII: HeIII fraction unchanged at %g. Wanted %g\n", curionfrac, desired_ion_frac);
635  walltime_measure("/HeIII/Ionize");
636 }
637 
638 /* Starts reionization by selecting the first halo and flagging all particles in the first HeIII bubble*/
639 void
640 do_heiii_reionization(double atime, FOFGroups * fof, DomainDecomp * ddecomp, Cosmology * CP, double uu_in_cgs, FILE * FdHelium)
641 {
643  return;
644  if(atime < 1/(1+QSOLightupParams.heIIIreion_start))
645  return;
646 
647  /* Do nothing if we are past the end of the table.*/
648  if(atime > He_zz[Nreionhist-1])
649  return;
650 
651  walltime_measure("/Misc");
652  //message(0, "HeII: Reionization initiated.\n");
653  turn_on_quasars(atime, fof, ddecomp, CP, uu_in_cgs, FdHelium);
654 }
655 
656 int
658 {
659  double desired_ion_frac = gsl_interp_eval(HeIII_intp, He_zz, XHeIII, atime, NULL);
660  double curionfrac = gas_ionization_fraction();
661  if(curionfrac < desired_ion_frac)
662  return 1;
663  return 0;
664 }
665 
666 int
668 {
670  return 0;
671  if(redshift > QSOLightupParams.heIIIreion_start)
672  return 0;
673 
674  /* Past the end of the table, it has finished.*/
675  if(redshift < 1./He_zz[Nreionhist-1] - 1)
676  return 0;
677 
678  return 1;
679 }
680 
681 int
683 {
685 }
static double * He_zz
static void turn_on_quasars(double atime, FOFGroups *fof, DomainDecomp *ddecomp, Cosmology *CP, double uu_in_cgs, FILE *FdHelium)
double get_long_mean_free_path_heating(double redshift)
static double Q_inst(double Emax, double alpha_q)
void set_qso_lightup_params(ParameterSet *ps)
#define E0_HeII
static void load_heii_reion_hist(const char *reion_hist_file)
static double * XHeIII
#define HEMASS
static double last_long_mfp_heating
static int Nreionhist
static void ionize_ngbiter(TreeWalkQueryQSOLightup *I, TreeWalkResultBase *O, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
void do_heiii_reionization(double atime, FOFGroups *fof, DomainDecomp *ddecomp, Cosmology *CP, double uu_in_cgs, FILE *FdHelium)
static int build_qso_candidate_list(int **qso_cand, FOFGroups *fof)
int need_change_helium_ionization_fraction(double atime)
static double gaussian_rng(double mu, double sigma, const int64_t seed)
void init_qso_lightup(char *reion_hist_file)
static int choose_QSO_halo(int64_t ncand, int64_t *ncand_before, int64_t *ncand_tot, int64_t randseed)
static gsl_interp * LMFP_intp
static double last_zz
int qso_lightup_on(void)
static int ionize_single_particle(int other, double a3inv, double uu_in_cgs)
static int count_QSO_halos(int ncand, int64_t *ncand_tot, MPI_Comm Comm)
static double * LMFP
int during_helium_reionization(double redshift)
static double qso_inst_heating
static struct qso_lightup_params QSOLightupParams
static double gas_ionization_fraction(void)
static gsl_interp * HeIII_intp
void set_qso_lightup_par(struct qso_lightup_params qso)
static int64_t ionize_all_part(int qso_ind, int *qso_cand, struct QSOPriv priv, ForceTree *tree)
static void ionize_copy(int place, TreeWalkQueryQSOLightup *I, TreeWalk *tw)
#define QSO_GET_PRIV(tw)
double sigma[3]
Definition: densitykernel.c:97
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147
void force_tree_rebuild_mask(ForceTree *tree, DomainDecomp *ddecomp, int mask, const int HybridNuGrav, const char *EmergencyOutputDir)
Definition: forcetree.c:161
void force_tree_free(ForceTree *tree)
Definition: forcetree.c:1409
#define GASMASK
Definition: forcetree.h:21
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define ta_malloc(name, type, nele)
Definition: mymalloc.h:25
#define myrealloc(ptr, size)
Definition: mymalloc.h:18
#define ta_free(p)
Definition: mymalloc.h:28
#define myfree(x)
Definition: mymalloc.h:19
double param_get_double(ParameterSet *ps, const char *name)
Definition: paramset.c:336
int param_get_int(ParameterSet *ps, const char *name)
Definition: paramset.c:368
struct part_manager_type PartManager[1]
Definition: partmanager.c:11
#define P
Definition: partmanager.h:88
#define eVinergs
Definition: physconst.h:15
#define HUBBLE
Definition: physconst.h:25
#define HYDROGEN_MASSFRAC
Definition: physconst.h:37
#define GRAVITY
Definition: physconst.h:5
#define GAMMA_MINUS1
Definition: physconst.h:35
#define PROTONMASS
Definition: physconst.h:21
static Cosmology * CP
Definition: power.c:27
struct slots_manager_type SlotsManager[1]
Definition: slotsmanager.c:7
#define SPHP(i)
Definition: slotsmanager.h:124
MyIDType MinID
Definition: fof.h:18
double HubbleParam
Definition: cosmology.h:20
double OmegaBaryon
Definition: cosmology.h:19
Definition: fof.h:62
struct Group * Group
Definition: fof.h:63
int Ngroups
Definition: fof.h:66
int64_t TotNgroups
Definition: fof.h:67
struct BaseGroup base
Definition: fof.h:27
double CM[3]
Definition: fof.h:34
double Mass
Definition: fof.h:31
TreeWalk * tw
Definition: treewalk.h:47
int64_t * N_ionized
FOFGroups * fof
enum NgbTreeFindSymmetric symmetric
Definition: treewalk.h:37
double Pos[3]
Definition: treewalk.h:23
void * priv
Definition: treewalk.h:85
TreeWalkHasWorkFunction haswork
Definition: treewalk.h:100
size_t ngbiter_type_elsize
Definition: treewalk.h:97
const ForceTree * tree
Definition: treewalk.h:88
TreeWalkProcessFunction postprocess
Definition: treewalk.h:104
TreeWalkNgbIterFunction ngbiter
Definition: treewalk.h:103
TreeWalkReduceResultFunction reduce
Definition: treewalk.h:102
const char * ev_label
Definition: treewalk.h:91
TreeWalkVisitFunction visit
Definition: treewalk.h:99
size_t result_type_elsize
Definition: treewalk.h:96
TreeWalkFillQueryFunction fill
Definition: treewalk.h:101
size_t query_type_elsize
Definition: treewalk.h:95
double CurrentParticleOffset[3]
Definition: partmanager.h:82
int64_t size
Definition: slotsmanager.h:12
struct slot_info info[6]
Definition: slotsmanager.h:112
double get_random_number(uint64_t id)
Definition: system.c:60
void sumup_large_ints(int n, int *src, int64_t *res)
Definition: system.c:192
#define MPI_INT64
Definition: system.h:12
int ThisTask
Definition: test_exchange.c:23
int NTask
Definition: test_exchange.c:23
int treewalk_visit_ngbiter(TreeWalkQueryBase *I, TreeWalkResultBase *O, LocalTreeWalk *lv)
Definition: treewalk.c:924
void treewalk_run(TreeWalk *tw, int *active_set, size_t size)
Definition: treewalk.c:619
int(* TreeWalkVisitFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, LocalTreeWalk *lv)
Definition: treewalk.h:68
void(* TreeWalkNgbIterFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
Definition: treewalk.h:70
void(* TreeWalkFillQueryFunction)(const int j, TreeWalkQueryBase *query, TreeWalk *tw)
Definition: treewalk.h:75
@ NGB_TREEFIND_ASYMMETRIC
Definition: treewalk.h:12
uint64_t MyIDType
Definition: types.h:10
#define walltime_measure(name)
Definition: walltime.h:8