MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
Classes | Macros | Functions | Variables
cooling_qso_lightup.c File Reference
#include <math.h>
#include <mpi.h>
#include <string.h>
#include <omp.h>
#include <gsl/gsl_interp.h>
#include "physconst.h"
#include "slotsmanager.h"
#include "partmanager.h"
#include "treewalk.h"
#include "hydra.h"
#include "drift.h"
#include "walltime.h"
#include "fof.h"
#include "utils/endrun.h"
#include "utils/paramset.h"
#include "utils/mymalloc.h"
#include "cooling_qso_lightup.h"
Include dependency graph for cooling_qso_lightup.c:

Go to the source code of this file.

Classes

struct  TreeWalkQueryQSOLightup
 
struct  qso_lightup_params
 
struct  QSOPriv
 

Macros

#define E0_HeII   54.4 /* HeII ionization potential in eV*/
 
#define HEMASS   4.002602 /* Helium mass in amu*/
 
#define QSO_GET_PRIV(tw)   ((struct QSOPriv *) (tw->priv))
 

Functions

void set_qso_lightup_par (struct qso_lightup_params qso)
 
void set_qso_lightup_params (ParameterSet *ps)
 
static double Q_inst (double Emax, double alpha_q)
 
static void load_heii_reion_hist (const char *reion_hist_file)
 
void init_qso_lightup (char *reion_hist_file)
 
double get_long_mean_free_path_heating (double redshift)
 
static double gaussian_rng (double mu, double sigma, const int64_t seed)
 
static int build_qso_candidate_list (int **qso_cand, FOFGroups *fof)
 
static int count_QSO_halos (int ncand, int64_t *ncand_tot, MPI_Comm Comm)
 
static int choose_QSO_halo (int64_t ncand, int64_t *ncand_before, int64_t *ncand_tot, int64_t randseed)
 
static double gas_ionization_fraction (void)
 
static int ionize_single_particle (int other, double a3inv, double uu_in_cgs)
 
static void ionize_ngbiter (TreeWalkQueryQSOLightup *I, TreeWalkResultBase *O, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
 
static void ionize_copy (int place, TreeWalkQueryQSOLightup *I, TreeWalk *tw)
 
static int64_t ionize_all_part (int qso_ind, int *qso_cand, struct QSOPriv priv, ForceTree *tree)
 
static void turn_on_quasars (double atime, FOFGroups *fof, DomainDecomp *ddecomp, Cosmology *CP, double uu_in_cgs, FILE *FdHelium)
 
void do_heiii_reionization (double atime, FOFGroups *fof, DomainDecomp *ddecomp, Cosmology *CP, double uu_in_cgs, FILE *FdHelium)
 
int need_change_helium_ionization_fraction (double atime)
 
int during_helium_reionization (double redshift)
 
int qso_lightup_on (void)
 

Variables

static struct qso_lightup_params QSOLightupParams
 
static double qso_inst_heating
 
static int Nreionhist
 
static double * He_zz
 
static double * XHeIII
 
static double * LMFP
 
static gsl_interp * HeIII_intp
 
static gsl_interp * LMFP_intp
 
static double last_zz
 
static double last_long_mfp_heating
 

Macro Definition Documentation

◆ E0_HeII

#define E0_HeII   54.4 /* HeII ionization potential in eV*/

This file implements the quasar heating model of Upton-Sanderbeck et al 2019 (in prep). A black hole particle with the right mass is chosen at random and an ionizing bubble created around it. Particles within that bubble are marked as ionized and heated according to emissivity. New bubbles are created until the total HeIII fraction matches the value in the external table. There is also a uniform background heating rate for long mean-free-path photons with very high energies. This heating is added only to not-yet-ionized particles, and is done in cooling_rates.c. Whatever UVB you use should not include these photons.

The text file contains the reionization history and is generated from various physical processes implemented in a python file. The text file fixes the end of helium reionization (which is reasonably well-known). The code has the start time of reionization as a free parameter: if this start of reionization is late then there will be a strong sudden burst of heating.

(Loosely) based on lightup_QSOs.py from https://github.com/uptonsanderbeck/helium_reionization HeII_heating.py contains the details of the reionization history and how it is generated.

This code should run only during a PM timestep, when all particles are active We lose time resolution but I cannot think of another way to ensure cooling is modelled correctly.

Definition at line 47 of file cooling_qso_lightup.c.

◆ HEMASS

#define HEMASS   4.002602 /* Helium mass in amu*/

Definition at line 48 of file cooling_qso_lightup.c.

◆ QSO_GET_PRIV

#define QSO_GET_PRIV (   tw)    ((struct QSOPriv *) (tw->priv))

Definition at line 413 of file cooling_qso_lightup.c.

Function Documentation

◆ build_qso_candidate_list()

static int build_qso_candidate_list ( int **  qso_cand,
FOFGroups fof 
)
static

Definition at line 282 of file cooling_qso_lightup.c.

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 }
static struct qso_lightup_params QSOLightupParams
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define myrealloc(ptr, size)
Definition: mymalloc.h:18
struct Group * Group
Definition: fof.h:63
int Ngroups
Definition: fof.h:66
double Mass
Definition: fof.h:31

