MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
blackhole.c
Go to the documentation of this file.
1 #include <mpi.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <math.h>
6 #include <gsl/gsl_math.h>
7 #include <omp.h>
8 
9 #include "physconst.h"
10 #include "cooling.h"
11 #include "gravity.h"
12 #include "densitykernel.h"
13 #include "treewalk.h"
14 #include "slotsmanager.h"
15 #include "blackhole.h"
16 #include "timestep.h"
17 #include "hydra.h"
18 #include "density.h"
19 #include "sfr_eff.h"
20 #include "winds.h"
21 #include "walltime.h"
27 {
32  int BlackHoleRepositionEnabled; /* If true, enable repositioning the BH to the potential minimum*/
33 
34  int BlackHoleKineticOn; /*If 1, perform AGN kinetic feedback when the Eddington accretion rate is low */
35  double BHKE_EddingtonThrFactor; /*Threshold of the Eddington rate for the kinetic feedback*/
36  double BHKE_EddingtonMFactor; /* Factor for mbh-dependent Eddington threshold */
37  double BHKE_EddingtonMPivot; /* Pivot MBH for mbh-dependent Eddington threshold */
38  double BHKE_EddingtonMIndex; /* Powlaw index for mbh-dependent Eddington threshold */
39  double BHKE_EffRhoFactor; /* Minimum kinetic feedback efficiency factor scales with BH density*/
40  double BHKE_EffCap; /* Cap of the kinetic feedback efficiency factor */
41  double BHKE_InjEnergyThr; /*Minimum injection of KineticFeedbackEnergy, controls the burstiness of kinetic feedback*/
42  double BHKE_SfrCritOverDensity; /*for KE efficiency calculation, borrow from sfr.params */
43  /**********************************************************************/
44  int MergeGravBound; /*if 1, apply gravitational bound criteria for BH mergers */
45 
46  int BH_DynFrictionMethod;/*0 for off; 1 for Star Only; 2 for DM+Star; 3 for DM+Star+Gas */
47  int BH_DFBoostFactor; /*Optional boost factor for DF */
48  double BH_DFbmax; /* the maximum impact range, in physical unit of kpc. */
49  int BH_DRAG; /*Hydro drag force*/
50 
51  double SeedBHDynMass; /* The initial dynamic mass of BH particle */
52 
54  double MaxSeedBlackHoleMass; /* Maximum black hole seed mass*/
55  double SeedBlackHoleMassIndex; /* Power law index for BH seed mass*/
56  /************************************************************************/
58 
59 int
61 {
63 }
64 
65 typedef struct {
71  MyFloat Vel[3];
72  MyFloat Accel[3];
76 
77 typedef struct {
79  MyFloat BH_MinPotPos[3];
80  MyFloat BH_MinPotVel[3];
83  int encounter;
85 
87  MyFloat GasVel[3];
88  /* used for AGN kinetic feedback */
90  MyFloat V1sumDM[3];
94 
95 typedef struct {
100 
101 
102 /*****************************************************************************/
103 typedef struct {
107 
108 typedef struct {
110 
111  MyFloat SurroundingVel[3];
115 
117 
118 typedef struct {
122 
123 /*****************************************************************************/
124 
125 
126 typedef struct {
136  int FdbkChannel; /* 0 thermal, 1 kinetic */
138 
139 typedef struct {
141  MyFloat Mass; /* the accreted Mdyn */
142  MyFloat AccretedMomentum[3];
145  MyFloat acMtrack; /* the accreted Mtrack */
146  int alignment; /* Ensure alignment*/
148 
149 typedef struct {
153 
154 struct BHPriv {
155  /* Temporary array to store the IDs of the swallowing black hole for gas.
156  * We store ID + 1 so that SwallowID == 0 can correspond to the unswallowed case. */
158  /* These are temporaries used in the accretion treewalk*/
162 
163  /*************************************************************************/
164  /* used in the dynamic friction treewalk*/
169 
170  /*************************************************************************/
171 
173 
174  /* These are temporaries used in the feedback treewalk.*/
178 
179  /* This is a temporary computed in the accretion treewalk and used
180  * in the feedback treewalk*/
182 
183  /* temporary computed for kinetic feedback energy threshold*/
188  /* mark the state of AGN kinetic feedback, 1 accumulate, 2 release */
189  int * KEflag;
190 
191  /* Time factors*/
192  double atime;
193  double a3inv;
194  double hubble;
195  struct UnitSystem units;
197  /* Counters*/
198  int64_t * N_sph_swallowed;
199  int64_t * N_BH_swallowed;
200 };
201 #define BH_GET_PRIV(tw) ((struct BHPriv *) (tw->priv))
202 
203 /* Structure needs to be packed to ensure disc write is the same on all architectures and the record size is correct. */
204 struct __attribute__((__packed__)) BHinfo{
205  /* Stores sizeof(struct BHinfo) - 2 * sizeof(size1) . Allows size of record to be stored in the struct.*/
206  int size1;
207  MyIDType ID;
208  MyFloat Mass;
209  MyFloat Mdot;
210  MyFloat Density;
211  int minTimeBin;
212  int encounter;
213 
214  double MinPotPos[3];
215  MyFloat MinPot;
216  MyFloat BH_Entropy;
217  MyFloat BH_SurroundingGasVel[3];
218  MyFloat BH_accreted_momentum[3];
219 
220  MyFloat BH_accreted_Mass;
221  MyFloat BH_accreted_BHMass;
222  MyFloat BH_FeedbackWeightSum;
223 
224  MyIDType SPH_SwallowID;
225  MyIDType SwallowID;
226 
227  int CountProgs;
228  int Swallowed;
229 
230  /****************************************/
231  double Pos[3];
232  MyFloat BH_SurroundingDensity;
233  MyFloat BH_SurroundingParticles;
234  MyFloat BH_SurroundingVel[3];
235  MyFloat BH_SurroundingRmsVel;
236 
237  double BH_DFAccel[3];
238  double BH_DragAccel[3];
239  double BH_GravAccel[3];
240  double Velocity[3];
241  double Mtrack;
242  double Mdyn;
243 
244  double KineticFdbkEnergy;
245  double NumDM;
246  double V1sumDM[3];
247  double V2sumDM;
248  double MgasEnc;
249  int KEflag;
250 
251  MyDouble a;
252  /* See size1 above*/
253  int size2;
254 };
255 
256 /*Set the parameters of the BH module*/
258 {
259  int ThisTask;
260  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
261  if(ThisTask == 0) {
262  blackhole_params.BlackHoleAccretionFactor = param_get_double(ps, "BlackHoleAccretionFactor");
263  blackhole_params.BlackHoleEddingtonFactor = param_get_double(ps, "BlackHoleEddingtonFactor");
264 
265  blackhole_params.BlackHoleFeedbackFactor = param_get_double(ps, "BlackHoleFeedbackFactor");
266 
267  blackhole_params.BlackHoleFeedbackMethod = (enum BlackHoleFeedbackMethod) param_get_enum(ps, "BlackHoleFeedbackMethod");
268  blackhole_params.BlackHoleRepositionEnabled = param_get_int(ps, "BlackHoleRepositionEnabled");
269 
270  blackhole_params.BlackHoleKineticOn = param_get_int(ps,"BlackHoleKineticOn");
271  blackhole_params.BHKE_EddingtonThrFactor = param_get_double(ps, "BHKE_EddingtonThrFactor");
272  blackhole_params.BHKE_EddingtonMFactor = param_get_double(ps, "BHKE_EddingtonMFactor");
273  blackhole_params.BHKE_EddingtonMPivot = param_get_double(ps, "BHKE_EddingtonMPivot");
274  blackhole_params.BHKE_EddingtonMIndex = param_get_double(ps, "BHKE_EddingtonMIndex");
275  blackhole_params.BHKE_EffRhoFactor = param_get_double(ps, "BHKE_EffRhoFactor");
276  blackhole_params.BHKE_EffCap = param_get_double(ps, "BHKE_EffCap");
277  blackhole_params.BHKE_InjEnergyThr = param_get_double(ps, "BHKE_InjEnergyThr");
279  /***********************************************************************************/
280  blackhole_params.BH_DynFrictionMethod = param_get_int(ps, "BH_DynFrictionMethod");
281  blackhole_params.BH_DFBoostFactor = param_get_int(ps, "BH_DFBoostFactor");
282  blackhole_params.BH_DFbmax = param_get_double(ps, "BH_DFbmax");
283  blackhole_params.BH_DRAG = param_get_int(ps, "BH_DRAG");
284  blackhole_params.MergeGravBound = param_get_int(ps, "MergeGravBound");
285  blackhole_params.SeedBHDynMass = param_get_double(ps,"SeedBHDynMass");
286 
287  blackhole_params.SeedBlackHoleMass = param_get_double(ps, "SeedBlackHoleMass");
288  blackhole_params.MaxSeedBlackHoleMass = param_get_double(ps,"MaxSeedBlackHoleMass");
289  blackhole_params.SeedBlackHoleMassIndex = param_get_double(ps,"SeedBlackHoleMassIndex");
290  /***********************************************************************************/
291  }
292  MPI_Bcast(&blackhole_params, sizeof(struct BlackholeParams), MPI_BYTE, 0, MPI_COMM_WORLD);
293 }
294 
295 /* accretion routines */
296 static void
298 
299 static void
301 
302 static void
304 
305 /* Initializes the minimum potentials*/
306 static void
308 
309 static void
313  LocalTreeWalk * lv);
314 
315 
316 
317 /*************************************************************************************/
318 /* DF routines */
319 static void
321 
322 static int
324 
325 static void
327 
328 static void
330 
331 static void
335  LocalTreeWalk * lv);
336 
337 /*************************************************************************************/
338 
339 
340 
341 /* feedback routines */
342 
343 static void
345 
346 static int
348 
349 static void
351 
352 static void
354 
355 static void
359  LocalTreeWalk * lv);
360 
361 #define BHPOTVALUEINIT 1.0e29
362 
363 static double blackhole_soundspeed(double entropy, double rho, const double atime) {
364  /* rho is comoving !*/
365  double cs = sqrt(GAMMA * entropy * pow(rho, GAMMA_MINUS1));
366 
367  cs *= pow(atime, -1.5 * GAMMA_MINUS1);
368 
369  return cs;
370 }
371 
372 /* Adds the injected black hole energy to an internal energy and caps it at a maximum temperature*/
373 static double
374 add_injected_BH_energy(double unew, double injected_BH_energy, double mass, double uu_in_cgs)
375 {
376  unew += injected_BH_energy / mass;
377  const double u_to_temp_fac = (4 / (8 - 5 * (1 - HYDROGEN_MASSFRAC))) * PROTONMASS / BOLTZMANN * GAMMA_MINUS1 * uu_in_cgs;
378  /* Cap temperature*/
379  if(unew > 5.0e8 / u_to_temp_fac)
380  unew = 5.0e8 / u_to_temp_fac;
381 
382  return unew;
383 }
384 
385 static int
386 get_random_dir(int i, double dir[3])
387 {
388  double theta = acos(2 * get_random_number(P[i].ID + 3) - 1);
389  double phi = 2 * M_PI * get_random_number(P[i].ID + 4);
390 
391  dir[0] = sin(theta) * cos(phi);
392  dir[1] = sin(theta) * sin(phi);
393  dir[2] = cos(theta);
394  return 0;
395 }
396 
397 /* check if two BHs are gravitationally bounded, input dv, da, dx in code unit */
398 /* same as Bellovary2011, Tremmel2017 */
399 static int
400 check_grav_bound(double dx[3], double dv[3], double da[3], const double atime)
401 {
402  int j;
403  double KE = 0;
404  double PE = 0;
405 
406  for(j = 0; j < 3; j++){
407  KE += 0.5 * pow(dv[j], 2);
408  PE += da[j] * dx[j];
409  }
410 
411  KE /= (atime * atime); /* convert to proper velocity */
412  PE /= atime; /* convert to proper unit */
413 
414  /* The gravitationally bound condition is PE + KE < 0.
415  * Still merge if it is marginally bound so that we merge
416  * particles at zero distance and velocity from each other.*/
417  return (PE + KE <= 0);
418 }
419 
420 
421 
422 static void
423 collect_BH_info(int * ActiveBlackHoles, int NumActiveBlackHoles, struct BHPriv *priv, FILE * FdBlackholeDetails)
424 {
425  int i;
426 
427  struct BHinfo * infos = (struct BHinfo *) mymalloc("BHDetailCache", NumActiveBlackHoles * sizeof(struct BHinfo));
428  report_memory_usage("BLACKHOLE");
429 
430  const int size = sizeof(struct BHinfo) - sizeof(infos[0].size1) - sizeof(infos[0].size2);
431 
432  #pragma omp parallel for
433  for(i = 0; i < NumActiveBlackHoles; i++)
434  {
435  const int p_i = ActiveBlackHoles ? ActiveBlackHoles[i] : i;
436  int PI = P[p_i].PI;
437 
438  struct BHinfo *info = &infos[i];
439  info->size1 = size;
440  info->size2 = size;
441  info->ID = P[p_i].ID;
442  info->Mass = BHP(p_i).Mass;
443  info->Mdot = BHP(p_i).Mdot;
444  info->Density = BHP(p_i).Density;
445  info->minTimeBin = BHP(p_i).minTimeBin;
446  info->encounter = BHP(p_i).encounter;
447 
448  if(priv->MinPot) {
449  info->MinPot = priv->MinPot[PI];
450  }
451  info->BH_Entropy = priv->BH_Entropy[PI];
452  int k;
453  for(k=0; k < 3; k++) {
454  info->MinPotPos[k] = BHP(p_i).MinPotPos[k] - PartManager->CurrentParticleOffset[k];
455  info->BH_SurroundingGasVel[k] = priv->BH_SurroundingGasVel[PI][k];
456  info->BH_accreted_momentum[k] = priv->BH_accreted_momentum[PI][k];
457  info->BH_DragAccel[k] = BHP(p_i).DragAccel[k];
458  info->BH_GravAccel[k] = P[p_i].GravAccel[k];
459  info->Pos[k] = P[p_i].Pos[k] - PartManager->CurrentParticleOffset[k];
460  info->Velocity[k] = P[p_i].Vel[k];
461  info->BH_DFAccel[k] = BHP(p_i).DFAccel[k];
462  }
463 
464  /****************************************************************************/
465  /* Output some DF info for debugging */
466  info->BH_SurroundingDensity = priv->BH_SurroundingDensity[PI];
467  info->BH_SurroundingRmsVel = priv->BH_SurroundingRmsVel[PI];
468  info->BH_SurroundingParticles = priv->BH_SurroundingParticles[PI];
469  info->BH_SurroundingVel[0] = priv->BH_SurroundingVel[PI][0];
470  info->BH_SurroundingVel[1] = priv->BH_SurroundingVel[PI][1];
471  info->BH_SurroundingVel[2] = priv->BH_SurroundingVel[PI][2];
472 
473  /****************************************************************************/
474  info->BH_accreted_BHMass = priv->BH_accreted_BHMass[PI];
475  info->BH_accreted_Mass = priv->BH_accreted_Mass[PI];
476  info->BH_FeedbackWeightSum = priv->BH_FeedbackWeightSum[PI];
477 
478  info->SPH_SwallowID = priv->SPH_SwallowID[PI];
479  info->SwallowID = BHP(p_i).SwallowID;
480  info->CountProgs = BHP(p_i).CountProgs;
481  info->Swallowed = P[p_i].Swallowed;
482  /************************************************************************************************/
483  /* When SeedBHDynMass is larger than gas particle mass, we have three mass tracer of blackhole. */
484  /* BHP(p_i).Mass : intrinsic mass of BH, accreted every (active) time step. */
485  /* P[p_i].Mass : Dynamic mass of BH, used for gravitational interaction. */
486  /* Starts to accrete gas particle when BHP(p_i).Mass > SeedBHDynMass */
487  /* BHP(p_i).Mtrack: Initialized as gas particle mass, and is capped at SeedBHDynMass, */
488  /* it traces BHP(p_i).Mass by swallowing gas when BHP(p_i).Mass < SeedBHDynMass */
489  /************************************************************************************************/
490  info->Mtrack = BHP(p_i).Mtrack;
491  info->Mdyn = P[p_i].Mass;
492 
493  info->KineticFdbkEnergy = BHP(p_i).KineticFdbkEnergy;
494  info->NumDM = priv->NumDM[PI];
495  info->V1sumDM[0] = priv->V1sumDM[PI][0];
496  info->V1sumDM[1] = priv->V1sumDM[PI][1];
497  info->V1sumDM[2] = priv->V1sumDM[PI][2];
498  info->V2sumDM = priv->V2sumDM[PI];
499  info->MgasEnc = priv->MgasEnc[PI];
500  info->KEflag = priv->KEflag[PI];
501 
502  info->a = priv->atime;
503  }
504 
505  fwrite(infos,sizeof(struct BHinfo),NumActiveBlackHoles,FdBlackholeDetails);
506  fflush(FdBlackholeDetails);
507  myfree(infos);
508  int64_t totalN;
509 
510  sumup_large_ints(1, &NumActiveBlackHoles, &totalN);
511  message(0, "Written details of %ld blackholes in %lu bytes each.\n", totalN, sizeof(struct BHinfo));
512 }
513 
514 
515 void
516 blackhole(const ActiveParticles * act, double atime, Cosmology * CP, ForceTree * tree, const struct UnitSystem units, FILE * FdBlackHoles, FILE * FdBlackholeDetails)
517 {
518  /* Do nothing if no black holes*/
519  int64_t totbh;
520  MPI_Allreduce(&SlotsManager->info[5].size, &totbh, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
521  if(totbh == 0)
522  return;
523  int i;
524 
525  walltime_measure("/Misc");
526  struct BHPriv priv[1] = {0};
527  priv->units = units;
528 
529  /*************************************************************************/
530  TreeWalk tw_dynfric[1] = {{0}};
531  tw_dynfric->ev_label = "BH_DYNFRIC";
533  tw_dynfric->ngbiter_type_elsize = sizeof(TreeWalkNgbIterBHDynfric);
535  tw_dynfric->haswork = blackhole_dynfric_haswork;
539  tw_dynfric->query_type_elsize = sizeof(TreeWalkQueryBHDynfric);
540  tw_dynfric->result_type_elsize = sizeof(TreeWalkResultBHDynfric);
541  tw_dynfric->tree = tree;
542  tw_dynfric->priv = priv;
543 
544  /*************************************************************************/
545  TreeWalk tw_accretion[1] = {{0}};
546 
547  tw_accretion->ev_label = "BH_ACCRETION";
549  tw_accretion->ngbiter_type_elsize = sizeof(TreeWalkNgbIterBHAccretion);
551  tw_accretion->haswork = blackhole_dynfric_haswork;
556  tw_accretion->query_type_elsize = sizeof(TreeWalkQueryBHAccretion);
557  tw_accretion->result_type_elsize = sizeof(TreeWalkResultBHAccretion);
558  tw_accretion->tree = tree;
559  tw_accretion->priv = priv;
560 
561  /*************************************************************************/
562 
563  TreeWalk tw_feedback[1] = {{0}};
564  tw_feedback->ev_label = "BH_FEEDBACK";
566  tw_feedback->ngbiter_type_elsize = sizeof(TreeWalkNgbIterBHFeedback);
568  tw_feedback->haswork = blackhole_feedback_haswork;
572  tw_feedback->query_type_elsize = sizeof(TreeWalkQueryBHFeedback);
573  tw_feedback->result_type_elsize = sizeof(TreeWalkResultBHFeedback);
574  tw_feedback->tree = tree;
575  tw_feedback->priv = priv;
576  tw_feedback->repeatdisallowed = 1;
577 
578  priv->atime = atime;
579  priv->a3inv = 1./(atime * atime * atime);
580  priv->hubble = hubble_function(CP, atime);
581  priv->CP = CP;
582 
583  /* Build the queue once, since it is really 'all black holes' and similar for all treewalks*/
584  treewalk_build_queue(tw_dynfric, act->ActiveParticle, act->NumActiveParticle, 0);
585  /* Now we have a BH queue and we can re-use it*/
586  int * ActiveBlackHoles = tw_dynfric->WorkSet;
587  int64_t NumActiveBlackHoles = tw_dynfric->WorkSetSize;
588  /* If this queue is empty, nothing to do.*/
589  MPI_Allreduce(&NumActiveBlackHoles, &totbh, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
590  if(totbh == 0) {
591  myfree(ActiveBlackHoles);
592  return;
593  }
594 
595  /* We can re-use the current queue for these treewalks*/
596  tw_accretion->haswork = NULL;
597  tw_dynfric->haswork = NULL;
598 
599  /*************************************************************************/
600  /* Dynamical Friction Treewalk */
601 
602  /* Environment variables for DF */
603  priv->BH_SurroundingRmsVel = (MyFloat *) mymalloc("BH_SurroundingRmsVel", SlotsManager->info[5].size * sizeof(priv->BH_SurroundingRmsVel));
604  priv->BH_SurroundingVel = (MyFloat (*) [3]) mymalloc("BH_SurroundingVel", 3* SlotsManager->info[5].size * sizeof(priv->BH_SurroundingVel[0]));
605  priv->BH_SurroundingParticles = (int *)mymalloc("BH_SurroundingParticles", SlotsManager->info[5].size * sizeof(priv->BH_SurroundingParticles));
606  priv->BH_SurroundingDensity = (MyFloat *) mymalloc("BH_SurroundingDensity", SlotsManager->info[5].size * sizeof(priv->BH_SurroundingDensity));
607  /* guard treewalk */
609  treewalk_run(tw_dynfric, ActiveBlackHoles, NumActiveBlackHoles);
610 
611  /*************************************************************************/
612 
613  walltime_measure("/BH/DynFric");
614 
615  /* Let's determine which particles may be swallowed and calculate total feedback weights */
616  priv->SPH_SwallowID = (MyIDType *) mymalloc("SPH_SwallowID", SlotsManager->info[0].size * sizeof(MyIDType));
617  memset(priv->SPH_SwallowID, 0, SlotsManager->info[0].size * sizeof(MyIDType));
618 
619  /* Computed in accretion, used in feedback*/
620  priv->BH_FeedbackWeightSum = (MyFloat *) mymalloc("BH_FeedbackWeightSum", SlotsManager->info[5].size * sizeof(MyFloat));
621 
622  /* These are initialized in preprocess and used to reposition the BH in postprocess*/
623  priv->MinPot = (MyFloat *) mymalloc("BH_MinPot", SlotsManager->info[5].size * sizeof(MyFloat));
624 
625  /* Local to this treewalk*/
626  priv->BH_Entropy = (MyFloat *) mymalloc("BH_Entropy", SlotsManager->info[5].size * sizeof(MyFloat));
627  priv->BH_SurroundingGasVel = (MyFloat (*) [3]) mymalloc("BH_SurroundVel", 3* SlotsManager->info[5].size * sizeof(priv->BH_SurroundingGasVel[0]));
628 
629  /* For AGN kinetic feedback */
630  priv->NumDM = mymalloc("NumDM", SlotsManager->info[5].size * sizeof(MyFloat));
631  priv->V2sumDM = mymalloc("V2sumDM", SlotsManager->info[5].size * sizeof(MyFloat));
632  priv->V1sumDM = (MyFloat (*) [3]) mymalloc("V1sumDM", 3* SlotsManager->info[5].size * sizeof(priv->V1sumDM[0]));
633  priv->MgasEnc = mymalloc("MgasEnc", SlotsManager->info[5].size * sizeof(MyFloat));
634  /* mark the state of AGN kinetic feedback */
635  priv->KEflag = mymalloc("KEflag", SlotsManager->info[5].size * sizeof(int));
636 
637  /* This allocates memory*/
638  treewalk_run(tw_accretion, ActiveBlackHoles, NumActiveBlackHoles);
639 
640  /*************************************************************************/
641 
642  walltime_measure("/BH/Accretion");
643  MPIU_Barrier(MPI_COMM_WORLD);
644 
645  /* Now do the swallowing of particles and dump feedback energy */
646 
647  /* Ionization counters*/
648  priv[0].N_sph_swallowed = ta_malloc("n_sph_swallowed", int64_t, omp_get_max_threads());
649  priv[0].N_BH_swallowed = ta_malloc("n_BH_swallowed", int64_t, omp_get_max_threads());
650  memset(priv[0].N_sph_swallowed, 0, sizeof(int64_t) * omp_get_max_threads());
651  memset(priv[0].N_BH_swallowed, 0, sizeof(int64_t) * omp_get_max_threads());
652 
653  priv->BH_accreted_Mass = (MyFloat *) mymalloc("BH_accretedmass", SlotsManager->info[5].size * sizeof(MyFloat));
654  priv->BH_accreted_BHMass = (MyFloat *) mymalloc("BH_accreted_BHMass", SlotsManager->info[5].size * sizeof(MyFloat));
655  priv->BH_accreted_Mtrack = (MyFloat *) mymalloc("BH_accreted_Mtrack", SlotsManager->info[5].size * sizeof(MyFloat));
656  priv->BH_accreted_momentum = (MyFloat (*) [3]) mymalloc("BH_accretemom", 3* SlotsManager->info[5].size * sizeof(priv->BH_accreted_momentum[0]));
657 
658  treewalk_run(tw_feedback, ActiveBlackHoles, NumActiveBlackHoles);
659 
660  /*************************************************************************/
661  walltime_measure("/BH/Feedback");
662 
663  if(FdBlackholeDetails){
664  collect_BH_info(ActiveBlackHoles, NumActiveBlackHoles, priv, FdBlackholeDetails);
665  }
666 
668  myfree(priv->BH_accreted_Mtrack);
669  myfree(priv->BH_accreted_BHMass);
670  myfree(priv->BH_accreted_Mass);
671 
672  /*****************************************************************/
673  myfree(priv->KEflag);
674  myfree(priv->MgasEnc);
675  myfree(priv->V1sumDM);
676  myfree(priv->V2sumDM);
677  myfree(priv->NumDM);
678 
680  myfree(priv->BH_Entropy);
681  myfree(priv->MinPot);
682 
684  myfree(priv->SPH_SwallowID);
685 
686  /*****************************************************************/
689  myfree(priv->BH_SurroundingVel);
691 
692  /*****************************************************************/
693  myfree(ActiveBlackHoles);
694 
695  int64_t Ntot_gas_swallowed, Ntot_BH_swallowed;
696  int64_t N_sph_swallowed = 0, N_BH_swallowed = 0;
697  for(i = 0; i < omp_get_max_threads(); i++) {
698  N_sph_swallowed += priv[0].N_sph_swallowed[i];
699  N_BH_swallowed += priv[0].N_BH_swallowed[i];
700  }
701  ta_free(priv[0].N_BH_swallowed);
702  ta_free(priv[0].N_sph_swallowed);
703 
704  MPI_Reduce(&N_sph_swallowed, &Ntot_gas_swallowed, 1, MPI_INT64, MPI_SUM, 0, MPI_COMM_WORLD);
705  MPI_Reduce(&N_BH_swallowed, &Ntot_BH_swallowed, 1, MPI_INT64, MPI_SUM, 0, MPI_COMM_WORLD);
706 
707  MPIU_Barrier(MPI_COMM_WORLD);
708  message(0, "Accretion done: %d gas particles swallowed, %d BH particles swallowed\n",
709  Ntot_gas_swallowed, Ntot_BH_swallowed);
710 
711  int total_bh;
712  double total_mdoteddington;
713  double total_mass_holes, total_mdot;
714 
715  double Local_BH_mass = 0;
716  double Local_BH_Mdot = 0;
717  double Local_BH_Medd = 0;
718  int Local_BH_num = 0;
719  /* Compute total mass of black holes
720  * present by summing contents of black hole array*/
721  #pragma omp parallel for reduction(+ : Local_BH_num) reduction(+: Local_BH_mass) reduction(+: Local_BH_Mdot) reduction(+: Local_BH_Medd)
722  for(i = 0; i < SlotsManager->info[5].size; i ++)
723  {
724  if(BhP[i].SwallowID != (MyIDType) -1)
725  continue;
726  Local_BH_num++;
727  Local_BH_mass += BhP[i].Mass;
728  Local_BH_Mdot += BhP[i].Mdot;
729  Local_BH_Medd += BhP[i].Mdot/BhP[i].Mass;
730  }
731 
732  MPI_Reduce(&Local_BH_mass, &total_mass_holes, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
733  MPI_Reduce(&Local_BH_Mdot, &total_mdot, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
734  MPI_Reduce(&Local_BH_Medd, &total_mdoteddington, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
735  MPI_Reduce(&Local_BH_num, &total_bh, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
736 
737  if(FdBlackHoles)
738  {
739  /* convert to solar masses per yr */
740  double mdot_in_msun_per_year =
742 
743  total_mdoteddington *= 1.0 / ((4 * M_PI * GRAVITY * LIGHTCGS * PROTONMASS /
744  (0.1 * LIGHTCGS * LIGHTCGS * THOMPSON)) * units.UnitTime_in_s);
745 
746  fprintf(FdBlackHoles, "%g %d %g %g %g %g\n",
747  atime, total_bh, total_mass_holes, total_mdot, mdot_in_msun_per_year, total_mdoteddington);
748  fflush(FdBlackHoles);
749  }
750  walltime_measure("/BH/Info");
751 }
752 
753 
754 /*************************************************************************************/
755 /* DF routines */
756 static void
758 
759  int PI = P[n].PI;
760  int j;
761 
762  /***********************************************************************************/
763  /* This is Gizmo's implementation of dynamic friction */
764  /* c.f. section 3.1 in http://www.tapir.caltech.edu/~phopkins/public/notes_blackholes.pdf */
765  /* Compute dynamic friction accel when DF turned on */
766  /* averaged value for colomb logarithm and integral over the distribution function */
767  /* acc_friction = -4*pi*G^2 * Mbh * log(lambda) * rho * f_of_x * bhvel / |bhvel^3| */
768  /* f_of_x = [erf(x) - 2*x*exp(-x^2)/sqrt(pi)] */
769  /* lambda = b_max * v^2 / G / (M+m) */
770  /* b_max = Size of system (e.g. Rvir) */
771  /* v = Relative velocity of BH with respect to the environment */
772  /* M = Mass of BH */
773  /* m = individual mass elements composing the large system (e.g. m<<M) */
774  /* x = v/sqrt(2)/sigma */
775  /* sigma = width of the max. distr. of the host system */
776  /* (e.g. sigma = v_disp / 3 */
777 
778  if(BH_GET_PRIV(tw)->BH_SurroundingDensity[PI] > 0){
779  double bhvel;
780  double lambda, x, f_of_x;
781  const double a_erf = 8 * (M_PI - 3) / (3 * M_PI * (4. - M_PI));
782 
783  /* normalize velocity/dispersion */
784  BH_GET_PRIV(tw)->BH_SurroundingRmsVel[PI] /= BH_GET_PRIV(tw)->BH_SurroundingDensity[PI];
785  BH_GET_PRIV(tw)->BH_SurroundingRmsVel[PI] = sqrt(BH_GET_PRIV(tw)->BH_SurroundingRmsVel[PI]);
786  for(j = 0; j < 3; j++)
787  BH_GET_PRIV(tw)->BH_SurroundingVel[PI][j] /= BH_GET_PRIV(tw)->BH_SurroundingDensity[PI];
788 
789  /* Calculate Coulumb Logarithm */
790  bhvel = 0;
791  for(j = 0; j < 3; j++)
792  {
793  bhvel += pow(P[n].Vel[j] - BH_GET_PRIV(tw)->BH_SurroundingVel[PI][j], 2);
794  }
795  bhvel = sqrt(bhvel);
796 
797  /* There is no parameter in physical unit, so I kept everything in code unit */
798 
799  x = bhvel / sqrt(2) / (BH_GET_PRIV(tw)->BH_SurroundingRmsVel[PI] / 3);
800  /* First term is aproximation of the error function */
801  f_of_x = x / fabs(x) * sqrt(1 - exp(-x * x * (4 / M_PI + a_erf * x * x)
802  / (1 + a_erf * x * x))) - 2 * x / sqrt(M_PI) * exp(-x * x);
803  /* Floor at zero */
804  if (f_of_x < 0)
805  f_of_x = 0;
806 
807  lambda = 1. + blackhole_params.BH_DFbmax * pow((bhvel/BH_GET_PRIV(tw)->atime),2) / BH_GET_PRIV(tw)->CP->GravInternal / P[n].Mass;
808 
809  for(j = 0; j < 3; j++)
810  {
811  /* prevent DFAccel from exploding */
812  if(bhvel > 0){
813  BHP(n).DFAccel[j] = - 4. * M_PI * BH_GET_PRIV(tw)->CP->GravInternal * BH_GET_PRIV(tw)->CP->GravInternal * P[n].Mass * BH_GET_PRIV(tw)->BH_SurroundingDensity[PI] *
814  log(lambda) * f_of_x * (P[n].Vel[j] - BH_GET_PRIV(tw)->BH_SurroundingVel[PI][j]) / pow(bhvel, 3);
815  BHP(n).DFAccel[j] *= BH_GET_PRIV(tw)->atime; // convert to code unit of acceleration
816  BHP(n).DFAccel[j] *= blackhole_params.BH_DFBoostFactor; // Add a boost factor
817  }
818  else{
819  BHP(n).DFAccel[j] = 0;
820  }
821  }
822 #ifdef DEBUG
823  message(2,"x=%e, log(lambda)=%e, fof_x=%e, Mbh=%e, ratio=%e \n",
824  x,log(lambda),f_of_x,P[n].Mass,BHP(n).DFAccel[0]/P[n].GravAccel[0]);
825 #endif
826  }
827  else
828  {
829  message(2, "Dynamic Friction density is zero for BH %ld. Surroundingpart %d, mass %g, hsml %g, dens %g, pos %g %g %g.\n",
830  P[n].ID, BH_GET_PRIV(tw)->BH_SurroundingParticles[PI], BHP(n).Mass, P[n].Hsml, BHP(n).Density, P[n].Pos[0], P[n].Pos[1], P[n].Pos[2]);
831  for(j = 0; j < 3; j++)
832  {
833  BHP(n).DFAccel[j] = 0;
834  }
835  }
836 }
837 
838 /*******************************************************************/
839 static int
841  /*Black hole not being swallowed*/
842  return (P[n].Type == 5) && (!P[n].Swallowed);
843 }
844 
845 static void
847  int PI = P[place].PI;
848 
855 
856 }
857 
858 static void
860  /* SPH kernel width should be the only thing needed */
861  I->Hsml = P[place].Hsml;
862 }
863 
864 
865 static void
869  LocalTreeWalk * lv){
870 
871  if(iter->base.other == -1) {
872  iter->base.mask = 1 + 2 + 4 + 8 + 16 + 32;
873  iter->base.Hsml = I->Hsml;
876  return;
877  }
878 
879  int other = iter->base.other;
880  double r = iter->base.r;
881  double r2 = iter->base.r2;
882 
883  /* Collect Star/+DM/+Gas density/velocity for DF computation */
884  if(P[other].Type == 4 || (P[other].Type == 1 && blackhole_params.BH_DynFrictionMethod > 1) ||
885  (P[other].Type == 0 && blackhole_params.BH_DynFrictionMethod == 3) ){
886  if(r2 < iter->dynfric_kernel.HH) {
887  double u = r * iter->dynfric_kernel.Hinv;
888  double wk = density_kernel_wk(&iter->dynfric_kernel, u);
889  float mass_j = P[other].Mass;
890  int k;
891  O->SurroundingParticles += 1;
892  O->SurroundingDensity += (mass_j * wk);
893  for (k = 0; k < 3; k++){
894  O->SurroundingVel[k] += (mass_j * wk * P[other].Vel[k]);
895  O->SurroundingRmsVel += (mass_j * wk * pow(P[other].Vel[k], 2));
896  }
897  }
898  }
899 }
900 
901 /*************************************************************************************/
902 
903 
904 static void
906 {
907  int k;
908  int PI = P[i].PI;
909  if(BHP(i).Density > 0)
910  {
911  BH_GET_PRIV(tw)->BH_Entropy[PI] /= BHP(i).Density;
912  for(k = 0; k < 3; k++)
913  BH_GET_PRIV(tw)->BH_SurroundingGasVel[PI][k] /= BHP(i).Density;
914  }
915 
916  double mdot = 0; /* if no accretion model is enabled, we have mdot=0 */
917 
918  double rho = BHP(i).Density;
919  double bhvel = 0;
920  for(k = 0; k < 3; k++)
921  bhvel += pow(P[i].Vel[k] - BH_GET_PRIV(tw)->BH_SurroundingGasVel[PI][k], 2);
922 
923  bhvel = sqrt(bhvel);
924  bhvel /= BH_GET_PRIV(tw)->atime;
925  double rho_proper = rho * BH_GET_PRIV(tw)->a3inv;
926 
927  double soundspeed = blackhole_soundspeed(BH_GET_PRIV(tw)->BH_Entropy[PI], rho, BH_GET_PRIV(tw)->atime);
928 
929  /* Note: we take here a radiative efficiency of 0.1 for Eddington accretion */
930  double meddington = (4 * M_PI * GRAVITY * LIGHTCGS * PROTONMASS / (0.1 * LIGHTCGS * LIGHTCGS * THOMPSON)) * BHP(i).Mass
931  * BH_GET_PRIV(tw)->units.UnitTime_in_s / BH_GET_PRIV(tw)->CP->HubbleParam;
932 
933  double norm = pow((pow(soundspeed, 2) + pow(bhvel, 2)), 1.5);
934 
935  if(norm > 0)
936  mdot = 4. * M_PI * blackhole_params.BlackHoleAccretionFactor * BH_GET_PRIV(tw)->CP->GravInternal * BH_GET_PRIV(tw)->CP->GravInternal *
937  BHP(i).Mass * BHP(i).Mass * rho_proper / norm;
938 
940  mdot > blackhole_params.BlackHoleEddingtonFactor * meddington) {
941  mdot = blackhole_params.BlackHoleEddingtonFactor * meddington;
942  }
943  BHP(i).Mdot = mdot;
944 
945  double dtime = get_dloga_for_bin(P[i].TimeBin, P[i].Ti_drift) / BH_GET_PRIV(tw)->hubble;
946 
947  BHP(i).Mass += BHP(i).Mdot * dtime;
948 
949  /*************************************************************************/
950 
951  if(blackhole_params.BH_DRAG > 0){
952  /* a_BH = (v_gas - v_BH) Mdot/M_BH */
953  /* motivated by BH gaining momentum from the accreted gas */
954  /*c.f.section 3.2,in http://www.tapir.caltech.edu/~phopkins/public/notes_blackholes.pdf */
955  double fac = 0;
956  if (blackhole_params.BH_DRAG == 1) fac = BHP(i).Mdot/P[i].Mass;
957  if (blackhole_params.BH_DRAG == 2) fac = blackhole_params.BlackHoleEddingtonFactor * meddington/BHP(i).Mass;
958  fac *= BH_GET_PRIV(tw)->atime; /* dv = acc * kick_fac = acc * a^{-1}dt, therefore acc = a*dv/dt */
959  for(k = 0; k < 3; k++) {
960  BHP(i).DragAccel[k] = -(P[i].Vel[k] - BH_GET_PRIV(tw)->BH_SurroundingGasVel[PI][k])*fac;
961  }
962  }
963  else{
964  for(k = 0; k < 3; k++){
965  BHP(i).DragAccel[k] = 0;
966  }
967  }
968  /*************************************************************************/
969 
971  /* accumulate kenetic feedback energy by dE = epsilon x mdot x c^2 */
972  /* epsilon = Min(rho_BH/(BHKE_EffRhoFactor*rho_sfr),BHKE_EffCap) */
973  /* KE is released when exceeding injection energy threshold */
974  BH_GET_PRIV(tw)->KEflag[PI] = 0;
975  double Edd_ratio = BHP(i).Mdot/meddington;
976  double lam_thresh = blackhole_params.BHKE_EddingtonThrFactor;
978  if (lam_thresh > x)
979  lam_thresh = x;
980  if (Edd_ratio < lam_thresh){
981  /* mark this timestep is accumulating KE feedback energy */
982  BH_GET_PRIV(tw)->KEflag[PI] = 1;
983  const double rho_crit_baryon = BH_GET_PRIV(tw)->CP->OmegaBaryon * 3 * pow(BH_GET_PRIV(tw)->CP->Hubble, 2) / (8 * M_PI * BH_GET_PRIV(tw)->CP->GravInternal);
984  const double rho_sfr = blackhole_params.BHKE_SfrCritOverDensity * rho_crit_baryon;
985  double epsilon = (BHP(i).Density/rho_sfr)/blackhole_params.BHKE_EffRhoFactor;
986  if (epsilon > blackhole_params.BHKE_EffCap){
987  epsilon = blackhole_params.BHKE_EffCap;
988  }
989 
990  BHP(i).KineticFdbkEnergy += epsilon * (BHP(i).Mdot * dtime * pow(LIGHTCGS / BH_GET_PRIV(tw)->units.UnitVelocity_in_cm_per_s, 2));
991  }
992 
993  /* decide whether to release KineticFdbkEnergy*/
994  double vdisp = 0;
995  double numdm = BH_GET_PRIV(tw)->NumDM[PI];
996  if (numdm>0){
997  vdisp = BH_GET_PRIV(tw)->V2sumDM[PI]/numdm;
998  int d;
999  for(d = 0; d<3; d++){
1000  vdisp -= pow(BH_GET_PRIV(tw)->V1sumDM[PI][d]/numdm,2);
1001  }
1002  if(vdisp > 0)
1003  vdisp = sqrt(vdisp / 3);
1004  }
1005 
1006  double KE_thresh = 0.5 * vdisp * vdisp * BH_GET_PRIV(tw)->MgasEnc[PI];
1007  KE_thresh *= blackhole_params.BHKE_InjEnergyThr;
1008 
1009  if (BHP(i).KineticFdbkEnergy > KE_thresh){
1010  /* mark KineticFdbkEnergy is ready to be released in the feedback treewalk */
1011  BH_GET_PRIV(tw)->KEflag[PI] = 2;
1012  }
1013  }
1014 }
1015 
1016 static void
1018 {
1019  int j;
1020  BH_GET_PRIV(tw)->MinPot[P[n].PI] = P[n].Potential;
1021 
1022  for(j = 0; j < 3; j++) {
1023  BHP(n).MinPotPos[j] = P[n].Pos[j];
1024  }
1025 
1026 }
1027 
1028 static void
1030 {
1031  const int PI = P[n].PI;
1032  if(BH_GET_PRIV(tw)->BH_accreted_BHMass[PI] > 0){
1033  BHP(n).Mass += BH_GET_PRIV(tw)->BH_accreted_BHMass[PI];
1034  }
1035  if(BH_GET_PRIV(tw)->BH_accreted_Mass[PI] > 0)
1036  {
1037  /* velocity feedback due to accretion; momentum conservation.
1038  * This does nothing with repositioning on.*/
1039  const MyFloat accmass = BH_GET_PRIV(tw)->BH_accreted_Mass[PI];
1040  int k;
1041  /* Need to add the momentum from Mtrack as well*/
1042  for(k = 0; k < 3; k++)
1043  P[n].Vel[k] = (P[n].Vel[k] * P[n].Mass + BH_GET_PRIV(tw)->BH_accreted_momentum[PI][k]) /
1044  (P[n].Mass + accmass + BH_GET_PRIV(tw)->BH_accreted_Mtrack[PI]);
1045  P[n].Mass += accmass;
1046  }
1047 
1049  if(BH_GET_PRIV(tw)->BH_accreted_Mtrack[PI] > 0){
1050  BHP(n).Mtrack += BH_GET_PRIV(tw)->BH_accreted_Mtrack[PI];
1051  }
1052  if(BHP(n).Mtrack > blackhole_params.SeedBHDynMass){
1053  BHP(n).Mtrack = blackhole_params.SeedBHDynMass; /*cap Mtrack at SeedBHDynMass*/
1054  }
1055  }
1056  /* Reset KineticFdbkEnerg to 0 after released */
1057  if(BH_GET_PRIV(tw)->KEflag[PI] == 2){
1058  BHP(n).KineticFdbkEnergy = 0;
1059  }
1060 }
1061 
1062 static void
1066  LocalTreeWalk * lv)
1067 {
1068 
1069  if(iter->base.other == -1) {
1070  O->BH_minTimeBin = TIMEBINS;
1071  O->encounter = 0;
1072 
1074 
1075  int d;
1076  for(d = 0; d < 3; d++) {
1077  O->BH_MinPotPos[d] = I->base.Pos[d];
1078  }
1079  iter->base.mask = 1 + 2 + 4 + 8 + 16 + 32;
1080  iter->base.Hsml = I->Hsml;
1082 
1085  return;
1086  }
1087 
1088  int other = iter->base.other;
1089  double r = iter->base.r;
1090  double r2 = iter->base.r2;
1091 
1092  if(P[other].Mass < 0) return;
1093 
1094  if(P[other].Type != 5) {
1095  if (O->BH_minTimeBin > P[other].TimeBin)
1096  O->BH_minTimeBin = P[other].TimeBin;
1097  }
1098 
1099  /* BH does not accrete wind */
1100  if(winds_is_particle_decoupled(other)) return;
1101 
1102  /* Find the black hole potential minimum. */
1103  if(r2 < iter->accretion_kernel.HH)
1104  {
1105  if(P[other].Potential < O->BH_MinPot)
1106  {
1107  int d;
1108  O->BH_MinPot = P[other].Potential;
1109  for(d = 0; d < 3; d++) {
1110  O->BH_MinPotPos[d] = P[other].Pos[d];
1111  O->BH_MinPotVel[d] = P[other].Vel[d];
1112  }
1113  }
1114  }
1115 
1116  /* Accretion / merger doesn't do self interaction */
1117  if(P[other].ID == I->ID) return;
1118 
1119  /* we have a black hole merger. Now we use 2 times GravitationalSoftening as merging criteria, previously we used the SPH smoothing length. */
1120  if(P[other].Type == 5 && r < (2*FORCE_SOFTENING(0,1)/2.8))
1121  {
1122  O->encounter = 1; // mark the event when two BHs encounter each other
1123 
1124  int flag = 0; // the flag for BH merge
1125 
1126  if(blackhole_params.BlackHoleRepositionEnabled == 1) // directly merge if reposition is enabled
1127  flag = 1;
1129  flag = 1;
1130  /* apply Grav Bound check only when Reposition is disabled, otherwise BHs would be repositioned to the same location but not merge */
1132 
1133  double dx[3];
1134  double dv[3];
1135  double da[3];
1136  int d;
1137 
1138  for(d = 0; d < 3; d++){
1139  dx[d] = NEAREST(I->base.Pos[d] - P[other].Pos[d], PartManager->BoxSize);
1140  dv[d] = I->Vel[d] - P[other].Vel[d];
1141  /* we include long range PM force, short range force and DF */
1142  da[d] = (I->Accel[d] - P[other].GravAccel[d] - P[other].GravPM[d] - BHP(other).DFAccel[d]);
1143  }
1144  flag = check_grav_bound(dx,dv,da, BH_GET_PRIV(lv->tw)->atime);
1145  /*if(flag == 0)
1146  message(0, "dx %g %g %g dv %g %g %g da %g %g %g\n",dx[0], dx[1], dx[2], dv[0], dv[1], dv[2], da[0], da[1], da[2]);*/
1147  }
1148 
1149  /* do the merge */
1150  if(flag == 1)
1151  {
1152  O->encounter = 0;
1153  MyIDType readid, newswallowid;
1154 
1155  #pragma omp atomic read
1156  readid = (BHP(other).SwallowID);
1157 
1158  /* Here we mark the black hole as "ready to be swallowed" using the SwallowID.
1159  * The actual swallowing is done in the feedback treewalk by setting Swallowed = 1
1160  * and merging the masses.*/
1161  do {
1162  /* Generate the new ID from the old*/
1163  if(readid != (MyIDType) -1 && readid < I->ID ) {
1164  /* Already marked, prefer to be swallowed by a bigger ID */
1165  newswallowid = I->ID;
1166  } else if(readid == (MyIDType) -1 && P[other].ID < I->ID) {
1167  /* Unmarked, the BH with bigger ID swallows */
1168  newswallowid = I->ID;
1169  }
1170  else
1171  break;
1172  /* Swap in the new id only if the old one hasn't changed:
1173  * in principle an extension, but supported on at least clang >= 9, gcc >= 5 and icc >= 18.*/
1174  } while(!__atomic_compare_exchange_n(&(BHP(other).SwallowID), &readid, newswallowid, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
1175  }
1176  }
1177 
1178 
1179  if(P[other].Type == 0) {
1180  if(r2 < iter->accretion_kernel.HH) {
1181  double u = r * iter->accretion_kernel.Hinv;
1182  double wk = density_kernel_wk(&iter->accretion_kernel, u);
1183  float mass_j = P[other].Mass;
1184 
1185  O->SmoothedEntropy += (mass_j * wk * SPHP(other).Entropy);
1186  O->GasVel[0] += (mass_j * wk * P[other].Vel[0]);
1187  O->GasVel[1] += (mass_j * wk * P[other].Vel[1]);
1188  O->GasVel[2] += (mass_j * wk * P[other].Vel[2]);
1189 
1190  /* here we have a gas particle; check for swallowing */
1191 
1192  /* compute accretion probability */
1193  double p = 0;
1194 
1195  MyFloat BHPartMass = I->Mass;
1196  /* If SeedBHDynMass is larger than gas paricle mass, we use Mtrack to do the gas accretion
1197  * when BHP.Mass < SeedBHDynMass. Mtrack is initialized as gas particle mass and is capped
1198  * at SeedBHDynMass. Mtrack traces the BHP.Mass by stochastically swallowing gas and
1199  * therefore ensures mass conservation.*/
1201  BHPartMass = I->Mtrack;
1202 
1203  /* This is an averaged Mdot, because Mdot increases BH_Mass but not Mass.
1204  * So if the total accretion is significantly above the dynamical mass,
1205  * a particle is swallowed. */
1206  if((I->BH_Mass - BHPartMass) > 0 && I->Density > 0)
1207  p = (I->BH_Mass - BHPartMass) * wk / I->Density;
1208 
1209  /* compute random number, uniform in [0,1] */
1210  const double w = get_random_number(P[other].ID);
1211  if(w < p)
1212  {
1213  MyIDType * SPH_SwallowID = BH_GET_PRIV(lv->tw)->SPH_SwallowID;
1214  MyIDType readid, newswallowid;
1215  #pragma omp atomic read
1216  readid = SPH_SwallowID[P[other].PI];
1217  do {
1218  /* Already marked, prefer to be swallowed by a bigger ID.
1219  * Not marked, the SwallowID is 0 */
1220  if(readid < I->ID + 1) {
1221  newswallowid = I->ID + 1;
1222  }
1223  else
1224  break;
1225  /* Swap in the new id only if the old one hasn't changed*/
1226  } while(!__atomic_compare_exchange_n(&SPH_SwallowID[P[other].PI], &readid, newswallowid, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
1227  }
1228  }
1229 
1230  if(r2 < iter->feedback_kernel.HH) {
1231  /* update the feedback weighting */
1232  double mass_j;
1234  double redshift = 1./BH_GET_PRIV(lv->tw)->atime - 1;
1235  double nh0 = get_neutral_fraction_sfreff(redshift, BH_GET_PRIV(lv->tw)->hubble, &P[other], &SPHP(other));
1236  if(r2 > 0)
1237  O->FeedbackWeightSum += (P[other].Mass * nh0) / r2;
1238  } else {
1240  mass_j = P[other].Mass;
1241  } else {
1242  mass_j = P[other].Hsml * P[other].Hsml * P[other].Hsml;
1243  }
1245  double u = r * iter->feedback_kernel.Hinv;
1246  O->FeedbackWeightSum += (mass_j *
1248  );
1249  } else {
1250  O->FeedbackWeightSum += (mass_j);
1251  }
1252  }
1253  }
1254  }
1255 
1256  /* collect info for sigmaDM and Menc for kinetic feedback */
1258  if(P[other].Type == 1 && r2 < iter->feedback_kernel.HH){
1259  O->NumDM += 1;
1260  int d;
1261  for(d = 0; d < 3; d++){
1262  double vel = P[other].Vel[d] - I->Vel[d];
1263  O->V1sumDM[d] += vel;
1264  O->V2sumDM += vel * vel;
1265  }
1266  }
1267  if(P[other].Type == 0 && r2 < iter->feedback_kernel.HH){
1268  O->MgasEnc += P[other].Mass;
1269  }
1270  }
1271 }
1272 
1273 
1277 static void
1281  LocalTreeWalk * lv)
1282 {
1283 
1284  if(iter->base.other == -1) {
1285 
1286  iter->base.mask = 1 + 32;
1287  iter->base.Hsml = I->Hsml;
1288  /* Swallow is symmetric, but feedback dumping is asymetric;
1289  * we apply a cut in r to break the symmetry. */
1292  return;
1293  }
1294 
1295  int other = iter->base.other;
1296  double r2 = iter->base.r2;
1297  double r = iter->base.r;
1298  /* Exclude self interaction */
1299 
1300  if(P[other].ID == I->ID) return;
1301 
1302  /* BH does not accrete wind */
1303  if(winds_is_particle_decoupled(other))
1304  return;
1305 
1306 
1307  /* we have a black hole merger! */
1308  if(P[other].Type == 5 && BHP(other).SwallowID != (MyIDType) -1)
1309  {
1310  if(BHP(other).SwallowID != I->ID) return;
1311 
1312  /* Swallow the particle*/
1313  /* A note on Swallowed vs SwallowID: black hole particles which have been completely swallowed
1314  * (ie, their mass has been added to another particle) have Swallowed = 1.
1315  * These particles are ignored in future tree walks. We set Swallowed here so that this process is atomic:
1316  * the total mass in the tree is always conserved.
1317  *
1318  * We also set SwallowID != -1 in the accretion treewalk. This marks the black hole as ready to be swallowed
1319  * by something. It is actually swallowed only by the nearby black hole with the largest ID. In rare cases
1320  * it may happen that the swallower is itself swallowed before swallowing the marked black hole. However,
1321  * in practice the new swallower should also take the marked black hole next timestep.
1322  */
1323 
1324  BHP(other).SwallowTime = BH_GET_PRIV(lv->tw)->atime;
1325  P[other].Swallowed = 1;
1326  O->BH_CountProgs += BHP(other).CountProgs;
1327  O->BH_Mass += (BHP(other).Mass);
1328 
1329  if (blackhole_params.SeedBHDynMass>0 && I->Mtrack>0){
1330  /* Make sure that the final dynamic mass (I->Mass + O->Mass) = MAX(SeedDynMass, total_gas_accreted),
1331  I->Mtrack only need to be updated when I->Mtrack < SeedBHDynMass, */
1333  /* I->Mass = SeedBHDynMass, total_gas_accreted = I->Mtrack + BHP(other).Mtrack */
1334  O->acMtrack += BHP(other).Mtrack;
1335  double delta_m = I->Mtrack + BHP(other).Mtrack - blackhole_params.SeedBHDynMass;
1336  O->Mass += DMAX(0,delta_m);
1337  }
1339  /* I->Mass = gas_accreted, total_gas_accreted = I->Mass + BHP(other).Mtrack */
1340  O->Mass += BHP(other).Mtrack;
1341  }
1343  /* I->Mass = SeedBHDynMass, P[other].Mass = gas_accreted,
1344  total_gas_accreted = I->track + P[other].Mass */
1345  O->acMtrack += BHP(other).Mtrack;
1346  O->Mass += (P[other].Mass + I->Mtrack - blackhole_params.SeedBHDynMass);
1347  }
1349  /* trivial case, total_gas_accreted = I->Mass + P[other].Mass */
1350  O->Mass += P[other].Mass;
1351  }
1352  }
1353  else{
1354  O->Mass += P[other].Mass;
1355  }
1356 
1357  /* Conserve momentum during accretion*/
1358  int d;
1359  for(d = 0; d < 3; d++)
1360  O->AccretedMomentum[d] += (P[other].Mass * P[other].Vel[d]);
1361 
1362  if(BHP(other).SwallowTime < BH_GET_PRIV(lv->tw)->atime)
1363  endrun(2, "Encountered BH %i swallowed at earlier time %g\n", other, BHP(other).SwallowTime);
1364 
1365  int tid = omp_get_thread_num();
1366  BH_GET_PRIV(lv->tw)->N_BH_swallowed[tid]++;
1367 
1368  }
1369 
1370  MyIDType * SPH_SwallowID = BH_GET_PRIV(lv->tw)->SPH_SwallowID;
1371 
1372  /* perform thermal or kinetic feedback into non-swallowed particles. */
1373  if(P[other].Type == 0 && SPH_SwallowID[P[other].PI] == 0 &&
1374  (r2 < iter->feedback_kernel.HH && P[other].Mass > 0) &&
1375  (I->FeedbackWeightSum > 0))
1376  {
1377  double u = r * iter->feedback_kernel.Hinv;
1378  double wk = 1.0;
1379  double mass_j;
1380 
1382  mass_j = P[other].Mass;
1383  } else {
1384  mass_j = P[other].Hsml * P[other].Hsml * P[other].Hsml;
1385  }
1387  wk = density_kernel_wk(&iter->feedback_kernel, u);
1388 
1389  /* thermal feedback */
1390  if(I->FeedbackEnergy > 0 && I->FdbkChannel == 0){
1391  const double injected_BH = I->FeedbackEnergy * mass_j * wk / I->FeedbackWeightSum;
1392  /* Set a flag for star-forming particles:
1393  * we want these to cool to the EEQOS via
1394  * tcool rather than trelax.*/
1395  if(sfreff_on_eeqos(&SPHP(other), BH_GET_PRIV(lv->tw)->a3inv)) {
1396  /* We cannot atomically set a bitfield.
1397  * This flag is never read in this thread loop, and we are careful not to
1398  * do this with a swallowed particle (as this can race with IsGarbage being set).
1399  * So lack of atomicity is (I think) not a problem.*/
1400  //#pragma omp atomic write
1401  P[other].BHHeated = 1;
1402  }
1403  const double enttou = pow(SPHP(other).Density * BH_GET_PRIV(lv->tw)->a3inv, GAMMA_MINUS1) / GAMMA_MINUS1;
1404  const double uu_in_cgs = BH_GET_PRIV(lv->tw)->units.UnitEnergy_in_cgs / BH_GET_PRIV(lv->tw)->units.UnitMass_in_g;
1405 
1406  double entold, entnew;
1407  double * entptr = &(SPHP(other).Entropy);
1408  #pragma omp atomic read
1409  entold = *entptr;
1410  do {
1411  entnew = add_injected_BH_energy(entold * enttou, injected_BH, P[other].Mass, uu_in_cgs) / enttou;
1412  /* Swap in the new gas entropy only if the old one hasn't changed.*/
1413  } while(!__atomic_compare_exchange(entptr, &entold, &entnew, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
1414  }
1415 
1416  /* kinetic feedback */
1417  if(I->KEFeedbackEnergy > 0 && I->FdbkChannel == 1){
1418  /* Kick the gas particle*/
1419  double dvel = sqrt(2 * I->KEFeedbackEnergy * wk / I->Density);
1420  double dir[3];
1421  get_random_dir(other, dir);
1422  int j;
1423  for(j = 0; j < 3; j++){
1424  #pragma omp atomic update
1425  P[other].Vel[j] += (dvel*dir[j]);
1426  }
1427  }
1428  }
1429 
1430  /* Swallowing a gas */
1431  /* This will only be true on one thread so we do not need a lock here*/
1432  /* Note that it will rarely happen that gas is swallowed by a BH which is itself swallowed.
1433  * In that case we do not swallow this particle: all swallowing changes before this are temporary*/
1434  if(P[other].Type == 0 && SPH_SwallowID[P[other].PI] == I->ID+1)
1435  {
1436  /* We do not know how to notify the tree of mass changes. so
1437  * enforce a mass conservation. */
1439  /* we just add gas mass to Mtrack instead of dynMass */
1440  O->acMtrack += P[other].Mass;
1441  } else
1442  O->Mass += P[other].Mass;
1443  P[other].Mass = 0;
1444  /* Conserve momentum during accretion*/
1445  int d;
1446  for(d = 0; d < 3; d++)
1447  O->AccretedMomentum[d] += (P[other].Mass * P[other].Vel[d]);
1448 
1450 
1451  int tid = omp_get_thread_num();
1452  BH_GET_PRIV(lv->tw)->N_sph_swallowed[tid]++;
1453  }
1454 }
1455 
1456 static void
1458 {
1459  int k;
1460  MyFloat * MinPot = BH_GET_PRIV(tw)->MinPot;
1461 
1462  int PI = P[place].PI;
1463  if(MinPot[PI] > remote->BH_MinPot)
1464  {
1465  BHP(place).JumpToMinPot = blackhole_params.BlackHoleRepositionEnabled;
1466  MinPot[PI] = remote->BH_MinPot;
1467  for(k = 0; k < 3; k++) {
1468  /* Movement occurs in drift.c */
1469  BHP(place).MinPotPos[k] = remote->BH_MinPotPos[k];
1470  BHP(place).MinPotVel[k] = remote->BH_MinPotVel[k];
1471  }
1472  }
1473 
1474  BHP(place).encounter = remote->encounter;
1475 
1476  if (mode == 0 || BHP(place).minTimeBin > remote->BH_minTimeBin) {
1477  BHP(place).minTimeBin = remote->BH_minTimeBin;
1478  }
1479 
1482  for (k = 0; k < 3; k++){
1483  TREEWALK_REDUCE(BH_GET_PRIV(tw)->BH_SurroundingGasVel[PI][k], remote->GasVel[k]);
1484  TREEWALK_REDUCE(BH_GET_PRIV(tw)->V1sumDM[PI][k], remote->V1sumDM[k]);
1485  }
1486  TREEWALK_REDUCE(BH_GET_PRIV(tw)->NumDM[PI], remote->NumDM);
1487  TREEWALK_REDUCE(BH_GET_PRIV(tw)->V2sumDM[PI], remote->V2sumDM);
1488  TREEWALK_REDUCE(BH_GET_PRIV(tw)->MgasEnc[PI], remote->MgasEnc);
1489 }
1490 
1491 static void
1493 {
1494  int k;
1495  for(k = 0; k < 3; k++)
1496  {
1497  I->Vel[k] = P[place].Vel[k];
1498  I->Accel[k] = P[place].GravAccel[k] + P[place].GravPM[k] + BHP(place).DFAccel[k];
1499  }
1500  I->Hsml = P[place].Hsml;
1501  I->Mass = P[place].Mass;
1502  I->BH_Mass = BHP(place).Mass;
1503  I->Density = BHP(place).Density;
1504  I->ID = P[place].ID;
1505  I->Mtrack = BHP(place).Mtrack;
1506 }
1507 
1508 static int
1510 {
1511  /*Black hole not being swallowed*/
1512  return (P[n].Type == 5) && (!P[n].Swallowed) && (BHP(n).SwallowID == (MyIDType) -1);
1513 }
1514 
1515 static void
1517 {
1518  I->Hsml = P[i].Hsml;
1519  I->BH_Mass = BHP(i).Mass;
1520  I->ID = P[i].ID;
1521  I->Mtrack = BHP(i).Mtrack;
1522  I->Density = BHP(i).Density;
1523  int PI = P[i].PI;
1524 
1525  I->FeedbackWeightSum = BH_GET_PRIV(tw)->BH_FeedbackWeightSum[PI];
1526  I->FdbkChannel = 0; /* thermal feedback mode */
1527 
1528  double dtime = get_dloga_for_bin(P[i].TimeBin, P[i].Ti_drift) / BH_GET_PRIV(tw)->hubble;
1529 
1530  I->FeedbackEnergy = blackhole_params.BlackHoleFeedbackFactor * 0.1 * BHP(i).Mdot * dtime *
1532  I->KEFeedbackEnergy = 0;
1533  if (blackhole_params.BlackHoleKineticOn == 1 && BH_GET_PRIV(tw)->KEflag[PI] > 0){
1534  I->FdbkChannel = 1; /* kinetic feedback mode, (no thermal feedback for this timestep) */
1535  /* KEflag = 1: KEFeedbackEnergy is accumulating; KEflag = 2: KEFeedbackEnergy is released. */
1536  if (BH_GET_PRIV(tw)->KEflag[PI] == 2){
1537  I->KEFeedbackEnergy = BHP(i).KineticFdbkEnergy;
1538  }
1539  }
1540 }
1541 
1542 static void
1544 {
1545  int k;
1546  int PI = P[place].PI;
1547 
1548  TREEWALK_REDUCE(BH_GET_PRIV(tw)->BH_accreted_Mass[PI], remote->Mass);
1551  for(k = 0; k < 3; k++) {
1553  }
1554 
1555  TREEWALK_REDUCE(BHP(place).CountProgs, remote->BH_CountProgs);
1556 }
1557 
1558 /* Sample from a power law to get the initial BH mass*/
1559 static double
1561 {
1562  /* compute random number, uniform in [0,1] */
1563  const double w = get_random_number(ID+23);
1564  /* Normalisation for this power law index*/
1567  /* Sample from the CDF:
1568  * w = [M^(1+x) - M_0^(1+x)]/[M_1^(1+x) - M_0^(1+x)]
1569  * w [M_1^(1+x) - M_0^(1+x)] + M_0^(1+x) = M^(1+x)
1570  * M = pow((w [M_1^(1+x) - M_0^(1+x)] + M_0^(1+x)), 1/(1+x))*/
1571  double mass = pow(w * norm + pow(blackhole_params.SeedBlackHoleMass, 1+blackhole_params.SeedBlackHoleMassIndex),
1573  return mass;
1574 }
1575 
1576 void blackhole_make_one(int index, const double atime) {
1577  if(P[index].Type != 0)
1578  endrun(7772, "Only Gas turns into blackholes, what's wrong?");
1579 
1580  int child = index;
1581 
1582  /* Make the new particle a black hole: use all the P[i].Mass
1583  * so we don't have lots of low mass tracers.
1584  * If the BH seed mass is small this may lead to a mismatch
1585  * between the gas and BH mass. */
1586  child = slots_convert(child, 5, -1, PartManager, SlotsManager);
1587 
1588  BHP(child).base.ID = P[child].ID;
1589  /* The accretion mass should always be the seed black hole mass,
1590  * irrespective of the gravitational mass of the particle.*/
1592  BHP(child).Mass = bh_powerlaw_seed_mass(P[child].ID);
1593  else
1594  BHP(child).Mass = blackhole_params.SeedBlackHoleMass;
1595 
1596  BHP(child).Mseed = BHP(child).Mass;
1597  BHP(child).Mdot = 0;
1598  BHP(child).FormationTime = atime;
1599  BHP(child).SwallowID = (MyIDType) -1;
1600  BHP(child).Density = 0;
1601 
1602  /* It is important to initialize MinPotPos to the current position of
1603  * a BH to avoid drifting to unknown locations (0,0,0) immediately
1604  * after the BH is created. */
1605  int j;
1606  for(j = 0; j < 3; j++) {
1607  BHP(child).MinPotPos[j] = P[child].Pos[j];
1608  BHP(child).DFAccel[j] = 0;
1609  BHP(child).DragAccel[j] = 0;
1610  }
1611  BHP(child).JumpToMinPot = 0;
1612  BHP(child).CountProgs = 1;
1613 
1615  BHP(child).Mtrack = P[child].Mass;
1616  P[child].Mass = blackhole_params.SeedBHDynMass;
1617  }
1618  else{
1619  BHP(child).Mtrack = -1; /* This column is not used then. */
1620  }
1621  /* Initialize KineticFdbkEnergy, keep zero if BlackHoleKineticOn is not turned on */
1622  BHP(child).KineticFdbkEnergy = 0;
1623 }
int BHGetRepositionEnabled(void)
Definition: blackhole.c:60
static void blackhole_accretion_postprocess(int n, TreeWalk *tw)
Definition: blackhole.c:905
#define BH_GET_PRIV(tw)
Definition: blackhole.c:201
static void blackhole_accretion_copy(int place, TreeWalkQueryBHAccretion *I, TreeWalk *tw)
Definition: blackhole.c:1492
void blackhole(const ActiveParticles *act, double atime, Cosmology *CP, ForceTree *tree, const struct UnitSystem units, FILE *FdBlackHoles, FILE *FdBlackholeDetails)
Definition: blackhole.c:516
struct BlackholeParams blackhole_params
static void blackhole_dynfric_reduce(int place, TreeWalkResultBHDynfric *remote, enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: blackhole.c:846
static void blackhole_feedback_postprocess(int n, TreeWalk *tw)
Definition: blackhole.c:1029
static void blackhole_dynfric_ngbiter(TreeWalkQueryBHDynfric *I, TreeWalkResultBHDynfric *O, TreeWalkNgbIterBHDynfric *iter, LocalTreeWalk *lv)
Definition: blackhole.c:866
#define BHPOTVALUEINIT
Definition: blackhole.c:361
void blackhole_make_one(int index, const double atime)
Definition: blackhole.c:1576
void set_blackhole_params(ParameterSet *ps)
Definition: blackhole.c:257
static void blackhole_dynfric_copy(int place, TreeWalkQueryBHDynfric *I, TreeWalk *tw)
Definition: blackhole.c:859
static int blackhole_dynfric_haswork(int n, TreeWalk *tw)
Definition: blackhole.c:840
static void blackhole_feedback_ngbiter(TreeWalkQueryBHFeedback *I, TreeWalkResultBHFeedback *O, TreeWalkNgbIterBHFeedback *iter, LocalTreeWalk *lv)
Definition: blackhole.c:1278
static void blackhole_feedback_copy(int place, TreeWalkQueryBHFeedback *I, TreeWalk *tw)
Definition: blackhole.c:1516
static int check_grav_bound(double dx[3], double dv[3], double da[3], const double atime)
Definition: blackhole.c:400
static void blackhole_accretion_preprocess(int n, TreeWalk *tw)
Definition: blackhole.c:1017
static double blackhole_soundspeed(double entropy, double rho, const double atime)
Definition: blackhole.c:363
static void blackhole_accretion_ngbiter(TreeWalkQueryBHAccretion *I, TreeWalkResultBHAccretion *O, TreeWalkNgbIterBHAccretion *iter, LocalTreeWalk *lv)
Definition: blackhole.c:1063
static void blackhole_dynfric_postprocess(int n, TreeWalk *tw)
Definition: blackhole.c:757
static int blackhole_feedback_haswork(int n, TreeWalk *tw)
Definition: blackhole.c:1509
static void blackhole_accretion_reduce(int place, TreeWalkResultBHAccretion *remote, enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: blackhole.c:1457
static int get_random_dir(int i, double dir[3])
Definition: blackhole.c:386
static double bh_powerlaw_seed_mass(MyIDType ID)
Definition: blackhole.c:1560
static double add_injected_BH_energy(double unew, double injected_BH_energy, double mass, double uu_in_cgs)
Definition: blackhole.c:374
static void blackhole_feedback_reduce(int place, TreeWalkResultBHFeedback *remote, enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: blackhole.c:1543
struct __attribute__((__packed__))
Definition: blackhole.c:204
static void collect_BH_info(int *ActiveBlackHoles, int NumActiveBlackHoles, struct BHPriv *priv, FILE *FdBlackholeDetails)
Definition: blackhole.c:423
BlackHoleFeedbackMethod
Definition: blackhole.h:8
@ BH_FEEDBACK_OPTTHIN
Definition: blackhole.h:13
@ BH_FEEDBACK_MASS
Definition: blackhole.h:11
@ BH_FEEDBACK_SPLINE
Definition: blackhole.h:10
double hubble_function(const Cosmology *CP, double a)
Definition: cosmology.c:58
enum DensityKernelType GetDensityKernelType(void)
Definition: density.c:63
double(* wk)(DensityKernel *kernel, double q)
Definition: densitykernel.c:94
void density_kernel_init(DensityKernel *kernel, double H, enum DensityKernelType type)
double density_kernel_wk(DensityKernel *kernel, double u)
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147
double FORCE_SOFTENING(int i, int type)
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define ta_malloc(name, type, nele)
Definition: mymalloc.h:25
#define ta_free(p)
Definition: mymalloc.h:28
#define myfree(x)
Definition: mymalloc.h:19
#define report_memory_usage(x)
Definition: mymalloc.h:30
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
int param_get_enum(ParameterSet *ps, const char *name)
Definition: paramset.c:378
struct part_manager_type PartManager[1]
Definition: partmanager.c:11
#define P
Definition: partmanager.h:88
#define NEAREST(x, BoxSize)
Definition: partmanager.h:99
#define SOLAR_MASS
Definition: physconst.h:6
#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
#define GAMMA
Definition: physconst.h:34
#define THOMPSON
Definition: physconst.h:23
#define SEC_PER_YEAR
Definition: physconst.h:32
#define BOLTZMANN
Definition: physconst.h:11
#define LIGHTCGS
Definition: physconst.h:18
static Cosmology * CP
Definition: power.c:27
int sfreff_on_eeqos(const struct sph_particle_data *sph, const double a3inv)
Definition: sfr_eff.c:486
double get_neutral_fraction_sfreff(double redshift, double hubble, struct particle_data *partdata, struct sph_particle_data *sphdata)
Definition: sfr_eff.c:520
void slots_mark_garbage(int i, struct part_manager_type *pman, struct slots_manager_type *sman)
Definition: slotsmanager.c:577
struct slots_manager_type SlotsManager[1]
Definition: slotsmanager.c:7
int slots_convert(int parent, int ptype, int placement, struct part_manager_type *pman, struct slots_manager_type *sman)
Definition: slotsmanager.c:60
#define BhP
Definition: slotsmanager.h:121
#define BHP(i)
Definition: slotsmanager.h:125
#define SPHP(i)
Definition: slotsmanager.h:124
int * ActiveParticle
Definition: timestep.h:13
int64_t NumActiveParticle
Definition: timestep.h:12
double hubble
Definition: blackhole.c:194
int64_t * N_sph_swallowed
Definition: blackhole.c:198
MyFloat(* BH_SurroundingVel)[3]
Definition: blackhole.c:167
double a3inv
Definition: blackhole.c:193
MyFloat * BH_accreted_BHMass
Definition: blackhole.c:176
MyFloat * MinPot
Definition: blackhole.c:159
MyFloat * MgasEnc
Definition: blackhole.c:187
MyFloat * BH_accreted_Mass
Definition: blackhole.c:175
struct UnitSystem units
Definition: blackhole.c:195
MyFloat * BH_SurroundingDensity
Definition: blackhole.c:165
int64_t * N_BH_swallowed
Definition: blackhole.c:199
MyFloat * BH_SurroundingRmsVel
Definition: blackhole.c:168
MyFloat * BH_Entropy
Definition: blackhole.c:160
int * KEflag
Definition: blackhole.c:189
MyFloat(* BH_SurroundingGasVel)[3]
Definition: blackhole.c:161
double atime
Definition: blackhole.c:192
MyIDType * SPH_SwallowID
Definition: blackhole.c:157
MyFloat(* V1sumDM)[3]
Definition: blackhole.c:185
int * BH_SurroundingParticles
Definition: blackhole.c:166
MyFloat * BH_FeedbackWeightSum
Definition: blackhole.c:181
MyFloat * NumDM
Definition: blackhole.c:184
Cosmology * CP
Definition: blackhole.c:196
MyFloat * V2sumDM
Definition: blackhole.c:186
MyFloat * BH_accreted_Mtrack
Definition: blackhole.c:177
MyFloat(* BH_accreted_momentum)[3]
Definition: blackhole.c:172
double BHKE_EffRhoFactor
Definition: blackhole.c:39
enum BlackHoleFeedbackMethod BlackHoleFeedbackMethod
Definition: blackhole.c:30
double SeedBHDynMass
Definition: blackhole.c:51
double BHKE_EddingtonMFactor
Definition: blackhole.c:36
int BlackHoleKineticOn
Definition: blackhole.c:34
int BH_DFBoostFactor
Definition: blackhole.c:47
double BlackHoleAccretionFactor
Definition: blackhole.c:28
double BHKE_SfrCritOverDensity
Definition: blackhole.c:42
double BHKE_EddingtonMPivot
Definition: blackhole.c:37
double SeedBlackHoleMassIndex
Definition: blackhole.c:55
int BlackHoleRepositionEnabled
Definition: blackhole.c:32
double BH_DFbmax
Definition: blackhole.c:48
double BHKE_EffCap
Definition: blackhole.c:40
int MergeGravBound
Definition: blackhole.c:44
double BlackHoleFeedbackFactor
Definition: blackhole.c:29
double BHKE_EddingtonThrFactor
Definition: blackhole.c:35
double SeedBlackHoleMass
Definition: blackhole.c:53
double BlackHoleEddingtonFactor
Definition: blackhole.c:31
int BH_DynFrictionMethod
Definition: blackhole.c:46
double BHKE_InjEnergyThr
Definition: blackhole.c:41
double BHKE_EddingtonMIndex
Definition: blackhole.c:38
double MaxSeedBlackHoleMass
Definition: blackhole.c:54
double Hubble
Definition: cosmology.h:21
TreeWalk * tw
Definition: treewalk.h:47
DensityKernel feedback_kernel
Definition: blackhole.c:98
DensityKernel accretion_kernel
Definition: blackhole.c:97
TreeWalkNgbIterBase base
Definition: blackhole.c:96
DensityKernel dynfric_kernel
Definition: blackhole.c:120
TreeWalkNgbIterBase base
Definition: blackhole.c:119
TreeWalkNgbIterBase base
Definition: blackhole.c:150
DensityKernel feedback_kernel
Definition: blackhole.c:151
enum NgbTreeFindSymmetric symmetric
Definition: treewalk.h:37
TreeWalkQueryBase base
Definition: blackhole.c:66
TreeWalkQueryBase base
Definition: blackhole.c:104
TreeWalkQueryBase base
Definition: blackhole.c:127
double Pos[3]
Definition: treewalk.h:23
TreeWalkResultBase base
Definition: blackhole.c:78
MyFloat SurroundingVel[3]
Definition: blackhole.c:111
TreeWalkResultBase base
Definition: blackhole.c:109
MyFloat AccretedMomentum[3]
Definition: blackhole.c:142
TreeWalkResultBase base
Definition: blackhole.c:140
void * priv
Definition: treewalk.h:85
TreeWalkHasWorkFunction haswork
Definition: treewalk.h:100
int64_t WorkSetSize
Definition: treewalk.h:163
size_t ngbiter_type_elsize
Definition: treewalk.h:97
TreeWalkProcessFunction preprocess
Definition: treewalk.h:105
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
int * WorkSet
Definition: treewalk.h:161
TreeWalkVisitFunction visit
Definition: treewalk.h:99
int repeatdisallowed
Definition: treewalk.h:117
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 UnitMass_in_g
Definition: unitsystem.h:8
double UnitTime_in_s
Definition: unitsystem.h:11
double UnitVelocity_in_cm_per_s
Definition: unitsystem.h:9
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 MPIU_Barrier(comm)
Definition: system.h:103
#define MPI_INT64
Definition: system.h:12
int ThisTask
Definition: test_exchange.c:23
#define DMAX(x, y)
Definition: test_interp.c:11
double get_dloga_for_bin(int timebin, const inttime_t Ti_Current)
Definition: timebinmgr.c:279
#define TIMEBINS
Definition: timebinmgr.h:13
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
void treewalk_build_queue(TreeWalk *tw, int *active_set, const size_t size, int may_have_garbage)
Definition: treewalk.c:394
int(* TreeWalkVisitFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, LocalTreeWalk *lv)
Definition: treewalk.h:68
void(* TreeWalkReduceResultFunction)(const int j, TreeWalkResultBase *result, const enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: treewalk.h:76
TreeWalkReduceMode
Definition: treewalk.h:15
void(* TreeWalkProcessFunction)(const int i, TreeWalk *tw)
Definition: treewalk.h:73
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
#define TREEWALK_REDUCE(A, B)
Definition: treewalk.h:189
@ NGB_TREEFIND_ASYMMETRIC
Definition: treewalk.h:12
@ NGB_TREEFIND_SYMMETRIC
Definition: treewalk.h:11
#define HAS(val, flag)
Definition: types.h:22
uint64_t MyIDType
Definition: types.h:10
LOW_PRECISION MyFloat
Definition: types.h:19
HIGH_PRECISION MyDouble
Definition: types.h:20
#define walltime_measure(name)
Definition: walltime.h:8
int winds_is_particle_decoupled(int i)
Definition: winds.c:124