diff --git a/configure.ac b/configure.ac index 1130f5d875ccbaa6450371640f0156cefd9a56ca..71f3e0d5ec7c07fd18219d84e61114359939fdc5 100644 --- a/configure.ac +++ b/configure.ac @@ -1340,7 +1340,7 @@ case "$with_subgrid" in with_subgrid_stars=EAGLE with_subgrid_star_formation=EAGLE with_subgrid_feedback=EAGLE - with_subgrid_black_holes=none + with_subgrid_black_holes=EAGLE ;; *) AC_MSG_ERROR([Unknown subgrid choice: $with_subgrid]) @@ -1789,8 +1789,11 @@ case "$with_black_holes" in none) AC_DEFINE([BLACK_HOLES_NONE], [1], [No black hole model]) ;; + EAGLE) + AC_DEFINE([BLACK_HOLES_EAGLE], [1], [EAGLE black hole model]) + ;; *) - AC_MSG_ERROR([Unknown stellar model: $with_black_holes]) + AC_MSG_ERROR([Unknown black-hole model: $with_black_holes]) ;; esac diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index a3da1b4f8edc8c1af225ac941441a92b14a13f99..c6b3046d2d3591c937dfd98cf75fb7697b90110f 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -776,7 +776,7 @@ INPUT += @top_srcdir@/src/star_formation/EAGLE INPUT += @top_srcdir@/src/tracers/EAGLE INPUT += @top_srcdir@/src/stars/EAGLE INPUT += @top_srcdir@/src/feedback/EAGLE -INPUT += @top_srcdir@/src/black_holes/Default +INPUT += @top_srcdir@/src/black_holes/EAGLE # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml index 246d0d4834ff8ce4d08421d6fa754dd81f0b1383..4dcc1e0381adcb0405dd5b502c3d64896269a10f 100644 --- a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml @@ -147,3 +147,11 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. diff --git a/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml index f6693c89b0d281e99adb36d905e2897e49495a1e..e1d9100a8f87bb8670fcb82ce4451084ecb1375a 100644 --- a/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml @@ -148,3 +148,11 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. diff --git a/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml index 5490b3a263badb6fca4b3bf134375a72c773e367..3efe5ba972903dbc3644581d214285603e75ca78 100644 --- a/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml @@ -148,3 +148,13 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. + + \ No newline at end of file diff --git a/examples/EAGLE_low_z/EAGLE_12/README b/examples/EAGLE_low_z/EAGLE_12/README index 9fb2930f625fd2e85293b9ebcc91f46e59f02745..0530021202191bac74690e47106ca3488011d1b5 100644 --- a/examples/EAGLE_low_z/EAGLE_12/README +++ b/examples/EAGLE_low_z/EAGLE_12/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 8 cores. MD5 checksum of the ICs: -d1ebf1c7ffb0488c3628e08ed6d2dbf4 EAGLE_ICs_12.hdf5 +6f96624175df28f2c4a02265b871b3d5 EAGLE_ICs_12.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml index 873b8ec1084a78f830f6a927f173d42be9c5a210..1b81cbc213a0e12111d35e5efde31606d60fcd59 100644 --- a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml @@ -140,3 +140,11 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. diff --git a/examples/EAGLE_low_z/EAGLE_25/README b/examples/EAGLE_low_z/EAGLE_25/README index 0fe37964d3734b63acd10195822e11094219586a..80907ce9ffff04382afceb7c906dca089a05b8bb 100644 --- a/examples/EAGLE_low_z/EAGLE_25/README +++ b/examples/EAGLE_low_z/EAGLE_25/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 64 cores. MD5 checksum of the ICs: -592faecfc51239a00396f5a4acc2fa4d EAGLE_ICs_25.hdf5 +e6a5de0e962d8ffb7589671b9613daa0 EAGLE_ICs_25.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml index 51bfe6af3d904c822bbc31720ad64fb95652eede..569dbdd1487faea311318fa508d5a14832f074d4 100644 --- a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml @@ -147,3 +147,11 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. diff --git a/examples/EAGLE_low_z/EAGLE_50/README b/examples/EAGLE_low_z/EAGLE_50/README index 211360be35f83423160ceb9912f7f0ce9ee444c8..f63cd92438fb94dc9d34b1c048b0203fb903e020 100644 --- a/examples/EAGLE_low_z/EAGLE_50/README +++ b/examples/EAGLE_low_z/EAGLE_50/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 512 cores. MD5 checksum of the ICs: -203985136c665e8677a4faa77f94110a EAGLE_ICs_50.hdf5 +fca1c2efea5c81f74a0d260fefb19595 EAGLE_ICs_50.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml index 07c527158d7b2dd38205a202d6a8ad5fae6a2b8f..4e1fbb8cdaf025d10ef07af09085290c91223dca 100644 --- a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml @@ -142,3 +142,11 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. diff --git a/examples/EAGLE_low_z/EAGLE_6/README b/examples/EAGLE_low_z/EAGLE_6/README index 844c779e222b1cd7ebefaa61d637092a5a8c1e79..a236f338649e2199220c7f1c28e5531b1fd980c7 100644 --- a/examples/EAGLE_low_z/EAGLE_6/README +++ b/examples/EAGLE_low_z/EAGLE_6/README @@ -10,4 +10,4 @@ been removed. Everything is ready to be run without cosmological integration. MD5 checksum of the ICs: -1923db3cf59b2e80b23e4e1aee13e012 EAGLE_ICs_6.hdf5 +ddd26b959815b131cd1af1479e752da7 EAGLE_ICs_6.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml index 3632fcd82d50cf3f1d53b40bf329a1dbb35124fe..978f21ea5f7df1c1bb13329cbea796254d3d3b2a 100644 --- a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml +++ b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml @@ -68,7 +68,7 @@ InitialConditions: periodic: 1 cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget - + EAGLEChemistry: # Solar abundances init_abundance_metal: 0.014 init_abundance_Hydrogen: 0.70649785 @@ -151,3 +151,12 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. + diff --git a/examples/main.c b/examples/main.c index 08e8eacf5510bf7af77175d9a74460651f656800..4efd82d799f339a337df019c4f9ef12cbff07075 100644 --- a/examples/main.c +++ b/examples/main.c @@ -98,6 +98,7 @@ int main(int argc, char *argv[]) { struct stars_props stars_properties; struct feedback_props feedback_properties; struct entropy_floor_properties entropy_floor; + struct black_holes_props black_holes_properties; struct part *parts = NULL; struct phys_const prog_const; struct space s; @@ -389,6 +390,16 @@ int main(int argc, char *argv[]) { return 1; } + if (!with_hydro && with_black_holes) { + if (myrank == 0) { + argparse_usage(&argparse); + printf( + "\nError: Cannot process black holes without gas, --hydro must be " + "chosen.\n"); + } + return 1; + } + /* Let's pin the main thread, now we know if affinity will be used. */ #if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE) if (with_aff && @@ -754,6 +765,16 @@ int main(int argc, char *argv[]) { } else bzero(&feedback_properties, sizeof(struct feedback_props)); + /* Initialise the black holes properties */ + if (with_black_holes) { +#ifdef BLACK_HOLES_NONE + error("ERROR: Running with black_holes but compiled without it."); +#endif + black_holes_props_init(&black_holes_properties, &prog_const, &us, params, + &hydro_properties, &cosmo); + } else + bzero(&black_holes_properties, sizeof(struct black_holes_props)); + /* Initialise the gravity properties */ if (with_self_gravity) gravity_props_init(&gravity_properties, params, &cosmo, with_cosmology, @@ -866,7 +887,7 @@ int main(int argc, char *argv[]) { long long N_total[4] = {0, 0, 0, 0}; #if defined(WITH_MPI) long long N_long[4] = {Ngas, Ngpart, Nspart, Nbpart}; - MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, + MPI_Allreduce(&N_long, &N_total, 4, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); #else N_total[0] = Ngas; @@ -1004,8 +1025,9 @@ int main(int argc, char *argv[]) { engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2], engine_policies, talking, &reparttype, &us, &prog_const, &cosmo, &hydro_properties, &entropy_floor, &gravity_properties, - &stars_properties, &feedback_properties, &mesh, &potential, - &cooling_func, &starform, &chemistry); + &stars_properties, &black_holes_properties, + &feedback_properties, &mesh, &potential, &cooling_func, + &starform, &chemistry); engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff, talking, restart_file); diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index f0a3c398e3afb895fb1932b654816437f2b0f429..375c5abc22d117b7322dd49e37109430b3ef0e32 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -374,3 +374,12 @@ EAGLEFeedback: SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. + +# EAGLE AGN model +EAGLEAGN: + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event. + diff --git a/src/Makefile.am b/src/Makefile.am index 3f7a61bc097802e3e28744234e0c96116918187c..adf9f30ae367854a6742adb333005b0d11b9d8b4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,7 +53,7 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ star_formation_struct.h star_formation.h star_formation_iact.h \ star_formation_logger.h star_formation_logger_struct.h \ velociraptor_struct.h velociraptor_io.h random.h memuse.h black_holes.h black_holes_io.h \ - feedback.h feedback_struct.h feedback_properties.h + black_holes_properties.h feedback.h feedback_struct.h feedback_properties.h # source files for EAGLE cooling EAGLE_COOLING_SOURCES = @@ -84,9 +84,9 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c engine_maketasks.c # Include files for distribution, not installation. nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ gravity_iact.h kernel_long_gravity.h vector.h cache.h runner_doiact.h runner_doiact_vec.h runner_doiact_grav.h \ - runner_doiact_nosort.h runner_doiact_stars.h units.h intrinsics.h minmax.h kick.h timestep.h drift.h \ - adiabatic_index.h io_properties.h dimension.h part_type.h periodic.h memswap.h dump.h logger.h sign.h \ - logger_io.h timestep_limiter.h \ + runner_doiact_nosort.h runner_doiact_stars.h runner_doiact_black_holes.h units.h intrinsics.h minmax.h \ + kick.h timestep.h drift.h adiabatic_index.h io_properties.h dimension.h part_type.h periodic.h memswap.h \ + dump.h logger.h sign.h logger_io.h timestep_limiter.h \ gravity.h gravity_io.h gravity_cache.h \ gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \ gravity/Default/gravity_debug.h gravity/Default/gravity_part.h \ @@ -196,7 +196,11 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h feedback/EAGLE/feedback_properties.h feedback/EAGLE/imf.h feedback/EAGLE/interpolate.h \ feedback/EAGLE/yield_tables.h \ black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h \ - black_holes/Default/black_holes_part.h + black_holes/Default/black_holes_part.h black_holes/Default/black_holes_iact.h \ + black_holes/Default/black_holes_properties.h \ + black_holes/EAGLE/black_holes.h black_holes/EAGLE/black_holes_io.h \ + black_holes/EAGLE/black_holes_part.h black_holes/EAGLE/black_holes_iact.h \ + black_holes/EAGLE/black_holes_properties.h # Sources and flags for regular library diff --git a/src/active.h b/src/active.h index a0f793b5531eaa19f6d1e92a5f243d753759d277..e7cd35fa570f15694ab8fa3011d6a02b65837886 100644 --- a/src/active.h +++ b/src/active.h @@ -372,7 +372,7 @@ __attribute__((always_inline)) INLINE static int bpart_is_active( if (ti_end < ti_current) error( - "s-particle in an impossible time-zone! bp->ti_end=%lld " + "b-particle in an impossible time-zone! bp->ti_end=%lld " "e->ti_current=%lld", ti_end, ti_current); #endif diff --git a/src/black_holes.h b/src/black_holes.h index 0decc3fbf5eddd30cc5b5a15b73d0a4c2447e5ee..8b658e45f37e4e49e1a14f1e3e853b8dbed476d8 100644 --- a/src/black_holes.h +++ b/src/black_holes.h @@ -25,6 +25,10 @@ /* Select the correct star model */ #if defined(BLACK_HOLES_NONE) #include "./black_holes/Default/black_holes.h" +#include "./black_holes/Default/black_holes_iact.h" +#elif defined(BLACK_HOLES_EAGLE) +#include "./black_holes/EAGLE/black_holes.h" +#include "./black_holes/EAGLE/black_holes_iact.h" #else #error "Invalid choice of star model" #endif diff --git a/src/black_holes/Default/black_holes.h b/src/black_holes/Default/black_holes.h index f0f53bdf46cdd6afe062777c993e333ac5ef56c6..3c35d0862df99ff194fd609d6058c1be7065b0bf 100644 --- a/src/black_holes/Default/black_holes.h +++ b/src/black_holes/Default/black_holes.h @@ -20,6 +20,9 @@ #define SWIFT_DEFAULT_BLACK_HOLES_H #include <float.h> + +/* Local includes */ +#include "black_holes_properties.h" #include "dimension.h" #include "kernel_hydro.h" #include "minmax.h" @@ -42,9 +45,10 @@ __attribute__((always_inline)) INLINE static float black_holes_compute_timestep( * read in to do some conversions. * * @param bp The particle to act upon + * @param props The properties of the black holes model. */ __attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( - struct bpart* bp) { + struct bpart* bp, const struct black_holes_props* props) { bp->time_bin = 0; } @@ -135,6 +139,23 @@ black_holes_bpart_has_no_neighbours(struct bpart* restrict bp, bp->density.wcount_dh = 0.f; } +/** + * @brief Compute the accretion rate of the black hole and all the quantites + * required for the feedback loop. + * + * Nothing to do here. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param cosmo The cosmological model. + * @param dt The time-step size (in physical internal units). + */ +__attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo, + const double dt) {} + /** * @brief Reset acceleration fields of a particle * diff --git a/src/black_holes/Default/black_holes_iact.h b/src/black_holes/Default/black_holes_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..4aeb27b4e13a614036962a564d99a80c6990ebec --- /dev/null +++ b/src/black_holes/Default/black_holes_iact.h @@ -0,0 +1,96 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_DEFAULT_BH_IACT_H +#define SWIFT_DEFAULT_BH_IACT_H + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas, not updated). + * @param xpj The extended data of the second particle (not updated). + * @param cosmo The cosmological model. + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_density( + const float r2, const float *dx, const float hi, const float hj, + struct bpart *restrict bi, const struct part *restrict pj, + const struct xpart *restrict xpj, const struct cosmology *cosmo, + const integertime_t ti_current) { + + float wi, wi_dx; + + /* Get r and 1/r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the number of neighbours */ + bi->density.wcount += wi; + bi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + +#ifdef DEBUG_INTERACTIONS_BH + /* Update ngb counters */ + if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_BH) + bi->ids_ngbs_density[si->num_ngb_density] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_density; +#endif +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param cosmo The cosmological model. + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_feedback(const float r2, const float *dx, const float hi, + const float hj, struct bpart *restrict bi, + struct part *restrict pj, + const struct xpart *restrict xpj, + const struct cosmology *cosmo, + const integertime_t ti_current) { +#ifdef DEBUG_INTERACTIONS_BH + /* Update ngb counters */ + if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_BH) + bi->ids_ngbs_force[si->num_ngb_force] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_force; +#endif +} + +#endif diff --git a/src/black_holes/Default/black_holes_properties.h b/src/black_holes/Default/black_holes_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..e6e66d5b1ce14e920a498a3a97013c0d33596b38 --- /dev/null +++ b/src/black_holes/Default/black_holes_properties.h @@ -0,0 +1,126 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_DEFAULT_BLACK_HOLES_PROPERTIES_H +#define SWIFT_DEFAULT_BLACK_HOLES_PROPERTIES_H + +#include "chemistry.h" +#include "hydro_properties.h" + +/** + * @brief Properties of the black hole scheme. + * + * In this default scheme, we only have the properties + * required by the neighbour search. + */ +struct black_holes_props { + + /*! Resolution parameter */ + float eta_neighbours; + + /*! Target weightd number of neighbours (for info only)*/ + float target_neighbours; + + /*! Smoothing length tolerance */ + float h_tolerance; + + /*! Tolerance on neighbour number (for info only)*/ + float delta_neighbours; + + /*! Maximal number of iterations to converge h */ + int max_smoothing_iterations; + + /*! Maximal change of h over one time-step */ + float log_max_h_change; +}; + +/** + * @brief Initialise the black hole properties from the parameter file. + * + * We read the defaults from the hydro properties. + * + * @param bp The #black_holes_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +static INLINE void black_holes_props_init(struct black_holes_props *bp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *hydro_props, + const struct cosmology *cosmo) { + + /* Kernel properties */ + bp->eta_neighbours = parser_get_opt_param_float( + params, "BlackHoles:resolution_eta", hydro_props->eta_neighbours); + + /* Tolerance for the smoothing length Newton-Raphson scheme */ + bp->h_tolerance = parser_get_opt_param_float(params, "BlackHoles:h_tolerance", + hydro_props->h_tolerance); + + /* Get derived properties */ + bp->target_neighbours = pow_dimension(bp->eta_neighbours) * kernel_norm; + const float delta_eta = bp->eta_neighbours * (1.f + bp->h_tolerance); + bp->delta_neighbours = + (pow_dimension(delta_eta) - pow_dimension(bp->eta_neighbours)) * + kernel_norm; + + /* Number of iterations to converge h */ + bp->max_smoothing_iterations = + parser_get_opt_param_int(params, "BlackHoles:max_ghost_iterations", + hydro_props->max_smoothing_iterations); + + /* Time integration properties */ + const float max_volume_change = + parser_get_opt_param_float(params, "BlackHoles:max_volume_change", -1); + if (max_volume_change == -1) + bp->log_max_h_change = hydro_props->log_max_h_change; + else + bp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); +} + +/** + * @brief Write a black_holes_props struct to the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_dump( + const struct black_holes_props *props, FILE *stream) { + restart_write_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, "black_holes props", "black holes props"); +} + +/** + * @brief Restore a black_holes_props struct from the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_restore( + const struct black_holes_props *props, FILE *stream) { + restart_read_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, NULL, "black holes props"); +} + +#endif /* SWIFT_DEFAULT_BLACK_HOLES_PROPERTIES_H */ diff --git a/src/black_holes/EAGLE/black_holes.h b/src/black_holes/EAGLE/black_holes.h new file mode 100644 index 0000000000000000000000000000000000000000..909057bb962cd9a21af9a708d8796f5cd18d6a8d --- /dev/null +++ b/src/black_holes/EAGLE/black_holes.h @@ -0,0 +1,315 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_BLACK_HOLES_H +#define SWIFT_EAGLE_BLACK_HOLES_H + +/* Local includes */ +#include "black_holes_properties.h" +#include "cosmology.h" +#include "dimension.h" +#include "kernel_hydro.h" +#include "minmax.h" +#include "physical_constants.h" + +/* Standard includes */ +#include <float.h> + +/** + * @brief Computes the gravity time-step of a given black hole particle. + * + * @param bp Pointer to the s-particle data. + */ +__attribute__((always_inline)) INLINE static float black_holes_compute_timestep( + const struct bpart* const bp) { + + return FLT_MAX; +} + +/** + * @brief Initialises the b-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param bp The particle to act upon + * @param props The properties of the black holes model. + */ +__attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( + struct bpart* bp, const struct black_holes_props* props) { + + bp->time_bin = 0; + bp->subgrid_mass = bp->mass; + bp->total_accreted_mass = 0.f; + bp->accretion_rate = 0.f; + bp->formation_time = -1.f; +} + +/** + * @brief Prepares a b-particle for its interactions + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_init_bpart( + struct bpart* bp) { + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_density[i] = -1; + bp->num_ngb_density = 0; +#endif + + bp->density.wcount = 0.f; + bp->density.wcount_dh = 0.f; + bp->rho_gas = 0.f; + bp->sound_speed_gas = 0.f; + bp->velocity_gas[0] = 0.f; + bp->velocity_gas[1] = 0.f; + bp->velocity_gas[2] = 0.f; + bp->ngb_mass = 0.f; + bp->num_ngbs = 0; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * @param bp The particle + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void black_holes_predict_extra( + struct bpart* restrict bp, float dt_drift) {} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param bp The particle. + */ +__attribute__((always_inline)) INLINE static void +black_holes_reset_predicted_values(struct bpart* restrict bp) {} + +/** + * @brief Kick the additional variables + * + * @param bp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void black_holes_kick_extra( + struct bpart* bp, float dt) {} + +/** + * @brief Finishes the calculation of density on black holes + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_end_density( + struct bpart* bp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Finish the calculation by inserting the missing h-factors */ + bp->density.wcount *= h_inv_dim; + bp->density.wcount_dh *= h_inv_dim_plus_one; + bp->rho_gas *= h_inv_dim; + bp->sound_speed_gas *= h_inv_dim; + bp->velocity_gas[0] *= h_inv_dim; + bp->velocity_gas[1] *= h_inv_dim; + bp->velocity_gas[2] *= h_inv_dim; + + const float rho_inv = 1.f / bp->rho_gas; + + /* Finish the calculation by undoing the mass smoothing */ + bp->sound_speed_gas *= rho_inv; + bp->velocity_gas[0] *= rho_inv; + bp->velocity_gas[1] *= rho_inv; + bp->velocity_gas[2] *= rho_inv; +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +black_holes_bpart_has_no_neighbours(struct bpart* restrict bp, + const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Re-set problematic values */ + bp->density.wcount = kernel_root * h_inv_dim; + bp->density.wcount_dh = 0.f; +} + +/** + * @brief Compute the accretion rate of the black hole and all the quantites + * required for the feedback loop. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param cosmo The cosmological model. + * @param dt The time-step size (in physical internal units). + */ +__attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo, + const double dt) { + + /* Gather some physical constants (all in internal units) */ + const double G = constants->const_newton_G; + const double c = constants->const_speed_light_c; + const double proton_mass = constants->const_proton_mass; + const double sigma_Thomson = constants->const_thomson_cross_section; + + /* Gather the parameters of the model */ + const double f_Edd = props->f_Edd; + const double epsilon_r = props->epsilon_r; + const double epsilon_f = props->epsilon_f; + const double num_ngbs_to_heat = props->num_ngbs_to_heat; + const double delta_T = props->AGN_delta_T_desired; + const double delta_u = delta_T * props->temp_to_u_factor; + + /* (Subgrid) mass of the BH (internal units) */ + const double BH_mass = bp->subgrid_mass; + + /* Convert the quantities we gathered to physical frame (all internal units) + */ + const double gas_rho_phys = bp->rho_gas * cosmo->a3_inv; + const double gas_c_phys = bp->sound_speed_gas * cosmo->a_factor_sound_speed; + const double gas_v_peculiar[3] = {bp->velocity_gas[0] * cosmo->a_inv, + bp->velocity_gas[1] * cosmo->a_inv, + bp->velocity_gas[2] * cosmo->a_inv}; + + const double bh_v_peculiar[3] = {bp->v[0] * cosmo->a_inv, + bp->v[1] * cosmo->a_inv, + bp->v[2] * cosmo->a_inv}; + + /* Difference in peculiar velocity between the gas and the BH + * Note that there is no need for a Hubble flow term here. We are + * computing the gas velocity at the position of the black hole. */ + const double v_diff_peculiar[3] = {gas_v_peculiar[0] - bh_v_peculiar[0], + gas_v_peculiar[1] - bh_v_peculiar[1], + gas_v_peculiar[2] - bh_v_peculiar[2]}; + const double v_diff_norm2 = v_diff_peculiar[0] * v_diff_peculiar[0] + + v_diff_peculiar[1] * v_diff_peculiar[1] + + v_diff_peculiar[2] * v_diff_peculiar[2]; + + /* We can now compute the Bondi accretion rate (internal units) */ + const double gas_c_phys2 = gas_c_phys * gas_c_phys; + const double denominator2 = v_diff_norm2 + gas_c_phys2; + const double denominator_inv = 1. / sqrt(denominator2); + const double Bondi_rate = 4. * M_PI * G * G * BH_mass * BH_mass * + gas_rho_phys * denominator_inv * denominator_inv * + denominator_inv; + + /* Compute the Eddington rate (internal units) */ + const double Eddington_rate = + 4. * M_PI * G * BH_mass * proton_mass / (epsilon_r * c * sigma_Thomson); + + /* Limit the accretion rate to the Eddington fraction */ + const double accr_rate = max(Bondi_rate, f_Edd * Eddington_rate); + bp->accretion_rate = accr_rate; + + /* Factor in the radiative efficiency */ + const double mass_rate = (1. - epsilon_r) * accr_rate; + const double luminosity = epsilon_r * accr_rate * c * c; + + /* Integrate forward in time */ + bp->subgrid_mass += mass_rate * dt; + bp->total_accreted_mass += mass_rate * dt; + bp->energy_reservoir += luminosity * epsilon_f * dt; + + /* Energy required to have a feedback event */ + const double mean_ngb_mass = bp->ngb_mass / ((double)bp->num_ngbs); + const double E_feedback_event = num_ngbs_to_heat * delta_u * mean_ngb_mass; + + /* Are we doing some feedback? */ + if (bp->energy_reservoir > E_feedback_event) { + + /* Default probability of heating */ + double target_prob = bp->energy_reservoir / (delta_u * bp->ngb_mass); + + /* Calculate the change in internal energy of the gas particles that get + * heated. Adjust the prbability if needed. */ + double gas_delta_u; + double prob; + if (target_prob <= 1.) { + + /* Normal case */ + prob = target_prob; + gas_delta_u = delta_u; + + } else { + + /* Special case: we need to adjust the energy irrespective of the + * desired deltaT to ensure we inject all the available energy. */ + + prob = 1.; + gas_delta_u = bp->energy_reservoir / bp->ngb_mass; + } + + /* Store all of this in the black hole for delivery onto the gas. */ + bp->to_distribute.AGN_heating_probability = prob; + bp->to_distribute.AGN_delta_u = gas_delta_u; + + /* Decrement the energy in the reservoir by the mean expected energy */ + const double energy_used = bp->energy_reservoir / max(prob, 1.); + bp->energy_reservoir -= energy_used; + + } else { + + /* Flag that we don't want to heat anyone */ + bp->to_distribute.AGN_heating_probability = 0.f; + bp->to_distribute.AGN_delta_u = 0.f; + } +} + +/** + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on black hole, therefore no need to use + * it. + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_reset_feedback( + struct bpart* restrict bp) { + + bp->to_distribute.AGN_heating_probability = 0.f; + bp->to_distribute.AGN_delta_u = 0.f; + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_force[i] = -1; + bp->num_ngb_force = 0; +#endif +} + +#endif /* SWIFT_EAGLE_BLACK_HOLES_H */ diff --git a/src/black_holes/EAGLE/black_holes_iact.h b/src/black_holes/EAGLE/black_holes_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..3174dea57ade8a9e3a1d9db3aa54cda428e0bc9f --- /dev/null +++ b/src/black_holes/EAGLE/black_holes_iact.h @@ -0,0 +1,150 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_BH_IACT_H +#define SWIFT_EAGLE_BH_IACT_H + +/* Local includes */ +#include "hydro.h" +#include "random.h" + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas, not updated). + * @param xpj The extended data of the second particle (not updated). + * @param cosmo The cosmological model. + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_density( + const float r2, const float *dx, const float hi, const float hj, + struct bpart *restrict bi, const struct part *restrict pj, + const struct xpart *restrict xpj, const struct cosmology *cosmo, + const integertime_t ti_current) { + + float wi, wi_dx; + + /* Get r and 1/r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the number of neighbours */ + bi->density.wcount += wi; + bi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + /* Contribution to the number of neighbours */ + bi->num_ngbs += 1; + + /* Neighbour gas mass */ + const float mj = hydro_get_mass(pj); + + /* Contribution to the BH gas density */ + bi->rho_gas += mj * wi; + + /* Contribution to the total neighbour mass */ + bi->ngb_mass += mj; + + /* Neighbour sounds speed */ + const float cj = hydro_get_comoving_soundspeed(pj); + + /* Contribution to the smoothed sound speed */ + bi->sound_speed_gas += mj * cj * wi; + + /* Neighbour peculiar drifted velocity */ + const float vj[3] = {pj->v[0], pj->v[1], pj->v[2]}; + + /* Contribution to the smoothed velocity */ + bi->velocity_gas[0] += mj * vj[0] * wi; + bi->velocity_gas[1] += mj * vj[1] * wi; + bi->velocity_gas[2] += mj * vj[2] * wi; + +#ifdef DEBUG_INTERACTIONS_BH + /* Update ngb counters */ + if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_BH) + bi->ids_ngbs_density[si->num_ngb_density] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_density; +#endif +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param cosmo The cosmological model. + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_feedback(const float r2, const float *dx, const float hi, + const float hj, struct bpart *restrict bi, + struct part *restrict pj, + struct xpart *restrict xpj, + const struct cosmology *cosmo, + const integertime_t ti_current) { + + /* Get the heating probability */ + const float prob = bi->to_distribute.AGN_heating_probability; + + /* Are we doing some feedback? */ + if (prob > 0.f) { + + /* Draw a random number (Note mixing both IDs) */ + const float rand = random_unit_interval(bi->id + pj->id, ti_current, + random_number_BH_feedback); + + /* Are we lucky? */ + if (rand < prob) { + + /* Compute new energy of this particle */ + const double u_init = hydro_get_physical_internal_energy(pj, xpj, cosmo); + const float delta_u = bi->to_distribute.AGN_delta_u; + const double u_new = u_init + delta_u; + + hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(pj, cosmo, u_new); + } + } + +#ifdef DEBUG_INTERACTIONS_BH + /* Update ngb counters */ + if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_BH) + bi->ids_ngbs_force[si->num_ngb_force] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_force; +#endif +} + +#endif /* SWIFT_EAGLE_BH_IACT_H */ diff --git a/src/black_holes/EAGLE/black_holes_io.h b/src/black_holes/EAGLE/black_holes_io.h new file mode 100644 index 0000000000000000000000000000000000000000..cc6cb962db61f9e45ded990b9dca7f00db075733 --- /dev/null +++ b/src/black_holes/EAGLE/black_holes_io.h @@ -0,0 +1,114 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_BLACK_HOLES_IO_H +#define SWIFT_EAGLE_BLACK_HOLES_IO_H + +#include "black_holes_part.h" +#include "io_properties.h" + +/** + * @brief Specifies which b-particle fields to read from a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void black_holes_read_particles(struct bpart *bparts, + struct io_props *list, + int *num_fields) { + + /* Say how much we want to read */ + *num_fields = 6; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, bparts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, bparts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + bparts, mass); + list[3] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, bparts, id); + list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, + UNIT_CONV_LENGTH, bparts, h); + list[5] = io_make_input_field("EnergyReservoir", FLOAT, 1, OPTIONAL, + UNIT_CONV_ENERGY, bparts, energy_reservoir); +} + +/** + * @brief Specifies which b-particle fields to write to a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + */ +INLINE static void black_holes_write_particles(const struct bpart *bparts, + struct io_props *list, + int *num_fields) { + + /* Say how much we want to write */ + *num_fields = 12; + + /* List what we want to write */ + list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, + bparts, x); + list[1] = + io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, bparts, v); + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, bparts, mass); + list[3] = io_make_output_field("ParticleIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, + bparts, id); + list[4] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, + bparts, h); + list[5] = io_make_output_field("SubgridMasses", FLOAT, 1, UNIT_CONV_MASS, + bparts, subgrid_mass); + list[6] = io_make_output_field("FormationTime", FLOAT, 1, UNIT_CONV_TIME, + bparts, formation_time); + list[7] = io_make_output_field("GasDensity", FLOAT, 1, UNIT_CONV_DENSITY, + bparts, rho_gas); + list[8] = io_make_output_field("GasSoundSpeed", FLOAT, 1, UNIT_CONV_SPEED, + bparts, sound_speed_gas); + list[9] = io_make_output_field("EnergyReservoir", FLOAT, 1, UNIT_CONV_ENERGY, + bparts, energy_reservoir); + list[10] = io_make_output_field("AccretionRate", FLOAT, 1, + UNIT_CONV_MASS_PER_UNIT_TIME, bparts, + accretion_rate); + list[11] = io_make_output_field("TotalAccretedMass", FLOAT, 1, + UNIT_CONV_MASS_PER_UNIT_TIME, bparts, + total_accreted_mass); + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + + list += *num_fields; + *num_fields += 4; + + list[0] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS, + bparts, num_ngb_density); + list[1] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS, + bparts, num_ngb_force); + list[2] = io_make_output_field("Ids_ngb_density", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES, + UNIT_CONV_NO_UNITS, bparts, ids_ngbs_density); + list[3] = io_make_output_field("Ids_ngb_force", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES, + UNIT_CONV_NO_UNITS, bparts, ids_ngbs_force); +#endif +} + +#endif /* SWIFT_EAGLE_BLACK_HOLES_IO_H */ diff --git a/src/black_holes/EAGLE/black_holes_part.h b/src/black_holes/EAGLE/black_holes_part.h new file mode 100644 index 0000000000000000000000000000000000000000..b6d15dfa06be1c4572845d1b9656b053439f70d1 --- /dev/null +++ b/src/black_holes/EAGLE/black_holes_part.h @@ -0,0 +1,137 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_BLACK_HOLE_PART_H +#define SWIFT_EAGLE_BLACK_HOLE_PART_H + +/** + * @brief Particle fields for the black hole particles. + * + * All quantities related to gravity are stored in the associate #gpart. + */ +struct bpart { + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Particle velocity. */ + float v[3]; + + /*! Black hole mass */ + float mass; + + /* Particle cutoff radius. */ + float h; + + /*! Particle time bin */ + timebin_t time_bin; + + struct { + + /* Number of neighbours. */ + float wcount; + + /* Number of neighbours spatial derivative. */ + float wcount_dh; + + } density; + + /*! Union for the formation time and formation scale factor */ + union { + + /*! Formation time */ + float formation_time; + + /*! Formation scale factor */ + float formation_scale_factor; + }; + + /*! Subgrid mass of the black hole */ + float subgrid_mass; + + /*! Total accreted mass of the black hole */ + float total_accreted_mass; + + /*! Energy reservoir for feedback */ + float energy_reservoir; + + /*! Instantaneous accretion rate */ + float accretion_rate; + + /*! Density of the gas surrounding the black hole. */ + float rho_gas; + + /*! Smoothed sound speed of the gas surrounding the black hole. */ + float sound_speed_gas; + + /*! Smoothed velocity (peculiar) of the gas surrounding the black hole */ + float velocity_gas[3]; + + /*! Total mass of the gas neighbours. */ + float ngb_mass; + + /*! Integer number of neighbours */ + int num_ngbs; + + /*! Properties used in the feedback loop to distribute to gas neighbours. */ + struct { + + /*! Probability of heating neighbouring gas particles for AGN feedback */ + float AGN_heating_probability; + + /*! Change in energy from SNII feedback energy injection */ + float AGN_delta_u; + + } to_distribute; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + +#endif + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + /*! Number of interactions in the density SELF and PAIR */ + int num_ngb_density; + + /*! List of interacting particles in the density SELF and PAIR */ + long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES]; + + /*! Number of interactions in the force SELF and PAIR */ + int num_ngb_force; + + /*! List of interacting particles in the force SELF and PAIR */ + long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES]; +#endif + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_EAGLE_BLACK_HOLE_PART_H */ diff --git a/src/black_holes/EAGLE/black_holes_properties.h b/src/black_holes/EAGLE/black_holes_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..01e420509d6eaee7dd07e5af23fc845acbcd330d --- /dev/null +++ b/src/black_holes/EAGLE/black_holes_properties.h @@ -0,0 +1,181 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_BLACK_HOLES_PROPERTIES_H +#define SWIFT_EAGLE_BLACK_HOLES_PROPERTIES_H + +#include "chemistry.h" +#include "hydro_properties.h" + +/** + * @brief Properties of black holes and AGN feedback in the EAGEL model. + */ +struct black_holes_props { + + /* ----- Basic neighbour search properties ------ */ + + /*! Resolution parameter */ + float eta_neighbours; + + /*! Target weightd number of neighbours (for info only)*/ + float target_neighbours; + + /*! Smoothing length tolerance */ + float h_tolerance; + + /*! Tolerance on neighbour number (for info only)*/ + float delta_neighbours; + + /*! Maximal number of iterations to converge h */ + int max_smoothing_iterations; + + /*! Maximal change of h over one time-step */ + float log_max_h_change; + + /* ----- Initialisation properties ------ */ + + /* ----- Properties of the accretion model ------ */ + + /*! Maximal fraction of the Eddington rate allowed. */ + float f_Edd; + + /*! Radiative efficiency of the black holes. */ + float epsilon_r; + + /*! Feedback coupling efficiency of the black holes. */ + float epsilon_f; + + /* ---- Properties of the feedback model ------- */ + + /*! Temperature increase induced by AGN feedback (Kelvin) */ + float AGN_delta_T_desired; + + /*! Number of gas neighbours to heat in a feedback event */ + float num_ngbs_to_heat; + + /* ---- Common conversion factors --------------- */ + + /*! Conversion factor from temperature to internal energy */ + float temp_to_u_factor; +}; + +/** + * @brief Initialise the black hole properties from the parameter file. + * + * For the basic black holes neighbour finding properties we use the + * defaults from the hydro scheme if the users did not provide specific + * values. + * + * @param bp The #black_holes_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +INLINE static void black_holes_props_init(struct black_holes_props *bp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *hydro_props, + const struct cosmology *cosmo) { + + /* Read in the basic neighbour search properties or default to the hydro + ones if the user did not provide any different values */ + + /* Kernel properties */ + bp->eta_neighbours = parser_get_opt_param_float( + params, "BlackHoles:resolution_eta", hydro_props->eta_neighbours); + + /* Tolerance for the smoothing length Newton-Raphson scheme */ + bp->h_tolerance = parser_get_opt_param_float(params, "BlackHoles:h_tolerance", + hydro_props->h_tolerance); + + /* Get derived properties */ + bp->target_neighbours = pow_dimension(bp->eta_neighbours) * kernel_norm; + const float delta_eta = bp->eta_neighbours * (1.f + bp->h_tolerance); + bp->delta_neighbours = + (pow_dimension(delta_eta) - pow_dimension(bp->eta_neighbours)) * + kernel_norm; + + /* Number of iterations to converge h */ + bp->max_smoothing_iterations = + parser_get_opt_param_int(params, "BlackHoles:max_ghost_iterations", + hydro_props->max_smoothing_iterations); + + /* Time integration properties */ + const float max_volume_change = + parser_get_opt_param_float(params, "BlackHoles:max_volume_change", -1); + if (max_volume_change == -1) + bp->log_max_h_change = hydro_props->log_max_h_change; + else + bp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); + + /* Accretion parameters ---------------------------------- */ + + bp->f_Edd = parser_get_param_float(params, "EAGLEAGN:max_eddington_fraction"); + bp->epsilon_r = + parser_get_param_float(params, "EAGLEAGN:radiative_efficiency"); + bp->epsilon_f = + parser_get_param_float(params, "EAGLEAGN:coupling_efficiency"); + + /* Feedback parameters ---------------------------------- */ + + bp->AGN_delta_T_desired = + parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_K"); + + bp->num_ngbs_to_heat = + parser_get_param_float(params, "EAGLEAGN:AGN_num_ngb_to_heat"); + + /* Common conversion factors ----------------------------- */ + + /* Calculate temperature to internal energy conversion factor (all internal + * units) */ + const double k_B = phys_const->const_boltzmann_k; + const double m_p = phys_const->const_proton_mass; + const double mu = hydro_props->mu_ionised; + bp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p); +} + +/** + * @brief Write a black_holes_props struct to the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_dump( + const struct black_holes_props *props, FILE *stream) { + restart_write_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, "black_holes props", "black holes props"); +} + +/** + * @brief Restore a black_holes_props struct from the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_restore( + const struct black_holes_props *props, FILE *stream) { + restart_read_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, NULL, "black holes props"); +} + +#endif /* SWIFT_EAGLE_BLACK_HOLES_PROPERTIES_H */ diff --git a/src/black_holes_io.h b/src/black_holes_io.h index 05e66645b182bbd26907b37d5c5560aa5e8b27a7..bc3b1be3be44588794ababfc9500be7812c14f5e 100644 --- a/src/black_holes_io.h +++ b/src/black_holes_io.h @@ -24,6 +24,8 @@ /* Load the correct star type */ #if defined(BLACK_HOLES_NONE) #include "./black_holes/Default/black_holes_io.h" +#elif defined(BLACK_HOLES_EAGLE) +#include "./black_holes/EAGLE/black_holes_io.h" #else #error "Invalid choice of star model" #endif diff --git a/src/black_holes_properties.h b/src/black_holes_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..3226b5391a979d27ab125c88d50acb3d59e1edf0 --- /dev/null +++ b/src/black_holes_properties.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_PROPERTIES_H +#define SWIFT_BLACK_HOLES_PROPERTIES_H + +/* Config parameters. */ +#include "../config.h" + +/* Select the correct black_holes model */ +#if defined(BLACK_HOLES_NONE) +#include "./black_holes/Default/black_holes_properties.h" +#elif defined(BLACK_HOLES_EAGLE) +#include "./black_holes/EAGLE/black_holes_properties.h" +#else +#error "Invalid choice of black hole model" +#endif + +#endif /* SWIFT_BLACK_HOLES_PROPERTIES_H */ diff --git a/src/cell.c b/src/cell.c index 86b54823a1860416968ab236e36d4ee43566d6af..4c6bc5b6fdb168c315f777600ea47ae19d021bb1 100644 --- a/src/cell.c +++ b/src/cell.c @@ -497,12 +497,15 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc, /* Start by packing the data of the current cell. */ pc->hydro.h_max = c->hydro.h_max; pc->stars.h_max = c->stars.h_max; + pc->black_holes.h_max = c->black_holes.h_max; pc->hydro.ti_end_min = c->hydro.ti_end_min; pc->hydro.ti_end_max = c->hydro.ti_end_max; pc->grav.ti_end_min = c->grav.ti_end_min; pc->grav.ti_end_max = c->grav.ti_end_max; pc->stars.ti_end_min = c->stars.ti_end_min; pc->stars.ti_end_max = c->stars.ti_end_max; + pc->black_holes.ti_end_min = c->black_holes.ti_end_min; + pc->black_holes.ti_end_max = c->black_holes.ti_end_max; pc->hydro.ti_old_part = c->hydro.ti_old_part; pc->grav.ti_old_part = c->grav.ti_old_part; pc->grav.ti_old_multipole = c->grav.ti_old_multipole; @@ -510,6 +513,7 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc, pc->hydro.count = c->hydro.count; pc->grav.count = c->grav.count; pc->stars.count = c->stars.count; + pc->black_holes.count = c->black_holes.count; pc->maxdepth = c->maxdepth; /* Copy the Multipole related information */ @@ -602,19 +606,24 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c, /* Unpack the current pcell. */ c->hydro.h_max = pc->hydro.h_max; c->stars.h_max = pc->stars.h_max; + c->black_holes.h_max = pc->black_holes.h_max; c->hydro.ti_end_min = pc->hydro.ti_end_min; c->hydro.ti_end_max = pc->hydro.ti_end_max; c->grav.ti_end_min = pc->grav.ti_end_min; c->grav.ti_end_max = pc->grav.ti_end_max; c->stars.ti_end_min = pc->stars.ti_end_min; c->stars.ti_end_max = pc->stars.ti_end_max; + c->black_holes.ti_end_min = pc->black_holes.ti_end_min; + c->black_holes.ti_end_max = pc->black_holes.ti_end_max; c->hydro.ti_old_part = pc->hydro.ti_old_part; c->grav.ti_old_part = pc->grav.ti_old_part; c->grav.ti_old_multipole = pc->grav.ti_old_multipole; c->stars.ti_old_part = pc->stars.ti_old_part; + c->black_holes.ti_old_part = pc->black_holes.ti_old_part; c->hydro.count = pc->hydro.count; c->grav.count = pc->grav.count; c->stars.count = pc->stars.count; + c->black_holes.count = pc->black_holes.count; c->maxdepth = pc->maxdepth; #ifdef SWIFT_DEBUG_CHECKS @@ -664,6 +673,7 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c, temp->hydro.dx_max_sort = 0.f; temp->stars.dx_max_part = 0.f; temp->stars.dx_max_sort = 0.f; + temp->black_holes.dx_max_part = 0.f; temp->nodeID = c->nodeID; temp->parent = c; c->progeny[k] = temp; @@ -1786,6 +1796,8 @@ void cell_clean_links(struct cell *c, void *data) { c->grav.mm = NULL; c->stars.density = NULL; c->stars.feedback = NULL; + c->black_holes.density = NULL; + c->black_holes.feedback = NULL; } /** @@ -2311,6 +2323,44 @@ void cell_activate_drift_spart(struct cell *c, struct scheduler *s) { } } +/** + * @brief Activate the #bpart drifts on the given cell. + */ +void cell_activate_drift_bpart(struct cell *c, struct scheduler *s) { + + /* If this cell is already marked for drift, quit early. */ + if (cell_get_flag(c, cell_flag_do_bh_drift)) return; + + /* Mark this cell for drifting. */ + cell_set_flag(c, cell_flag_do_bh_drift); + + /* Set the do_black_holes_sub_drifts all the way up and activate the super + drift if this has not yet been done. */ + if (c == c->hydro.super) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->black_holes.drift == NULL) + error("Trying to activate un-existing c->black_holes.drift"); +#endif + scheduler_activate(s, c->black_holes.drift); + } else { + for (struct cell *parent = c->parent; + parent != NULL && !cell_get_flag(parent, cell_flag_do_bh_sub_drift); + parent = parent->parent) { + /* Mark this cell for drifting */ + cell_set_flag(parent, cell_flag_do_bh_sub_drift); + + if (parent == c->hydro.super) { +#ifdef SWIFT_DEBUG_CHECKS + if (parent->black_holes.drift == NULL) + error("Trying to activate un-existing parent->black_holes.drift"); +#endif + scheduler_activate(s, parent->black_holes.drift); + break; + } + } + } +} + /** * @brief Activate the drifts on the given cell. */ @@ -2412,7 +2462,6 @@ void cell_activate_stars_sorts_up(struct cell *c, struct scheduler *s) { #endif scheduler_activate(s, c->stars.sorts); if (c->nodeID == engine_rank) { - // MATTHIEU: to do: do we actually need both drifts here? cell_activate_drift_spart(c, s); } } else { @@ -2662,6 +2711,100 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj, } /* Otherwise, pair interation */ } +/** + * @brief Traverse a sub-cell task and activate the black_holes drift tasks that + * are required by a black_holes task + * + * @param ci The first #cell we recurse in. + * @param cj The second #cell we recurse in. + * @param s The task #scheduler. + */ +void cell_activate_subcell_black_holes_tasks(struct cell *ci, struct cell *cj, + struct scheduler *s) { + const struct engine *e = s->space->e; + + /* Store the current dx_max and h_max values. */ + ci->black_holes.dx_max_part_old = ci->black_holes.dx_max_part; + ci->black_holes.h_max_old = ci->black_holes.h_max; + ci->hydro.dx_max_part_old = ci->hydro.dx_max_part; + ci->hydro.h_max_old = ci->hydro.h_max; + + if (cj != NULL) { + cj->black_holes.dx_max_part_old = cj->black_holes.dx_max_part; + cj->black_holes.h_max_old = cj->black_holes.h_max; + cj->hydro.dx_max_part_old = cj->hydro.dx_max_part; + cj->hydro.h_max_old = cj->hydro.h_max; + } + + /* Self interaction? */ + if (cj == NULL) { + /* Do anything? */ + if (!cell_is_active_black_holes(ci, e) || ci->hydro.count == 0 || + ci->black_holes.count == 0) + return; + + /* Recurse? */ + if (cell_can_recurse_in_self_black_holes_task(ci)) { + /* Loop over all progenies and pairs of progenies */ + for (int j = 0; j < 8; j++) { + if (ci->progeny[j] != NULL) { + cell_activate_subcell_black_holes_tasks(ci->progeny[j], NULL, s); + for (int k = j + 1; k < 8; k++) + if (ci->progeny[k] != NULL) + cell_activate_subcell_black_holes_tasks(ci->progeny[j], + ci->progeny[k], s); + } + } + } else { + /* We have reached the bottom of the tree: activate drift */ + cell_activate_drift_bpart(ci, s); + cell_activate_drift_part(ci, s); + } + } + + /* Otherwise, pair interation */ + else { + /* Should we even bother? */ + if (!cell_is_active_black_holes(ci, e) && + !cell_is_active_black_holes(cj, e)) + return; + + /* Get the orientation of the pair. */ + double shift[3]; + int sid = space_getsid(s->space, &ci, &cj, shift); + + /* recurse? */ + if (cell_can_recurse_in_pair_black_holes_task(ci, cj) && + cell_can_recurse_in_pair_black_holes_task(cj, ci)) { + struct cell_split_pair *csp = &cell_split_pairs[sid]; + for (int k = 0; k < csp->count; k++) { + const int pid = csp->pairs[k].pid; + const int pjd = csp->pairs[k].pjd; + if (ci->progeny[pid] != NULL && cj->progeny[pjd] != NULL) + cell_activate_subcell_black_holes_tasks(ci->progeny[pid], + cj->progeny[pjd], s); + } + } + + /* Otherwise, activate the drifts. */ + else { + if (cell_is_active_black_holes(ci, e)) { + + /* Activate the drifts if the cells are local. */ + if (ci->nodeID == engine_rank) cell_activate_drift_bpart(ci, s); + if (cj->nodeID == engine_rank) cell_activate_drift_part(cj, s); + } + + if (cell_is_active_black_holes(cj, e)) { + + /* Activate the drifts if the cells are local. */ + if (ci->nodeID == engine_rank) cell_activate_drift_part(ci, s); + if (cj->nodeID == engine_rank) cell_activate_drift_bpart(cj, s); + } + } + } /* Otherwise, pair interation */ +} + /** * @brief Traverse a sub-cell task and activate the gravity drift tasks that * are required by a self gravity task. @@ -3394,6 +3537,205 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { return rebuild; } +/** + * @brief Un-skips all the black hole tasks associated with a given cell and + * checks if the space needs to be rebuilt. + * + * @param c the #cell. + * @param s the #scheduler. + * + * @return 1 If the space needs rebuilding. 0 otherwise. + */ +int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s) { + struct engine *e = s->space->e; + const int nodeID = e->nodeID; + int rebuild = 0; + + if (c->black_holes.drift != NULL && cell_is_active_black_holes(c, e)) { + cell_activate_drift_bpart(c, s); + } + + /* Un-skip the density tasks involved with this cell. */ + for (struct link *l = c->black_holes.density; l != NULL; l = l->next) { + struct task *t = l->t; + struct cell *ci = t->ci; + struct cell *cj = t->cj; + const int ci_active = cell_is_active_black_holes(ci, e); + const int cj_active = (cj != NULL) ? cell_is_active_black_holes(cj, e) : 0; +#ifdef WITH_MPI + const int ci_nodeID = ci->nodeID; + const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1; +#else + const int ci_nodeID = nodeID; + const int cj_nodeID = nodeID; +#endif + + /* Activate the drifts */ + if (t->type == task_type_self && ci_active) { + cell_activate_drift_part(ci, s); + cell_activate_drift_bpart(ci, s); + } + + /* Only activate tasks that involve a local active cell. */ + if ((ci_active || cj_active) && + (ci_nodeID == nodeID || cj_nodeID == nodeID)) { + scheduler_activate(s, t); + + if (t->type == task_type_pair) { + /* Do ci */ + if (ci_active) { + + /* Activate the drift tasks. */ + if (ci_nodeID == nodeID) cell_activate_drift_bpart(ci, s); + if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); + } + + /* Do cj */ + if (cj_active) { + + /* Activate the drift tasks. */ + if (cj_nodeID == nodeID) cell_activate_drift_bpart(cj, s); + if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); + } + } + + else if (t->type == task_type_sub_pair || t->type == task_type_sub_self) { + cell_activate_subcell_black_holes_tasks(ci, cj, s); + } + } + + /* Only interested in pair interactions as of here. */ + if (t->type == task_type_pair || t->type == task_type_sub_pair) { + /* Check whether there was too much particle motion, i.e. the + cell neighbour conditions were violated. */ + if (cell_need_rebuild_for_black_holes_pair(ci, cj)) rebuild = 1; + if (cell_need_rebuild_for_black_holes_pair(cj, ci)) rebuild = 1; + +#ifdef WITH_MPI + /* Activate the send/recv tasks. */ + if (ci_nodeID != nodeID) { + if (cj_active) { + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_xv); + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rho); + + /* If the local cell is active, more stuff will be needed. */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_bpart, + ci_nodeID); + cell_activate_drift_bpart(cj, s); + + /* If the local cell is active, send its ti_end values. */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_tend_bpart, + ci_nodeID); + } + + if (ci_active) { + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_bpart); + + /* If the foreign cell is active, we want its ti_end values. */ + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_tend_bpart); + + /* Is the foreign cell active and will need stuff from us? */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_xv, ci_nodeID); + scheduler_activate_send(s, cj->mpi.send, task_subtype_rho, ci_nodeID); + + /* Drift the cell which will be sent; note that not all sent + particles will be drifted, only those that are needed. */ + cell_activate_drift_part(cj, s); + } + + } else if (cj_nodeID != nodeID) { + /* If the local cell is active, receive data from the foreign cell. */ + if (ci_active) { + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_xv); + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rho); + + /* If the local cell is active, more stuff will be needed. */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_bpart, + cj_nodeID); + cell_activate_drift_bpart(ci, s); + + /* If the local cell is active, send its ti_end values. */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_tend_bpart, + cj_nodeID); + } + + if (cj_active) { + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_bpart); + + /* If the foreign cell is active, we want its ti_end values. */ + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_tend_bpart); + + /* Is the foreign cell active and will need stuff from us? */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_xv, cj_nodeID); + scheduler_activate_send(s, ci->mpi.send, task_subtype_rho, cj_nodeID); + + /* Drift the cell which will be sent; note that not all sent + particles will be drifted, only those that are needed. */ + cell_activate_drift_part(ci, s); + } + } +#endif + } + } + + /* Un-skip the feedback tasks involved with this cell. */ + for (struct link *l = c->black_holes.feedback; l != NULL; l = l->next) { + struct task *t = l->t; + struct cell *ci = t->ci; + struct cell *cj = t->cj; + const int ci_active = cell_is_active_black_holes(ci, e); + const int cj_active = (cj != NULL) ? cell_is_active_black_holes(cj, e) : 0; +#ifdef WITH_MPI + const int ci_nodeID = ci->nodeID; + const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1; +#else + const int ci_nodeID = nodeID; + const int cj_nodeID = nodeID; +#endif + + if (t->type == task_type_self && ci_active) { + scheduler_activate(s, t); + } + + else if (t->type == task_type_sub_self && ci_active) { + scheduler_activate(s, t); + } + + else if (t->type == task_type_pair || t->type == task_type_sub_pair) { + /* We only want to activate the task if the cell is active and is + going to update some gas on the *local* node */ + if ((ci_nodeID == nodeID && cj_nodeID == nodeID) && + (ci_active || cj_active)) { + scheduler_activate(s, t); + + } else if ((ci_nodeID == nodeID && cj_nodeID != nodeID) && (cj_active)) { + scheduler_activate(s, t); + + } else if ((ci_nodeID != nodeID && cj_nodeID == nodeID) && (ci_active)) { + scheduler_activate(s, t); + } + } + + /* Nothing more to do here, all drifts activated above */ + } + + /* Unskip all the other task types. */ + if (c->nodeID == nodeID && cell_is_active_black_holes(c, e)) { + if (c->black_holes.ghost != NULL) + scheduler_activate(s, c->black_holes.ghost); + if (c->black_holes.black_holes_in != NULL) + scheduler_activate(s, c->black_holes.black_holes_in); + if (c->black_holes.black_holes_out != NULL) + scheduler_activate(s, c->black_holes.black_holes_out); + if (c->kick1 != NULL) scheduler_activate(s, c->kick1); + if (c->kick2 != NULL) scheduler_activate(s, c->kick2); + if (c->timestep != NULL) scheduler_activate(s, c->timestep); + if (c->logger != NULL) scheduler_activate(s, c->logger); + } + + return rebuild; +} + /** * @brief Set the super-cell pointers for all cells in a hierarchy. * @@ -4236,7 +4578,8 @@ void cell_check_timesteps(struct cell *c) { #ifdef SWIFT_DEBUG_CHECKS if (c->hydro.ti_end_min == 0 && c->grav.ti_end_min == 0 && - c->stars.ti_end_min == 0 && c->nr_tasks > 0) + c->stars.ti_end_min == 0 && c->black_holes.ti_end_min == 0 && + c->nr_tasks > 0) error("Cell without assigned time-step"); if (c->split) { diff --git a/src/cell.h b/src/cell.h index 0c2a1bb5be414530e4ac42af76229c49843ee544..123b7fc5fda2f4b9d6b94c50eeaee991c849a23d 100644 --- a/src/cell.h +++ b/src/cell.h @@ -172,6 +172,26 @@ struct pcell { } stars; + /*! Black hole variables */ + struct { + + /*! Number of #spart in this cell. */ + int count; + + /*! Maximal smoothing length. */ + double h_max; + + /*! Minimal integer end-of-timestep in this cell for black hole tasks */ + integertime_t ti_end_min; + + /*! Maximal integer end-of-timestep in this cell for black hole tasks */ + integertime_t ti_end_max; + + /*! Integer time of the last drift of the #spart in this cell */ + integertime_t ti_old_part; + + } black_holes; + /*! Maximal depth in that part of the tree */ int maxdepth; @@ -606,6 +626,22 @@ struct cell { /*! The drift task for bparts */ struct task *drift; + /*! Implicit tasks marking the entry of the BH physics block of tasks + */ + struct task *black_holes_in; + + /*! Implicit tasks marking the exit of the BH physics block of tasks */ + struct task *black_holes_out; + + /*! The star ghost task itself */ + struct task *ghost; + + /*! Linked list of the tasks computing this cell's star density. */ + struct link *density; + + /*! Linked list of the tasks computing this cell's star feedback. */ + struct link *feedback; + /*! Max smoothing length in this cell. */ double h_max; @@ -627,6 +663,9 @@ struct cell { /*! Maximum part movement in this cell since last construction. */ float dx_max_part; + /*! Values of dx_max before the drifts, used for sub-cell tasks. */ + float dx_max_part_old; + /*! Maximum end of (integer) time step in this cell for black tasks. */ integertime_t ti_end_min; @@ -784,6 +823,7 @@ void cell_check_multipole_drift_point(struct cell *c, void *data); void cell_reset_task_counters(struct cell *c); int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s); int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s); +int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s); int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s); void cell_drift_part(struct cell *c, const struct engine *e, int force); void cell_drift_gpart(struct cell *c, const struct engine *e, int force); @@ -799,12 +839,15 @@ void cell_activate_subcell_grav_tasks(struct cell *ci, struct cell *cj, struct scheduler *s); void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj, struct scheduler *s); +void cell_activate_subcell_black_holes_tasks(struct cell *ci, struct cell *cj, + struct scheduler *s); void cell_activate_subcell_external_grav_tasks(struct cell *ci, struct scheduler *s); void cell_activate_super_spart_drifts(struct cell *c, struct scheduler *s); void cell_activate_drift_part(struct cell *c, struct scheduler *s); void cell_activate_drift_gpart(struct cell *c, struct scheduler *s); void cell_activate_drift_spart(struct cell *c, struct scheduler *s); +void cell_activate_drift_bpart(struct cell *c, struct scheduler *s); void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s); void cell_activate_stars_sorts(struct cell *c, int sid, struct scheduler *s); void cell_activate_limiter(struct cell *c, struct scheduler *s); @@ -969,6 +1012,43 @@ cell_can_recurse_in_self_stars_task(const struct cell *c) { (kernel_gamma * c->hydro.h_max_old < 0.5f * c->dmin); } +/** + * @brief Can a sub-pair black hole task recurse to a lower level based + * on the status of the particles in the cell. + * + * @param ci The #cell with black holes. + * @param cj The #cell with hydro parts. + */ +__attribute__((always_inline)) INLINE static int +cell_can_recurse_in_pair_black_holes_task(const struct cell *ci, + const struct cell *cj) { + + /* Is the cell split ? */ + /* If so, is the cut-off radius plus the max distance the parts have moved */ + /* smaller than the sub-cell sizes ? */ + /* Note: We use the _old values as these might have been updated by a drift */ + return ci->split && cj->split && + ((kernel_gamma * ci->black_holes.h_max_old + + ci->black_holes.dx_max_part_old) < 0.5f * ci->dmin) && + ((kernel_gamma * cj->hydro.h_max_old + cj->hydro.dx_max_part_old) < + 0.5f * cj->dmin); +} + +/** + * @brief Can a sub-self black hole task recurse to a lower level based + * on the status of the particles in the cell. + * + * @param c The #cell. + */ +__attribute__((always_inline)) INLINE static int +cell_can_recurse_in_self_black_holes_task(const struct cell *c) { + + /* Is the cell split and not smaller than the smoothing length? */ + return c->split && + (kernel_gamma * c->black_holes.h_max_old < 0.5f * c->dmin) && + (kernel_gamma * c->hydro.h_max_old < 0.5f * c->dmin); +} + /** * @brief Can a pair hydro task associated with a cell be split into smaller * sub-tasks. @@ -985,7 +1065,8 @@ __attribute__((always_inline)) INLINE static int cell_can_split_pair_hydro_task( /* into account any part motion (i.e. dx_max == 0 here) */ return c->split && (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin) && - (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin); + (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin) && + (space_stretch * kernel_gamma * c->black_holes.h_max < 0.5f * c->dmin); } /** @@ -1004,7 +1085,8 @@ __attribute__((always_inline)) INLINE static int cell_can_split_self_hydro_task( /* tasks will be created. So no need to check for h_max */ return c->split && (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin) && - (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin); + (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin) && + (space_stretch * kernel_gamma * c->black_holes.h_max < 0.5f * c->dmin); } /** @@ -1054,6 +1136,7 @@ cell_need_rebuild_for_hydro_pair(const struct cell *ci, const struct cell *cj) { } return 0; } + /** * @brief Have star particles in a pair of cells moved too much and require a * rebuild? @@ -1075,6 +1158,28 @@ cell_need_rebuild_for_stars_pair(const struct cell *ci, const struct cell *cj) { return 0; } +/** + * @brief Have star particles in a pair of cells moved too much and require a + * rebuild? + * + * @param ci The first #cell. + * @param cj The second #cell. + */ +__attribute__((always_inline)) INLINE static int +cell_need_rebuild_for_black_holes_pair(const struct cell *ci, + const struct cell *cj) { + + /* Is the cut-off radius plus the max distance the parts in both cells have */ + /* moved larger than the cell size ? */ + /* Note ci->dmin == cj->dmin */ + if (kernel_gamma * max(ci->black_holes.h_max, cj->hydro.h_max) + + ci->black_holes.dx_max_part + cj->hydro.dx_max_part > + cj->dmin) { + return 1; + } + return 0; +} + /** * @brief Add a unique tag to a cell, mostly for MPI communications. * diff --git a/src/engine.c b/src/engine.c index afa859e9aeff53e7d03eae5eeaa24a9d017766d1..a82e8db6d6b73ef95c5705b8b6a4adb3b74cec05 100644 --- a/src/engine.c +++ b/src/engine.c @@ -2873,13 +2873,10 @@ void engine_collect_end_of_step_recurse_stars(struct cell *c, void engine_collect_end_of_step_recurse_black_holes(struct cell *c, const struct engine *e) { -/* Skip super-cells (Their values are already set) */ -#ifdef WITH_MPI - // MATTHIEU - if (c->timestep != NULL) - return; // || c->mpi.black_holes.recv_ti != NULL) return; -#else + /* Skip super-cells (Their values are already set) */ if (c->timestep != NULL) return; +#ifdef WITH_MPI + if (cell_get_recv(c, task_subtype_tend_bpart) != NULL) return; #endif /* WITH_MPI */ #ifdef SWIFT_DEBUG_CHECKS @@ -3305,8 +3302,9 @@ void engine_skip_force_and_kick(struct engine *e) { /* Skip everything that updates the particles */ if (t->type == task_type_drift_part || t->type == task_type_drift_gpart || - t->type == task_type_drift_spart || t->type == task_type_kick1 || - t->type == task_type_kick2 || t->type == task_type_timestep || + t->type == task_type_drift_spart || t->type == task_type_drift_bpart || + t->type == task_type_kick1 || t->type == task_type_kick2 || + t->type == task_type_timestep || t->type == task_type_timestep_limiter || t->subtype == task_subtype_force || t->subtype == task_subtype_limiter || t->subtype == task_subtype_grav || @@ -3320,9 +3318,11 @@ void engine_skip_force_and_kick(struct engine *e) { t->type == task_type_extra_ghost || t->subtype == task_subtype_gradient || t->subtype == task_subtype_stars_feedback || + t->subtype == task_subtype_bh_feedback || t->subtype == task_subtype_tend_part || t->subtype == task_subtype_tend_gpart || t->subtype == task_subtype_tend_spart || + t->subtype == task_subtype_tend_bpart || t->subtype == task_subtype_rho || t->subtype == task_subtype_gpart) t->skip = 1; } @@ -4115,6 +4115,7 @@ void engine_unskip(struct engine *e) { const int with_ext_grav = e->policy & engine_policy_external_gravity; const int with_stars = e->policy & engine_policy_stars; const int with_feedback = e->policy & engine_policy_feedback; + const int with_black_holes = e->policy & engine_policy_black_holes; #ifdef WITH_PROFILER static int count = 0; @@ -4134,7 +4135,8 @@ void engine_unskip(struct engine *e) { (with_ext_grav && c->nodeID == nodeID && cell_is_active_gravity(c, e)) || (with_feedback && cell_is_active_stars(c, e)) || - (with_stars && c->nodeID == nodeID && cell_is_active_stars(c, e))) { + (with_stars && c->nodeID == nodeID && cell_is_active_stars(c, e)) || + (with_black_holes && cell_is_active_black_holes(c, e))) { if (num_active_cells != k) memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int)); @@ -4857,6 +4859,7 @@ void engine_unpin(void) { * @param entropy_floor The #entropy_floor_properties for this run. * @param gravity The #gravity_props used for this run. * @param stars The #stars_props used for this run. + * @param black_holes The #black_holes_props used for this run. * @param feedback The #feedback_props used for this run. * @param mesh The #pm_mesh used for the long-range periodic forces. * @param potential The properties of the external potential. @@ -4872,6 +4875,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, struct cosmology *cosmo, struct hydro_props *hydro, const struct entropy_floor_properties *entropy_floor, struct gravity_props *gravity, const struct stars_props *stars, + const struct black_holes_props *black_holes, const struct feedback_props *feedback, struct pm_mesh *mesh, const struct external_potential *potential, struct cooling_function_data *cooling_func, @@ -4939,6 +4943,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, e->entropy_floor = entropy_floor; e->gravity_properties = gravity; e->stars_properties = stars; + e->black_holes_properties = black_holes; e->mesh = mesh; e->external_potential = potential; e->cooling_func = cooling_func; @@ -6083,6 +6088,7 @@ void engine_struct_dump(struct engine *e, FILE *stream) { cooling_struct_dump(e->cooling_func, stream); starformation_struct_dump(e->star_formation, stream); feedback_struct_dump(e->feedback_props, stream); + black_holes_struct_dump(e->black_holes_properties, stream); chemistry_struct_dump(e->chemistry, stream); parser_struct_dump(e->parameter_file, stream); if (e->output_list_snapshots) @@ -6190,6 +6196,11 @@ void engine_struct_restore(struct engine *e, FILE *stream) { feedback_struct_restore(feedback_properties, stream); e->feedback_props = feedback_properties; + struct black_holes_props *black_holes_properties = + (struct black_holes_props *)malloc(sizeof(struct black_holes_props)); + black_holes_struct_restore(black_holes_properties, stream); + e->black_holes_properties = black_holes_properties; + struct chemistry_global_data *chemistry = (struct chemistry_global_data *)malloc( sizeof(struct chemistry_global_data)); diff --git a/src/engine.h b/src/engine.h index 3afa8c221d2d1690dec45d2017a18fc0d3aee8ca..1a653fdfd40c88775b7bbb67265922c9db68a5fa 100644 --- a/src/engine.h +++ b/src/engine.h @@ -34,6 +34,7 @@ /* Includes. */ #include "barrier.h" +#include "black_holes_properties.h" #include "chemistry_struct.h" #include "clocks.h" #include "collectgroup.h" @@ -399,6 +400,9 @@ struct engine { /* Properties of the star model */ const struct stars_props *stars_properties; + /* Properties of the black hole model */ + const struct black_holes_props *black_holes_properties; + /* Properties of the self-gravity scheme */ struct gravity_props *gravity_properties; @@ -473,6 +477,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, struct cosmology *cosmo, struct hydro_props *hydro, const struct entropy_floor_properties *entropy_floor, struct gravity_props *gravity, const struct stars_props *stars, + const struct black_holes_props *black_holes, const struct feedback_props *feedback, struct pm_mesh *mesh, const struct external_potential *potential, struct cooling_function_data *cooling_func, diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c index ca69599b8e64460484e174aca9fc786dd45d8349..201bc357d7e85c89712847804e0ba3d111c8661f 100644 --- a/src/engine_maketasks.c +++ b/src/engine_maketasks.c @@ -292,6 +292,75 @@ void engine_addtasks_send_stars(struct engine *e, struct cell *ci, #endif } +/** + * @brief Add send tasks for the black holes pairs to a hierarchy of cells. + * + * @param e The #engine. + * @param ci The sending #cell. + * @param cj Dummy cell containing the nodeID of the receiving node. + * @param t_feedback The send_feed #task, if it has already been created. + * @param t_ti The recv_ti_end #task, if it has already been created. + */ +void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, + struct cell *cj, struct task *t_feedback, + struct task *t_ti) { + +#ifdef WITH_MPI + + struct link *l = NULL; + struct scheduler *s = &e->sched; + const int nodeID = cj->nodeID; + + /* Check if any of the density tasks are for the target node. */ + for (l = ci->black_holes.density; l != NULL; l = l->next) + if (l->t->ci->nodeID == nodeID || + (l->t->cj != NULL && l->t->cj->nodeID == nodeID)) + break; + + /* If so, attach send tasks. */ + if (l != NULL) { + + if (t_feedback == NULL) { + + /* Make sure this cell is tagged. */ + cell_ensure_tagged(ci); + + /* Create the tasks and their dependencies? */ + t_feedback = scheduler_addtask(s, task_type_send, task_subtype_bpart, + ci->mpi.tag, 0, ci, cj); + + t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend_bpart, + ci->mpi.tag, 0, ci, cj); + + /* The send_black_holes task should unlock the super_cell's kick task. */ + scheduler_addunlock(s, t_feedback, + ci->hydro.super->black_holes.black_holes_out); + + /* Ghost before you send */ + scheduler_addunlock(s, ci->hydro.super->black_holes.ghost, t_feedback); + + /* Drift before you send */ + scheduler_addunlock(s, ci->hydro.super->black_holes.drift, t_feedback); + + scheduler_addunlock(s, ci->super->timestep, t_ti); + } + + engine_addlink(e, &ci->mpi.send, t_feedback); + engine_addlink(e, &ci->mpi.send, t_ti); + } + + /* Recurse? */ + if (ci->split) + for (int k = 0; k < 8; k++) + if (ci->progeny[k] != NULL) + engine_addtasks_send_black_holes(e, ci->progeny[k], cj, t_feedback, + t_ti); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + /** * @brief Add recv tasks for hydro pairs to a hierarchy of cells. * @@ -445,6 +514,66 @@ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, #endif } +/** + * @brief Add recv tasks for black_holes pairs to a hierarchy of cells. + * + * @param e The #engine. + * @param c The foreign #cell. + * @param t_feedback The recv_feed #task, if it has already been created. + * @param t_ti The recv_ti_end #task, if it has already been created. + */ +void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c, + struct task *t_feedback, + struct task *t_ti) { + +#ifdef WITH_MPI + struct scheduler *s = &e->sched; + + /* Have we reached a level where there are any black_holes tasks ? */ + if (t_feedback == NULL && c->black_holes.density != NULL) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure this cell has a valid tag. */ + if (c->mpi.tag < 0) error("Trying to receive from untagged cell."); +#endif // SWIFT_DEBUG_CHECKS + + /* Create the tasks. */ + t_feedback = scheduler_addtask(s, task_type_recv, task_subtype_bpart, + c->mpi.tag, 0, c, NULL); + + t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend_bpart, + c->mpi.tag, 0, c, NULL); + } + + if (t_feedback != NULL) { + engine_addlink(e, &c->mpi.recv, t_feedback); + engine_addlink(e, &c->mpi.recv, t_ti); + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == e->nodeID) error("Local cell!"); +#endif + + for (struct link *l = c->black_holes.density; l != NULL; l = l->next) { + scheduler_addunlock(s, l->t, t_feedback); + } + + for (struct link *l = c->black_holes.feedback; l != NULL; l = l->next) { + scheduler_addunlock(s, t_feedback, l->t); + scheduler_addunlock(s, l->t, t_ti); + } + } + + /* Recurse? */ + if (c->split) + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) + engine_addtasks_recv_black_holes(e, c->progeny[k], t_feedback, t_ti); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + /** * @brief Add recv tasks for gravity pairs to a hierarchy of cells. * @@ -729,6 +858,7 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { const int with_feedback = (e->policy & engine_policy_feedback); const int with_cooling = (e->policy & engine_policy_cooling); const int with_star_formation = (e->policy & engine_policy_star_formation); + const int with_black_holes = (e->policy & engine_policy_black_holes); /* Are we in a super-cell ? */ if (c->hydro.super == c) { @@ -775,6 +905,13 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { scheduler_addunlock(s, c->stars.drift, c->super->kick2); } + /* Black holes */ + if (with_black_holes) { + c->black_holes.drift = scheduler_addtask( + s, task_type_drift_bpart, task_subtype_none, 0, 0, c, NULL); + scheduler_addunlock(s, c->black_holes.drift, c->super->kick2); + } + /* Subgrid tasks: cooling */ if (with_cooling) { @@ -810,6 +947,25 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { c->stars.stars_in); } } + + /* Subgrid tasks: black hole feedback */ + if (with_black_holes) { + + c->black_holes.black_holes_in = + scheduler_addtask(s, task_type_bh_in, task_subtype_none, 0, + /* implicit = */ 1, c, NULL); + + c->black_holes.black_holes_out = + scheduler_addtask(s, task_type_bh_out, task_subtype_none, 0, + /* implicit = */ 1, c, NULL); + + c->black_holes.ghost = scheduler_addtask( + s, task_type_bh_ghost, task_subtype_none, 0, 0, c, NULL); + + scheduler_addunlock(s, c->super->kick2, c->black_holes.black_holes_in); + scheduler_addunlock(s, c->black_holes.black_holes_out, + c->super->timestep); + } } } else { /* We are above the super-cell so need to go deeper */ @@ -1085,6 +1241,10 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->stars.density, t); } else if (t->subtype == task_subtype_stars_feedback) { engine_addlink(e, &ci->stars.feedback, t); + } else if (t->subtype == task_subtype_bh_density) { + engine_addlink(e, &ci->black_holes.density, t); + } else if (t->subtype == task_subtype_bh_feedback) { + engine_addlink(e, &ci->black_holes.feedback, t); } /* Link pair tasks to cells. */ @@ -1104,6 +1264,12 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, } else if (t->subtype == task_subtype_stars_feedback) { engine_addlink(e, &ci->stars.feedback, t); engine_addlink(e, &cj->stars.feedback, t); + } else if (t->subtype == task_subtype_bh_density) { + engine_addlink(e, &ci->black_holes.density, t); + engine_addlink(e, &cj->black_holes.density, t); + } else if (t->subtype == task_subtype_bh_feedback) { + engine_addlink(e, &ci->black_holes.feedback, t); + engine_addlink(e, &cj->black_holes.feedback, t); } #ifdef SWIFT_DEBUG_CHECKS else if (t_subtype == task_subtype_external_grav) { @@ -1125,6 +1291,10 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->stars.density, t); } else if (t->subtype == task_subtype_stars_feedback) { engine_addlink(e, &ci->stars.feedback, t); + } else if (t->subtype == task_subtype_bh_density) { + engine_addlink(e, &ci->black_holes.density, t); + } else if (t->subtype == task_subtype_bh_feedback) { + engine_addlink(e, &ci->black_holes.feedback, t); } /* Link sub-pair tasks to cells. */ @@ -1144,6 +1314,12 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, } else if (t->subtype == task_subtype_stars_feedback) { engine_addlink(e, &ci->stars.feedback, t); engine_addlink(e, &cj->stars.feedback, t); + } else if (t->subtype == task_subtype_bh_density) { + engine_addlink(e, &ci->black_holes.density, t); + engine_addlink(e, &cj->black_holes.density, t); + } else if (t->subtype == task_subtype_bh_feedback) { + engine_addlink(e, &ci->black_holes.feedback, t); + engine_addlink(e, &cj->black_holes.feedback, t); } #ifdef SWIFT_DEBUG_CHECKS else if (t_subtype == task_subtype_external_grav) { @@ -1398,6 +1574,7 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, const int with_cooling = (e->policy & engine_policy_cooling); const int with_limiter = (e->policy & engine_policy_limiter); const int with_feedback = (e->policy & engine_policy_feedback); + const int with_black_holes = (e->policy & engine_policy_black_holes); #ifdef EXTRA_HYDRO_LOOP struct task *t_gradient = NULL; #endif @@ -1405,6 +1582,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, struct task *t_limiter = NULL; struct task *t_star_density = NULL; struct task *t_star_feedback = NULL; + struct task *t_bh_density = NULL; + struct task *t_bh_feedback = NULL; for (int ind = 0; ind < num_elements; ind++) { @@ -1451,6 +1630,15 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, task_subtype_stars_feedback, flags, 0, ci, NULL); } + /* The black hole feedback tasks */ + if (with_black_holes) { + t_bh_density = scheduler_addtask( + sched, task_type_self, task_subtype_bh_density, flags, 0, ci, NULL); + t_bh_feedback = + scheduler_addtask(sched, task_type_self, task_subtype_bh_feedback, + flags, 0, ci, NULL); + } + /* Link the tasks to the cells */ engine_addlink(e, &ci->hydro.force, t_force); if (with_limiter) { @@ -1460,6 +1648,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->stars.density, t_star_density); engine_addlink(e, &ci->stars.feedback, t_star_feedback); } + if (with_black_holes) { + engine_addlink(e, &ci->black_holes.density, t_bh_density); + engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); + } #ifdef EXTRA_HYDRO_LOOP @@ -1500,6 +1692,21 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, ci->hydro.super->stars.stars_out); } + if (with_black_holes) { + + scheduler_addunlock(sched, ci->hydro.super->black_holes.drift, + t_bh_density); + scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_bh_density); + scheduler_addunlock(sched, ci->hydro.super->black_holes.black_holes_in, + t_bh_density); + scheduler_addunlock(sched, t_bh_density, + ci->hydro.super->black_holes.ghost); + scheduler_addunlock(sched, ci->hydro.super->black_holes.ghost, + t_bh_feedback); + scheduler_addunlock(sched, t_bh_feedback, + ci->hydro.super->black_holes.black_holes_out); + } + if (with_limiter) { scheduler_addunlock(sched, ci->super->kick2, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); @@ -1544,6 +1751,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, task_subtype_stars_feedback, flags, 0, ci, cj); } + /* The black hole feedback tasks */ + if (with_black_holes) { + t_bh_density = scheduler_addtask( + sched, task_type_pair, task_subtype_bh_density, flags, 0, ci, cj); + t_bh_feedback = scheduler_addtask( + sched, task_type_pair, task_subtype_bh_feedback, flags, 0, ci, cj); + } + engine_addlink(e, &ci->hydro.force, t_force); engine_addlink(e, &cj->hydro.force, t_force); if (with_limiter) { @@ -1556,6 +1771,12 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->stars.feedback, t_star_feedback); engine_addlink(e, &cj->stars.feedback, t_star_feedback); } + if (with_black_holes) { + engine_addlink(e, &ci->black_holes.density, t_bh_density); + engine_addlink(e, &cj->black_holes.density, t_bh_density); + engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); + engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback); + } #ifdef EXTRA_HYDRO_LOOP @@ -1624,6 +1845,22 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, ci->hydro.super->stars.stars_out); } + if (with_black_holes) { + + scheduler_addunlock(sched, ci->hydro.super->black_holes.drift, + t_bh_density); + scheduler_addunlock(sched, ci->hydro.super->hydro.drift, + t_bh_density); + scheduler_addunlock( + sched, ci->hydro.super->black_holes.black_holes_in, t_bh_density); + scheduler_addunlock(sched, t_bh_density, + ci->hydro.super->black_holes.ghost); + scheduler_addunlock(sched, ci->hydro.super->black_holes.ghost, + t_bh_feedback); + scheduler_addunlock(sched, t_bh_feedback, + ci->hydro.super->black_holes.black_holes_out); + } + if (with_limiter) { scheduler_addunlock(sched, ci->super->kick2, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); @@ -1660,6 +1897,23 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, cj->hydro.super->stars.stars_out); } + if (with_black_holes) { + + scheduler_addunlock(sched, cj->hydro.super->black_holes.drift, + t_bh_density); + scheduler_addunlock(sched, cj->hydro.super->hydro.drift, + t_bh_density); + scheduler_addunlock(sched, + cj->hydro.super->black_holes.black_holes_in, + t_bh_density); + scheduler_addunlock(sched, t_bh_density, + cj->hydro.super->black_holes.ghost); + scheduler_addunlock(sched, cj->hydro.super->black_holes.ghost, + t_bh_feedback); + scheduler_addunlock(sched, t_bh_feedback, + cj->hydro.super->black_holes.black_holes_out); + } + if (with_limiter) { scheduler_addunlock(sched, cj->super->kick2, t_limiter); scheduler_addunlock(sched, t_limiter, cj->super->timestep); @@ -1702,6 +1956,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, task_subtype_stars_feedback, flags, 0, ci, NULL); } + /* The black hole feedback tasks */ + if (with_black_holes) { + t_bh_density = + scheduler_addtask(sched, task_type_sub_self, + task_subtype_bh_density, flags, 0, ci, NULL); + t_bh_feedback = + scheduler_addtask(sched, task_type_sub_self, + task_subtype_bh_feedback, flags, 0, ci, NULL); + } + /* Add the link between the new loop and the cell */ engine_addlink(e, &ci->hydro.force, t_force); if (with_limiter) { @@ -1711,6 +1975,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->stars.density, t_star_density); engine_addlink(e, &ci->stars.feedback, t_star_feedback); } + if (with_black_holes) { + engine_addlink(e, &ci->black_holes.density, t_bh_density); + engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); + } #ifdef EXTRA_HYDRO_LOOP @@ -1757,6 +2025,21 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, ci->hydro.super->stars.stars_out); } + if (with_black_holes) { + + scheduler_addunlock(sched, ci->hydro.super->black_holes.drift, + t_bh_density); + scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_bh_density); + scheduler_addunlock(sched, ci->hydro.super->black_holes.black_holes_in, + t_bh_density); + scheduler_addunlock(sched, t_bh_density, + ci->hydro.super->black_holes.ghost); + scheduler_addunlock(sched, ci->hydro.super->black_holes.ghost, + t_bh_feedback); + scheduler_addunlock(sched, t_bh_feedback, + ci->hydro.super->black_holes.black_holes_out); + } + if (with_limiter) { scheduler_addunlock(sched, ci->super->kick2, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); @@ -1803,6 +2086,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, task_subtype_stars_feedback, flags, 0, ci, cj); } + /* The black hole feedback tasks */ + if (with_black_holes) { + t_bh_density = + scheduler_addtask(sched, task_type_sub_pair, + task_subtype_bh_density, flags, 0, ci, cj); + t_bh_feedback = + scheduler_addtask(sched, task_type_sub_pair, + task_subtype_bh_feedback, flags, 0, ci, cj); + } + engine_addlink(e, &ci->hydro.force, t_force); engine_addlink(e, &cj->hydro.force, t_force); if (with_limiter) { @@ -1815,6 +2108,12 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->stars.feedback, t_star_feedback); engine_addlink(e, &cj->stars.feedback, t_star_feedback); } + if (with_black_holes) { + engine_addlink(e, &ci->black_holes.density, t_bh_density); + engine_addlink(e, &cj->black_holes.density, t_bh_density); + engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); + engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback); + } #ifdef EXTRA_HYDRO_LOOP @@ -1882,6 +2181,22 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, ci->hydro.super->stars.stars_out); } + if (with_black_holes) { + + scheduler_addunlock(sched, ci->hydro.super->black_holes.drift, + t_bh_density); + scheduler_addunlock(sched, ci->hydro.super->hydro.drift, + t_bh_density); + scheduler_addunlock( + sched, ci->hydro.super->black_holes.black_holes_in, t_bh_density); + scheduler_addunlock(sched, t_bh_density, + ci->hydro.super->black_holes.ghost); + scheduler_addunlock(sched, ci->hydro.super->black_holes.ghost, + t_bh_feedback); + scheduler_addunlock(sched, t_bh_feedback, + ci->hydro.super->black_holes.black_holes_out); + } + if (with_limiter) { scheduler_addunlock(sched, ci->super->kick2, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); @@ -1920,6 +2235,23 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, cj->hydro.super->stars.stars_out); } + if (with_black_holes) { + + scheduler_addunlock(sched, cj->hydro.super->black_holes.drift, + t_bh_density); + scheduler_addunlock(sched, cj->hydro.super->hydro.drift, + t_bh_density); + scheduler_addunlock(sched, + cj->hydro.super->black_holes.black_holes_in, + t_bh_density); + scheduler_addunlock(sched, t_bh_density, + cj->hydro.super->black_holes.ghost); + scheduler_addunlock(sched, cj->hydro.super->black_holes.ghost, + t_bh_feedback); + scheduler_addunlock(sched, t_bh_feedback, + cj->hydro.super->black_holes.black_holes_out); + } + if (with_limiter) { scheduler_addunlock(sched, cj->super->kick2, t_limiter); scheduler_addunlock(sched, t_limiter, cj->super->timestep); @@ -1956,6 +2288,7 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, const int periodic = e->s->periodic; const int with_feedback = (e->policy & engine_policy_feedback); const int with_stars = (e->policy & engine_policy_stars); + const int with_black_holes = (e->policy & engine_policy_black_holes); struct space *s = e->s; struct scheduler *sched = &e->sched; @@ -1978,7 +2311,8 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, struct cell *ci = &cells[cid]; /* Skip cells without hydro or star particles */ - if ((ci->hydro.count == 0) && (!with_stars || ci->stars.count == 0)) + if ((ci->hydro.count == 0) && (!with_stars || ci->stars.count == 0) && + (!with_black_holes || ci->black_holes.count == 0)) continue; /* If the cell is local build a self-interaction */ @@ -2008,7 +2342,8 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, /* Is that neighbour local and does it have gas or star particles ? */ if ((cid >= cjd) || ((cj->hydro.count == 0) && - (!with_feedback || cj->stars.count == 0)) || + (!with_feedback || cj->stars.count == 0) && + (!with_black_holes || cj->black_holes.count == 0)) || (ci->nodeID != nodeID && cj->nodeID != nodeID)) continue; @@ -2083,9 +2418,6 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements, struct cell *cj = cell_type_pairs[k].cj; const int type = cell_type_pairs[k].type; - /* Add the send task for the particle timesteps. */ - // engine_addtasks_send_timestep(e, ci, cj, NULL, NULL, with_limiter); - /* Add the send tasks for the cells in the proxy that have a hydro * connection. */ if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) @@ -2098,6 +2430,13 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements, if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro)) engine_addtasks_send_stars(e, ci, cj, /*t_feedback=*/NULL, /*t_ti=*/NULL); + /* Add the send tasks for the cells in the proxy that have a black holes + * connection. */ + if ((e->policy & engine_policy_black_holes) && + (type & proxy_cell_type_hydro)) + engine_addtasks_send_black_holes(e, ci, cj, /*t_feedback=*/NULL, + /*t_ti=*/NULL); + /* Add the send tasks for the cells in the proxy that have a gravity * connection. */ if ((e->policy & engine_policy_self_gravity) && @@ -2117,9 +2456,6 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements, struct cell *ci = cell_type_pairs[k].ci; const int type = cell_type_pairs[k].type; - /* Add the recv task for the particle timesteps. */ - // engine_addtasks_recv_timestep(e, ci, NULL, NULL, with_limiter); - /* Add the recv tasks for the cells in the proxy that have a hydro * connection. */ if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) @@ -2130,6 +2466,12 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements, if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro)) engine_addtasks_recv_stars(e, ci, NULL, NULL); + /* Add the recv tasks for the cells in the proxy that have a black holes + * connection. */ + if ((e->policy & engine_policy_black_holes) && + (type & proxy_cell_type_hydro)) + engine_addtasks_recv_black_holes(e, ci, NULL, NULL); + /* Add the recv tasks for the cells in the proxy that have a gravity * connection. */ if ((e->policy & engine_policy_self_gravity) && diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c index fd0d99432ef184643245696ed8770db215560386..ba52dd48de43b1388735a440f2a6f842dfbc22c6 100644 --- a/src/engine_marktasks.c +++ b/src/engine_marktasks.c @@ -164,6 +164,37 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (cell_is_active_stars(ci, e)) scheduler_activate(s, t); } + /* Activate the black hole density */ + else if (t_type == task_type_self && + t_subtype == task_subtype_bh_density) { + if (cell_is_active_black_holes(ci, e)) { + scheduler_activate(s, t); + cell_activate_drift_part(ci, s); + cell_activate_drift_bpart(ci, s); + } + } + + /* Store current values of dx_max and h_max. */ + else if (t_type == task_type_sub_self && + t_subtype == task_subtype_bh_density) { + if (cell_is_active_black_holes(ci, e)) { + scheduler_activate(s, t); + cell_activate_subcell_black_holes_tasks(ci, NULL, s); + } + } + + else if (t_type == task_type_self && + t_subtype == task_subtype_bh_feedback) { + if (cell_is_active_black_holes(ci, e)) { + scheduler_activate(s, t); + } + } + + else if (t_type == task_type_sub_self && + t_subtype == task_subtype_bh_feedback) { + if (cell_is_active_black_holes(ci, e)) scheduler_activate(s, t); + } + /* Activate the gravity drift */ else if (t_type == task_type_self && t_subtype == task_subtype_grav) { if (cell_is_active_gravity(ci, e)) { @@ -210,6 +241,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements, const int ci_active_stars = cell_is_active_stars(ci, e); const int cj_active_stars = cell_is_active_stars(cj, e); + const int ci_active_black_holes = cell_is_active_black_holes(ci, e); + const int cj_active_black_holes = cell_is_active_black_holes(cj, e); + /* Only activate tasks that involve a local active cell. */ if ((t_subtype == task_subtype_density || t_subtype == task_subtype_gradient || @@ -330,6 +364,62 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } } + /* Black_Holes density */ + else if ((t_subtype == task_subtype_bh_density) && + (ci_active_black_holes || cj_active_black_holes) && + (ci_nodeID == nodeID || cj_nodeID == nodeID)) { + + scheduler_activate(s, t); + + /* Set the correct sorting flags */ + if (t_type == task_type_pair) { + + /* Do ci */ + if (ci_active_black_holes) { + + /* Activate the drift tasks. */ + if (ci_nodeID == nodeID) cell_activate_drift_bpart(ci, s); + if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); + } + + /* Do cj */ + if (cj_active_black_holes) { + + /* Activate the drift tasks. */ + if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); + if (cj_nodeID == nodeID) cell_activate_drift_bpart(cj, s); + } + } + + /* Store current values of dx_max and h_max. */ + else if (t_type == task_type_sub_pair && + t_subtype == task_subtype_bh_density) { + cell_activate_subcell_black_holes_tasks(ci, cj, s); + } + } + + /* Black_Holes feedback */ + else if (t_subtype == task_subtype_bh_feedback) { + + /* We only want to activate the task if the cell is active and is + going to update some gas on the *local* node */ + if ((ci_nodeID == nodeID && cj_nodeID == nodeID) && + (ci_active_black_holes || cj_active_black_holes)) { + + scheduler_activate(s, t); + + } else if ((ci_nodeID == nodeID && cj_nodeID != nodeID) && + (cj_active_black_holes)) { + + scheduler_activate(s, t); + + } else if ((ci_nodeID != nodeID && cj_nodeID == nodeID) && + (ci_active_black_holes)) { + + scheduler_activate(s, t); + } + } + /* Gravity */ else if ((t_subtype == task_subtype_grav) && ((ci_active_gravity && ci_nodeID == nodeID) || @@ -531,6 +621,85 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #endif } + /* Only interested in black hole density tasks as of here. */ + else if (t->subtype == task_subtype_bh_density) { + + /* Too much particle movement? */ + if (cell_need_rebuild_for_black_holes_pair(ci, cj)) *rebuild_space = 1; + if (cell_need_rebuild_for_black_holes_pair(cj, ci)) *rebuild_space = 1; + +#ifdef WITH_MPI + /* Activate the send/recv tasks. */ + if (ci_nodeID != nodeID) { + + if (cj_active_black_holes) { + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_xv); + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rho); + + /* If the local cell is active, more stuff will be needed. */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_bpart, + ci_nodeID); + cell_activate_drift_bpart(cj, s); + + /* If the local cell is active, send its ti_end values. */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_tend_bpart, + ci_nodeID); + } + + if (ci_active_black_holes) { + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_bpart); + + /* If the foreign cell is active, we want its ti_end values. */ + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_tend_bpart); + + /* Is the foreign cell active and will need stuff from us? */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_xv, + ci_nodeID); + scheduler_activate_send(s, cj->mpi.send, task_subtype_rho, + ci_nodeID); + + /* Drift the cell which will be sent; note that not all sent + particles will be drifted, only those that are needed. */ + cell_activate_drift_part(cj, s); + } + + } else if (cj_nodeID != nodeID) { + + /* If the local cell is active, receive data from the foreign cell. */ + if (ci_active_black_holes) { + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_xv); + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rho); + + /* If the local cell is active, more stuff will be needed. */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_bpart, + cj_nodeID); + cell_activate_drift_bpart(ci, s); + + /* If the local cell is active, send its ti_end values. */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_tend_bpart, + cj_nodeID); + } + + if (cj_active_black_holes) { + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_bpart); + + /* If the foreign cell is active, we want its ti_end values. */ + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_tend_bpart); + + /* Is the foreign cell active and will need stuff from us? */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_xv, + cj_nodeID); + scheduler_activate_send(s, ci->mpi.send, task_subtype_rho, + cj_nodeID); + + /* Drift the cell which will be sent; note that not all sent + particles will be drifted, only those that are needed. */ + cell_activate_drift_part(ci, s); + } + } +#endif + } + /* Only interested in gravity tasks as of here. */ else if (t_subtype == task_subtype_grav) { @@ -609,7 +778,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Kick ? */ else if (t_type == task_type_kick1 || t_type == task_type_kick2) { - if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e)) + if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e) || + cell_is_active_stars(t->ci, e) || + cell_is_active_black_holes(t->ci, e)) scheduler_activate(s, t); } @@ -667,12 +838,25 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (cell_is_active_stars(t->ci, e)) scheduler_activate(s, t); } + /* Black hole ghost tasks ? */ + else if (t_type == task_type_bh_ghost) { + if (cell_is_active_black_holes(t->ci, e)) scheduler_activate(s, t); + } + + /* Black holes implicit tasks? */ + else if (t_type == task_type_bh_in || t_type == task_type_bh_out) { + if (cell_is_active_black_holes(t->ci, e)) scheduler_activate(s, t); + } + /* Time-step? */ else if (t_type == task_type_timestep) { t->ci->hydro.updated = 0; t->ci->grav.updated = 0; t->ci->stars.updated = 0; - if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e)) + t->ci->black_holes.updated = 0; + if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e) || + cell_is_active_stars(t->ci, e) || + cell_is_active_black_holes(t->ci, e)) scheduler_activate(s, t); } diff --git a/src/feedback/EAGLE/feedback.c b/src/feedback/EAGLE/feedback.c index 37d028b64f944cd59cd931cb013b04bcde81c88d..c6bffce2cae2ca6b536cf7046076d38ef3ab544a 100644 --- a/src/feedback/EAGLE/feedback.c +++ b/src/feedback/EAGLE/feedback.c @@ -794,8 +794,6 @@ void compute_stellar_evolution(const struct feedback_props* feedback_props, /** * @brief Initialize the global properties of the feedback scheme. * - * By default, takes the values provided by the hydro. - * * @param fp The #feedback_props. * @param phys_const The physical constants in the internal unit system. * @param us The internal unit system. diff --git a/src/parser.h b/src/parser.h index 9bbc1abcebd052a1160d4fdb7bbef7b11fd2bba5..87e618503dca567f7cf381475fcc91f1575b3913 100644 --- a/src/parser.h +++ b/src/parser.h @@ -32,7 +32,7 @@ /* Some constants. */ #define PARSER_MAX_LINE_SIZE 256 -#define PARSER_MAX_NO_OF_PARAMS 256 +#define PARSER_MAX_NO_OF_PARAMS 512 #define PARSER_MAX_NO_OF_SECTIONS 64 /* A parameter in the input file */ diff --git a/src/part.h b/src/part.h index 629f6dbfa58692fc067546b2eb32c89519dc93d7..a9fdb7889368ca10c2e6f157da1564b679370264 100644 --- a/src/part.h +++ b/src/part.h @@ -109,6 +109,8 @@ /* Import the right black hole particle definition */ #if defined(BLACK_HOLES_NONE) #include "./black_holes/Default/black_holes_part.h" +#elif defined(BLACK_HOLES_EAGLE) +#include "./black_holes/EAGLE/black_holes_part.h" #else #error "Invalid choice of black hole particle" #endif diff --git a/src/runner.c b/src/runner.c index 3ff1c1274c09936c7b77e939a5c0b6b88750858d..6c782536dbe4202e4919e46aabeac81524d2deb3 100644 --- a/src/runner.c +++ b/src/runner.c @@ -41,6 +41,8 @@ #include "active.h" #include "approx_math.h" #include "atomic.h" +#include "black_holes.h" +#include "black_holes_properties.h" #include "cell.h" #include "chemistry.h" #include "const.h" @@ -126,6 +128,20 @@ #undef FUNCTION_TASK_LOOP #undef FUNCTION +/* Import the black hole density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_black_holes.h" +#undef FUNCTION_TASK_LOOP +#undef FUNCTION + +/* Import the black hole feedback loop functions. */ +#define FUNCTION feedback +#define FUNCTION_TASK_LOOP TASK_LOOP_FEEDBACK +#include "runner_doiact_black_holes.h" +#undef FUNCTION_TASK_LOOP +#undef FUNCTION + /** * @brief Intermediate task after the density to check that the smoothing * lengths are correct. @@ -485,6 +501,352 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { if (timer) TIMER_TOC(timer_do_stars_ghost); } +/** + * @brief Intermediate task after the density to check that the smoothing + * lengths are correct. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_black_holes_ghost(struct runner *r, struct cell *c, int timer) { + + struct bpart *restrict bparts = c->black_holes.parts; + const struct engine *e = r->e; + const int with_cosmology = e->policy & engine_policy_cosmology; + const struct cosmology *cosmo = e->cosmology; + const float black_holes_h_max = e->hydro_properties->h_max; + const float black_holes_h_min = e->hydro_properties->h_min; + const float eps = e->black_holes_properties->h_tolerance; + const float black_holes_eta_dim = + pow_dimension(e->black_holes_properties->eta_neighbours); + const int max_smoothing_iter = e->hydro_properties->max_smoothing_iterations; + int redo = 0, bcount = 0; + + /* Running value of the maximal smoothing length */ + double h_max = c->black_holes.h_max; + + TIMER_TIC; + + /* Anything to do here? */ + if (c->black_holes.count == 0) return; + if (!cell_is_active_black_holes(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + runner_do_black_holes_ghost(r, c->progeny[k], 0); + + /* Update h_max */ + h_max = max(h_max, c->progeny[k]->black_holes.h_max); + } + } + } else { + + /* Init the list of active particles that have to be updated. */ + int *sid = NULL; + float *h_0 = NULL; + float *left = NULL; + float *right = NULL; + if ((sid = (int *)malloc(sizeof(int) * c->black_holes.count)) == NULL) + error("Can't allocate memory for sid."); + if ((h_0 = (float *)malloc(sizeof(float) * c->black_holes.count)) == NULL) + error("Can't allocate memory for h_0."); + if ((left = (float *)malloc(sizeof(float) * c->black_holes.count)) == NULL) + error("Can't allocate memory for left."); + if ((right = (float *)malloc(sizeof(float) * c->black_holes.count)) == NULL) + error("Can't allocate memory for right."); + for (int k = 0; k < c->black_holes.count; k++) + if (bpart_is_active(&bparts[k], e)) { + sid[bcount] = k; + h_0[bcount] = bparts[k].h; + left[bcount] = 0.f; + right[bcount] = black_holes_h_max; + ++bcount; + } + + /* While there are particles that need to be updated... */ + for (int num_reruns = 0; bcount > 0 && num_reruns < max_smoothing_iter; + num_reruns++) { + + /* Reset the redo-count. */ + redo = 0; + + /* Loop over the remaining active parts in this cell. */ + for (int i = 0; i < bcount; i++) { + + /* Get a direct pointer on the part. */ + struct bpart *bp = &bparts[sid[i]]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Is this part within the timestep? */ + if (!bpart_is_active(bp, e)) + error("Ghost applied to inactive particle"); +#endif + + /* Get some useful values */ + const float h_init = h_0[i]; + const float h_old = bp->h; + const float h_old_dim = pow_dimension(h_old); + const float h_old_dim_minus_one = pow_dimension_minus_one(h_old); + + float h_new; + int has_no_neighbours = 0; + + if (bp->density.wcount == 0.f) { /* No neighbours case */ + + /* Flag that there were no neighbours */ + has_no_neighbours = 1; + + /* Double h and try again */ + h_new = 2.f * h_old; + + } else { + + /* Finish the density calculation */ + black_holes_end_density(bp, cosmo); + + /* Compute one step of the Newton-Raphson scheme */ + const float n_sum = bp->density.wcount * h_old_dim; + const float n_target = black_holes_eta_dim; + const float f = n_sum - n_target; + const float f_prime = + bp->density.wcount_dh * h_old_dim + + hydro_dimension * bp->density.wcount * h_old_dim_minus_one; + + /* Improve the bisection bounds */ + if (n_sum < n_target) + left[i] = max(left[i], h_old); + else if (n_sum > n_target) + right[i] = min(right[i], h_old); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check the validity of the left and right bounds */ + if (left[i] > right[i]) + error("Invalid left (%e) and right (%e)", left[i], right[i]); +#endif + + /* Skip if h is already h_max and we don't have enough neighbours */ + /* Same if we are below h_min */ + if (((bp->h >= black_holes_h_max) && (f < 0.f)) || + ((bp->h <= black_holes_h_min) && (f > 0.f))) { + + /* Get particle time-step */ + double dt; + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(bp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current - 1, bp->time_bin); + + dt = cosmology_get_delta_time(e->cosmology, ti_begin, + ti_begin + ti_step); + } else { + dt = get_timestep(bp->time_bin, e->time_base); + } + + /* Compute variables required for the feedback loop */ + black_holes_prepare_feedback(bp, e->black_holes_properties, + e->physical_constants, e->cosmology, + dt); + + /* Reset quantities computed by the feedback loop */ + black_holes_reset_feedback(bp); + + /* Ok, we are done with this particle */ + continue; + } + + /* Normal case: Use Newton-Raphson to get a better value of h */ + + /* Avoid floating point exception from f_prime = 0 */ + h_new = h_old - f / (f_prime + FLT_MIN); + + /* Be verbose about the particles that struggle to converge */ + if (num_reruns > max_smoothing_iter - 10) { + + message( + "Smoothing length convergence problem: iter=%d p->id=%lld " + "h_init=%12.8e h_old=%12.8e h_new=%12.8e f=%f f_prime=%f " + "n_sum=%12.8e n_target=%12.8e left=%12.8e right=%12.8e", + num_reruns, bp->id, h_init, h_old, h_new, f, f_prime, n_sum, + n_target, left[i], right[i]); + } + + /* Safety check: truncate to the range [ h_old/2 , 2h_old ]. */ + h_new = min(h_new, 2.f * h_old); + h_new = max(h_new, 0.5f * h_old); + + /* Verify that we are actually progrssing towards the answer */ + h_new = max(h_new, left[i]); + h_new = min(h_new, right[i]); + } + + /* Check whether the particle has an inappropriate smoothing length */ + if (fabsf(h_new - h_old) > eps * h_old) { + + /* Ok, correct then */ + + /* Case where we have been oscillating around the solution */ + if ((h_new == left[i] && h_old == right[i]) || + (h_old == left[i] && h_new == right[i])) { + + /* Bissect the remaining interval */ + bp->h = pow_inv_dimension( + 0.5f * (pow_dimension(left[i]) + pow_dimension(right[i]))); + + } else { + + /* Normal case */ + bp->h = h_new; + } + + /* If below the absolute maximum, try again */ + if (bp->h < black_holes_h_max && bp->h > black_holes_h_min) { + + /* Flag for another round of fun */ + sid[redo] = sid[i]; + h_0[redo] = h_0[i]; + left[redo] = left[i]; + right[redo] = right[i]; + redo += 1; + + /* Re-initialise everything */ + black_holes_init_bpart(bp); + + /* Off we go ! */ + continue; + + } else if (bp->h <= black_holes_h_min) { + + /* Ok, this particle is a lost cause... */ + bp->h = black_holes_h_min; + + } else if (bp->h >= black_holes_h_max) { + + /* Ok, this particle is a lost cause... */ + bp->h = black_holes_h_max; + + /* Do some damage control if no neighbours at all were found */ + if (has_no_neighbours) { + black_holes_bpart_has_no_neighbours(bp, cosmo); + } + + } else { + error( + "Fundamental problem with the smoothing length iteration " + "logic."); + } + } + + /* We now have a particle whose smoothing length has converged */ + + /* Check if h_max has increased */ + h_max = max(h_max, bp->h); + + /* Get particle time-step */ + double dt; + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(bp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current - 1, bp->time_bin); + + dt = cosmology_get_delta_time(e->cosmology, ti_begin, + ti_begin + ti_step); + } else { + dt = get_timestep(bp->time_bin, e->time_base); + } + + /* Compute variables required for the feedback loop */ + black_holes_prepare_feedback(bp, e->black_holes_properties, + e->physical_constants, e->cosmology, dt); + + /* Reset quantities computed by the feedback loop */ + black_holes_reset_feedback(bp); + } + + /* We now need to treat the particles whose smoothing length had not + * converged again */ + + /* Re-set the counter for the next loop (potentially). */ + bcount = redo; + if (bcount > 0) { + + /* Climb up the cell hierarchy. */ + for (struct cell *finger = c; finger != NULL; finger = finger->parent) { + + /* Run through this cell's density interactions. */ + for (struct link *l = finger->black_holes.density; l != NULL; + l = l->next) { + +#ifdef SWIFT_DEBUG_CHECKS + if (l->t->ti_run < r->e->ti_current) + error("Density task should have been run."); +#endif + + /* Self-interaction? */ + if (l->t->type == task_type_self) + runner_doself_subset_branch_bh_density(r, finger, bparts, sid, + bcount); + + /* Otherwise, pair interaction? */ + else if (l->t->type == task_type_pair) { + + /* Left or right? */ + if (l->t->ci == finger) + runner_dopair_subset_branch_bh_density(r, finger, bparts, sid, + bcount, l->t->cj); + else + runner_dopair_subset_branch_bh_density(r, finger, bparts, sid, + bcount, l->t->ci); + } + + /* Otherwise, sub-self interaction? */ + else if (l->t->type == task_type_sub_self) + runner_dosub_subset_bh_density(r, finger, bparts, sid, bcount, + NULL, 1); + + /* Otherwise, sub-pair interaction? */ + else if (l->t->type == task_type_sub_pair) { + + /* Left or right? */ + if (l->t->ci == finger) + runner_dosub_subset_bh_density(r, finger, bparts, sid, bcount, + l->t->cj, 1); + else + runner_dosub_subset_bh_density(r, finger, bparts, sid, bcount, + l->t->ci, 1); + } + } + } + } + } + + if (bcount) { + error("Smoothing length failed to converge on %i particles.", bcount); + } + + /* Be clean */ + free(left); + free(right); + free(sid); + free(h_0); + } + + /* Update h_max */ + c->black_holes.h_max = h_max; + + /* The ghost may not always be at the top level. + * Therefore we need to update h_max between the super- and top-levels */ + if (c->black_holes.ghost) { + for (struct cell *tmp = c->parent; tmp != NULL; tmp = tmp->parent) { + atomic_max_d(&tmp->black_holes.h_max, h_max); + } + } + + if (timer) TIMER_TOC(timer_do_black_holes_ghost); +} + /** * @brief Calculate gravity acceleration from external potential * @@ -1999,6 +2361,35 @@ static void runner_do_unskip_stars(struct cell *c, struct engine *e) { if (forcerebuild) atomic_inc(&e->forcerebuild); } +/** + * @brief Unskip any black hole tasks associated with active cells. + * + * @param c The cell. + * @param e The engine. + */ +static void runner_do_unskip_black_holes(struct cell *c, struct engine *e) { + + /* Ignore empty cells. */ + if (c->black_holes.count == 0) return; + + /* Skip inactive cells. */ + if (!cell_is_active_black_holes(c, e)) return; + + /* Recurse */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *cp = c->progeny[k]; + runner_do_unskip_black_holes(cp, e); + } + } + } + + /* Unskip any active tasks. */ + const int forcerebuild = cell_unskip_black_holes_tasks(c, &e->sched); + if (forcerebuild) atomic_inc(&e->forcerebuild); +} + /** * @brief Unskip any gravity tasks associated with active cells. * @@ -2056,6 +2447,10 @@ void runner_do_unskip_mapper(void *map_data, int num_elements, /* Stars tasks */ if (e->policy & engine_policy_stars) runner_do_unskip_stars(c, e); + + /* Black hole tasks */ + if (e->policy & engine_policy_black_holes) + runner_do_unskip_black_holes(c, e); } } } @@ -3450,6 +3845,92 @@ void runner_do_recv_spart(struct runner *r, struct cell *c, int clear_sorts, #endif } +/** + * @brief Construct the cell properties from the received #bpart. + * + * Note that we do not need to clear the sorts since we do not sort + * the black holes. + * + * @param r The runner thread. + * @param c The cell. + * @param clear_sorts Should we clear the sort flag and hence trigger a sort ? + * @param timer Are we timing this ? + */ +void runner_do_recv_bpart(struct runner *r, struct cell *c, int clear_sorts, + int timer) { + +#ifdef WITH_MPI + + struct bpart *restrict bparts = c->black_holes.parts; + const size_t nr_bparts = c->black_holes.count; + const integertime_t ti_current = r->e->ti_current; + + TIMER_TIC; + + integertime_t ti_black_holes_end_min = max_nr_timesteps; + integertime_t ti_black_holes_end_max = 0; + timebin_t time_bin_min = num_time_bins; + timebin_t time_bin_max = 0; + float h_max = 0.f; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) error("Updating a local cell!"); +#endif + + /* If this cell is a leaf, collect the particle data. */ + if (!c->split) { + + /* Collect everything... */ + for (size_t k = 0; k < nr_bparts; k++) { +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + bparts[k].num_ngb_force = 0; +#endif + if (bparts[k].time_bin == time_bin_inhibited) continue; + time_bin_min = min(time_bin_min, bparts[k].time_bin); + time_bin_max = max(time_bin_max, bparts[k].time_bin); + h_max = max(h_max, bparts[k].h); + } + + /* Convert into a time */ + ti_black_holes_end_min = get_integer_time_end(ti_current, time_bin_min); + ti_black_holes_end_max = get_integer_time_end(ti_current, time_bin_max); + } + + /* Otherwise, recurse and collect. */ + else { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL && c->progeny[k]->black_holes.count > 0) { + runner_do_recv_bpart(r, c->progeny[k], clear_sorts, 0); + ti_black_holes_end_min = + min(ti_black_holes_end_min, c->progeny[k]->black_holes.ti_end_min); + ti_black_holes_end_max = + max(ti_black_holes_end_max, c->progeny[k]->grav.ti_end_max); + h_max = max(h_max, c->progeny[k]->black_holes.h_max); + } + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_black_holes_end_min < ti_current) + error( + "Received a cell at an incorrect time c->ti_end_min=%lld, " + "e->ti_current=%lld.", + ti_black_holes_end_min, ti_current); +#endif + + /* ... and store. */ + // c->grav.ti_end_min = ti_gravity_end_min; + // c->grav.ti_end_max = ti_gravity_end_max; + c->black_holes.ti_old_part = ti_current; + c->black_holes.h_max = h_max; + + if (timer) TIMER_TOC(timer_dorecv_bpart); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + /** * @brief The #runner main thread routine. * @@ -3532,6 +4013,10 @@ void *runner_main(void *data) { runner_doself_branch_stars_density(r, ci); else if (t->subtype == task_subtype_stars_feedback) runner_doself_branch_stars_feedback(r, ci); + else if (t->subtype == task_subtype_bh_density) + runner_doself_branch_bh_density(r, ci); + else if (t->subtype == task_subtype_bh_feedback) + runner_doself_branch_bh_feedback(r, ci); else error("Unknown/invalid task subtype (%d).", t->subtype); break; @@ -3553,6 +4038,10 @@ void *runner_main(void *data) { runner_dopair_branch_stars_density(r, ci, cj); else if (t->subtype == task_subtype_stars_feedback) runner_dopair_branch_stars_feedback(r, ci, cj); + else if (t->subtype == task_subtype_bh_density) + runner_dopair_branch_bh_density(r, ci, cj); + else if (t->subtype == task_subtype_bh_feedback) + runner_dopair_branch_bh_feedback(r, ci, cj); else error("Unknown/invalid task subtype (%d).", t->subtype); break; @@ -3572,6 +4061,10 @@ void *runner_main(void *data) { runner_dosub_self_stars_density(r, ci, 1); else if (t->subtype == task_subtype_stars_feedback) runner_dosub_self_stars_feedback(r, ci, 1); + else if (t->subtype == task_subtype_bh_density) + runner_dosub_self_bh_density(r, ci, 1); + else if (t->subtype == task_subtype_bh_feedback) + runner_dosub_self_bh_feedback(r, ci, 1); else error("Unknown/invalid task subtype (%d).", t->subtype); break; @@ -3591,6 +4084,10 @@ void *runner_main(void *data) { runner_dosub_pair_stars_density(r, ci, cj, 1); else if (t->subtype == task_subtype_stars_feedback) runner_dosub_pair_stars_feedback(r, ci, cj, 1); + else if (t->subtype == task_subtype_bh_density) + runner_dosub_pair_bh_density(r, ci, cj, 1); + else if (t->subtype == task_subtype_bh_feedback) + runner_dosub_pair_bh_feedback(r, ci, cj, 1); else error("Unknown/invalid task subtype (%d).", t->subtype); break; @@ -3625,6 +4122,9 @@ void *runner_main(void *data) { case task_type_stars_ghost: runner_do_stars_ghost(r, ci, 1); break; + case task_type_bh_ghost: + runner_do_black_holes_ghost(r, ci, 1); + break; case task_type_drift_part: runner_do_drift_part(r, ci, 1); break; @@ -3699,6 +4199,8 @@ void *runner_main(void *data) { runner_do_recv_gpart(r, ci, 1); } else if (t->subtype == task_subtype_spart) { runner_do_recv_spart(r, ci, 1, 1); + } else if (t->subtype == task_subtype_bpart) { + runner_do_recv_bpart(r, ci, 1, 1); } else if (t->subtype == task_subtype_multipole) { cell_unpack_multipoles(ci, (struct gravity_tensors *)t->buff); free(t->buff); diff --git a/src/runner_doiact_black_holes.h b/src/runner_doiact_black_holes.h new file mode 100644 index 0000000000000000000000000000000000000000..efd2d02b3aca3c530b4e1af13e38328368c99f7c --- /dev/null +++ b/src/runner_doiact_black_holes.h @@ -0,0 +1,765 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Before including this file, define FUNCTION, which is the + name of the interaction function. This creates the interaction functions + runner_dopair_FUNCTION, runner_doself_FUNCTION and runner_dosub_FUNCTION + calling the pairwise interaction function runner_iact_FUNCTION. */ + +#define PASTE(x, y) x##_##y + +#define _DOSELF1_BH(f) PASTE(runner_doself_bh, f) +#define DOSELF1_BH _DOSELF1_BH(FUNCTION) + +#define _DO_SYM_PAIR1_BH(f) PASTE(runner_do_sym_pair_bh, f) +#define DO_SYM_PAIR1_BH _DO_SYM_PAIR1_BH(FUNCTION) + +#define _DO_NONSYM_PAIR1_BH_NAIVE(f) PASTE(runner_do_nonsym_pair_bh_naive, f) +#define DO_NONSYM_PAIR1_BH_NAIVE _DO_NONSYM_PAIR1_BH_NAIVE(FUNCTION) + +#define _DOPAIR1_BH_NAIVE(f) PASTE(runner_dopair_bh_naive, f) +#define DOPAIR1_BH_NAIVE _DOPAIR1_BH_NAIVE(FUNCTION) + +#define _DOPAIR1_SUBSET_BH(f) PASTE(runner_dopair_subset_bh, f) +#define DOPAIR1_SUBSET_BH _DOPAIR1_SUBSET_BH(FUNCTION) + +#define _DOPAIR1_SUBSET_BH_NAIVE(f) PASTE(runner_dopair_subset_bh_naive, f) +#define DOPAIR1_SUBSET_BH_NAIVE _DOPAIR1_SUBSET_BH_NAIVE(FUNCTION) + +#define _DOSELF1_SUBSET_BH(f) PASTE(runner_doself_subset_bh, f) +#define DOSELF1_SUBSET_BH _DOSELF1_SUBSET_BH(FUNCTION) + +#define _DOSELF1_SUBSET_BRANCH_BH(f) PASTE(runner_doself_subset_branch_bh, f) +#define DOSELF1_SUBSET_BRANCH_BH _DOSELF1_SUBSET_BRANCH_BH(FUNCTION) + +#define _DOPAIR1_SUBSET_BRANCH_BH(f) PASTE(runner_dopair_subset_branch_bh, f) +#define DOPAIR1_SUBSET_BRANCH_BH _DOPAIR1_SUBSET_BRANCH_BH(FUNCTION) + +#define _DOSUB_SUBSET_BH(f) PASTE(runner_dosub_subset_bh, f) +#define DOSUB_SUBSET_BH _DOSUB_SUBSET_BH(FUNCTION) + +#define _DOSELF1_BRANCH_BH(f) PASTE(runner_doself_branch_bh, f) +#define DOSELF1_BRANCH_BH _DOSELF1_BRANCH_BH(FUNCTION) + +#define _DOPAIR1_BRANCH_BH(f) PASTE(runner_dopair_branch_bh, f) +#define DOPAIR1_BRANCH_BH _DOPAIR1_BRANCH_BH(FUNCTION) + +#define _DOSUB_PAIR1_BH(f) PASTE(runner_dosub_pair_bh, f) +#define DOSUB_PAIR1_BH _DOSUB_PAIR1_BH(FUNCTION) + +#define _DOSUB_SELF1_BH(f) PASTE(runner_dosub_self_bh, f) +#define DOSUB_SELF1_BH _DOSUB_SELF1_BH(FUNCTION) + +#define _TIMER_DOSELF_BH(f) PASTE(timer_doself_bh, f) +#define TIMER_DOSELF_BH _TIMER_DOSELF_BH(FUNCTION) + +#define _TIMER_DOPAIR_BH(f) PASTE(timer_dopair_bh, f) +#define TIMER_DOPAIR_BH _TIMER_DOPAIR_BH(FUNCTION) + +#define _TIMER_DOSUB_SELF_BH(f) PASTE(timer_dosub_self_bh, f) +#define TIMER_DOSUB_SELF_BH _TIMER_DOSUB_SELF_BH(FUNCTION) + +#define _TIMER_DOSUB_PAIR_BH(f) PASTE(timer_dosub_pair_bh, f) +#define TIMER_DOSUB_PAIR_BH _TIMER_DOSUB_PAIR_BH(FUNCTION) + +#define _IACT_BH(f) PASTE(runner_iact_nonsym_bh, f) +#define IACT_BH _IACT_BH(FUNCTION) + +/** + * @brief Calculate the number density of #part around the #bpart + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void DOSELF1_BH(struct runner *r, struct cell *c, int timer) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != engine_rank) error("Should be run on a different node"); +#endif + + TIMER_TIC; + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + /* Anything to do here? */ + if (c->hydro.count == 0 || c->black_holes.count == 0) return; + if (!cell_is_active_black_holes(c, e)) return; + + const int bcount = c->black_holes.count; + const int count = c->hydro.count; + struct bpart *restrict bparts = c->black_holes.parts; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + + /* Loop over the bparts in ci. */ + for (int sid = 0; sid < bcount; sid++) { + + /* Get a hold of the ith bpart in ci. */ + struct bpart *restrict si = &bparts[sid]; + + /* Skip inactive particles */ + if (!bpart_is_active(si, e)) continue; + + const float hi = si->h; + const float hig2 = hi * hi * kernel_gamma2; + const float six[3] = {(float)(si->x[0] - c->loc[0]), + (float)(si->x[1] - c->loc[1]), + (float)(si->x[2] - c->loc[2])}; + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts[pjd]; + struct xpart *restrict xpj = &xparts[pjd]; + const float hj = pj->h; + + /* Early abort? */ + if (part_is_inhibited(pj, e)) continue; + + /* Compute the pairwise distance. */ + const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), + (float)(pj->x[1] - c->loc[1]), + (float)(pj->x[2] - c->loc[2])}; + float dx[3] = {six[0] - pjx[0], six[1] - pjx[1], six[2] - pjx[2]}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles have been drifted to the current time */ + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + if (r2 < hig2) { + IACT_BH(r2, dx, hi, hj, si, pj, xpj, cosmo, ti_current); + } + } /* loop over the parts in ci. */ + } /* loop over the bparts in ci. */ + + TIMER_TOC(TIMER_DOSELF_BH); +} + +/** + * @brief Calculate the number density of cj #part around the ci #bpart + * + * @param r runner task + * @param ci The first #cell + * @param cj The second #cell + */ +void DO_NONSYM_PAIR1_BH_NAIVE(struct runner *r, struct cell *restrict ci, + struct cell *restrict cj) { + +#ifdef SWIFT_DEBUG_CHECKS +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + if (ci->nodeID != engine_rank) error("Should be run on a different node"); +#else + if (cj->nodeID != engine_rank) error("Should be run on a different node"); +#endif +#endif + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + /* Anything to do here? */ + if (cj->hydro.count == 0 || ci->black_holes.count == 0) return; + if (!cell_is_active_black_holes(ci, e)) return; + + const int bcount_i = ci->black_holes.count; + const int count_j = cj->hydro.count; + struct bpart *restrict bparts_i = ci->black_holes.parts; + struct part *restrict parts_j = cj->hydro.parts; + struct xpart *restrict xparts_j = cj->hydro.xparts; + + /* Get the relative distance between the pairs, wrapping. */ + double shift[3] = {0.0, 0.0, 0.0}; + for (int k = 0; k < 3; k++) { + if (cj->loc[k] - ci->loc[k] < -e->s->dim[k] / 2) + shift[k] = e->s->dim[k]; + else if (cj->loc[k] - ci->loc[k] > e->s->dim[k] / 2) + shift[k] = -e->s->dim[k]; + } + + /* Loop over the bparts in ci. */ + for (int sid = 0; sid < bcount_i; sid++) { + + /* Get a hold of the ith bpart in ci. */ + struct bpart *restrict si = &bparts_i[sid]; + + /* Skip inactive particles */ + if (!bpart_is_active(si, e)) continue; + + const float hi = si->h; + const float hig2 = hi * hi * kernel_gamma2; + const float six[3] = {(float)(si->x[0] - (cj->loc[0] + shift[0])), + (float)(si->x[1] - (cj->loc[1] + shift[1])), + (float)(si->x[2] - (cj->loc[2] + shift[2]))}; + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_j[pjd]; + struct xpart *restrict xpj = &xparts_j[pjd]; + const float hj = pj->h; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + + /* Compute the pairwise distance. */ + const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]), + (float)(pj->x[1] - cj->loc[1]), + (float)(pj->x[2] - cj->loc[2])}; + float dx[3] = {six[0] - pjx[0], six[1] - pjx[1], six[2] - pjx[2]}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles have been drifted to the current time */ + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + if (r2 < hig2) { + IACT_BH(r2, dx, hi, hj, si, pj, xpj, cosmo, ti_current); + } + } /* loop over the parts in cj. */ + } /* loop over the parts in ci. */ +} + +void DOPAIR1_BH_NAIVE(struct runner *r, struct cell *restrict ci, + struct cell *restrict cj, int timer) { + + TIMER_TIC; + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_bh = ci->nodeID == r->e->nodeID; + const int do_cj_bh = cj->nodeID == r->e->nodeID; +#else + /* here we are updating the hydro -> switch ci, cj */ + const int do_ci_bh = cj->nodeID == r->e->nodeID; + const int do_cj_bh = ci->nodeID == r->e->nodeID; +#endif + if (do_ci_bh && ci->black_holes.count != 0 && cj->hydro.count != 0) + DO_NONSYM_PAIR1_BH_NAIVE(r, ci, cj); + if (do_cj_bh && cj->black_holes.count != 0 && ci->hydro.count != 0) + DO_NONSYM_PAIR1_BH_NAIVE(r, cj, ci); + + TIMER_TOC(TIMER_DOPAIR_BH); +} + +/** + * @brief Compute the interactions between a cell pair, but only for the + * given indices in ci. + * + * Version using a brute-force algorithm. + * + * @param r The #runner. + * @param ci The first #cell. + * @param bparts_i The #bpart to interact with @c cj. + * @param ind The list of indices of particles in @c ci to interact with. + * @param bcount The number of particles in @c ind. + * @param cj The second #cell. + * @param shift The shift vector to apply to the particles in ci. + */ +void DOPAIR1_SUBSET_BH_NAIVE(struct runner *r, struct cell *restrict ci, + struct bpart *restrict bparts_i, int *restrict ind, + const int bcount, struct cell *restrict cj, + const double *shift) { + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != engine_rank) error("Should be run on a different node"); +#endif + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + const int count_j = cj->hydro.count; + struct part *restrict parts_j = cj->hydro.parts; + struct xpart *restrict xparts_j = cj->hydro.xparts; + + /* Early abort? */ + if (count_j == 0) return; + + /* Loop over the parts_i. */ + for (int pid = 0; pid < bcount; pid++) { + + /* Get a hold of the ith part in ci. */ + struct bpart *restrict bpi = &bparts_i[ind[pid]]; + + const double pix = bpi->x[0] - (shift[0]); + const double piy = bpi->x[1] - (shift[1]); + const double piz = bpi->x[2] - (shift[2]); + const float hi = bpi->h; + const float hig2 = hi * hi * kernel_gamma2; + +#ifdef SWIFT_DEBUG_CHECKS + if (!bpart_is_active(bpi, e)) + error("Trying to correct smoothing length of inactive particle !"); +#endif + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_j[pjd]; + struct xpart *restrict xpj = &xparts_j[pjd]; + + /* Skip inhibited particles */ + if (part_is_inhibited(pj, e)) continue; + + const double pjx = pj->x[0]; + const double pjy = pj->x[1]; + const double pjz = pj->x[2]; + const float hj = pj->h; + + /* Compute the pairwise distance. */ + float dx[3] = {(float)(pix - pjx), (float)(piy - pjy), + (float)(piz - pjz)}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles have been drifted to the current time */ + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + /* Hit or miss? */ + if (r2 < hig2) { + IACT_BH(r2, dx, hi, hj, bpi, pj, xpj, cosmo, ti_current); + } + } /* loop over the parts in cj. */ + } /* loop over the parts in ci. */ +} + +/** + * @brief Compute the interactions between a cell pair, but only for the + * given indices in ci. + * + * @param r The #runner. + * @param ci The first #cell. + * @param bparts The #bpart to interact. + * @param ind The list of indices of particles in @c ci to interact with. + * @param bcount The number of particles in @c ind. + */ +void DOSELF1_SUBSET_BH(struct runner *r, struct cell *restrict ci, + struct bpart *restrict bparts, int *restrict ind, + const int bcount) { + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != engine_rank) error("Should be run on a different node"); +#endif + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + const int count_i = ci->hydro.count; + struct part *restrict parts_j = ci->hydro.parts; + struct xpart *restrict xparts_j = ci->hydro.xparts; + + /* Early abort? */ + if (count_i == 0) return; + + /* Loop over the parts in ci. */ + for (int bpid = 0; bpid < bcount; bpid++) { + + /* Get a hold of the ith part in ci. */ + struct bpart *bpi = &bparts[ind[bpid]]; + const float bpix[3] = {(float)(bpi->x[0] - ci->loc[0]), + (float)(bpi->x[1] - ci->loc[1]), + (float)(bpi->x[2] - ci->loc[2])}; + const float hi = bpi->h; + const float hig2 = hi * hi * kernel_gamma2; + +#ifdef SWIFT_DEBUG_CHECKS + if (!bpart_is_active(bpi, e)) + error("Inactive particle in subset function!"); +#endif + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_i; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_j[pjd]; + struct xpart *restrict xpj = &xparts_j[pjd]; + + /* Early abort? */ + if (part_is_inhibited(pj, e)) continue; + + /* Compute the pairwise distance. */ + const float pjx[3] = {(float)(pj->x[0] - ci->loc[0]), + (float)(pj->x[1] - ci->loc[1]), + (float)(pj->x[2] - ci->loc[2])}; + float dx[3] = {bpix[0] - pjx[0], bpix[1] - pjx[1], bpix[2] - pjx[2]}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles have been drifted to the current time */ + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + /* Hit or miss? */ + if (r2 < hig2) { + IACT_BH(r2, dx, hi, pj->h, bpi, pj, xpj, cosmo, ti_current); + } + } /* loop over the parts in cj. */ + } /* loop over the parts in ci. */ +} + +/** + * @brief Determine which version of DOSELF1_SUBSET_BH needs to be called + * depending on the optimisation level. + * + * @param r The #runner. + * @param ci The first #cell. + * @param bparts The #bpart to interact. + * @param ind The list of indices of particles in @c ci to interact with. + * @param bcount The number of particles in @c ind. + */ +void DOSELF1_SUBSET_BRANCH_BH(struct runner *r, struct cell *restrict ci, + struct bpart *restrict bparts, int *restrict ind, + const int bcount) { + + DOSELF1_SUBSET_BH(r, ci, bparts, ind, bcount); +} + +/** + * @brief Determine which version of DOPAIR1_SUBSET_BH needs to be called + * depending on the orientation of the cells or whether DOPAIR1_SUBSET_BH + * needs to be called at all. + * + * @param r The #runner. + * @param ci The first #cell. + * @param bparts_i The #bpart to interact with @c cj. + * @param ind The list of indices of particles in @c ci to interact with. + * @param bcount The number of particles in @c ind. + * @param cj The second #cell. + */ +void DOPAIR1_SUBSET_BRANCH_BH(struct runner *r, struct cell *restrict ci, + struct bpart *restrict bparts_i, + int *restrict ind, int const bcount, + struct cell *restrict cj) { + + const struct engine *e = r->e; + + /* Anything to do here? */ + if (cj->hydro.count == 0) return; + + /* Get the relative distance between the pairs, wrapping. */ + double shift[3] = {0.0, 0.0, 0.0}; + for (int k = 0; k < 3; k++) { + if (cj->loc[k] - ci->loc[k] < -e->s->dim[k] / 2) + shift[k] = e->s->dim[k]; + else if (cj->loc[k] - ci->loc[k] > e->s->dim[k] / 2) + shift[k] = -e->s->dim[k]; + } + + DOPAIR1_SUBSET_BH_NAIVE(r, ci, bparts_i, ind, bcount, cj, shift); +} + +void DOSUB_SUBSET_BH(struct runner *r, struct cell *ci, struct bpart *bparts, + int *ind, const int bcount, struct cell *cj, + int gettimer) { + + const struct engine *e = r->e; + struct space *s = e->s; + + /* Should we even bother? */ + if (!cell_is_active_black_holes(ci, e) && + (cj == NULL || !cell_is_active_black_holes(cj, e))) + return; + + /* Find out in which sub-cell of ci the parts are. */ + struct cell *sub = NULL; + if (ci->split) { + for (int k = 0; k < 8; k++) { + if (ci->progeny[k] != NULL) { + if (&bparts[ind[0]] >= &ci->progeny[k]->black_holes.parts[0] && + &bparts[ind[0]] < + &ci->progeny[k] + ->black_holes.parts[ci->progeny[k]->black_holes.count]) { + sub = ci->progeny[k]; + break; + } + } + } + } + + /* Is this a single cell? */ + if (cj == NULL) { + + /* Recurse? */ + if (cell_can_recurse_in_self_black_holes_task(ci)) { + + /* Loop over all progeny. */ + DOSUB_SUBSET_BH(r, sub, bparts, ind, bcount, NULL, 0); + for (int j = 0; j < 8; j++) + if (ci->progeny[j] != sub && ci->progeny[j] != NULL) + DOSUB_SUBSET_BH(r, sub, bparts, ind, bcount, ci->progeny[j], 0); + + } + + /* Otherwise, compute self-interaction. */ + else + DOSELF1_SUBSET_BRANCH_BH(r, ci, bparts, ind, bcount); + } /* self-interaction. */ + + /* Otherwise, it's a pair interaction. */ + else { + + /* Recurse? */ + if (cell_can_recurse_in_pair_black_holes_task(ci, cj) && + cell_can_recurse_in_pair_black_holes_task(cj, ci)) { + + /* Get the type of pair and flip ci/cj if needed. */ + double shift[3] = {0.0, 0.0, 0.0}; + const int sid = space_getsid(s, &ci, &cj, shift); + + struct cell_split_pair *csp = &cell_split_pairs[sid]; + for (int k = 0; k < csp->count; k++) { + const int pid = csp->pairs[k].pid; + const int pjd = csp->pairs[k].pjd; + if (ci->progeny[pid] == sub && cj->progeny[pjd] != NULL) + DOSUB_SUBSET_BH(r, ci->progeny[pid], bparts, ind, bcount, + cj->progeny[pjd], 0); + if (ci->progeny[pid] != NULL && cj->progeny[pjd] == sub) + DOSUB_SUBSET_BH(r, cj->progeny[pjd], bparts, ind, bcount, + ci->progeny[pid], 0); + } + } + + /* Otherwise, compute the pair directly. */ + else if (cell_is_active_black_holes(ci, e) && cj->hydro.count > 0) { + + /* Do any of the cells need to be drifted first? */ + if (cell_is_active_black_holes(ci, e)) { + if (!cell_are_bpart_drifted(ci, e)) error("Cell should be drifted!"); + if (!cell_are_part_drifted(cj, e)) error("Cell should be drifted!"); + } + + DOPAIR1_SUBSET_BRANCH_BH(r, ci, bparts, ind, bcount, cj); + } + + } /* otherwise, pair interaction. */ +} + +/** + * @brief Determine which version of DOSELF1_BH needs to be called depending + * on the optimisation level. + * + * @param r #runner + * @param c #cell c + * + */ +void DOSELF1_BRANCH_BH(struct runner *r, struct cell *c) { + + const struct engine *restrict e = r->e; + + /* Anything to do here? */ + if (c->black_holes.count == 0) return; + + /* Anything to do here? */ + if (!cell_is_active_black_holes(c, e)) return; + + /* Did we mess up the recursion? */ + if (c->black_holes.h_max_old * kernel_gamma > c->dmin) + error("Cell smaller than smoothing length"); + + DOSELF1_BH(r, c, 1); +} + +/** + * @brief Determine which version of DOPAIR1_BH needs to be called depending + * on the orientation of the cells or whether DOPAIR1_BH needs to be called + * at all. + * + * @param r #runner + * @param ci #cell ci + * @param cj #cell cj + * + */ +void DOPAIR1_BRANCH_BH(struct runner *r, struct cell *ci, struct cell *cj) { + + const struct engine *restrict e = r->e; + + const int ci_active = cell_is_active_black_holes(ci, e); + const int cj_active = cell_is_active_black_holes(cj, e); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_bh = ci->nodeID == e->nodeID; + const int do_cj_bh = cj->nodeID == e->nodeID; +#else + /* here we are updating the hydro -> switch ci, cj */ + const int do_ci_bh = cj->nodeID == e->nodeID; + const int do_cj_bh = ci->nodeID == e->nodeID; +#endif + const int do_ci = (ci->black_holes.count != 0 && cj->hydro.count != 0 && + ci_active && do_ci_bh); + const int do_cj = (cj->black_holes.count != 0 && ci->hydro.count != 0 && + cj_active && do_cj_bh); + + /* Anything to do here? */ + if (!do_ci && !do_cj) return; + + /* Check that cells are drifted. */ + if (do_ci && + (!cell_are_bpart_drifted(ci, e) || !cell_are_part_drifted(cj, e))) + error("Interacting undrifted cells."); + + if (do_cj && + (!cell_are_part_drifted(ci, e) || !cell_are_bpart_drifted(cj, e))) + error("Interacting undrifted cells."); + + /* No sorted intreactions here -> use the naive ones */ + DOPAIR1_BH_NAIVE(r, ci, cj, 1); +} + +/** + * @brief Compute grouped sub-cell interactions for pairs + * + * @param r The #runner. + * @param ci The first #cell. + * @param cj The second #cell. + * @param gettimer Do we have a timer ? + * + * @todo Hard-code the sid on the recursive calls to avoid the + * redundant computations to find the sid on-the-fly. + */ +void DOSUB_PAIR1_BH(struct runner *r, struct cell *ci, struct cell *cj, + int gettimer) { + + TIMER_TIC; + + struct space *s = r->e->s; + const struct engine *e = r->e; + + /* Should we even bother? */ + const int should_do_ci = ci->black_holes.count != 0 && cj->hydro.count != 0 && + cell_is_active_black_holes(ci, e); + const int should_do_cj = cj->black_holes.count != 0 && ci->hydro.count != 0 && + cell_is_active_black_holes(cj, e); + if (!should_do_ci && !should_do_cj) return; + + /* Get the type of pair and flip ci/cj if needed. */ + double shift[3]; + const int sid = space_getsid(s, &ci, &cj, shift); + + /* Recurse? */ + if (cell_can_recurse_in_pair_black_holes_task(ci, cj) && + cell_can_recurse_in_pair_black_holes_task(cj, ci)) { + struct cell_split_pair *csp = &cell_split_pairs[sid]; + for (int k = 0; k < csp->count; k++) { + const int pid = csp->pairs[k].pid; + const int pjd = csp->pairs[k].pjd; + if (ci->progeny[pid] != NULL && cj->progeny[pjd] != NULL) + DOSUB_PAIR1_BH(r, ci->progeny[pid], cj->progeny[pjd], 0); + } + } + + /* Otherwise, compute the pair directly. */ + else { + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_bh = ci->nodeID == e->nodeID; + const int do_cj_bh = cj->nodeID == e->nodeID; +#else + /* here we are updating the hydro -> switch ci, cj */ + const int do_ci_bh = cj->nodeID == e->nodeID; + const int do_cj_bh = ci->nodeID == e->nodeID; +#endif + const int do_ci = ci->black_holes.count != 0 && cj->hydro.count != 0 && + cell_is_active_black_holes(ci, e) && do_ci_bh; + const int do_cj = cj->black_holes.count != 0 && ci->hydro.count != 0 && + cell_is_active_black_holes(cj, e) && do_cj_bh; + + if (do_ci) { + + /* Make sure both cells are drifted to the current timestep. */ + if (!cell_are_bpart_drifted(ci, e)) + error("Interacting undrifted cells (bparts)."); + + if (!cell_are_part_drifted(cj, e)) + error("Interacting undrifted cells (parts)."); + } + + if (do_cj) { + + /* Make sure both cells are drifted to the current timestep. */ + if (!cell_are_part_drifted(ci, e)) + error("Interacting undrifted cells (parts)."); + + if (!cell_are_bpart_drifted(cj, e)) + error("Interacting undrifted cells (bparts)."); + } + + if (do_ci || do_cj) DOPAIR1_BRANCH_BH(r, ci, cj); + } + + TIMER_TOC(TIMER_DOSUB_PAIR_BH); +} + +/** + * @brief Compute grouped sub-cell interactions for self tasks + * + * @param r The #runner. + * @param ci The first #cell. + * @param gettimer Do we have a timer ? + */ +void DOSUB_SELF1_BH(struct runner *r, struct cell *ci, int gettimer) { + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != engine_rank) + error("This function should not be called on foreign cells"); +#endif + + /* Should we even bother? */ + if (ci->hydro.count == 0 || ci->black_holes.count == 0 || + !cell_is_active_black_holes(ci, r->e)) + return; + + /* Recurse? */ + if (cell_can_recurse_in_self_black_holes_task(ci)) { + + /* Loop over all progeny. */ + for (int k = 0; k < 8; k++) + if (ci->progeny[k] != NULL) { + DOSUB_SELF1_BH(r, ci->progeny[k], 0); + for (int j = k + 1; j < 8; j++) + if (ci->progeny[j] != NULL) + DOSUB_PAIR1_BH(r, ci->progeny[k], ci->progeny[j], 0); + } + } + + /* Otherwise, compute self-interaction. */ + else { + + /* Drift the cell to the current timestep if needed. */ + if (!cell_are_bpart_drifted(ci, r->e)) error("Interacting undrifted cell."); + + DOSELF1_BRANCH_BH(r, ci); + } + + TIMER_TOC(TIMER_DOSUB_SELF_BH); +} diff --git a/src/scheduler.c b/src/scheduler.c index 6dba5f1593c8bf050a260ded5061ebc54fc63310..af01e12f66540a039d5c485c266ac8f92c5d5a8d 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -539,6 +539,8 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { access the engine... */ const int with_feedback = (s->space->e->policy & engine_policy_feedback); const int with_stars = (s->space->e->policy & engine_policy_stars); + const int with_black_holes = + (s->space->e->policy & engine_policy_black_holes); /* Iterate on this task until we're done with it. */ int redo = 1; @@ -549,14 +551,18 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { /* Is this a non-empty self-task? */ const int is_self = (t->type == task_type_self) && (t->ci != NULL) && - ((t->ci->hydro.count > 0) || (with_stars && t->ci->stars.count > 0)); + ((t->ci->hydro.count > 0) || (with_stars && t->ci->stars.count > 0) || + (with_black_holes && t->ci->black_holes.count > 0)); /* Is this a non-empty pair-task? */ - const int is_pair = - (t->type == task_type_pair) && (t->ci != NULL) && (t->cj != NULL) && - ((t->ci->hydro.count > 0) || - (with_feedback && t->ci->stars.count > 0)) && - ((t->cj->hydro.count > 0) || (with_feedback && t->cj->stars.count > 0)); + const int is_pair = (t->type == task_type_pair) && (t->ci != NULL) && + (t->cj != NULL) && + ((t->ci->hydro.count > 0) || + (with_feedback && t->ci->stars.count > 0) || + (with_black_holes && t->ci->black_holes.count > 0)) && + ((t->cj->hydro.count > 0) || + (with_feedback && t->cj->stars.count > 0) || + (with_black_holes && t->cj->black_holes.count > 0)); /* Empty task? */ if (!is_self && !is_pair) { @@ -1175,7 +1181,7 @@ void scheduler_reweight(struct scheduler *s, int verbose) { const float scount_i = (t->ci != NULL) ? t->ci->stars.count : 0.f; const float scount_j = (t->cj != NULL) ? t->cj->stars.count : 0.f; const float bcount_i = (t->ci != NULL) ? t->ci->black_holes.count : 0.f; - // const float bcount_j = (t->cj != NULL) ? t->cj->black_holes.count : 0.f; + const float bcount_j = (t->cj != NULL) ? t->cj->black_holes.count : 0.f; switch (t->type) { case task_type_sort: @@ -1195,7 +1201,13 @@ void scheduler_reweight(struct scheduler *s, int verbose) { cost = 1.f * wscale * gcount_i; else if (t->subtype == task_subtype_stars_density) cost = 1.f * wscale * scount_i * count_i; - else + else if (t->subtype == task_subtype_stars_feedback) + cost = 1.f * wscale * scount_i * count_i; + else if (t->subtype == task_subtype_bh_density) + cost = 1.f * wscale * bcount_i * count_i; + else if (t->subtype == task_subtype_bh_feedback) + cost = 1.f * wscale * bcount_i * count_i; + else // hydro loops cost = 1.f * (wscale * count_i) * count_i; break; @@ -1205,7 +1217,9 @@ void scheduler_reweight(struct scheduler *s, int verbose) { cost = 3.f * (wscale * gcount_i) * gcount_j; else cost = 2.f * (wscale * gcount_i) * gcount_j; - } else if (t->subtype == task_subtype_stars_density) { + + } else if (t->subtype == task_subtype_stars_density || + t->subtype == task_subtype_stars_feedback) { if (t->ci->nodeID != nodeID) cost = 3.f * wscale * count_i * scount_j * sid_scale[t->flags]; else if (t->cj->nodeID != nodeID) @@ -1213,7 +1227,18 @@ void scheduler_reweight(struct scheduler *s, int verbose) { else cost = 2.f * wscale * (scount_i * count_j + scount_j * count_i) * sid_scale[t->flags]; - } else { + + } else if (t->subtype == task_subtype_bh_density || + t->subtype == task_subtype_bh_feedback) { + if (t->ci->nodeID != nodeID) + cost = 3.f * wscale * count_i * bcount_j * sid_scale[t->flags]; + else if (t->cj->nodeID != nodeID) + cost = 3.f * wscale * bcount_i * count_j * sid_scale[t->flags]; + else + cost = 2.f * wscale * (bcount_i * count_j + bcount_j * count_i) * + sid_scale[t->flags]; + + } else { // hydro loops if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) cost = 3.f * (wscale * count_i) * count_j * sid_scale[t->flags]; else @@ -1225,7 +1250,8 @@ void scheduler_reweight(struct scheduler *s, int verbose) { #ifdef SWIFT_DEBUG_CHECKS if (t->flags < 0) error("Negative flag value!"); #endif - if (t->subtype == task_subtype_stars_density) { + if (t->subtype == task_subtype_stars_density || + t->subtype == task_subtype_stars_feedback) { if (t->ci->nodeID != nodeID) { cost = 3.f * (wscale * count_i) * scount_j * sid_scale[t->flags]; } else if (t->cj->nodeID != nodeID) { @@ -1235,7 +1261,18 @@ void scheduler_reweight(struct scheduler *s, int verbose) { sid_scale[t->flags]; } - } else { + } else if (t->subtype == task_subtype_bh_density || + t->subtype == task_subtype_bh_feedback) { + if (t->ci->nodeID != nodeID) { + cost = 3.f * (wscale * count_i) * bcount_j * sid_scale[t->flags]; + } else if (t->cj->nodeID != nodeID) { + cost = 3.f * (wscale * bcount_i) * count_j * sid_scale[t->flags]; + } else { + cost = 2.f * wscale * (bcount_i * count_j + bcount_j * count_i) * + sid_scale[t->flags]; + } + + } else { // hydro loops if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) { cost = 3.f * (wscale * count_i) * count_j * sid_scale[t->flags]; } else { @@ -1247,6 +1284,12 @@ void scheduler_reweight(struct scheduler *s, int verbose) { case task_type_sub_self: if (t->subtype == task_subtype_stars_density) { cost = 1.f * (wscale * scount_i) * count_i; + } else if (t->subtype == task_subtype_stars_feedback) { + cost = 1.f * (wscale * scount_i) * count_i; + } else if (t->subtype == task_subtype_bh_density) { + cost = 1.f * (wscale * bcount_i) * count_i; + } else if (t->subtype == task_subtype_bh_feedback) { + cost = 1.f * (wscale * bcount_i) * count_i; } else { cost = 1.f * (wscale * count_i) * count_i; } @@ -1260,6 +1303,9 @@ void scheduler_reweight(struct scheduler *s, int verbose) { case task_type_stars_ghost: if (t->ci == t->ci->hydro.super) cost = wscale * scount_i; break; + case task_type_bh_ghost: + if (t->ci == t->ci->hydro.super) cost = wscale * bcount_i; + break; case task_type_drift_part: cost = wscale * count_i; break; @@ -1535,6 +1581,10 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { err = MPI_Irecv(t->ci->stars.parts, t->ci->stars.count, spart_mpi_type, t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], &t->req); + } else if (t->subtype == task_subtype_bpart) { + err = MPI_Irecv(t->ci->black_holes.parts, t->ci->black_holes.count, + bpart_mpi_type, t->ci->nodeID, t->flags, + subtaskMPI_comms[t->subtype], &t->req); } else if (t->subtype == task_subtype_multipole) { t->buff = (struct gravity_tensors *)malloc( sizeof(struct gravity_tensors) * t->ci->mpi.pcell_size); @@ -1659,6 +1709,16 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { err = MPI_Issend(t->ci->stars.parts, t->ci->stars.count, spart_mpi_type, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], &t->req); + } else if (t->subtype == task_subtype_bpart) { + if ((t->ci->black_holes.count * sizeof(struct bpart)) > + s->mpi_message_limit) + err = MPI_Isend(t->ci->black_holes.parts, t->ci->black_holes.count, + bpart_mpi_type, t->cj->nodeID, t->flags, + subtaskMPI_comms[t->subtype], &t->req); + else + err = MPI_Issend(t->ci->black_holes.parts, t->ci->black_holes.count, + bpart_mpi_type, t->cj->nodeID, t->flags, + subtaskMPI_comms[t->subtype], &t->req); } else if (t->subtype == task_subtype_multipole) { t->buff = (struct gravity_tensors *)malloc( sizeof(struct gravity_tensors) * t->ci->mpi.pcell_size); diff --git a/src/space.c b/src/space.c index 562766d9793c33743167f8c84490c9edbea1e661..1712e512e5b65bfbe71231fc2cbaba20447a5a8e 100644 --- a/src/space.c +++ b/src/space.c @@ -235,6 +235,9 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->stars.ghost = NULL; c->stars.density = NULL; c->stars.feedback = NULL; + c->black_holes.ghost = NULL; + c->black_holes.density = NULL; + c->black_holes.feedback = NULL; c->kick1 = NULL; c->kick2 = NULL; c->timestep = NULL; @@ -245,6 +248,8 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->stars.stars_in = NULL; c->stars.stars_out = NULL; c->black_holes.drift = NULL; + c->black_holes.black_holes_in = NULL; + c->black_holes.black_holes_out = NULL; c->grav.drift = NULL; c->grav.drift_out = NULL; c->hydro.cooling = NULL; @@ -347,6 +352,7 @@ void space_regrid(struct space *s, int verbose) { const size_t nr_parts = s->nr_parts; const size_t nr_sparts = s->nr_sparts; + const size_t nr_bparts = s->nr_bparts; const ticks tic = getticks(); const integertime_t ti_current = (s->e != NULL) ? s->e->ti_current : 0; @@ -366,6 +372,9 @@ void space_regrid(struct space *s, int verbose) { if (c->stars.h_max > h_max) { h_max = c->stars.h_max; } + if (c->black_holes.h_max > h_max) { + h_max = c->black_holes.h_max; + } } /* Can we instead use all the top-level cells? */ @@ -378,6 +387,9 @@ void space_regrid(struct space *s, int verbose) { if (c->nodeID == engine_rank && c->stars.h_max > h_max) { h_max = c->stars.h_max; } + if (c->nodeID == engine_rank && c->black_holes.h_max > h_max) { + h_max = c->black_holes.h_max; + } } /* Last option: run through the particles */ @@ -388,6 +400,9 @@ void space_regrid(struct space *s, int verbose) { for (size_t k = 0; k < nr_sparts; k++) { if (s->sparts[k].h > h_max) h_max = s->sparts[k].h; } + for (size_t k = 0; k < nr_bparts; k++) { + if (s->bparts[k].h > h_max) h_max = s->bparts[k].h; + } } } @@ -555,6 +570,8 @@ void space_regrid(struct space *s, int verbose) { error("Failed to init spinlock for multipoles."); if (lock_init(&s->cells_top[k].stars.lock) != 0) error("Failed to init spinlock for stars."); + if (lock_init(&s->cells_top[k].black_holes.lock) != 0) + error("Failed to init spinlock for black holes."); if (lock_init(&s->cells_top[k].stars.star_formation_lock) != 0) error("Failed to init spinlock for star formation."); } @@ -584,6 +601,7 @@ void space_regrid(struct space *s, int verbose) { c->hydro.ti_old_part = ti_current; c->grav.ti_old_part = ti_current; c->stars.ti_old_part = ti_current; + c->black_holes.ti_old_part = ti_current; c->grav.ti_old_multipole = ti_current; #ifdef WITH_MPI c->mpi.tag = -1; @@ -3331,6 +3349,7 @@ void space_split_recursive(struct space *s, struct cell *c, /* Update the cell-wide properties */ h_max = max(h_max, cp->hydro.h_max); stars_h_max = max(stars_h_max, cp->stars.h_max); + black_holes_h_max = max(black_holes_h_max, cp->black_holes.h_max); ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min); ti_hydro_end_max = max(ti_hydro_end_max, cp->hydro.ti_end_max); ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max); @@ -3683,6 +3702,7 @@ void space_recycle(struct space *s, struct cell *c) { /* Clear the cell. */ if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->grav.plock) != 0 || lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0 || + lock_destroy(&c->black_holes.lock) != 0 || lock_destroy(&c->stars.star_formation_lock)) error("Failed to destroy spinlocks."); @@ -3733,6 +3753,7 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin, /* Clear the cell. */ if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->grav.plock) != 0 || lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0 || + lock_destroy(&c->black_holes.lock) != 0 || lock_destroy(&c->stars.star_formation_lock)) error("Failed to destroy spinlocks."); @@ -3833,6 +3854,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { lock_init(&cells[j]->grav.plock) != 0 || lock_init(&cells[j]->grav.mlock) != 0 || lock_init(&cells[j]->stars.lock) != 0 || + lock_init(&cells[j]->black_holes.lock) != 0 || lock_init(&cells[j]->stars.star_formation_lock) != 0) error("Failed to initialize cell spinlocks."); } @@ -4227,6 +4249,7 @@ void space_first_init_bparts_mapper(void *restrict map_data, int count, struct bpart *restrict bp = (struct bpart *)map_data; const struct space *restrict s = (struct space *)extra_data; const struct engine *e = s->e; + const struct black_holes_props *props = e->black_holes_properties; #ifdef SWIFT_DEBUG_CHECKS const ptrdiff_t delta = bp - s->bparts; @@ -4260,10 +4283,17 @@ void space_first_init_bparts_mapper(void *restrict map_data, int count, #endif } + /* Check that the smoothing lengths are non-zero */ + for (int k = 0; k < count; k++) { + if (bp[k].h <= 0.) + error("Invalid value of smoothing length for bpart %lld h=%e", bp[k].id, + bp[k].h); + } + /* Initialise the rest */ for (int k = 0; k < count; k++) { - black_holes_first_init_bpart(&bp[k]); + black_holes_first_init_bpart(&bp[k], props); #ifdef SWIFT_DEBUG_CHECKS if (bp[k].gpart && bp[k].gpart->id_or_neg_offset != -(k + delta)) diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h index 4d1013c0f6dd13552cb533aa942f47dd588a469c..aaec0197100ab84003180f77ceb2570b5d342cbe 100644 --- a/src/star_formation/EAGLE/star_formation.h +++ b/src/star_formation/EAGLE/star_formation.h @@ -201,7 +201,7 @@ INLINE static double EOS_pressure(const double n_H, * @param hydro_props The properties of the hydro scheme. * @param us The internal system of units. * @param cooling The cooling data struct. - * @param entropy_floor The entropy floor assumed in this run. + * @param entropy_floor_props The entropy floor assumed in this run. */ INLINE static int star_formation_is_star_forming( const struct part* restrict p, const struct xpart* restrict xp, @@ -376,6 +376,10 @@ INLINE static void star_formation_update_part_not_SFR( * @param starform the star formation law properties to use. * @param cosmo the cosmological parameters and properties. * @param with_cosmology if we run with cosmology. + * @param phys_const the physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cooling The cooling data struct. */ INLINE static void star_formation_copy_properties( const struct part* p, const struct xpart* xp, struct spart* sp, diff --git a/src/swift.h b/src/swift.h index cd7159e63d129e97f8aac5c8d426fd041bd298b0..562a1fd5f49e3599693940aafd4cb305743eed7e 100644 --- a/src/swift.h +++ b/src/swift.h @@ -25,6 +25,7 @@ /* Local headers. */ #include "active.h" #include "atomic.h" +#include "black_holes_properties.h" #include "cache.h" #include "cell.h" #include "chemistry.h" diff --git a/src/task.c b/src/task.c index 005bd229ab8c88cfff1d800025091db6991a91cb..ad287157b892391bace086ead1ada1981d027ce9 100644 --- a/src/task.c +++ b/src/task.c @@ -88,15 +88,19 @@ const char *taskID_names[task_type_count] = {"none", "stars_ghost_in", "stars_ghost", "stars_ghost_out", - "stars_sort"}; + "stars_sort", + "bh_in", + "bh_out", + "bh_ghost"}; /* Sub-task type names. */ const char *subtaskID_names[task_subtype_count] = { - "none", "density", "gradient", "force", - "limiter", "grav", "external_grav", "tend_part", - "tend_gpart", "tend_spart", "tend_bpart", "xv", - "rho", "gpart", "multipole", "spart", - "stars_density", "stars_feedback"}; + "none", "density", "gradient", "force", + "limiter", "grav", "external_grav", "tend_part", + "tend_gpart", "tend_spart", "tend_bpart", "xv", + "rho", "gpart", "multipole", "spart", + "stars_density", "stars_feedback", "bpart", "bh_density", + "bh_feedback"}; #ifdef WITH_MPI /* MPI communicators for the subtypes. */ @@ -165,6 +169,11 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( return task_action_spart; break; + case task_type_drift_bpart: + case task_type_bh_ghost: + return task_action_bpart; + break; + case task_type_self: case task_type_pair: case task_type_sub_self: @@ -183,6 +192,11 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( return task_action_all; break; + case task_subtype_bh_density: + case task_subtype_bh_feedback: + return task_action_all; + break; + case task_subtype_grav: case task_subtype_external_grav: return task_action_gpart; @@ -706,6 +720,12 @@ void task_get_group_name(int type, int subtype, char *cluster) { case task_subtype_stars_feedback: strcpy(cluster, "StarsFeedback"); break; + case task_subtype_bh_density: + strcpy(cluster, "BHDensity"); + break; + case task_subtype_bh_feedback: + strcpy(cluster, "BHFeedback"); + break; default: strcpy(cluster, "None"); break; diff --git a/src/task.h b/src/task.h index b62b2d531b5cf4ae98331281249199388ee0070c..33c15372bb3af30c74e36fb572a2f12b8d29e6d4 100644 --- a/src/task.h +++ b/src/task.h @@ -84,6 +84,9 @@ enum task_types { task_type_stars_ghost, task_type_stars_ghost_out, /* Implicit */ task_type_stars_sort, + task_type_bh_in, /* Implicit */ + task_type_bh_out, /* Implicit */ + task_type_bh_ghost, task_type_count } __attribute__((packed)); @@ -109,6 +112,9 @@ enum task_subtypes { task_subtype_spart, task_subtype_stars_density, task_subtype_stars_feedback, + task_subtype_bpart, + task_subtype_bh_density, + task_subtype_bh_feedback, task_subtype_count } __attribute__((packed)); @@ -120,6 +126,7 @@ enum task_actions { task_action_part, task_action_gpart, task_action_spart, + task_action_bpart, task_action_all, task_action_multipole, task_action_count diff --git a/src/units.c b/src/units.c index 1194735aafd80d51897e4c4cb3e4b0976478145a..55a1c9e1bdf62fce97b22189b4741be0fea2abb4 100644 --- a/src/units.c +++ b/src/units.c @@ -377,6 +377,7 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5], break; case UNIT_CONV_SFR: + case UNIT_CONV_MASS_PER_UNIT_TIME: baseUnitsExp[UNIT_MASS] = 1.f; baseUnitsExp[UNIT_TIME] = -1.f; break; diff --git a/src/units.h b/src/units.h index 62669425e52c4e39800330a4150259856d8fc0bb..cc87696ccfdeb40b7cf4f23ccfcd853f8c8c1e56 100644 --- a/src/units.h +++ b/src/units.h @@ -96,7 +96,8 @@ enum unit_conversion_factor { UNIT_CONV_VOLUME, UNIT_CONV_INV_VOLUME, UNIT_CONV_SFR, - UNIT_CONV_SSFR + UNIT_CONV_SSFR, + UNIT_CONV_MASS_PER_UNIT_TIME }; void units_init_cgs(struct unit_system*); diff --git a/tools/plot_task_dependencies.py b/tools/plot_task_dependencies.py index 14ba7c99f621c0c62c3c9cb8e8d3b0c78e491401..9a8b63ab315215c2973187a026fc1a5e182cd47b 100644 --- a/tools/plot_task_dependencies.py +++ b/tools/plot_task_dependencies.py @@ -136,6 +136,20 @@ def appendData(data): return data[0] +def taskIsBlackHoles(name): + """ + Does the task concern black holes? + + Parameters + ---------- + + name: str + Task name + """ + if "bh" in name or "bpart" in name: + return True + return False + def taskIsStars(name): """ Does the task concern stars? @@ -150,7 +164,6 @@ def taskIsStars(name): return True return False - def taskIsHydro(name): """ Does the task concern the hydro? @@ -163,7 +176,7 @@ def taskIsHydro(name): """ if "_part" in name: return True - if "density" in name and "stars" not in name: + if "density" in name and "stars" not in name and "bh" not in name: return True if "rho" in name: return True @@ -291,6 +304,9 @@ def writeTask(f, name, implicit, mpi, with_calls): if mpi: txt += "shape=diamond," + if taskIsBlackHoles(name): + txt += "color=forestgreen," + if taskIsStars(name): txt += "color=darkorange1," diff --git a/tools/task_plots/analyse_tasks.py b/tools/task_plots/analyse_tasks.py index e938d0610ab878efbd6463909bbf75fe2ec60bc2..798efdfdaa7fb982de34917488ab372b2f0d350a 100755 --- a/tools/task_plots/analyse_tasks.py +++ b/tools/task_plots/analyse_tasks.py @@ -77,6 +77,7 @@ TASKTYPES = [ "extra_ghost", "drift_part", "drift_spart", + "drift_bpart", "drift_gpart", "drift_gpart_out", "hydro_end_force", @@ -103,6 +104,9 @@ TASKTYPES = [ "stars_ghost", "stars_ghost_out", "stars_sort", + "bh_in", + "bh_out", + "bh_ghost", "count", ] @@ -117,6 +121,7 @@ SUBTYPES = [ "tend_part", "tend_gpart", "tend_spart", + "tend_bpart", "xv", "rho", "gpart", @@ -124,6 +129,9 @@ SUBTYPES = [ "spart", "stars_density", "stars_feedback", + "bpart", + "bh_density", + "bh_feedback", "count", ] diff --git a/tools/task_plots/plot_tasks.py b/tools/task_plots/plot_tasks.py index b692ee963784b9487f5e19ca7274c0ecaa9b7a89..a76fcbb3ac4092daafa2c2334a810dbada49c2a4 100755 --- a/tools/task_plots/plot_tasks.py +++ b/tools/task_plots/plot_tasks.py @@ -162,6 +162,7 @@ TASKTYPES = [ "extra_ghost", "drift_part", "drift_spart", + "drift_bpart", "drift_gpart", "drift_gpart_out", "hydro_end_force", @@ -188,6 +189,9 @@ TASKTYPES = [ "stars_ghost", "stars_ghost_out", "stars_sort", + "bh_in", + "bh_out", + "bh_ghost", "count", ] @@ -202,6 +206,7 @@ SUBTYPES = [ "tend_part", "tend_gpart", "tend_spart", + "tend_bpart", "xv", "rho", "gpart", @@ -209,6 +214,9 @@ SUBTYPES = [ "spart", "stars_density", "stars_feedback", + "bpart", + "bh_density", + "bh_feedback", "count", ] @@ -242,10 +250,14 @@ FULLTYPES = [ "send/tend_gpart", "recv/tend_spart", "send/tend_spart", + "recv/tend_bpart", + "send/tend_bpart", "recv/gpart", "send/gpart", "recv/spart", "send/spart", + "recv/bpart", + "send/bpart", "self/stars_density", "pair/stars_density", "sub_self/stars_density", @@ -254,6 +266,14 @@ FULLTYPES = [ "pair/stars_feedback", "sub_self/stars_feedback", "sub_pair/stars_feedback", + "self/bh_density", + "pair/bh_density", + "sub_self/bh_density", + "sub_pair/bh_density", + "self/bh_feedback", + "pair/bh_feedback", + "sub_self/bh_feedback", + "sub_pair/bh_feedback", ] # A number of colours for the various types. Recycled when there are