References FOFGroups::Group, Group::Mass, mymalloc, myrealloc, FOFGroups::Ngroups, qso_lightup_params::qso_candidate_max_mass, qso_lightup_params::qso_candidate_min_mass, and QSOLightupParams.

Referenced by turn_on_quasars().

Here is the caller graph for this function:

◆ choose_QSO_halo()

static int choose_QSO_halo ( int64_t  ncand,
int64_t *  ncand_before,
int64_t *  ncand_tot,
int64_t  randseed 
)
static

Definition at line 340 of file cooling_qso_lightup.c.

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 }
double get_random_number(uint64_t id)
Definition: system.c:60

References get_random_number().

Referenced by turn_on_quasars().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ count_QSO_halos()

static int count_QSO_halos ( int  ncand,
int64_t *  ncand_tot,
MPI_Comm  Comm 
)
static

Definition at line 309 of file cooling_qso_lightup.c.

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 }
#define ta_malloc(name, type, nele)
Definition: mymalloc.h:25
#define ta_free(p)
Definition: mymalloc.h:28
int ThisTask
Definition: test_exchange.c:23
int NTask
Definition: test_exchange.c:23

References NTask, ta_free, ta_malloc, and ThisTask.

Referenced by turn_on_quasars().

Here is the caller graph for this function:

◆ do_heiii_reionization()

void do_heiii_reionization ( double  atime,
FOFGroups fof,
DomainDecomp ddecomp,
Cosmology CP,
double  uu_in_cgs,
FILE *  FdHelium 
)

Definition at line 640 of file cooling_qso_lightup.c.

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 }
static double * He_zz
static void turn_on_quasars(double atime, FOFGroups *fof, DomainDecomp *ddecomp, Cosmology *CP, double uu_in_cgs, FILE *FdHelium)
static int Nreionhist
static Cosmology * CP
Definition: power.c:27
#define walltime_measure(name)
Definition: walltime.h:8

References CP, QSOPriv::fof, He_zz, qso_lightup_params::heIIIreion_start, Nreionhist, qso_lightup_params::QSOLightupOn, QSOLightupParams, turn_on_quasars(), QSOPriv::uu_in_cgs, and walltime_measure.

Referenced by run().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ during_helium_reionization()

int during_helium_reionization ( double  redshift)

Definition at line 667 of file cooling_qso_lightup.c.

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 }

References He_zz, qso_lightup_params::heIIIreion_start, Nreionhist, qso_lightup_params::QSOLightupOn, and QSOLightupParams.

Referenced by run().

Here is the caller graph for this function:

◆ gas_ionization_fraction()

static double gas_ionization_fraction ( void  )
static

