33 #include <gsl/gsl_interp.h>
48 #define HEMASS 4.002602
97 MPI_Comm_rank(MPI_COMM_WORLD, &
ThisTask);
116 double intflux = (pow(Emax,-alpha_q+1.)-pow(
E0_HeII,-alpha_q+1.))/(pow(Emax,-alpha_q)- pow(
E0_HeII,-alpha_q));
141 MPI_Comm_rank(MPI_COMM_WORLD, &
ThisTask);
143 message(0,
"HeII: Loading HeII reionization history from file: %s\n",reion_hist_file);
145 fd = fopen(reion_hist_file,
"r");
147 endrun(456,
"HeII: Could not open reionization history file at: '%s'\n", reion_hist_file);
154 char * retval = fgets(buffer, 1024, fd);
158 retval = strtok(buffer,
" \t\n");
160 if(!retval || retval[0] ==
'#')
169 MPI_Bcast(&
Nreionhist, 1, MPI_INT, 0, MPI_COMM_WORLD);
172 endrun(1,
"HeII: Reionization history contains: %d entries, not enough.\n",
Nreionhist);
181 double qso_spectral_index = 0, photon_threshold_energy = 0;
187 char * line = fgets(buffer, 1024, fd);
191 char * retval = strtok(line,
" \t\n");
192 if(!retval || retval[0] ==
'#')
196 qso_spectral_index = atof(retval);
202 photon_threshold_energy = atof(retval);
207 He_zz[i] = 1./(1+atof(retval));
209 retval = strtok(NULL,
" \t");
211 endrun(12,
"HeII: Line %s of reionization table was incomplete!\n", line);
214 retval = strtok(NULL,
" \t");
216 endrun(12,
"HeII: Line %s of reionization table was incomplete!\n", line);
217 LMFP[i] = atof(retval);
257 double atime = 1/(1+redshift);
267 return long_mfp_heating;
275 double z1 = sqrt(-2 * log(u1) ) * cos(2 * M_PI * u2);
276 return mu +
sigma * z1;
286 *qso_cand = (
int *)
mymalloc(
"Quasar_candidates",
sizeof(
int) * (fof->
Ngroups+1));
287 for(i = 0; i < fof->
Ngroups; i++)
295 (*qso_cand)[ncand] = i;
299 (*qso_cand)[ncand] = -1;
300 *qso_cand = (
int *)
myrealloc(*qso_cand, (ncand+1) *
sizeof(int));
311 int64_t ncand_total = 0, ncand_before = 0;
313 MPI_Comm_size(Comm, &
NTask);
315 int * candcounts = (
int*)
ta_malloc(
"qso_cand_counts",
int,
NTask);
318 MPI_Allgather(&ncand, 1, MPI_INT, candcounts, 1, MPI_INT, Comm);
320 for(i = 0; i <
NTask; i++)
323 ncand_before += candcounts[i];
324 ncand_total += candcounts[i];
328 *ncand_tot = ncand_total;
340 choose_QSO_halo(int64_t ncand, int64_t * ncand_before, int64_t * ncand_tot, int64_t randseed)
343 int64_t qso = drand * (*ncand_tot);
346 if(qso < *ncand_before)
348 if(qso < *ncand_before || qso >= *ncand_before + ncand)
353 return qso - *ncand_before;
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)
365 if (
P[i].Type == 0 &&
P[i].HeIIIionized == 1){
373 return (
double) n_ionized_tot / (double) n_gas_tot;
384 #pragma omp atomic capture
386 done =
P[other].HeIIIionized;
387 P[other].HeIIIionized = 1;
403 SPHP(other).Entropy += deltau / uu_in_cgs / entropytou;
413 #define QSO_GET_PRIV(tw) ((struct QSOPriv *) (tw->priv))
425 if(iter->
other == -1) {
436 int other = iter->
other;
439 if(
P[other].Type != 0)
447 int tid = omp_get_thread_num();
461 for(k = 0; k < 3; k++)
497 memset(priv.
N_ionized, 0,
sizeof(int64_t) * omp_get_max_threads());
506 int64_t N_ionized = 0;
508 for(i = 0; i < omp_get_max_threads(); i++)
523 int * qso_cand = NULL;
524 int64_t n_gas_tot=0, tot_n_ionized=0, ncand_tot=0;
530 priv.
a3inv = 1/pow(atime, 3);
537 #pragma omp parallel for reduction(+: nionized)
543 message(0,
"HeII: Helium ionization finished, flash-ionizing %ld particles (%g of total)\n", nion_tot, (
double) nion_tot /(
double) n_gas_tot);
550 int64_t non_overlapping_bubble_number = n_gas_tot * totbubblegasmass /
CP->
OmegaBaryon;
552 double curionfrac = initionfrac;
554 message(0,
"HeII: Started helium reionization model with ionization fraction %g\n", initionfrac);
555 if(curionfrac < desired_ion_frac) {
560 int64_t ncand_before =
count_QSO_halos(ncand, &ncand_tot, MPI_COMM_WORLD);
569 message(0,
"HeII: Built quasar candidate list from %d quasars\n", ncand_tot);
572 for(iteration = 0; curionfrac < desired_ion_frac; iteration++){
576 endrun(12,
"HeII: QSO %d > no. candidates %d! Cannot happen\n", new_qso, ncand);
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);
584 int64_t n_ionized =
ionize_all_part(new_qso, qso_cand, priv, &gasTree);
585 int64_t tot_qso_ionized = 0;
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;
591 double qso_pos[3] = {0};
593 int qplace = qso_cand[new_qso];
595 for(k = 0; k < 3; k++) {
600 message(1,
"HeII: Quasar %d changed the HeIII ionization fraction to %g, ionizing %ld\n", qplace, curionfrac, tot_qso_ionized);
607 MPI_Allreduce(MPI_IN_PLACE, qso_pos, 3, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
609 fprintf(FdHelium,
"%g %g %g %g %g %ld\n", atime, qso_pos[0], qso_pos[1], qso_pos[2], curionfrac, tot_qso_ionized);
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);
622 memmove(qso_cand+new_qso, qso_cand+new_qso+1, ncand - new_qso+1);
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);
634 message(0,
"HeII: HeIII fraction unchanged at %g. Wanted %g\n", curionfrac, desired_ion_frac);
661 if(curionfrac < desired_ion_frac)
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)
static void load_heii_reion_hist(const char *reion_hist_file)
static double last_long_mfp_heating
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 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)
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)
void message(int where, const char *fmt,...)
void endrun(int where, const char *fmt,...)
void force_tree_rebuild_mask(ForceTree *tree, DomainDecomp *ddecomp, int mask, const int HybridNuGrav, const char *EmergencyOutputDir)
void force_tree_free(ForceTree *tree)
#define mymalloc(name, size)
#define ta_malloc(name, type, nele)
#define myrealloc(ptr, size)
double param_get_double(ParameterSet *ps, const char *name)
int param_get_int(ParameterSet *ps, const char *name)
struct part_manager_type PartManager[1]
#define HYDROGEN_MASSFRAC
struct slots_manager_type SlotsManager[1]
enum NgbTreeFindSymmetric symmetric
TreeWalkHasWorkFunction haswork
size_t ngbiter_type_elsize
TreeWalkProcessFunction postprocess
TreeWalkNgbIterFunction ngbiter
TreeWalkReduceResultFunction reduce
TreeWalkVisitFunction visit
size_t result_type_elsize
TreeWalkFillQueryFunction fill
double CurrentParticleOffset[3]
double qso_candidate_min_mass
double heIIIreion_finish_frac
double qso_candidate_max_mass
double get_random_number(uint64_t id)
void sumup_large_ints(int n, int *src, int64_t *res)
int treewalk_visit_ngbiter(TreeWalkQueryBase *I, TreeWalkResultBase *O, LocalTreeWalk *lv)
void treewalk_run(TreeWalk *tw, int *active_set, size_t size)
int(* TreeWalkVisitFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, LocalTreeWalk *lv)
void(* TreeWalkNgbIterFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
void(* TreeWalkFillQueryFunction)(const int j, TreeWalkQueryBase *query, TreeWalk *tw)
@ NGB_TREEFIND_ASYMMETRIC
#define walltime_measure(name)