6 #include <gsl/gsl_math.h>
201 #define BH_GET_PRIV(tw) ((struct BHPriv *) (tw->priv))
217 MyFloat BH_SurroundingGasVel[3];
218 MyFloat BH_accreted_momentum[3];
233 MyFloat BH_SurroundingParticles;
237 double BH_DFAccel[3];
238 double BH_DragAccel[3];
239 double BH_GravAccel[3];
244 double KineticFdbkEnergy;
260 MPI_Comm_rank(MPI_COMM_WORLD, &
ThisTask);
361 #define BHPOTVALUEINIT 1.0e29
376 unew += injected_BH_energy / mass;
379 if(unew > 5.0e8 / u_to_temp_fac)
380 unew = 5.0e8 / u_to_temp_fac;
391 dir[0] = sin(theta) * cos(phi);
392 dir[1] = sin(theta) * sin(phi);
406 for(j = 0; j < 3; j++){
407 KE += 0.5 * pow(dv[j], 2);
411 KE /= (atime * atime);
417 return (PE + KE <= 0);
427 struct BHinfo * infos = (
struct BHinfo *)
mymalloc(
"BHDetailCache", NumActiveBlackHoles *
sizeof(
struct BHinfo));
430 const int size =
sizeof(
struct BHinfo) - sizeof(infos[0].size1) - sizeof(infos[0].size2);
432 #pragma omp parallel for
433 for(i = 0; i < NumActiveBlackHoles; i++)
435 const int p_i = ActiveBlackHoles ? ActiveBlackHoles[i] : i;
438 struct BHinfo *info = &infos[i];
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;
449 info->MinPot = priv->
MinPot[PI];
453 for(k=0; k < 3; k++) {
457 info->BH_DragAccel[k] =
BHP(p_i).DragAccel[k];
458 info->BH_GravAccel[k] =
P[p_i].GravAccel[k];
460 info->Velocity[k] =
P[p_i].Vel[k];
461 info->BH_DFAccel[k] =
BHP(p_i).DFAccel[k];
479 info->SwallowID =
BHP(p_i).SwallowID;
480 info->CountProgs =
BHP(p_i).CountProgs;
481 info->Swallowed =
P[p_i].Swallowed;
490 info->Mtrack =
BHP(p_i).Mtrack;
491 info->Mdyn =
P[p_i].Mass;
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];
502 info->a = priv->
atime;
505 fwrite(infos,
sizeof(
struct BHinfo),NumActiveBlackHoles,FdBlackholeDetails);
506 fflush(FdBlackholeDetails);
511 message(0,
"Written details of %ld blackholes in %lu bytes each.\n", totalN,
sizeof(
struct BHinfo));
526 struct BHPriv priv[1] = {0};
531 tw_dynfric->
ev_label =
"BH_DYNFRIC";
541 tw_dynfric->
tree = tree;
542 tw_dynfric->
priv = priv;
547 tw_accretion->
ev_label =
"BH_ACCRETION";
558 tw_accretion->
tree = tree;
559 tw_accretion->
priv = priv;
564 tw_feedback->
ev_label =
"BH_FEEDBACK";
574 tw_feedback->
tree = tree;
575 tw_feedback->
priv = priv;
586 int * ActiveBlackHoles = tw_dynfric->
WorkSet;
587 int64_t NumActiveBlackHoles = tw_dynfric->
WorkSetSize;
589 MPI_Allreduce(&NumActiveBlackHoles, &totbh, 1,
MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
609 treewalk_run(tw_dynfric, ActiveBlackHoles, NumActiveBlackHoles);
638 treewalk_run(tw_accretion, ActiveBlackHoles, NumActiveBlackHoles);
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());
658 treewalk_run(tw_feedback, ActiveBlackHoles, NumActiveBlackHoles);
663 if(FdBlackholeDetails){
664 collect_BH_info(ActiveBlackHoles, NumActiveBlackHoles, priv, FdBlackholeDetails);
695 int64_t Ntot_gas_swallowed, Ntot_BH_swallowed;
697 for(i = 0; i < omp_get_max_threads(); i++) {
708 message(0,
"Accretion done: %d gas particles swallowed, %d BH particles swallowed\n",
709 Ntot_gas_swallowed, Ntot_BH_swallowed);
712 double total_mdoteddington;
713 double total_mass_holes, total_mdot;
715 double Local_BH_mass = 0;
716 double Local_BH_Mdot = 0;
717 double Local_BH_Medd = 0;
718 int Local_BH_num = 0;
721 #pragma omp parallel for reduction(+ : Local_BH_num) reduction(+: Local_BH_mass) reduction(+: Local_BH_Mdot) reduction(+: Local_BH_Medd)
727 Local_BH_mass +=
BhP[i].Mass;
728 Local_BH_Mdot +=
BhP[i].Mdot;
729 Local_BH_Medd +=
BhP[i].Mdot/
BhP[i].Mass;
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);
740 double mdot_in_msun_per_year =
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);
780 double lambda, x, f_of_x;
781 const double a_erf = 8 * (M_PI - 3) / (3 * M_PI * (4. - M_PI));
786 for(j = 0; j < 3; j++)
791 for(j = 0; j < 3; j++)
799 x = bhvel / sqrt(2) / (
BH_GET_PRIV(tw)->BH_SurroundingRmsVel[PI] / 3);
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);
809 for(j = 0; j < 3; j++)
814 log(lambda) * f_of_x * (
P[n].Vel[j] -
BH_GET_PRIV(tw)->BH_SurroundingVel[PI][j]) / pow(bhvel, 3);
819 BHP(n).DFAccel[j] = 0;
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]);
829 message(2,
"Dynamic Friction density is zero for BH %ld. Surroundingpart %d, mass %g, hsml %g, dens %g, pos %g %g %g.\n",
831 for(j = 0; j < 3; j++)
833 BHP(n).DFAccel[j] = 0;
842 return (
P[n].Type == 5) && (!
P[n].Swallowed);
847 int PI =
P[place].PI;
861 I->
Hsml =
P[place].Hsml;
872 iter->
base.
mask = 1 + 2 + 4 + 8 + 16 + 32;
880 double r = iter->
base.
r;
881 double r2 = iter->
base.
r2;
886 if(r2 < iter->dynfric_kernel.HH) {
889 float mass_j =
P[other].Mass;
893 for (k = 0; k < 3; k++){
909 if(
BHP(i).Density > 0)
912 for(k = 0; k < 3; k++)
918 double rho =
BHP(i).Density;
920 for(k = 0; k < 3; k++)
933 double norm = pow((pow(soundspeed, 2) + pow(bhvel, 2)), 1.5);
937 BHP(i).Mass *
BHP(i).Mass * rho_proper / norm;
947 BHP(i).Mass +=
BHP(i).Mdot * dtime;
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;
964 for(k = 0; k < 3; k++){
965 BHP(i).DragAccel[k] = 0;
975 double Edd_ratio =
BHP(i).Mdot/meddington;
980 if (Edd_ratio < lam_thresh){
999 for(d = 0; d<3; d++){
1003 vdisp = sqrt(vdisp / 3);
1006 double KE_thresh = 0.5 * vdisp * vdisp *
BH_GET_PRIV(tw)->MgasEnc[PI];
1009 if (
BHP(i).KineticFdbkEnergy > KE_thresh){
1022 for(j = 0; j < 3; j++) {
1023 BHP(n).MinPotPos[j] =
P[n].Pos[j];
1031 const int PI =
P[n].PI;
1042 for(k = 0; k < 3; k++)
1044 (
P[n].Mass + accmass +
BH_GET_PRIV(tw)->BH_accreted_Mtrack[PI]);
1045 P[n].Mass += accmass;
1058 BHP(n).KineticFdbkEnergy = 0;
1076 for(d = 0; d < 3; d++) {
1079 iter->
base.
mask = 1 + 2 + 4 + 8 + 16 + 32;
1089 double r = iter->
base.
r;
1090 double r2 = iter->
base.
r2;
1092 if(
P[other].Mass < 0)
return;
1094 if(
P[other].Type != 5) {
1103 if(r2 < iter->accretion_kernel.HH)
1105 if(
P[other].Potential < O->BH_MinPot)
1109 for(d = 0; d < 3; d++) {
1117 if(
P[other].ID == I->
ID)
return;
1138 for(d = 0; d < 3; d++){
1140 dv[d] = I->
Vel[d] -
P[other].Vel[d];
1142 da[d] = (I->
Accel[d] -
P[other].GravAccel[d] -
P[other].GravPM[d] -
BHP(other).DFAccel[d]);
1155 #pragma omp atomic read
1156 readid = (
BHP(other).SwallowID);
1163 if(readid != (
MyIDType) -1 && readid < I->
ID ) {
1165 newswallowid = I->
ID;
1166 }
else if(readid == (
MyIDType) -1 &&
P[other].ID < I->
ID) {
1168 newswallowid = I->
ID;
1174 }
while(!__atomic_compare_exchange_n(&(
BHP(other).SwallowID), &readid, newswallowid, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
1179 if(
P[other].Type == 0) {
1180 if(r2 < iter->accretion_kernel.HH) {
1183 float mass_j =
P[other].Mass;
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]);
1215 #pragma omp atomic read
1220 if(readid < I->ID + 1) {
1221 newswallowid = I->
ID + 1;
1226 }
while(!__atomic_compare_exchange_n(&
SPH_SwallowID[
P[other].PI], &readid, newswallowid, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
1230 if(r2 < iter->feedback_kernel.HH) {
1240 mass_j =
P[other].Mass;
1242 mass_j =
P[other].Hsml *
P[other].Hsml *
P[other].Hsml;
1258 if(
P[other].Type == 1 && r2 < iter->feedback_kernel.HH){
1261 for(d = 0; d < 3; d++){
1262 double vel =
P[other].Vel[d] - I->
Vel[d];
1267 if(
P[other].Type == 0 && r2 < iter->feedback_kernel.HH){
1296 double r2 = iter->
base.
r2;
1297 double r = iter->
base.
r;
1300 if(
P[other].ID == I->
ID)
return;
1308 if(
P[other].Type == 5 &&
BHP(other).SwallowID != (
MyIDType) -1)
1310 if(
BHP(other).SwallowID != I->
ID)
return;
1325 P[other].Swallowed = 1;
1350 O->
Mass +=
P[other].Mass;
1354 O->
Mass +=
P[other].Mass;
1359 for(d = 0; d < 3; d++)
1363 endrun(2,
"Encountered BH %i swallowed at earlier time %g\n", other,
BHP(other).SwallowTime);
1365 int tid = omp_get_thread_num();
1374 (r2 < iter->feedback_kernel.HH &&
P[other].Mass > 0) &&
1382 mass_j =
P[other].Mass;
1384 mass_j =
P[other].Hsml *
P[other].Hsml *
P[other].Hsml;
1401 P[other].BHHeated = 1;
1406 double entold, entnew;
1407 double * entptr = &(
SPHP(other).Entropy);
1408 #pragma omp atomic read
1413 }
while(!__atomic_compare_exchange(entptr, &entold, &entnew, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
1423 for(j = 0; j < 3; j++){
1424 #pragma omp atomic update
1425 P[other].Vel[j] += (dvel*dir[j]);
1442 O->
Mass +=
P[other].Mass;
1446 for(d = 0; d < 3; d++)
1451 int tid = omp_get_thread_num();
1462 int PI =
P[place].PI;
1467 for(k = 0; k < 3; k++) {
1482 for (k = 0; k < 3; k++){
1495 for(k = 0; k < 3; k++)
1497 I->
Vel[k] =
P[place].Vel[k];
1498 I->
Accel[k] =
P[place].GravAccel[k] +
P[place].GravPM[k] +
BHP(place).DFAccel[k];
1500 I->
Hsml =
P[place].Hsml;
1501 I->
Mass =
P[place].Mass;
1504 I->
ID =
P[place].ID;
1512 return (
P[n].Type == 5) && (!
P[n].Swallowed) && (
BHP(n).SwallowID == (
MyIDType) -1);
1518 I->
Hsml =
P[i].Hsml;
1546 int PI =
P[place].PI;
1551 for(k = 0; k < 3; k++) {
1577 if(
P[index].Type != 0)
1578 endrun(7772,
"Only Gas turns into blackholes, what's wrong?");
1588 BHP(child).base.ID =
P[child].ID;
1596 BHP(child).Mseed =
BHP(child).Mass;
1597 BHP(child).Mdot = 0;
1600 BHP(child).Density = 0;
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;
1611 BHP(child).JumpToMinPot = 0;
1612 BHP(child).CountProgs = 1;
1615 BHP(child).Mtrack =
P[child].Mass;
1619 BHP(child).Mtrack = -1;
1622 BHP(child).KineticFdbkEnergy = 0;
int BHGetRepositionEnabled(void)
static void blackhole_accretion_postprocess(int n, TreeWalk *tw)
static void blackhole_accretion_copy(int place, TreeWalkQueryBHAccretion *I, TreeWalk *tw)
void blackhole(const ActiveParticles *act, double atime, Cosmology *CP, ForceTree *tree, const struct UnitSystem units, FILE *FdBlackHoles, FILE *FdBlackholeDetails)
struct BlackholeParams blackhole_params
static void blackhole_dynfric_reduce(int place, TreeWalkResultBHDynfric *remote, enum TreeWalkReduceMode mode, TreeWalk *tw)
static void blackhole_feedback_postprocess(int n, TreeWalk *tw)
static void blackhole_dynfric_ngbiter(TreeWalkQueryBHDynfric *I, TreeWalkResultBHDynfric *O, TreeWalkNgbIterBHDynfric *iter, LocalTreeWalk *lv)
void blackhole_make_one(int index, const double atime)
void set_blackhole_params(ParameterSet *ps)
static void blackhole_dynfric_copy(int place, TreeWalkQueryBHDynfric *I, TreeWalk *tw)
static int blackhole_dynfric_haswork(int n, TreeWalk *tw)
static void blackhole_feedback_ngbiter(TreeWalkQueryBHFeedback *I, TreeWalkResultBHFeedback *O, TreeWalkNgbIterBHFeedback *iter, LocalTreeWalk *lv)
static void blackhole_feedback_copy(int place, TreeWalkQueryBHFeedback *I, TreeWalk *tw)
static int check_grav_bound(double dx[3], double dv[3], double da[3], const double atime)
static void blackhole_accretion_preprocess(int n, TreeWalk *tw)
static double blackhole_soundspeed(double entropy, double rho, const double atime)
static void blackhole_accretion_ngbiter(TreeWalkQueryBHAccretion *I, TreeWalkResultBHAccretion *O, TreeWalkNgbIterBHAccretion *iter, LocalTreeWalk *lv)
static void blackhole_dynfric_postprocess(int n, TreeWalk *tw)
static int blackhole_feedback_haswork(int n, TreeWalk *tw)
static void blackhole_accretion_reduce(int place, TreeWalkResultBHAccretion *remote, enum TreeWalkReduceMode mode, TreeWalk *tw)
static int get_random_dir(int i, double dir[3])
static double bh_powerlaw_seed_mass(MyIDType ID)
static double add_injected_BH_energy(double unew, double injected_BH_energy, double mass, double uu_in_cgs)
static void blackhole_feedback_reduce(int place, TreeWalkResultBHFeedback *remote, enum TreeWalkReduceMode mode, TreeWalk *tw)
struct __attribute__((__packed__))
static void collect_BH_info(int *ActiveBlackHoles, int NumActiveBlackHoles, struct BHPriv *priv, FILE *FdBlackholeDetails)
double hubble_function(const Cosmology *CP, double a)
enum DensityKernelType GetDensityKernelType(void)
double(* wk)(DensityKernel *kernel, double q)
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,...)
void endrun(int where, const char *fmt,...)
double FORCE_SOFTENING(int i, int type)
#define mymalloc(name, size)
#define ta_malloc(name, type, nele)
#define report_memory_usage(x)
double param_get_double(ParameterSet *ps, const char *name)
int param_get_int(ParameterSet *ps, const char *name)
int param_get_enum(ParameterSet *ps, const char *name)
struct part_manager_type PartManager[1]
#define NEAREST(x, BoxSize)
#define HYDROGEN_MASSFRAC
int sfreff_on_eeqos(const struct sph_particle_data *sph, const double a3inv)
double get_neutral_fraction_sfreff(double redshift, double hubble, struct particle_data *partdata, struct sph_particle_data *sphdata)
void slots_mark_garbage(int i, struct part_manager_type *pman, struct slots_manager_type *sman)
struct slots_manager_type SlotsManager[1]
int slots_convert(int parent, int ptype, int placement, struct part_manager_type *pman, struct slots_manager_type *sman)
int64_t NumActiveParticle
int64_t * N_sph_swallowed
MyFloat(* BH_SurroundingVel)[3]
MyFloat * BH_accreted_BHMass
MyFloat * BH_accreted_Mass
MyFloat * BH_SurroundingDensity
MyFloat * BH_SurroundingRmsVel
MyFloat(* BH_SurroundingGasVel)[3]
int * BH_SurroundingParticles
MyFloat * BH_FeedbackWeightSum
MyFloat * BH_accreted_Mtrack
MyFloat(* BH_accreted_momentum)[3]
enum BlackHoleFeedbackMethod BlackHoleFeedbackMethod
double BHKE_EddingtonMFactor
double BlackHoleAccretionFactor
double BHKE_SfrCritOverDensity
double BHKE_EddingtonMPivot
double SeedBlackHoleMassIndex
int BlackHoleRepositionEnabled
double BlackHoleFeedbackFactor
double BHKE_EddingtonThrFactor
double BlackHoleEddingtonFactor
double BHKE_EddingtonMIndex
double MaxSeedBlackHoleMass
DensityKernel feedback_kernel
DensityKernel accretion_kernel
DensityKernel dynfric_kernel
DensityKernel feedback_kernel
enum NgbTreeFindSymmetric symmetric
MyFloat FeedbackWeightSum
MyFloat FeedbackWeightSum
MyFloat SurroundingDensity
MyFloat SurroundingVel[3]
MyFloat SurroundingRmsVel
MyFloat AccretedMomentum[3]
TreeWalkHasWorkFunction haswork
size_t ngbiter_type_elsize
TreeWalkProcessFunction preprocess
TreeWalkProcessFunction postprocess
TreeWalkNgbIterFunction ngbiter
TreeWalkReduceResultFunction reduce
TreeWalkVisitFunction visit
size_t result_type_elsize
TreeWalkFillQueryFunction fill
double UnitVelocity_in_cm_per_s
double CurrentParticleOffset[3]
double get_random_number(uint64_t id)
void sumup_large_ints(int n, int *src, int64_t *res)
#define MPIU_Barrier(comm)
double get_dloga_for_bin(int timebin, const inttime_t Ti_Current)
int treewalk_visit_ngbiter(TreeWalkQueryBase *I, TreeWalkResultBase *O, LocalTreeWalk *lv)
void treewalk_run(TreeWalk *tw, int *active_set, size_t size)
void treewalk_build_queue(TreeWalk *tw, int *active_set, const size_t size, int may_have_garbage)
int(* TreeWalkVisitFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, LocalTreeWalk *lv)
void(* TreeWalkReduceResultFunction)(const int j, TreeWalkResultBase *result, const enum TreeWalkReduceMode mode, TreeWalk *tw)
void(* TreeWalkProcessFunction)(const int i, TreeWalk *tw)
void(* TreeWalkNgbIterFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
void(* TreeWalkFillQueryFunction)(const int j, TreeWalkQueryBase *query, TreeWalk *tw)
#define TREEWALK_REDUCE(A, B)
@ NGB_TREEFIND_ASYMMETRIC
#define walltime_measure(name)
int winds_is_particle_decoupled(int i)