Definition at line 359 of file cooling_qso_lightup.c.

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 }
struct part_manager_type PartManager[1]
Definition: partmanager.c:11
#define P
Definition: partmanager.h:88
struct slots_manager_type SlotsManager[1]
Definition: slotsmanager.c:7
int64_t size
Definition: slotsmanager.h:12
struct slot_info info[6]
Definition: slotsmanager.h:112
void sumup_large_ints(int n, int *src, int64_t *res)
Definition: system.c:192
#define MPI_INT64
Definition: system.h:12

References slots_manager_type::info, MPI_INT64, part_manager_type::NumPart, P, PartManager, slot_info::size, SlotsManager, and sumup_large_ints().

Referenced by need_change_helium_ionization_fraction(), and turn_on_quasars().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ gaussian_rng()

static double gaussian_rng ( double  mu,
double  sigma,
const int64_t  seed 
)
static

Definition at line 271 of file cooling_qso_lightup.c.

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 }
double sigma[3]
Definition: densitykernel.c:97

References get_random_number(), and sigma.

Referenced by ionize_ngbiter().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_long_mean_free_path_heating()

double get_long_mean_free_path_heating ( double  redshift)

Definition at line 249 of file cooling_qso_lightup.c.

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 }
static double last_long_mfp_heating
static gsl_interp * LMFP_intp
static double last_zz
static double * LMFP

References He_zz, qso_lightup_params::heIIIreion_start, last_long_mfp_heating, last_zz, LMFP, LMFP_intp, Nreionhist, qso_lightup_params::QSOLightupOn, and QSOLightupParams.

Referenced by get_lambdanet().

Here is the caller graph for this function:

◆ init_qso_lightup()

void init_qso_lightup ( char *  reion_hist_file)

Definition at line 238 of file cooling_qso_lightup.c.

239 {
241  load_heii_reion_hist(reion_hist_file);
242 }
static void load_heii_reion_hist(const char *reion_hist_file)

References load_heii_reion_hist(), qso_lightup_params::QSOLightupOn, and QSOLightupParams.

Referenced by init_cooling().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ionize_all_part()

static int64_t ionize_all_part ( int  qso_ind,
int *  qso_cand,
struct QSOPriv  priv,
ForceTree tree 
)
static

Definition at line 473 of file cooling_qso_lightup.c.

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 }
static void ionize_ngbiter(TreeWalkQueryQSOLightup *I, TreeWalkResultBase *O, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
static void ionize_copy(int place, TreeWalkQueryQSOLightup *I, TreeWalk *tw)
int64_t * N_ionized
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
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

References TreeWalk::ev_label, TreeWalk::fill, TreeWalk::haswork, ionize_copy(), ionize_ngbiter(), QSOPriv::N_ionized, TreeWalk::ngbiter, TreeWalk::ngbiter_type_elsize, TreeWalk::postprocess, TreeWalk::priv, TreeWalk::query_type_elsize, TreeWalk::reduce, TreeWalk::result_type_elsize, ta_free, ta_malloc, TreeWalk::tree, treewalk_run(), treewalk_visit_ngbiter(), and TreeWalk::visit.

Referenced by turn_on_quasars().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ionize_copy()

static void ionize_copy ( int  place,
TreeWalkQueryQSOLightup I,
TreeWalk tw 
)
static

Definition at line 453 of file cooling_qso_lightup.c.

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 }
#define QSO_GET_PRIV(tw)
MyIDType MinID
Definition: fof.h:18
Definition: fof.h:62
struct BaseGroup base
Definition: fof.h:27
double CM[3]
Definition: fof.h:34
double Pos[3]
Definition: treewalk.h:23

References TreeWalkQueryQSOLightup::base, Group::base, Group::CM, FOFGroups::Group, TreeWalkQueryQSOLightup::ID, BaseGroup::MinID, TreeWalkQueryBase::Pos, and QSO_GET_PRIV.

Referenced by ionize_all_part().

Here is the caller graph for this function:

◆ ionize_ngbiter()

static void ionize_ngbiter ( TreeWalkQueryQSOLightup I,
TreeWalkResultBase O,
TreeWalkNgbIterBase iter,
LocalTreeWalk lv 
)
static

Ionize and heat the particles

Definition at line 419 of file cooling_qso_lightup.c.

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 }
static double gaussian_rng(double mu, double sigma, const int64_t seed)
static int ionize_single_particle(int other, double a3inv, double uu_in_cgs)
TreeWalk * tw
Definition: treewalk.h:47
enum NgbTreeFindSymmetric symmetric
Definition: treewalk.h:37
@ NGB_TREEFIND_ASYMMETRIC
Definition: treewalk.h:12

References gaussian_rng(), TreeWalkNgbIterBase::Hsml, TreeWalkQueryQSOLightup::ID, ionize_single_particle(), TreeWalkNgbIterBase::mask, qso_lightup_params::mean_bubble, NGB_TREEFIND_ASYMMETRIC, TreeWalkNgbIterBase::other, P, QSO_GET_PRIV, QSOLightupParams, TreeWalkNgbIterBase::symmetric, LocalTreeWalk::tw, and qso_lightup_params::var_bubble.

Referenced by ionize_all_part().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ionize_single_particle()

static int ionize_single_particle ( int  other,
double  a3inv,
double  uu_in_cgs 
)
static

Definition at line 380 of file cooling_qso_lightup.c.

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 }
#define HEMASS
static double qso_inst_heating
#define HYDROGEN_MASSFRAC
Definition: physconst.h:37
#define GAMMA_MINUS1
Definition: physconst.h:35
#define PROTONMASS
Definition: physconst.h:21
#define SPHP(i)
Definition: slotsmanager.h:124

References GAMMA_MINUS1, HEMASS, HYDROGEN_MASSFRAC, P, PROTONMASS, qso_inst_heating, and SPHP.

Referenced by ionize_ngbiter(), and turn_on_quasars().

Here is the caller graph for this function:

◆ load_heii_reion_hist()

static void load_heii_reion_hist ( const char *  reion_hist_file)
static

Definition at line 137 of file cooling_qso_lightup.c.

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 }
static double Q_inst(double Emax, double alpha_q)
static double * XHeIII
static gsl_interp * HeIII_intp
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147

References endrun(), He_zz, HeIII_intp, qso_lightup_params::heIIIreion_start, LMFP, LMFP_intp, message(), mymalloc, Nreionhist, Q_inst(), qso_inst_heating, QSOLightupParams, ThisTask, and XHeIII.

Referenced by init_qso_lightup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ need_change_helium_ionization_fraction()

int need_change_helium_ionization_fraction ( double  atime)

Definition at line 657 of file cooling_qso_lightup.c.

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 }
static double gas_ionization_fraction(void)

References gas_ionization_fraction(), He_zz, HeIII_intp, and XHeIII.

Referenced by run().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Q_inst()

static double Q_inst ( double  Emax,
double  alpha_q 
)
static

Definition at line 113 of file cooling_qso_lightup.c.

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 }
#define E0_HeII
#define eVinergs
Definition: physconst.h:15

References E0_HeII, and eVinergs.

Referenced by load_heii_reion_hist().

Here is the caller graph for this function:

◆ qso_lightup_on()

int qso_lightup_on ( void  )

Definition at line 682 of file cooling_qso_lightup.c.

683 {
685 }

References qso_lightup_params::QSOLightupOn, and QSOLightupParams.

Referenced by open_outputfiles().

Here is the caller graph for this function:

◆ set_qso_lightup_par()

void set_qso_lightup_par ( struct qso_lightup_params  qso)

Definition at line 88 of file cooling_qso_lightup.c.

89 {
90  QSOLightupParams = qso;
91 }

References QSOLightupParams.

◆ set_qso_lightup_params()

void set_qso_lightup_params ( ParameterSet ps)

Definition at line 94 of file cooling_qso_lightup.c.

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 }
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

References qso_lightup_params::heIIIreion_finish_frac, qso_lightup_params::mean_bubble, param_get_double(), param_get_int(), qso_lightup_params::qso_candidate_max_mass, qso_lightup_params::qso_candidate_min_mass, qso_lightup_params::QSOLightupOn, QSOLightupParams, ThisTask, and qso_lightup_params::var_bubble.

Referenced by read_parameter_file().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ turn_on_quasars()

static void turn_on_quasars ( double  atime,
FOFGroups fof,
DomainDecomp ddecomp,
Cosmology CP,
double  uu_in_cgs,
FILE *  FdHelium 
)
static

Definition at line 520 of file cooling_qso_lightup.c.

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 }
static int build_qso_candidate_list(int **qso_cand, FOFGroups *fof)
static int choose_QSO_halo(int64_t ncand, int64_t *ncand_before, int64_t *ncand_tot, int64_t randseed)
static int count_QSO_halos(int ncand, int64_t *ncand_tot, MPI_Comm Comm)
static int64_t ionize_all_part(int qso_ind, int *qso_cand, struct QSOPriv priv, ForceTree *tree)
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 myfree(x)
Definition: mymalloc.h:19
#define HUBBLE
Definition: physconst.h:25
#define GRAVITY
Definition: physconst.h:5
double HubbleParam
Definition: cosmology.h:20
double OmegaBaryon
Definition: cosmology.h:19
int64_t TotNgroups
Definition: fof.h:67
FOFGroups * fof
double CurrentParticleOffset[3]
Definition: partmanager.h:82

References QSOPriv::a3inv, part_manager_type::BoxSize, build_qso_candidate_list(), choose_QSO_halo(), Group::CM, count_QSO_halos(), CP, part_manager_type::CurrentParticleOffset, endrun(), QSOPriv::fof, force_tree_free(), force_tree_rebuild_mask(), gas_ionization_fraction(), GASMASK, GRAVITY, FOFGroups::Group, He_zz, HeIII_intp, qso_lightup_params::heIIIreion_finish_frac, HUBBLE, Cosmology::HubbleParam, slots_manager_type::info, ionize_all_part(), ionize_single_particle(), qso_lightup_params::mean_bubble, message(), MPI_INT64, myfree, part_manager_type::NumPart, Cosmology::OmegaBaryon, P, PartManager, QSOLightupParams, slot_info::size, SlotsManager, sumup_large_ints(), FOFGroups::TotNgroups, QSOPriv::uu_in_cgs, walltime_measure, and XHeIII.

Referenced by do_heiii_reionization().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ He_zz

double* He_zz
static

◆ HeIII_intp

gsl_interp* HeIII_intp
static

◆ last_long_mfp_heating

double last_long_mfp_heating
static

Definition at line 245 of file cooling_qso_lightup.c.

Referenced by get_long_mean_free_path_heating().

◆ last_zz

double last_zz
static

Definition at line 244 of file cooling_qso_lightup.c.

Referenced by get_long_mean_free_path_heating().

◆ LMFP

double* LMFP
static

Definition at line 83 of file cooling_qso_lightup.c.

Referenced by get_long_mean_free_path_heating(), and load_heii_reion_hist().

◆ LMFP_intp

gsl_interp* LMFP_intp
static

Definition at line 85 of file cooling_qso_lightup.c.

Referenced by get_long_mean_free_path_heating(), and load_heii_reion_hist().

◆ Nreionhist

int Nreionhist
static

◆ qso_inst_heating

double qso_inst_heating
static

Definition at line 79 of file cooling_qso_lightup.c.

Referenced by ionize_single_particle(), and load_heii_reion_hist().

◆ QSOLightupParams

struct qso_lightup_params QSOLightupParams
static

◆ XHeIII

double* XHeIII
static