diff --git a/.gitignore b/.gitignore index 1fd274009591c7ac98d71e05d536c98d4c17c485..c29fa3e3a48e9846b5c7c422b746589cb740802d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,12 +30,17 @@ examples/fof_mpi examples/*/*/*.xmf examples/*/*/*.dat examples/*/*/*.png +examples/*/*/*.pdf examples/*/*/*.mp4 -examples/*/*/*.txt examples/*/*/*.rst examples/*/*/*.hdf5 examples/*/*/*.csv examples/*/*/*.dot +examples/*/*/cell_hierarchy.html +examples/*/*/energy.txt +examples/*/*/task_level.txt +examples/*/*/timesteps_*.txt +examples/*/*/SFR.txt examples/*/*/partition_fixed_costs.h examples/*/*/memuse_report-step*.dat examples/*/*/memuse_report-step*.log @@ -58,6 +63,7 @@ examples/Cooling/CoolingRates/cooling_rates examples/Cooling/CoolingRates/cooling_element_*.dat examples/Cooling/CoolingRates/cooling_output.dat examples/SubgridTests/StellarEvolution/StellarEvolutionSolution* +examples/SubgridTests/CosmologicalStellarEvolution/StellarEvolutionSolution* tests/testActivePair tests/testActivePair.sh @@ -112,8 +118,10 @@ tests/testFFT tests/testInteractions tests/testInteractions.sh tests/testSymmetry +tests/testHydroMPIrules tests/testMaths tests/testRandom +tests/testRandomSpacing tests/testThreadpool tests/testParser tests/testFeedback diff --git a/Makefile.am b/Makefile.am index c71cc8d00c797f0e2afc034cb1abfff7eba14c88..40ba64dcdd1c7270712288bce938ab56e918694d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,9 @@ SUBDIRS = src argparse examples doc tests tools if HAVEEAGLECOOLING SUBDIRS += examples/Cooling/CoolingRates endif +if HAVELOGGER +SUBDIRS += logger +endif # Non-standard files that should be part of the distribution. EXTRA_DIST = INSTALL.swift .clang-format format.sh diff --git a/README b/README index 5490ef8426c62529e6af81144e0701a409c8d4b7..8d722a66da5083889e0adfb5af51206509bef53d 100644 --- a/README +++ b/README @@ -6,7 +6,7 @@ /____/ |__/|__/___/_/ /_/ SPH With Inter-dependent Fine-grained Tasking - Version : 0.8.3 + Version : 0.8.4 Website: www.swiftsim.com Twitter: @SwiftSimulation @@ -71,6 +71,8 @@ Parameters: from all ranks. -y, --task-dumps=<int> Time-step frequency at which task analysis files and/or tasks are dumped. + --cell-dumps=<int> Time-step frequency at which cell graphs + are dumped. -Y, --threadpool-dumps=<int> Time-step frequency at which threadpool tasks are dumped. diff --git a/README.md b/README.md index b52f8595e2984f95f82f571db07a65631f7c3231..f91b03d3f6a9656e33adc3216a15ed41e7b971de 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Runtime parameters /____/ |__/|__/___/_/ /_/ SPH With Inter-dependent Fine-grained Tasking - Version : 0.8.3 + Version : 0.8.4 Website: www.swiftsim.com Twitter: @SwiftSimulation @@ -122,6 +122,8 @@ Parameters: from all ranks. -y, --task-dumps=<int> Time-step frequency at which task analysis files and/or tasks are dumped. + --cell-dumps=<int> Time-step frequency at which cell graphs + are dumped. -Y, --threadpool-dumps=<int> Time-step frequency at which threadpool tasks are dumped. diff --git a/configure.ac b/configure.ac index c715fb34baeb469e87977da779d82f48f564999e..8d189c1210abf48304ca39b0fc6450323091eb7e 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Init the project. -AC_INIT([SWIFT],[0.8.3],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) +AC_INIT([SWIFT],[0.8.4],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) swift_config_flags="$*" # We want to stop when given unrecognised options. No subdirs so this is safe. @@ -86,6 +86,7 @@ AC_ARG_ENABLE([logger], if test "$with_logger" = "yes"; then AC_DEFINE([WITH_LOGGER], 1, [logger enabled]) fi +AM_CONDITIONAL([HAVELOGGER],[test $with_logger = "yes"]) # Interprocedural optimization support. Needs special handling for linking and # archiving as well as compilation with Intels, needs to be done before @@ -225,16 +226,6 @@ if test "x$enable_debug" = "xyes"; then fi fi -# Check if stand-alone FoF is on. -AC_ARG_ENABLE([stand-alone-fof], - [AS_HELP_STRING([--enable-stand-alone-fof], - [Activate the compilation of the stand-alone friends-of-friends post-processing tool.], - )], - [enable_standalone_fof="$enableval"], - [enable_standalone_fof="no"] -) -AM_CONDITIONAL([HAVEFOF],[test $enable_standalone_fof = "yes"]) - # Check if task debugging is on. AC_ARG_ENABLE([task-debugging], [AS_HELP_STRING([--enable-task-debugging], @@ -372,6 +363,21 @@ elif test "$no_gravity_below_id" != "no"; then AC_DEFINE_UNQUOTED([SWIFT_NO_GRAVITY_BELOW_ID], [$enableval] ,[Particles with smaller ID than this will have zero gravity forces]) fi +# Check if we want to use boundary particles. +AC_ARG_ENABLE([boundary-particles], + [AS_HELP_STRING([--enable-boundary-particles], + [Set all particles with an ID smaller than @<:@N@:>@ as boundary particles (i.e. receive zero gravity + hydro forces).] + )], + [boundary_particles="$enableval"], + [boundary_particles="no"] +) +if test "$boundary_particles" == "yes"; then + AC_MSG_ERROR(Need to specify the ID below which particles get zero forces when using --enable-boundary-particles!) +elif test "$boundary_particles" != "no"; then + AC_DEFINE_UNQUOTED([SWIFT_NO_GRAVITY_BELOW_ID], [$enableval] ,[Particles with smaller ID than this will have zero gravity forces]) + AC_DEFINE_UNQUOTED([SWIFT_BOUNDARY_PARTICLES], [$enableval] ,[Particles with smaller ID than this will be considered as boundaries.]) +fi + # Check whether we have any of the ARM v8.1 tick timers AX_ASM_ARM_PMCCNTR AX_ASM_ARM_CNTVCT @@ -427,14 +433,10 @@ HAVEVECTORIZATION=0 if test "$enable_opt" = "yes" ; then - # Add code optimisation flags and tuning to host. This is a funny macro - # that does not like CFLAGS being already set. Work around that as we have - # at least set it to "", so it is set. + # Choose the best flags for this compiler and architecture ac_test_CFLAGS="no" - old_CFLAGS="$CFLAGS" AX_CC_MAXOPT ac_test_CFLAGS="yes" - CFLAGS="$old_CFLAGS $CFLAGS" # Check SSE & AVX support (some overlap with AX_CC_MAXOPT). # Don't use the SIMD_FLAGS result with Intel compilers. The -x<code> @@ -995,6 +997,41 @@ fi AC_SUBST([TBBMALLOC_LIBS]) AM_CONDITIONAL([HAVETBBMALLOC],[test -n "$TBBMALLOC_LIBS"]) +# Check for python. +have_python="no" +AC_ARG_WITH([python], + [AS_HELP_STRING([--with-python=PATH], + [root directory where python is installed @<:@yes/no@:>@] + )], + [with_python="$withval"], + [with_python="no"] +) +if test "x$with_python" != "xno"; then + if test "$with_python" == ""; then + # use linux default python + with_python="/usr/" + fi + AM_PATH_PYTHON([3], [], [AC_MSG_ERROR(python not found)]) + AC_ARG_VAR([PYTHON_INCS], [Include flags for python, bypassing python-config]) + AC_ARG_VAR([PYTHON_CONFIG], [Path to python-config]) + AS_IF([test -z "$PYTHON_INCS"], [ + AS_IF([test -z "$PYTHON_CONFIG"], [ + AC_PATH_PROGS([PYTHON_CONFIG], + [python$PYTHON_VERSION-config python-config], + [no], + [`dirname $PYTHON`]) + AS_IF([test "$PYTHON_CONFIG" = no], [AC_MSG_ERROR([cannot find python-config for $PYTHON.])]) + ]) + AC_MSG_CHECKING([python include flags]) + PYTHON_INCS=`$PYTHON_CONFIG --includes` + AC_MSG_RESULT([$PYTHON_INCS]) + ]) + have_python="yes" +fi +AC_SUBST([PYTHON_INCS]) +AM_CONDITIONAL([HAVEPYTHON],[test -n "$PYTHON_INCS"]) + + # Check for HDF5. This is required. AX_LIB_HDF5 if test "$with_hdf5" != "yes"; then @@ -1086,7 +1123,7 @@ if test "x$with_velociraptor" != "xno"; then AC_PROG_FC AC_FC_LIBRARY_LDFLAGS if test "x$with_velociraptor" != "xyes" -a "x$with_velociraptor" != "x"; then - VELOCIRAPTOR_LIBS="-L$with_velociraptor -lvelociraptor -lmpi -lstdc++ -lhdf5_cpp" + VELOCIRAPTOR_LIBS="-L$with_velociraptor -lvelociraptor -lmpi -lstdc++ -lhdf5" CFLAGS="$CFLAGS -fopenmp" else VELOCIRAPTOR_LIBS="" @@ -1322,6 +1359,7 @@ with_subgrid_cooling=none with_subgrid_chemistry=none with_subgrid_tracers=none with_subgrid_entropy_floor=none +with_subgrid_pressure_floor=none with_subgrid_stars=none with_subgrid_star_formation=none with_subgrid_feedback=none @@ -1333,14 +1371,14 @@ case "$with_subgrid" in none) ;; GEAR) - with_subgrid_cooling=grackle + with_subgrid_cooling=grackle_0 with_subgrid_chemistry=GEAR - with_subgrid_tracers=none - with_subgrid_entropy_floor=none + with_subgrid_pressure_floor=GEAR with_subgrid_stars=GEAR with_subgrid_star_formation=GEAR with_subgrid_feedback=none with_subgrid_black_holes=none + enable_fof=no ;; EAGLE) with_subgrid_cooling=EAGLE @@ -1351,27 +1389,58 @@ case "$with_subgrid" in with_subgrid_star_formation=EAGLE with_subgrid_feedback=EAGLE with_subgrid_black_holes=EAGLE + enable_fof=yes ;; *) AC_MSG_ERROR([Unknown subgrid choice: $with_subgrid]) ;; esac +# Check if FoF is on. +AC_ARG_ENABLE([fof], + [AS_HELP_STRING([--enable-fof], + [Activate the friends-of-friends (FoF) code.], + )], + [enable_fof="$enableval"], + [enable_fof="no"] +) +if test "$enable_fof" = "yes"; then + AC_DEFINE([WITH_FOF], 1, [Enable FoF]) +fi + +# Check if stand-alone FoF is on. +AC_ARG_ENABLE([stand-alone-fof], + [AS_HELP_STRING([--enable-stand-alone-fof], + [Activate the compilation of the stand-alone friends-of-friends (FoF) post-processing tool.], + )], + [enable_standalone_fof="$enableval"], + [enable_standalone_fof="no"] +) +if test "$enable_standalone_fof" = "yes"; then + enable_fof="yes + stand-alone tool" + AC_DEFINE([WITH_FOF], 1, [Enable FoF]) + AC_DEFINE([WITH_STAND_ALONE_FOF], 1, [Enable stand-alone FoF]) +fi +AM_CONDITIONAL([HAVESTANDALONEFOF],[test $enable_standalone_fof = "yes"]) + # Gravity scheme. AC_ARG_WITH([gravity], [AS_HELP_STRING([--with-gravity=<scheme>], - [Gravity scheme to use @<:@default, with-potential, default: default@:>@] + [Gravity scheme to use @<:@basic, with-potential, with-multi-softening default: with-multi-softening@:>@] )], [with_gravity="$withval"], - [with_gravity="default"] + [with_gravity="with-multi-softening"] ) case "$with_gravity" in with-potential) - AC_DEFINE([POTENTIAL_GRAVITY], [1], [Gravity scheme with potential calculation]) + AC_DEFINE([POTENTIAL_GRAVITY], [1], [Basic gravity scheme with potential calculation]) ;; - default) - AC_DEFINE([DEFAULT_GRAVITY], [1], [Default gravity scheme]) + with-multi-softening) + AC_DEFINE([MULTI_SOFTENING_GRAVITY], [1], [Gravity scheme with per-particle type softening value and background particles]) + ;; + basic) + AC_DEFINE([DEFAULT_GRAVITY], [1], [Basic gravity scheme]) ;; *) AC_MSG_ERROR([Unknown gravity scheme: $with_gravity]) @@ -1606,7 +1675,8 @@ esac # Cooling function AC_ARG_WITH([cooling], [AS_HELP_STRING([--with-cooling=<function>], - [cooling function @<:@none, const-du, const-lambda, EAGLE, grackle, grackle1, grackle2, grackle3 default: none@:>@] + [cooling function @<:@none, const-du, const-lambda, EAGLE, grackle_* default: none@:>@. + For Grackle, you need to provide the primordial chemistry parameter (e.g. grackle_0)] )], [with_cooling="$withval"], [with_cooling="none"] @@ -1633,21 +1703,10 @@ case "$with_cooling" in compton) AC_DEFINE([COOLING_COMPTON], [1], [Compton cooling off the CMB]) ;; - grackle) - AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) - AC_DEFINE([COOLING_GRACKLE_MODE], [0], [Grackle chemistry network, mode 0]) - ;; - grackle1) - AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) - AC_DEFINE([COOLING_GRACKLE_MODE], [1], [Grackle chemistry network, mode 1]) - ;; - grackle2) + grackle_*) AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) - AC_DEFINE([COOLING_GRACKLE_MODE], [2], [Grackle chemistry network, mode 2]) - ;; - grackle3) - AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) - AC_DEFINE([COOLING_GRACKLE_MODE], [3], [Grackle chemistry network, mode 3]) + primordial_chemistry=${with_cooling:8} + AC_DEFINE_UNQUOTED([COOLING_GRACKLE_MODE], [$primordial_chemistry], [Grackle chemistry network]) ;; EAGLE) AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model]) @@ -1813,7 +1872,7 @@ esac # External potential AC_ARG_WITH([ext-potential], [AS_HELP_STRING([--with-ext-potential=<pot>], - [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, nfw, hernquist, disc-patch, sine-wave, default: none@:>@] + [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, nfw, hernquist, disc-patch, sine-wave, constant, default: none@:>@] )], [with_potential="$withval"], [with_potential="none"] @@ -1846,6 +1905,9 @@ case "$with_potential" in point-mass-softened) AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS_SOFT], [1], [Softened point-mass potential with form 1/(r^2 + softening^2).]) ;; + constant) + AC_DEFINE([EXTERNAL_POTENTIAL_CONSTANT], [1], [Constant gravitational acceleration.]) + ;; *) AC_MSG_ERROR([Unknown external potential: $with_potential]) ;; @@ -1879,6 +1941,35 @@ case "$with_entropy_floor" in ;; esac +# Pressure floor +AC_ARG_WITH([pressure-floor], + [AS_HELP_STRING([--with-pressure-floor=<floor>], + [pressure floor @<:@none, GEAR, default: none@:>@ + The hydro model needs to be compatible.] + )], + [with_pressure_floor="$withval"], + [with_pressure_floor="none"] +) +if test "$with_subgrid" != "none"; then + if test "$with_pressure_floor" != "none"; then + AC_MSG_ERROR([Cannot provide with-subgrid and with-pressure-floor together]) + else + with_pressure_floor="$with_subgrid_pressure_floor" + fi +fi + +case "$with_pressure_floor" in + none) + AC_DEFINE([PRESSURE_FLOOR_NONE], [1], [No pressure floor]) + ;; + GEAR) + AC_DEFINE([PRESSURE_FLOOR_GEAR], [1], [GEAR pressure floor]) + ;; + *) + AC_MSG_ERROR([Unknown pressure floor model]) + ;; +esac + # Star formation AC_ARG_WITH([star-formation], [AS_HELP_STRING([--with-star-formation=<sfm>], @@ -1936,7 +2027,7 @@ AM_CONDITIONAL([HAVEEAGLEFEEDBACK], [test $with_feedback = "EAGLE"]) # Handle .in files. AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile examples/Cooling/CoolingRates/Makefile doc/Makefile doc/Doxyfile tests/Makefile]) -AC_CONFIG_FILES([argparse/Makefile tools/Makefile]) +AC_CONFIG_FILES([argparse/Makefile tools/Makefile logger/Makefile logger/tests/Makefile]) AC_CONFIG_FILES([tests/testReading.sh], [chmod +x tests/testReading.sh]) AC_CONFIG_FILES([tests/testActivePair.sh], [chmod +x tests/testActivePair.sh]) AC_CONFIG_FILES([tests/test27cells.sh], [chmod +x tests/test27cells.sh]) @@ -1989,7 +2080,7 @@ AC_MSG_RESULT([ CPU profiler : $have_profiler Pthread barriers : $have_pthread_barrier VELOCIraptor enabled : $have_velociraptor - Particle Logger : $with_logger + FoF activated: : $enable_fof Hydro scheme : $with_hydro Dimensionality : $with_dimension @@ -2004,6 +2095,7 @@ AC_MSG_RESULT([ Make gravity glass : $gravity_glass_making External potential : $with_potential + Pressure floor : $with_pressure_floor Entropy floor : $with_entropy_floor Cooling function : $with_cooling Chemistry : $with_chemistry @@ -2013,7 +2105,6 @@ AC_MSG_RESULT([ Star feedback model : $with_feedback Black holes model : $with_black_holes - Stand-alone FoF tool: : $enable_standalone_fof Individual timers : $enable_timers Task debugging : $enable_task_debugging Threadpool debugging : $enable_threadpool_debugging @@ -2024,5 +2115,9 @@ AC_MSG_RESULT([ Naive stars interactions : $enable_naive_interactions_stars Gravity checks : $gravity_force_checks Custom icbrtf : $enable_custom_icbrtf + Boundary particles : $boundary_particles + + Particle Logger : $with_logger + Python enabled : $have_python ------------------------]) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index c6b3046d2d3591c937dfd98cf75fb7697b90110f..94424f644e2f9e6dc4c436a42423ba667186e02b 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -777,6 +777,7 @@ INPUT += @top_srcdir@/src/tracers/EAGLE INPUT += @top_srcdir@/src/stars/EAGLE INPUT += @top_srcdir@/src/feedback/EAGLE INPUT += @top_srcdir@/src/black_holes/EAGLE +INPUT += @top_srcdir@/logger # 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/doc/RTD/source/AnalysisTools/index.rst b/doc/RTD/source/AnalysisTools/index.rst index f7f2f979666270ce371b532b6eab7bad3a23c9bd..8b4467f5f36a5e07f0b5446f4f590b2643990731 100644 --- a/doc/RTD/source/AnalysisTools/index.rst +++ b/doc/RTD/source/AnalysisTools/index.rst @@ -21,11 +21,13 @@ Cell graph ---------- An interactive graph of the cells is available with the configuration option ``--enable-cell-graph``. -During a run, SWIFT will generate a ``cell_hierarchy_*.csv`` file per MPI rank. -The command ``tools/make_cell_hierarchy.sh cell_hierarchy_*.csv`` merges the files together and generates the file ``cell_hierarchy.html`` +During a run, SWIFT will generate a ``cell_hierarchy_*.csv`` file per MPI rank at the frequency given by the parameter ``--cell-dumps=n``. +The command ``tools/make_cell_hierarchy.sh cell_hierarchy_0000_*.csv`` merges the files at time step 0 together and generates the file ``cell_hierarchy.html`` that contains the graph and can be read with your favorite web browser. -With chrome, you cannot access the files directly, you will need to either access them through an existing server (e.g. public http provided by your university) +With most web browsers, you cannot access the files directly. +If it is the case, the cells will never appear (but everything else should be fine). +To solve this problem, you will need to either access them through an existing server (e.g. public http provided by your university) or install ``npm`` and then run the following commands .. code-block:: bash @@ -34,6 +36,14 @@ or install ``npm`` and then run the following commands http-server . Now you can open the web page ``http://localhost:8080/cell_hierarchy.html``. +When running a large simulation, the data loading may take a while (a few seconds for EAGLE_6). +Your browser should not be hanging, but will seems to be idle. + +If you wish to add some information to the graph, you can do it by modifying the files ``src/space.c`` and ``tools/data/cell_hierarchy.html``. +In the first one, you will need to modify the calls to ``fprintf`` in the functions ``space_write_cell_hierarchy`` and ``space_write_cell``. +Here the code is simply writing CSV files containing all the required information about the cells. +In the second one, you will need to find the function ``mouseover`` and add the field that you have created. +You can also increase the size of the bubble through the style parameter ``height``. Memory usage reports -------------------- diff --git a/doc/RTD/source/CommandLineOptions/index.rst b/doc/RTD/source/CommandLineOptions/index.rst index 1144477548062bb61e47a88d3a1ee062b89b97cf..5251b36f7394465c59577932155544a755c0ee43 100644 --- a/doc/RTD/source/CommandLineOptions/index.rst +++ b/doc/RTD/source/CommandLineOptions/index.rst @@ -11,7 +11,10 @@ For instance, just running the ``swift`` binary will not use any SPH or gravity; the particles will just sit still! Below is a list of the command line options and when they should be used. The same list -can be found by typing ``./swift -h``:: +can be found by typing ``./swift -h``: + +.. code-block:: none + -h, --help show this help message and exit @@ -65,5 +68,7 @@ can be found by typing ``./swift -h``:: from all ranks. -y, --task-dumps=<int> Time-step frequency at which task analysis files and/or tasks are dumped. + --cell-dumps=<int> Time-step frequency at which cell graphs + are dumped. -Y, --threadpool-dumps=<int> Time-step frequency at which threadpool tasks are dumped. diff --git a/doc/RTD/source/GettingStarted/compiling_code.rst b/doc/RTD/source/GettingStarted/compiling_code.rst index 0cfde4d18db62c2e0b41e652c73a6b6ad268440e..297a2393db00498efc1590b1a02d6c3d95b8fc23 100644 --- a/doc/RTD/source/GettingStarted/compiling_code.rst +++ b/doc/RTD/source/GettingStarted/compiling_code.rst @@ -5,6 +5,24 @@ Compiling SWIFT =============== +Compilers +--------- + +SWIFT is a C99 code, and as such requires a C compiler that is able +to work with code built for that standard. + +SWIFT has been tested with the Intel, GCC, LLVM (clang) C compilers. + +We suggest: + ++ Intel >= 2018 ++ GCC >= 8.2.0 ++ LLVM >= 7.0.0 + +We have specific issues with the following compilers: + ++ GCC 7.3.0 with the -mskylake-avx512 flag. + Dependencies ------------ diff --git a/doc/RTD/source/HydroSchemes/anarchy_sph.rst b/doc/RTD/source/HydroSchemes/anarchy_sph.rst index 8d09280039c25608d3664e1a18d9b5c28d3108b6..267842936bddaf70f36c4aa4f53c27eb8c802108 100644 --- a/doc/RTD/source/HydroSchemes/anarchy_sph.rst +++ b/doc/RTD/source/HydroSchemes/anarchy_sph.rst @@ -11,10 +11,22 @@ includes: + Pressure-Energy SPH + Thermal diffusion following Price (2012) + A simplified version of the 'Inviscid SPH' artificial viscosity - (Cullen & Denhen 2010). + (Cullen & Denhen 2010), with a Balsara switch. More information will be made available in a forthcoming publication. +The simplified version of the 'Inviscid SPH' artificial viscosity calculates +the time differential of the velocity divergence explicitly, using the value +from the previous step. We also use the Balsara switch instead of the improved +neighbour-based limiter from Cullen & Dehnen 2010, to avoid matrix calculations. + +To configure with this scheme, use + +.. code-block:: bash + + ./configure --with-hydro=anarchy-pu --with-kernel=quintic-spline --disable-hand-vec + + The scheme as-implemented in SWIFT is slightly different to the one implemented in the original EAGLE code: @@ -26,8 +38,88 @@ implemented in the original EAGLE code: + Recommended kernel changed from Wendland-C2 (with 100 Ngb) to Quintic Spline (with ~82 Ngb). +The parameters available for this scheme, and their defaults, are: + +.. code-block:: yaml + + SPH: + viscosity_alpha: 0.1 # Initial value for the alpha viscosity + viscosity_length: 0.25 # Viscosity decay length (in terms of sound-crossing time) + # These are enforced each time-step + viscosity_alpha_max: 2.0 # Maximal allowed value for the viscosity alpha + viscosity_alpha_min: 0.0 # Minimal allowed value for the viscosity alpha + + diffusion_alpha: 0.0 # Initial value for the diffusion alpha + diffusion_beta: 0.01 # Timescale to raise the diffusion coefficient over + # (decay is on the sound-crossing time) + # These are enforced each time-step + diffusion_alpha_max: 1.0 + diffusion_alpha_min: 0.0 + + +There is also a compile-time parameter, ``viscosity_beta`` that we set to +3.0. During feedback events, the viscosity is set to the compile-time +``hydro_props_default_viscosity_alpha_feedback_reset = 2.0`` and the +diffusion is set to ``hydro_props_default_diffusion_alpha_feedback_reset = +0.0``. These can be changed in ``src/hydro/AnarchyPU/hydro_parameters.h``. + + +ANARCHY-DU SPH +============== + +This is the new scheme that will be used in EAGLE-XL. This scheme includes: + ++ Durier & Dalla Vecchia (2012) time-step limiter ++ Density-Energy SPH ++ Thermal diffusion following Price (2012) ++ A simplified version of the 'Inviscid SPH' artificial viscosity + (Cullen & Dehnen 2010), with a Balsara switch ++ A diffusion limiter, used to prevent energy leakage out of EAGLE + supernovae (Borrow in prep). + +More information will be made available in a forthcoming publication. + +The simplified version of the 'Inviscid SPH' artificial viscosity calculates +the time differential of the velocity divergence explicitly, using the value +from the previous step. We also use the Balsara switch instead of the improved +neighbour-based limiter from Cullen & Dehnen 2010, to avoid matrix +calculations. + +The diffusion limiter is implemented to ensure that the diffusion is turned +ff in very viscous flows and works as follows: + +.. code-block:: C + + float new_diffusion_alpha = old_diffusion_alpha; + + const float viscous_diffusion_limit = + diffusion_alpha_max * + (1.f - maximum_alpha_visc_over_ngb / viscosity_alpha_max); + + new_diffusion_alpha = min(new_diffusion_alpha, viscous_diffusion_limit); + + +The parameters available for this scheme, and their defaults, are: + +.. code-block:: yaml + + SPH: + viscosity_alpha: 0.1 # Initial value for the alpha viscosity + viscosity_length: 0.25 # Viscosity decay length (in terms of sound-crossing time) + # These are enforced each time-step + viscosity_alpha_max: 2.0 # Maximal allowed value for the viscosity alpha + viscosity_alpha_min: 0.0 # Minimal allowed value for the viscosity alpha + + diffusion_alpha: 0.0 # Initial value for the diffusion alpha + diffusion_beta: 0.25 # Timescale to raise the diffusion coefficient over + # (decay is on the sound-crossing time) + # These are enforced each time-step + diffusion_alpha_max: 1.0 + diffusion_alpha_min: 0.0 -.. code-block:: bash - - ./configure --with-hydro=anarchy-pu --with-kernel=quintic-spline --disable-hand-vec +There is also a compile-time parameter, ``viscosity_beta`` that we set to +3.0. During feedback events, the viscosity is set to the compile-time +``hydro_props_default_viscosity_alpha_feedback_reset = 2.0`` and the +diffusion is set to ``hydro_props_default_diffusion_alpha_feedback_reset = +0.0``. These can be changed in ``src/hydro/AnarchyPU/hydro_parameters.h``. diff --git a/doc/RTD/source/HydroSchemes/gizmo.rst b/doc/RTD/source/HydroSchemes/gizmo.rst index bbfcae04e1abac57b1476e4533bf92e051e6769d..fe8555f9bb7318d197162b2ffd90f90cb8ebc5b4 100644 --- a/doc/RTD/source/HydroSchemes/gizmo.rst +++ b/doc/RTD/source/HydroSchemes/gizmo.rst @@ -25,3 +25,6 @@ this at compile-time with the following configuration flags: .. code-block:: bash ./configure --with-hydro="gizmo-mfm" --with-riemann-solver="hllc" + + +These schemes should be treated as experimental and not used for production runs. \ No newline at end of file diff --git a/doc/RTD/source/HydroSchemes/hopkins_sph.rst b/doc/RTD/source/HydroSchemes/hopkins_sph.rst index e4f1479230df96eabaa1fe16994960059858613b..53ae687d549651f897b7b23b629d02c0b0ecd4a5 100644 --- a/doc/RTD/source/HydroSchemes/hopkins_sph.rst +++ b/doc/RTD/source/HydroSchemes/hopkins_sph.rst @@ -9,8 +9,8 @@ Pressure-Entropy SPH :hidden: :caption: Contents: -A pressure-entropy SPH scheme is available in SWIFT, inspired by Hopkins 2013. -This includes a Monaghan AV scheme and a Balsara switch. +A Pressure-Entropy SPH scheme is available in SWIFT, inspired by Hopkins 2013. +This includes a fixed Monaghan AV scheme and a Balsara switch. .. code-block:: bash @@ -18,6 +18,15 @@ This includes a Monaghan AV scheme and a Balsara switch. ./configure --with-hydro="pressure-entropy" +The parameters available for this scheme, and their defaults, are: + +.. code-block:: yaml + + SPH: + viscosity_alpha: 0.8 # Fixed value for the alpha viscosity + + + Pressure-Energy SPH =================== @@ -29,8 +38,38 @@ scheme it includes a Monaghan AV scheme and a Balsara switch. ./configure --with-hydro="pressure-energy" -Both of the above schemes use a very simple, fixed artificial viscosity, only -the ``SPH:viscosity_alpha`` parameter has any effect for this scheme. This will -change the strength of the artificial viscosity throughout the simulation, and -has a default of 0.8. +The parameters available for this scheme, and their defaults, are: + +.. code-block:: yaml + + SPH: + viscosity_alpha: 0.8 # Fixed value for the alpha viscosity + + +There is a variant of this implementation that includes a Morris & Monaghan +(1997) variable artificial viscosity that aims to reduce disappation away +from strong shocks. This implementation also includes a Balsara switch. +To use this scheme, you should use: + +.. code-block:: bash + + ./configure --with-hydro="pressure-energy-monaghan" + + +The parameters available for this scheme, and their defaults, are: + +.. code-block:: yaml + + SPH: + viscosity_alpha: 0.8 # Initial value for the alpha viscosity + viscosity_length: 0.25 # Viscosity decay length (in terms of sound-crossing time) + # These are enforced each time-step + viscosity_alpha_max: 2.0 # Maximal allowed value for the viscosity alpha + viscosity_alpha_min: 0.1 # Minimal allowed value for the viscosity alpha + + +There is also a compile-time parameter, ``viscosity_beta`` that we set to +3.0. During feedback events, the viscosity is set to the compile-time +``hydro_props_default_viscosity_alpha_feedback_reset = 2.0``. These can be +changed in ``src/hydro/AnarchyPU/hydro_parameters.h``. diff --git a/doc/RTD/source/InitialConditions/index.rst b/doc/RTD/source/InitialConditions/index.rst index e585c9aa55f269ebbbf9b2d83034b96a688a99f4..66fb9f01f1189c8d9f96ed2f1399e0c5001a8bc3 100644 --- a/doc/RTD/source/InitialConditions/index.rst +++ b/doc/RTD/source/InitialConditions/index.rst @@ -44,27 +44,29 @@ There are several groups that contain 'auxiliary' information, such as the particles. Some types are currently ignored by SWIFT but are kept in the file format for compatibility reasons. -+---------------------+------------------------+----------------------------+ -| HDF5 Group Name | Physical Particle Type | In code ``enum part_type`` | -+=====================+========================+============================+ -| ``/PartType0/`` | Gas | ``swift_type_gas`` | -+---------------------+------------------------+----------------------------+ -| ``/PartType1/`` | Dark Matter | ``swift_type_dark_matter`` | -+---------------------+------------------------+----------------------------+ -| ``/PartType2/`` | Ignored | | -+---------------------+------------------------+----------------------------+ -| ``/PartType3/`` | Ignored | | -+---------------------+------------------------+----------------------------+ -| ``/PartType4/`` | Stars | ``swift_type_star`` | -+---------------------+------------------------+----------------------------+ -| ``/PartType5/`` | Black Holes | ``swift_type_black_hole`` | -+---------------------+------------------------+----------------------------+ ++---------------------+------------------------+----------------------------------------+ +| HDF5 Group Name | Physical Particle Type | In code ``enum part_type`` | ++=====================+========================+========================================+ +| ``/PartType0/`` | Gas | ``swift_type_gas`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType1/`` | Dark Matter | ``swift_type_dark_matter`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType2/`` | Background Dark Matter | ``swift_type_dark_matter_background`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType3/`` | Ignored | | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType4/`` | Stars | ``swift_type_star`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType5/`` | Black Holes | ``swift_type_black_hole`` | ++---------------------+------------------------+----------------------------------------+ The last column in the table gives the ``enum`` value from ``part_type.h`` corresponding to a given entry in the files. -Note that the only particles that have hydrodynamical forces calculated between -them are those in ``PartType0``. +Note that the only particles that have hydrodynamical forces calculated +between them are those in ``PartType0``. The background dark matter +particles are used for zoom-in simulations and can have different masses +(and as a consequence softening length) within the ``/PartType2`` arrays. Necessary Components @@ -121,8 +123,14 @@ GADGET-2 based analysis programs: this to 1. If this field is present in a SWIFT IC file and has a value different from 1, the code will return an error message. + ``Time``, time of the start of the simulation in internal units or expressed - as a scale-factor for cosmological runs. SWIFT ignores this and reads it from - the parameter file. + as a scale-factor for cosmological runs. **SWIFT ignores this and reads it + from the parameter file**, behaviour that matches the GADGET-2 code. Note + that SWIFT writes the current time since the Big Bang, not scale-factor, to + this variable in snapshots. ++ ``Redshift``, the redshift at the start of the simulation. SWIFT checks this + (if present) against ``a_begin`` in the parameter file at the start of + cosmological runs. Note that we explicitly do **not** compare the ``Time`` + variable due to its ambiguous meaning. Particle Data @@ -137,8 +145,8 @@ individual particle type (e.g. ``/PartType0/``) that have the following *dataset within [0, L)^3 where L is the side-length of the simulation volume. In the case of cosmological simulations, these are the co-moving positions. + ``Velocities``, an array of shape (N, 3) that is the cartesian velocities of - the particles. When running cosmological simulations, these are the peculiar - velocities. Note that this is different from GADGET which uses peculiar + the particles. When running cosmological simulations, these are the **peculiar + velocities**. Note that this is different from GADGET which uses peculiar velocities divided by ``sqrt(a)`` (see below for a fix). + ``ParticleIDs``, an array of length N that are unique identifying numbers for each particle. Note that these have to be unique to a particle, and cannot be diff --git a/doc/RTD/source/ParameterFiles/parameter_description.rst b/doc/RTD/source/ParameterFiles/parameter_description.rst index 9f2ed813accc3e4aa91b2b916fb0c27a391544ee..4107bb836b20840a2b0fe4473fc816b6cf1dca90 100644 --- a/doc/RTD/source/ParameterFiles/parameter_description.rst +++ b/doc/RTD/source/ParameterFiles/parameter_description.rst @@ -174,7 +174,7 @@ use the following parameters: h: 0.6777 Omega_m: 0.307 Omega_lambda: 0.693 - Omega_b: 0.0455 + Omega_b: 0.0482519 Omega_r: 0. # (Optional) w_0: -1.0 # (Optional) w_a: 0. # (Optional) @@ -191,18 +191,38 @@ The behaviour of the self-gravity solver can be modified by the parameters provided in the ``Gravity`` section. The theory document puts these parameters into the context of the equations being solved. We give a brief overview here. -* The Plummer-equivalent co-moving softening length used for all particles :math:`\epsilon_{com}`: ``comoving_softening``, -* The Plummer-equivalent maximal physical softening length used for all particles :math:`\epsilon_{max}`: ``comoving_softening``, - -At any redshift :math:`z`, the Plummer-equivalent softening length used by the -code will be :math:`\epsilon=\min(\epsilon_{max}, -\frac{\epsilon_{com}}{z+1})`. This is expressed in internal units. +* The Plummer-equivalent co-moving softening length used for all dark matter particles :math:`\epsilon_{\rm com,DM}`: ``comoving_DM_softening``, +* The Plummer-equivalent co-moving softening length used for all baryon particles (gas, stars, BHs) :math:`\epsilon_{\rm com,bar}`: ``comoving_baryon_softening``, +* The Plummer-equivalent maximal physical softening length used for all dark matter particles :math:`\epsilon_{\rm max,DM}`: ``max_physical_DM_softening``, +* The Plummer-equivalent maximal physical softening length used for all baryon particles (gas, stars, BHs) :math:`\epsilon_{\rm max,bar}`: ``max_physical_baryon_softening``, + +At any redshift :math:`z`, the Plummer-equivalent softening length used by +the code will be :math:`\epsilon=\min(\epsilon_{max}, +\frac{\epsilon_{com}}{z+1})`. The same calculation is performed +independently for the dark matter and baryon particles. All the softening +quantities are expressed in internal units. Calculations that only involve +DM or baryons can leave the unused quantities out of the parameter +file. For non-cosmological runs, only the physical softening lengths need +to be supplied. + +In case of zoom simulations, the softening of the additional, more massive, background +particles is specified via the parameter +``softening_ratio_background``. Since these particles will typically have +different masses to degrade the resolution away from the zoom region, the +particles won't have a single softening value. Instead, we specify the +fraction of the mean inter-particle separation to use. The code will then +derive the softening length of each particle assuming the mean density of +the Universe. That is :math:`\epsilon_{\rm background} = +f\sqrt[3]{\frac{m}{\Omega_m\rho_{\rm crit}}}`, where :math:`f` is the +user-defined value (typically of order 0.05). + +The accuracy of the gravity calculation is governed by the following two parameters: * The opening angle (multipole acceptance criterion) used in the FMM :math:`\theta`: ``theta``, * The time-step size pre-factor :math:`\eta`: ``eta``, The time-step of a given particle is given by :math:`\Delta t = -\eta\sqrt{\frac{\epsilon}{|\overrightarrow{a}|}}`, where +\sqrt{\frac{2\eta\epsilon}{|\overrightarrow{a}|}}`, where :math:`\overrightarrow{a}` is the particle's acceleration. `Power et al. (2003) <http://adsabs.harvard.edu/abs/2003MNRAS.338...14P>`_ recommend using :math:`\eta=0.025`. The last tree-related parameter is @@ -236,15 +256,17 @@ simulation: # Parameters for the self-gravity scheme for the EAGLE-100 box Gravity: - eta: 0.025 - theta: 0.7 - comoving_softening: 0.0026994 # 0.7 proper kpc at z=2.8. - max_physical_softening: 0.0007 # 0.7 proper kpc - rebuild_frequency: 0.01 # Default optional value + eta: 0.025 + theta: 0.7 mesh_side_length: 512 - a_smooth: 1.25 # Default optional value - r_cut_max: 4.5 # Default optional value - r_cut_min: 0.1 # Default optional value + comoving_DM_softening: 0.0026994 # 0.7 proper kpc at z=2.8. + max_physical_DM_softening: 0.0007 # 0.7 proper kpc + comoving_baryon_softening: 0.0026994 # 0.7 proper kpc at z=2.8. + max_physical_baryon_softening: 0.0007 # 0.7 proper kpc + rebuild_frequency: 0.01 # Default optional value + a_smooth: 1.25 # Default optional value + r_cut_max: 4.5 # Default optional value + r_cut_min: 0.1 # Default optional value .. _Parameters_SPH: @@ -252,6 +274,142 @@ simulation: SPH --- +The ``SPH`` section is used to set parameters that describe the SPH +calculations. There are some scheme-specific values that are detailed in the +:ref:`hydro` section. The common parameters are detailed below. + +In all cases, users have to specify two values: + +* The smoothing length in terms of mean inter-particle separation: + ``resolution_eta`` +* The CFL condition that enters the time-step calculation: ``CFL_condition`` + +These quantities are dimensionless. The first, ``resolution_eta``, specifies +how smooth the simulation should be, and is used here instead of the number +of neighbours to smooth over as this also takes into account non-uniform +particle distributions. A value of 1.2348 gives approximately 48 neighbours +in 3D with the cubic spline kernel. More information on the choices behind +these parameters can be found in +`Dehnen & Aly 2012 <https://ui.adsabs.harvard.edu/abs/2012MNRAS.425.1068D/>`_. + + +The second quantity, the CFL condition, specifies how accurate the time +integration should be and enters as a pre-factor into the hydrodynamics +time-step calculation. This factor should be strictly bounded by 0 and 1, and +typically takes a value of 0.1 for SPH calculations. + +The next set of parameters deal with the calculation of the smoothing lengths +directly and are all optional: + +* The (relative) tolerance to converge smoothing lengths within: + ``h_tolerance`` (Default: 1e-4) +* The maximal smoothing length in internal units: ``h_max`` (Default: FLT_MAX) +* The minimal allowed smoothing length in terms of the gravitational + softening: ``h_min_ratio`` (Default: 0.0, i.e. no minimum) +* The maximal (relative) allowed change in volume over one time-step: + ``max_volume_change`` (Default: 1.4) +* The maximal number of iterations allowed to converge the smoothing + lengths: ``max_ghost_iterations`` (Default: 30) + +These parameters all set the accuracy of the smoothing lengths in various +ways. The first, the relative tolerance for the smoothing length, specifies +the convergence criteria for the smoothing length when using the +Newton-Raphson scheme. This works with the maximal number of iterations, +``max_ghost_iterations`` (so called because the smoothing length calculation +occurs in the ghost task), to ensure that the values of the smoothing lengths +are consistent with the local number density. We solve: + +.. math:: + (\eta h_i)^{n_D} = n_i^{-1} + +with :math:`h` the smoothing length, :math:`n_D` the number of spatial +dimensions, :math:`\eta` the value of ``resolution_eta``, and :math:`n_i` the +local number density. We change the value of the smoothing length, :math:`h`, +to be consistent with the number density. + +The maximal smoothing length, by default, is set to ``FLT_MAX``, and if set +prevents the smoothing length from going beyond ``h_max`` (in internal units) +during the run, irrespective of the above equation. The minimal smoothing +length is set in terms of the gravitational softening, ``h_min_ratio``, to +prevent the smoothing length from going below this value in dense +environments. This will lead to smoothing over more particles than specified +by :math:`\eta`. + +The final set of parameters in this section determine the initial and minimum +temperatures of the particles. + +* The initial temperature of all particles: ``initial_temperature`` (Default: + InternalEnergy from the initial conditions) +* The minimal temperature of any particle: ``minimal_temperature`` (Default: 0) +* The mass fraction of hydrogen used to set the initial temperature: + ``H_mass_fraction`` (Default: 0.755) +* The ionization temperature (from neutral to ionized) for primordial gas, + again used in this conversion: ``H_ionization_temperature`` (Default: 1e4) + +These parameters, if not present, are set to the default values. The initial +temperature is used, along with the hydrogen mass fraction and ionization +temperature, to set the initial internal energy per unit mass (or entropy per +unit mass) of the particles. + +Throughout the run, if the temperature of a particle drops below +``minimal_temperature``, the particle has energy added to it such that it +remains at that temperature. The run is not terminated prematurely. The +temperatures specified in this section are in internal units. + +The full section to start a typical cosmological run would be: + +.. code:: YAML + + SPH: + resolution_eta: 1.2 + CFL_condition: 0.1 + h_tolerance: 1e-4 + h_min_ratio: 0.1 + initial_temperature: 273 + minimal_temperature: 100 + H_mass_fraction: 0.755 + H_ionization_temperature: 1e4 + +.. _Parameters_Stars: + +Stars +----- + +The ``Stars`` section is used to set parameters that describe the Stars +calculations when doing feedback or enrichment. Note that if stars only act +gravitationally (i.e. SWIFT is run *without* ``--feedback``) no parameters +in this section are used. + +The first four parameters are related to the neighbour search: + +* The (relative) tolerance to converge smoothing lengths within: + ``h_tolerance`` (Default: same as SPH scheme) +* The maximal smoothing length in internal units: ``h_max`` (Default: same + as SPH scheme) +* The minimal allowed smoothing length in terms of the gravitational + softening: ``h_min_ratio`` (Default: same as SPH scheme) +* The maximal (relative) allowed change in volume over one time-step: + ``max_volume_change`` (Default: same as SPH scheme) + +These four parameters are optional and will default to their SPH equivalent +if left unspecified. That is the value specified by the user in that +section or the default SPH value if left unspecified there as well. + +The two remaining parameters can be used to overwrite the birth time (or +scale-factor) of the stars that were read from the ICs. This can be useful +to start a simulation with stars already of a given age. The parameters +are: + +* Whether or not to overwrite anything: ``overwrite_birth_time`` + (Default: 0) +* The value to use: ``birth_time`` + +If the birth time is set to ``-1`` then the stars will never enter any +feedback or enrichment loop. When these values are not specified, SWIFT +will start and use the birth times specified in the ICs. If no values are +given in the ICs, the stars' birth times will be zeroed, which can cause +issues depending on the type of run performed. + .. _Parameters_time_integration: Time Integration @@ -291,7 +449,9 @@ end scale-factors in the cosmology section of the parameter file. Additionally, when running a cosmological volume, advanced users can specify the value of the dimensionless pre-factor entering the time-step condition linked with the motion of particles with respect to the background expansion and mesh -size. See the theory document for the exact equations. +size. See the theory document for the exact equations. Note that we explicitly +ignore the ``Header/Time`` attribute in initial conditions files, and only read +the start and end times or scale factors from the parameter file. * Dimensionless pre-factor of the maximal allowed displacement: ``max_dt_RMS_factor`` (default: ``0.25``) @@ -923,7 +1083,7 @@ at some arbitrary steps, and indeed do better than the initial partition earlier in the run. This can be done using *fixed cost* repartitioning. Fixed costs are output during each repartition step into the file -`partition_fixed_costs.h`, this should be created by a test run of your your +`partition_fixed_costs.h`, this should be created by a test run of your full simulation (with possibly with a smaller volume, but all the physics enabled). This file can then be used to replace the same file found in the `src/` directory and SWIFT should then be recompiled. Once you have that, you diff --git a/doc/RTD/source/Snapshots/index.rst b/doc/RTD/source/Snapshots/index.rst index 30cdc0e1281ae0420b44d88001992ccbbe588136..fa11058edfa202b548936dac3ca55ada86eb9e84 100644 --- a/doc/RTD/source/Snapshots/index.rst +++ b/doc/RTD/source/Snapshots/index.rst @@ -107,21 +107,89 @@ There are several groups that contain 'auxiliary' information, such as the particles. The type use the naming convention of Gadget-2 (with the OWLS and EAGLE extensions). -+---------------------+------------------------+----------------------------+ -| HDF5 Group Name | Physical Particle Type | In code ``enum part_type`` | -+=====================+========================+============================+ -| ``/PartType0/`` | Gas | ``swift_type_gas`` | -+---------------------+------------------------+----------------------------+ -| ``/PartType1/`` | Dark Matter | ``swift_type_dark_matter`` | -+---------------------+------------------------+----------------------------+ -| ``/PartType4/`` | Stars | ``swift_type_star`` | -+---------------------+------------------------+----------------------------+ -| ``/PartType5/`` | Black Holes | ``swift_type_black_hole`` | -+---------------------+------------------------+----------------------------+ ++---------------------+------------------------+----------------------------------------+ +| HDF5 Group Name | Physical Particle Type | In code ``enum part_type`` | ++=====================+========================+========================================+ +| ``/PartType0/`` | Gas | ``swift_type_gas`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType1/`` | Dark Matter | ``swift_type_dark_matter`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType2/`` | Background Dark Matter | ``swift_type_dark_matter_background`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType4/`` | Stars | ``swift_type_star`` | ++---------------------+------------------------+----------------------------------------+ +| ``/PartType5/`` | Black Holes | ``swift_type_black_hole`` | ++---------------------+------------------------+----------------------------------------+ The last column in the table gives the ``enum`` value from ``part_type.h`` corresponding to a given entry in the files. +Unit information for individual fields +-------------------------------------- + +Each particle field contains meta-data about the units and how to +convert it to CGS in physical or co-moving frames. The meta-data is in +part designed for users to directly read and in part for machine +reading of the information. Each field contains the exponent of the +scale-factor, reduced Hubble constant [#f1]_ and each of the 5 base units +that is required to convert the field values to physical CGS +units. These fields are: + ++----------------------+---------------------------------------+ +| Meta-data field name | Description | ++======================+=======================================+ +| ``U_L exponent`` | Power of the length unit | ++----------------------+---------------------------------------+ +| ``U_M exponent`` | Power of the mass unit | ++----------------------+---------------------------------------+ +| ``U_t exponent`` | Power of the time unit | ++----------------------+---------------------------------------+ +| ``U_I exponent`` | Power of the current unit | ++----------------------+---------------------------------------+ +| ``U_T exponent`` | Power of the temperature unit | ++----------------------+---------------------------------------+ +| ``a-scale exponent`` | Power of the scale-factor | ++----------------------+---------------------------------------+ +| ``h-scale exponent`` | Power of the reduced Hubble constant | ++----------------------+---------------------------------------+ + +These are used by the ``swiftsimio`` python library to read units and +we encourage users to use this meta-data directly in their automated +tools. + +As an example, the fluid densities (which are written in the co-moving +frame) have the following conversion factors: + + * ``U_L exponent``: -3 + * ``U_M exponent``: 1 + * ``a-scale exponent``: -3 + +This condensed information is stored in the string ``Expression for +physical CGS units``, which in the case of the densities would read +``a^-3 U_M U_L^-3 [ g cm^-3 ]``. The values of the ``U_x`` can be +found in the ``Units System`` group at the root of the snapshot (see +above). Note that only unit factors with non-zero exponents are +printed to this string. + +Additionally, the meta-data contains the numerical conversion factor +from the field to co-moving CGS and physical CGS assuming the units in +the ``Unit System`` group. These are: + + * ``Conversion factor to CGS (not including cosmological corrections`` + * ``Conversion factor to phyical CGS (including cosmological corrections)`` + +These are designed for the users to directly use if they don't want to +compute the individual exponents themselves. As an example, in the +case of the densities and assuming the usual system of units +(:math:`10^{10} \rm{M}_\odot`, :math:`100 \rm{km/s}`, :math:`\rm{Mpc}`) at redshift +0.1, the conversion factors are: + + * Conversion to CGS: :math:`6.76814403 \times 10^{-31}` + * Conversion to physical CGS: :math:`9.00808555 \times 10^{-31}` + +In the case of a non-cosmological simulation, these two expressions +are identical since :math:`a=1`. + Quick access to particles via hash-tables ----------------------------------------- @@ -197,3 +265,13 @@ position `[1, 1, 1]` one could use a piece of code similar to: For large simulations, this vastly reduces the amount of data that needs to be read from the disk. + +Note that this is all automated in the ``swiftsimio`` python library +and we highly encourage its use. + +.. [#f1] Note that all quantities in SWIFT are always "h-free" in the + sense that they are expressed in units withouy any h + terms. This implies that the ``h-scale exponent`` field value + is always 0. SWIFT nevertheless includes this field to be + comprehensive and to prevent confusion with other software + packages that express their quantities with h-full units. diff --git a/doc/RTD/source/SubgridModels/EAGLE/index.rst b/doc/RTD/source/SubgridModels/EAGLE/index.rst index 56736cc4cca4bd9042a2c84d07dcf535f48c566c..ba0f62b4d77634707a36cc41d691609dffa23ce5 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/index.rst +++ b/doc/RTD/source/SubgridModels/EAGLE/index.rst @@ -238,6 +238,13 @@ Whilst one would use the following values for solar abundances init_abundance_Silicon: 6.825874e-4 # Mass fraction in Silicon init_abundance_Iron: 1.1032152e-3 # Mass fraction in Iron +Note that the code will verify that the input values make broad sense. This +means that SWIFT checks on startup that: + + * :math:`Z_{\rm H}+Z_{\rm He}+Z_{\rm metals} \approx 1` + * :math:`Z_{\rm C} + Z_{\rm N} + Z_{\rm O} + Z_{\rm Ne} + Z_{\rm Mg} + Z_{\rm Si} + Z_{\rm Fe} \lesssim Z_{\rm metals}` + * :math:`Z_{\rm H} + Z_{\rm He} + Z_{\rm C} + Z_{\rm N} + Z_{\rm O} + Z_{\rm Ne} + Z_{\rm Mg} + Z_{\rm Si} + Z_{\rm Fe} \approx 1` + Individual element abundances for each particle can also be read directly from the ICs. By default these are overwritten in the code by the values read from the YAML file. However, users can set the @@ -577,6 +584,11 @@ Black-hole creation Black-hole accretion ~~~~~~~~~~~~~~~~~~~~ +.. _EAGLE_black_hole_reposition: + +Black-hole repositioning +~~~~~~~~~~~~~~~~~~~~~~~~ + .. _EAGLE_black_hole_feedback: AGN feedback diff --git a/doc/RTD/source/SubgridModels/GEAR/index.rst b/doc/RTD/source/SubgridModels/GEAR/index.rst index 2a211759bfc4895fd07279b72f78200d6ea47546..152056ef2eb60da189bf18b3b3c9909f88ace6de 100644 --- a/doc/RTD/source/SubgridModels/GEAR/index.rst +++ b/doc/RTD/source/SubgridModels/GEAR/index.rst @@ -5,6 +5,29 @@ GEAR model =========== +Pressure Floor +~~~~~~~~~~~~~~ + +In order to avoid the artificial collapse of unresolved clumps, a minimum in pressure is applied to the particles. +This additional pressure can be seen as the pressure due to unresolved hydrodynamics turbulence and is given by: + +.. math:: + P_\textrm{Jeans} = \frac{\rho}{\gamma} \left( \frac{4}{\pi} G h^2 \rho N_\textrm{Jeans}^{2/3} - \sigma^2 \right) + +where :math:`\rho` is the density, :math:`\gamma` the adiabatic index, :math:`G` is the gravitational constant, +:math:`h` the smoothing length, :math:`N_\textrm{Jeans}` is the number of particle required in order to resolve a clump and +:math:`\sigma` the velocity dispersion. + + +This must be directly implemented into the hydro schemes, therefore only a subset of schemes (Gadget-2 and Pressure-Energy) are currently implemented. +In order to implement it, you need equation 12 in `Hopkins 2013 <https://arxiv.org/abs/1206.5006>`_: + +.. math:: + m_i \frac{\mathrm{d}v_i}{\mathrm{d}t} = - \sum_j x_i x_j \left[ \frac{P_i}{y_i^2} f_{ij} \nabla_i W_{ij}(h_i) + \frac{P_j}{y_j^2} f_{ji} \nabla_j W_{ji}(h_j) \right] + +and simply replace the :math:`P_i, P_j` by the pressure with the floor. +Here the :math:`x, y` are simple weights that should never have the pressure floor included even if they are related to the pressure (e.g. pressure-entropy). + Cooling: Grackle ~~~~~~~~~~~~~~~~ diff --git a/doc/RTD/source/conf.py b/doc/RTD/source/conf.py index 8b9df41fc476cf719f2f50b79b26360c144d6f4e..30780800e7829caccb5c443c4bfd97649479b590 100644 --- a/doc/RTD/source/conf.py +++ b/doc/RTD/source/conf.py @@ -25,7 +25,7 @@ author = 'SWIFT Team' # The short X.Y version version = '0.8' # The full version, including alpha/beta/rc tags -release = '0.8.3' +release = '0.8.4' # -- General configuration --------------------------------------------------- diff --git a/doc/RTD/source/index.rst b/doc/RTD/source/index.rst index ffd59d8b8e4b79e3b60aa81a00153d968ba42da0..154ad36f2e2281314dc12f33872fa24f7284de95 100644 --- a/doc/RTD/source/index.rst +++ b/doc/RTD/source/index.rst @@ -21,6 +21,7 @@ difference is the parameter file that will need to be adapted for SWIFT. Snapshots/index HydroSchemes/index SubgridModels/index + random FriendsOfFriends/index EquationOfState/index ExternalPotentials/index diff --git a/doc/RTD/source/random.rst b/doc/RTD/source/random.rst new file mode 100644 index 0000000000000000000000000000000000000000..24e4c0da676aae706be9c5f1ff333b88a78f6860 --- /dev/null +++ b/doc/RTD/source/random.rst @@ -0,0 +1,60 @@ +.. Random number generator + Folkert Nobels, 11th of July 2019 + +Random number generator +======================= + +Often subgrid models require random numbers, for this purpose +SWIFT has a random number generator. The random number generator +of SWIFT is based on a combination of the standard `rand_r` and `erand48` +random number generators. Since for some applications in cosmology +we want to be able to sample random numbers with a probability lower than +:math:`10^{-8}`, we could not simply use the 32-bit `rand_r` due to its cut-off +and spacing of around :math:`2^{-32} \approx 2 \times 10^{-10}`. +For the `erand48` algorithm with 48 bits the spacing and cutoff are +significantly smaller and around :math:`2^{-48} \approx 3.5 \times 10^{-15}`, +so this is very suitable for our applications. + +Reproducible random numbers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In our simulations we want to be able to reproduce the exact same random +numbers if we do exactly the same simulation twice. Because of this we +want to seed the random number generator in a reproducible way. In order to do this +we seed with the particle ID of the current particle and the current +integer simulation time. + +To produce several different types of random numbers we have an additional +argument called the type of random number which is basically the nth random +number for the specified seed, which is added to the particle ID, thus providing +a distinct state per random number type. + +Implementation +~~~~~~~~~~~~~~ + +Our random number generator packs the particle ID (plus the random number type) and +the current simulation time as two 64-bit values, plus a constant 16-bit value, +into a 144-bit buffer. This buffer is treated as an array of 9 `uint16` values. + +In a first pass we initialize the seed to 0 and run through the 9 `uint16` values, +xor-ing them with the seed and calling `rand_r` on the seed to advance it. Using +`rand_r` with the thus-generated seed, we generate a sequence of 9 16-bit values +and xor them with the original 144-bit buffer. + +The 9 bytes of this modified buffer are then used for three passes of `erand48`, +xor-ing the state in the same way as before. `erand48` is then called one final +time with the last state, producing a random double-precision value with a +48-bit mantissa. + +What to do if we break the random number generator? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The most likely case is that the RNG is not strong enough for our application, +in this case we could simply do multiple passes of both shuffling the state and +generating the final value from the state. This increases the computational cost but +will make the random number generator stronger. + +An other case is that we need probabilities that are lower than :math:`1 \times 10^{-17}`, +in this case we simply cannot use our random number generator and for example +need to generate two random numbers in order to probe these low probabilities. + diff --git a/examples/Cooling/ConstantCosmoTempEvolution/const_cosmo_temp_evol.yml b/examples/Cooling/ConstantCosmoTempEvolution/const_cosmo_temp_evol.yml index 01dcbc96fd329f53b1fd345bc198b05a53861982..50a95809873aa020257c8ef8ebf2444ac0be8be7 100644 --- a/examples/Cooling/ConstantCosmoTempEvolution/const_cosmo_temp_evol.yml +++ b/examples/Cooling/ConstantCosmoTempEvolution/const_cosmo_temp_evol.yml @@ -13,7 +13,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter # Parameters governing the time integration TimeIntegration: diff --git a/examples/Cooling/ConstantCosmoTempEvolution/plot_thermal_history.py b/examples/Cooling/ConstantCosmoTempEvolution/plot_thermal_history.py index 1494102531104b252e3edaa467920db7383ac6e6..1f9ae6557d1a55bd75d3440679ae42d653da3399 100644 --- a/examples/Cooling/ConstantCosmoTempEvolution/plot_thermal_history.py +++ b/examples/Cooling/ConstantCosmoTempEvolution/plot_thermal_history.py @@ -68,21 +68,21 @@ for snap in snap_list: z = np.append(z, data.metadata.z) # Convert gas temperatures and densities to right units - data.gas.temperature.convert_to_cgs() + data.gas.temperatures.convert_to_cgs() # Get mean and standard deviation of temperature - T_mean.append(np.mean(data.gas.temperature) * data.gas.temperature.units) - T_std.append(np.std(data.gas.temperature) * data.gas.temperature.units) + T_mean.append(np.mean(data.gas.temperatures) * data.gas.temperatures.units) + T_std.append(np.std(data.gas.temperatures) * data.gas.temperatures.units) # Get mean and standard deviation of density - rho_mean.append(np.mean(data.gas.density) * data.gas.density.units) - rho_std.append(np.std(data.gas.density) * data.gas.density.units) + rho_mean.append(np.mean(data.gas.densities) * data.gas.densities.units) + rho_std.append(np.std(data.gas.densities) * data.gas.densities.units) ## Turn into numpy arrays -T_mean = np.array(T_mean) * data.gas.temperature.units -T_std = np.array(T_std) * data.gas.temperature.units -rho_mean = np.array(rho_mean) * data.gas.density.units -rho_std = np.array(rho_std) * data.gas.density.units +T_mean = np.array(T_mean) * data.gas.temperatures.units +T_std = np.array(T_std) * data.gas.temperatures.units +rho_mean = np.array(rho_mean) * data.gas.densities.units +rho_std = np.array(rho_std) * data.gas.densities.units ## Put Density into units of mean baryon density today diff --git a/examples/Cooling/CoolingBox/plotEnergy.py b/examples/Cooling/CoolingBox/plotEnergy.py index 9c7af57d3d9dfdcfa222e9f77701f230d25f9ddc..6234c319a3001fa4e364639bb2c5480a31cf99dd 100644 --- a/examples/Cooling/CoolingBox/plotEnergy.py +++ b/examples/Cooling/CoolingBox/plotEnergy.py @@ -87,7 +87,7 @@ temp_snap = np.zeros(N) time_snap_cgs = np.zeros(N) for i in range(N): snap = File(files[i], 'r') - u = snap["/PartType0/InternalEnergy"][:] * snap["/PartType0/Masses"][:] + u = snap["/PartType0/InternalEnergies"][:] * snap["/PartType0/Masses"][:] u = sum(u) / total_mass[0] temp_snap[i] = energyUnits(u) time_snap_cgs[i] = snap["/Header"].attrs["Time"] * unit_time diff --git a/examples/Cooling/CoolingRedshiftDependence/plotSolution.py b/examples/Cooling/CoolingRedshiftDependence/plotSolution.py index 9cde36cb05b88e60b3bf3527f3857a3774bf5dca..cb624be3cfb1f0f803cfce7a14fb1a772cccf515 100644 --- a/examples/Cooling/CoolingRedshiftDependence/plotSolution.py +++ b/examples/Cooling/CoolingRedshiftDependence/plotSolution.py @@ -94,15 +94,17 @@ def get_data(handle: float, n_snaps: int): t0 = data.metadata.t.to(Myr).value times.append(data.metadata.t.to(Myr).value - t0) - temps.append(np.mean(data.gas.temperature.to(K).value)) + temps.append(np.mean(data.gas.temperatures.to(K).value)) densities.append( - np.mean(data.gas.density.to(mh / cm ** 3).value) + np.mean(data.gas.densities.to(mh / cm ** 3).value) / (data.metadata.scale_factor ** 3) ) try: energies.append( - np.mean((data.gas.internal_energy * data.gas.masses).to(erg).value) + np.mean( + (data.gas.internal_energies * data.gas.masses).to(erg).value + ) * data.metadata.scale_factor ** (2) ) radiated_energies.append( diff --git a/examples/Cooling/FeedbackEvent_3D/.gitignore b/examples/Cooling/FeedbackEvent_3D/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3a8485fdcf1c2638f90686598a442bfef6f50f64 --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/.gitignore @@ -0,0 +1,2 @@ +nocool_*/* +default_*/* diff --git a/examples/Cooling/FeedbackEvent_3D/README.md b/examples/Cooling/FeedbackEvent_3D/README.md new file mode 100644 index 0000000000000000000000000000000000000000..565990dd302ccb334c67ffa234294c031b8e6d9f --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/README.md @@ -0,0 +1,27 @@ +Feedback Event +============== + +This is a modified version of the SedovBlast_3D test, that is supposed to be used +with the sub-grid model, i.e. with + ++ Hydrodynamics ++ Cooling ++ Star formation + +This is here to ensure that we are handling the feedback events correctly in the code. + +We should not form any stars here, but ensure that flag is switched on just in case. + +This test emulates what the EAGLE model does to particles for feedback, i.e. + ++ Heats a single particle to 10^7.5 K ++ Does _not_ switch off cooling ++ Runs to completion. + + +Running Multiple Tests +---------------------- + +If you would like to run a suite of tests, try the runs.sh script. You'll +need to set the directories in the parameter file to be one higher, i.e. +../coolingtables rather than ./coolingtables. diff --git a/examples/Cooling/FeedbackEvent_3D/feedback.yml b/examples/Cooling/FeedbackEvent_3D/feedback.yml new file mode 100644 index 0000000000000000000000000000000000000000..5e4ddfc618ca64e6633772f155abbdc021cedc0e --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/feedback.yml @@ -0,0 +1,90 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841586e+43 # Grams + UnitLength_in_cgs: 3.08567758e+24 # Centimeters + UnitVelocity_in_cgs: 100000.0 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 1e-4 # The end time of the simulation (in internal units). + dt_min: 1e-9 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: feedback # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1e-5 # Time difference between consecutive outputs (in internal units) + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-6 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./feedback.hdf5 + periodic: 1 + cleanup_smoothing_lengths: 1 + + +# Dimensionless pre-factor for the time-step condition +LambdaCooling: + lambda_nH2_cgs: 1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) + cooling_tstep_mult: 1.0 # Dimensionless pre-factor for the time-step condition + +# Cooling with Grackle 2.0 +GrackleCooling: + CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + WithUVbackground: 0 # Enable or not the UV background + Redshift: 0 # Redshift to use (-1 means time based redshift) + WithMetalCooling: 1 # Enable or not the metal cooling + ProvideVolumetricHeatingRates: 0 # User provide volumetric heating rates + ProvideSpecificHeatingRates: 0 # User provide specific heating rates + SelfShieldingMethod: 0 # Grackle (<= 3) or Gear self shielding method + MaxSteps: 1000 + ConvergenceLimit: 1e-2 + +GearChemistry: + InitialMetallicity: 0.01295 + +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0.014 + init_abundance_Hydrogen: 0.70649785 + init_abundance_Helium: 0.28055534 + init_abundance_Carbon: 2.0665436e-3 + init_abundance_Nitrogen: 8.3562563e-4 + init_abundance_Oxygen: 5.4926244e-3 + init_abundance_Neon: 1.4144605e-3 + init_abundance_Magnesium: 5.907064e-4 + init_abundance_Silicon: 6.825874e-4 + init_abundance_Iron: 1.1032152e-3 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + diff --git a/examples/Cooling/FeedbackEvent_3D/getGlass.sh b/examples/Cooling/FeedbackEvent_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ffd92e88deae6e91237059adac2a6c2067caee46 --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5 diff --git a/examples/Cooling/FeedbackEvent_3D/makeIC.py b/examples/Cooling/FeedbackEvent_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..24052cee995675742808df6cc16ae4b523389351 --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/makeIC.py @@ -0,0 +1,73 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2019 Josh Borrow (joshua.borrow@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/>. +# +############################################################################## + +from swiftsimio import Writer +from swiftsimio.units import cosmo_units + +from unyt import cm, kpc, mh, msun, K, s, kb + +import h5py +import numpy as np + +# Generates a SWIFT IC file for the Feedback blast test. + +# Parameters +gamma = 5.0 / 3.0 +initial_density = 0.1 * mh / (cm ** 3) +initial_temperature = 2550 * (5/4) * K # Equilibrium temperature at solar abundance +inject_temperature = 10 ** (7.5) * K +mu = 0.5 +particle_mass = 1e6 * msun + +if __name__ == "__main__": + unit_system = cosmo_units + file_name = "feedback.hdf5" + + # Read in glass file + with h5py.File("glassCube_32.hdf5", "r") as handle: + positions = handle["/PartType0/Coordinates"][:] + h = handle["PartType0/SmoothingLength"][:] * 0.3 + + number_of_particles = len(h) + side_length = (number_of_particles * particle_mass / initial_density) ** (1 / 3) + side_length.convert_to_base(unit_system) + + print(f"Your box has a side length of {side_length}") + + # Find the central particle + central_particle = np.sum((positions - 0.5) ** 2, axis=1).argmin() + + # Inject the feedback into that central particle + background_internal_energy = ( + (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * initial_temperature + ) + heated_internal_energy = (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * inject_temperature + internal_energy = np.ones_like(h) * background_internal_energy + internal_energy[central_particle] = heated_internal_energy + + # Now we have all the information we need to set up the initial conditions! + output = Writer(unit_system=unit_system, box_size=side_length) + + output.gas.coordinates = positions * side_length + output.gas.velocities = np.zeros_like(positions) * cm / s + output.gas.smoothing_length = h * side_length + output.gas.internal_energy = internal_energy + output.gas.masses = np.ones_like(h) * particle_mass + + output.write(file_name) diff --git a/examples/Cooling/FeedbackEvent_3D/plotEnergy.py b/examples/Cooling/FeedbackEvent_3D/plotEnergy.py new file mode 100644 index 0000000000000000000000000000000000000000..6a199928b34d3d5f6ac163bfec70f9610ccafddf --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/plotEnergy.py @@ -0,0 +1,79 @@ +""" +Plots the energy from the energy.txt file for this simulation. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from swiftsimio import load + +from unyt import Gyr, erg, mh, kb + +from makeIC import gamma, initial_density, initial_temperature, inject_temperature, mu, particle_mass + +try: + plt.style.use("mnras_durham") +except: + pass + + +# Snapshot for grabbing the units. +snapshot = load("feedback_0000.hdf5") +units = snapshot.metadata.units +energy_units = units.mass * units.length ** 2 / (units.time ** 2) + +data = np.loadtxt("energy.txt").T + +# Assign correct units to each + +time = data[0] * units.time +mass = data[1] * units.mass +total_energy = data[2] * energy_units +kinetic_energy = data[3] * energy_units +thermal_energy = data[4] * energy_units +radiative_cool = data[8] * energy_units + +# Now we have to figure out how much energy we actually 'injected' +background_internal_energy = ( + (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * initial_temperature +) + +heated_internal_energy = (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * inject_temperature + +injected_energy = (heated_internal_energy - background_internal_energy) * particle_mass + +# Also want to remove the 'background' energy +n_parts = snapshot.metadata.n_gas +total_background_energy = background_internal_energy * n_parts * particle_mass + +# Now we can plot + +fig, ax = plt.subplots() + +ax.plot( + time.to(Gyr), + (kinetic_energy).to(erg), + label="Kinetic" +) + +ax.plot( + time.to(Gyr), + (thermal_energy - total_background_energy).to(erg), + label="Thermal" +) + +ax.plot( + time.to(Gyr), + (radiative_cool ).to(erg), + label="Lost to cooling" +) + +ax.set_xlim(0, 0.05 * Gyr) + +ax.set_xlabel("Time [Gyr]") +ax.set_ylabel("Energy [erg]") + +ax.legend() + +fig.tight_layout() +fig.savefig("Energy.pdf") \ No newline at end of file diff --git a/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py b/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py new file mode 100644 index 0000000000000000000000000000000000000000..be1ec94d93a8aad59c481a6a662a1462073baec6 --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py @@ -0,0 +1,111 @@ +""" +Plots the energy from the energy.txt file for this simulation. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from swiftsimio import load + +from unyt import Gyr, erg, mh, kb, Myr +from scipy.interpolate import interp1d + +from makeIC import gamma, initial_density, initial_temperature, inject_temperature, mu, particle_mass + +try: + plt.style.use("mnras_durham") +except: + pass + +time_to_plot = 25 * Myr +diffusion_parameters = [0.1 * x for x in range(11)] +plot_directory_name = "default_diffmax" + +kinetic_energy_at_time = [] +thermal_energy_at_time = [] +radiative_energy_at_time = [] + +for diffusion in diffusion_parameters: + directory_name = f"{plot_directory_name}_{diffusion:1.1f}" + +# Snapshot for grabbing the units. + snapshot = load(f"{directory_name}/feedback_0000.hdf5") + units = snapshot.metadata.units + energy_units = units.mass * units.length ** 2 / (units.time ** 2) + + data = np.loadtxt(f"{directory_name}/energy.txt").T + +# Assign correct units to each + + time = data[0] * units.time + mass = data[1] * units.mass + total_energy = data[2] * energy_units + kinetic_energy = data[3] * energy_units + thermal_energy = data[4] * energy_units + radiative_cool = data[8] * energy_units + +# Now we have to figure out how much energy we actually 'injected' + background_internal_energy = ( + (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * initial_temperature + ) + + heated_internal_energy = (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * inject_temperature + + injected_energy = (heated_internal_energy - background_internal_energy) * particle_mass + +# Also want to remove the 'background' energy + n_parts = snapshot.metadata.n_gas + total_background_energy = background_internal_energy * n_parts * particle_mass + + kinetic_energy_interpolated = interp1d( + time.to(Myr), + kinetic_energy.to(erg) + ) + + thermal_energy_interpolated = interp1d( + time.to(Myr), + (thermal_energy - total_background_energy).to(erg) + ) + + radiative_cool_interpolated = interp1d( + time.to(Myr), + radiative_cool.to(erg) + ) + + kinetic_energy_at_time.append(kinetic_energy_interpolated(time_to_plot.to(Myr))) + thermal_energy_at_time.append(thermal_energy_interpolated(time_to_plot.to(Myr))) + radiative_energy_at_time.append(radiative_cool_interpolated(time_to_plot.to(Myr))) + + +# Now we can plot + + +fig, ax = plt.subplots() + +ax.plot( + diffusion_parameters, + kinetic_energy_at_time, + label="Kinetic" +) + +ax.plot( + diffusion_parameters, + thermal_energy_at_time, + label="Thermal" +) + +ax.plot( + diffusion_parameters, + radiative_energy_at_time, + label="Lost to cooling" +) + +ax.set_xlim(0, 1.0) + +ax.set_xlabel(r"Diffusion $\alpha_{\rm max}$") +ax.set_ylabel(f"Energy in component at $t=${time_to_plot} [erg]") + +ax.legend() + +fig.tight_layout() +fig.savefig("EnergyFuncDiff.pdf") diff --git a/examples/Cooling/FeedbackEvent_3D/plotSolution.py b/examples/Cooling/FeedbackEvent_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..6631fff2244e73244d0311f4e823c42dfcea7609 --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/plotSolution.py @@ -0,0 +1,232 @@ +############################################################################### +# This file is part of the ANARCHY paper. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2019 Josh Borrow (joshua.boorrow@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/>. +# +################################################################################ + +import sys +import matplotlib + +matplotlib.use("Agg") + +import matplotlib.pyplot as plt +import numpy as np +from scipy import stats +from unyt import cm, s, km, kpc, Pa, msun, K, keV, mh + +kPa = 1000 * Pa +plot_radius = 7 * kpc + +from swiftsimio import load + +snap = int(sys.argv[1]) + +sim = load(f"feedback_{snap:04d}.hdf5") + +# Set up plotting stuff +try: + plt.style.use("mnras_durham") +except: + rcParams = { + "font.serif": ["STIX", "Times New Roman", "Times"], + "font.family": ["serif"], + "mathtext.fontset": "stix", + "font.size": 8, + } + plt.rcParams.update(rcParams) + + +def get_data_dump(metadata): + """ + Gets a big data dump from the SWIFT metadata + """ + + try: + viscosity = metadata.viscosity_info + except: + viscosity = "No info" + + try: + diffusion = metadata.diffusion_info + except: + diffusion = "No info" + + output = ( + "$\\bf{SWIFT}$\n" + + metadata.code_info + + "\n\n" + + "$\\bf{Compiler}$\n" + + metadata.compiler_info + + "\n\n" + + "$\\bf{Hydrodynamics}$\n" + + metadata.hydro_info + + "\n\n" + + "$\\bf{Viscosity}$\n" + + viscosity + + "\n\n" + + "$\\bf{Diffusion}$\n" + + diffusion + ) + + return output + + +# Read the simulation data +boxSize = sim.metadata.boxsize[0] + +x = sim.gas.coordinates[:, 0] - boxSize / 2 +y = sim.gas.coordinates[:, 1] - boxSize / 2 +z = sim.gas.coordinates[:, 2] - boxSize / 2 +r = np.sqrt(x ** 2 + y ** 2 + z ** 2) +vel = sim.gas.velocities +v_r = (x * vel[:, 0] + y * vel[:, 1] + z * vel[:, 2]) / r + +# Remove unyt information +v_r = v_r.to(km / s).value +r = r.to(kpc).value + + +data = dict( + x=r, + v_r=v_r, + u=sim.gas.temperatures.to(K).value, + S=sim.gas.entropies.to(keV / K).value, + P=sim.gas.pressures.to(kPa).value, + rho=sim.gas.densities.to(mh / (cm ** 3)).value, +) + +# Try to add on the viscosity and diffusion. +try: + data["visc"] = sim.gas.viscosity.value +except: + pass + +try: + data["diff"] = sim.gas.diffusion.value +except: + pass + +# Bin the data +x_bin_edge = np.linspace(0.0, boxSize.to(kpc).value) +x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) +binned = { + k: stats.binned_statistic(data["x"], v, statistic="mean", bins=x_bin_edge)[0] + for k, v in data.items() +} +square_binned = { + k: stats.binned_statistic(data["x"], v ** 2, statistic="mean", bins=x_bin_edge)[0] + for k, v in data.items() +} +sigma = { + k: np.sqrt(v2 - v ** 2) + for k, v2, v in zip(binned.keys(), square_binned.values(), binned.values()) +} + +# Now we can do the plotting. +fig, ax = plt.subplots(2, 3, figsize=(6.974, 6.974 * (2.0 / 3.0))) +ax = ax.flatten() + +# These are stored in priority order +plot = dict( + v_r="Radial Velocity ($v_r$) [km s$^{-1}$]", + u="Temperature ($T$) [K]", + rho=r"Density ($\rho$) [cm$^{-1}$]", + visc=r"Viscosity Coefficient ($\alpha_V$)", + diff=r"Diffusion Coefficient ($\alpha_D$)", + P="Pressure ($P$) [kPa]", + S="Entropy ($A$) [keV K$^{-1}$]", +) + +log = dict( + v_r=False, v_phi=False, u=False, S=False, P=False, rho=False, visc=False, diff=False +) +ylim = dict( + v_r=[-4, 25], + u=[4750, 6000], + rho=[0.09, 0.16], + visc=[0, 2.0], + diff=[0, 0.25], + P=[3e-18, 10e-18], + S=[-0.5e60, 4e60], +) + +current_axis = 0 + +for key, label in plot.items(): + if current_axis > 4: + break + else: + axis = ax[current_axis] + + try: + if log[key]: + axis.semilogy() + + # Raw data + axis.plot( + data["x"], + data[key], + ".", + color="C1", + ms=0.5, + alpha=0.5, + markeredgecolor="none", + rasterized=True, + zorder=0, + ) + # Binned data + axis.errorbar( + x_bin, + binned[key], + yerr=sigma[key], + fmt=".", + ms=3.0, + color="C3", + lw=0.5, + zorder=2, + ) + + axis.set_xlabel("Radius ($r$) [kpc]", labelpad=0) + axis.set_ylabel(label, labelpad=0) + + axis.set_xlim(0.0, plot_radius.to(kpc)) + + try: + axis.set_ylim(*ylim[key]) + except KeyError: + # No worries pal + pass + + current_axis += 1 + except KeyError: + # Mustn't have that data! + continue + + +info_axis = ax[-1] + +info = get_data_dump(sim.metadata) + +info_axis.text( + 0.5, 0.45, info, ha="center", va="center", fontsize=5, transform=info_axis.transAxes +) + +info_axis.axis("off") + + +fig.tight_layout(pad=0.5) +fig.savefig("FeedbackEvent.pdf", dpi=300) diff --git a/examples/Cooling/FeedbackEvent_3D/run.sh b/examples/Cooling/FeedbackEvent_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..9111b8e8cf2cb9b0b333a1b97c04dac3975fac5c --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/run.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Run SWIFT +../../swift --hydro --cooling --limiter --threads=4 feedback.yml 2>&1 | tee output.log + +# Plot the solution +python plotSolution.py 5 +python plotEnergy.py diff --git a/examples/Cooling/FeedbackEvent_3D/runs.sh b/examples/Cooling/FeedbackEvent_3D/runs.sh new file mode 100644 index 0000000000000000000000000000000000000000..5e3853f1e196a2bedfcb32579209fd2f614cfadc --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/runs.sh @@ -0,0 +1,31 @@ +#!/bin/bash -l + +#SBATCH -J SWIFTDiffusionCalibration +#SBATCH -N 1 +#SBATCH -o swift_diffusion.out +#SBATCH -e swift_diffusion.err +#SBATCH -p cosma +#SBATCH -A durham +#SBATCH --exclusive + +#SBATCH -t 1:00:00 + + +for diffusion_alpha_max in 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0; +do + mkdir default_diffmax_$diffusion_alpha_max + + cd default_diffmax_$diffusion_alpha_max + + ../../../swift --hydro --cooling --limiter --threads=16 --param="SPH:diffusion_alpha_max:${diffusion_alpha_max}" ../feedback.yml 2>&1 | tee output.log + + cd .. + + mkdir nocool_diffmax_$diffusion_alpha_max + + cd nocool_diffmax_$diffusion_alpha_max + + ../../../swift --hydro --temperature --limiter --threads=16 --param="SPH:diffusion_alpha_max:${diffusion_alpha_max}" ../feedback.yml 2>&1 | tee output.log + + cd .. +done diff --git a/examples/Cosmology/ComovingSodShock_1D/plotSolution.py b/examples/Cosmology/ComovingSodShock_1D/plotSolution.py index 95674c04bfafd0cd549b69814df82f9a4f80a949..b09873339fc4ded024c70f66301c5cce762b97fc 100644 --- a/examples/Cosmology/ComovingSodShock_1D/plotSolution.py +++ b/examples/Cosmology/ComovingSodShock_1D/plotSolution.py @@ -82,12 +82,12 @@ git = str(sim["Code"].attrs["Git Revision"]) x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] * anow -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] try: - alpha = sim["/PartType0/Viscosity"][:] + alpha = sim["/PartType0/ViscosityParameters"][:] plot_alpha = True except: plot_alpha = False diff --git a/examples/Cosmology/ComovingSodShock_2D/plotSolution.py b/examples/Cosmology/ComovingSodShock_2D/plotSolution.py index 8adb3cf5c550ab9724f6a8f34c1a1260a25712e1..ec28b9449bfa6b00d3088952a6b1bf871462f86c 100644 --- a/examples/Cosmology/ComovingSodShock_2D/plotSolution.py +++ b/examples/Cosmology/ComovingSodShock_2D/plotSolution.py @@ -83,10 +83,10 @@ git = sim["Code"].attrs["Git Revision"] x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] * anow -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] N = 1000 # Number of points x_min = -1. diff --git a/examples/Cosmology/ComovingSodShock_3D/plotSolution.py b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py index d85f4be9a49d108d133928a81ea4482fa9099792..34abae364f5421a0436352b9564537f2f31e8742 100644 --- a/examples/Cosmology/ComovingSodShock_3D/plotSolution.py +++ b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py @@ -83,19 +83,19 @@ git = sim["Code"].attrs["Git Revision"] x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] * anow -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] try: - diffusion = sim["/PartType0/Diffusion"][:] + diffusion = sim["/PartType0/DiffusionParameters"][:] plot_diffusion = True except: plot_diffusion = False try: - viscosity = sim["/PartType0/Viscosity"][:] + viscosity = sim["/PartType0/ViscosityParameters"][:] plot_viscosity = True except: plot_viscosity = False diff --git a/examples/Cosmology/ConstantCosmoVolume/constant_volume.yml b/examples/Cosmology/ConstantCosmoVolume/constant_volume.yml index ebfcc4ffd72121571fa1a69f900985917b440c65..a6ff72555ef68964508493856127d4cc739b7722 100644 --- a/examples/Cosmology/ConstantCosmoVolume/constant_volume.yml +++ b/examples/Cosmology/ConstantCosmoVolume/constant_volume.yml @@ -49,6 +49,8 @@ Gravity: mesh_side_length: 32 eta: 0.025 theta: 0.3 - comoving_softening: 0.08 # 80 kpc = 1/25 of mean inter-particle separation - max_physical_softening: 0.08 # 80 kpc = 1/25 of mean inter-particle separation + comoving_DM_softening: 0.08 # 80 kpc = 1/25 of mean inter-particle separation + max_physical_DM_softening: 0.08 # 80 kpc = 1/25 of mean inter-particle separation + comoving_baryon_softening: 0.08 # 80 kpc = 1/25 of mean inter-particle separation + max_physical_baryon_softening: 0.08 # 80 kpc = 1/25 of mean inter-particle separation diff --git a/examples/Cosmology/ConstantCosmoVolume/plotSolution.py b/examples/Cosmology/ConstantCosmoVolume/plotSolution.py index f77889d7cb19c230accf25290b88a321e0f59616..6df0bfa4e1f7c4932f652d82a8ca95f5c54b0e9f 100644 --- a/examples/Cosmology/ConstantCosmoVolume/plotSolution.py +++ b/examples/Cosmology/ConstantCosmoVolume/plotSolution.py @@ -109,19 +109,19 @@ for i in range(119): z[i] = sim["/Cosmology"].attrs["Redshift"][0] a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] - S = sim["/PartType0/Entropy"][:] + S = sim["/PartType0/Entropies"][:] S_mean[i] = np.mean(S) S_std[i] = np.std(S) - u = sim["/PartType0/InternalEnergy"][:] + u = sim["/PartType0/InternalEnergies"][:] u_mean[i] = np.mean(u) u_std[i] = np.std(u) - P = sim["/PartType0/Pressure"][:] + P = sim["/PartType0/Pressures"][:] P_mean[i] = np.mean(P) P_std[i] = np.std(P) - rho = sim["/PartType0/Density"][:] + rho = sim["/PartType0/Densities"][:] rho_mean[i] = np.mean(rho) rho_std[i] = np.std(rho) diff --git a/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py b/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py index eef247fb761e75f8dde8e8abe84075efbd7cb46a..285ed3ae218549468c309e9109ef53f10a2f66ab 100644 --- a/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py +++ b/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py @@ -78,10 +78,10 @@ gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] m = sim["/PartType0/Masses"][:] try: phi = sim["/PartType0/Potential"][:] @@ -98,8 +98,8 @@ if os.path.exists(filename_g): sim_g = h5py.File(filename_g, "r") x_g = sim_g["/PartType0/Coordinates"][:,0] v_g = sim_g["/PartType0/Velocities"][:,0] - u_g = sim_g["/PartType0/InternalEnergy"][:] - rho_g = sim_g["/PartType0/Density"][:] + u_g = sim_g["/PartType0/InternalEnergies"][:] + rho_g = sim_g["/PartType0/Densities"][:] phi_g = sim_g["/PartType0/Potential"][:] a_g = sim_g["/Header"].attrs["Time"] print("Gadget Scale-factor:", a_g, "redshift:", 1/a_g - 1.) diff --git a/examples/Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml b/examples/Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml index 6a7c5166635b7fa0ed5f69c41461d867c3b254ad..d43c78972b0bc8d1f250b95190dafef305abca3f 100644 --- a/examples/Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml +++ b/examples/Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml @@ -51,5 +51,7 @@ Gravity: eta: 0.025 theta: 0.3 r_cut_max: 5. - comoving_softening: 0.001 - max_physical_softening: 0.001 + comoving_DM_softening: 0.001 + max_physical_DM_softening: 0.001 + comoving_baryon_softening: 0.001 + max_physical_baryon_softening: 0.001 diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/eagle_100.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/eagle_100.yml index 5a3066195647b79eeb6a6d67d037d15ce8370c39..5d449cfceb589e7580ba5f52375d115678f51321 100644 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/eagle_100.yml +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/eagle_100.yml @@ -23,7 +23,7 @@ TimeIntegration: dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). Scheduler: - max_top_level_cells: 80 + max_top_level_cells: 64 # Parameters governing the snapshots Snapshots: @@ -43,8 +43,9 @@ Statistics: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.85 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). + mesh_side_length: 512 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters related to the initial conditions InitialConditions: diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/eagle_12.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/eagle_12.yml index 0660d98e87adfae62a2d795efec7ad6509cc1354..f232bb7e930a4977b65686424d04a7eed254761d 100644 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/eagle_12.yml +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/eagle_12.yml @@ -41,12 +41,11 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - mesh_side_length: 32 eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). mesh_side_length: 32 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters related to the initial conditions InitialConditions: diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/eagle_25.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/eagle_25.yml index 558c68ffaad204ebbe1d5781f945f0d95108d227..20f4c1eef17fafd8a87d1d251147438fc201a355 100644 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/eagle_25.yml +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/eagle_25.yml @@ -43,9 +43,9 @@ Statistics: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). - mesh_side_length: 32 + mesh_side_length: 64 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters related to the initial conditions InitialConditions: diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/eagle_50.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/eagle_50.yml index 3cab2b1dc869b5187cf647caa7893281b783591a..0ccf061c2e942126bbc0c2c69998bbca6a9b824f 100644 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/eagle_50.yml +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/eagle_50.yml @@ -23,7 +23,7 @@ TimeIntegration: dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). Scheduler: - max_top_level_cells: 20 + max_top_level_cells: 32 # Parameters governing the snapshots Snapshots: @@ -42,9 +42,9 @@ Statistics: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). - mesh_side_length: 64 + mesh_side_length: 128 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters related to the initial conditions InitialConditions: diff --git a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml index 3270af86afffba13cfac6be7c7c21dd84980adc5..ad20f401d26bde02a6a44299843b25e07a8f83d9 100644 --- a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml @@ -17,7 +17,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter # Parameters governing the time integration TimeIntegration: @@ -27,8 +27,8 @@ TimeIntegration: # Parameters governing the snapshots Snapshots: basename: eagle # Common part of the name of output files - scale_factor_first: 0.05 - delta_time: 1.02 + output_list_on: 1 + output_list: ./output_list.txt # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +38,12 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). + theta: 0.5 # Opening angle (Multipole acceptance criterion) mesh_side_length: 64 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: @@ -51,6 +53,11 @@ SPH: minimal_temperature: 100.0 # (internal units) initial_temperature: 268.7 +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 + # Parameters for the Friends-Of-Friends algorithm FOF: basename: fof_output # Filename for the FOF outputs. @@ -62,10 +69,10 @@ FOF: Scheduler: max_top_level_cells: 16 - cell_split_size: 100 tasks_per_cell: 5 Restarts: + onexit: 1 delta_hours: 1.0 # Parameters related to the initial conditions @@ -92,7 +99,7 @@ EAGLEChemistry: EAGLECooling: dir_name: ./coolingtables/ - H_reion_z: 11.5 + H_reion_z: 7.5 # Planck 2018 H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 @@ -170,3 +177,4 @@ EAGLEAGN: 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. + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. \ No newline at end of file diff --git a/examples/EAGLE_ICs/EAGLE_12/output_list.txt b/examples/EAGLE_ICs/EAGLE_12/output_list.txt new file mode 100644 index 0000000000000000000000000000000000000000..592ab8483d015fe1bfafe5cc603fabc230b25589 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_12/output_list.txt @@ -0,0 +1,38 @@ +# Redshift +18.08 +15.28 +13.06 +11.26 +9.79 +8.57 +7.54 +6.67 +5.92 +5.28 +4.72 +4.24 +3.81 +3.43 +3.09 +2.79 +2.52 +2.28 +2.06 +1.86 +1.68 +1.51 +1.36 +1.21 +1.08 +0.96 +0.85 +0.74 +0.64 +0.55 +0.46 +0.37 +0.29 +0.21 +0.14 +0.07 +0.00 diff --git a/examples/EAGLE_ICs/EAGLE_12/run.sh b/examples/EAGLE_ICs/EAGLE_12/run.sh index 12c962c29ddd21290624906dfbcca166e171203b..86fe2b1db0a7725b617656d3b52f0dce89435ea3 100755 --- a/examples/EAGLE_ICs/EAGLE_12/run.sh +++ b/examples/EAGLE_ICs/EAGLE_12/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_12.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --black-holes --cooling --star-formation --feedback --fof --threads=16 eagle_12.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml index 6461982f70c9635652b8253887a9998777f37c69..fd902d27daaca3c6c3ca9c5d52fbf43b1283c581 100644 --- a/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml @@ -17,7 +17,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter # Parameters governing the time integration TimeIntegration: @@ -27,8 +27,8 @@ TimeIntegration: # Parameters governing the snapshots Snapshots: basename: eagle # Common part of the name of output files - scale_factor_first: 0.05 - delta_time: 1.02 + output_list_on: 1 + output_list: ./output_list.txt # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +38,12 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). + theta: 0.5 # Opening angle (Multipole acceptance criterion) mesh_side_length: 128 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: @@ -51,6 +53,11 @@ SPH: minimal_temperature: 100.0 # (internal units) initial_temperature: 268.7 +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 + # Parameters for the Friends-Of-Friends algorithm FOF: basename: fof_output # Filename for the FOF outputs. @@ -62,10 +69,10 @@ FOF: Scheduler: max_top_level_cells: 16 - cell_split_size: 100 tasks_per_cell: 5 Restarts: + onexit: 1 delta_hours: 1.0 # Parameters related to the initial conditions @@ -93,7 +100,7 @@ EAGLEChemistry: # EAGLE cooling parameters EAGLECooling: dir_name: ./coolingtables/ - H_reion_z: 11.5 + H_reion_z: 7.5 # Planck 2018 H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 @@ -171,3 +178,4 @@ EAGLEAGN: 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. + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. \ No newline at end of file diff --git a/examples/EAGLE_ICs/EAGLE_25/output_list.txt b/examples/EAGLE_ICs/EAGLE_25/output_list.txt new file mode 100644 index 0000000000000000000000000000000000000000..592ab8483d015fe1bfafe5cc603fabc230b25589 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_25/output_list.txt @@ -0,0 +1,38 @@ +# Redshift +18.08 +15.28 +13.06 +11.26 +9.79 +8.57 +7.54 +6.67 +5.92 +5.28 +4.72 +4.24 +3.81 +3.43 +3.09 +2.79 +2.52 +2.28 +2.06 +1.86 +1.68 +1.51 +1.36 +1.21 +1.08 +0.96 +0.85 +0.74 +0.64 +0.55 +0.46 +0.37 +0.29 +0.21 +0.14 +0.07 +0.00 diff --git a/examples/EAGLE_ICs/EAGLE_25/run.sh b/examples/EAGLE_ICs/EAGLE_25/run.sh index 8b317183d850d5048253fedb8db7e24edd9c02da..11e93971767690c84921396842924853fa69ab23 100755 --- a/examples/EAGLE_ICs/EAGLE_25/run.sh +++ b/examples/EAGLE_ICs/EAGLE_25/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_25.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --black-holes --cooling --star-formation --feedback --fof --threads=16 eagle_25.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml index c6f7f0e01bd292b03b0eb31c6c1b57d5163dafe7..3091fb0be35111f0e6046fd99f0c426840d00231 100644 --- a/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml @@ -17,7 +17,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter # Parameters governing the time integration TimeIntegration: @@ -27,8 +27,8 @@ TimeIntegration: # Parameters governing the snapshots Snapshots: basename: eagle # Common part of the name of output files - scale_factor_first: 0.05 - delta_time: 1.02 + output_list_on: 1 + output_list: ./output_list.txt # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +38,12 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). + theta: 0.5 # Opening angle (Multipole acceptance criterion) mesh_side_length: 256 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: @@ -51,6 +53,11 @@ SPH: minimal_temperature: 100.0 # (internal units) initial_temperature: 268.7 +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 + # Parameters for the Friends-Of-Friends algorithm FOF: basename: fof_output # Filename for the FOF outputs. @@ -62,10 +69,10 @@ FOF: Scheduler: max_top_level_cells: 32 - cell_split_size: 100 tasks_per_cell: 5 Restarts: + onexit: 1 delta_hours: 1.0 # Parameters related to the initial conditions @@ -93,7 +100,7 @@ EAGLEChemistry: # EAGLE cooling parameters EAGLECooling: dir_name: ./coolingtables/ - H_reion_z: 11.5 + H_reion_z: 7.5 # Planck 2018 H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 @@ -171,5 +178,4 @@ EAGLEAGN: 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. - - + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. diff --git a/examples/EAGLE_ICs/EAGLE_50/output_list.txt b/examples/EAGLE_ICs/EAGLE_50/output_list.txt new file mode 100644 index 0000000000000000000000000000000000000000..592ab8483d015fe1bfafe5cc603fabc230b25589 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_50/output_list.txt @@ -0,0 +1,38 @@ +# Redshift +18.08 +15.28 +13.06 +11.26 +9.79 +8.57 +7.54 +6.67 +5.92 +5.28 +4.72 +4.24 +3.81 +3.43 +3.09 +2.79 +2.52 +2.28 +2.06 +1.86 +1.68 +1.51 +1.36 +1.21 +1.08 +0.96 +0.85 +0.74 +0.64 +0.55 +0.46 +0.37 +0.29 +0.21 +0.14 +0.07 +0.00 diff --git a/examples/EAGLE_ICs/EAGLE_50/run.sh b/examples/EAGLE_ICs/EAGLE_50/run.sh index 3ef38da33fe1cc820e3ee5e7afb620e7e9109196..492ac35ebe1af20392aff879aef078c0f9bd264f 100755 --- a/examples/EAGLE_ICs/EAGLE_50/run.sh +++ b/examples/EAGLE_ICs/EAGLE_50/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_50.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --black-holes --cooling --star-formation --feedback --fof --threads=16 eagle_50.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml b/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml index 5275f709c710d2da1d5396e98f1d4d918e482c6d..0cc97babbd2b89a7507808bfcad2648e0c03ce47 100644 --- a/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml +++ b/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml @@ -1,3 +1,6 @@ +MetaData: + run_name: EAGLE-L0100N1504-Ref + # Define the system of units to use internally. InternalUnitSystem: UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams @@ -13,7 +16,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter # Parameters governing the time integration TimeIntegration: @@ -42,9 +45,11 @@ Statistics: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.85 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). mesh_side_length: 256 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: @@ -52,6 +57,13 @@ SPH: h_min_ratio: 0.1 # Minimal smoothing in units of softening. CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) + overwrite_birth_time: 1 + birth_time: 0.33333 # Pretend all the stars were born at z = 2 + +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml index 9205b9f6f560398b046625f6c65fcc887492991d..73f4e1a8d4269567d4139af6b992754d17494d3d 100644 --- a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml @@ -1,3 +1,6 @@ +MetaData: + run_name: EAGLE-L0012N0188-Ref + # Define the system of units to use internally. InternalUnitSystem: UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams @@ -13,7 +16,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter # Parameters governing the time integration TimeIntegration: @@ -43,9 +46,11 @@ Statistics: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). mesh_side_length: 32 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: @@ -54,6 +59,13 @@ SPH: CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 + overwrite_birth_time: 1 + birth_time: 0.33333 # Pretend all the stars were born at z = 2 + # Parameters for the Friends-Of-Friends algorithm FOF: basename: fof_output # Filename for the FOF outputs. @@ -162,3 +174,4 @@ EAGLEAGN: 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. + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. \ No newline at end of file diff --git a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml index ccd89efbead0c511316bd664f6754ed0797c7c50..f7a9394299fbf641a98b2ffc2d7c4bac364c164e 100644 --- a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml @@ -1,3 +1,6 @@ +MetaData: + run_name: EAGLE-L0025N0376-Ref + # Define the system of units to use internally. InternalUnitSystem: UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams @@ -21,7 +24,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter Scheduler: links_per_tasks: 500 @@ -49,10 +52,13 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). + theta: 0.7 # Opening angle (Multipole acceptance criterion) mesh_side_length: 64 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). + # Parameters for the hydrodynamics scheme SPH: @@ -61,6 +67,13 @@ SPH: CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 + overwrite_birth_time: 1 + birth_time: 0.33333 # Pretend all the stars were born at z = 2 + # Parameters for the Friends-Of-Friends algorithm FOF: basename: fof_output # Filename for the FOF outputs. @@ -169,3 +182,4 @@ EAGLEAGN: 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. + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. \ No newline at end of file diff --git a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml index e2a98725a94c9fd34b45a95530f480562c0aaec8..ea46a9ad677d8e37ec48a83645a4501e8bdc842f 100644 --- a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml @@ -1,3 +1,6 @@ +MetaData: + run_name: EAGLE-L0050N0752-Ref + # Define the system of units to use internally. InternalUnitSystem: UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams @@ -13,7 +16,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter # Parameters governing the time integration TimeIntegration: @@ -42,9 +45,11 @@ Statistics: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). mesh_side_length: 128 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: @@ -53,6 +58,13 @@ SPH: CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 + overwrite_birth_time: 1 + birth_time: 0.33333 # Pretend all the stars were born at z = 2 + # Parameters for the Friends-Of-Friends algorithm FOF: basename: fof_output # Filename for the FOF outputs. @@ -161,3 +173,4 @@ EAGLEAGN: 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. + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. \ No newline at end of file diff --git a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml index 9136f85424beaf70b614cde0e70b67c134056831..27082dd0b881279c6631dfdc1edb0ac8ea3d07c6 100644 --- a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml +++ b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml @@ -1,3 +1,6 @@ +MetaData: + run_name: EAGLE-L0006N0094-Ref + # Define the system of units to use internally. InternalUnitSystem: UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams @@ -21,7 +24,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter Scheduler: max_top_level_cells: 8 @@ -52,9 +55,11 @@ Statistics: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). mesh_side_length: 16 + comoving_DM_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_DM_softening: 0.0007 # Max physical DM softening length (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). + max_physical_baryon_softening: 0.0007 # Max physical DM softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: @@ -63,6 +68,13 @@ SPH: CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.1642 # Target smoothing length in units of the mean inter-particle separation + h_tolerance: 7e-3 + overwrite_birth_time: 1 + birth_time: 0.33333 # Pretend all the stars were born at z = 2 + # Parameters for the Friends-Of-Friends algorithm FOF: basename: fof_output # Filename for the FOF outputs. @@ -171,3 +183,4 @@ EAGLEAGN: 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. + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. \ No newline at end of file diff --git a/examples/GiantImpacts/GiantImpact/uranus_1e6.yml b/examples/GiantImpacts/GiantImpact/uranus_1e6.yml index 355748d847097623f171078c2ca8372e06a06efa..b98467616f659babb488352c87697a43fedcc0db 100644 --- a/examples/GiantImpacts/GiantImpact/uranus_1e6.yml +++ b/examples/GiantImpacts/GiantImpact/uranus_1e6.yml @@ -48,8 +48,8 @@ SPH: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.003 # Comoving softening length (in internal units). - max_physical_softening: 0.003 # Physical softening length (in internal units). + comoving_baryon_softening: 0.003 # Comoving softening length (in internal units). + max_physical_baryon_softening: 0.003 # Physical softening length (in internal units). # Parameters for the task scheduling Scheduler: diff --git a/examples/HydroTests/BlobTest_3D/makeMovie.py b/examples/HydroTests/BlobTest_3D/makeMovie.py index 9ae4a538e000fa7006f093b67d325ff433e97089..55acdaab01e22ecf8bae0dd24967a348ceabf34b 100644 --- a/examples/HydroTests/BlobTest_3D/makeMovie.py +++ b/examples/HydroTests/BlobTest_3D/makeMovie.py @@ -24,7 +24,7 @@ resolution = 1024 snapshot_name = "blob" cmap = "Spectral_r" text_args = dict(color="black") -# plot = "pressure" +# plot = "pressures" # name = "Pressure $P$" plot = "density" name = "Fluid Density $\\rho$" diff --git a/examples/HydroTests/BlobTest_3D/makeSliceMovie.py b/examples/HydroTests/BlobTest_3D/makeSliceMovie.py new file mode 100644 index 0000000000000000000000000000000000000000..726c4ac01b9ae5b046a8bb3b6c1b9206764907bb --- /dev/null +++ b/examples/HydroTests/BlobTest_3D/makeSliceMovie.py @@ -0,0 +1,190 @@ +""" +Makes a movie of the output of the blob test. + +Josh Borrow (joshua.borrow@durham.ac.uk) 2019 + +LGPLv3 +""" + +from swiftsimio import load +from swiftsimio.visualisation import slice + +from p_tqdm import p_map + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.colors import LogNorm +from matplotlib.animation import FuncAnimation + +info_frames = 15 +start_frame = 0 +end_frame = 101 +resolution = 1024 +snapshot_name = "blob" +cmap = "Spectral_r" +text_args = dict(color="black") +# plot = "pressure" +# name = "Pressure $P$" +plot = "density" +name = "Fluid Density $\\rho$" + + +def get_image(n): + """ + Gets the image for snapshot n, and also returns the associated + SWIFT metadata object. + """ + filename = f"{snapshot_name}_{n:04d}.hdf5" + + data = load(filename) + boxsize = data.metadata.boxsize[0].value + + output = np.zeros((resolution, resolution * 4), dtype=float) + + x, y, z = data.gas.coordinates.value.T + + # This is an oblong box but we can only make squares! + for box, box_edges in enumerate([[0.0, 1.1], [0.9, 2.1], [1.9, 3.1], [2.9, 4.0]]): + mask = np.logical_and(x >= box_edges[0], x <= box_edges[1]) + masked_x = x[mask] - np.float64(box) + masked_y = y[mask] + masked_z = z[mask] + + hsml = data.gas.smoothing_length.value[mask] + + if plot == "density": + mass = data.gas.masses.value[mask] + image = slice( + x=masked_y, + y=masked_x, + z=masked_z, + m=mass, + h=hsml, + z_slice=0.5, + res=resolution, + ) + else: + quantity = getattr(data.gas, plot).value[mask] + # Need to divide out the particle density for non-projected density quantities + image = scatter( + x=masked_y, + y=masked_x, + z=masked_z, + m=quantity, + h=hsml, + z_slice=0.5, + res=resolution, + ) / scatter( + x=masked_y, + y=masked_x, + z=masked_z, + m=np.ones_like(quantity), + h=hsml, + z_slice=0.5, + res=resolution, + ) + + output[:, box * resolution : (box + 1) * resolution] = image + + return output, data.metadata + + +def get_data_dump(metadata): + """ + Gets a big data dump from the SWIFT metadata + """ + + try: + viscosity = metadata.viscosity_info + except: + viscosity = "No info" + + try: + diffusion = metadata.diffusion_info + except: + diffusion = "No info" + + output = ( + "$\\bf{Blob}$ $\\bf{Test}$\n\n" + "$\\bf{SWIFT}$\n" + + metadata.code_info + + "\n\n" + + "$\\bf{Compiler}$\n" + + metadata.compiler_info + + "\n\n" + + "$\\bf{Hydrodynamics}$\n" + + metadata.hydro_info + + "\n\n" + + "$\\bf{Viscosity}$\n" + + viscosity + + "\n\n" + + "$\\bf{Diffusion}$\n" + + diffusion + ) + + return output + + +def time_formatter(metadata): + return f"$t = {metadata.t:2.2f}$" + + +# Generate the frames and unpack our variables +images, metadata = zip(*p_map(get_image, list(range(start_frame, end_frame)))) + +# The edges are funny because of the non-periodicity. +central_region = images[0][ + resolution // 10 : resolution - resolution // 10, + resolution // 10 : resolution - resolution // 10, +] +norm = LogNorm(vmin=np.min(central_region), vmax=np.max(central_region), clip="black") + +fig, ax = plt.subplots(figsize=(8 * 4, 8), dpi=resolution // 8) + +fig.subplots_adjust(0, 0, 1, 1) +ax.axis("off") + +# Set up the initial state +image = ax.imshow(np.zeros_like(images[0]), norm=norm, cmap=cmap, origin="lower") + +description_text = ax.text( + 0.5, + 0.5, + get_data_dump(metadata[0]), + va="center", + ha="center", + **text_args, + transform=ax.transAxes, +) + +time_text = ax.text( + (1 - 0.025 * 0.25), + 0.975, + time_formatter(metadata[0]), + **text_args, + va="top", + ha="right", + transform=ax.transAxes, +) + +info_text = ax.text( + 0.025 * 0.25, 0.975, name, **text_args, va="top", ha="left", transform=ax.transAxes +) + + +def animate(n): + # Display just our original frames at t < 0 + if n >= 0: + image.set_array(images[n]) + description_text.set_text("") + time_text.set_text(time_formatter(metadata[n])) + + return (image,) + + +animation = FuncAnimation( + fig, animate, range(start_frame - info_frames, end_frame), interval=40 +) + +animation.save("blob_slice.mp4") diff --git a/examples/HydroTests/Diffusion_1D/plotSolution.py b/examples/HydroTests/Diffusion_1D/plotSolution.py index 66c8ffc6418f06589a2918ae4d8ed460b0081972..a394e919b4e2e80728c97499c3c3544256a6ad2c 100644 --- a/examples/HydroTests/Diffusion_1D/plotSolution.py +++ b/examples/HydroTests/Diffusion_1D/plotSolution.py @@ -24,6 +24,7 @@ import numpy as np try: from scipy.integrate import solve_ivp + solve_ode = True except: solve_ode = False @@ -32,6 +33,7 @@ from swiftsimio import load matplotlib.use("Agg") + def solve_analytic(u_0, u_1, t_0, t_1, alpha=0.1): """ Solves the analytic equation: @@ -63,7 +65,12 @@ def solve_analytic(u_0, u_1, t_0, t_1, alpha=0.1): return np.array([-1.0 * common, 1.0 * common]) - ret = solve_ivp(gradient, t_span=[t_0.value, t_1.value], y0=[u_0.value, u_1.value], t_eval=np.linspace(t_0.value, t_1.value, 100)) + ret = solve_ivp( + gradient, + t_span=[t_0.value, t_1.value], + y0=[u_0.value, u_1.value], + t_eval=np.linspace(t_0.value, t_1.value, 100), + ) t = ret.t high = ret.y[1] @@ -81,7 +88,7 @@ def get_data_dump(metadata): viscosity = metadata.viscosity_info except: viscosity = "No info" - + try: diffusion = metadata.diffusion_info except: @@ -136,6 +143,7 @@ def setup_axes(size=[8, 8], dpi=300): return fig, ax + def mean_std_max_min(data): """ Returns: @@ -157,7 +165,7 @@ def extract_plottables_u(data_list): """ data = [ - np.diff(x.gas.internal_energy.value) / np.mean(x.gas.internal_energy.value) + np.diff(x.gas.internal_energies.value) / np.mean(x.gas.internal_energies.value) for x in data_list ] @@ -175,8 +183,10 @@ def extract_plottables_x(data_list): dx = boxsize / n_part original_x = np.arange(n_part, dtype=float) * dx + (0.5 * dx) - - deviations = [1e6 * abs(original_x - x.gas.coordinates.value[:, 0]) / dx for x in data_list] + + deviations = [ + 1e6 * abs(original_x - x.gas.coordinates.value[:, 0]) / dx for x in data_list + ] return mean_std_max_min(deviations) @@ -187,7 +197,7 @@ def extract_plottables_rho(data_list): mean, stdev, max, min * 1e6 deviations from mean density """ - P = [x.gas.density.value for x in data_list] + P = [x.gas.densities.value for x in data_list] mean_P = [np.mean(x) for x in P] deviations = [1e6 * (x - y) / x for x, y in zip(mean_P, P)] @@ -241,28 +251,34 @@ def make_plot(start: int, stop: int, handle: str): if solve_ode: times_ode, diff = solve_analytic( - u_0=data_list[0].gas.internal_energy.min(), - u_1=data_list[0].gas.internal_energy.max(), + u_0=data_list[0].gas.internal_energies.min(), + u_1=data_list[0].gas.internal_energies.max(), t_0=t[0], t_1=t[-1], alpha=( - np.sqrt(5.0/3.0 * (5.0/3.0 - 1.0)) * - alpha / data_list[0].gas.smoothing_length[0].value - ) + np.sqrt(5.0 / 3.0 * (5.0 / 3.0 - 1.0)) + * alpha + / data_list[0].gas.smoothing_length[0].value + ), ) ax[1].plot( times_ode, - (diff) / np.mean(data_list[0].gas.internal_energy), + (diff) / np.mean(data_list[0].gas.internal_energies), label="Analytic", linestyle="dotted", - c="C3" + c="C3", ) - #import pdb;pdb.set_trace() + # import pdb;pdb.set_trace() ax[2].fill_between( - t, x_means - x_stdevs, x_means + x_stdevs, color="C0", alpha=0.5, edgecolor="none" + t, + x_means - x_stdevs, + x_means + x_stdevs, + color="C0", + alpha=0.5, + edgecolor="none", ) ax[2].plot(t, x_means, label="Mean", c="C0") ax[2].plot(t, x_maxs, label="Max", linestyle="dashed", c="C1") @@ -270,11 +286,18 @@ def make_plot(start: int, stop: int, handle: str): try: # Give diffusion info a go; this may not be present - diff_means, diff_stdevs, diff_maxs, diff_mins = extract_plottables_diff(data_list) + diff_means, diff_stdevs, diff_maxs, diff_mins = extract_plottables_diff( + data_list + ) ax[3].set_ylabel(r"Diffusion parameter $\alpha_{diff}$") ax[3].fill_between( - t, diff_means - diff_stdevs, diff_means + diff_stdevs, color="C0", alpha=0.5, edgecolor="none" + t, + diff_means - diff_stdevs, + diff_means + diff_stdevs, + color="C0", + alpha=0.5, + edgecolor="none", ) ax[3].plot(t, diff_means, label="Mean", c="C0") ax[3].plot(t, diff_maxs, label="Max", linestyle="dashed", c="C1") @@ -284,9 +307,16 @@ def make_plot(start: int, stop: int, handle: str): # Diffusion info must not be present. rho_means, rho_stdevs, rho_maxs, rho_mins = extract_plottables_rho(data_list) - ax[3].set_ylabel("Deviation from mean density $(\\rho_i - \\bar{\\rho}) / \\bar{\\rho}$ [$\\times 10^{6}$]") + ax[3].set_ylabel( + "Deviation from mean density $(\\rho_i - \\bar{\\rho}) / \\bar{\\rho}$ [$\\times 10^{6}$]" + ) ax[3].fill_between( - t, rho_means - rho_stdevs, rho_means + rho_stdevs, color="C0", alpha=0.5, edgecolor="none" + t, + rho_means - rho_stdevs, + rho_means + rho_stdevs, + color="C0", + alpha=0.5, + edgecolor="none", ) ax[3].plot(t, rho_means, label="Mean", c="C0") ax[3].plot(t, rho_maxs, label="Max", linestyle="dashed", c="C1") diff --git a/examples/HydroTests/EvrardCollapse_3D/plotSolution.py b/examples/HydroTests/EvrardCollapse_3D/plotSolution.py index 8422b9c45fd573f3d0ae36324d6e39ab23cceb25..b405771a4792e5ca4fef181b3787952bf0078d67 100644 --- a/examples/HydroTests/EvrardCollapse_3D/plotSolution.py +++ b/examples/HydroTests/EvrardCollapse_3D/plotSolution.py @@ -75,10 +75,10 @@ x = sqrt((coords[:,0] - 0.5 * boxSize)**2 + (coords[:,1] - 0.5 * boxSize)**2 + \ (coords[:,2] - 0.5 * boxSize)**2) vels = sim["/PartType0/Velocities"] v = sqrt(vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2) -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] # Bin the data x_bin_edge = logspace(-3., log10(2.), 100) diff --git a/examples/HydroTests/Gradients/plot.py b/examples/HydroTests/Gradients/plot.py index d6750ffc581437ebbf402ec44bcb1d14cb82a698..7b4248c9fc9c9d6b9c5b15410185d6489849bed8 100644 --- a/examples/HydroTests/Gradients/plot.py +++ b/examples/HydroTests/Gradients/plot.py @@ -30,7 +30,7 @@ inputfile = sys.argv[1] outputfile = "gradients_{0}.png".format(sys.argv[2]) f = h5py.File(inputfile, "r") -rho = np.array(f["/PartType0/Density"]) +rho = np.array(f["/PartType0/Densities"]) gradrho = np.array(f["/PartType0/GradDensity"]) coords = np.array(f["/PartType0/Coordinates"]) diff --git a/examples/HydroTests/GreshoVortex_2D/plotSolution.py b/examples/HydroTests/GreshoVortex_2D/plotSolution.py index d497a6b297bf38b39cf85a9107a769c20f815b77..2d4697b6ffaac0639da67ee90d824c75791ea573 100644 --- a/examples/HydroTests/GreshoVortex_2D/plotSolution.py +++ b/examples/HydroTests/GreshoVortex_2D/plotSolution.py @@ -100,10 +100,10 @@ r = sqrt(x**2 + y**2) v_r = (x * vel[:,0] + y * vel[:,1]) / r v_phi = (-y * vel[:,0] + x * vel[:,1]) / r v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2) -rho = sim["/PartType0/Density"][:] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Densities"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] # Bin te data r_bin_edge = np.arange(0., 1., 0.02) diff --git a/examples/HydroTests/GreshoVortex_3D/plotSolution.py b/examples/HydroTests/GreshoVortex_3D/plotSolution.py index 545440c997d9ebc3ab11562d0a7d9fa143e23ed2..20beab7514759c764f5ca7c379183506b764a819 100644 --- a/examples/HydroTests/GreshoVortex_3D/plotSolution.py +++ b/examples/HydroTests/GreshoVortex_3D/plotSolution.py @@ -103,19 +103,19 @@ r = sqrt(x**2 + y**2) v_r = (x * vel[:,0] + y * vel[:,1]) / r v_phi = (-y * vel[:,0] + x * vel[:,1]) / r v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2) -rho = sim["/PartType0/Density"][:] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Densities"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] try: - diffusion = sim["/PartType0/Diffusion"][:] + diffusion = sim["/PartType0/DiffusionParameters"][:] plot_diffusion = True except: plot_diffusion = False try: - viscosity = sim["/PartType0/Viscosity"][:] + viscosity = sim["/PartType0/ViscosityParameters"][:] plot_viscosity = True except: plot_viscosity = False diff --git a/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py b/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py index 1719162dec34626d6f4ecb8267c4d06f85b3db26..d617fb239ce21acab73b5cb057dd3cdf4b260d59 100644 --- a/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py +++ b/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py @@ -55,11 +55,11 @@ snap = int(sys.argv[1]) # Open the file and read the relevant data file = h5py.File("interactingBlastWaves_{0:04d}.hdf5".format(snap), "r") x = file["/PartType0/Coordinates"][:,0] -rho = file["/PartType0/Density"] +rho = file["/PartType0/Densities"] v = file["/PartType0/Velocities"][:,0] -u = file["/PartType0/InternalEnergy"] -S = file["/PartType0/Entropy"] -P = file["/PartType0/Pressure"] +u = file["/PartType0/InternalEnergies"] +S = file["/PartType0/Entropies"] +P = file["/PartType0/Pressures"] time = file["/Header"].attrs["Time"][0] scheme = file["/HydroScheme"].attrs["Scheme"] diff --git a/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py b/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py index a52784891ab4689dcd59dc27945e573e602785f3..e40ba44dedb6e43dd25f0ce7e0b5681e61048888 100644 --- a/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py +++ b/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py @@ -71,7 +71,7 @@ if __name__ == "__main__": import matplotlib.pyplot as plt - filename = "kelvinhelmholtz" + filename = "kelvinHelmholtz" dpi = 512 @@ -93,7 +93,7 @@ if __name__ == "__main__": fig, ax = plt.subplots(1, 1, figsize=(1, 1), frameon=False) ax.axis("off") # Remove annoying black frame. - data_x, data_y, density = load_and_extract("kelvinhelmholtz_0000.hdf5") + data_x, data_y, density = load_and_extract("kelvinHelmholtz_0000.hdf5") x = np.linspace(0, 1, dpi) y = np.linspace(0, 1, dpi) diff --git a/examples/HydroTests/KelvinHelmholtz_2D/makeMovieSwiftsimIO.py b/examples/HydroTests/KelvinHelmholtz_2D/makeMovieSwiftsimIO.py new file mode 100644 index 0000000000000000000000000000000000000000..a86445e0a369bb3697793be72a3053d20f597e45 --- /dev/null +++ b/examples/HydroTests/KelvinHelmholtz_2D/makeMovieSwiftsimIO.py @@ -0,0 +1,102 @@ +""" +Makes a movie of the KH 2D data. + +You will need to run your movie with far higher time-resolution than usual to +get a nice movie; around 450 snapshots over 6s is required. + +Edit this file near the bottom with the number of snaps you have. + +Written by Josh Borrow (joshua.borrow@durham.ac.uk) +""" + +import os +import h5py as h5 +import numpy as np +import scipy.interpolate as si + +from swiftsimio import load +from swiftsimio.visualisation import project_gas_pixel_grid + +def load_and_extract(filename): + """ + Load the data and extract relevant info. + """ + + return load(filename) + + +def make_plot(filename, array, nx, ny, dx, dy): + """ + Load the data and plop it on the grid using nearest + neighbour searching for finding the 'correct' value of + the density. + """ + + data = load_and_extract(filename) + + mesh = project_gas_pixel_grid(data, nx) + + array.set_array(mesh) + + return array, + + +def frame(n, *args): + """ + Make a single frame. Requires the global variables plot and dpi. + """ + + global plot, dpi + + fn = "{}_{:04d}.hdf5".format(filename, n) + + return make_plot(fn, plot, dpi, dpi, (0, 1), (0, 1)) + + +if __name__ == "__main__": + import matplotlib + matplotlib.use("Agg") + + from tqdm import tqdm + from matplotlib.animation import FuncAnimation + from scipy.stats import gaussian_kde + + import matplotlib.pyplot as plt + + filename = "kelvinHelmholtz" + dpi = 512 + + + # Look for the number of files in the directory. + i = 0 + while True: + if os.path.isfile("{}_{:04d}.hdf5".format(filename, i)): + i += 1 + else: + break + + if i > 10000: + raise FileNotFoundError( + "Could not find the snapshots in the directory") + + frames = tqdm(np.arange(0, i)) + + # Creation of first frame + fig, ax = plt.subplots(1, 1, figsize=(1, 1), frameon=False) + ax.axis("off") # Remove annoying black frame. + + data = load_and_extract("kelvinHelmholtz_0000.hdf5") + + + mesh = project_gas_pixel_grid(data, dpi) + + # Global variable for set_array + plot = ax.imshow(mesh, extent=[0, 1, 0, 1], animated=True, interpolation="none") + + anim = FuncAnimation(fig, frame, frames, interval=40, blit=False) + + # Remove all whitespace + fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=None, hspace=None) + + # Actually make the movie + anim.save("khmovie.mp4", dpi=dpi, bitrate=4096) diff --git a/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py b/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py index 77ab6fb244da25d13760f90653fac7eac11a0ee7..f599fcb784633b2d6765ea79767fc658196faa5f 100644 --- a/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py +++ b/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py @@ -77,10 +77,10 @@ x = pos[:,0] - boxSize / 2 y = pos[:,1] - boxSize / 2 vel = sim["/PartType0/Velocities"][:,:] v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2) -rho = sim["/PartType0/Density"][:] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Densities"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] # Plot the interesting quantities figure() diff --git a/examples/HydroTests/KelvinHelmholtz_2D/run.sh b/examples/HydroTests/KelvinHelmholtz_2D/run.sh index 355bf052a7ad124bcb4d88254ad780a7ffa97aba..ee3cdc4f542e3be1f28ba0deff279c612b9c49b0 100755 --- a/examples/HydroTests/KelvinHelmholtz_2D/run.sh +++ b/examples/HydroTests/KelvinHelmholtz_2D/run.sh @@ -10,6 +10,6 @@ fi # Run SWIFT ../../swift --hydro --threads=4 kelvinHelmholtz.yml 2>&1 | tee output.log + # Plot the solution -python plotSolution.py 6 -python makeMovie.py +python3 makeMovieSwiftsimIO.py diff --git a/examples/HydroTests/Noh_1D/plotSolution.py b/examples/HydroTests/Noh_1D/plotSolution.py index 25b9b2f16b24cba5def592a5cf00dbae82195ef7..7f0b5d403ef816b0dda57823010472476a7ecc32 100644 --- a/examples/HydroTests/Noh_1D/plotSolution.py +++ b/examples/HydroTests/Noh_1D/plotSolution.py @@ -69,10 +69,10 @@ git = sim["Code"].attrs["Git Revision"] x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] N = 1001 # Number of points x_min = -1 diff --git a/examples/HydroTests/Noh_2D/plotSolution.py b/examples/HydroTests/Noh_2D/plotSolution.py index 775ddf4e8a7954c14034ad51a6b66622c41a6996..b53212c4688ec790ad8f3f83f81243f9ec52266d 100644 --- a/examples/HydroTests/Noh_2D/plotSolution.py +++ b/examples/HydroTests/Noh_2D/plotSolution.py @@ -71,10 +71,10 @@ x = sim["/PartType0/Coordinates"][:,0] y = sim["/PartType0/Coordinates"][:,1] vx = sim["/PartType0/Velocities"][:,0] vy = sim["/PartType0/Velocities"][:,1] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] r = np.sqrt((x-1)**2 + (y-1)**2) v = -np.sqrt(vx**2 + vy**2) diff --git a/examples/HydroTests/Noh_3D/plotSolution.py b/examples/HydroTests/Noh_3D/plotSolution.py index 386b9f728b5e8d8e38fb7ec9aeaa336d194e35dd..20e8ca805de1cb700b8b462ae27495080f5d3268 100644 --- a/examples/HydroTests/Noh_3D/plotSolution.py +++ b/examples/HydroTests/Noh_3D/plotSolution.py @@ -74,10 +74,10 @@ z = sim["/PartType0/Coordinates"][:,2] vx = sim["/PartType0/Velocities"][:,0] vy = sim["/PartType0/Velocities"][:,1] vz = sim["/PartType0/Velocities"][:,2] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] r = np.sqrt((x-1)**2 + (y-1)**2 + (z-1)**2) v = -np.sqrt(vx**2 + vy**2 + vz**2) diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/README b/examples/HydroTests/Rayleigh-Taylor_2D/README new file mode 100644 index 0000000000000000000000000000000000000000..b1ac67c0c24d98d6b3e03cec70697114bb2fb434 --- /dev/null +++ b/examples/HydroTests/Rayleigh-Taylor_2D/README @@ -0,0 +1,17 @@ +Rayleigh Taylor +--------------- + +This is a common test for hydrodynamics (see Abel 2011, Saitoh and Makino 2013, Hopkins 2013). +It consists in a low density layer of fluid at the bottom and a high density layer at the top. +Due to a constant vertical gravitational acceleration, the two fluids mix togerther through the +Rayleigh Taylor instability (e.g. nuclear mushroom cloud). + +In this example, we follow the implementation of Saitoh and Makino 2013, but with a longer box in order +to avoid an interaction with the wall (see last image in Figure 10). + +The code needs to be compiled with the following options: `--with-hydro-dimension=2`, +`--with-ext-potential=constant`, `--enable-boundary-particles` and `--with-adiabatic-index=7/5`. +I also recommend to use `--with-kernel=wendland-C2`. + +The option `--enable-boundary-particles` requires the ID of the last boundary particle. +This value is provided by the script makeIC.py and depends on the resolution. diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/generate_movie_from_png.sh b/examples/HydroTests/Rayleigh-Taylor_2D/generate_movie_from_png.sh new file mode 100644 index 0000000000000000000000000000000000000000..f97e447dc21abeabac6fe98eb23e17efd9051cf4 --- /dev/null +++ b/examples/HydroTests/Rayleigh-Taylor_2D/generate_movie_from_png.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +d1=gizmo/png +d2=anarchy/png +d3=pu/png + +for i in {0..1040} +do + echo $i + tmp=`printf "%04i" $i` + convert $d1/rayleigh_taylor_$tmp.png $d2/rayleigh_taylor_$tmp.png $d3/rayleigh_taylor_$tmp.png +append movie/rayleigh_taylor_$tmp.png +done + +ffmpeg -framerate 10 -i movie/rayleigh_taylor_%04d.png -c:v libx264 -r 30 -pix_fmt yuv420p rayleigh_taylor.mp4 diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/makeIC.py b/examples/HydroTests/Rayleigh-Taylor_2D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..d75d5f215f8b71d859693b842a1e637b52095f45 --- /dev/null +++ b/examples/HydroTests/Rayleigh-Taylor_2D/makeIC.py @@ -0,0 +1,250 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2019 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/>. +# +############################################################################## + +import h5py +import numpy as np +from scipy.optimize import bisect + +# Generates a swift IC file for the Rayleigh-Taylor instability in a periodic +# box following the conditions given in Saitoh and Makino 2013: 1202.4277v3 + +# Parameters +N = [128, 192] # Particles along one edge +gamma = 7./5. # Gas adiabatic index +dv = 0.025 # velocity perturbation +rho_h = 2 # high density region +rho_l = 1 # Low density region +g = -0.5 # gravitational acceleration +box_size = [1., 1.5] # size of the box + +fixed = [0.1, 1.4] # y-range of non fixed particles +perturbation_box = [0.3, 1.2] # y-range for the velocity perturbation +fileOutputName = "rayleigh_taylor.hdf5" + + +# --------------------------------------------------- + +if (N[0] / box_size[0] != N[1] / box_size[1]): + raise Exception("Suppose the same ratio for each directions") + + +def density(y): + """ + Mass density as function of the position y. + """ + if isinstance(y, float) or isinstance(y, int): + y = np.array(y) + + ind = y < 0.5 * box_size[1] + rho = np.zeros(y.shape) + tmp = (gamma - 1.) * g / (gamma * P0) + alpha = 1. / (gamma - 1.) + + rho[ind] = rho_l * (1 + rho_l * tmp * (y[ind] - 0.5 * box_size[1]))**alpha + rho[~ind] = rho_h * (1 + rho_h * tmp * (y[~ind] - 0.5 * box_size[1]))**alpha + + return rho + + +def mass(y): + """ + Integral of the density + """ + if isinstance(y, float) or isinstance(y, int): + y = np.array(y) + + ind = y < 0.5 * box_size[1] + m = np.zeros(y.shape) + + B = (gamma - 1.) * g / (gamma * P0) + alpha = 1. / (gamma - 1.) + + m[ind] = (1 + B * rho_l * (y[ind] - 0.5 * box_size[1]))**(alpha + 1) + + m[~ind] = (1 + B * rho_h * (y[~ind] - 0.5 * box_size[1]))**(alpha + 1) + + m -= (1 - 0.5 * B * box_size[1] * rho_l)**(alpha + 1) + + return box_size[0] * m / (B * (alpha + 1)) + + +P0 = rho_h / gamma # integration constant of the hydrostatic equation +numPart = N[0] * N[1] + +m_tot = mass(box_size[1]) +m_part = m_tot / numPart + + +def inv_mass(m): + """ + Inverse of the function `mass`. + """ + left = 0 + right = box_size[1] + + def f(y, x): + return x - mass(y) + + return bisect(f, left, right, args=(m)) + + +def entropy(y): + """ + Entropy as function of the position y. + Here we assume isoentropic medium. + """ + if isinstance(y, float) or isinstance(y, int): + y = np.array(y) + + ind = y < 0.5 * box_size[1] + + a = np.zeros(y.shape) + a[ind] = P0 * rho_l**(-gamma) + a[~ind] = P0 * rho_h**(-gamma) + return a + + +def smoothing_length(rho, m): + """ + Compute the smoothing length + """ + return 1.23 * np.sqrt(m / rho) + + +def growth_rate(): + """ + Compute the growth rate of the instability. + Assumes a wavelength equal to the boxsize. + """ + ymin = 0. + ymax = box_size[1] + A = density(ymax) - density(ymin) + A /= density(ymax) + density(ymin) + return np.sqrt(A * np.abs(g) * ymax / (2. * np.pi)) + + +def vy(x, y): + """ + Velocity along the direction y + """ + ind = y > perturbation_box[0] + ind = np.logical_and(ind, y < perturbation_box[1]) + + v = np.zeros(len(x)) + + v[ind] = 1 + np.cos(4 * np.pi * x[ind]) + v[ind] *= 1 + np.cos(5 * np.pi * (y[ind] - 0.5 * box_size[1])) + v[ind] *= dv + return v + + +if __name__ == "__main__": + # Start by generating grids of particles + + coords = np.zeros((numPart, 3)) + m = np.ones(numPart) * m_part + u = np.zeros(numPart) + vel = np.zeros((numPart, 3)) + ids = np.zeros(numPart) + + # generate grid of particles + y_prev = 0 + uni_id = 1 + + # loop over y + eps = 1e-3 * box_size[1] / N[1] + for j in range(N[1]): + m_y = m_part * N[0] + mass(y_prev) + if m_y > m_tot: + y_j = box_size[1] - eps * (box_size[1] - y_prev) + else: + y_j = inv_mass(m_y) + y_prev = y_j + + # loop over x + for i in range(N[0]): + + index = j * N[0] + i + + x = i * box_size[0] / float(N[0]) + + coords[index, 0] = x + coords[index, 1] = y_j + if (y_j < fixed[0] or y_j > fixed[1]): + ids[index] = uni_id + uni_id += 1 + + print("You need to compile the code with " + "--enable-boundary-particles=%i" % uni_id) + ids[ids == 0] = np.linspace(uni_id, numPart, numPart-uni_id+1) + + # density + rho = density(coords[:, 1]) + + # internal energy + a = entropy(coords[:, 1]) + u = a * rho**(gamma-1) / (gamma - 1.) + + # smoothing length + h = smoothing_length(rho, m) + + # Velocity perturbation + vel[:, 1] = vy(coords[:, 0], coords[:, 1]) + + # File + fileOutput = h5py.File(fileOutputName, 'w') + + # Header + grp = fileOutput.create_group("/Header") + grp.attrs["BoxSize"] = box_size + grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] + grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] + grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] + grp.attrs["Time"] = 0.0 + grp.attrs["NumFileOutputsPerSnapshot"] = 1 + grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + grp.attrs["Flag_Entropy_ICs"] = 0 + grp.attrs["Dimension"] = 2 + + # Units + grp = fileOutput.create_group("/Units") + grp.attrs["Unit length in cgs (U_L)"] = 1. + grp.attrs["Unit mass in cgs (U_M)"] = 1. + grp.attrs["Unit time in cgs (U_t)"] = 1. + grp.attrs["Unit current in cgs (U_I)"] = 1. + grp.attrs["Unit temperature in cgs (U_T)"] = 1. + + # Particle group + grp = fileOutput.create_group("/PartType0") + ds = grp.create_dataset('Coordinates', (numPart, 3), 'd') + ds[()] = coords + ds = grp.create_dataset('Velocities', (numPart, 3), 'f') + ds[()] = vel + ds = grp.create_dataset('Masses', (numPart, 1), 'f') + ds[()] = m.reshape((numPart, 1)) + ds = grp.create_dataset('SmoothingLength', (numPart, 1), 'f') + ds[()] = h.reshape((numPart, 1)) + ds = grp.create_dataset('InternalEnergy', (numPart, 1), 'f') + ds[()] = u.reshape((numPart, 1)) + ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L') + ds[()] = ids.reshape((numPart, 1)) + ds = grp.create_dataset('Density', (numPart, 1), 'f') + ds[()] = rho.reshape((numPart, 1)) + + fileOutput.close() diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/makeMovie.py b/examples/HydroTests/Rayleigh-Taylor_2D/makeMovie.py new file mode 100644 index 0000000000000000000000000000000000000000..b00c2946994e0e3d8e93b0824a4bc44b9079967e --- /dev/null +++ b/examples/HydroTests/Rayleigh-Taylor_2D/makeMovie.py @@ -0,0 +1,280 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) +# Josh Borrow (joshua.borrow@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/>. +# +############################################################################## + +from swiftsimio import load +from swiftsimio.visualisation import scatter +from matplotlib.animation import FuncAnimation +from matplotlib.colors import LogNorm +import matplotlib.pyplot as plt + +from numpy import max, min +import numpy as np + +try: + # Try and load this, otherwise we're stuck with serial + from p_tqdm import p_map +except: + print("Try installing the p_tqdm package to make movie frames in parallel") + p_map = map + pass + + +info_frames = 40 +generate_png = False +text_args = dict(color="black") + + +class Metadata(object): + """ + Copy the useful data in order to decrease the memory usage + """ + def __init__(self, data): + metadata = data.metadata + self.t = metadata.t + try: + self.viscosity_info = metadata.viscosity_info + except: + self.viscosity_info = "No info" + try: + self.diffusion_info = metadata.diffusion_info + except: + self.diffusion_info = "No info" + + self.code_info = metadata.code_info + self.compiler_info = metadata.compiler_info + self.hydro_info = metadata.hydro_info + + +def project(data, m_res, property, ylim): + x, y, _ = data.gas.coordinates.value.T + + mask = np.logical_and(y >= ylim[0], y <= ylim[1]) + + x = x[mask] + y = y[mask] - np.float64(ylim[0]) + + h = data.gas.smoothing_length[mask] + + if property == "density": + property = "masses" + + if property is not None: + quant = getattr(data.gas, property).value[mask] + else: + quant = np.ones_like(x) + + image = scatter(x=x, y=y, m=quant, h=h, res=m_res) + + return image.T + + +def load_and_make_image(filename, res, property): + image = np.zeros(res, dtype=np.float32) + m_res = min(res) + border = int(0.2 * m_res) + + # first part of the image + ylim = np.array([0., 1.]) + + data = load(filename) + image[:m_res, :m_res] = project(data, m_res, property, ylim) + if property != "density": + image[:m_res, :m_res] /= project(data, m_res, None, ylim) + + # second part of the image + ylim = np.array([0.5, 1.5]) + + left = -m_res + border + image[left:, :] = project(data, m_res, property, ylim)[left:, :] + if property != "density": + image[left:, :] /= project(data, m_res, None, ylim)[left:, :] + + metadata = Metadata(data) + return image, metadata + + +def time_formatter(metadata): + return f"$t = {metadata.t:2.2f}$" + + +def create_movie(filename, start, stop, resolution, property, output_filename): + """ + Creates the movie with: + + snapshots named {filename}_{start}.hdf5 to {filename}_{stop}.hdf5 + at {resolution} x {resolution} pixel size and smoothing the given + {property} and outputting to {output_filename}. + """ + + if property != "density": + name = property + else: + name = "Fluid Density $\\rho$" + + def baked_in_load(n): + f = filename + "_{:04d}.hdf5".format(n) + return load_and_make_image(f, resolution, property) + + # Make frames in parallel (reading also parallel!) + frames, metadata = zip(*p_map(baked_in_load, list(range(start, stop)))) + + vmax = max(list(p_map(max, frames))) + vmin = min(list(p_map(min, frames))) + + fig, ax = plt.subplots(figsize=(8, 1.5 * 8), dpi=resolution[0] // 8) + ax.axis("off") + fig.subplots_adjust(0, 0, 1, 1) + + norm = LogNorm(vmin=vmin, vmax=vmax, clip="black") + + image = ax.imshow(np.zeros_like(frames[0]), origin="lower", + norm=norm) + + description_text = ax.text( + 0.5, 0.5, + get_simulation_information(metadata[0]), + va="center", ha="center", + **text_args, transform=ax.transAxes, + ) + + time_text = ax.text( + (1 - 0.025 * 0.25), 0.975, + time_formatter(metadata[0]), + **text_args, + va="top", ha="right", + transform=ax.transAxes, + ) + + ax.text( + 0.025 * 0.25, 0.975, name, **text_args, va="top", ha="left", + transform=ax.transAxes + ) + + def frame(n): + if n >= 0: + image.set_array(frames[n]) + description_text.set_text("") + time_text.set_text(time_formatter(metadata[n])) + + if generate_png: + name = filename + "_{:04d}".format(n+info_frames) + fig.savefig(name + ".png") + + else: + return (image,) + + if generate_png: + for i in range(-info_frames, stop-start): + frame(i) + else: + animation = FuncAnimation(fig, frame, + range(-info_frames, stop-start), + interval=40) + animation.save(output_filename) + + +def get_simulation_information(metadata): + """ + Generates a string from the SWIFT metadata + """ + viscosity = metadata.viscosity_info + diffusion = metadata.diffusion_info + + output = ( + "$\\bf{Rayleigh-Taylor}$ $\\bf{Instability}$\n\n" + "$\\bf{SWIFT}$\n" + + metadata.code_info + + "\n\n" + + "$\\bf{Compiler}$\n" + + metadata.compiler_info + + "\n\n" + + "$\\bf{Hydrodynamics}$\n" + + metadata.hydro_info + + "\n\n" + + "$\\bf{Viscosity}$\n" + + viscosity + + "\n\n" + + "$\\bf{Diffusion}$\n" + + diffusion + ) + + return output + + +if __name__ == "__main__": + from argparse import ArgumentParser + + parser = ArgumentParser(description="Creates a movie of the whole box.") + + parser.add_argument( + "-s", + "--snapshot", + help="Snapshot name. Default: rayleigh_taylor", + type=str, + default="rayleigh_taylor", + ) + + parser.add_argument( + "-i", "--initial", help="Initial snapshot. Default: 0", + type=int, default=0 + ) + + parser.add_argument( + "-f", "--final", help="Final snapshot. Default: 40", + type=int, default=40 + ) + + parser.add_argument( + "-o", + "--output", + help="Output filename. Default: rayleigh_taylor.mp4", + type=str, + default="rayleigh_taylor.mp4", + ) + + parser.add_argument( + "-p", + "--property", + help="(swiftsimio) Property to plot. Default: density", + type=str, + default="density", + ) + + parser.add_argument( + "-r", + "--resolution", + help="Resolution to make the movie at. Default: 512", + type=int, + default=512, + ) + + vars = parser.parse_args() + + yres = int(1.5 * vars.resolution) + vars.resolution = [yres, vars.resolution] + + create_movie( + filename=vars.snapshot, + start=vars.initial, + stop=vars.final, + resolution=vars.resolution, + property=vars.property, + output_filename=vars.output, + ) diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/plotInitialProfile.py b/examples/HydroTests/Rayleigh-Taylor_2D/plotInitialProfile.py new file mode 100644 index 0000000000000000000000000000000000000000..8928a6e597adbe4e52f905ba95fa68f424b6cabb --- /dev/null +++ b/examples/HydroTests/Rayleigh-Taylor_2D/plotInitialProfile.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +import makeIC +import matplotlib.pyplot as plt +from swiftsimio import load +import numpy as np + + +N = 1000 +filename = "rayleigh_taylor_0000.hdf5" + +f = load(filename) + +# Get data from snapshot +x, y, _ = f.gas.coordinates.value.T +rho = f.gas.densities.value +a = f.gas.entropies.value + +# Get analytical solution +y_an = np.linspace(0, makeIC.box_size[1], N) +rho_an = makeIC.density(y_an) +a_an = makeIC.entropy(y_an) + +plt.figure() +plt.plot(a, y, ".r", label="Entropy") +plt.plot(a_an, y_an, "--r") + +plt.plot(rho, y, ".k", label="Density") +plt.plot(rho_an, y_an, "--k") + +plt.ylabel("Position") +plt.xlabel("Density / Entropy") + +plt.xlim(0, 2.5) +plt.ylim(0, makeIC.box_size[1]) +plt.legend() + +plt.show() diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/rayleigh_taylor.yml b/examples/HydroTests/Rayleigh-Taylor_2D/rayleigh_taylor.yml new file mode 100644 index 0000000000000000000000000000000000000000..9e843c49448c27ff8c1452f7e28adbc6fe9a9cd6 --- /dev/null +++ b/examples/HydroTests/Rayleigh-Taylor_2D/rayleigh_taylor.yml @@ -0,0 +1,37 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 10. # The end time of the simulation (in internal units). + dt_min: 1e-8 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: rayleigh_taylor # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.01 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./rayleigh_taylor.hdf5 # The file to read + periodic: 1 + +ConstantPotential: + g_cgs: [0, -0.5, 0] \ No newline at end of file diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/run.sh b/examples/HydroTests/Rayleigh-Taylor_2D/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..0c8d3eaab9a401c4be3a586e3b7b8900406bbdbb --- /dev/null +++ b/examples/HydroTests/Rayleigh-Taylor_2D/run.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +# Generate the initial conditions if they are not present. +if [ ! -e rayleigh_taylor.hdf5 ] +then + echo "Generating initial conditions for the Rayleigh Taylor example..." + python makeIC.py +fi + +# Run SWIFT +../../swift --hydro --external-gravity --threads=8 rayleigh_taylor.yml 2>&1 | tee output.log + +python makeMovie.py -i 0 -f 1001 diff --git a/examples/HydroTests/SedovBlast_1D/plotSolution.py b/examples/HydroTests/SedovBlast_1D/plotSolution.py index c6d4a989da7493f7b500946610eea8832696bf6f..d82ad9e94610a916d900ea93863b2881757e73b3 100644 --- a/examples/HydroTests/SedovBlast_1D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_1D/plotSolution.py @@ -78,13 +78,13 @@ x = pos[:,0] - boxSize / 2 vel = sim["/PartType0/Velocities"][:,:] r = abs(x) v_r = x * vel[:,0] / r -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] try: - alpha = sim["/PartType0/Viscosity"][:] + alpha = sim["/PartType0/ViscosityParameters"][:] plot_alpha = True except: plot_alpha = False diff --git a/examples/HydroTests/SedovBlast_2D/plotSolution.py b/examples/HydroTests/SedovBlast_2D/plotSolution.py index 2b5de6f32b8673bbc825fbb5236f4e2ab3b4f408..6f504a09c9432368ce141ec0d28c28699f5ba7f3 100644 --- a/examples/HydroTests/SedovBlast_2D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_2D/plotSolution.py @@ -80,10 +80,10 @@ y = pos[:,1] - boxSize / 2 vel = sim["/PartType0/Velocities"][:,:] r = sqrt(x**2 + y**2) v_r = (x * vel[:,0] + y * vel[:,1]) / r -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] # Bin te data r_bin_edge = np.arange(0., 0.5, 0.01) diff --git a/examples/HydroTests/SedovBlast_3D/plotSolution.py b/examples/HydroTests/SedovBlast_3D/plotSolution.py index b0f2e08441b3fa550e61602ba852228a04362fbc..fec4f1101406be5803a3f1601812d1cb85275409 100644 --- a/examples/HydroTests/SedovBlast_3D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_3D/plotSolution.py @@ -81,19 +81,19 @@ z = pos[:,2] - boxSize / 2 vel = sim["/PartType0/Velocities"][:,:] r = sqrt(x**2 + y**2 + z**2) v_r = (x * vel[:,0] + y * vel[:,1] + z * vel[:,2]) / r -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] try: - diffusion = sim["/PartType0/Diffusion"][:] + diffusion = sim["/PartType0/DiffusionParameters"][:] plot_diffusion = True except: plot_diffusion = False try: - viscosity = sim["/PartType0/Viscosity"][:] + viscosity = sim["/PartType0/ViscosityParameters"][:] plot_viscosity = True except: plot_viscosity = False diff --git a/examples/HydroTests/SineWavePotential_1D/plotSolution.py b/examples/HydroTests/SineWavePotential_1D/plotSolution.py index 3bb889aaabd3cdac0274afb09647d0e3aebb02cc..ae99d98aaaa11d9c68473b74106054963a075895 100644 --- a/examples/HydroTests/SineWavePotential_1D/plotSolution.py +++ b/examples/HydroTests/SineWavePotential_1D/plotSolution.py @@ -43,9 +43,9 @@ fileName = sys.argv[1] file = h5py.File(fileName, 'r') coords = np.array(file["/PartType0/Coordinates"]) -rho = np.array(file["/PartType0/Density"]) -P = np.array(file["/PartType0/Pressure"]) -u = np.array(file["/PartType0/InternalEnergy"]) +rho = np.array(file["/PartType0/Densities"]) +P = np.array(file["/PartType0/Pressures"]) +u = np.array(file["/PartType0/InternalEnergies"]) m = np.array(file["/PartType0/Masses"]) vs = np.array(file["/PartType0/Velocities"]) ids = np.array(file["/PartType0/ParticleIDs"]) diff --git a/examples/HydroTests/SineWavePotential_2D/plotSolution.py b/examples/HydroTests/SineWavePotential_2D/plotSolution.py index ee02f59c404db87a790465d2786e6296525e36b0..5c87b0f4f3682d486063522715517763b1035567 100644 --- a/examples/HydroTests/SineWavePotential_2D/plotSolution.py +++ b/examples/HydroTests/SineWavePotential_2D/plotSolution.py @@ -38,8 +38,8 @@ fileName = sys.argv[1] file = h5py.File(fileName, 'r') coords = np.array(file["/PartType0/Coordinates"]) -rho = np.array(file["/PartType0/Density"]) -u = np.array(file["/PartType0/InternalEnergy"]) +rho = np.array(file["/PartType0/Densities"]) +u = np.array(file["/PartType0/InternalEnergies"]) agrav = np.array(file["/PartType0/GravAcceleration"]) m = np.array(file["/PartType0/Masses"]) ids = np.array(file["/PartType0/ParticleIDs"]) diff --git a/examples/HydroTests/SineWavePotential_3D/plotSolution.py b/examples/HydroTests/SineWavePotential_3D/plotSolution.py index 13cae037b64eff4ad4fec0003bf0f5ad3ce94896..7bfa82a5990572c478976614c50107e5254f0e00 100644 --- a/examples/HydroTests/SineWavePotential_3D/plotSolution.py +++ b/examples/HydroTests/SineWavePotential_3D/plotSolution.py @@ -38,8 +38,8 @@ fileName = sys.argv[1] file = h5py.File(fileName, 'r') coords = np.array(file["/PartType0/Coordinates"]) -rho = np.array(file["/PartType0/Density"]) -u = np.array(file["/PartType0/InternalEnergy"]) +rho = np.array(file["/PartType0/Densities"]) +u = np.array(file["/PartType0/InternalEnergies"]) agrav = np.array(file["/PartType0/GravAcceleration"]) m = np.array(file["/PartType0/Masses"]) ids = np.array(file["/PartType0/ParticleIDs"]) diff --git a/examples/HydroTests/SodShockSpherical_2D/plotSolution.py b/examples/HydroTests/SodShockSpherical_2D/plotSolution.py index 57b7f7ddc64bc25df031eb0cba7547f40d46b31a..61060631eea1a7320ef207457d35031318bceccf 100644 --- a/examples/HydroTests/SodShockSpherical_2D/plotSolution.py +++ b/examples/HydroTests/SodShockSpherical_2D/plotSolution.py @@ -74,10 +74,10 @@ coords = sim["/PartType0/Coordinates"] x = sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2) vels = sim["/PartType0/Velocities"] v = sqrt(vels[:,0]**2 + vels[:,1]**2) -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] # Bin the data rho_bin,x_bin_edge,_ = \ diff --git a/examples/HydroTests/SodShockSpherical_3D/plotSolution.py b/examples/HydroTests/SodShockSpherical_3D/plotSolution.py index 539bfba799e3231bd26ae2eb39c271baa1fa6a4b..0a92f3aaf1831d67cb59dd71bc08cd8d973d9def 100644 --- a/examples/HydroTests/SodShockSpherical_3D/plotSolution.py +++ b/examples/HydroTests/SodShockSpherical_3D/plotSolution.py @@ -75,10 +75,10 @@ x = sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2 + \ (coords[:,2] - 0.5)**2) vels = sim["/PartType0/Velocities"] v = sqrt(vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2) -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] # Bin the data rho_bin,x_bin_edge,_ = \ diff --git a/examples/HydroTests/SodShock_1D/plotSolution.py b/examples/HydroTests/SodShock_1D/plotSolution.py index a7e6d374bac616440dace666b85c3e7ade479bcd..770d05ac0493323c2cdd0f5905e409113d1a9eae 100644 --- a/examples/HydroTests/SodShock_1D/plotSolution.py +++ b/examples/HydroTests/SodShock_1D/plotSolution.py @@ -79,12 +79,12 @@ git = str(sim["Code"].attrs["Git Revision"]) x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] try: - alpha = sim["/PartType0/Viscosity"][:] + alpha = sim["/PartType0/ViscosityParameters"][:] plot_alpha = True except: plot_alpha = False diff --git a/examples/HydroTests/SodShock_2D/plotSolution.py b/examples/HydroTests/SodShock_2D/plotSolution.py index 19cbe0ffb766845c051ffb6cea81bd918d890e36..769079da8824d58535e239bd8b54b592ce981a37 100644 --- a/examples/HydroTests/SodShock_2D/plotSolution.py +++ b/examples/HydroTests/SodShock_2D/plotSolution.py @@ -79,10 +79,10 @@ git = sim["Code"].attrs["Git Revision"] x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] N = 1000 # Number of points x_min = -1. diff --git a/examples/HydroTests/SodShock_3D/plotSolution.py b/examples/HydroTests/SodShock_3D/plotSolution.py index 69b2fe4887e986156ed01e0f4177d01ccbed6035..c2028cedcea820e56c07962fe3ca2f1fe1347d40 100644 --- a/examples/HydroTests/SodShock_3D/plotSolution.py +++ b/examples/HydroTests/SodShock_3D/plotSolution.py @@ -79,19 +79,19 @@ git = sim["Code"].attrs["Git Revision"] x = sim["/PartType0/Coordinates"][:,0] v = sim["/PartType0/Velocities"][:,0] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] try: - diffusion = sim["/PartType0/Diffusion"][:] + diffusion = sim["/PartType0/DiffusionParameters"][:] plot_diffusion = True except: plot_diffusion = False try: - viscosity = sim["/PartType0/Viscosity"][:] + viscosity = sim["/PartType0/ViscosityParameters"][:] plot_viscosity = True except: plot_viscosity = False diff --git a/examples/HydroTests/SodShock_BCC_3D/plotSolution.py b/examples/HydroTests/SodShock_BCC_3D/plotSolution.py index dc1b15c3eac862365d4422f95a14ffe1713f91a6..9660e12a3d226bd6b0e3c152031c93cedb345933 100644 --- a/examples/HydroTests/SodShock_BCC_3D/plotSolution.py +++ b/examples/HydroTests/SodShock_BCC_3D/plotSolution.py @@ -35,7 +35,7 @@ from analyticSolution import analytic snap = int(sys.argv[1]) -sim = load(f"sodshock_{snap:04d}.hdf5") +sim = load(f"sodShock_{snap:04d}.hdf5") # Set up plotting stuff try: @@ -94,10 +94,10 @@ time = sim.metadata.t.value data = dict( x=sim.gas.coordinates.value[:, 0], v=sim.gas.velocities.value[:, 0], - u=sim.gas.internal_energy.value, - S=sim.gas.entropy.value, - P=sim.gas.pressure.value, - rho=sim.gas.density.value, + u=sim.gas.internal_energies.value, + S=sim.gas.entropies.value, + P=sim.gas.pressures.value, + rho=sim.gas.densities.value, y=sim.gas.coordinates.value[:, 1], z=sim.gas.coordinates.value[:, 2], ) @@ -164,12 +164,9 @@ for key, label in plot.items(): zorder=-1, ) - mask_noraster = np.logical_and.reduce([ - data["y"] < 0.52, - data["y"] > 0.48, - data["z"] < 0.52, - data["z"] > 0.48 - ]) + mask_noraster = np.logical_and.reduce( + [data["y"] < 0.52, data["y"] > 0.48, data["z"] < 0.52, data["z"] > 0.48] + ) axis.plot( data["x"][mask_noraster], diff --git a/examples/HydroTests/SodShock_BCC_3D/sodShock.yml b/examples/HydroTests/SodShock_BCC_3D/sodShock.yml index 373a70bf6b6782d7bdb4ed91417a13270f6521e6..816af9c9ad620ce8617340d749b8ab3e61e53ec6 100644 --- a/examples/HydroTests/SodShock_BCC_3D/sodShock.yml +++ b/examples/HydroTests/SodShock_BCC_3D/sodShock.yml @@ -28,9 +28,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. - viscosity_alpha: 1.0 - viscosity_alpha_max: 1.0 - viscosity_alpha_min: 1.0 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/HydroTests/SquareTest_2D/makeIC.py b/examples/HydroTests/SquareTest_2D/makeIC.py index bbb6837c019dde14d833588924901cb49e301b25..ed3264a142751cd8a69594ad99f3d6bf6a183b1a 100644 --- a/examples/HydroTests/SquareTest_2D/makeIC.py +++ b/examples/HydroTests/SquareTest_2D/makeIC.py @@ -25,8 +25,8 @@ from numpy import * # Parameters L = 64 # Number of particles on the side gamma = 5./3. # Gas adiabatic index -rho0 = 4 # Gas central density -rho1 = 1 # Gas outskirt density +rho0 = 4.0 # Gas central density +rho1 = 1.0 # Gas outskirt density P0 = 2.5 # Gas central pressure P1 = 2.5 # Gas central pressure vx = 0.0 # Random velocity for all particles diff --git a/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py b/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py index 6c7af6d33ea020be773805d223095c99fbf78951..1f6885a2854a1ec2e1d7dd1b8647f36c99f67a6a 100644 --- a/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py +++ b/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py @@ -26,11 +26,11 @@ from numpy import * # Parameters L = 2 * 64 # Number of particles on the side gamma = 5.0 / 3.0 # Gas adiabatic index -rho0 = 4 # Gas central density -rho1 = 1 # Gas outskirt density -P0 = 2.5 # Gas central pressure -P1 = 2.5 # Gas central pressure -vx = 0.0 # Random velocity for all particles +rho0 = 4.0 # Gas central density +rho1 = 1.0 # Gas outskirt density +P0 = 2.5 # Gas central pressure +P1 = 2.5 # Gas central pressure +vx = 0.0 # Random velocity for all particles vy = 0.0 fileOutputName = "square.hdf5" # --------------------------------------------------- diff --git a/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py b/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py index 956da800c9096232d2e82cf4cff4c780672e0a8f..d8701c3d44390f1d2637f798c0e9af23531c4600 100644 --- a/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py +++ b/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py @@ -88,10 +88,10 @@ while centre_y < 0.: pos = sim["/PartType0/Coordinates"][:,:] vel = sim["/PartType0/Velocities"][:,:] v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2) -rho = sim["/PartType0/Density"][:] -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Densities"][:] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] x = pos[:,0] - centre_x y = pos[:,1] - centre_y diff --git a/examples/HydroTests/VacuumSpherical_2D/plotSolution.py b/examples/HydroTests/VacuumSpherical_2D/plotSolution.py index 6a65206ae20ccf79392054d047ba6be04f169f3e..de551c0ef4a5f4e027402c881069b7b4780f43d8 100644 --- a/examples/HydroTests/VacuumSpherical_2D/plotSolution.py +++ b/examples/HydroTests/VacuumSpherical_2D/plotSolution.py @@ -63,12 +63,12 @@ snap = int(sys.argv[1]) file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") coords = file["/PartType0/Coordinates"] x = np.sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2) -rho = file["/PartType0/Density"][:] +rho = file["/PartType0/Densities"][:] vels = file["/PartType0/Velocities"] v = np.sqrt(vels[:,0]**2 + vels[:,1]**2) -u = file["/PartType0/InternalEnergy"][:] -S = file["/PartType0/Entropy"][:] -P = file["/PartType0/Pressure"][:] +u = file["/PartType0/InternalEnergies"][:] +S = file["/PartType0/Entropies"][:] +P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] scheme = file["/HydroScheme"].attrs["Scheme"] diff --git a/examples/HydroTests/VacuumSpherical_3D/plotSolution.py b/examples/HydroTests/VacuumSpherical_3D/plotSolution.py index c73e48ee2d311692cdf4aa3b0e52f4766b339df8..4a04acde575eae46de67c70b536b8774befd96ae 100644 --- a/examples/HydroTests/VacuumSpherical_3D/plotSolution.py +++ b/examples/HydroTests/VacuumSpherical_3D/plotSolution.py @@ -64,12 +64,12 @@ file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") coords = file["/PartType0/Coordinates"] x = np.sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2 + \ (coords[:,2] - 0.5)**2) -rho = file["/PartType0/Density"][:] +rho = file["/PartType0/Densities"][:] vels = file["/PartType0/Velocities"] v = np.sqrt(vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2) -u = file["/PartType0/InternalEnergy"][:] -S = file["/PartType0/Entropy"][:] -P = file["/PartType0/Pressure"][:] +u = file["/PartType0/InternalEnergies"][:] +S = file["/PartType0/Entropies"][:] +P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] scheme = file["/HydroScheme"].attrs["Scheme"] diff --git a/examples/HydroTests/Vacuum_1D/plotSolution.py b/examples/HydroTests/Vacuum_1D/plotSolution.py index fceac10c25fd58b5bbcb6e31884cd62b4cfd61f5..eac7dc9e3ac43822ad167372f9f33bf2f5af0e2a 100644 --- a/examples/HydroTests/Vacuum_1D/plotSolution.py +++ b/examples/HydroTests/Vacuum_1D/plotSolution.py @@ -61,11 +61,11 @@ snap = int(sys.argv[1]) # Open the file and read the relevant data file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") x = file["/PartType0/Coordinates"][:,0] -rho = file["/PartType0/Density"] +rho = file["/PartType0/Densities"] v = file["/PartType0/Velocities"][:,0] -u = file["/PartType0/InternalEnergy"] -S = file["/PartType0/Entropy"] -P = file["/PartType0/Pressure"] +u = file["/PartType0/InternalEnergies"] +S = file["/PartType0/Entropies"] +P = file["/PartType0/Pressures"] time = file["/Header"].attrs["Time"][0] scheme = file["/HydroScheme"].attrs["Scheme"] diff --git a/examples/HydroTests/Vacuum_2D/plotSolution.py b/examples/HydroTests/Vacuum_2D/plotSolution.py index 4d197234237df10b8cdbf197048a65991da023cf..ffd0eb1cdd857764f7ecc4e2d0c93fee3c5f29e8 100644 --- a/examples/HydroTests/Vacuum_2D/plotSolution.py +++ b/examples/HydroTests/Vacuum_2D/plotSolution.py @@ -62,11 +62,11 @@ snap = int(sys.argv[1]) # Open the file and read the relevant data file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") x = file["/PartType0/Coordinates"][:,0] -rho = file["/PartType0/Density"][:] +rho = file["/PartType0/Densities"][:] v = file["/PartType0/Velocities"][:,0] -u = file["/PartType0/InternalEnergy"][:] -S = file["/PartType0/Entropy"][:] -P = file["/PartType0/Pressure"][:] +u = file["/PartType0/InternalEnergies"][:] +S = file["/PartType0/Entropies"][:] +P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] scheme = file["/HydroScheme"].attrs["Scheme"] diff --git a/examples/HydroTests/Vacuum_3D/plotSolution.py b/examples/HydroTests/Vacuum_3D/plotSolution.py index 4d197234237df10b8cdbf197048a65991da023cf..ffd0eb1cdd857764f7ecc4e2d0c93fee3c5f29e8 100644 --- a/examples/HydroTests/Vacuum_3D/plotSolution.py +++ b/examples/HydroTests/Vacuum_3D/plotSolution.py @@ -62,11 +62,11 @@ snap = int(sys.argv[1]) # Open the file and read the relevant data file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") x = file["/PartType0/Coordinates"][:,0] -rho = file["/PartType0/Density"][:] +rho = file["/PartType0/Densities"][:] v = file["/PartType0/Velocities"][:,0] -u = file["/PartType0/InternalEnergy"][:] -S = file["/PartType0/Entropy"][:] -P = file["/PartType0/Pressure"][:] +u = file["/PartType0/InternalEnergies"][:] +S = file["/PartType0/Entropies"][:] +P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] scheme = file["/HydroScheme"].attrs["Scheme"] diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/isolated_galaxy.yml index dccfb28a3f1c888d2a83b5e28b759a30a6928754..27ab01d984319fea68d4d1ae8fb435a6adf895ce 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/isolated_galaxy.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/isolated_galaxy.yml @@ -8,11 +8,9 @@ InternalUnitSystem: # Parameters for the self-gravity scheme Gravity: - mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion). - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). + max_physical_DM_softening: 0.7 # Physical softening length (in internal units). # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) TimeIntegration: diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml index 4d3485d838f56067cbb8493c79d06dff8a3dceba..dcd580243c51b0cbfb24c684709e1e511829f089 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml @@ -10,8 +10,8 @@ InternalUnitSystem: Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion). - comoving_softening: 0.1 # Comoving softening length (in internal units). - max_physical_softening: 0.1 # Physical softening length (in internal units). + max_physical_DM_softening: 0.1 # Physical softening length (in internal units). + max_physical_baryon_softening: 0.1 # Physical softening length (in internal units). # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) TimeIntegration: @@ -45,6 +45,11 @@ SPH: h_max: 10. minimal_temperature: 100. +# Parameters for the stars neighbour search +Stars: + overwrite_birth_time: 1 # Make sure the stars in the ICs do not do any feedback + birth_time: -1. # by setting all of their birth times to -1 + # Standard EAGLE cooling options EAGLECooling: dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plotSolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plotSolution.py index 89a87923148cb6872ab17b6d7229aef597ef3287..1ff8df3569f25590e5acb8046edeca0a1333d556 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plotSolution.py +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plotSolution.py @@ -29,7 +29,7 @@ rcParams.update(params) rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) snap = int(sys.argv[1]) -filename = "output_%.4d.hdf5"%snap +filename = "output_%.4d.hdf5" % snap f = h5.File(filename, "r") @@ -40,7 +40,7 @@ year_in_cgs = 3600.0 * 24 * 365.0 Msun_in_cgs = 1.98848e33 G_in_cgs = 6.67259e-8 pc_in_cgs = 3.08567758e18 -Msun_p_pc2 = Msun_in_cgs / pc_in_cgs**2 +Msun_p_pc2 = Msun_in_cgs / pc_in_cgs ** 2 # Gemoetry info boxsize = f["/Header"].attrs["BoxSize"] @@ -52,66 +52,94 @@ unit_mass_in_cgs = f["/Units"].attrs["Unit mass in cgs (U_M)"] unit_time_in_cgs = f["/Units"].attrs["Unit time in cgs (U_t)"] # Calculate Gravitational constant in internal units -G = G_in_cgs * ( unit_length_in_cgs**3 / unit_mass_in_cgs / unit_time_in_cgs**2)**(-1) +G = G_in_cgs * (unit_length_in_cgs ** 3 / unit_mass_in_cgs / unit_time_in_cgs ** 2) ** ( + -1 +) # Read parameters of the SF model KS_law_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_exponent"]) KS_law_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_normalisation"]) KS_thresh_Z0 = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_Z0"]) KS_thresh_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_slope"]) -KS_thresh_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_norm_H_p_cm3"]) +KS_thresh_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:threshold_norm_H_p_cm3"] +) KS_gas_fraction = float(f["/Parameters"].attrs["EAGLEStarFormation:gas_fraction"]) -KS_thresh_max_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_max_density_H_p_cm3"]) -KS_high_den_thresh = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_threshold_H_p_cm3"]) -KS_law_slope_high_den = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_exponent"]) -EOS_gamma_effective = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_gamma_effective"]) -EOS_density_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_density_norm_H_p_cm3"]) -EOS_temp_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_temperature_norm_K"]) +KS_thresh_max_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:threshold_max_density_H_p_cm3"] +) +KS_high_den_thresh = float( + f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_threshold_H_p_cm3"] +) +KS_law_slope_high_den = float( + f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_exponent"] +) +EOS_gamma_effective = float( + f["/Parameters"].attrs["EAGLEStarFormation:EOS_gamma_effective"] +) +EOS_density_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:EOS_density_norm_H_p_cm3"] +) +EOS_temp_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:EOS_temperature_norm_K"] +) # Read reference metallicity EAGLE_Z = float(f["/Parameters"].attrs["EAGLEChemistry:init_abundance_metal"]) # Read parameters of the entropy floor -EAGLEfloor_Jeans_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"]) -EAGLEfloor_Jeans_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_temperature_norm_K"]) -EAGLEfloor_Jeans_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_gamma_effective"]) -EAGLEfloor_cool_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"]) -EAGLEfloor_cool_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_temperature_norm_K"]) -EAGLEfloor_cool_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_gamma_effective"]) +EAGLEfloor_Jeans_rho_norm = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"] +) +EAGLEfloor_Jeans_temperature_norm_K = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_temperature_norm_K"] +) +EAGLEfloor_Jeans_gamma_effective = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_gamma_effective"] +) +EAGLEfloor_cool_rho_norm = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"] +) +EAGLEfloor_cool_temperature_norm_K = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_temperature_norm_K"] +) +EAGLEfloor_cool_gamma_effective = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_gamma_effective"] +) # Properties of the KS law -KS_law_norm_cgs = KS_law_norm * Msun_in_cgs / ( 1e6 * pc_in_cgs**2 * year_in_cgs ) -gamma = 5./3. +KS_law_norm_cgs = KS_law_norm * Msun_in_cgs / (1e6 * pc_in_cgs ** 2 * year_in_cgs) +gamma = 5.0 / 3.0 EOS_press_norm = k_in_cgs * EOS_temp_norm * EOS_density_norm # Star formation threshold -SF_thresh = KS_thresh_norm * (EAGLE_Z / KS_thresh_Z0)**(KS_thresh_slope) +SF_thresh = KS_thresh_norm * (EAGLE_Z / KS_thresh_Z0) ** (KS_thresh_slope) # Read gas properties gas_pos = f["/PartType0/Coordinates"][:, :] gas_mass = f["/PartType0/Masses"][:] -gas_rho = f["/PartType0/Density"][:] +gas_rho = f["/PartType0/Densities"][:] gas_T = f["/PartType0/Temperature"][:] gas_SFR = f["/PartType0/SFR"][:] -gas_XH = f["/PartType0/ElementAbundance"][:, 0] -gas_Z = f["/PartType0/Metallicity"][:] -gas_hsml = f["/PartType0/SmoothingLength"][:] +gas_XH = f["/PartType0/ElementMassFractions"][:, 0] +gas_Z = f["/PartType0/Metallicities"][:] +gas_hsml = f["/PartType0/SmoothingLengths"][:] gas_sSFR = gas_SFR / gas_mass # Read the Star properties stars_pos = f["/PartType4/Coordinates"][:, :] stars_BirthDensity = f["/PartType4/BirthDensity"][:] stars_BirthTime = f["/PartType4/BirthTime"][:] -stars_XH = f["/PartType4/ElementAbundance"][:,0] +stars_XH = f["/PartType4/ElementAbundance"][:, 0] # Centre the box gas_pos[:, 0] -= centre[0] gas_pos[:, 1] -= centre[1] gas_pos[:, 2] -= centre[2] -stars_pos[:,0] -= centre[0] -stars_pos[:,1] -= centre[1] -stars_pos[:,2] -= centre[2] +stars_pos[:, 0] -= centre[0] +stars_pos[:, 1] -= centre[1] +stars_pos[:, 2] -= centre[2] # Turn the mass into better units gas_mass *= unit_mass_in_cgs / Msun_in_cgs @@ -132,9 +160,13 @@ stars_BirthDensity *= stars_XH # Equations of state eos_cool_rho = np.logspace(-5, 5, 1000) -eos_cool_T = EAGLEfloor_cool_temperature_norm_K * (eos_cool_rho / EAGLEfloor_cool_rho_norm) ** ( EAGLEfloor_cool_gamma_effective - 1.0 ) +eos_cool_T = EAGLEfloor_cool_temperature_norm_K * ( + eos_cool_rho / EAGLEfloor_cool_rho_norm +) ** (EAGLEfloor_cool_gamma_effective - 1.0) eos_Jeans_rho = np.logspace(-1, 5, 1000) -eos_Jeans_T = EAGLEfloor_Jeans_temperature_norm_K * (eos_Jeans_rho / EAGLEfloor_Jeans_rho_norm) ** (EAGLEfloor_Jeans_gamma_effective - 1.0 ) +eos_Jeans_T = EAGLEfloor_Jeans_temperature_norm_K * ( + eos_Jeans_rho / EAGLEfloor_Jeans_rho_norm +) ** (EAGLEfloor_Jeans_gamma_effective - 1.0) ########################################################################3 @@ -156,7 +188,15 @@ subplot(111, xscale="log", yscale="log") plot(eos_cool_rho, eos_cool_T, "k--", lw=0.6) plot(eos_Jeans_rho, eos_Jeans_T, "k--", lw=0.6) plot([SF_thresh, SF_thresh], [1, 1e10], "k:", lw=0.6) -text(SF_thresh*0.9, 2e4, "$n_{\\rm H, thresh}=%.3f~{\\rm cm^{-3}}$"%SF_thresh, fontsize=8, rotation=90, ha="right", va="bottom") +text( + SF_thresh * 0.9, + 2e4, + "$n_{\\rm H, thresh}=%.3f~{\\rm cm^{-3}}$" % SF_thresh, + fontsize=8, + rotation=90, + ha="right", + va="bottom", +) scatter(gas_nH[gas_SFR > 0.0], gas_T[gas_SFR > 0.0], s=0.2) xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) @@ -188,37 +228,61 @@ star_mask = ( & (stars_pos[:, 2] > -1.0) ) -stars_BirthDensity = stars_BirthDensity[star_mask] -#stars_BirthFlag = stars_BirthFlag[star_mask] +stars_BirthDensity = stars_BirthDensity[star_mask] +# stars_BirthFlag = stars_BirthFlag[star_mask] stars_BirthTime = stars_BirthTime[star_mask] # Histogram of the birth density figure() subplot(111, xscale="linear", yscale="linear") -hist(np.log10(stars_BirthDensity),density=True,bins=20,range=[-2,5]) +hist(np.log10(stars_BirthDensity), density=True, bins=20, range=[-2, 5]) xlabel("${\\rm Stellar~birth~density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) ylabel("${\\rm Probability}$", labelpad=-7) savefig("BirthDensity.png", dpi=200) # Plot of the specific star formation rate in the galaxy -rhos = 10**np.linspace(-1,np.log10(KS_high_den_thresh),100) -rhoshigh = 10**np.linspace(np.log10(KS_high_den_thresh),5,100) +rhos = 10 ** np.linspace(-1, np.log10(KS_high_den_thresh), 100) +rhoshigh = 10 ** np.linspace(np.log10(KS_high_den_thresh), 5, 100) -P_effective = EOS_press_norm * ( rhos / EOS_density_norm)**(EOS_gamma_effective) -P_norm_high = EOS_press_norm * (KS_high_den_thresh / EOS_density_norm)**(EOS_gamma_effective) -sSFR = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction *P_effective)**((KS_law_slope-1.)/2.) -KS_law_norm_high_den_cgs = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction * P_norm_high)**((KS_law_slope-1.)/2.) -sSFR_high_den = KS_law_norm_high_den_cgs * ((rhoshigh/KS_high_den_thresh)**EOS_gamma_effective)**((KS_law_slope_high_den-1)/2.) +P_effective = EOS_press_norm * (rhos / EOS_density_norm) ** (EOS_gamma_effective) +P_norm_high = EOS_press_norm * (KS_high_den_thresh / EOS_density_norm) ** ( + EOS_gamma_effective +) +sSFR = ( + KS_law_norm_cgs + * (Msun_p_pc2) ** (-KS_law_slope) + * (gamma / G_in_cgs * KS_gas_fraction * P_effective) ** ((KS_law_slope - 1.0) / 2.0) +) +KS_law_norm_high_den_cgs = ( + KS_law_norm_cgs + * (Msun_p_pc2) ** (-KS_law_slope) + * (gamma / G_in_cgs * KS_gas_fraction * P_norm_high) ** ((KS_law_slope - 1.0) / 2.0) +) +sSFR_high_den = KS_law_norm_high_den_cgs * ( + (rhoshigh / KS_high_den_thresh) ** EOS_gamma_effective +) ** ((KS_law_slope_high_den - 1) / 2.0) # density - sSFR plane figure() subplot(111) -hist2d(np.log10(gas_nH), np.log10(gas_sSFR), bins=50,range=[[-1.5,5],[-.5,2.5]]) -plot(np.log10(rhos),np.log10(sSFR)+np.log10(year_in_cgs)+9.,'k--',label='sSFR low density EAGLE') -plot(np.log10(rhoshigh),np.log10(sSFR_high_den)+np.log10(year_in_cgs)+9.,'k--',label='sSFR high density EAGLE') +hist2d(np.log10(gas_nH), np.log10(gas_sSFR), bins=50, range=[[-1.5, 5], [-0.5, 2.5]]) +plot( + np.log10(rhos), + np.log10(sSFR) + np.log10(year_in_cgs) + 9.0, + "k--", + label="sSFR low density EAGLE", +) +plot( + np.log10(rhoshigh), + np.log10(sSFR_high_den) + np.log10(year_in_cgs) + 9.0, + "k--", + label="sSFR high density EAGLE", +) xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=2) ylabel("${\\rm sSFR}~[{\\rm Gyr^{-1}}]$", labelpad=0) -xticks([-1, 0, 1, 2, 3, 4], ["$10^{-1}$", "$10^0$", "$10^1$", "$10^2$", "$10^3$", "$10^4$"]) +xticks( + [-1, 0, 1, 2, 3, 4], ["$10^{-1}$", "$10^0$", "$10^1$", "$10^2$", "$10^3$", "$10^4$"] +) yticks([0, 1, 2], ["$10^0$", "$10^1$", "$10^2$"]) xlim(-1.4, 4.9) ylim(-0.5, 2.2) diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py index 67da3c390be1240323941b835e056dcd6e27feed..94f27c87ff46c48ffc6b0df8c3e02c7abb6df875 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py @@ -1,24 +1,25 @@ ############################################################################### - # This file is part of SWIFT. - # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) - # 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/>. - # - ############################################################################## +# This file is part of SWIFT. +# Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) +# 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/>. +# +############################################################################## import matplotlib + matplotlib.use("Agg") from pylab import * from scipy import stats @@ -27,40 +28,42 @@ import numpy as np import glob import os.path -def find_indices(a,b): - result = np.zeros(len(b)) - for i in range(len(b)): - result[i] = ((np.where(a == b[i]))[0])[0] - return result +def find_indices(a, b): + result = np.zeros(len(b)) + for i in range(len(b)): + result[i] = ((np.where(a == b[i]))[0])[0] + + return result # Plot parameters -params = {'axes.labelsize': 10, -'axes.titlesize': 10, -'font.size': 12, -'legend.fontsize': 12, -'xtick.labelsize': 10, -'ytick.labelsize': 10, -'text.usetex': True, - 'figure.figsize' : (9.90,6.45), -'figure.subplot.left' : 0.1, -'figure.subplot.right' : 0.99, -'figure.subplot.bottom' : 0.1, -'figure.subplot.top' : 0.95, -'figure.subplot.wspace' : 0.2, -'figure.subplot.hspace' : 0.2, -'lines.markersize' : 6, -'lines.linewidth' : 3., -'text.latex.unicode': True +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 12, + "legend.fontsize": 12, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (9.90, 6.45), + "figure.subplot.left": 0.1, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.1, + "figure.subplot.top": 0.95, + "figure.subplot.wspace": 0.2, + "figure.subplot.hspace": 0.2, + "lines.markersize": 6, + "lines.linewidth": 3.0, + "text.latex.unicode": True, } rcParams.update(params) -rc('font',**{'family':'sans-serif','sans-serif':['Times']}) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) # Number of snapshots and elements -newest_snap_name = max(glob.glob('output_*.hdf5'), key=os.path.getctime) -n_snapshots = int(newest_snap_name.replace('output_','').replace('.hdf5','')) + 1 +newest_snap_name = max(glob.glob("output_*.hdf5"), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace("output_", "").replace(".hdf5", "")) + 1 n_elements = 9 # Read the simulation data @@ -84,10 +87,10 @@ unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs unit_length_in_si = 0.01 * unit_length_in_cgs unit_mass_in_si = 0.001 * unit_mass_in_cgs unit_time_in_si = unit_time_in_cgs -unit_density_in_cgs = unit_mass_in_cgs*unit_length_in_cgs**-3 -unit_pressure_in_cgs = unit_mass_in_cgs/unit_length_in_cgs*unit_time_in_cgs**-2 -unit_int_energy_in_cgs = unit_energy_in_cgs/unit_mass_in_cgs -unit_entropy_in_cgs = unit_energy_in_cgs/unit_temp_in_cgs +unit_density_in_cgs = unit_mass_in_cgs * unit_length_in_cgs ** -3 +unit_pressure_in_cgs = unit_mass_in_cgs / unit_length_in_cgs * unit_time_in_cgs ** -2 +unit_int_energy_in_cgs = unit_energy_in_cgs / unit_mass_in_cgs +unit_entropy_in_cgs = unit_energy_in_cgs / unit_temp_in_cgs Gyr_in_cgs = 3.155e16 Msun_in_cgs = 1.989e33 @@ -95,61 +98,111 @@ box_energy = zeros(n_snapshots) box_mass = zeros(n_snapshots) box_star_mass = zeros(n_snapshots) box_metal_mass = zeros(n_snapshots) -element_mass = zeros((n_snapshots,n_elements)) +element_mass = zeros((n_snapshots, n_elements)) t = zeros(n_snapshots) # Read data from snapshots for i in range(n_snapshots): - print("reading snapshot "+str(i)) - # Read the simulation data - sim = h5py.File("output_%04d.hdf5"%i, "r") - t[i] = sim["/Header"].attrs["Time"][0] - #ids = sim["/PartType0/ParticleIDs"][:] - - masses = sim["/PartType0/Masses"][:] - box_mass[i] = np.sum(masses) - - star_masses = sim["/PartType4/Masses"][:] - box_star_mass[i] = np.sum(star_masses) - - metallicities = sim["/PartType0/Metallicity"][:] - box_metal_mass[i] = np.sum(metallicities * masses) - - internal_energies = sim["/PartType0/InternalEnergy"][:] - box_energy[i] = np.sum(masses * internal_energies) + print("reading snapshot " + str(i)) + # Read the simulation data + sim = h5py.File("output_%04d.hdf5" % i, "r") + t[i] = sim["/Header"].attrs["Time"][0] + # ids = sim["/PartType0/ParticleIDs"][:] + + masses = sim["/PartType0/Masses"][:] + box_mass[i] = np.sum(masses) + + star_masses = sim["/PartType4/Masses"][:] + box_star_mass[i] = np.sum(star_masses) + + metallicities = sim["/PartType0/Metallicities"][:] + box_metal_mass[i] = np.sum(metallicities * masses) + + internal_energies = sim["/PartType0/InternalEnergies"][:] + box_energy[i] = np.sum(masses * internal_energies) # Plot the interesting quantities figure() # Box mass -------------------------------- subplot(221) -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_mass[1:] - box_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +plot( + t[1:] * unit_time_in_cgs / Gyr_in_cgs, + (box_mass[1:] - box_mass[0]) * unit_mass_in_cgs / Msun_in_cgs, + linewidth=0.5, + color="k", + marker="*", + ms=0.5, + label="swift", +) xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) ylabel("Change in total gas particle mass (Msun)", labelpad=2) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) # Box metal mass -------------------------------- subplot(222) -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_metal_mass[1:] - box_metal_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', ms=0.5, label='swift') +plot( + t[1:] * unit_time_in_cgs / Gyr_in_cgs, + (box_metal_mass[1:] - box_metal_mass[0]) * unit_mass_in_cgs / Msun_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + label="swift", +) xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) ylabel("Change in total metal mass of gas particles (Msun)", labelpad=2) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) # Box energy -------------------------------- subplot(223) -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_energy[1:] - box_energy[0])* unit_energy_in_cgs, linewidth=0.5, color='k', ms=0.5, label='swift') +plot( + t[1:] * unit_time_in_cgs / Gyr_in_cgs, + (box_energy[1:] - box_energy[0]) * unit_energy_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + label="swift", +) xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) ylabel("Change in total energy of gas particles (erg)", labelpad=2) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) # Box mass -------------------------------- subplot(224) -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_mass[1:] - box_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='gas') -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_star_mass[1:] - box_star_mass[n_snapshots-1])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='r', marker = "*", ms=0.5, label='stars') -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_star_mass[1:] - box_star_mass[n_snapshots-1] + box_mass[1:] - box_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='g', marker = "*", ms=0.5, label='total') +plot( + t[1:] * unit_time_in_cgs / Gyr_in_cgs, + (box_mass[1:] - box_mass[0]) * unit_mass_in_cgs / Msun_in_cgs, + linewidth=0.5, + color="k", + marker="*", + ms=0.5, + label="gas", +) +plot( + t[1:] * unit_time_in_cgs / Gyr_in_cgs, + (box_star_mass[1:] - box_star_mass[n_snapshots - 1]) + * unit_mass_in_cgs + / Msun_in_cgs, + linewidth=0.5, + color="r", + marker="*", + ms=0.5, + label="stars", +) +plot( + t[1:] * unit_time_in_cgs / Gyr_in_cgs, + (box_star_mass[1:] - box_star_mass[n_snapshots - 1] + box_mass[1:] - box_mass[0]) + * unit_mass_in_cgs + / Msun_in_cgs, + linewidth=0.5, + color="g", + marker="*", + ms=0.5, + label="total", +) xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) ylabel("Change in total gas particle mass (Msun)", labelpad=2) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) legend() savefig("box_evolution.png", dpi=200) diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/isolated_galaxy.yml index deee132ee38ae5e04397839a21a677f4851e6bac..3bd743c2ec329057c7f63f987e4809744a07f7ba 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/isolated_galaxy.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/isolated_galaxy.yml @@ -8,11 +8,9 @@ InternalUnitSystem: # Parameters for the self-gravity scheme Gravity: - mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. eta: 0.025 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion). - comoving_softening: 0.0300 # Comoving softening length (in internal units). - max_physical_softening: 0.0300 # Physical softening length (in internal units). + max_physical_baryon_softening: 0.100 # Physical softening length (in internal units). # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) TimeIntegration: @@ -34,7 +32,7 @@ Statistics: time_first: 0. # (Optional) Time of the first stats output if non-cosmological time-integration (in internal units) Scheduler: - max_top_level_cells: 96 + max_top_level_cells: 32 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml index d347e1ad5bd8ddf6c132fade07e5d50688a451b0..fe57f693b0fcba6d8bc70c0fddf2d9dce2e60b99 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml @@ -8,11 +8,10 @@ InternalUnitSystem: # Parameters for the self-gravity scheme Gravity: - mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. - eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion). - comoving_softening: 0.01 # Comoving softening length (in internal units). - max_physical_softening: 0.01 # Physical softening length (in internal units). + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion). + max_physical_DM_softening: 0.1 # Physical softening length (in internal units). + max_physical_baryon_softening: 0.1 # Physical softening length (in internal units). # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) TimeIntegration: @@ -46,6 +45,11 @@ SPH: h_max: 10. minimal_temperature: 10. # Kelvin +# Parameters for the stars neighbour search +Stars: + overwrite_birth_time: 1 # Make sure the stars in the ICs do not do any feedback + birth_time: -1. # by setting all of their birth times to -1 + # Standard EAGLE cooling options EAGLECooling: dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py index 73e4878e8e00a35fe19c359652be0d57153dea62..044ad2bc78958cbf669c7257122b1ff80a94ba1a 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py @@ -49,7 +49,7 @@ rcParams.update(params) rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) snap = int(sys.argv[1]) -filename = "output_%.4d.hdf5"%snap +filename = "output_%.4d.hdf5" % snap f = h5.File(filename, "r") @@ -60,7 +60,7 @@ year_in_cgs = 3600.0 * 24 * 365.0 Msun_in_cgs = 1.98848e33 G_in_cgs = 6.67259e-8 pc_in_cgs = 3.08567758e18 -Msun_p_pc2 = Msun_in_cgs / pc_in_cgs**2 +Msun_p_pc2 = Msun_in_cgs / pc_in_cgs ** 2 # Gemoetry info boxsize = f["/Header"].attrs["BoxSize"] @@ -72,57 +72,85 @@ unit_mass_in_cgs = f["/Units"].attrs["Unit mass in cgs (U_M)"] unit_time_in_cgs = f["/Units"].attrs["Unit time in cgs (U_t)"] # Calculate Gravitational constant in internal units -G = G_in_cgs * ( unit_length_in_cgs**3 / unit_mass_in_cgs / unit_time_in_cgs**2)**(-1) +G = G_in_cgs * (unit_length_in_cgs ** 3 / unit_mass_in_cgs / unit_time_in_cgs ** 2) ** ( + -1 +) # Read parameters of the SF model KS_law_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_exponent"]) KS_law_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_normalisation"]) KS_thresh_Z0 = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_Z0"]) KS_thresh_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_slope"]) -KS_thresh_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_norm_H_p_cm3"]) +KS_thresh_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:threshold_norm_H_p_cm3"] +) KS_gas_fraction = float(f["/Parameters"].attrs["EAGLEStarFormation:gas_fraction"]) -KS_thresh_max_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_max_density_H_p_cm3"]) -KS_high_den_thresh = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_threshold_H_p_cm3"]) -KS_law_slope_high_den = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_exponent"]) -EOS_gamma_effective = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_gamma_effective"]) -EOS_density_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_density_norm_H_p_cm3"]) -EOS_temp_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_temperature_norm_K"]) +KS_thresh_max_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:threshold_max_density_H_p_cm3"] +) +KS_high_den_thresh = float( + f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_threshold_H_p_cm3"] +) +KS_law_slope_high_den = float( + f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_exponent"] +) +EOS_gamma_effective = float( + f["/Parameters"].attrs["EAGLEStarFormation:EOS_gamma_effective"] +) +EOS_density_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:EOS_density_norm_H_p_cm3"] +) +EOS_temp_norm = float( + f["/Parameters"].attrs["EAGLEStarFormation:EOS_temperature_norm_K"] +) # Read reference metallicity EAGLE_Z = float(f["/Parameters"].attrs["EAGLEChemistry:init_abundance_metal"]) # Read parameters of the entropy floor -EAGLEfloor_Jeans_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"]) -EAGLEfloor_Jeans_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_temperature_norm_K"]) -EAGLEfloor_Jeans_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_gamma_effective"]) -EAGLEfloor_cool_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"]) -EAGLEfloor_cool_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_temperature_norm_K"]) -EAGLEfloor_cool_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_gamma_effective"]) +EAGLEfloor_Jeans_rho_norm = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"] +) +EAGLEfloor_Jeans_temperature_norm_K = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_temperature_norm_K"] +) +EAGLEfloor_Jeans_gamma_effective = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_gamma_effective"] +) +EAGLEfloor_cool_rho_norm = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"] +) +EAGLEfloor_cool_temperature_norm_K = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_temperature_norm_K"] +) +EAGLEfloor_cool_gamma_effective = float( + f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_gamma_effective"] +) # Properties of the KS law -KS_law_norm_cgs = KS_law_norm * Msun_in_cgs / ( 1e6 * pc_in_cgs**2 * year_in_cgs ) -gamma = 5./3. +KS_law_norm_cgs = KS_law_norm * Msun_in_cgs / (1e6 * pc_in_cgs ** 2 * year_in_cgs) +gamma = 5.0 / 3.0 EOS_press_norm = k_in_cgs * EOS_temp_norm * EOS_density_norm # Star formation threshold -SF_thresh = KS_thresh_norm * (EAGLE_Z / KS_thresh_Z0)**(KS_thresh_slope) +SF_thresh = KS_thresh_norm * (EAGLE_Z / KS_thresh_Z0) ** (KS_thresh_slope) # Read gas properties gas_pos = f["/PartType0/Coordinates"][:, :] gas_mass = f["/PartType0/Masses"][:] -gas_rho = f["/PartType0/Density"][:] +gas_rho = f["/PartType0/Densities"][:] gas_T = f["/PartType0/Temperature"][:] gas_SFR = f["/PartType0/SFR"][:] -gas_XH = f["/PartType0/ElementAbundance"][:, 0] -gas_Z = f["/PartType0/Metallicity"][:] -gas_hsml = f["/PartType0/SmoothingLength"][:] +gas_XH = f["/PartType0/ElementMassFractions"][:, 0] +gas_Z = f["/PartType0/Metallicities"][:] +gas_hsml = f["/PartType0/SmoothingLengths"][:] gas_sSFR = gas_SFR / gas_mass # Read the Star properties stars_pos = f["/PartType4/Coordinates"][:, :] stars_BirthDensity = f["/PartType4/BirthDensity"][:] stars_BirthTime = f["/PartType4/BirthTime"][:] -stars_XH = f["/PartType4/ElementAbundance"][:,0] +stars_XH = f["/PartType4/ElementAbundance"][:, 0] stars_BirthTemperature = f["/PartType4/BirthTemperature"][:] # Centre the box @@ -130,9 +158,9 @@ gas_pos[:, 0] -= centre[0] gas_pos[:, 1] -= centre[1] gas_pos[:, 2] -= centre[2] -stars_pos[:,0] -= centre[0] -stars_pos[:,1] -= centre[1] -stars_pos[:,2] -= centre[2] +stars_pos[:, 0] -= centre[0] +stars_pos[:, 1] -= centre[1] +stars_pos[:, 2] -= centre[2] # Turn the mass into better units gas_mass *= unit_mass_in_cgs / Msun_in_cgs @@ -156,9 +184,13 @@ stars_BirthDensity *= stars_XH # Equations of state eos_cool_rho = np.logspace(-5, 5, 1000) -eos_cool_T = EAGLEfloor_cool_temperature_norm_K * (eos_cool_rho / EAGLEfloor_cool_rho_norm) ** ( EAGLEfloor_cool_gamma_effective - 1.0 ) +eos_cool_T = EAGLEfloor_cool_temperature_norm_K * ( + eos_cool_rho / EAGLEfloor_cool_rho_norm +) ** (EAGLEfloor_cool_gamma_effective - 1.0) eos_Jeans_rho = np.logspace(-1, 5, 1000) -eos_Jeans_T = EAGLEfloor_Jeans_temperature_norm_K * (eos_Jeans_rho / EAGLEfloor_Jeans_rho_norm) ** (EAGLEfloor_Jeans_gamma_effective - 1.0 ) +eos_Jeans_T = EAGLEfloor_Jeans_temperature_norm_K * ( + eos_Jeans_rho / EAGLEfloor_Jeans_rho_norm +) ** (EAGLEfloor_Jeans_gamma_effective - 1.0) ########################################################################3 @@ -180,7 +212,15 @@ subplot(111, xscale="log", yscale="log") plot(eos_cool_rho, eos_cool_T, "k--", lw=0.6) plot(eos_Jeans_rho, eos_Jeans_T, "k--", lw=0.6) plot([SF_thresh, SF_thresh], [1, 1e10], "k:", lw=0.6) -text(SF_thresh*0.9, 2e4, "$n_{\\rm H, thresh}=%.3f~{\\rm cm^{-3}}$"%SF_thresh, fontsize=8, rotation=90, ha="right", va="bottom") +text( + SF_thresh * 0.9, + 2e4, + "$n_{\\rm H, thresh}=%.3f~{\\rm cm^{-3}}$" % SF_thresh, + fontsize=8, + rotation=90, + ha="right", + va="bottom", +) scatter(gas_nH[gas_SFR > 0.0], gas_T[gas_SFR > 0.0], s=0.2) xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) @@ -199,66 +239,92 @@ star_mask = ( & (stars_pos[:, 2] > -1.0) ) -stars_BirthDensity = stars_BirthDensity[star_mask] -#stars_BirthFlag = stars_BirthFlag[star_mask] +stars_BirthDensity = stars_BirthDensity[star_mask] +# stars_BirthFlag = stars_BirthFlag[star_mask] stars_BirthTime = stars_BirthTime[star_mask] # Histogram of the birth density figure() subplot(111, xscale="linear", yscale="linear") -hist(np.log10(stars_BirthDensity),density=True,bins=20,range=[-2,5]) +hist(np.log10(stars_BirthDensity), density=True, bins=20, range=[-2, 5]) xlabel("${\\rm Stellar~birth~density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) ylabel("${\\rm Probability}$", labelpad=3) savefig("BirthDensity.png", dpi=200) -# Histogram of the birth temperature +# Histogram of the birth temperature figure() subplot(111, xscale="linear", yscale="linear") -hist(np.log10(stars_BirthTemperature),density=True,bins=20,range=[3.5,5.0]) +hist(np.log10(stars_BirthTemperature), density=True, bins=20, range=[3.5, 5.0]) xlabel("${\\rm Stellar~birth~temperature}~[{\\rm K}]$", labelpad=0) ylabel("${\\rm Probability}$", labelpad=3) savefig("BirthTemperature.png", dpi=200) # Plot of the specific star formation rate in the galaxy -rhos = 10**np.linspace(-1,np.log10(KS_high_den_thresh),100) -rhoshigh = 10**np.linspace(np.log10(KS_high_den_thresh),5,100) +rhos = 10 ** np.linspace(-1, np.log10(KS_high_den_thresh), 100) +rhoshigh = 10 ** np.linspace(np.log10(KS_high_den_thresh), 5, 100) -P_effective = EOS_press_norm * ( rhos / EOS_density_norm)**(EOS_gamma_effective) -P_norm_high = EOS_press_norm * (KS_high_den_thresh / EOS_density_norm)**(EOS_gamma_effective) -sSFR = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction *P_effective)**((KS_law_slope-1.)/2.) -KS_law_norm_high_den_cgs = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction * P_norm_high)**((KS_law_slope-1.)/2.) -sSFR_high_den = KS_law_norm_high_den_cgs * ((rhoshigh/KS_high_den_thresh)**EOS_gamma_effective)**((KS_law_slope_high_den-1)/2.) +P_effective = EOS_press_norm * (rhos / EOS_density_norm) ** (EOS_gamma_effective) +P_norm_high = EOS_press_norm * (KS_high_den_thresh / EOS_density_norm) ** ( + EOS_gamma_effective +) +sSFR = ( + KS_law_norm_cgs + * (Msun_p_pc2) ** (-KS_law_slope) + * (gamma / G_in_cgs * KS_gas_fraction * P_effective) ** ((KS_law_slope - 1.0) / 2.0) +) +KS_law_norm_high_den_cgs = ( + KS_law_norm_cgs + * (Msun_p_pc2) ** (-KS_law_slope) + * (gamma / G_in_cgs * KS_gas_fraction * P_norm_high) ** ((KS_law_slope - 1.0) / 2.0) +) +sSFR_high_den = KS_law_norm_high_den_cgs * ( + (rhoshigh / KS_high_den_thresh) ** EOS_gamma_effective +) ** ((KS_law_slope_high_den - 1) / 2.0) # density - sSFR plane figure() subplot(111) -hist2d(np.log10(gas_nH), np.log10(gas_sSFR), bins=50,range=[[-1.5,5],[-.5,2.5]]) -plot(np.log10(rhos),np.log10(sSFR)+np.log10(year_in_cgs)+9.,'k--',label='sSFR low density EAGLE') -plot(np.log10(rhoshigh),np.log10(sSFR_high_den)+np.log10(year_in_cgs)+9.,'k--',label='sSFR high density EAGLE') +hist2d(np.log10(gas_nH), np.log10(gas_sSFR), bins=50, range=[[-1.5, 5], [-0.5, 2.5]]) +plot( + np.log10(rhos), + np.log10(sSFR) + np.log10(year_in_cgs) + 9.0, + "k--", + label="sSFR low density EAGLE", +) +plot( + np.log10(rhoshigh), + np.log10(sSFR_high_den) + np.log10(year_in_cgs) + 9.0, + "k--", + label="sSFR high density EAGLE", +) xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=2) ylabel("${\\rm sSFR}~[{\\rm Gyr^{-1}}]$", labelpad=0) -xticks([-1, 0, 1, 2, 3, 4], ["$10^{-1}$", "$10^0$", "$10^1$", "$10^2$", "$10^3$", "$10^4$"]) +xticks( + [-1, 0, 1, 2, 3, 4], ["$10^{-1}$", "$10^0$", "$10^1$", "$10^2$", "$10^3$", "$10^4$"] +) yticks([0, 1, 2], ["$10^0$", "$10^1$", "$10^2$"]) xlim(-1.4, 4.9) ylim(-0.5, 2.2) savefig("density-sSFR.png", dpi=200) -SFR_low = 10**(np.log10(sSFR)+np.log10(year_in_cgs)+np.log10(median_gas_mass)) -SFR_high = 10**(np.log10(sSFR_high_den)+np.log10(year_in_cgs)+np.log10(median_gas_mass)) -SFR_low_min = np.floor(np.log10(.75*np.min(SFR_low))) -SFR_high_max = np.ceil(np.log10(1.25*np.max(SFR_high))) +SFR_low = 10 ** (np.log10(sSFR) + np.log10(year_in_cgs) + np.log10(median_gas_mass)) +SFR_high = 10 ** ( + np.log10(sSFR_high_den) + np.log10(year_in_cgs) + np.log10(median_gas_mass) +) +SFR_low_min = np.floor(np.log10(0.75 * np.min(SFR_low))) +SFR_high_max = np.ceil(np.log10(1.25 * np.max(SFR_high))) # 3D Density vs SFR rcParams.update({"figure.subplot.left": 0.18}) figure() subplot(111, xscale="log", yscale="log") scatter(gas_nH, gas_SFR, s=0.2) -plot(rhos,SFR_low,'k--',lw=1,label='SFR low density EAGLE') -plot(rhoshigh,SFR_high,'k--',lw=1,label='SFR high density EAGLE') +plot(rhos, SFR_low, "k--", lw=1, label="SFR low density EAGLE") +plot(rhoshigh, SFR_high, "k--", lw=1, label="SFR high density EAGLE") xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) ylabel("${\\rm SFR}~[{\\rm M_\\odot~\\cdot~yr^{-1}}]$", labelpad=2) xlim(1e-2, 1e5) -ylim(10**SFR_low_min, 10**(SFR_high_max+0.1)) +ylim(10 ** SFR_low_min, 10 ** (SFR_high_max + 0.1)) savefig("rho_SFR.png", dpi=200) rcParams.update({"figure.subplot.left": 0.15}) ########################################################################3 diff --git a/examples/Makefile.am b/examples/Makefile.am index ba92dd4cd4c2b1a4852714edc1e6c4aadd8b6c32..d8161ec28ec4b6ffc14ce0b0f9861c1b8fde2ce2 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -37,14 +37,14 @@ MPI_FLAGS = -DWITH_MPI $(PARMETIS_INCS) $(METIS_INCS) bin_PROGRAMS = swift # Also build the FOF tool? -if HAVEFOF +if HAVESTANDALONEFOF bin_PROGRAMS += fof endif # Build MPI versions as well? if HAVEMPI bin_PROGRAMS += swift_mpi -if HAVEFOF +if HAVESTANDALONEFOF bin_PROGRAMS += fof_mpi endif endif diff --git a/examples/PMillennium/PMillennium-1536/p-mill-1536.yml b/examples/PMillennium/PMillennium-1536/p-mill-1536.yml index 770cc4a03e8912eed51d1851b81578496a42070c..f343650452a24f620edef88e42666e09213dbd64 100644 --- a/examples/PMillennium/PMillennium-1536/p-mill-1536.yml +++ b/examples/PMillennium/PMillennium-1536/p-mill-1536.yml @@ -43,9 +43,9 @@ Statistics: Gravity: eta: 0.025 theta: 0.5 - comoving_softening: 0.0208333 # 20.8333 kpc = 1/25 mean inter-particle separation - max_physical_softening: 0.0208333 # 20.8333 kpc = 1/25 mean inter-particle separation - mesh_side_length: 256 + comoving_DM_softening: 0.0208333 # 20.8333 kpc = 1/25 mean inter-particle separation + max_physical_DM_softening: 0.0208333 # 20.8333 kpc = 1/25 mean inter-particle separation + mesh_side_length: 512 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/PMillennium/PMillennium-384/p-mill-384.yml b/examples/PMillennium/PMillennium-384/p-mill-384.yml index 428f893300bf3431f20518a6a1259651b3c67226..0e68969d0b590cec8058805e4644b2763c543be1 100644 --- a/examples/PMillennium/PMillennium-384/p-mill-384.yml +++ b/examples/PMillennium/PMillennium-384/p-mill-384.yml @@ -43,9 +43,9 @@ Statistics: Gravity: eta: 0.025 theta: 0.5 - comoving_softening: 0.08333 # 83.333 kpc = 1/25 mean inter-particle separation - max_physical_softening: 0.08333 # 83.333 kpc = 1/25 mean inter-particle separation - mesh_side_length: 64 + comoving_DM_softening: 0.08333 # 83.333 kpc = 1/25 mean inter-particle separation + max_physical_DM_softening: 0.08333 # 83.333 kpc = 1/25 mean inter-particle separation + mesh_side_length: 128 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/PMillennium/PMillennium-768/p-mill-768.yml b/examples/PMillennium/PMillennium-768/p-mill-768.yml index 339fad2c7059d8c8bcab2878fc8958a3bd1977d7..1cd9e63b1f03ac65baf72701de276b8ff43c9575 100644 --- a/examples/PMillennium/PMillennium-768/p-mill-768.yml +++ b/examples/PMillennium/PMillennium-768/p-mill-768.yml @@ -43,9 +43,9 @@ Statistics: Gravity: eta: 0.025 theta: 0.5 - comoving_softening: 0.041666 # 41.6666 kpc = 1/25 mean inter-particle separation - max_physical_softening: 0.041666 # 41.6666 kpc = 1/25 mean inter-particle separation - mesh_side_length: 128 + comoving_DM_softening: 0.041666 # 41.6666 kpc = 1/25 mean inter-particle separation + max_physical_DM_softening: 0.041666 # 41.6666 kpc = 1/25 mean inter-particle separation + mesh_side_length: 256 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml b/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml index 1221cefb92e51e54bfe73a0414b95a89fcda592a..e57617e149cb9f302d8fe08bd93d660059caa0ef 100644 --- a/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml +++ b/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml @@ -43,9 +43,11 @@ Statistics: Gravity: eta: 0.025 theta: 0.5 - comoving_softening: 0.02 # 20 kpc = 1/25 mean inter-particle separation - max_physical_softening: 0.00526 # 20 ckpc = 5.26 pkpc at z=2.8 (EAGLE-like evolution of softening). - mesh_side_length: 64 + comoving_DM_softening: 0.02 # 20 kpc = 1/25 mean inter-particle separation + max_physical_DM_softening: 0.00526 # 20 ckpc = 5.26 pkpc at z=2.8 (EAGLE-like evolution of softening). + comoving_baryon_softening: 0.02 # 20 kpc = 1/25 mean inter-particle separation + max_physical_baryon_softening: 0.00526 # 20 ckpc = 5.26 pkpc at z=2.8 (EAGLE-like evolution of softening). + mesh_side_length: 128 # Parameters of the hydro scheme SPH: diff --git a/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py index dab4b2c90a7b751c8d143ed38c614473c951988a..63e46ccaee7be9ea18090e13ae15bb0a1fae4bef 100644 --- a/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py +++ b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py @@ -128,7 +128,7 @@ for i in range(n_snapshots): z[i] = sim["/Cosmology"].attrs["Redshift"][0] a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] - u = sim["/PartType0/InternalEnergy"][:] + u = sim["/PartType0/InternalEnergies"][:] # Compute the temperature u *= unit_length_in_si ** 2 / unit_time_in_si ** 2 diff --git a/examples/SantaBarbara/SantaBarbara-256/rhoTPlot.py b/examples/SantaBarbara/SantaBarbara-256/rhoTPlot.py index c290268eaa548e188bb652104ea9e726ea88a267..3bcf01d2a49bc1c53b243ffcff12359201d26d87 100644 --- a/examples/SantaBarbara/SantaBarbara-256/rhoTPlot.py +++ b/examples/SantaBarbara/SantaBarbara-256/rhoTPlot.py @@ -28,10 +28,10 @@ def get_data(filename): data = SWIFTDataset(filename) - data.gas.density.convert_to_units(mh / (cm ** 3)) - data.gas.temperature.convert_to_cgs() + data.gas.densities.convert_to_units(mh / (cm ** 3)) + data.gas.temperatures.convert_to_cgs() - return data.gas.density, data.gas.temperature + return data.gas.densities, data.gas.temperatures def make_hist(filename, density_bounds, temperature_bounds, bins): @@ -155,10 +155,8 @@ def make_movie(args, density_bounds, temperature_bounds, bins): def format_metadata(metadata: SWIFTMetadata): t = metadata.t * units.units["Unit time in cgs (U_t)"] t.convert_to_units(Gyr) - - x = "$a$: {:2.2f}\n$z$: {:2.2f}\n$t$: {:2.2f}".format( - metadata.a, metadata.z, t - ) + + x = "$a$: {:2.2f}\n$z$: {:2.2f}\n$t$: {:2.2f}".format(metadata.a, metadata.z, t) return x diff --git a/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml b/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml index 04bb18191b750aa996e43b58b24451135bceedca..c95914d58ef01ac48f5db83ea4b3f3a1f93404f9 100644 --- a/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml +++ b/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml @@ -43,9 +43,11 @@ Statistics: Gravity: eta: 0.025 theta: 0.5 - comoving_softening: 0.01 # 10 kpc = 1/25 mean inter-particle separation - max_physical_softening: 0.00263 # 10 ckpc = 2.63 pkpc at z=2.8 (EAGLE-like evolution of softening). - mesh_side_length: 128 + comoving_DM_softening: 0.01 # 10 kpc = 1/25 mean inter-particle separation + max_physical_DM_softening: 0.00263 # 10 ckpc = 2.63 pkpc at z=2.8 (EAGLE-like evolution of softening). + comoving_baryon_softening: 0.01 # 10 kpc = 1/25 mean inter-particle separation + max_physical_baryon_softening: 0.00263 # 10 ckpc = 2.63 pkpc at z=2.8 (EAGLE-like evolution of softening). + mesh_side_length: 256 # Parameters of the hydro scheme SPH: diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml index ebe3a78ee0d03eb53752b1dfa8fa749931a754a9..975ceac9c3c9404dc2c194abc4de40bf7d529a11 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml @@ -29,9 +29,9 @@ TimeIntegration: # Parameters for the self-gravity scheme Gravity: eta: 0.025 - theta: 0.3 - comoving_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc - max_physical_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + theta: 0.5 + comoving_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc mesh_side_length: 64 # Parameters governing the snapshots diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/small_cosmo_volume.yml index 15007ca8d39a328166a208a2d4cbbc6aea580009..f81c38a85f04b074f4cf5598bc3d09716b49f790 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/small_cosmo_volume.yml @@ -22,9 +22,11 @@ TimeIntegration: # Parameters for the self-gravity scheme Gravity: eta: 0.025 - theta: 0.3 - comoving_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc - max_physical_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + theta: 0.5 + comoving_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + comoving_baryon_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_baryon_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc mesh_side_length: 64 # Parameters of the hydro scheme diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py index 4ba8ad66daca1d9614be8917a77407dd99209dea..4f02213ec2a66700d28ad5f8e57e00c30f3019d7 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py @@ -112,8 +112,8 @@ def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp): return ret -rho = sim["/PartType0/Density"][:] -u = sim["/PartType0/InternalEnergy"][:] +rho = sim["/PartType0/Densities"][:] +u = sim["/PartType0/InternalEnergies"][:] # Compute the temperature u *= unit_length_in_si ** 2 / unit_time_in_si ** 2 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py index a3458ac1598e5657f3f597dfb10b36a7a641e68f..1e8cf9ea1082372d8e395c352f908c7ce693d99f 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py @@ -126,7 +126,7 @@ for i in range(n_snapshots): z[i] = sim["/Cosmology"].attrs["Redshift"][0] a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] - u = sim["/PartType0/InternalEnergy"][:] + u = sim["/PartType0/InternalEnergies"][:] # Compute the temperature u *= (unit_length_in_si**2 / unit_time_in_si**2) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml index b108290fcd146461827d5858742bd0971ac66945..aea198bd789b48d608c6fc3fdc493303d6d9afd2 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml @@ -23,8 +23,10 @@ TimeIntegration: Gravity: eta: 0.025 theta: 0.3 - comoving_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc - max_physical_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + comoving_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + comoving_baryon_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_baryon_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc mesh_side_length: 64 # Parameters of the hydro scheme @@ -85,6 +87,16 @@ EAGLEChemistry: init_abundance_Silicon: 0.0 init_abundance_Iron: 0.0 +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + # Cooling with Grackle 3.0 GrackleCooling: CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) @@ -99,3 +111,6 @@ GrackleCooling: GearChemistry: InitialMetallicity: 0.01295 + +GEARPressureFloor: + Jeans_factor: 10. # Number of particles required to suppose a resolved clump and avoid the pressure floor. diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py index aa6c5df5fe5ff5c7d0944a45bb11344f70c57844..d707f70450471f2d2fc589dbc382366280e0e7f3 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py @@ -123,7 +123,7 @@ for i in range(n_snapshots): z[i] = sim["/Cosmology"].attrs["Redshift"][0] a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] - u = sim["/PartType0/InternalEnergy"][:] + u = sim["/PartType0/InternalEnergies"][:] # Compute the temperature u *= (unit_length_in_si**2 / unit_time_in_si**2) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml index e0d633079e941ade161b7e2fde0fbc063cbac254..5904a412f7bf63336de976baa6da4506bae4e36c 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml @@ -22,9 +22,11 @@ TimeIntegration: # Parameters for the self-gravity scheme Gravity: eta: 0.025 - theta: 0.3 - comoving_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc - max_physical_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + theta: 0.5 + comoving_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_DM_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + comoving_baryon_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_baryon_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc mesh_side_length: 64 # Parameters of the hydro scheme diff --git a/examples/SubgridTests/BlackHoleSwallowing/check_masses.py b/examples/SubgridTests/BlackHoleSwallowing/check_masses.py index c7f1d7b2c1f90efa285c13c75f0e5243f36e49ea..a5b55ed00df0851f989858ddffd00ea34df88a27 100644 --- a/examples/SubgridTests/BlackHoleSwallowing/check_masses.py +++ b/examples/SubgridTests/BlackHoleSwallowing/check_masses.py @@ -66,5 +66,5 @@ for i in range(np.size(ids_removed)): result = np.where(ids_gas == ids_removed) print result -#rho_gas = f["/PartType0/Density"][:] +#rho_gas = f["/PartType0/Densities"][:] #print np.mean(rho_gas), np.std(rho_gas) diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/README b/examples/SubgridTests/CosmologicalStellarEvolution/README new file mode 100644 index 0000000000000000000000000000000000000000..44a4836e523d4037e5f942e01e444599fa009c1a --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/README @@ -0,0 +1,7 @@ +Example for testing EAGLE stellar feedback. This consists of a uniform box of gas with a star in the center. The amount of feedback can then be checked by summing over the gas particles in the whole box and comparing to the expected amount of feedback from a single star over a given time period. + +If only mass enrichment is of interest, the box can be run with ICs generated from a smaller glass (eg glassCube_32.hdf5). In this case, however it is necessary to turn off energy feedback (eg. by setting the return value of compute_SNe in src/stars/EAGLE/stars.h to zero) or using a larger glass (glassCube_64.hdf5). + +Use the python script, plot_box_evolution.py to compare total mass evolution of gas particles in the whole box with what is expected based on EAGLE standalone feedback test. + +Use plot_paricle_evolution.py to plot the evolution of particles starting in the viscinity of the star at the beginning of the simulation. diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py b/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py new file mode 100644 index 0000000000000000000000000000000000000000..b5940eba43c89b8af4a883cc8f7022e33293b869 --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py @@ -0,0 +1,137 @@ +# Script for plotting energy evolution of uniform box of gas with single star in the +# centre when running with stochastic energy injection. It also checks that the change +# in total energy of the gas particles is within a specified tolerance from what is +# expected based on the mass of the star particle (Note that this tolerance could be +# somewhat high because of Poisson noise and the relatively small number of injection +# events) + +import matplotlib +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path +import numpy as np +import glob + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 9, +'legend.fontsize': 9, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (3.15,3.15), +'figure.subplot.left' : 0.3, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.18, +'figure.subplot.top' : 0.92, +'figure.subplot.wspace' : 0.21, +'figure.subplot.hspace' : 0.19, +'lines.markersize' : 6, +'lines.linewidth' : 2., +'text.latex.unicode': True +} + +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] +star_initial_mass = sim["/PartType4/Masses"][0] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Calculate solar mass in internal units +const_solar_mass = 1.98848e33 / unit_mass_in_cgs + +# Define Gyr +Gyr_in_cgs = 1e9 * 365 * 24 * 3600. + +# Find out how many particles (gas and star) we have +n_parts = sim["/Header"].attrs["NumPart_Total"][0] +n_sparts = sim["/Header"].attrs["NumPart_Total"][4] + +# Declare arrays for data +masses = zeros((n_parts,n_snapshots)) +star_masses = zeros((n_sparts,n_snapshots)) +internal_energy = zeros((n_parts,n_snapshots)) +velocity_parts = zeros((n_parts,3,n_snapshots)) +time = zeros(n_snapshots) + +# Read fields we are checking from snapshots +#for i in [0,n_snapshots-1]: +for i in range(n_snapshots): + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + print('reading snapshot '+str(i)) + masses[:,i] = sim["/PartType0/Masses"] + internal_energy[:,i] = sim["/PartType0/InternalEnergies"] + velocity_parts[:,:,i] = sim["/PartType0/Velocities"] + time[i] = sim["/Header"].attrs["Time"][0] + +# Check that the total amount of enrichment is as expected. +# Define tolerance. Note, relatively high value used due to +# Poisson noise associated with stochastic energy injection. +eps = 0.15 + +# Stochastic heating +vel2 = zeros((n_parts,n_snapshots)) +vel2[:,:] = velocity_parts[:,0,:]*velocity_parts[:,0,:] + velocity_parts[:,1,:]*velocity_parts[:,1,:] + velocity_parts[:,2,:]*velocity_parts[:,2,:] +total_kinetic_energy_cgs = np.sum(np.multiply(vel2,masses)*0.5,axis = 0) * unit_energy_in_cgs +total_energy_cgs = np.sum(np.multiply(internal_energy,masses),axis = 0) * unit_energy_in_cgs +total_energy_released_cgs = total_energy_cgs[n_snapshots-1] - total_energy_cgs[0] + total_kinetic_energy_cgs[n_snapshots-1] - total_kinetic_energy_cgs[0] + +# Calculate energy released +energy_per_sn = 1.0e51 / unit_energy_in_cgs +SNIa_efficiency = 2.e-3 +SNIa_timescale_Gyr = 2.0 +expected_energy_released_cgs = np.zeros(n_snapshots) +for i in range(n_snapshots): + age_Gyr = time[i] * unit_time_in_cgs / Gyr_in_cgs + total_sn = SNIa_efficiency * (1.0 - np.exp(-age_Gyr/SNIa_timescale_Gyr)) / const_solar_mass + expected_energy_released_cgs[i] = total_sn * energy_per_sn * unit_energy_in_cgs + +# Did we get it right? +if abs(total_energy_released_cgs - expected_energy_released_cgs[n_snapshots-1])/expected_energy_released_cgs[n_snapshots-1] < eps: + print("total stochastic energy release consistent with expectation. total stochastic energy release "+str(total_energy_released_cgs)+" expected "+ str(expected_energy_released_cgs[n_snapshots-1]) + " initial total internal energy "+ str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])) +else: + print("total stochastic energy release "+str(total_energy_released_cgs)+" expected "+ str(expected_energy_released_cgs[n_snapshots-1]) + " initial total internal energy "+ str(total_energy_cgs[0] + total_kinetic_energy_cgs[0]) + " energy change fraction of total " + str(total_energy_released_cgs/(total_energy_cgs[0]+total_kinetic_energy_cgs[0]))) + +# Plot the energy evolution +figure() +subplot(111) +plot(time*unit_time_in_cgs/Gyr_in_cgs, total_energy_cgs + total_kinetic_energy_cgs - total_energy_cgs[0] - total_kinetic_energy_cgs[0],color='k', linewidth=0.5, label="SWIFT") +plot(time*unit_time_in_cgs/Gyr_in_cgs, expected_energy_released_cgs,color = 'r', linewidth=0.5, label="expected") +xlabel("Time (Gyr)") +ylabel("Total energy (erg)") +legend() +savefig("continuous_energy_evolution.png", dpi=200) + diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/check_stellar_evolution.py b/examples/SubgridTests/CosmologicalStellarEvolution/check_stellar_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..5680eb4d64f29ab32831d995e31ae9c27de82a71 --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/check_stellar_evolution.py @@ -0,0 +1,318 @@ +import matplotlib + +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path +import numpy as np +import glob + +# Number of snapshots and elements +newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime) +n_snapshots = ( + int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1 +) +n_elements = 9 + +# Plot parameters +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 9, + "legend.fontsize": 9, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (3.15, 3.15), + "figure.subplot.left": 0.3, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.18, + "figure.subplot.top": 0.92, + "figure.subplot.wspace": 0.21, + "figure.subplot.hspace": 0.19, + "lines.markersize": 6, + "lines.linewidth": 2.0, + "text.latex.unicode": True, +} + +rcParams.update(params) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs[ + "Hydrogen ionization transition temperature" +][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] +star_initial_mass = sim["/PartType4/Masses"][0] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Find out how many particles (gas and star) we have +n_parts = sim["/Header"].attrs["NumPart_Total"][0] +n_sparts = sim["/Header"].attrs["NumPart_Total"][4] + +# Declare arrays for data +masses = zeros((n_parts, n_snapshots)) +star_masses = zeros((n_sparts, n_snapshots)) +mass_from_AGB = zeros((n_parts, n_snapshots)) +metal_mass_frac_from_AGB = zeros((n_parts, n_snapshots)) +mass_from_SNII = zeros((n_parts, n_snapshots)) +metal_mass_frac_from_SNII = zeros((n_parts, n_snapshots)) +mass_from_SNIa = zeros((n_parts, n_snapshots)) +metal_mass_frac_from_SNIa = zeros((n_parts, n_snapshots)) +iron_mass_frac_from_SNIa = zeros((n_parts, n_snapshots)) +metallicity = zeros((n_parts, n_snapshots)) +abundances = zeros((n_parts, n_elements, n_snapshots)) +internal_energy = zeros((n_parts, n_snapshots)) +coord_parts = zeros((n_parts, 3)) +velocity_parts = zeros((n_parts, 3, n_snapshots)) +coord_sparts = zeros(3) +time = zeros(n_snapshots) + +# Read fields we are checking from snapshots +for i in range(n_snapshots): + sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r") + print("reading snapshot " + str(i)) + abundances[:, :, i] = sim["/PartType0/ElementMassFractions"] + metallicity[:, i] = sim["/PartType0/Metallicities"] + masses[:, i] = sim["/PartType0/Masses"] + star_masses[:, i] = sim["/PartType4/Masses"] + mass_from_AGB[:, i] = sim["/PartType0/TotalMassFromAGB"] + metal_mass_frac_from_AGB[:, i] = sim["/PartType0/MetalMassFracFromAGB"] + mass_from_SNII[:, i] = sim["/PartType0/TotalMassFromSNII"] + metal_mass_frac_from_SNII[:, i] = sim["/PartType0/MetalMassFracFromSNII"] + mass_from_SNIa[:, i] = sim["/PartType0/TotalMassFromSNIa"] + metal_mass_frac_from_SNIa[:, i] = sim["/PartType0/MetalMassFracFromSNIa"] + iron_mass_frac_from_SNIa[:, i] = sim["/PartType0/IronMassFracFromSNIa"] + internal_energy[:, i] = sim["/PartType0/InternalEnergies"] + velocity_parts[:, :, i] = sim["/PartType0/Velocities"] + time[i] = sim["/Header"].attrs["Time"][0] + +# Define ejecta factor +ejecta_factor = 1.0e-2 +ejecta_factor_metallicity = 1.0 - 2.0 / n_elements +ejecta_factor_abundances = 1.0 / n_elements +ejected_mass = star_initial_mass +energy_per_SNe = 1.0e51 / unit_energy_in_cgs + +# Check that the total amount of enrichment is as expected. +# Define tolerance +eps = 0.01 + +# Total mass +total_part_mass = np.sum(masses, axis=0) +if ( + abs( + (total_part_mass[n_snapshots - 1] - total_part_mass[0]) / total_part_mass[0] + - ejected_mass / total_part_mass[0] + ) + * total_part_mass[0] + / ejected_mass + < eps +): + print("total mass released consistent with expectation") +else: + print( + "mass increase " + + str(total_part_mass[n_snapshots - 1] / total_part_mass[0]) + + " expected " + + str(1.0 + ejected_mass / total_part_mass[0]) + ) + +# Check that mass is conserved (i.e. total star mass decreases by same amount as total gas mass increases) +total_spart_mass = np.sum(star_masses, axis=0) +if ( + abs( + (total_part_mass[n_snapshots - 1] + total_spart_mass[n_snapshots - 1]) + / (total_part_mass[0] + total_spart_mass[0]) + - 1.0 + ) + < eps ** 3 +): + print("total mass conserved") +else: + print( + "initial part, spart mass " + + str(total_part_mass[0]) + + " " + + str(total_spart_mass[0]) + + " final mass " + + str(total_part_mass[n_snapshots - 1]) + + " " + + str(total_spart_mass[n_snapshots - 1]) + ) + +# Total metal mass from AGB +total_metal_mass_AGB = np.sum(np.multiply(metal_mass_frac_from_AGB, masses), axis=0) +expected_metal_mass_AGB = ejecta_factor * ejected_mass +if ( + abs(total_metal_mass_AGB[n_snapshots - 1] - expected_metal_mass_AGB) + / expected_metal_mass_AGB + < eps +): + print("total AGB metal mass released consistent with expectation") +else: + print( + "total AGB metal mass " + + str(total_metal_mass_AGB[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass_AGB) + ) + +# Total mass from AGB +total_AGB_mass = np.sum(mass_from_AGB, axis=0) +expected_AGB_mass = ejecta_factor * ejected_mass +if abs(total_AGB_mass[n_snapshots - 1] - expected_AGB_mass) / expected_AGB_mass < eps: + print("total AGB mass released consistent with expectation") +else: + print( + "total AGB mass " + + str(total_AGB_mass[n_snapshots - 1]) + + " expected " + + str(expected_AGB_mass) + ) + +# Total metal mass from SNII +total_metal_mass_SNII = np.sum(np.multiply(metal_mass_frac_from_SNII, masses), axis=0) +expected_metal_mass_SNII = ejecta_factor * ejected_mass +if ( + abs(total_metal_mass_SNII[n_snapshots - 1] - expected_metal_mass_SNII) + / expected_metal_mass_SNII + < eps +): + print("total SNII metal mass released consistent with expectation") +else: + print( + "total SNII metal mass " + + str(total_metal_mass_SNII[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass_SNII) + ) + +# Total mass from SNII +total_SNII_mass = np.sum(mass_from_SNII, axis=0) +expected_SNII_mass = ejecta_factor * ejected_mass +if ( + abs(total_SNII_mass[n_snapshots - 1] - expected_SNII_mass) / expected_SNII_mass + < eps +): + print("total SNII mass released consistent with expectation") +else: + print( + "total SNII mass " + + str(total_SNII_mass[n_snapshots - 1]) + + " expected " + + str(expected_SNII_mass) + ) + +# Total metal mass from SNIa +total_metal_mass_SNIa = np.sum(np.multiply(metal_mass_frac_from_SNIa, masses), axis=0) +expected_metal_mass_SNIa = ejecta_factor * ejected_mass +if ( + abs(total_metal_mass_SNIa[n_snapshots - 1] - expected_metal_mass_SNIa) + / expected_metal_mass_SNIa + < eps +): + print("total SNIa metal mass released consistent with expectation") +else: + print( + "total SNIa metal mass " + + str(total_metal_mass_SNIa[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass_SNIa) + ) + +# Total iron mass from SNIa +total_iron_mass_SNIa = np.sum(np.multiply(iron_mass_frac_from_SNIa, masses), axis=0) +expected_iron_mass_SNIa = ejecta_factor * ejected_mass +if ( + abs(total_iron_mass_SNIa[n_snapshots - 1] - expected_iron_mass_SNIa) + / expected_iron_mass_SNIa + < eps +): + print("total SNIa iron mass released consistent with expectation") +else: + print( + "total SNIa iron mass " + + str(total_iron_mass_SNIa[n_snapshots - 1]) + + " expected " + + str(expected_iron_mass_SNIa) + ) + +# Total mass from SNIa +total_SNIa_mass = np.sum(mass_from_SNIa, axis=0) +expected_SNIa_mass = ejecta_factor * ejected_mass +if ( + abs(total_SNIa_mass[n_snapshots - 1] - expected_SNIa_mass) / expected_SNIa_mass + < eps +): + print("total SNIa mass released consistent with expectation") +else: + print( + "total SNIa mass " + + str(total_SNIa_mass[n_snapshots - 1]) + + " expected " + + str(expected_SNIa_mass) + ) + +# Total metal mass +total_metal_mass = np.sum(np.multiply(metallicity, masses), axis=0) +expected_metal_mass = ejecta_factor_metallicity * ejected_mass +if ( + abs(total_metal_mass[n_snapshots - 1] - expected_metal_mass) / expected_metal_mass + < eps +): + print("total metal mass released consistent with expectation") +else: + print( + "total metal mass " + + str(total_metal_mass[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass) + ) + +# Total mass for each element +expected_element_mass = ejecta_factor_abundances * ejected_mass +for i in range(n_elements): + total_element_mass = np.sum(np.multiply(abundances[:, i, :], masses), axis=0) + if ( + abs(total_element_mass[n_snapshots - 1] - expected_element_mass) + / expected_element_mass + < eps + ): + print( + "total element mass released consistent with expectation for element " + + str(i) + ) + else: + print( + "total element mass " + + str(total_element_mass[n_snapshots - 1]) + + " expected " + + str(expected_element_mass) + + " for element " + + str(i) + ) + diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py b/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py new file mode 100644 index 0000000000000000000000000000000000000000..1cacc13653d821da3abd2a09566be347608c64f7 --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py @@ -0,0 +1,137 @@ +# Script for plotting energy evolution of uniform box of gas with single star in the +# centre when running with stochastic energy injection. It also checks that the change +# in total energy of the gas particles is within a specified tolerance from what is +# expected based on the mass of the star particle (Note that this tolerance could be +# somewhat high because of Poisson noise and the relatively small number of injection +# events) + +import matplotlib +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path +import numpy as np +import glob + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 9, +'legend.fontsize': 9, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (3.15,3.15), +'figure.subplot.left' : 0.3, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.18, +'figure.subplot.top' : 0.92, +'figure.subplot.wspace' : 0.21, +'figure.subplot.hspace' : 0.19, +'lines.markersize' : 6, +'lines.linewidth' : 2., +'text.latex.unicode': True +} + +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] +star_initial_mass = sim["/PartType4/Masses"][0] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Calculate solar mass in internal units +const_solar_mass = 1.98848e33 / unit_mass_in_cgs + +# Define Gyr +Gyr_in_cgs = 1e9 * 365 * 24 * 3600. + +# Find out how many particles (gas and star) we have +n_parts = sim["/Header"].attrs["NumPart_Total"][0] +n_sparts = sim["/Header"].attrs["NumPart_Total"][4] + +# Declare arrays for data +masses = zeros((n_parts,n_snapshots)) +star_masses = zeros((n_sparts,n_snapshots)) +internal_energy = zeros((n_parts,n_snapshots)) +velocity_parts = zeros((n_parts,3,n_snapshots)) +time = zeros(n_snapshots) + +# Read fields we are checking from snapshots +#for i in [0,n_snapshots-1]: +for i in range(n_snapshots): + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + print('reading snapshot '+str(i)) + masses[:,i] = sim["/PartType0/Masses"] + internal_energy[:,i] = sim["/PartType0/InternalEnergies"] + velocity_parts[:,:,i] = sim["/PartType0/Velocities"] + time[i] = sim["/Header"].attrs["Time"][0] + +# Check that the total amount of enrichment is as expected. +# Define tolerance. Note, relatively high value used due to +# Poisson noise associated with stochastic energy injection. +eps = 0.15 + +# Stochastic heating +vel2 = zeros((n_parts,n_snapshots)) +vel2[:,:] = velocity_parts[:,0,:]*velocity_parts[:,0,:] + velocity_parts[:,1,:]*velocity_parts[:,1,:] + velocity_parts[:,2,:]*velocity_parts[:,2,:] +total_kinetic_energy_cgs = np.sum(np.multiply(vel2,masses)*0.5,axis = 0) * unit_energy_in_cgs +total_energy_cgs = np.sum(np.multiply(internal_energy,masses),axis = 0) * unit_energy_in_cgs +total_energy_released_cgs = total_energy_cgs[n_snapshots-1] - total_energy_cgs[0] + total_kinetic_energy_cgs[n_snapshots-1] - total_kinetic_energy_cgs[0] + +# Calculate energy released +num_SNII_per_msun = 1.73621e-02 +energy_per_sn = 1.0e51 / unit_energy_in_cgs +expected_energy_released_cgs = np.zeros(n_snapshots) +for i in range(n_snapshots): + if time[i]*unit_time_in_cgs/Gyr_in_cgs < 0.03: + expected_energy_released_cgs[i] = 0 + else: + expected_energy_released_cgs[i] = num_SNII_per_msun * star_initial_mass / const_solar_mass * energy_per_sn * unit_energy_in_cgs + +# Did we get it right? +if abs(total_energy_released_cgs - expected_energy_released_cgs[n_snapshots-1])/expected_energy_released_cgs[n_snapshots-1] < eps: + print("total stochastic energy release consistent with expectation. total stochastic energy release "+str(total_energy_released_cgs)+" expected "+ str(expected_energy_released_cgs[n_snapshots-1]) + " initial total internal energy "+ str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])) +else: + print("total stochastic energy release "+str(total_energy_released_cgs)+" expected "+ str(expected_energy_released_cgs[n_snapshots-1]) + " initial total internal energy "+ str(total_energy_cgs[0] + total_kinetic_energy_cgs[0]) + " energy change fraction of total " + str(total_energy_released_cgs/(total_energy_cgs[0]+total_kinetic_energy_cgs[0]))) + +# Plot the energy evolution +figure() +subplot(111) +plot(time*unit_time_in_cgs/Gyr_in_cgs, total_energy_cgs + total_kinetic_energy_cgs - total_energy_cgs[0] - total_kinetic_energy_cgs[0],color='k', linewidth=0.5, label="SWIFT") +plot(time*unit_time_in_cgs/Gyr_in_cgs, expected_energy_released_cgs,color = 'r', linewidth=0.5, label="expected") +xlabel("Time (Gyr)") +ylabel("Total energy (erg)") +legend() +savefig("stochastic_energy_evolution.png", dpi=200) + diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/getEagleYieldTable.sh b/examples/SubgridTests/CosmologicalStellarEvolution/getEagleYieldTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..26eef020cab82acee2c80e88089df1790b281eab --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/getEagleYieldTable.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/YieldTables/EAGLE/yieldtables.tar.gz +tar -xf yieldtables.tar.gz diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/getGlass.sh b/examples/SubgridTests/CosmologicalStellarEvolution/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ffd92e88deae6e91237059adac2a6c2067caee46 --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5 diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/getSolutions.sh b/examples/SubgridTests/CosmologicalStellarEvolution/getSolutions.sh new file mode 100755 index 0000000000000000000000000000000000000000..1fe0f1507a7efa1da843970ddcede681e846e4ec --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/getSolutions.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/StellarEvolutionSolution.tar.gz +tar -xvzf StellarEvolutionSolution.tar.gz diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/makeIC.py b/examples/SubgridTests/CosmologicalStellarEvolution/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..abc2d8126ae8ac1173f8918ffc818f9cb6bcb4fe --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/makeIC.py @@ -0,0 +1,144 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (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/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Some constants +solar_mass_cgs = 1.988480e33 +kpc_in_cm = 3.085678e21 +mp_cgs = 1.67e-24 +boltzmann_k_cgs = 1.38e-16 + +# Parameters +gamma = 5./3. # Gas adiabatic index +rho_cgs = mp_cgs # Background density +u0_cgs = 1.2e12 # Desired initial internal energy (1.2e12 ~ 10^4K) +P_cgs = rho_cgs*u0_cgs*(gamma - 1.) # Background pressure +fileName = "stellar_evolution.hdf5" + +# Units +unit_l_cgs = 3.085678e24 # kpc +unit_m_cgs = 1.988480e43 # 10^10 Msun +unit_v_cgs = 1e5 # km / s +unit_A_cgs = 1. +unit_T_cgs = 1. +unit_t_cgs = unit_l_cgs / unit_v_cgs + +boxsize_cgs = 10. * kpc_in_cm +vol_cgs = boxsize_cgs**3 + +#--------------------------------------------------- +glass = h5py.File("glassCube_32.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:,:] +eps = 1e-6 +pos = (pos - pos.min()) / (pos.max() - pos.min() + eps) * boxsize_cgs / unit_l_cgs +h = glass["/PartType0/SmoothingLength"][:] * boxsize_cgs / unit_l_cgs + +numPart = size(h) + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m_cgs = zeros(numPart) +u_cgs = zeros(numPart) +m = zeros(numPart) +u = zeros(numPart) + +m_cgs[:] = rho_cgs * vol_cgs / numPart +u_cgs[:] = P_cgs / (rho_cgs * (gamma - 1)) + +# Stars +star_pos = zeros((1, 3)) +star_pos[:,:] = 0.5 * boxsize_cgs / unit_l_cgs + +star_v = zeros((1, 3)) +star_v[:,:] = 0. + +# increase mass to keep it at center +star_m_cgs = m_cgs[0] +star_ids = array([numPart + 1]) +star_h = array([h.max()]) + +#-------------------------------------------------- + +# Check quantities are correct for debugging +print("part mass/msun " + str(m_cgs[0]/solar_mass_cgs) + " stellar mass/msun " + str(star_m_cgs/solar_mass_cgs)) +print("boxsize kpc " + str(boxsize_cgs/kpc_in_cm)) +print("density cm^-3 " + str(rho_cgs/mp_cgs)) +print("initial temperature K " + str(u_cgs[0] / boltzmann_k_cgs*((gamma - 1)*rho_cgs))) + +# Convert to internal units +star_m = star_m_cgs/unit_m_cgs +m[:] = m_cgs/unit_m_cgs +u[:] = u_cgs*unit_v_cgs**-2 +boxsize = boxsize_cgs/unit_l_cgs + + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [boxsize]*3 +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 1, 0] +#grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 1, 0] +#grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 0 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = unit_l_cgs +grp.attrs["Unit mass in cgs (U_M)"] = unit_m_cgs +grp.attrs["Unit time in cgs (U_t)"] = unit_t_cgs +grp.attrs["Unit current in cgs (U_I)"] = unit_A_cgs +grp.attrs["Unit temperature in cgs (U_T)"] = unit_T_cgs + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +# stellar group +grp = file.create_group("/PartType4") +grp.create_dataset("Coordinates", data=star_pos, dtype="d") +grp.create_dataset('Velocities', data=star_v, dtype='f') +grp.create_dataset('Masses', data=star_m, dtype='f') +grp.create_dataset('SmoothingLength', data=star_h, dtype='f') +grp.create_dataset('ParticleIDs', data=star_ids, dtype='L') + +file.close() diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/plot_box_evolution.py b/examples/SubgridTests/CosmologicalStellarEvolution/plot_box_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..96f4acf4352660d9ff05ffa62b83b156e654da9f --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/plot_box_evolution.py @@ -0,0 +1,253 @@ +############################################################################### + # 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/>. + # + ############################################################################## + +# Script used to plot time evolution of gas particle properties. Intended to +# compare result of feedback due to one star placed in centre of uniform box +# of gas with output from EAGLE feedback test. Can also use as input output +# from SWIFT feedback test (tests/testFeedback) with the appropriate change +# to filepath. + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py +import numpy as np +import glob +import os.path + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.05, +'figure.subplot.right' : 0.995, +'figure.subplot.bottom' : 0.06, +'figure.subplot.top' : 0.92, +'figure.subplot.wspace' : 0.25, +'figure.subplot.hspace' : 0.2, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'))#, key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] +stellar_mass = sim["/PartType4/Masses"][0] +E_SNII_cgs = double(sim["/Parameters"].attrs["EAGLEFeedback:SNII_energy_erg"]) +E_SNIa_cgs = double(sim["/Parameters"].attrs["EAGLEFeedback:SNIa_energy_erg"]) +ejecta_vel_cgs = double(sim["/Parameters"].attrs["EAGLEFeedback:AGB_ejecta_velocity_km_p_s"]) * 1e5 + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_temp_in_cgs = sim["/Units"].attrs["Unit temperature in cgs (U_T)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_density_in_cgs = unit_mass_in_cgs*unit_length_in_cgs**-3 +unit_pressure_in_cgs = unit_mass_in_cgs/unit_length_in_cgs*unit_time_in_cgs**-2 +unit_int_energy_in_cgs = unit_energy_in_cgs/unit_mass_in_cgs +unit_entropy_in_cgs = unit_energy_in_cgs/unit_temp_in_cgs +Gyr_in_cgs = 1e9 * 365. * 24 * 3600. +Msun_in_cgs = 1.98848e33 + +# Declare arrays to store SWIFT data +swift_box_gas_mass = zeros(n_snapshots) +swift_box_star_mass = zeros(n_snapshots) +swift_box_gas_metal_mass = zeros(n_snapshots) +swift_element_mass = zeros((n_snapshots,n_elements)) +swift_internal_energy = zeros(n_snapshots) +swift_kinetic_energy = zeros(n_snapshots) +swift_total_energy = zeros(n_snapshots) +swift_mean_u_start = 0. +t = zeros(n_snapshots) + +# Read data from snapshots +for i in range(n_snapshots): + #print("reading snapshot "+str(i)) + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + t[i] = sim["/Header"].attrs["Time"][0] + + masses = sim["/PartType0/Masses"][:] + swift_box_gas_mass[i] = np.sum(masses) + + Z_star = sim["/PartType4/MetalMassFractions"][0] + star_masses = sim["/PartType4/Masses"][:] + swift_box_star_mass[i] = np.sum(star_masses) + + metallicities = sim["/PartType0/MetalMassFractions"][:] + swift_box_gas_metal_mass[i] = np.sum(metallicities * masses) + + element_abundances = sim["/PartType0/ElementMassFractions"][:][:] + for j in range(n_elements): + swift_element_mass[i,j] = np.sum(element_abundances[:,j] * masses) + + v = sim["/PartType0/Velocities"][:,:] + v2 = v[:,0]**2 + v[:,1]**2 + v[:,2]**2 + u = sim["/PartType0/InternalEnergies"][:] + swift_internal_energy[i] = np.sum(masses * u) + swift_kinetic_energy[i] = np.sum(0.5 * masses * v2) + swift_total_energy[i] = swift_kinetic_energy[i] + swift_internal_energy[i] + + if i == 0: + swift_mean_u_start = np.mean(u) + + sim.close() + +# Read expected yields from EAGLE. Choose which file to use based on metallicity used when +# running SWIFT (can be specified in yml file) +filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionTotal.txt"%Z_star + +# Read EAGLE test output +with open(filename) as f: + eagle_categories = f.readline() + eagle_data = f.readlines() + +eagle_data = [x.strip() for x in eagle_data] + +# Declare arrays to store EAGLE test output +eagle_time_Gyr = zeros(len(eagle_data)) +eagle_total_mass = zeros(len(eagle_data)) +eagle_total_metal_mass = zeros(len(eagle_data)) +eagle_total_element_mass = zeros((len(eagle_data),n_elements)) +eagle_energy_from_mass_cgs = zeros(len(eagle_data)) +eagle_energy_ejecta_cgs = zeros(len(eagle_data)) + +# Populate arrays with data from EAGLE test output +i = 0 +for line in eagle_data: + enrich_to_date = line.split(' ') + eagle_time_Gyr[i] = float(enrich_to_date[0]) + eagle_total_mass[i] = float(enrich_to_date[1]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + eagle_total_metal_mass[i] = float(enrich_to_date[2]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + for j in range(n_elements): + eagle_total_element_mass[i,j] = float(enrich_to_date[3+j]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + eagle_energy_from_mass_cgs[i] = eagle_total_mass[i] * Msun_in_cgs * swift_mean_u_start * unit_int_energy_in_cgs + eagle_energy_ejecta_cgs[i] = 0.5 * (eagle_total_mass[i] * Msun_in_cgs) * ejecta_vel_cgs**2 + i += 1 + +# Read the number of SNIa +filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionIa.txt"%Z_star +with open(filename) as f: + eagle_categories = f.readline() + eagle_data = f.readlines() +i = 0 +N_SNIa = zeros(len(eagle_data)) +for line in eagle_data: + enrich_to_date = line.split(' ') + N_SNIa[i] = float(enrich_to_date[-2]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + i += 1 + +cumulative_N_SNIa = np.cumsum(N_SNIa) +eagle_energy_SNIa_cgs = cumulative_N_SNIa * E_SNIa_cgs + +# SNII energy +N_SNII = 0.017362 * stellar_mass / Msun_in_cgs * unit_mass_in_cgs +eagle_energy_SNII_cgs = np.ones(len(eagle_data)) * N_SNII * E_SNII_cgs +eagle_energy_SNII_cgs[eagle_time_Gyr < 0.03] = 0. + +# Total energy +eagle_energy_total_cgs = eagle_energy_ejecta_cgs + eagle_energy_from_mass_cgs + eagle_energy_SNIa_cgs + + + +# Plot the interesting quantities +figure() + +suptitle("Star metallicity Z = %.4f"%Z_star) + +# Box gas mass -------------------------------- +subplot(221) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_mass[1:] - swift_box_gas_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +plot(eagle_time_Gyr[1:],eagle_total_mass[:-1],linewidth=0.5,color='r',label='eagle test total', ls='--') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total gas particle mass (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +legend() + +# Box star mass -------------------------------- +subplot(222) +plot(t * unit_time_in_cgs / Gyr_in_cgs, (swift_box_star_mass)* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +plot(eagle_time_Gyr[1:], swift_box_star_mass[0] * unit_mass_in_cgs / Msun_in_cgs - eagle_total_mass[:-1],linewidth=0.5,color='r',label='eagle test total') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total star particle mass (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +legend() + +# Box gas element mass -------------------------------- +colours = ['k','r','g','b','c','y','m','skyblue','plum'] +element_names = ['H','He','C','N','O','Ne','Mg','Si','Fe'] +subplot(223) +for j in range(n_elements): + plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_element_mass[1:,j] - swift_element_mass[0,j]) * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color=colours[j], ms=0.5, label=element_names[j]) + plot(eagle_time_Gyr[1:],eagle_total_element_mass[:-1,j],linewidth=1,color=colours[j],linestyle='--') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in element mass of gas particles (Msun)", labelpad=2) +xscale("log") +yscale("log") +legend(bbox_to_anchor=(1.005, 1.), ncol=1, fontsize=8, handlelength=1) + +# Box gas metal mass -------------------------------- +subplot(224) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_metal_mass[1:] - swift_box_gas_metal_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +plot(eagle_time_Gyr[1:],eagle_total_metal_mass[:-1],linewidth=0.5,color='r',label='eagle test') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total metal mass of gas particles (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +savefig("box_evolution_Z_%.4f.png"%(Z_star), dpi=200) + + + +# Energy plot +figure() +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_total_energy[1:] - swift_total_energy[0]) * unit_energy_in_cgs, linewidth=0.5, color='k', label='swift') +plot(eagle_time_Gyr[1:], eagle_energy_SNIa_cgs[:-1], linewidth=0.5, color='b', label='eagle SNIa') +plot(eagle_time_Gyr[1:], eagle_energy_SNII_cgs[:-1], linewidth=0.5, color='c', label='eagle SNII') +plot(eagle_time_Gyr[1:], eagle_energy_ejecta_cgs[:-1], linewidth=0.5, color='y', label='eagle ejecta velocity') +plot(eagle_time_Gyr[1:], eagle_energy_from_mass_cgs[:-1], linewidth=0.5, color='g', label='eagle mass energy') +plot(eagle_time_Gyr[1:], eagle_energy_total_cgs[:-1], linewidth=0.5, color='r', label='eagle total') +plot([0,0], [0,0]) +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in internal energy of gas particles (erg)", labelpad=2) +yscale("log") +legend(loc="lower right", ncol=2) + +savefig("Energy.png") diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/plot_particle_evolution.py b/examples/SubgridTests/CosmologicalStellarEvolution/plot_particle_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..be1588f9b707d448b2905611defd9e760c5f91de --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/plot_particle_evolution.py @@ -0,0 +1,223 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) +# 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/>. +# +############################################################################## + +# Assuming output snapshots contain evolution of box of gas with star at its +# centre, this script will plot the evolution of the radial velocities, internal +# energies, mass and metallicities of the nearest n particles to the star over +# the duration of the simulation. + +import matplotlib + +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py +import numpy as np +import glob +import os.path + +# Function to find index in array a for each element in array b +def find_indices(a, b): + result = np.zeros(len(b)) + for i in range(len(b)): + result[i] = ((np.where(a == b[i]))[0])[0] + return result + + +# Plot parameters +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 12, + "legend.fontsize": 12, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (9.90, 6.45), + "figure.subplot.left": 0.1, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.1, + "figure.subplot.top": 0.95, + "figure.subplot.wspace": 0.2, + "figure.subplot.hspace": 0.2, + "lines.markersize": 6, + "lines.linewidth": 3.0, + "text.latex.unicode": True, +} +rcParams.update(params) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + + +# Number of snapshots and elements +newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime) +n_snapshots = ( + int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1 +) +n_particles_to_plot = 500 + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_temp_in_cgs = sim["/Units"].attrs["Unit temperature in cgs (U_T)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs +unit_density_in_cgs = unit_mass_in_cgs * unit_length_in_cgs ** -3 +unit_pressure_in_cgs = unit_mass_in_cgs / unit_length_in_cgs * unit_time_in_cgs ** -2 +unit_int_energy_in_cgs = unit_energy_in_cgs / unit_mass_in_cgs +unit_entropy_in_cgs = unit_energy_in_cgs / unit_temp_in_cgs +Myr_in_cgs = 3.154e13 +Msun_in_cgs = 1.989e33 + +# Read data of zeroth snapshot +pos = sim["/PartType0/Coordinates"][:, :] +x = pos[:, 0] - boxSize / 2 +y = pos[:, 1] - boxSize / 2 +z = pos[:, 2] - boxSize / 2 +vel = sim["/PartType0/Velocities"][:, :] +r = sqrt(x ** 2 + y ** 2 + z ** 2) +v_r = (x * vel[:, 0] + y * vel[:, 1] + z * vel[:, 2]) / r +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] +mass = sim["/PartType0/Masses"][:] +IDs = sim["/PartType0/ParticleIDs"][:] + +# Find which particles are closest to centre of box +index = argsort(r) +part_IDs_to_plot = zeros(n_particles_to_plot) +part_IDs_to_plot = np.sort(IDs[index[0:n_particles_to_plot]]) + +# Declare arrrays to plot +masses_to_plot = zeros((n_particles_to_plot, n_snapshots)) +v_r_to_plot = zeros((n_particles_to_plot, n_snapshots)) +metallicities_to_plot = zeros((n_particles_to_plot, n_snapshots)) +internal_energies_to_plot = zeros((n_particles_to_plot, n_snapshots)) +t = zeros(n_snapshots) + +# Read data from rest of snapshots +for i in range(n_snapshots): + print("reading snapshot " + str(i)) + # Read the simulation data + sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r") + t[i] = sim["/Header"].attrs["Time"][0] + + pos = sim["/PartType0/Coordinates"][:, :] + x = pos[:, 0] - boxSize / 2 + y = pos[:, 1] - boxSize / 2 + z = pos[:, 2] - boxSize / 2 + vel = sim["/PartType0/Velocities"][:, :] + r = sqrt(x ** 2 + y ** 2 + z ** 2) + v_r = (x * vel[:, 0] + y * vel[:, 1] + z * vel[:, 2]) / r + u = sim["/PartType0/InternalEnergies"][:] + S = sim["/PartType0/Entropies"][:] + P = sim["/PartType0/Pressures"][:] + rho = sim["/PartType0/Densities"][:] + mass = sim["/PartType0/Masses"][:] + metallicity = sim["/PartType0/Metallicities"][:] + internal_energy = sim["/PartType0/InternalEnergies"][:] + IDs = sim["/PartType0/ParticleIDs"][:] + + # Find which particles we want to plot and store their data + indices = (find_indices(IDs, part_IDs_to_plot)).astype(int) + masses_to_plot[:, i] = mass[indices[:]] + v_r_to_plot[:, i] = v_r[indices[:]] + metallicities_to_plot[:, i] = metallicity[indices[:]] + internal_energies_to_plot[:, i] = internal_energy[indices[:]] + + +# Plot the interesting quantities +figure() + +# Radial velocity -------------------------------- +subplot(221) +for j in range(n_particles_to_plot): + plot( + t * unit_time_in_cgs / Myr_in_cgs, + v_r_to_plot[j, :] * unit_vel_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) +xlabel("Time (Myr)", labelpad=0) +ylabel("Radial velocity $(\\rm{cm} \cdot \\rm{s}^{-1})$", labelpad=0) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + +# Internal energy -------------------------------- +subplot(222) +for j in range(n_particles_to_plot): + plot( + t * unit_time_in_cgs / Myr_in_cgs, + internal_energies_to_plot[j, :] * unit_energy_in_cgs / unit_mass_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) +xlabel("Time (Myr)", labelpad=0) +ylabel("Internal energy $(\\rm{erg} \cdot \\rm{g}^{-1})$", labelpad=2) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + +# Masses -------------------------------- +subplot(223) +for j in range(n_particles_to_plot): + plot( + t * unit_time_in_cgs / Myr_in_cgs, + masses_to_plot[j, :] * unit_mass_in_cgs / Msun_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) +xlabel("Time (Myr)", labelpad=0) +ylabel("Mass (Msun)", labelpad=2) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + +# Metallicities -------------------------------- +subplot(224) +for j in range(n_particles_to_plot): + plot( + t * unit_time_in_cgs / Myr_in_cgs, + metallicities_to_plot[j, :], + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) +xlabel("Time (Myr)", labelpad=0) +ylabel("Metallicity", labelpad=2) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + +savefig("particle_evolution.png", dpi=200) diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/run.sh b/examples/SubgridTests/CosmologicalStellarEvolution/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..637a100567f691d883b95f78c4513e0640c1932a --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/run.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_32.hdf5 ] +then + echo "Fetching initial glass file for the Supernovae feedback example..." + ./getGlass.sh +fi +if [ ! -e stellar_evolution.hdf5 ] +then + echo "Generating initial conditions for the 3D stellar evolution example..." + python makeIC.py +fi + +# Get the Yield tables +if [ ! -e yieldtables ] +then + echo "Fetching Yield tables..." + ./getEagleYieldTable.sh +fi + +# Get the solutions +if [ ! -e StellarEvolutionSolution ] +then + echo "Fetching solutions ..." + ./getSolutions.sh +fi + +../../swift --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 2>&1 | tee output_0p08.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 2>&1 | tee output_0p04.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.01 2>&1 | tee output_0p01.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.001 2>&1 | tee output_0p001.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.0001 2>&1 | tee output_0p0001.log + +python plot_box_evolution.py diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/stellar_evolution.yml b/examples/SubgridTests/CosmologicalStellarEvolution/stellar_evolution.yml new file mode 100644 index 0000000000000000000000000000000000000000..b3d318d68b69d0940d7a37b17ae5331a711b140f --- /dev/null +++ b/examples/SubgridTests/CosmologicalStellarEvolution/stellar_evolution.yml @@ -0,0 +1,121 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1. # Amperes + UnitTemp_in_cgs: 1. # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.0099 # Initial scale-factor of the simulation (z = 100.0) + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0482519 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 5e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: stellar_evolution # Common part of the name of output files + delta_time: 1.03 + scale_factor_first: 0.02 + compression: 4 + +# Parameters governing the conserved quantities statistics +Statistics: + scale_factor_first: 0.00991 + delta_time: 1.1 + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100. # Kelvin + +# Properties of the stars +Stars: + overwrite_birth_time: 1 + birth_time: 0.00991 # Give the star in the ICs a decent birth time + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./stellar_evolution.hdf5 # The file to read + periodic: 1 + +Scheduler: + max_top_level_cells: 8 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# Metallicites read in for the gas and star +EAGLEChemistry: + init_abundance_metal: 0.01 + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0.0 + init_abundance_Nitrogen: 0.0 + init_abundance_Oxygen: 0.0 + init_abundance_Neon: 0.0 + init_abundance_Magnesium: 0.0 + init_abundance_Silicon: 0.0 + init_abundance_Iron: 0.0 + +# Standard EAGLE cooling options +EAGLECooling: + dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables + H_reion_z: 11.5 # Redshift of Hydrogen re-ionization + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 # Redshift of the centre of the Helium re-ionization Gaussian + He_reion_z_sigma: 0.5 # Spread in redshift of the Helium re-ionization Gaussian + He_reion_eV_p_H: 2.0 # Energy inject by Helium re-ionization in electron-volt per Hydrogen atom + +# Properties of the EAGLE feedback and enrichment model. +EAGLEFeedback: + use_SNII_feedback: 0 # Global switch for SNII thermal (stochastic) feedback. + use_SNIa_feedback: 0 # Global switch for SNIa thermal (continuous) feedback. + use_AGB_enrichment: 1 # Global switch for enrichement from AGB stars. + use_SNII_enrichment: 1 # Global switch for enrichement from SNII stars. + use_SNIa_enrichment: 1 # Global switch for enrichement from SNIa stars. + filename: ./yieldtables/ # Path to the directory containing the EAGLE yield tables. + IMF_min_mass_Msun: 0.1 # Minimal stellar mass considered for the Chabrier IMF in solar masses. + IMF_max_mass_Msun: 100.0 # Maximal stellar mass considered for the Chabrier IMF in solar masses. + SNII_min_mass_Msun: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + 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. diff --git a/examples/SubgridTests/SmoothedMetallicity/plotSolution.py b/examples/SubgridTests/SmoothedMetallicity/plotSolution.py index e5bca3dfb7fe1e43c836733894c9e297cdd468ca..068fe5378e19c34ee8a68398f4e0ed096d0982e0 100644 --- a/examples/SubgridTests/SmoothedMetallicity/plotSolution.py +++ b/examples/SubgridTests/SmoothedMetallicity/plotSolution.py @@ -3,20 +3,20 @@ # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) # 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/>. -# +# ############################################################################## # Computes the analytical solution of the 3D Smoothed Metallicity example. @@ -25,12 +25,13 @@ import h5py import sys import numpy as np import matplotlib + matplotlib.use("Agg") import matplotlib.pyplot as plt # Parameters -low_metal = -6 # low metal abundance -high_metal = -5 # High metal abundance +low_metal = -6 # low metal abundance +high_metal = -5 # High metal abundance sigma_metal = 0.1 # relative standard deviation for Z Nelem = 9 @@ -44,27 +45,27 @@ high_metal = [high_metal] * Nelem + np.linspace(0, 3, Nelem) # Plot parameters params = { - 'axes.labelsize': 10, - 'axes.titlesize': 10, - 'font.size': 12, - 'legend.fontsize': 12, - 'xtick.labelsize': 10, - 'ytick.labelsize': 10, - 'text.usetex': True, - 'figure.figsize': (9.90, 6.45), - 'figure.subplot.left': 0.045, - 'figure.subplot.right': 0.99, - 'figure.subplot.bottom': 0.05, - 'figure.subplot.top': 0.99, - 'figure.subplot.wspace': 0.15, - 'figure.subplot.hspace': 0.12, - 'lines.markersize': 6, - 'lines.linewidth': 3., - 'text.latex.unicode': True + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 12, + "legend.fontsize": 12, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (9.90, 6.45), + "figure.subplot.left": 0.045, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.05, + "figure.subplot.top": 0.99, + "figure.subplot.wspace": 0.15, + "figure.subplot.hspace": 0.12, + "lines.markersize": 6, + "lines.linewidth": 3.0, + "text.latex.unicode": True, } plt.rcParams.update(params) -plt.rc('font', **{'family': 'sans-serif', 'sans-serif': ['Times']}) +plt.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) snap = int(sys.argv[1]) @@ -83,18 +84,17 @@ git = sim["Code"].attrs["Git Revision"] pos = sim["/PartType0/Coordinates"][:, :] d = pos[:, 0] - boxSize / 2 -smooth_metal = sim["/PartType0/SmoothedElementAbundance"][:, :] -metal = sim["/PartType0/ElementAbundance"][:, :] -h = sim["/PartType0/SmoothingLength"][:] +smooth_metal = sim["/PartType0/SmoothedElementMassFractions"][:, :] +metal = sim["/PartType0/ElementMassFractions"][:, :] +h = sim["/PartType0/SmoothingLengths"][:] h = np.mean(h) -if (Nelem != metal.shape[1]): - print("Unexpected number of element, please check makeIC.py" - " and plotSolution.py") +if Nelem != metal.shape[1]: + print("Unexpected number of element, please check makeIC.py" " and plotSolution.py") exit(1) N = 1000 -d_a = np.linspace(-boxSize / 2., boxSize / 2., N) +d_a = np.linspace(-boxSize / 2.0, boxSize / 2.0, N) # Now, work our the solution.... @@ -142,14 +142,14 @@ def calc_a(d, high_metal, low_metal, std_dev, h): m = (high_metal[i] - low_metal[i]) / (2.0 * h) c = (high_metal[i] + low_metal[i]) / 2.0 # compute left linear part - s = d < - boxSize / 2.0 + h - a[s, i] = - m * (d[s] + boxSize / 2.0) + c + s = d < -boxSize / 2.0 + h + a[s, i] = -m * (d[s] + boxSize / 2.0) + c # compute middle linear part s = np.logical_and(d >= -h, d <= h) a[s, i] = m * d[s] + c # compute right linear part s = d > boxSize / 2.0 - h - a[s, i] = - m * (d[s] - boxSize / 2.0) + c + a[s, i] = -m * (d[s] - boxSize / 2.0) + c sigma[:, :, 0] = a * (1 + std_dev) sigma[:, :, 1] = a * (1 - std_dev) @@ -165,7 +165,7 @@ plt.figure() # Metallicity -------------------------------- plt.subplot(221) for e in range(Nelem): - plt.plot(metal[:, e], smooth_metal[:, e], '.', ms=0.5, alpha=0.2) + plt.plot(metal[:, e], smooth_metal[:, e], ".", ms=0.5, alpha=0.2) xmin, xmax = metal.min(), metal.max() ymin, ymax = smooth_metal.min(), smooth_metal.max() @@ -178,27 +178,28 @@ plt.ylabel("${\\rm{Smoothed~Metallicity}}~Z_\\textrm{sm}$", labelpad=0) # Metallicity -------------------------------- e = 0 plt.subplot(223) -plt.plot(d, smooth_metal[:, e], '.', color='r', ms=0.5, alpha=0.2) -plt.plot(d_a, sol[:, e], '--', color='b', alpha=0.8, lw=1.2) -plt.fill_between(d_a, sig[:, e, 0], sig[:, e, 1], facecolor="b", - interpolate=True, alpha=0.5) +plt.plot(d, smooth_metal[:, e], ".", color="r", ms=0.5, alpha=0.2) +plt.plot(d_a, sol[:, e], "--", color="b", alpha=0.8, lw=1.2) +plt.fill_between( + d_a, sig[:, e, 0], sig[:, e, 1], facecolor="b", interpolate=True, alpha=0.5 +) plt.xlabel("${\\rm{Distance}}~r$", labelpad=0) plt.ylabel("${\\rm{Smoothed~Metallicity}}~Z_\\textrm{sm}$", labelpad=0) plt.xlim(-0.5, 0.5) -plt.ylim(low_metal[e]-1, high_metal[e]+1) +plt.ylim(low_metal[e] - 1, high_metal[e] + 1) # Information ------------------------------------- plt.subplot(222, frameon=False) -plt.text(-0.49, 0.9, "Smoothed Metallicity in 3D at $t=%.2f$" % time, - fontsize=10) -plt.plot([-0.49, 0.1], [0.82, 0.82], 'k-', lw=1) +plt.text(-0.49, 0.9, "Smoothed Metallicity in 3D at $t=%.2f$" % time, fontsize=10) +plt.plot([-0.49, 0.1], [0.82, 0.82], "k-", lw=1) plt.text(-0.49, 0.7, "$\\textsc{Swift}$ %s" % git, fontsize=10) plt.text(-0.49, 0.6, scheme, fontsize=10) plt.text(-0.49, 0.5, kernel, fontsize=10) plt.text(-0.49, 0.4, chemistry + "'s Chemistry", fontsize=10) -plt.text(-0.49, 0.3, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), - fontsize=10) +plt.text( + -0.49, 0.3, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10 +) plt.xlim(-0.5, 0.5) plt.ylim(0, 1) plt.xticks([]) diff --git a/examples/SubgridTests/StellarEvolution/check_continuous_heating.py b/examples/SubgridTests/StellarEvolution/check_continuous_heating.py index f3c1b5d7fd682d914f2dbc05259c2dab0baf1e32..b5940eba43c89b8af4a883cc8f7022e33293b869 100644 --- a/examples/SubgridTests/StellarEvolution/check_continuous_heating.py +++ b/examples/SubgridTests/StellarEvolution/check_continuous_heating.py @@ -93,7 +93,7 @@ for i in range(n_snapshots): sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") print('reading snapshot '+str(i)) masses[:,i] = sim["/PartType0/Masses"] - internal_energy[:,i] = sim["/PartType0/InternalEnergy"] + internal_energy[:,i] = sim["/PartType0/InternalEnergies"] velocity_parts[:,:,i] = sim["/PartType0/Velocities"] time[i] = sim["/Header"].attrs["Time"][0] diff --git a/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py index 02c1e9343de7b58cfddc8dee3bf0215a4b80ccf4..5680eb4d64f29ab32831d995e31ae9c27de82a71 100644 --- a/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py +++ b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py @@ -1,4 +1,5 @@ import matplotlib + matplotlib.use("Agg") from pylab import * import h5py @@ -7,32 +8,35 @@ import numpy as np import glob # Number of snapshots and elements -newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) -n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime) +n_snapshots = ( + int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1 +) n_elements = 9 # Plot parameters -params = {'axes.labelsize': 10, -'axes.titlesize': 10, -'font.size': 9, -'legend.fontsize': 9, -'xtick.labelsize': 10, -'ytick.labelsize': 10, -'text.usetex': True, - 'figure.figsize' : (3.15,3.15), -'figure.subplot.left' : 0.3, -'figure.subplot.right' : 0.99, -'figure.subplot.bottom' : 0.18, -'figure.subplot.top' : 0.92, -'figure.subplot.wspace' : 0.21, -'figure.subplot.hspace' : 0.19, -'lines.markersize' : 6, -'lines.linewidth' : 2., -'text.latex.unicode': True +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 9, + "legend.fontsize": 9, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (3.15, 3.15), + "figure.subplot.left": 0.3, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.18, + "figure.subplot.top": 0.92, + "figure.subplot.wspace": 0.21, + "figure.subplot.hspace": 0.19, + "lines.markersize": 6, + "lines.linewidth": 2.0, + "text.latex.unicode": True, } rcParams.update(params) -rc('font',**{'family':'sans-serif','sans-serif':['Times']}) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) # Read the simulation data sim = h5py.File("stellar_evolution_0000.hdf5", "r") @@ -43,7 +47,9 @@ neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] eta = sim["/HydroScheme"].attrs["Kernel eta"][0] alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] -H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0] +H_transition_temp = sim["/HydroScheme"].attrs[ + "Hydrogen ionization transition temperature" +][0] T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] git = sim["Code"].attrs["Git Revision"] @@ -68,136 +74,245 @@ n_parts = sim["/Header"].attrs["NumPart_Total"][0] n_sparts = sim["/Header"].attrs["NumPart_Total"][4] # Declare arrays for data -masses = zeros((n_parts,n_snapshots)) -star_masses = zeros((n_sparts,n_snapshots)) -mass_from_AGB = zeros((n_parts,n_snapshots)) -metal_mass_frac_from_AGB = zeros((n_parts,n_snapshots)) -mass_from_SNII = zeros((n_parts,n_snapshots)) -metal_mass_frac_from_SNII = zeros((n_parts,n_snapshots)) -mass_from_SNIa = zeros((n_parts,n_snapshots)) -metal_mass_frac_from_SNIa = zeros((n_parts,n_snapshots)) -iron_mass_frac_from_SNIa = zeros((n_parts,n_snapshots)) -metallicity = zeros((n_parts,n_snapshots)) -abundances = zeros((n_parts,n_elements,n_snapshots)) -internal_energy = zeros((n_parts,n_snapshots)) -coord_parts = zeros((n_parts,3)) -velocity_parts = zeros((n_parts,3,n_snapshots)) +masses = zeros((n_parts, n_snapshots)) +star_masses = zeros((n_sparts, n_snapshots)) +mass_from_AGB = zeros((n_parts, n_snapshots)) +metal_mass_frac_from_AGB = zeros((n_parts, n_snapshots)) +mass_from_SNII = zeros((n_parts, n_snapshots)) +metal_mass_frac_from_SNII = zeros((n_parts, n_snapshots)) +mass_from_SNIa = zeros((n_parts, n_snapshots)) +metal_mass_frac_from_SNIa = zeros((n_parts, n_snapshots)) +iron_mass_frac_from_SNIa = zeros((n_parts, n_snapshots)) +metallicity = zeros((n_parts, n_snapshots)) +abundances = zeros((n_parts, n_elements, n_snapshots)) +internal_energy = zeros((n_parts, n_snapshots)) +coord_parts = zeros((n_parts, 3)) +velocity_parts = zeros((n_parts, 3, n_snapshots)) coord_sparts = zeros(3) time = zeros(n_snapshots) # Read fields we are checking from snapshots for i in range(n_snapshots): - sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") - print('reading snapshot '+str(i)) - abundances[:,:,i] = sim["/PartType0/ElementAbundance"] - metallicity[:,i] = sim["/PartType0/Metallicity"] - masses[:,i] = sim["/PartType0/Masses"] - star_masses[:,i] = sim["/PartType4/Masses"] - mass_from_AGB[:,i] = sim["/PartType0/TotalMassFromAGB"] - metal_mass_frac_from_AGB[:,i] = sim["/PartType0/MetalMassFracFromAGB"] - mass_from_SNII[:,i] = sim["/PartType0/TotalMassFromSNII"] - metal_mass_frac_from_SNII[:,i] = sim["/PartType0/MetalMassFracFromSNII"] - mass_from_SNIa[:,i] = sim["/PartType0/TotalMassFromSNIa"] - metal_mass_frac_from_SNIa[:,i] = sim["/PartType0/MetalMassFracFromSNIa"] - iron_mass_frac_from_SNIa[:,i] = sim["/PartType0/IronMassFracFromSNIa"] - internal_energy[:,i] = sim["/PartType0/InternalEnergy"] - velocity_parts[:,:,i] = sim["/PartType0/Velocities"] - time[i] = sim["/Header"].attrs["Time"][0] + sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r") + print("reading snapshot " + str(i)) + abundances[:, :, i] = sim["/PartType0/ElementMassFractions"] + metallicity[:, i] = sim["/PartType0/Metallicities"] + masses[:, i] = sim["/PartType0/Masses"] + star_masses[:, i] = sim["/PartType4/Masses"] + mass_from_AGB[:, i] = sim["/PartType0/TotalMassFromAGB"] + metal_mass_frac_from_AGB[:, i] = sim["/PartType0/MetalMassFracFromAGB"] + mass_from_SNII[:, i] = sim["/PartType0/TotalMassFromSNII"] + metal_mass_frac_from_SNII[:, i] = sim["/PartType0/MetalMassFracFromSNII"] + mass_from_SNIa[:, i] = sim["/PartType0/TotalMassFromSNIa"] + metal_mass_frac_from_SNIa[:, i] = sim["/PartType0/MetalMassFracFromSNIa"] + iron_mass_frac_from_SNIa[:, i] = sim["/PartType0/IronMassFracFromSNIa"] + internal_energy[:, i] = sim["/PartType0/InternalEnergies"] + velocity_parts[:, :, i] = sim["/PartType0/Velocities"] + time[i] = sim["/Header"].attrs["Time"][0] # Define ejecta factor ejecta_factor = 1.0e-2 -ejecta_factor_metallicity = 1.0 - 2.0/n_elements -ejecta_factor_abundances = 1.0/n_elements +ejecta_factor_metallicity = 1.0 - 2.0 / n_elements +ejecta_factor_abundances = 1.0 / n_elements ejected_mass = star_initial_mass -energy_per_SNe = 1.0e51/unit_energy_in_cgs +energy_per_SNe = 1.0e51 / unit_energy_in_cgs # Check that the total amount of enrichment is as expected. # Define tolerance eps = 0.01 # Total mass -total_part_mass = np.sum(masses,axis = 0) -if abs((total_part_mass[n_snapshots-1] - total_part_mass[0])/total_part_mass[0] - ejected_mass/total_part_mass[0])*total_part_mass[0]/ejected_mass < eps: - print("total mass released consistent with expectation") +total_part_mass = np.sum(masses, axis=0) +if ( + abs( + (total_part_mass[n_snapshots - 1] - total_part_mass[0]) / total_part_mass[0] + - ejected_mass / total_part_mass[0] + ) + * total_part_mass[0] + / ejected_mass + < eps +): + print("total mass released consistent with expectation") else: - print("mass increase "+str(total_part_mass[n_snapshots-1]/total_part_mass[0])+" expected "+ str(1.0+ejected_mass/total_part_mass[0])) + print( + "mass increase " + + str(total_part_mass[n_snapshots - 1] / total_part_mass[0]) + + " expected " + + str(1.0 + ejected_mass / total_part_mass[0]) + ) # Check that mass is conserved (i.e. total star mass decreases by same amount as total gas mass increases) -total_spart_mass = np.sum(star_masses,axis = 0) -if abs((total_part_mass[n_snapshots-1] + total_spart_mass[n_snapshots-1]) / (total_part_mass[0] + total_spart_mass[0]) - 1.0) < eps**3: - print("total mass conserved") +total_spart_mass = np.sum(star_masses, axis=0) +if ( + abs( + (total_part_mass[n_snapshots - 1] + total_spart_mass[n_snapshots - 1]) + / (total_part_mass[0] + total_spart_mass[0]) + - 1.0 + ) + < eps ** 3 +): + print("total mass conserved") else: - print("initial part, spart mass " + str(total_part_mass[0]) + " " + str(total_spart_mass[0]) + " final mass " + str(total_part_mass[n_snapshots-1]) + " " + str(total_spart_mass[n_snapshots-1])) + print( + "initial part, spart mass " + + str(total_part_mass[0]) + + " " + + str(total_spart_mass[0]) + + " final mass " + + str(total_part_mass[n_snapshots - 1]) + + " " + + str(total_spart_mass[n_snapshots - 1]) + ) # Total metal mass from AGB -total_metal_mass_AGB = np.sum(np.multiply(metal_mass_frac_from_AGB,masses),axis = 0) -expected_metal_mass_AGB = ejecta_factor*ejected_mass -if abs(total_metal_mass_AGB[n_snapshots-1] - expected_metal_mass_AGB)/expected_metal_mass_AGB < eps: - print("total AGB metal mass released consistent with expectation") +total_metal_mass_AGB = np.sum(np.multiply(metal_mass_frac_from_AGB, masses), axis=0) +expected_metal_mass_AGB = ejecta_factor * ejected_mass +if ( + abs(total_metal_mass_AGB[n_snapshots - 1] - expected_metal_mass_AGB) + / expected_metal_mass_AGB + < eps +): + print("total AGB metal mass released consistent with expectation") else: - print("total AGB metal mass "+str(total_metal_mass_AGB[n_snapshots-1])+" expected "+ str(expected_metal_mass_AGB)) + print( + "total AGB metal mass " + + str(total_metal_mass_AGB[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass_AGB) + ) # Total mass from AGB -total_AGB_mass = np.sum(mass_from_AGB,axis = 0) -expected_AGB_mass = ejecta_factor*ejected_mass -if abs(total_AGB_mass[n_snapshots-1] - expected_AGB_mass)/expected_AGB_mass < eps: - print("total AGB mass released consistent with expectation") +total_AGB_mass = np.sum(mass_from_AGB, axis=0) +expected_AGB_mass = ejecta_factor * ejected_mass +if abs(total_AGB_mass[n_snapshots - 1] - expected_AGB_mass) / expected_AGB_mass < eps: + print("total AGB mass released consistent with expectation") else: - print("total AGB mass "+str(total_AGB_mass[n_snapshots-1])+" expected "+ str(expected_AGB_mass)) + print( + "total AGB mass " + + str(total_AGB_mass[n_snapshots - 1]) + + " expected " + + str(expected_AGB_mass) + ) # Total metal mass from SNII -total_metal_mass_SNII = np.sum(np.multiply(metal_mass_frac_from_SNII,masses),axis = 0) -expected_metal_mass_SNII = ejecta_factor*ejected_mass -if abs(total_metal_mass_SNII[n_snapshots-1] - expected_metal_mass_SNII)/expected_metal_mass_SNII < eps: - print("total SNII metal mass released consistent with expectation") +total_metal_mass_SNII = np.sum(np.multiply(metal_mass_frac_from_SNII, masses), axis=0) +expected_metal_mass_SNII = ejecta_factor * ejected_mass +if ( + abs(total_metal_mass_SNII[n_snapshots - 1] - expected_metal_mass_SNII) + / expected_metal_mass_SNII + < eps +): + print("total SNII metal mass released consistent with expectation") else: - print("total SNII metal mass "+str(total_metal_mass_SNII[n_snapshots-1])+" expected "+ str(expected_metal_mass_SNII)) + print( + "total SNII metal mass " + + str(total_metal_mass_SNII[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass_SNII) + ) # Total mass from SNII -total_SNII_mass = np.sum(mass_from_SNII,axis = 0) -expected_SNII_mass = ejecta_factor*ejected_mass -if abs(total_SNII_mass[n_snapshots-1] - expected_SNII_mass)/expected_SNII_mass < eps: - print("total SNII mass released consistent with expectation") +total_SNII_mass = np.sum(mass_from_SNII, axis=0) +expected_SNII_mass = ejecta_factor * ejected_mass +if ( + abs(total_SNII_mass[n_snapshots - 1] - expected_SNII_mass) / expected_SNII_mass + < eps +): + print("total SNII mass released consistent with expectation") else: - print("total SNII mass "+str(total_SNII_mass[n_snapshots-1])+" expected "+ str(expected_SNII_mass)) + print( + "total SNII mass " + + str(total_SNII_mass[n_snapshots - 1]) + + " expected " + + str(expected_SNII_mass) + ) # Total metal mass from SNIa -total_metal_mass_SNIa = np.sum(np.multiply(metal_mass_frac_from_SNIa,masses),axis = 0) -expected_metal_mass_SNIa = ejecta_factor*ejected_mass -if abs(total_metal_mass_SNIa[n_snapshots-1] - expected_metal_mass_SNIa)/expected_metal_mass_SNIa < eps: - print("total SNIa metal mass released consistent with expectation") +total_metal_mass_SNIa = np.sum(np.multiply(metal_mass_frac_from_SNIa, masses), axis=0) +expected_metal_mass_SNIa = ejecta_factor * ejected_mass +if ( + abs(total_metal_mass_SNIa[n_snapshots - 1] - expected_metal_mass_SNIa) + / expected_metal_mass_SNIa + < eps +): + print("total SNIa metal mass released consistent with expectation") else: - print("total SNIa metal mass "+str(total_metal_mass_SNIa[n_snapshots-1])+" expected "+ str(expected_metal_mass_SNIa)) + print( + "total SNIa metal mass " + + str(total_metal_mass_SNIa[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass_SNIa) + ) # Total iron mass from SNIa -total_iron_mass_SNIa = np.sum(np.multiply(iron_mass_frac_from_SNIa,masses),axis = 0) -expected_iron_mass_SNIa = ejecta_factor*ejected_mass -if abs(total_iron_mass_SNIa[n_snapshots-1] - expected_iron_mass_SNIa)/expected_iron_mass_SNIa < eps: - print("total SNIa iron mass released consistent with expectation") +total_iron_mass_SNIa = np.sum(np.multiply(iron_mass_frac_from_SNIa, masses), axis=0) +expected_iron_mass_SNIa = ejecta_factor * ejected_mass +if ( + abs(total_iron_mass_SNIa[n_snapshots - 1] - expected_iron_mass_SNIa) + / expected_iron_mass_SNIa + < eps +): + print("total SNIa iron mass released consistent with expectation") else: - print("total SNIa iron mass "+str(total_iron_mass_SNIa[n_snapshots-1])+" expected "+ str(expected_iron_mass_SNIa)) + print( + "total SNIa iron mass " + + str(total_iron_mass_SNIa[n_snapshots - 1]) + + " expected " + + str(expected_iron_mass_SNIa) + ) # Total mass from SNIa -total_SNIa_mass = np.sum(mass_from_SNIa,axis = 0) -expected_SNIa_mass = ejecta_factor*ejected_mass -if abs(total_SNIa_mass[n_snapshots-1] - expected_SNIa_mass)/expected_SNIa_mass < eps: - print("total SNIa mass released consistent with expectation") +total_SNIa_mass = np.sum(mass_from_SNIa, axis=0) +expected_SNIa_mass = ejecta_factor * ejected_mass +if ( + abs(total_SNIa_mass[n_snapshots - 1] - expected_SNIa_mass) / expected_SNIa_mass + < eps +): + print("total SNIa mass released consistent with expectation") else: - print("total SNIa mass "+str(total_SNIa_mass[n_snapshots-1])+" expected "+ str(expected_SNIa_mass)) + print( + "total SNIa mass " + + str(total_SNIa_mass[n_snapshots - 1]) + + " expected " + + str(expected_SNIa_mass) + ) # Total metal mass -total_metal_mass = np.sum(np.multiply(metallicity,masses),axis = 0) -expected_metal_mass = ejecta_factor_metallicity*ejected_mass -if abs(total_metal_mass[n_snapshots-1] - expected_metal_mass)/expected_metal_mass < eps: - print("total metal mass released consistent with expectation") +total_metal_mass = np.sum(np.multiply(metallicity, masses), axis=0) +expected_metal_mass = ejecta_factor_metallicity * ejected_mass +if ( + abs(total_metal_mass[n_snapshots - 1] - expected_metal_mass) / expected_metal_mass + < eps +): + print("total metal mass released consistent with expectation") else: - print("total metal mass "+str(total_metal_mass[n_snapshots-1])+" expected "+ str(expected_metal_mass)) + print( + "total metal mass " + + str(total_metal_mass[n_snapshots - 1]) + + " expected " + + str(expected_metal_mass) + ) # Total mass for each element -expected_element_mass = ejecta_factor_abundances*ejected_mass +expected_element_mass = ejecta_factor_abundances * ejected_mass for i in range(n_elements): - total_element_mass = np.sum(np.multiply(abundances[:,i,:],masses),axis = 0) - if abs(total_element_mass[n_snapshots-1] - expected_element_mass)/expected_element_mass < eps: - print("total element mass released consistent with expectation for element "+str(i)) - else: - print("total element mass "+str(total_element_mass[n_snapshots-1])+" expected "+ str(expected_element_mass) + " for element "+ str(i)) + total_element_mass = np.sum(np.multiply(abundances[:, i, :], masses), axis=0) + if ( + abs(total_element_mass[n_snapshots - 1] - expected_element_mass) + / expected_element_mass + < eps + ): + print( + "total element mass released consistent with expectation for element " + + str(i) + ) + else: + print( + "total element mass " + + str(total_element_mass[n_snapshots - 1]) + + " expected " + + str(expected_element_mass) + + " for element " + + str(i) + ) + diff --git a/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py b/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py index da837540041a9295a33b55e16b5e996394576cd7..1cacc13653d821da3abd2a09566be347608c64f7 100644 --- a/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py +++ b/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py @@ -93,7 +93,7 @@ for i in range(n_snapshots): sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") print('reading snapshot '+str(i)) masses[:,i] = sim["/PartType0/Masses"] - internal_energy[:,i] = sim["/PartType0/InternalEnergy"] + internal_energy[:,i] = sim["/PartType0/InternalEnergies"] velocity_parts[:,:,i] = sim["/PartType0/Velocities"] time[i] = sim["/Header"].attrs["Time"][0] diff --git a/examples/SubgridTests/StellarEvolution/plot_box_evolution.py b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py index a46db721e153ade2d386a5790e26a290cead4f90..a4f601875beb40c8f264cda385815f2d6dd81a41 100644 --- a/examples/SubgridTests/StellarEvolution/plot_box_evolution.py +++ b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py @@ -90,8 +90,14 @@ Msun_in_cgs = 1.98848e33 # Declare arrays to store SWIFT data swift_box_gas_mass = zeros(n_snapshots) +swift_box_gas_mass_AGB = zeros(n_snapshots) +swift_box_gas_mass_SNII = zeros(n_snapshots) +swift_box_gas_mass_SNIa = zeros(n_snapshots) swift_box_star_mass = zeros(n_snapshots) swift_box_gas_metal_mass = zeros(n_snapshots) +swift_box_gas_metal_mass_AGB = zeros(n_snapshots) +swift_box_gas_metal_mass_SNII = zeros(n_snapshots) +swift_box_gas_metal_mass_SNIa = zeros(n_snapshots) swift_element_mass = zeros((n_snapshots,n_elements)) swift_internal_energy = zeros(n_snapshots) swift_kinetic_energy = zeros(n_snapshots) @@ -104,24 +110,40 @@ for i in range(n_snapshots): #print("reading snapshot "+str(i)) sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") t[i] = sim["/Header"].attrs["Time"][0] - + masses = sim["/PartType0/Masses"][:] swift_box_gas_mass[i] = np.sum(masses) - Z_star = sim["/PartType4/Metallicity"][0] + AGB_mass = sim["/PartType0/MassesFromAGB"][:] + SNII_mass = sim["/PartType0/MassesFromSNII"][:] + SNIa_mass = sim["/PartType0/MassesFromSNIa"][:] + + swift_box_gas_mass_AGB[i] = np.sum(AGB_mass) + swift_box_gas_mass_SNII[i] = np.sum(SNII_mass) + swift_box_gas_mass_SNIa[i] = np.sum(SNIa_mass) + + Z_star = sim["/PartType4/MetalMassFractions"][0] star_masses = sim["/PartType4/Masses"][:] swift_box_star_mass[i] = np.sum(star_masses) - metallicities = sim["/PartType0/Metallicity"][:] + metallicities = sim["/PartType0/MetalMassFractions"][:] swift_box_gas_metal_mass[i] = np.sum(metallicities * masses) - element_abundances = sim["/PartType0/ElementAbundance"][:][:] + AGB_Z_frac = sim["/PartType0/MetalMassFractionsFromAGB"][:] + SNII_Z_frac = sim["/PartType0/MetalMassFractionsFromSNII"][:] + SNIa_Z_frac = sim["/PartType0/MetalMassFractionsFromSNIa"][:] + + swift_box_gas_metal_mass_AGB[i] = np.sum(AGB_Z_frac * masses) + swift_box_gas_metal_mass_SNII[i] = np.sum(SNII_Z_frac * masses) + swift_box_gas_metal_mass_SNIa[i] = np.sum(SNIa_Z_frac * masses) + + element_abundances = sim["/PartType0/ElementMassFractions"][:][:] for j in range(n_elements): swift_element_mass[i,j] = np.sum(element_abundances[:,j] * masses) v = sim["/PartType0/Velocities"][:,:] v2 = v[:,0]**2 + v[:,1]**2 + v[:,2]**2 - u = sim["/PartType0/InternalEnergy"][:] + u = sim["/PartType0/InternalEnergies"][:] swift_internal_energy[i] = np.sum(masses * u) swift_kinetic_energy[i] = np.sum(0.5 * masses * v2) swift_total_energy[i] = swift_kinetic_energy[i] + swift_internal_energy[i] @@ -136,58 +158,32 @@ for i in range(n_snapshots): filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionTotal.txt"%Z_star # Read EAGLE test output -with open(filename) as f: - eagle_categories = f.readline() - eagle_data = f.readlines() - -eagle_data = [x.strip() for x in eagle_data] - -# Declare arrays to store EAGLE test output -eagle_time_Gyr = zeros(len(eagle_data)) -eagle_total_mass = zeros(len(eagle_data)) -eagle_total_metal_mass = zeros(len(eagle_data)) -eagle_total_element_mass = zeros((len(eagle_data),n_elements)) -eagle_energy_from_mass_cgs = zeros(len(eagle_data)) -eagle_energy_ejecta_cgs = zeros(len(eagle_data)) - -# Populate arrays with data from EAGLE test output -i = 0 -for line in eagle_data: - enrich_to_date = line.split(' ') - eagle_time_Gyr[i] = float(enrich_to_date[0]) - eagle_total_mass[i] = float(enrich_to_date[1]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs - eagle_total_metal_mass[i] = float(enrich_to_date[2]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs - for j in range(n_elements): - eagle_total_element_mass[i,j] = float(enrich_to_date[3+j]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs - eagle_energy_from_mass_cgs[i] = eagle_total_mass[i] * Msun_in_cgs * swift_mean_u_start * unit_int_energy_in_cgs - eagle_energy_ejecta_cgs[i] = 0.5 * (eagle_total_mass[i] * Msun_in_cgs) * ejecta_vel_cgs**2 - i += 1 +data = loadtxt(filename) +eagle_time_Gyr = data[:,0] +eagle_total_mass = data[:,1] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs +eagle_total_metal_mass = data[:,2] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs +eagle_total_element_mass = data[:, 3:3+n_elements] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + +eagle_energy_from_mass_cgs = eagle_total_mass * Msun_in_cgs * swift_mean_u_start * unit_int_energy_in_cgs +eagle_energy_ejecta_cgs = 0.5 * (eagle_total_mass * Msun_in_cgs) * ejecta_vel_cgs**2 + +# Read the mass per channel +filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionAGB.txt"%Z_star +data = loadtxt(filename) +eagle_total_mass_AGB = data[:,1] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs +eagle_total_metal_mass_AGB = data[:,2] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + +filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionII.txt"%Z_star +data = loadtxt(filename) +eagle_total_mass_SNII = data[:,1] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs +eagle_total_metal_mass_SNII = data[:,2] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs -# Read the number of SNIa filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionIa.txt"%Z_star -with open(filename) as f: - eagle_categories = f.readline() - eagle_data = f.readlines() -i = 0 -N_SNIa = zeros(len(eagle_data)) -for line in eagle_data: - enrich_to_date = line.split(' ') - N_SNIa[i] = float(enrich_to_date[-2]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs - i += 1 - -cumulative_N_SNIa = np.cumsum(N_SNIa) -eagle_energy_SNIa_cgs = cumulative_N_SNIa * E_SNIa_cgs - -# SNII energy -N_SNII = 0.017362 * stellar_mass / Msun_in_cgs * unit_mass_in_cgs -eagle_energy_SNII_cgs = np.ones(len(eagle_data)) * N_SNII * E_SNII_cgs -eagle_energy_SNII_cgs[eagle_time_Gyr < 0.03] = 0. +data = loadtxt(filename) +eagle_total_mass_SNIa = data[:,1] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs +eagle_total_metal_mass_SNIa = data[:,2] * stellar_mass / Msun_in_cgs * unit_mass_in_cgs -# Total energy -eagle_energy_total_cgs = eagle_energy_ejecta_cgs + eagle_energy_from_mass_cgs + eagle_energy_SNIa_cgs - - # Plot the interesting quantities figure() @@ -195,19 +191,25 @@ suptitle("Star metallicity Z = %.4f"%Z_star) # Box gas mass -------------------------------- subplot(221) -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_mass[1:] - swift_box_gas_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') -plot(eagle_time_Gyr[1:],eagle_total_mass[:-1],linewidth=0.5,color='r',label='eagle test total', ls='--') -xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) -ylabel("Change in total gas particle mass (Msun)", labelpad=2) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_mass[1:] - swift_box_gas_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', label='Total') +plot(t * unit_time_in_cgs / Gyr_in_cgs, swift_box_gas_mass_AGB * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='C0', label='AGB') +plot(t * unit_time_in_cgs / Gyr_in_cgs, swift_box_gas_mass_SNII * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='C1', label='SNII') +plot(t * unit_time_in_cgs / Gyr_in_cgs, swift_box_gas_mass_SNIa * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='C2', label='SNIa') +plot(eagle_time_Gyr[1:],eagle_total_mass[:-1],linewidth=0.5, color='k', ls='--') +plot(eagle_time_Gyr[1:],eagle_total_mass_AGB[:-1],linewidth=0.5, color='C0', ls='--') +plot(eagle_time_Gyr[1:],eagle_total_mass_SNII[:-1],linewidth=0.5, color='C1', ls='--') +plot(eagle_time_Gyr[1:],eagle_total_mass_SNIa[:-1],linewidth=0.5, color='C2', ls='--') +legend(loc="lower right", ncol=2, fontsize=8) +xlabel("${\\rm Time~[Gyr]}$", labelpad=0) +ylabel("Change in total gas particle mass ${[\\rm M_\\odot]}$", labelpad=2) ticklabel_format(style='sci', axis='y', scilimits=(0,0)) -legend() # Box star mass -------------------------------- subplot(222) -plot(t * unit_time_in_cgs / Gyr_in_cgs, (swift_box_star_mass)* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') -plot(eagle_time_Gyr[1:], swift_box_star_mass[0] * unit_mass_in_cgs / Msun_in_cgs - eagle_total_mass[:-1],linewidth=0.5,color='r',label='eagle test total') -xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) -ylabel("Change in total star particle mass (Msun)", labelpad=2) +plot(t * unit_time_in_cgs / Gyr_in_cgs, (swift_box_star_mass)* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', label='SWIFT') +plot(eagle_time_Gyr[1:], swift_box_star_mass[0] * unit_mass_in_cgs / Msun_in_cgs - eagle_total_mass[:-1],linewidth=0.5,color='k',label='EAGLE test', ls='--') +xlabel("${\\rm Time~[Gyr]}$", labelpad=0) +ylabel("Change in total star particle mass ${[\\rm M_\\odot]}$", labelpad=2) ticklabel_format(style='sci', axis='y', scilimits=(0,0)) legend() @@ -218,36 +220,26 @@ subplot(223) for j in range(n_elements): plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_element_mass[1:,j] - swift_element_mass[0,j]) * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color=colours[j], ms=0.5, label=element_names[j]) plot(eagle_time_Gyr[1:],eagle_total_element_mass[:-1,j],linewidth=1,color=colours[j],linestyle='--') -xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) -ylabel("Change in element mass of gas particles (Msun)", labelpad=2) +xlabel("${\\rm Time~[Gyr]}$", labelpad=0) +ylabel("Change in element mass of gas particles ${[\\rm M_\\odot]}$", labelpad=2) xscale("log") yscale("log") legend(bbox_to_anchor=(1.005, 1.), ncol=1, fontsize=8, handlelength=1) # Box gas metal mass -------------------------------- subplot(224) -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_metal_mass[1:] - swift_box_gas_metal_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') -plot(eagle_time_Gyr[1:],eagle_total_metal_mass[:-1],linewidth=0.5,color='r',label='eagle test') -xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) -ylabel("Change in total metal mass of gas particles (Msun)", labelpad=2) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_metal_mass[1:] - swift_box_gas_metal_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', label='Total') +plot(t * unit_time_in_cgs / Gyr_in_cgs, swift_box_gas_metal_mass_AGB * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='C0', label='AGB') +plot(t * unit_time_in_cgs / Gyr_in_cgs, swift_box_gas_metal_mass_SNII * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='C1', label='SNII') +plot(t * unit_time_in_cgs / Gyr_in_cgs, swift_box_gas_metal_mass_SNIa * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='C2', label='SNIa') +plot(eagle_time_Gyr[1:],eagle_total_metal_mass[:-1],linewidth=0.5,color='k', ls='--') +plot(eagle_time_Gyr[1:],eagle_total_metal_mass_AGB[:-1],linewidth=0.5, color='C0', ls='--') +plot(eagle_time_Gyr[1:],eagle_total_metal_mass_SNII[:-1],linewidth=0.5, color='C1', ls='--') +plot(eagle_time_Gyr[1:],eagle_total_metal_mass_SNIa[:-1],linewidth=0.5, color='C2', ls='--') +legend(loc="center right", ncol=2, fontsize=8) +xlabel("${\\rm Time~[Gyr]}$", labelpad=0) +ylabel("Change in total metal mass of gas particles ${[\\rm M_\\odot]}$", labelpad=2) ticklabel_format(style='sci', axis='y', scilimits=(0,0)) savefig("box_evolution_Z_%.4f.png"%(Z_star), dpi=200) - - -# Energy plot -figure() -plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_total_energy[1:] - swift_total_energy[0]) * unit_energy_in_cgs, linewidth=0.5, color='k', label='swift') -plot(eagle_time_Gyr[1:], eagle_energy_SNIa_cgs[:-1], linewidth=0.5, color='b', label='eagle SNIa') -plot(eagle_time_Gyr[1:], eagle_energy_SNII_cgs[:-1], linewidth=0.5, color='c', label='eagle SNII') -plot(eagle_time_Gyr[1:], eagle_energy_ejecta_cgs[:-1], linewidth=0.5, color='y', label='eagle ejecta velocity') -plot(eagle_time_Gyr[1:], eagle_energy_from_mass_cgs[:-1], linewidth=0.5, color='g', label='eagle mass energy') -plot(eagle_time_Gyr[1:], eagle_energy_total_cgs[:-1], linewidth=0.5, color='r', label='eagle total') -plot([0,0], [0,0]) -xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) -ylabel("Change in internal energy of gas particles (erg)", labelpad=2) -yscale("log") -legend(loc="lower right", ncol=2) - -savefig("Energy.png") diff --git a/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py index 8b935e537b14a9d1d9cc4eec7c5cd0794c6fc489..be1588f9b707d448b2905611defd9e760c5f91de 100644 --- a/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py +++ b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py @@ -1,29 +1,30 @@ ############################################################################### - # This file is part of SWIFT. - # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) - # 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/>. - # - ############################################################################## - -# Assuming output snapshots contain evolution of box of gas with star at its -# centre, this script will plot the evolution of the radial velocities, internal -# energies, mass and metallicities of the nearest n particles to the star over -# the duration of the simulation. +# This file is part of SWIFT. +# Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) +# 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/>. +# +############################################################################## + +# Assuming output snapshots contain evolution of box of gas with star at its +# centre, this script will plot the evolution of the radial velocities, internal +# energies, mass and metallicities of the nearest n particles to the star over +# the duration of the simulation. import matplotlib + matplotlib.use("Agg") from pylab import * from scipy import stats @@ -33,38 +34,42 @@ import glob import os.path # Function to find index in array a for each element in array b -def find_indices(a,b): - result = np.zeros(len(b)) - for i in range(len(b)): - result[i] = ((np.where(a == b[i]))[0])[0] - return result +def find_indices(a, b): + result = np.zeros(len(b)) + for i in range(len(b)): + result[i] = ((np.where(a == b[i]))[0])[0] + return result + # Plot parameters -params = {'axes.labelsize': 10, -'axes.titlesize': 10, -'font.size': 12, -'legend.fontsize': 12, -'xtick.labelsize': 10, -'ytick.labelsize': 10, -'text.usetex': True, - 'figure.figsize' : (9.90,6.45), -'figure.subplot.left' : 0.1, -'figure.subplot.right' : 0.99, -'figure.subplot.bottom' : 0.1, -'figure.subplot.top' : 0.95, -'figure.subplot.wspace' : 0.2, -'figure.subplot.hspace' : 0.2, -'lines.markersize' : 6, -'lines.linewidth' : 3., -'text.latex.unicode': True +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 12, + "legend.fontsize": 12, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (9.90, 6.45), + "figure.subplot.left": 0.1, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.1, + "figure.subplot.top": 0.95, + "figure.subplot.wspace": 0.2, + "figure.subplot.hspace": 0.2, + "lines.markersize": 6, + "lines.linewidth": 3.0, + "text.latex.unicode": True, } rcParams.update(params) -rc('font',**{'family':'sans-serif','sans-serif':['Times']}) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) # Number of snapshots and elements -newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) -n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime) +n_snapshots = ( + int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1 +) n_particles_to_plot = 500 # Read the simulation data @@ -87,25 +92,25 @@ unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs unit_length_in_si = 0.01 * unit_length_in_cgs unit_mass_in_si = 0.001 * unit_mass_in_cgs unit_time_in_si = unit_time_in_cgs -unit_density_in_cgs = unit_mass_in_cgs*unit_length_in_cgs**-3 -unit_pressure_in_cgs = unit_mass_in_cgs/unit_length_in_cgs*unit_time_in_cgs**-2 -unit_int_energy_in_cgs = unit_energy_in_cgs/unit_mass_in_cgs -unit_entropy_in_cgs = unit_energy_in_cgs/unit_temp_in_cgs +unit_density_in_cgs = unit_mass_in_cgs * unit_length_in_cgs ** -3 +unit_pressure_in_cgs = unit_mass_in_cgs / unit_length_in_cgs * unit_time_in_cgs ** -2 +unit_int_energy_in_cgs = unit_energy_in_cgs / unit_mass_in_cgs +unit_entropy_in_cgs = unit_energy_in_cgs / unit_temp_in_cgs Myr_in_cgs = 3.154e13 Msun_in_cgs = 1.989e33 # Read data of zeroth snapshot -pos = sim["/PartType0/Coordinates"][:,:] -x = pos[:,0] - boxSize / 2 -y = pos[:,1] - boxSize / 2 -z = pos[:,2] - boxSize / 2 -vel = sim["/PartType0/Velocities"][:,:] -r = sqrt(x**2 + y**2 + z**2) -v_r = (x * vel[:,0] + y * vel[:,1] + z * vel[:,2]) / r -u = sim["/PartType0/InternalEnergy"][:] -S = sim["/PartType0/Entropy"][:] -P = sim["/PartType0/Pressure"][:] -rho = sim["/PartType0/Density"][:] +pos = sim["/PartType0/Coordinates"][:, :] +x = pos[:, 0] - boxSize / 2 +y = pos[:, 1] - boxSize / 2 +z = pos[:, 2] - boxSize / 2 +vel = sim["/PartType0/Velocities"][:, :] +r = sqrt(x ** 2 + y ** 2 + z ** 2) +v_r = (x * vel[:, 0] + y * vel[:, 1] + z * vel[:, 2]) / r +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] mass = sim["/PartType0/Masses"][:] IDs = sim["/PartType0/ParticleIDs"][:] @@ -123,34 +128,34 @@ t = zeros(n_snapshots) # Read data from rest of snapshots for i in range(n_snapshots): - print("reading snapshot "+str(i)) - # Read the simulation data - sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") - t[i] = sim["/Header"].attrs["Time"][0] - - pos = sim["/PartType0/Coordinates"][:,:] - x = pos[:,0] - boxSize / 2 - y = pos[:,1] - boxSize / 2 - z = pos[:,2] - boxSize / 2 - vel = sim["/PartType0/Velocities"][:,:] - r = sqrt(x**2 + y**2 + z**2) - v_r = (x * vel[:,0] + y * vel[:,1] + z * vel[:,2]) / r - u = sim["/PartType0/InternalEnergy"][:] - S = sim["/PartType0/Entropy"][:] - P = sim["/PartType0/Pressure"][:] - rho = sim["/PartType0/Density"][:] - mass = sim["/PartType0/Masses"][:] - metallicity = sim["/PartType0/Metallicity"][:] - internal_energy = sim["/PartType0/InternalEnergy"][:] - IDs = sim["/PartType0/ParticleIDs"][:] - - # Find which particles we want to plot and store their data - indices = (find_indices(IDs,part_IDs_to_plot)).astype(int) - masses_to_plot[:,i] = mass[indices[:]] - v_r_to_plot[:,i] = v_r[indices[:]] - metallicities_to_plot[:,i] = metallicity[indices[:]] - internal_energies_to_plot[:,i] = internal_energy[indices[:]] - + print("reading snapshot " + str(i)) + # Read the simulation data + sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r") + t[i] = sim["/Header"].attrs["Time"][0] + + pos = sim["/PartType0/Coordinates"][:, :] + x = pos[:, 0] - boxSize / 2 + y = pos[:, 1] - boxSize / 2 + z = pos[:, 2] - boxSize / 2 + vel = sim["/PartType0/Velocities"][:, :] + r = sqrt(x ** 2 + y ** 2 + z ** 2) + v_r = (x * vel[:, 0] + y * vel[:, 1] + z * vel[:, 2]) / r + u = sim["/PartType0/InternalEnergies"][:] + S = sim["/PartType0/Entropies"][:] + P = sim["/PartType0/Pressures"][:] + rho = sim["/PartType0/Densities"][:] + mass = sim["/PartType0/Masses"][:] + metallicity = sim["/PartType0/Metallicities"][:] + internal_energy = sim["/PartType0/InternalEnergies"][:] + IDs = sim["/PartType0/ParticleIDs"][:] + + # Find which particles we want to plot and store their data + indices = (find_indices(IDs, part_IDs_to_plot)).astype(int) + masses_to_plot[:, i] = mass[indices[:]] + v_r_to_plot[:, i] = v_r[indices[:]] + metallicities_to_plot[:, i] = metallicity[indices[:]] + internal_energies_to_plot[:, i] = internal_energy[indices[:]] + # Plot the interesting quantities figure() @@ -158,33 +163,61 @@ figure() # Radial velocity -------------------------------- subplot(221) for j in range(n_particles_to_plot): - plot(t * unit_time_in_cgs / Myr_in_cgs, v_r_to_plot[j,:] * unit_vel_in_cgs, linewidth=0.5, color='k', ms=0.5, alpha=0.1) + plot( + t * unit_time_in_cgs / Myr_in_cgs, + v_r_to_plot[j, :] * unit_vel_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) xlabel("Time (Myr)", labelpad=0) ylabel("Radial velocity $(\\rm{cm} \cdot \\rm{s}^{-1})$", labelpad=0) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) # Internal energy -------------------------------- subplot(222) for j in range(n_particles_to_plot): - plot(t * unit_time_in_cgs / Myr_in_cgs, internal_energies_to_plot[j,:] * unit_energy_in_cgs / unit_mass_in_cgs, linewidth=0.5, color='k', ms=0.5, alpha=0.1) + plot( + t * unit_time_in_cgs / Myr_in_cgs, + internal_energies_to_plot[j, :] * unit_energy_in_cgs / unit_mass_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) xlabel("Time (Myr)", labelpad=0) ylabel("Internal energy $(\\rm{erg} \cdot \\rm{g}^{-1})$", labelpad=2) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) # Masses -------------------------------- subplot(223) for j in range(n_particles_to_plot): - plot(t * unit_time_in_cgs / Myr_in_cgs, masses_to_plot[j,:] * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', ms=0.5, alpha=0.1) + plot( + t * unit_time_in_cgs / Myr_in_cgs, + masses_to_plot[j, :] * unit_mass_in_cgs / Msun_in_cgs, + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) xlabel("Time (Myr)", labelpad=0) ylabel("Mass (Msun)", labelpad=2) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) # Metallicities -------------------------------- subplot(224) for j in range(n_particles_to_plot): - plot(t * unit_time_in_cgs / Myr_in_cgs, metallicities_to_plot[j,:] , linewidth=0.5, color='k', ms=0.5, alpha=0.1) + plot( + t * unit_time_in_cgs / Myr_in_cgs, + metallicities_to_plot[j, :], + linewidth=0.5, + color="k", + ms=0.5, + alpha=0.1, + ) xlabel("Time (Myr)", labelpad=0) ylabel("Metallicity", labelpad=2) -ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) savefig("particle_evolution.png", dpi=200) diff --git a/examples/SubgridTests/StellarEvolution/run.sh b/examples/SubgridTests/StellarEvolution/run.sh index 7cf174cbfca12dad84eca72ab0329eb065f1c046..2f650146394e5d0d5ea93a0d415f471452b685e8 100755 --- a/examples/SubgridTests/StellarEvolution/run.sh +++ b/examples/SubgridTests/StellarEvolution/run.sh @@ -26,22 +26,22 @@ then ./getSolutions.sh fi -../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 2>&1 | tee output.log +../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 2>&1 | tee output_0p08.log python plot_box_evolution.py -../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 2>&1 | tee output.log +../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 2>&1 | tee output_0p04.log python plot_box_evolution.py -../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.01 2>&1 | tee output.log +../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.01 2>&1 | tee output_0p01.log python plot_box_evolution.py -../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.001 2>&1 | tee output.log +../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.001 2>&1 | tee output_0p001.log python plot_box_evolution.py -../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.0001 2>&1 | tee output.log +../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.0001 2>&1 | tee output_0p0001.log python plot_box_evolution.py diff --git a/examples/SubgridTests/StellarEvolution/stellar_evolution.yml b/examples/SubgridTests/StellarEvolution/stellar_evolution.yml index 669d9e3f7c78e0ebf2b68ff0c3a3bcb5369b4a50..63c7a4d2624793af26bdbaf628715243e2ab511d 100644 --- a/examples/SubgridTests/StellarEvolution/stellar_evolution.yml +++ b/examples/SubgridTests/StellarEvolution/stellar_evolution.yml @@ -6,15 +6,6 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1. # Amperes UnitTemp_in_cgs: 1. # Kelvin -# Cosmological parameters -Cosmology: - h: 0.6777 # Reduced Hubble constant - a_begin: 0.5 # Initial scale-factor of the simulation - a_end: 1.0 # Final scale factor of the simulation - Omega_m: 0.307 # Matter density parameter - Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter - # Parameters governing the time integration TimeIntegration: time_begin: 0 # The starting time of the simulation (in internal units). @@ -27,6 +18,7 @@ Snapshots: basename: stellar_evolution # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 3.e-5 # Time difference between consecutive outputs without cosmology (internal units) + compression: 4 # Parameters governing the conserved quantities statistics Statistics: @@ -42,7 +34,8 @@ SPH: # Properties of the stars Stars: - birth_time: 0. # Give the star in the ICs a decent birth time + overwrite_birth_time: 1 + birth_time: 0. # Give the star in the ICs a decent birth time # Parameters related to the initial conditions InitialConditions: @@ -88,7 +81,7 @@ EAGLECooling: # Properties of the EAGLE feedback and enrichment model. EAGLEFeedback: use_SNII_feedback: 0 # Global switch for SNII thermal (stochastic) feedback. - use_SNIa_feedback: 1 # Global switch for SNIa thermal (continuous) feedback. + use_SNIa_feedback: 0 # Global switch for SNIa thermal (continuous) feedback. use_AGB_enrichment: 1 # Global switch for enrichement from AGB stars. use_SNII_enrichment: 1 # Global switch for enrichement from SNII stars. use_SNIa_enrichment: 1 # Global switch for enrichement from SNIa stars. diff --git a/examples/main.c b/examples/main.c index 24b26852cdf25b3a6d2d217b06c0911e9f76c544..d0fec1a2a2b62b69293f6a36aeb32a7ca04e1672 100644 --- a/examples/main.c +++ b/examples/main.c @@ -148,6 +148,7 @@ int main(int argc, char *argv[]) { int with_aff = 0; int dry_run = 0; int dump_tasks = 0; + int dump_cells = 0; int dump_threadpool = 0; int nsteps = -2; int restart = 0; @@ -263,6 +264,9 @@ int main(int argc, char *argv[]) { OPT_INTEGER('y', "task-dumps", &dump_tasks, "Time-step frequency at which task graphs are dumped.", NULL, 0, 0), + OPT_INTEGER(0, "cell-dumps", &dump_cells, + "Time-step frequency at which cell graphs are dumped.", NULL, + 0, 0), OPT_INTEGER('Y', "threadpool-dumps", &dump_threadpool, "Time-step frequency at which threadpool tasks are dumped.", NULL, 0, 0), @@ -323,6 +327,16 @@ int main(int argc, char *argv[]) { } #endif +#ifndef SWIFT_CELL_GRAPH + if (dump_cells) { + if (myrank == 0) { + error( + "complete cell dumps are only created when " + "configured with --enable-cell-graph."); + } + } +#endif + #ifndef SWIFT_DEBUG_THREADPOOL if (dump_threadpool) { printf( @@ -358,16 +372,22 @@ int main(int argc, char *argv[]) { return 1; } - if (with_black_holes && !with_external_gravity && !with_self_gravity) { + if (with_black_holes && !with_self_gravity) { if (myrank == 0) { argparse_usage(&argparse); printf( - "\nError: Cannot process black holes without gravity, -g or -G " - "must be chosen.\n"); + "\nError: Cannot process black holes without self-gravity, -G must " + "be chosen.\n"); } return 1; } + if (with_fof) { +#ifndef WITH_FOF + error("Running with FOF but compiled without it!"); +#endif + } + if (with_fof && !with_self_gravity) { if (myrank == 0) printf( @@ -434,6 +454,9 @@ int main(int argc, char *argv[]) { /* Genesis 1.1: And then, there was time ! */ clocks_set_cpufreq(cpufreq); + /* Are we running with gravity */ + const int with_gravity = (with_self_gravity || with_external_gravity); + /* How vocal are we ? */ const int talking = (verbose == 1 && myrank == 0) || (verbose == 2); @@ -533,9 +556,12 @@ int main(int argc, char *argv[]) { if (with_mpole_reconstruction && nr_nodes > 1) error("Cannot reconstruct m-poles every step over MPI (yet)."); if (with_limiter) error("Can't run with time-step limiter over MPI (yet)"); +#ifdef WITH_LOGGER + error("Can't run with the particle logger over MPI (yet)"); +#endif #endif - /* Temporary early aborts for modes not supported with hand-vec. */ + /* Temporary early aborts for modes not supported with hand-vec. */ #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \ !defined(CHEMISTRY_NONE) error( @@ -739,12 +765,6 @@ int main(int argc, char *argv[]) { const int generate_gas_in_ics = parser_get_opt_param_int( params, "InitialConditions:generate_gas_in_ics", 0); - /* Some checks that we are not doing something stupid */ - if (generate_gas_in_ics && flag_entropy_ICs) - error("Can't generate gas if the entropy flag is set in the ICs."); - if (generate_gas_in_ics && !with_cosmology) - error("Can't generate gas if the run is not cosmological."); - /* Initialise the cosmology */ if (with_cosmology) cosmology_init(params, &us, &prog_const, &cosmo); @@ -771,6 +791,13 @@ int main(int argc, char *argv[]) { else bzero(&entropy_floor, sizeof(struct entropy_floor_properties)); + /* Initialise the pressure floor */ + if (with_hydro) + pressure_floor_init(&pressure_floor_props, &prog_const, &us, + &hydro_properties, params); + else + bzero(&pressure_floor_props, sizeof(struct pressure_floor_properties)); + /* Initialise the stars properties */ if (with_stars) stars_props_init(&stars_properties, &prog_const, &us, params, @@ -798,13 +825,6 @@ int main(int argc, char *argv[]) { } 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, - periodic); - else - bzero(&gravity_properties, sizeof(struct gravity_props)); - /* Initialise the cooling function properties */ #ifdef COOLING_NONE if (with_cooling || with_temperature) { @@ -843,7 +863,9 @@ int main(int argc, char *argv[]) { /* Initialise the FOF properties */ bzero(&fof_properties, sizeof(struct fof_props)); +#ifdef WITH_FOF if (with_fof) fof_init(&fof_properties, params, &prog_const, &us); +#endif /* Be verbose about what happens next */ if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); @@ -854,34 +876,36 @@ int main(int argc, char *argv[]) { fflush(stdout); /* Get ready to read particles of all kinds */ - size_t Ngas = 0, Ngpart = 0, Nspart = 0, Nbpart = 0; + size_t Ngas = 0, Ngpart = 0, Ngpart_background = 0, Nspart = 0, Nbpart = 0; double dim[3] = {0., 0., 0.}; + if (myrank == 0) clocks_gettime(&tic); #if defined(HAVE_HDF5) #if defined(WITH_MPI) #if defined(HAVE_PARALLEL_HDF5) read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, - &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, - with_hydro, (with_external_gravity || with_self_gravity), - with_stars, with_black_holes, cleanup_h, cleanup_sqrt_a, - cosmo.h, cosmo.a, myrank, nr_nodes, MPI_COMM_WORLD, - MPI_INFO_NULL, nr_threads, dry_run); + &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, + &flag_entropy_ICs, with_hydro, with_gravity, with_stars, + with_black_holes, with_cosmology, cleanup_h, + cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, + MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, dry_run); #else read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, - &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, - with_hydro, (with_external_gravity || with_self_gravity), - with_stars, with_black_holes, cleanup_h, cleanup_sqrt_a, + &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, + &flag_entropy_ICs, with_hydro, with_gravity, with_stars, + with_black_holes, with_cosmology, cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, dry_run); #endif #else read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, - &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, - with_hydro, (with_external_gravity || with_self_gravity), - with_stars, with_black_holes, cleanup_h, cleanup_sqrt_a, + &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, + &flag_entropy_ICs, with_hydro, with_gravity, with_stars, + with_black_holes, with_cosmology, cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, dry_run); #endif #endif + if (myrank == 0) { clocks_gettime(&toc); message("Reading initial conditions took %.3f %s.", @@ -889,6 +913,12 @@ int main(int argc, char *argv[]) { fflush(stdout); } + /* Some checks that we are not doing something stupid */ + if (generate_gas_in_ics && flag_entropy_ICs) + error("Can't generate gas if the entropy flag is set in the ICs."); + if (generate_gas_in_ics && !with_cosmology) + error("Can't generate gas if the run is not cosmological."); + #ifdef SWIFT_DEBUG_CHECKS /* Check once and for all that we don't have unwanted links */ if (!with_stars && !dry_run) { @@ -911,23 +941,48 @@ int main(int argc, char *argv[]) { #endif /* Get the total number of particles across all nodes. */ - long long N_total[4] = {0, 0, 0, 0}; + long long N_total[swift_type_count + 1] = {0}; + long long Nbaryons = Ngas + Nspart + Nbpart; #if defined(WITH_MPI) - long long N_long[4] = {Ngas, Ngpart, Nspart, Nbpart}; - MPI_Allreduce(&N_long, &N_total, 4, MPI_LONG_LONG_INT, MPI_SUM, - MPI_COMM_WORLD); + long long N_long[swift_type_count + 1] = {0}; + N_long[swift_type_gas] = Ngas; + N_long[swift_type_dark_matter] = + with_gravity ? Ngpart - Ngpart_background - Nbaryons : 0; + N_long[swift_type_dark_matter_background] = Ngpart_background; + N_long[swift_type_stars] = Nspart; + N_long[swift_type_black_hole] = Nbpart; + N_long[swift_type_count] = Ngpart; + MPI_Allreduce(&N_long, &N_total, swift_type_count + 1, MPI_LONG_LONG_INT, + MPI_SUM, MPI_COMM_WORLD); #else - N_total[0] = Ngas; - N_total[1] = Ngpart; - N_total[2] = Nspart; - N_total[3] = Nbpart; + N_total[swift_type_gas] = Ngas; + N_total[swift_type_dark_matter] = + with_gravity ? Ngpart - Ngpart_background - Nbaryons : 0; + N_total[swift_type_dark_matter_background] = Ngpart_background; + N_total[swift_type_stars] = Nspart; + N_total[swift_type_black_hole] = Nbpart; + N_total[swift_type_count] = Ngpart; #endif if (myrank == 0) message( "Read %lld gas particles, %lld stars particles, %lld black hole " - "particles and %lld gparts from the ICs.", - N_total[0], N_total[2], N_total[3], N_total[1]); + "particles, %lld DM particles and %lld DM background particles from " + "the ICs.", + N_total[swift_type_gas], N_total[swift_type_stars], + N_total[swift_type_black_hole], N_total[swift_type_dark_matter], + N_total[swift_type_dark_matter_background]); + + const int with_DM_particles = N_total[swift_type_dark_matter] > 0; + const int with_baryon_particles = + (N_total[swift_type_gas] + N_total[swift_type_stars] + + N_total[swift_type_black_hole] > + 0) || + (with_DM_particles && generate_gas_in_ics); + + /* Do we have background DM particles? */ + const int with_DM_background_particles = + N_total[swift_type_dark_matter_background] > 0; /* Verify that the fields to dump actually exist */ if (myrank == 0) io_check_output_fields(params, N_total); @@ -936,8 +991,8 @@ int main(int argc, char *argv[]) { if (myrank == 0) clocks_gettime(&tic); space_init(&s, params, &cosmo, dim, parts, gparts, sparts, bparts, Ngas, Ngpart, Nspart, Nbpart, periodic, replicate, generate_gas_in_ics, - with_hydro, with_self_gravity, with_star_formation, talking, - dry_run); + with_hydro, with_self_gravity, with_star_formation, + with_DM_background_particles, talking, dry_run); if (myrank == 0) { clocks_gettime(&toc); @@ -946,6 +1001,14 @@ int main(int argc, char *argv[]) { fflush(stdout); } + /* Initialise the gravity properties */ + bzero(&gravity_properties, sizeof(struct gravity_props)); + if (with_self_gravity) + gravity_props_init(&gravity_properties, params, &prog_const, &cosmo, + with_cosmology, with_baryon_particles, + with_DM_particles, with_DM_background_particles, + periodic); + /* Initialise the external potential properties */ bzero(&potential, sizeof(struct external_potential)); if (with_external_gravity) @@ -970,19 +1033,24 @@ int main(int argc, char *argv[]) { if (with_cosmology && with_self_gravity && !dry_run) space_check_cosmology(&s, &cosmo, myrank); -/* Also update the total counts (in case of changes due to replication) */ + /* Also update the total counts (in case of changes due to replication) */ + Nbaryons = s.nr_parts + s.nr_sparts + s.nr_bparts; #if defined(WITH_MPI) - N_long[0] = s.nr_parts; - N_long[1] = s.nr_gparts; - N_long[2] = s.nr_sparts; - N_long[3] = s.nr_bparts; - MPI_Allreduce(&N_long, &N_total, 4, MPI_LONG_LONG_INT, MPI_SUM, - MPI_COMM_WORLD); + N_long[swift_type_gas] = s.nr_parts; + N_long[swift_type_dark_matter] = + with_gravity ? s.nr_gparts - Ngpart_background - Nbaryons : 0; + N_long[swift_type_count] = s.nr_gparts; + N_long[swift_type_stars] = s.nr_sparts; + N_long[swift_type_black_hole] = s.nr_bparts; + MPI_Allreduce(&N_long, &N_total, swift_type_count + 1, MPI_LONG_LONG_INT, + MPI_SUM, MPI_COMM_WORLD); #else - N_total[0] = s.nr_parts; - N_total[1] = s.nr_gparts; - N_total[2] = s.nr_sparts; - N_total[3] = s.nr_bparts; + N_total[swift_type_gas] = s.nr_parts; + N_total[swift_type_dark_matter] = + with_gravity ? s.nr_gparts - Ngpart_background - Nbaryons : 0; + N_total[swift_type_count] = s.nr_gparts; + N_total[swift_type_stars] = s.nr_sparts; + N_total[swift_type_black_hole] = s.nr_bparts; #endif /* Say a few nice things about the space we just created. */ @@ -1001,18 +1069,18 @@ int main(int argc, char *argv[]) { } /* Verify that we are not using basic modes incorrectly */ - if (with_hydro && N_total[0] == 0) { + if (with_hydro && N_total[swift_type_gas] == 0) { error( "ERROR: Running with hydrodynamics but no gas particles found in the " "ICs!"); } - if ((with_self_gravity || with_external_gravity) && N_total[1] == 0) { + if (with_gravity && N_total[swift_type_count] == 0) { error( "ERROR: Running with gravity but no gravity particles found in " "the ICs!"); } - /* Verify that each particle is in it's proper cell. */ + /* Verify that each particle is in its proper cell. */ if (talking && !dry_run) { int icount = 0; space_map_cells_pre(&s, 0, &map_cellcheck, &icount); @@ -1049,12 +1117,14 @@ int main(int argc, char *argv[]) { /* Initialize the engine with the space and policies. */ if (myrank == 0) clocks_gettime(&tic); - engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2], N_total[3], - engine_policies, talking, &reparttype, &us, &prog_const, &cosmo, - &hydro_properties, &entropy_floor, &gravity_properties, - &stars_properties, &black_holes_properties, - &feedback_properties, &mesh, &potential, &cooling_func, - &starform, &chemistry, &fof_properties); + engine_init( + &e, &s, params, N_total[swift_type_gas], N_total[swift_type_count], + N_total[swift_type_stars], N_total[swift_type_black_hole], + N_total[swift_type_dark_matter_background], engine_policies, talking, + &reparttype, &us, &prog_const, &cosmo, &hydro_properties, + &entropy_floor, &gravity_properties, &stars_properties, + &black_holes_properties, &feedback_properties, &mesh, &potential, + &cooling_func, &starform, &chemistry, &fof_properties); engine_config(/*restart=*/0, /*fof=*/0, &e, params, nr_nodes, myrank, nr_threads, with_aff, talking, restart_file); @@ -1067,12 +1137,13 @@ int main(int argc, char *argv[]) { /* Get some info to the user. */ if (myrank == 0) { - long long N_DM = N_total[1] - N_total[2] - N_total[3] - N_total[0]; + const long long N_DM = N_total[swift_type_dark_matter] + + N_total[swift_type_dark_matter_background]; message( "Running on %lld gas particles, %lld stars particles %lld black " "hole particles and %lld DM particles (%lld gravity particles)", - N_total[0], N_total[2], N_total[3], N_total[1] > 0 ? N_DM : 0, - N_total[1]); + N_total[swift_type_gas], N_total[swift_type_stars], + N_total[swift_type_black_hole], N_DM, N_total[swift_type_count]); message( "from t=%.3e until t=%.3e with %d ranks, %d threads / rank and %d " "task queues / rank (dt_min=%.3e, dt_max=%.3e)...", @@ -1100,11 +1171,6 @@ int main(int argc, char *argv[]) { if (s.periodic) gravity_exact_force_ewald_init(e.s->dim[0]); #endif -/* Init the runner history. */ -#ifdef HIST - for (k = 0; k < runner_hist_N; k++) runner_hist_bins[k] = 0; -#endif - if (!restart) { #ifdef WITH_MPI @@ -1212,6 +1278,13 @@ int main(int argc, char *argv[]) { task_dump_stats(dumpfile, &e, /* header = */ 0, /* allranks = */ 1); } +#ifdef SWIFT_CELL_GRAPH + /* Dump the cell data using the given frequency. */ + if (dump_cells && (dump_cells == 1 || j % dump_cells == 1)) { + space_write_cell_hierarchy(e.s, j + 1); + } +#endif + /* Dump memory use report if collected. */ #ifdef SWIFT_MEMUSE_REPORTS { @@ -1243,17 +1316,6 @@ int main(int argc, char *argv[]) { #endif // SWIFT_DEBUG_THREADPOOL } -/* Print the values of the runner histogram. */ -#ifdef HIST - printf("main: runner histogram data:\n"); - for (k = 0; k < runner_hist_N; k++) - printf(" %e %e %e\n", - runner_hist_a + k * (runner_hist_b - runner_hist_a) / runner_hist_N, - runner_hist_a + - (k + 1) * (runner_hist_b - runner_hist_a) / runner_hist_N, - (double)runner_hist_bins[k]); -#endif - /* Write final time information */ if (myrank == 0) { diff --git a/examples/main_fof.c b/examples/main_fof.c index d449eae070b0c02a11df4e96ad4a3987130bd3a5..a789eadc0422e6335d9855b7624b9a6196c493a9 100644 --- a/examples/main_fof.c +++ b/examples/main_fof.c @@ -409,10 +409,6 @@ int main(int argc, char *argv[]) { /* Initialise the cosmology */ cosmology_init(params, &us, &prog_const, &cosmo); - /* Initialise the gravity scheme */ - gravity_props_init(&gravity_properties, params, &cosmo, /*with_cosmology=*/1, - periodic); - /* Initialise the FOF properties */ bzero(&fof_properties, sizeof(struct fof_props)); if (with_fof) fof_init(&fof_properties, params, &prog_const, &us); @@ -427,30 +423,34 @@ int main(int argc, char *argv[]) { /* Get ready to read particles of all kinds */ int flag_entropy_ICs = 0; - size_t Ngas = 0, Ngpart = 0, Nspart = 0, Nbpart = 0; + size_t Ngas = 0, Ngpart = 0, Ngpart_background = 0, Nspart = 0, Nbpart = 0; double dim[3] = {0., 0., 0.}; + if (myrank == 0) clocks_gettime(&tic); #if defined(HAVE_HDF5) #if defined(WITH_MPI) #if defined(HAVE_PARALLEL_HDF5) - read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, - &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, - with_hydro, /*with_grav=*/1, with_stars, with_black_holes, - cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, - nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, - /*dry_run=*/0); + read_ic_parallel( + ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, + &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, + /*with_grav=*/1, with_stars, with_black_holes, /*with_cosmology=*/1, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, + MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, + /*dry_run=*/0); #else - read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, - &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, - /*with_grav=*/1, with_stars, with_black_holes, cleanup_h, - cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, - MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, /*dry_run=*/0); + read_ic_serial( + ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, + &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, + /*with_grav=*/1, with_stars, with_black_holes, /*with_cosmology=*/1, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, + MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, /*dry_run=*/0); #endif #else - read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, - &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, - /*with_grav=*/1, with_stars, with_black_holes, cleanup_h, - cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, /*dry_run=*/0); + read_ic_single( + ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, + &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, + /*with_grav=*/1, with_stars, with_black_holes, /*with_cosmology=*/1, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, /*dry_run=*/0); #endif #endif if (myrank == 0) { @@ -467,30 +467,51 @@ int main(int argc, char *argv[]) { #endif /* Get the total number of particles across all nodes. */ - long long N_total[4] = {0, 0, 0}; + long long N_total[swift_type_count + 1] = {0}; + long long Nbaryons = Ngas + Nspart + Nbpart; #if defined(WITH_MPI) - long long N_long[4] = {Ngas, Ngpart, Nspart, Nbpart}; - MPI_Allreduce(&N_long, &N_total, 4, MPI_LONG_LONG_INT, MPI_SUM, - MPI_COMM_WORLD); + long long N_long[swift_type_count + 1] = {0}; + N_long[swift_type_gas] = Ngas; + N_long[swift_type_dark_matter] = Ngpart - Ngpart_background - Nbaryons; + N_long[swift_type_dark_matter_background] = Ngpart_background; + N_long[swift_type_stars] = Nspart; + N_long[swift_type_black_hole] = Nbpart; + N_long[swift_type_count] = Ngpart; + MPI_Allreduce(&N_long, &N_total, swift_type_count + 1, MPI_LONG_LONG_INT, + MPI_SUM, MPI_COMM_WORLD); #else - N_total[0] = Ngas; - N_total[1] = Ngpart; - N_total[2] = Nspart; - N_total[3] = Nbpart; + N_total[swift_type_gas] = Ngas; + N_total[swift_type_dark_matter] = Ngpart - Ngpart_background - Nbaryons; + N_total[swift_type_dark_matter_background] = Ngpart_background; + N_total[swift_type_stars] = Nspart; + N_total[swift_type_black_hole] = Nbpart; + N_total[swift_type_count] = Ngpart; #endif if (myrank == 0) message( "Read %lld gas particles, %lld stars particles, %lld black hole " - "particles and %lld gparts from the ICs.", - N_total[0], N_total[2], N_total[3], N_total[1]); + "particles, %lld DM particles and %lld DM background particles from " + "the ICs.", + N_total[swift_type_gas], N_total[swift_type_stars], + N_total[swift_type_black_hole], N_total[swift_type_dark_matter], + N_total[swift_type_dark_matter_background]); + + const int with_DM_particles = N_total[swift_type_dark_matter] > 0; + const int with_baryon_particles = + (N_total[swift_type_gas] + N_total[swift_type_stars] + + N_total[swift_type_black_hole]) > 0; + + /* Do we have background DM particles? */ + const int with_DM_background_particles = + N_total[swift_type_dark_matter_background] > 0; /* Initialize the space with these data. */ if (myrank == 0) clocks_gettime(&tic); space_init(&s, params, &cosmo, dim, parts, gparts, sparts, bparts, Ngas, Ngpart, Nspart, Nbpart, periodic, replicate, /*generate_gas_in_ics=*/0, /*hydro=*/N_total[0] > 0, /*gravity=*/1, - /*with_star_formation=*/0, talking, + /*with_star_formation=*/0, with_DM_background_particles, talking, /*dry_run=*/0); if (myrank == 0) { @@ -500,6 +521,12 @@ int main(int argc, char *argv[]) { fflush(stdout); } + /* Initialise the gravity scheme */ + bzero(&gravity_properties, sizeof(struct gravity_props)); + gravity_props_init(&gravity_properties, params, &prog_const, &cosmo, + /*with_cosmology=*/1, with_baryon_particles, + with_DM_particles, with_DM_background_particles, periodic); + /* Initialise the long-range gravity mesh */ if (periodic) { #ifdef HAVE_FFTW @@ -512,17 +539,22 @@ int main(int argc, char *argv[]) { pm_mesh_init_no_mesh(&mesh, s.dim); } - /* Also update the total counts (in case of changes due to replication) */ + /* Also update the total counts (in case of changes due to replication) */ + Nbaryons = s.nr_parts + s.nr_sparts + s.nr_bparts; #if defined(WITH_MPI) - N_long[0] = s.nr_parts; - N_long[1] = s.nr_gparts; - N_long[2] = s.nr_sparts; - MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, - MPI_COMM_WORLD); + N_long[swift_type_gas] = s.nr_parts; + N_long[swift_type_dark_matter] = s.nr_gparts - Ngpart_background - Nbaryons; + N_long[swift_type_count] = s.nr_gparts; + N_long[swift_type_stars] = s.nr_sparts; + N_long[swift_type_black_hole] = s.nr_bparts; + MPI_Allreduce(&N_long, &N_total, swift_type_count + 1, MPI_LONG_LONG_INT, + MPI_SUM, MPI_COMM_WORLD); #else - N_total[0] = s.nr_parts; - N_total[1] = s.nr_gparts; - N_total[2] = s.nr_sparts; + N_total[swift_type_gas] = s.nr_parts; + N_total[swift_type_dark_matter] = s.nr_gparts - Ngpart_background - Nbaryons; + N_total[swift_type_count] = s.nr_gparts; + N_total[swift_type_stars] = s.nr_sparts; + N_total[swift_type_black_hole] = s.nr_bparts; #endif /* Say a few nice things about the space we just created. */ @@ -547,14 +579,16 @@ int main(int argc, char *argv[]) { /* Initialize the engine with the space and policies. */ if (myrank == 0) clocks_gettime(&tic); - engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2], N_total[3], - engine_policies, talking, &reparttype, &us, &prog_const, &cosmo, - /*hydro_properties=*/NULL, /*entropy_floor=*/NULL, - &gravity_properties, - /*stars_properties=*/NULL, /*black_holes_properties=*/NULL, - /*feedback_properties=*/NULL, &mesh, /*potential=*/NULL, - /*cooling_func=*/NULL, - /*starform=*/NULL, /*chemistry=*/NULL, &fof_properties); + engine_init( + &e, &s, params, N_total[swift_type_gas], N_total[swift_type_count], + N_total[swift_type_stars], N_total[swift_type_black_hole], + N_total[swift_type_dark_matter_background], engine_policies, talking, + &reparttype, &us, &prog_const, &cosmo, + /*hydro_properties=*/NULL, /*entropy_floor=*/NULL, &gravity_properties, + /*stars_properties=*/NULL, /*black_holes_properties=*/NULL, + /*feedback_properties=*/NULL, &mesh, /*potential=*/NULL, + /*cooling_func=*/NULL, + /*starform=*/NULL, /*chemistry=*/NULL, &fof_properties); engine_config(/*restart=*/0, /*fof=*/1, &e, params, nr_nodes, myrank, nr_threads, with_aff, talking, NULL); @@ -567,12 +601,13 @@ int main(int argc, char *argv[]) { /* Get some info to the user. */ if (myrank == 0) { - long long N_DM = N_total[1] - N_total[2] - N_total[3] - N_total[0]; + const long long N_DM = N_total[swift_type_dark_matter] + + N_total[swift_type_dark_matter_background]; message( - "Running on %lld gas particles, %lld stars particles %lld black " + "Running FOF on %lld gas particles, %lld stars particles %lld black " "hole particles and %lld DM particles (%lld gravity particles)", - N_total[0], N_total[2], N_total[3], N_total[1] > 0 ? N_DM : 0, - N_total[1]); + N_total[swift_type_gas], N_total[swift_type_stars], + N_total[swift_type_black_hole], N_DM, N_total[swift_type_count]); message( "from t=%.3e until t=%.3e with %d ranks, %d threads / rank and %d " "task queues / rank (dt_min=%.3e, dt_max=%.3e)...", diff --git a/examples/nIFTyCluster/Baryonic/clean.sh b/examples/nIFTyCluster/Baryonic/clean.sh old mode 100644 new mode 100755 diff --git a/examples/nIFTyCluster/Baryonic/convert_to_swift.py b/examples/nIFTyCluster/Baryonic/convert_to_swift.py index 1de42ac8021e5fa05c4bfe78df8a303669db4a6e..323ca05f1c1df5bbe2744fb882be38d0834c82e3 100644 --- a/examples/nIFTyCluster/Baryonic/convert_to_swift.py +++ b/examples/nIFTyCluster/Baryonic/convert_to_swift.py @@ -18,12 +18,12 @@ filename = "/cosma7/data/dp004/jlvc76/nIFTy/IC_CLUSTER_00019" length = unyt.kpc mass = 1e10 * unyt.msun -time = unyt.s * unyt.kpc / unyt.km +time = (1.0 * unyt.s * unyt.kpc / unyt.km).to("s") velocity = length / time energy_per_unit_mass = (length / time) ** 2 -nifty_units = unyt.UnitSystem("nifty", length, mass, time) +nifty_units = unyt.UnitSystem("nifty", 1e3 * length, mass, time) writer = Writer( unit_system=nifty_units, diff --git a/examples/nIFTyCluster/Baryonic/create_ics.sh b/examples/nIFTyCluster/Baryonic/create_ics.sh old mode 100644 new mode 100755 diff --git a/examples/nIFTyCluster/Baryonic/getIC.sh b/examples/nIFTyCluster/Baryonic/getIC.sh old mode 100644 new mode 100755 diff --git a/examples/nIFTyCluster/Baryonic/nifty.yml b/examples/nIFTyCluster/Baryonic/nifty.yml index 4656a642ef77543c827f98fe0b55cb1187786fe4..44fea0aa343078beaf0102fd0b0edc2c8ee0652f 100644 --- a/examples/nIFTyCluster/Baryonic/nifty.yml +++ b/examples/nIFTyCluster/Baryonic/nifty.yml @@ -1,7 +1,10 @@ +MetaData: + run_name: nIFTY cluster + # Define the system of units to use internally. InternalUnitSystem: UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams - UnitLength_in_cgs: 3.08567758e21 # kpc in centimeters + UnitLength_in_cgs: 3.08567758e24 # Mpc in centimeters UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin @@ -37,23 +40,24 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 20.0 # Comoving softening length (in internal units). - max_physical_softening: 5.0 # Physical softening length (in internal units). - mesh_side_length: 512 - softening_ratio: 0.04 # 1/25 - softening_ratio_background: 0.04 # 1/25 + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.5 # Opening angle (Multipole acceptance criterion) + comoving_DM_softening: 0.02 # Comoving softening length (in internal units). + comoving_baryon_softening: 0.02 # Comoving softening length (in internal units). + max_physical_DM_softening: 0.005 # Max physical softening length (in internal units). + max_physical_baryon_softening: 0.005 # Max physical softening length (in internal units). + softening_ratio_background: 0.04 # 1/25th of mean inter-particle separation + mesh_side_length: 512 # Parameters for the hydrodynamics scheme SPH: - resolution_eta: 1.48691 # 100 ngb with wendland0-c2 + resolution_eta: 1.2 # ~50 ngb with Wendland-C2 h_min_ratio: 0.1 # Minimal smoothing in units of softening. CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) Scheduler: - max_top_level_cells: 32 # (Optional) Maximal number of top-level cells in any dimension. The number of top-level cells will be the cube of this (this is the default value). + max_top_level_cells: 32 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/nIFTyCluster/Baryonic/run.sh b/examples/nIFTyCluster/Baryonic/run.sh old mode 100644 new mode 100755 diff --git a/examples/nIFTyCluster/Baryonic/setup.sh b/examples/nIFTyCluster/Baryonic/setup.sh old mode 100644 new mode 100755 diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 7cabf3c191880360307e36afa71b5f6830f0fb6b..c029a0a5b862a2d20188c87873f71179a45f20e8 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -21,7 +21,7 @@ Cosmology: a_end: 1.0 # Final scale factor of the simulation Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter - Omega_b: 0.0455 # Baryon density parameter + Omega_b: 0.0482519 # Baryon density parameter Omega_r: 0. # (Optional) Radiation density parameter w_0: -1.0 # (Optional) Dark-energy equation-of-state parameter at z=0. w_a: 0. # (Optional) Dark-energy equation-of-state time evolution parameter. @@ -48,18 +48,29 @@ SPH: diffusion_alpha_max: 1.0 # (Optional) Override the maximal thermal diffusion coefficient that is allowed for a given particle. diffusion_alpha_min: 0.0 # (Optional) Override the minimal thermal diffusion coefficient that is allowed for a given particle. +# Parameters of the stars neighbour search +Stars: + resolution_eta: 1.2348 # (Optional) Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). Defaults to the SPH value. + h_tolerance: 1e-4 # (Optional) Relative accuracy of the Netwon-Raphson scheme for the smoothing lengths. Defaults to the SPH value. + max_ghost_iterations: 30 # (Optional) Maximal number of iterations allowed to converge towards the smoothing length. Defaults to the SPH value. + max_volume_change: 1.4 # (Optional) Maximal allowed change of kernel volume over one time-step. Defaults to the SPH value. + overwrite_birth_time: 0 # (Optional) Do we want to overwrite the birth time of the stars read from the ICs? (default: 0). + birth_time: -1 # (Optional) Initial birth times of *all* the stars to be used if we are overwriting them. (-1 means the stars remain inactive feedback-wise througout the run). # Parameters for the self-gravity scheme Gravity: - mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. - eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion). - comoving_softening: 0.0026994 # Comoving softening length (in internal units). - max_physical_softening: 0.0007 # Physical softening length (in internal units). - rebuild_frequency: 0.01 # (Optional) Frequency of the gravity-tree rebuild in units of the number of g-particles (this is the default value). - a_smooth: 1.25 # (Optional) Smoothing scale in top-level cell sizes to smooth the long-range forces over (this is the default value). - r_cut_max: 4.5 # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value). - r_cut_min: 0.1 # (Optional) Cut-off in number of top-level cells below which no truncation of FMM forces are performed (this is the default value). + mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion). + comoving_DM_softening: 0.0026994 # Comoving Plummer-equivalent softening length for DM particles (in internal units). + max_physical_DM_softening: 0.0007 # Maximal Plummer-equivalent softening length in physical coordinates for DM particles (in internal units). + comoving_baryon_softening: 0.0026994 # Comoving Plummer-equivalent softening length for baryon particles (in internal units). + max_physical_baryon_softening: 0.0007 # Maximal Plummer-equivalent softening length in physical coordinates for baryon particles (in internal units). + softening_ratio_background: 0.04 # Fraction of the mean inter-particle separation to use as Plummer-equivalent softening for the background DM particles. + rebuild_frequency: 0.01 # (Optional) Frequency of the gravity-tree rebuild in units of the number of g-particles (this is the default value). + a_smooth: 1.25 # (Optional) Smoothing scale in top-level cell sizes to smooth the long-range forces over (this is the default value). + r_cut_max: 4.5 # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value). + r_cut_min: 0.1 # (Optional) Cut-off in number of top-level cells below which no truncation of FMM forces are performed (this is the default value). # Parameters for the Friends-Of-Friends algorithm FOF: @@ -124,9 +135,9 @@ Snapshots: # Parameters governing the logger snapshot system Logger: delta_step: 10 # Update the particle log every this many updates - initial_buffer_size: 1 # buffer size in GB - buffer_scale: 10 # (Optional) When buffer size is too small, update it with required memory times buffer_scale basename: index # Common part of the filenames + initial_buffer_size: 1 # (Optional) Buffer size in GB + buffer_scale: 10 # (Optional) When buffer size is too small, update it with required memory times buffer_scale # Parameters governing the conserved quantities statistics Statistics: @@ -286,6 +297,11 @@ EAGLEEntropyFloor: Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor +# Parameters related to pressure floors ---------------------------------------------- + +GEARPressureFloor: + Jeans_factor: 10. # Number of particles required to suppose a resolved clump and avoid the pressure floor. + # Parameters related to cooling function ---------------------------------------------- # Constant du/dt cooling function @@ -339,6 +355,11 @@ EAGLEChemistry: # Parameters related to star formation models ----------------------------------------------- +# GEAR star formation model (Revaz and Jablonka 2018) +GEARStarFormation: + star_formation_efficiency: 0.01 # star formation efficiency (c_*) + maximal_temperature: 3e4 # Upper limit to the temperature of a star forming particle + # EAGLE star formation model (Schaye and Dalla Vecchia 2008) EAGLEStarFormation: EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. @@ -395,6 +416,8 @@ EAGLEFeedback: 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. +# Parameters related to AGN models ----------------------------------------------- + # EAGLE AGN model EAGLEAGN: subgrid_seed_mass_Msun: 1.5e5 # Black hole subgrid mass at creation time in solar masses. @@ -404,4 +427,4 @@ EAGLEAGN: 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. - + max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. diff --git a/logger/Makefile.am b/logger/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..3bfd5af848c504d50fe201e02f49186287fbfb5a --- /dev/null +++ b/logger/Makefile.am @@ -0,0 +1,73 @@ +# This file is part of SWIFT. +# Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), +# Matthieu Schaller (matthieu.schaller@durham.ac.uk). +# 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 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 General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Add the non-standard paths to the included library headers +AM_CFLAGS = $(PYTHON_INCS) -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(GRACKLE_INCS) + + +AM_LDFLAGS = $(HDF5_LDFLAGS) + +# Assign a "safe" version number +BIN_LDFLAGS = -version-info 0:0:0 + +# The git command, if available. +GIT_CMD = @GIT_CMD@ + +# Additional dependencies for shared libraries. +EXTRA_LIBS = $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS) \ + $(VELOCIRAPTOR_LIBS) $(GSL_LIBS) -L../src/.libs -lswiftsim + +# MPI libraries. +# MPI_LIBS = $(MPI_THREAD_LIBS) +# MPI_FLAGS = -DWITH_MPI + +# Build the liblogger library +lib_LTLIBRARIES = liblogger.la +# Build a MPI-enabled version too? +# if HAVEMPI +# lib_LTLIBRARIES += liblogger_mpi.la +# endif + +# subdirectories +SUBDIRS = tests + +# List required headers +include_HEADERS = logger_header.h logger_loader_io.h logger_particle.h logger_time.h logger_tools.h logger_reader.h \ + logger_logfile.h + +# Common source files +AM_SOURCES = logger_header.c logger_loader_io.c logger_particle.c logger_time.c logger_tools.c logger_reader.c \ + logger_logfile.c +if HAVEPYTHON +AM_SOURCES += logger_python_wrapper.c +endif + +# Include files for distribution, not installation. +nobase_noinst_HEADERS = + +# Sources and flags for regular library +liblogger_la_SOURCES = $(AM_SOURCES) +liblogger_la_CFLAGS = $(AM_CFLAGS) +liblogger_la_LDFLAGS = $(AM_LDFLAGS) $(EXTRA_LIBS) $(BIN_LDFLAGS) + +# Sources and flags for MPI library +# liblogger_mpi_la_SOURCES = $(AM_SOURCES) +# liblogger_mpi_la_CFLAGS = $(AM_CFLAGS) $(MPI_FLAGS) +# liblogger_mpi_la_LDFLAGS = $(AM_LDFLAGS) $(MPI_LIBS) $(EXTRA_LIBS) +# liblogger_mpi_la_SHORTNAME = mpi +# liblogger_mpi_la_LIBADD = diff --git a/logger/logger_header.c b/logger/logger_header.c new file mode 100644 index 0000000000000000000000000000000000000000..61e5da246c9aa07eeeb42e751832f017fa04ca0a --- /dev/null +++ b/logger/logger_header.c @@ -0,0 +1,196 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#include "logger_header.h" + +#include "logger_loader_io.h" +#include "logger_logfile.h" +#include "logger_reader.h" +#include "logger_tools.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* Name of each offset direction. */ +const char *logger_offset_name[logger_offset_count] = { + "Forward", + "Backward", + "Corrupted", +}; + +/** + * @brief Print the properties of the header to stdout. + * + * @param h The #header. + */ +void header_print(const struct header *h) { +#ifdef SWIFT_DEBUG_CHECKS + message("Debug checks enabled."); +#endif + message("First Offset: %lu.", h->offset_first_record); + message("Offset direction: %s.", logger_offset_name[h->offset_direction]); + message("Number masks: %i.", h->number_mask); + + for (size_t i = 0; i < h->number_mask; i++) { + message(" Mask: %s.", h->masks[i].name); + message(" Value: %u.", h->masks[i].mask); + message(" Size: %i.", h->masks[i].size); + message(""); + } +}; + +/** + * @brief free the allocated memory. + * + * @param h The #header. + */ +void header_free(struct header *h) { free(h->masks); }; + +/** + * @brief Check if a field is present in the header. + * + * @param h The #header. + * @param field name of the requested field. + * @return Index of the field (-1 if not found). + */ +int header_get_field_index(const struct header *h, const char *field) { + for (size_t i = 0; i < h->number_mask; i++) { + if (strcmp(h->masks[i].name, field) == 0) { + return i; + } + } + + return -1; +}; + +/** + * @brief Update the offset direction in the structure and + * write it to the logfile. + * + * @param h #header file structure. + * @param new_value The new value to write. + * + */ +void header_change_offset_direction(struct header *h, + enum logger_offset_direction new_value) { + h->offset_direction = new_value; + /* Skip file format and version numbers. */ + size_t offset = LOGGER_VERSION_SIZE + 2 * sizeof(int); + + logger_loader_io_write_data(h->log->log.map + offset, sizeof(unsigned int), + &new_value); +} + +/** + * @brief read the logger header. + * + * @param h out: The #header. + * @param log The #logger_logfile. + */ +void header_read(struct header *h, struct logger_logfile *log) { + void *map = log->log.map; + + /* Set pointer to log. */ + h->log = log; + + /* read the file format. */ + char file_format[STRING_SIZE]; + map = logger_loader_io_read_data(map, LOGGER_VERSION_SIZE, &file_format); + if (strcmp(file_format, "SWIFT_LOGGER")) + error("Wrong file format (%s).", file_format); + + /* Read the major version number. */ + map = logger_loader_io_read_data(map, sizeof(int), &h->major_version); + + /* Read the minor version number. */ + map = logger_loader_io_read_data(map, sizeof(int), &h->minor_version); + + struct logger_reader *reader = log->reader; + if (&reader->log != log) error("Wrong link to the reader."); + + if (reader->verbose > 0) + message("File version %i.%i.", h->major_version, h->minor_version); + + /* Read the offset directions. */ + map = logger_loader_io_read_data(map, sizeof(int), &h->offset_direction); + + if (!header_is_forward(h) && !header_is_backward(h) && + !header_is_corrupted(h)) + error("Wrong offset value in the header (%i).", h->offset_direction); + + /* Read offset to first record. */ + map = logger_loader_io_read_data(map, LOGGER_OFFSET_SIZE, + &h->offset_first_record); + + /* Read the size of the strings. */ + map = + logger_loader_io_read_data(map, sizeof(unsigned int), &h->string_length); + + /* Check if value defined in this file is large enough. */ + if (STRING_SIZE < h->string_length) { + error("Name too large in log file %i.", h->string_length); + } + + /* Read the number of masks. */ + map = logger_loader_io_read_data(map, sizeof(unsigned int), &h->number_mask); + + /* Allocate the masks memory. */ + h->masks = malloc(sizeof(struct mask_data) * h->number_mask); + + /* Loop over all masks. */ + for (size_t i = 0; i < h->number_mask; i++) { + /* Read the mask name. */ + map = logger_loader_io_read_data(map, h->string_length, h->masks[i].name); + + /* Set the mask value. */ + h->masks[i].mask = 1 << i; + + /* Read the mask data size. */ + map = logger_loader_io_read_data(map, sizeof(unsigned int), + &h->masks[i].size); + } + + /* Check the logfile header's size. */ + if (map != log->log.map + h->offset_first_record) { + header_print(h); + size_t offset = map - log->log.map; + error("Wrong header size (in header %zi, current %zi).", + h->offset_first_record, offset); + } +}; + +/** + * @brief Count number of bits in a given mask (without the record header). + * + * @param h #header file structure. + * @param mask Mask to compute. + * + * @return number of bits in mask. + */ +size_t header_get_record_size_from_mask(const struct header *h, + const size_t mask) { + size_t count = 0; + /* Loop over each masks. */ + for (size_t i = 0; i < h->number_mask; i++) { + if (mask & h->masks[i].mask) { + count += h->masks[i].size; + } + } + return count; +} diff --git a/logger/logger_header.h b/logger/logger_header.h new file mode 100644 index 0000000000000000000000000000000000000000..c388ef65cda21d00f53ddc54e97f43671edf1aeb --- /dev/null +++ b/logger/logger_header.h @@ -0,0 +1,119 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef LOGGER_LOGGER_HEADER_H +#define LOGGER_LOGGER_HEADER_H + +#include "logger_tools.h" + +#include <stdio.h> +#include <stdlib.h> + +#define LOGGER_VERSION_SIZE 20 +#define LOGGER_OFFSET_SIZE 7 +#define LOGGER_MASK_SIZE 1 + +enum logger_offset_direction { + logger_offset_backward = 0, + logger_offset_forward, + logger_offset_corrupted, + /* Number of offset type. */ + logger_offset_count, +}; + +/** + * @brief Names of the offset directions. + */ +extern const char *logger_offset_name[]; + +struct logger_logfile; + +/** + * @brief This structure contains everything from the file header. + * + * This structure is initialized by #header_read and need to be freed + * with #header_free. + * + * The information contained by the header can be easily access with + * the functions #header_get_record_size_from_mask and #header_get_field_index. + * + * The only function that modify the file is #header_change_offset_direction. + */ +struct header { + /* Dump's major version. */ + int major_version; + + /* Dump's minor version. */ + int minor_version; + + /* Offset of the first record. */ + size_t offset_first_record; + + /* Number of bytes for strings. */ + unsigned int string_length; + + /* Number of masks. */ + unsigned int number_mask; + + /* List of masks. */ + struct mask_data *masks; + + /* Direction of the offset in the records. */ + enum logger_offset_direction offset_direction; + + /* The corresponding log. */ + struct logger_logfile *log; +}; + +void header_print(const struct header *h); +void header_free(struct header *h); +int header_get_field_index(const struct header *h, const char *field); +void header_read(struct header *h, struct logger_logfile *log); +size_t header_get_record_size_from_mask(const struct header *h, + const size_t mask); +void header_change_offset_direction(struct header *h, + enum logger_offset_direction new_value); + +/** + * @brief Check if the offset are forward. + * @param h The #header. + */ +__attribute__((always_inline)) INLINE static int header_is_forward( + const struct header *h) { + return h->offset_direction == logger_offset_forward; +} + +/** + * @brief Check if the offset are backward. + * @param h The #header. + */ +__attribute__((always_inline)) INLINE static int header_is_backward( + const struct header *h) { + return h->offset_direction == logger_offset_backward; +} + +/** + * @brief Check if the offset are corrupted. + * @param h The #header. + */ +__attribute__((always_inline)) INLINE static int header_is_corrupted( + const struct header *h) { + return h->offset_direction == logger_offset_corrupted; +} + +#endif // LOGGER_LOGGER_HEADER_H diff --git a/logger/logger_loader_io.c b/logger/logger_loader_io.c new file mode 100644 index 0000000000000000000000000000000000000000..f18f9bb7eb2eaf88ba11eaf916c0a68a27cfd2d2 --- /dev/null +++ b/logger/logger_loader_io.c @@ -0,0 +1,95 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "logger_header.h" +#include "logger_loader_io.h" +#include "logger_tools.h" + +/** + * @brief get the size of a file. + * + * @param fd file id. + * + * @return file size. + */ +size_t logger_loader_io_get_file_size(int fd) { + struct stat s; + int status = fstat(fd, &s); + if (status != 0) error("Unable to get file size (%s).", strerror(errno)); + return s.st_size; +} + +/** + * @brief Map a file. + * + * #logger_loader_io_munmap_file should be called to unmap the file. + * + * @param filename file to read. + * @param file_size (out) size of the file. + * @param read_only Open the file in read only mode? + * + */ +void *logger_loader_io_mmap_file(char *filename, size_t *file_size, + int read_only) { + /* open the file. */ + int fd; + + if (read_only) + fd = open(filename, O_RDONLY); + else + fd = open(filename, O_RDWR); + + if (fd == -1) + error("Unable to open file %s (%s).", filename, strerror(errno)); + + /* get the file size. */ + *file_size = logger_loader_io_get_file_size(fd); + + /* map the memory. */ + int mode = PROT_READ; + if (!read_only) mode |= PROT_WRITE; + + void *map = mmap(NULL, *file_size, mode, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) + error("Failed to allocate map of size %zi bytes (%s).", *file_size, + strerror(errno)); + + /* Close the file. */ + close(fd); + + return map; +} + +/** + * @brief Unmap a file. + * + * @param map file mapping. + * @param file_size The file size. + * + */ +void logger_loader_io_munmap_file(void *map, size_t file_size) { + /* unmap the file. */ + if (munmap(map, file_size) != 0) { + error("Unable to unmap the file (%s).", strerror(errno)); + } +} diff --git a/logger/logger_loader_io.h b/logger/logger_loader_io.h new file mode 100644 index 0000000000000000000000000000000000000000..d44fea673017644306e73261afdbc6dec26948c6 --- /dev/null +++ b/logger/logger_loader_io.h @@ -0,0 +1,98 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +/** + * @file logger_loader_io.h + * @brief This file contains basic IO function. + */ +#ifndef LOGGER_LOGGER_LOADER_IO_H +#define LOGGER_LOGGER_LOADER_IO_H + +#include "logger_header.h" +#include "logger_tools.h" + +#include <stdio.h> +#include <stdlib.h> + +size_t logger_loader_io_get_file_size(int fd); +void *logger_loader_io_mmap_file(char *filename, size_t *file_size, + int read_only); +void logger_loader_io_munmap_file(void *map, size_t file_size); + +/** + * @brief read a mask with its offset. + * + * @param h #header file structure. + * @param data Pointer to the data to read. + * @param mask (output) mask read from the data. + * @param diff_offset (output) offset difference to previous/next corresponding + * record. + * + * @return memory after the record header. + */ +__attribute__((always_inline)) INLINE static void *logger_loader_io_read_mask( + const struct header *h, void *data, size_t *mask, size_t *diff_offset) { + /* read mask */ + if (mask) { + *mask = 0; + memcpy(mask, data, LOGGER_MASK_SIZE); + } + data += LOGGER_MASK_SIZE; + + /* read offset */ + if (diff_offset) { + *diff_offset = 0; + memcpy(diff_offset, data, LOGGER_OFFSET_SIZE); + } + data += LOGGER_OFFSET_SIZE; + + return data; +} + +/** + * @brief read a single value from a file. + * + * @param data Pointer to the data to read. + * @param size size of the data to read. + * @param p pointer where to store the data. + + * @return memory after the data written. + */ +__attribute__((always_inline)) INLINE static void *logger_loader_io_read_data( + void *data, const size_t size, void *p) { + memcpy(p, data, size); + return data + size; +}; + +/** + * @brief write a single value in a file. + * + * @param data Pointer to the data to read. + * @param size size of the data to write. + * @param p pointer to the data. + * + * @return memory after the data written. + */ +__attribute__((always_inline)) INLINE static void *logger_loader_io_write_data( + void *data, const size_t size, const void *p) { + memcpy(data, p, size); + + return data + size; +}; + +#endif // LOGGER_LOGGER_LOADER_IO_H diff --git a/logger/logger_logfile.c b/logger/logger_logfile.c new file mode 100644 index 0000000000000000000000000000000000000000..c70068cd24c01a5ba231e97e343a0c076dc0ecb4 --- /dev/null +++ b/logger/logger_logfile.c @@ -0,0 +1,175 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#include "logger_logfile.h" +#include "logger_loader_io.h" +#include "logger_reader.h" + +/** + * @brief Initialize the #logger_logfile. + * + * If required this function will also reverse the offsets. + * @param log The #logger_logfile. + * @param filename the log's filename. + * @param reader The #logger_reader. + * @param only_header Read only the header. + */ +void logger_logfile_init_from_file(struct logger_logfile *log, char *filename, + struct logger_reader *reader, + int only_header) { + + /* Set the pointer to the reader. */ + log->reader = reader; + if (&reader->log != log) error("Wrong link to the reader."); + + /* Set pointers to zero. */ + time_array_init(&log->times); + + /* Open file, map it and get its size. */ + if (reader->verbose > 1) message("Mapping the log file."); + log->log.map = logger_loader_io_mmap_file(filename, &log->log.file_size, + /* read_only */ 1); + + /* Read the header. */ + if (reader->verbose > 1) message("Reading the header."); + header_read(&log->header, log); + + /* Print the header. */ + if (reader->verbose > 0) { + header_print(&log->header); + } + + /* No need to continue if only the + header is required. */ + if (only_header) return; + + /* Check if the offset are corrupted. */ + if (header_is_corrupted(&log->header)) { + error("The offsets have been corrupted."); + } + + /* Reverse the offsets direction. */ + if (header_is_backward(&log->header)) { + logger_logfile_reverse_offset(log, filename); + } + + /* Initialize the time array. */ + if (reader->verbose > 1) message("Reading the time stamps."); + time_array_populate(&log->times, log); + + /* Print the time array. */ + if (reader->verbose > 0) { + time_array_print(&log->times); + } +} + +/** + * @brief Free the allocated memory and unmap the file. + * + * @param log The #logger_logfile. + */ +void logger_logfile_free(struct logger_logfile *log) { + logger_loader_io_munmap_file(log->log.map, log->log.file_size); + + time_array_free(&log->times); +} + +/** + * @brief Reverse offset in log file + * + * @param log The #logger_logfile + * @param filename The log's filename. + */ +void logger_logfile_reverse_offset(struct logger_logfile *log, char *filename) { + + /* Close and reopen the file in write mode. */ + logger_loader_io_munmap_file(log->log.map, log->log.file_size); + log->log.map = logger_loader_io_mmap_file(filename, &log->log.file_size, + /* read_only */ 0); + + /* Get pointers */ + struct header *header = &log->header; + const struct logger_reader *reader = log->reader; + if (&reader->log != log) error("Wrong link to the reader."); + + /* Check if the offsets need to be reversed. */ + if (!header_is_backward(header)) { + error("The offsets are already reversed."); + } + +#ifdef SWIFT_DEBUG_CHECKS + if (reader->verbose > 0) { + message("Check record's headers..."); + } + + /* check that the record offset points to another record. */ + for (size_t offset_debug = header->offset_first_record; + offset_debug < log->log.file_size; + offset_debug = tools_check_record_consistency(reader, offset_debug)) { + } + + if (reader->verbose > 0) { + message("Record's headers are correct."); + } +#endif + + message("WARNING: Modifying the logfile, do not kill the job!"); + + /* Set the offset direction to a corrupted status. */ + header_change_offset_direction(header, logger_offset_corrupted); + + if (reader->verbose > 0) { + message("Reversing offsets..."); + } + + /* reverse the record's offset. */ + for (size_t offset = header->offset_first_record; offset < log->log.file_size; + offset = tools_reverse_offset(header, log->log.map, offset)) { + } + + if (reader->verbose > 0) { + message("Reversing done."); + } + + /* Now that the offset are effectively reversed, can set the direction to + forward. */ + header_change_offset_direction(header, logger_offset_forward); + + message("WARNING: Modification done, you can now safely kill the job."); + +#ifdef SWIFT_DEBUG_CHECKS + if (reader->verbose > 0) { + message("Check record's headers..."); + } + + /* check that the record offset points to another record. */ + for (size_t offset_debug = header->offset_first_record; + offset_debug < log->log.file_size; + offset_debug = tools_check_record_consistency(reader, offset_debug)) { + } + + if (reader->verbose > 0) { + message("Record's headers are correct."); + } +#endif + + /* Close and reopen the file in read mode. */ + logger_loader_io_munmap_file(log->log.map, log->log.file_size); + log->log.map = logger_loader_io_mmap_file(filename, &log->log.file_size, + /* read_only */ 1); +} diff --git a/logger/logger_logfile.h b/logger/logger_logfile.h new file mode 100644 index 0000000000000000000000000000000000000000..0b6ef728d524bb104b83fc28b9250c51a764dfd4 --- /dev/null +++ b/logger/logger_logfile.h @@ -0,0 +1,69 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +/** + * @file logger_logfile.h + * @brief This file contains the high level function for the log. + */ +#ifndef LOGGER_LOGGER_LOGFILE_H +#define LOGGER_LOGGER_LOGFILE_H + +#include "logger_header.h" +#include "logger_time.h" + +struct logger_reader; + +/** + * @brief This structure deals with the log file. + * + * This structure is initialized by the #logger_reader + * and deals with the log file. + * It maps it, reverse the offsets (if required) and unmap it. + * + * The structure is initialized with #logger_logfile_init_from_file and + * freed with #logger_logfile_free. + */ +struct logger_logfile { + + /* Information contained in the file header. */ + struct header header; + + /* The reader that is using this log file. */ + struct logger_reader *reader; + + /* Information about the time records. */ + struct time_array times; + + /* The log's variables. */ + struct { + /* Mapped data. */ + void *map; + + /* File size. */ + size_t file_size; + + } log; +}; + +void logger_logfile_init_from_file(struct logger_logfile *log, char *filename, + struct logger_reader *reader, + int only_header); +void logger_logfile_reverse_offset(struct logger_logfile *log, char *filename); +void logger_logfile_free(struct logger_logfile *log); + +#endif // LOGGER_LOGGER_LOGFILE_H diff --git a/logger/logger_particle.c b/logger/logger_particle.c new file mode 100644 index 0000000000000000000000000000000000000000..6809e0edf6125e66cbb8807cc98eeb31b5e04ecd --- /dev/null +++ b/logger/logger_particle.c @@ -0,0 +1,253 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#include "logger_particle.h" +#include "logger_header.h" +#include "logger_loader_io.h" +#include "logger_reader.h" +#include "logger_time.h" +#include "logger_tools.h" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +/** + * @brief Print the properties of a logger_particle. + * + * @param p The #logger_particle to print + */ +void logger_particle_print(const struct logger_particle *p) { + message("ID: %lu.", p->id); + message("Mass: %g", p->mass); + message("Time: %g.", p->time); + message("Cutoff Radius: %g.", p->h); + message("Positions: (%g, %g, %g).", p->pos[0], p->pos[1], p->pos[2]); + message("Velocities: (%g, %g, %g).", p->vel[0], p->vel[1], p->vel[2]); + message("Accelerations: (%g, %g, %g).", p->acc[0], p->acc[1], p->acc[2]); + message("Entropy: %g.", p->entropy); + message("Density: %g.", p->density); +} + +/** + * @brief Initialize a logger_particle. + * + * @param part The #logger_particle to initialize. + */ +void logger_particle_init(struct logger_particle *part) { + for (size_t k = 0; k < DIM; k++) { + part->pos[k] = 0; + part->vel[k] = 0; + part->acc[k] = 0; + } + + part->entropy = -1; + part->density = -1; + part->h = -1; + part->mass = -1; + part->id = SIZE_MAX; +} + +/** + * @brief Read a single named entry for a particle. + * + * @param part The #logger_particle to update. + * @param map The mapped data. + * @param field field to read. + * @param size number of bits to read. + * + * @return mapped data after the block read. + */ +void *logger_particle_read_field(struct logger_particle *part, void *map, + const char *field, const size_t size) { + void *p = NULL; + + /* Get the correct pointer. */ + if (strcmp("positions", field) == 0) { + p = &part->pos; + } else if (strcmp("velocities", field) == 0) { + p = &part->vel; + } else if (strcmp("accelerations", field) == 0) { + p = &part->acc; + } else if (strcmp("entropy", field) == 0) { + p = &part->entropy; + } else if (strcmp("smoothing length", field) == 0) { + p = &part->h; + } else if (strcmp("density", field) == 0) { + p = &part->density; + } else if (strcmp("consts", field) == 0) { + p = malloc(size); + } else { + error("Type %s not defined.", field); + } + + /* read the data. */ + map = logger_loader_io_read_data(map, size, p); + + /* Split the required fields. */ + if (strcmp("consts", field) == 0) { + part->mass = 0; + part->id = 0; + memcpy(&part->mass, p, sizeof(float)); + p += sizeof(float); + memcpy(&part->id, p, sizeof(size_t)); + p -= sizeof(float); + free(p); + } + + return map; +} + +/** + * @brief Read a particle entry in the log file. + * + * @param reader The #logger_reader. + * @param part The #logger_particle to update. + * @param offset offset of the record to read. + * @param time time to interpolate. + * @param reader_type #logger_reader_type. + * + * @return position after the record. + */ +size_t logger_particle_read(struct logger_particle *part, + const struct logger_reader *reader, size_t offset, + const double time, + const enum logger_reader_type reader_type) { + + /* Get a few pointers. */ + const struct header *h = &reader->log.header; + void *map = reader->log.log.map; + + const struct time_array *times = &reader->log.times; + + size_t mask = 0; + size_t h_offset = 0; + + logger_particle_init(part); + + /* Read the record's mask. */ + map = logger_loader_io_read_mask(h, map + offset, &mask, &h_offset); + + /* Check if it is not a time record. */ + if (mask == 128) error("Unexpected mask: %lu.", mask); + + /* Read all the fields. */ + for (size_t i = 0; i < h->number_mask; i++) { + if (mask & h->masks[i].mask) { + map = logger_particle_read_field(part, map, h->masks[i].name, + h->masks[i].size); + } + } + + /* Get the time of current record. + This check is required for the manipulating the file before + the initialization of the time_array. */ + if (times->size != 0) { + part->time = time_array_get_time(times, offset); + } else + part->time = -1; + + /* update the offset. */ + offset = (size_t)(map - reader->log.log.map); + + /* Check if an interpolation is required. */ + if (reader_type == logger_reader_const) return offset; + + /* Start reading next record. */ + struct logger_particle part_next; + + /* Check that the offset are in the correct direction. */ + if (!header_is_forward(h)) { + error("Cannot read a particle with non forward offsets."); + } + + /* No next particle. */ + if (h_offset == 0) return (size_t)(map - reader->log.log.map); + + /* get absolute offset of next particle. */ + h_offset += offset - header_get_record_size_from_mask(h, mask) - + LOGGER_MASK_SIZE - LOGGER_OFFSET_SIZE; + + /* Get time of next record. */ + part_next.time = time_array_get_time(times, h_offset); + + /* Read next record. */ + h_offset = logger_particle_read(&part_next, reader, h_offset, part_next.time, + logger_reader_const); + + /* Interpolate the two particles. */ + logger_particle_interpolate(part, &part_next, time); + + return offset; +} + +/** + * @brief interpolate two particles at a given time + * + * @param part_curr #logger_particle In: current particle (before time), Out: + * interpolated particle + * @param part_next #logger_particle next particle (after time) + * @param time interpolation time + * + */ +void logger_particle_interpolate(struct logger_particle *part_curr, + const struct logger_particle *part_next, + const double time) { + + /* Check that a particle is provided. */ + if (!part_curr) error("part_curr is NULL."); + if (!part_next) error("part_next is NULL."); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check the particle order. */ + if (part_next->time <= part_curr->time) + error("Wrong particle order (next before current)."); + if ((time < part_curr->time) || (part_next->time < time)) + error( + "Cannot extrapolate (particle time: %f, " + "interpolating time: %f, next particle time: %f).", + part_curr->time, time, part_next->time); +#endif + + /* Compute the interpolation scaling. */ + double scaling = part_next->time - part_curr->time; + + scaling = (time - part_curr->time) / scaling; + + double tmp; + float ftmp; + + /* interpolate vectors. */ + for (size_t i = 0; i < DIM; i++) { + tmp = (part_next->pos[i] - part_curr->pos[i]); + part_curr->pos[i] += tmp * scaling; + + ftmp = (part_next->vel[i] - part_curr->vel[i]); + part_curr->vel[i] += ftmp * scaling; + + ftmp = (part_next->acc[i] - part_curr->acc[i]); + part_curr->acc[i] += ftmp * scaling; + } + + /* interpolate scalars. */ + ftmp = (part_next->entropy - part_curr->entropy); + part_curr->entropy += ftmp * scaling; + + /* set time. */ + part_curr->time = time; +} diff --git a/logger/logger_particle.h b/logger/logger_particle.h new file mode 100644 index 0000000000000000000000000000000000000000..addd23564b65a734152ae8f538596d79019dd36f --- /dev/null +++ b/logger/logger_particle.h @@ -0,0 +1,107 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef LOGGER_LOGGER_PARTICLE_H +#define LOGGER_LOGGER_PARTICLE_H + +#include "logger_header.h" +#include "logger_time.h" +#include "logger_tools.h" + +#include <stdio.h> +#include <stdlib.h> + +#if defined(HYDRO_DIMENSION_1D) +#define DIM 1 +#elif defined(HYDRO_DIMENSION_2D) +#define DIM 2 +#elif defined(HYDRO_DIMENSION_3D) +#define DIM 3 +#endif + +struct logger_reader; + +/** + * @brief Store the data from a record. + * + * This structure contains all the required fields + * present in a file. + * + * As we need only a few particles, no need to keep it small. + * + * The particle is initialized with #logger_particle_init + * and can be updated with a record through #logger_particle_read. + * + * In #logger_particle_read, we use #logger_particle_read_field on + * each field and #logger_particle_interpolate if a linear + * interpolation is required. + */ +struct logger_particle { + /* position. */ + double pos[DIM]; + + /* velocity. */ + float vel[DIM]; + + /* acceleration. */ + float acc[DIM]; + + /* entropy. */ + float entropy; + + /* smoothing length. */ + float h; + + /* density. */ + float density; + + /* mass. */ + float mass; + + /* unique id. */ + size_t id; + + /* time of the record. */ + double time; +}; + +/** + * @brief Defines the type of interpolation + */ +enum logger_reader_type { + logger_reader_const, /* Constant interpolation. */ + logger_reader_lin, /* Linear interpolation. */ +}; + +void logger_particle_print(const struct logger_particle *p); + +size_t logger_particle_read(struct logger_particle *part, + const struct logger_reader *reader, size_t offset, + const double time, + const enum logger_reader_type reader_type); + +void logger_particle_init(struct logger_particle *part); + +void *logger_particle_read_field(struct logger_particle *part, void *map, + const char *field, const size_t size); + +void logger_particle_interpolate(struct logger_particle *part_curr, + const struct logger_particle *part_next, + const double time); + +#endif // LOGGER_LOGGER_PARTICLE_H diff --git a/logger/logger_python_wrapper.c b/logger/logger_python_wrapper.c new file mode 100644 index 0000000000000000000000000000000000000000..07c87b4989896977c56ddff4df243a5310d393a7 --- /dev/null +++ b/logger/logger_python_wrapper.c @@ -0,0 +1,290 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#include "logger_header.h" +#include "logger_loader_io.h" +#include "logger_particle.h" +#include "logger_reader.h" +#include "logger_time.h" + +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + +#include <Python.h> +#include <errno.h> +#include <numpy/arrayobject.h> +#include <stdio.h> +#include <stdlib.h> + +/** + * @brief load data from the offset without any interpolation + * + * <b>offset</b> PyArrayObject list of offset for each particle. + * + * <b>filename</b> string filename of the log file. + * + * <b>verbose</b> Verbose level. + * + * <b>returns</b> dictionnary containing the data read. + */ +static PyObject *loadFromIndex(__attribute__((unused)) PyObject *self, + PyObject *args) { + + /* input variables. */ + PyArrayObject *offset = NULL; + char *filename = NULL; + + /* output variables. */ + PyArrayObject *pos = NULL; + PyArrayObject *vel = NULL; + PyArrayObject *acc = NULL; + PyArrayObject *entropy = NULL; + PyArrayObject *h_sph = NULL; + PyArrayObject *rho = NULL; + PyArrayObject *mass = NULL; + PyArrayObject *id = NULL; + + size_t time_offset; + int verbose = 2; + + /* parse arguments. */ + if (!PyArg_ParseTuple(args, "OsL|i", &offset, &filename, &time_offset, + &verbose)) + return NULL; + + if (!PyArray_Check(offset)) { + error("Offset is not a numpy array."); + } + if (PyArray_NDIM(offset) != 1) { + error("Offset is not a 1 dimensional array."); + } + if (PyArray_TYPE(offset) != NPY_UINT64) { + error("Offset does not contain unsigned int."); + } + + /* initialize the reader. */ + struct logger_reader reader; + logger_reader_init(&reader, filename, verbose); + struct header *h = &reader.log.header; + + /* init array. */ + npy_intp dim[2]; + dim[0] = PyArray_DIMS(offset)[0]; + dim[1] = DIM; + + /* Get required time. */ + double time = time_array_get_time(&reader.log.times, time_offset); + + /* init output. */ + if (header_get_field_index(h, "positions") != -1) { + pos = (PyArrayObject *)PyArray_SimpleNew(2, dim, NPY_DOUBLE); + } + + if (header_get_field_index(h, "velocities") != -1) { + vel = (PyArrayObject *)PyArray_SimpleNew(2, dim, NPY_FLOAT); + } + + if (header_get_field_index(h, "accelerations") != -1) { + acc = (PyArrayObject *)PyArray_SimpleNew(2, dim, NPY_FLOAT); + } + + if (header_get_field_index(h, "entropy") != -1) { + entropy = + (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT); + } + + if (header_get_field_index(h, "smoothing length") != -1) { + h_sph = + (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT); + } + + if (header_get_field_index(h, "density") != -1) { + rho = + (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT); + } + + if (header_get_field_index(h, "consts") != -1) { + mass = + (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT); + id = (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_ULONG); + } + + if (verbose > 1) message("Reading particles."); + + /* loop over all particles. */ + for (npy_intp i = 0; i < PyArray_DIMS(offset)[0]; i++) { + struct logger_particle part; + + /* Get the offset. */ + size_t offset_particle = *(size_t *)PyArray_GETPTR1(offset, i); + + /* Read the particle. */ + logger_particle_read(&part, &reader, offset_particle, time, + logger_reader_lin); + + double *dtmp; + float *ftmp; + size_t *stmp; + + /* copy the data. */ + for (size_t k = 0; k < DIM; k++) { + if (pos) { + dtmp = PyArray_GETPTR2(pos, i, k); + *dtmp = part.pos[k]; + } + + if (vel) { + ftmp = PyArray_GETPTR2(vel, i, k); + *ftmp = part.vel[k]; + } + + if (acc) { + ftmp = PyArray_GETPTR2(acc, i, k); + *ftmp = part.acc[k]; + } + } + + if (entropy) { + ftmp = PyArray_GETPTR1(entropy, i); + *ftmp = part.entropy; + } + + if (rho) { + ftmp = PyArray_GETPTR1(rho, i); + *ftmp = part.density; + } + + if (h_sph) { + ftmp = PyArray_GETPTR1(h_sph, i); + *ftmp = part.h; + } + + if (mass) { + ftmp = PyArray_GETPTR1(mass, i); + *ftmp = part.mass; + } + + if (id) { + stmp = PyArray_GETPTR1(id, i); + *stmp = part.id; + } + } + + /* Free the memory. */ + logger_reader_free(&reader); + + /* construct return value. */ + PyObject *dict = PyDict_New(); + PyObject *key = PyUnicode_FromString("positions"); + PyDict_SetItem(dict, key, PyArray_Return(pos)); + + if (vel) { + key = PyUnicode_FromString("velocities"); + PyDict_SetItem(dict, key, PyArray_Return(vel)); + } + + if (acc) { + key = PyUnicode_FromString("accelerations"); + PyDict_SetItem(dict, key, PyArray_Return(acc)); + } + + if (entropy) { + key = PyUnicode_FromString("entropy"); + PyDict_SetItem(dict, key, PyArray_Return(entropy)); + } + + if (rho) { + key = PyUnicode_FromString("rho"); + PyDict_SetItem(dict, key, PyArray_Return(rho)); + } + + if (h_sph) { + key = PyUnicode_FromString("h_sph"); + PyDict_SetItem(dict, key, PyArray_Return(h_sph)); + } + + if (mass) { + key = PyUnicode_FromString("mass"); + PyDict_SetItem(dict, key, PyArray_Return(mass)); + } + + if (id) { + key = PyUnicode_FromString("id"); + PyDict_SetItem(dict, key, PyArray_Return(id)); + } + + return dict; +} + +/** + * @brief Reverse offset in log file + * + * <b>filename</b> string filename of the log file + * <b>verbose</b> Verbose level + */ +static PyObject *pyReverseOffset(__attribute__((unused)) PyObject *self, + PyObject *args) { + /* input variables. */ + char *filename = NULL; + + int verbose = 0; + + /* parse the arguments. */ + if (!PyArg_ParseTuple(args, "s|i", &filename, &verbose)) return NULL; + + /* initialize the reader which reverse the offset if necessary. */ + struct logger_reader reader; + logger_reader_init(&reader, filename, verbose); + + /* Free the reader. */ + logger_reader_free(&reader); + + return Py_BuildValue(""); +} + +/* definition of the method table. */ + +static PyMethodDef libloggerMethods[] = { + {"loadFromIndex", loadFromIndex, METH_VARARGS, + "Load snapshot directly from the offset in an index file."}, + {"reverseOffset", pyReverseOffset, METH_VARARGS, + "Reverse the offset (from pointing backward to forward)."}, + + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +static struct PyModuleDef libloggermodule = { + PyModuleDef_HEAD_INIT, + "liblogger", + "Module reading a SWIFTsim logger snapshot", + -1, + libloggerMethods, + NULL, /* m_slots */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL /* m_free */ +}; + +PyMODINIT_FUNC PyInit_liblogger(void) { + PyObject *m; + m = PyModule_Create(&libloggermodule); + if (m == NULL) return NULL; + + import_array(); + + return m; +} diff --git a/logger/logger_reader.c b/logger/logger_reader.c new file mode 100644 index 0000000000000000000000000000000000000000..0954b9c5a8e56213de4d5b2a445aeeb9105e327c --- /dev/null +++ b/logger/logger_reader.c @@ -0,0 +1,90 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ + +#include "logger_reader.h" + +/** + * @brief Initialize the reader. + * + * @param reader The #logger_reader. + * @param filename The log filename. + * @param verbose The verbose level. + */ +void logger_reader_init(struct logger_reader *reader, char *filename, + int verbose) { + if (verbose > 1) message("Initializing the reader."); + + /* Initialize the reader variables. */ + reader->verbose = verbose; + + /* Initialize the log file. */ + logger_logfile_init_from_file(&reader->log, filename, reader, + /* only_header */ 0); + + if (verbose > 1) message("Initialization done."); +} + +/** + * @brief Free the reader. + * + * @param reader The #logger_reader. + */ +void logger_reader_free(struct logger_reader *reader) { + /* Free the log. */ + logger_logfile_free(&reader->log); +} + +/** + * @brief Read a record (timestamp or particle) + * + * @param reader The #logger_reader. + * @param lp (out) The #logger_particle (if the record is a particle). + * @param time (out) The time read (if the record is a timestamp). + * @param is_particle Is the record a particle (or a timestamp)? + * @param offset The offset in the file. + * + * @return The offset after this record. + */ +size_t reader_read_record(struct logger_reader *reader, + struct logger_particle *lp, double *time, + int *is_particle, size_t offset) { + + struct logger_logfile *log = &reader->log; + + /* Read mask to find out if timestamp or particle. */ + size_t mask = 0; + logger_loader_io_read_mask(&log->header, log->log.map + offset, &mask, NULL); + + /* Check if timestamp or not. */ + int ind = header_get_field_index(&log->header, "timestamp"); + if (ind == -1) { + error("File header does not contain a mask for time."); + } + if (log->header.masks[ind].mask == mask) { + *is_particle = 0; + integertime_t int_time = 0; + offset = time_read(&int_time, time, reader, offset); + } else { + *is_particle = 1; + offset = + logger_particle_read(lp, reader, offset, *time, logger_reader_const); + } + + return offset; +} diff --git a/logger/logger_reader.h b/logger/logger_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..124d271f57587a26dbfb59299678f0ce5cfbdf79 --- /dev/null +++ b/logger/logger_reader.h @@ -0,0 +1,81 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +/** + * @file logger_reader.h + * @brief This file contains the C functions shown to the external user. + * + * Here is a quick summary of our different elements: + * + * The logger is a time adaptive way to write snapshots. + * It consists of a set of files: the log file, the parameter file and the index + * files. + * + * The <b>parameter file</b> contains all the information related to the code + * (e.g. boxsize). + * + * The <b>index files</b> are not mandatory files that indicates the position of + * the particles in the log file at a given time step. They are useful to + * speedup the reading. + * + * The <b>log file</b> consists in a large file where the particles are logged + * one after the other. It contains a <b>log file header</b> at the beginning of + * the file and a large collection of <b>records</b>. + * + * The records are logged one after the other and each contains a <b>record + * header</b> and then a list of <b>named entries</b>. In the record header, a + * <b>mask</b> is provided that corresponds to the type of named entries present + * in this record. It also contains the <b>offset</b> to the previous or next + * record for this particle. + */ + +#ifndef LOGGER_LOGGER_READER_H +#define LOGGER_LOGGER_READER_H + +#include "logger_loader_io.h" +#include "logger_logfile.h" +#include "logger_particle.h" + +/** + * @brief Main structure of the logger. + * + * This structure contains all the variables required for the logger. + * It should be the only structure that the user see. + * + * It is initialized with #logger_reader_init and freed with + * #logger_reader_free. + */ +struct logger_reader { + + /* Time of each index file. #TODO */ + double *times; + + /* Informations contained in the file header. */ + struct logger_logfile log; + + /* Level of verbosity. */ + int verbose; +}; + +void logger_reader_init(struct logger_reader *reader, char *filename, + int verbose); +void logger_reader_free(struct logger_reader *reader); +size_t reader_read_record(struct logger_reader *reader, + struct logger_particle *lp, double *time, + int *is_particle, size_t offset); +#endif // LOGGER_LOGGER_READER_H diff --git a/logger/logger_time.c b/logger/logger_time.c new file mode 100644 index 0000000000000000000000000000000000000000..d2c6ebc3f9e3171ba7fdec6c6a63eb23d7001df6 --- /dev/null +++ b/logger/logger_time.c @@ -0,0 +1,315 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#include "logger_time.h" +#include "logger_loader_io.h" +#include "logger_logfile.h" +#include "logger_reader.h" + +/** + * @brief Check if enough space is available and increase it if required. + * + * @param t The #time_array. + */ +void time_array_ensure_size(struct time_array *t) { + /* Check if we still have some place. */ + if (t->size < t->capacity) return; + + /* Increase the size */ + t->capacity *= 2; + + /* Allocate the new array */ + struct time_record *tmp = malloc(sizeof(struct time_record) * t->capacity); + if (tmp == NULL) error("Failed to allocate the time records."); + + /* Copy the memory */ + memcpy(tmp, t->records, sizeof(struct time_record) * t->size); + + /* Cleanup the memory */ + free(t->records); + + /* Set the pointer to the new array */ + t->records = tmp; +} + +/** + * @brief Add an element to the #time_array. + * + * @param t The #time_array. + * @param int_time The time in integer. + * @param time The time in double. + * @param offset The offset of the record. + */ +void time_array_append(struct time_array *t, const integertime_t int_time, + const double time, const size_t offset) { + + /* Increase the available space if required */ + time_array_ensure_size(t); + + /* Copy the values */ + t->records[t->size].time = time; + t->records[t->size].int_time = int_time; + t->records[t->size].offset = offset; + + /* Increase the size used. */ + t->size += 1; +} + +/** + * @brief read a time record. + * + * @param int_time integer time read. + * @param time time read. + * @param reader The #logger_reader. + * @param offset position in the file. + * + */ +size_t time_read(integertime_t *int_time, double *time, + const struct logger_reader *reader, size_t offset) { + + /* Initialize variables. */ + const struct header *h = &reader->log.header; + void *map = h->log->log.map; + + size_t mask = 0; + size_t prev_offset = 0; + *int_time = 0; + *time = 0; + + /* read record header. */ + map = logger_loader_io_read_mask(h, map + offset, &mask, &prev_offset); + +#ifdef SWIFT_DEBUG_CHECKS + + /* check if time mask is present in log file header. */ + int ind = header_get_field_index(h, "timestamp"); + if (ind == -1) error("File header does not contain a mask for time."); + + /* check if reading a time record. */ + if (h->masks[ind].mask != mask) error("Not a time record."); +#endif + + /* read the record. */ + map = + logger_loader_io_read_data(map, sizeof(unsigned long long int), int_time); + map = logger_loader_io_read_data(map, sizeof(double), time); + + return map - h->log->log.map; +} + +/** + * @brief get offset of first time record + * + * @param h file #header + * @return offset of first time record + * + */ +size_t time_offset_first_record(const struct header *h) { + + /* Initialize a few variables. */ + size_t offset = h->offset_first_record; + void *map = h->log->log.map; + + /* Check that the first record is really a time record. */ + int i = header_get_field_index(h, "timestamp"); + + if (i == -1) error("Time mask not present in the log file header."); + + size_t mask = 0; + logger_loader_io_read_mask(h, map + offset, &mask, NULL); + + if (mask != h->masks[i].mask) error("Log file should begin by timestep."); + + return h->offset_first_record; +} + +/** + * @brief Initialize an empty time array. + * + * @param t #time_array to initialize. + */ +void time_array_init(struct time_array *t) { + /* Allocate the arrays */ + t->records = malloc(sizeof(struct time_record) * LOGGER_TIME_INIT_SIZE); + if (t->records == NULL) error("Failed to initialize the time records."); + + /* Initialize the sizes */ + t->size = 0; + t->capacity = LOGGER_TIME_INIT_SIZE; +} + +/** + * @brief Initialize a time array from a file. + * + * @param t #time_array to initialize. + * @param log The #logger_logfile. + */ +void time_array_populate(struct time_array *t, struct logger_logfile *log) { + + /* Initialize a few variables. */ + integertime_t int_time = 0; + double time = 0; + + /* get file size. */ + size_t file_size = log->log.file_size; + + /* get first time stamp. */ + size_t offset = time_offset_first_record(&log->header); + while (offset < file_size) { + /* read current time record and store it. */ + size_t tmp_offset = offset; + time_read(&int_time, &time, log->reader, tmp_offset); + time_array_append(t, int_time, time, offset); + + /* get next record. */ + int test = tools_get_next_record(&log->header, log->log.map, &offset, + log->log.file_size); + if (test == -1) break; + } +} + +/** + * @brief access the time of a given record (by its offset). + * + * @param t #time_array to access. + * @param offset offset of the record. + * + * @return integer time of the record. + */ +integertime_t time_array_get_integertime(struct time_array *t, + const size_t offset) { + size_t ind = time_array_get_index(t, offset); + return t->records[ind].int_time; +} + +/** + * @brief access the time of a given record (by its offset). + * + * @param t #time_array to access. + * @param offset offset of the record. + * + * @return time of the record. + */ +double time_array_get_time(const struct time_array *t, const size_t offset) { + size_t ind = time_array_get_index(t, offset); + return t->records[ind].time; +} + +/** + * @brief Find the index of the last time record written before a given offset. + * + * @param t #time_array to access. + * @param offset offset of the record. + * + * @return The index of the last time record. + */ +size_t time_array_get_index(const struct time_array *t, const size_t offset) { + +#ifdef SWIFT_DEBUG_CHECKS + if (!t) error("NULL pointer."); + + if (offset < t->records[0].offset || offset > t->records[t->size - 1].offset) + error("Offset outside of range."); +#endif + + /* left will contain the index at the end of the loop */ + size_t left = 0; + size_t right = t->size - 1; + + /* Find the time_array with the correct offset through a bisection method. */ + while (left <= right) { + size_t center = (left + right) / 2; + const size_t offset_center = t->records[center].offset; + + if (offset > offset_center) { + left = center + 1; + } else if (offset < offset_center) { + right = center - 1; + } else { + return center; + } + } + + return right; +} + +/** + * @brief free memory of a #time_array + * + * @param t #time_array to free + */ +void time_array_free(struct time_array *t) { + /* Free the arrays */ + free(t->records); + t->records = NULL; + + /* Reset the counters */ + t->size = 0; + t->capacity = 0; +} + +/** + * @brief print a #time_array + * + * @param t #time_array to print + */ +void time_array_print(const struct time_array *t) { + const size_t threshold = 4; + + size_t n = t->size; + size_t up_threshold = n - threshold; + + printf("Times (size %lu): [%lli (%g)", n, t->records[0].int_time, + t->records[0].time); + + /* Loop over all elements. */ + for (size_t i = 1; i < n; i++) { + /* Skip the times at the center of the array. */ + if (i < threshold || i > up_threshold) + printf(", %lli (%g)", t->records[i].int_time, t->records[i].time); + + if (i == threshold) printf(", ..."); + } + + printf("]\n"); +} + +/** + * @brief print a #time_array (offset) + * + * @param t #time_array to print + */ +void time_array_print_offset(const struct time_array *t) { + const size_t threshold = 4; + + size_t n = t->size; + size_t up_threshold = n - threshold; + + printf("Times (size %lu): [%lu", n, t->records[0].offset); + + /* Loop over all elements. */ + for (size_t i = 1; i < n; i++) { + /* Skip the offset in the middle of the array. */ + if (i < threshold || i > up_threshold) + printf(", %lu", t->records[i].offset); + + if (i == threshold) printf(", ..."); + } + + printf("]\n"); +} diff --git a/logger/logger_time.h b/logger/logger_time.h new file mode 100644 index 0000000000000000000000000000000000000000..b27abffb9c1b3aa02c82c1739d1206b43f3ac431 --- /dev/null +++ b/logger/logger_time.h @@ -0,0 +1,95 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef LOGGER_LOGGER_TIMELINE_H +#define LOGGER_LOGGER_TIMELINE_H + +#include "logger_header.h" +#include "logger_tools.h" + +typedef int8_t timebin_t; +typedef long long integertime_t; + +struct logger_reader; + +#define LOGGER_TIME_INIT_SIZE 1024 + +/** + * @brief This structure contains all the information present in a time record. + */ +struct time_record { + /* Integertime of the records. */ + integertime_t int_time; + + /* Double time of the records. */ + double time; + + /* Offset in the file of the time records. */ + size_t offset; +}; + +/** + * @brief This structure contains all the time record. + * + * In order to obtain easily the time step of a record, + * this structure is required. It contains all the time step + * with their integer time, double time and position in the file. + * + * This structure is initialized with #time_array_init and #time_array_populate, + * and freed with #time_array_free. + * + * The time step of an offset can be obtained with + * #time_array_get_integertime, #time_array_get_time and + * #time_array_get_index. + */ +struct time_array { + + /* The complete list of time record */ + struct time_record *records; + + /* Number of element in the arrays. */ + size_t size; + + /* Maximum number of element available */ + size_t capacity; +}; + +void time_array_append(struct time_array *t, const integertime_t int_time, + const double time, const size_t offset); +size_t time_read(integertime_t *int_time, double *time, + const struct logger_reader *reader, size_t offset); + +void time_array_init(struct time_array *t); +void time_array_populate(struct time_array *t, struct logger_logfile *log); + +integertime_t time_array_get_integertime(struct time_array *t, + const size_t offset); + +double time_array_get_time(const struct time_array *t, const size_t offset); + +size_t time_array_get_index(const struct time_array *t, const size_t offset); + +void time_array_free(struct time_array *t); + +void time_array_print(const struct time_array *t); + +void time_array_print_offset(const struct time_array *t); + +size_t time_offset_first_record(const struct header *h); + +#endif // LOGGER_LOGGER_TIMELINE_H diff --git a/logger/logger_tools.c b/logger/logger_tools.c new file mode 100644 index 0000000000000000000000000000000000000000..a9a6ecfcb0acf72b11898d00fdfeff90fd70406d --- /dev/null +++ b/logger/logger_tools.c @@ -0,0 +1,231 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#include "logger_tools.h" +#include "logger_header.h" +#include "logger_loader_io.h" +#include "logger_reader.h" + +#include "logger_particle.h" + +#include <stdio.h> + +/** + * @brief get the offset of the next corresponding record. + * + * @param h #header structure of the file + * @param map file mapping + * @param offset In: initial offset, Out: offset of the next record + * @param file_size The file size. + * + * @return -1 if no next record, otherwise 0 + */ +int tools_get_next_record(const struct header *h, void *map, size_t *offset, + size_t file_size) { + if (header_is_forward(h)) + return _tools_get_next_record_forward(h, map, offset); + if (header_is_backward(h)) + return _tools_get_next_record_backward(h, map, offset, file_size); + else + error("Offsets are corrupted."); +} + +/** + * @brief internal function of #tools_get_next_record. Should not be used + * outside. + * + * @param h #header structure of the file + * @param map file mapping + * @param offset (Out) offset of the next record + * + * @return error code, -1 if no next record + */ +int _tools_get_next_record_forward(const struct header *h, void *map, + size_t *offset) { + size_t diff_offset = 0; + + /* Read the offset. */ + map = logger_loader_io_read_mask(h, map + *offset, NULL, &diff_offset); + + if (diff_offset == 0) return -1; + + /* Set the absolute offset. */ + *offset += diff_offset; + return 0; +} + +/** + * @brief internal function of #tools_get_next_record. Should not be used (very + * slow) + * + * @param h #header structure of the file + * @param map file mapping + * @param offset In: initial offset, Out: offset of the next record + * @param file_size The file size. + * + * @return error code, -1 if no next record + */ +int _tools_get_next_record_backward(const struct header *h, void *map, + size_t *offset, size_t file_size) { +#ifndef SWIFT_DEBUG_CHECKS + error("Should not be used, method too slow"); +#endif + size_t current_offset = *offset; + size_t record_header = LOGGER_MASK_SIZE + LOGGER_OFFSET_SIZE; + + while (current_offset < file_size) { + size_t mask = 0; + size_t prev_offset; + logger_loader_io_read_mask(h, map + current_offset, &mask, &prev_offset); + + prev_offset = current_offset - prev_offset - record_header; + if (*offset == prev_offset) { + *offset = current_offset - record_header; + return 0; + } + + current_offset += header_get_record_size_from_mask(h, mask); + } + + return -1; +} + +/** + * @brief switch side offset. + * + * From current record, switch side of the offset of the previous one. + * @param h #header structure of the file. + * @param file_map file mapping. + * @param offset position of the record. + * + * @return position after the record. + */ +size_t tools_reverse_offset(const struct header *h, void *file_map, + size_t offset) { + size_t mask = 0; + size_t prev_offset = 0; + const size_t cur_offset = offset; + void *map = file_map; + + /* read mask + offset. */ + map = logger_loader_io_read_mask(h, map + offset, &mask, &prev_offset); + + /* write offset of zero (in case it is the last record). */ + const size_t zero = 0; + map -= LOGGER_OFFSET_SIZE; + map = logger_loader_io_write_data(map, LOGGER_OFFSET_SIZE, &zero); + + /* set offset after current record. */ + map += header_get_record_size_from_mask(h, mask); + size_t after_current_record = (size_t)(map - file_map); + + /* first records do not have a previous partner. */ + if (prev_offset == cur_offset) return after_current_record; + + if (prev_offset > cur_offset) + error("Unexpected offset: header %lu, current %lu.", prev_offset, + cur_offset); + + /* modify previous offset. */ + map = file_map + cur_offset - prev_offset + LOGGER_MASK_SIZE; + map = logger_loader_io_write_data(map, LOGGER_OFFSET_SIZE, &prev_offset); + +#ifdef SWIFT_DEBUG_CHECKS + size_t prev_mask = 0; + map -= LOGGER_MASK_SIZE + LOGGER_OFFSET_SIZE; + logger_loader_io_read_mask(h, map, &prev_mask, NULL); + + /* Check if we are not mixing time stamp and particles */ + if ((prev_mask != 128 && mask == 128) || (prev_mask == 128 && mask != 128)) + error("Unexpected mask: %lu, got %lu.", mask, prev_mask); + +#endif // SWIFT_DEBUG_CHECKS + + return after_current_record; +} + +/** + * @brief debugging function checking the offset and the mask of a record. + * + * Compare the mask with the one pointed by the header. + * if the record is a particle, check the id too. + * + * @param reader The #logger_reader. + * @param offset position of the record. + * + * @return position after the record. + */ +size_t tools_check_record_consistency(const struct logger_reader *reader, + size_t offset) { +#ifndef SWIFT_DEBUG_CHECKS + error("Should not check in non debug mode."); +#endif + + const struct header *h = &reader->log.header; + void *file_init = reader->log.log.map; + void *map = file_init + offset; + + size_t mask; + size_t pointed_offset; + + /* read mask + offset. */ + map = logger_loader_io_read_mask(h, map, &mask, &pointed_offset); + + /* get absolute offset. */ + if (header_is_forward(h)) + pointed_offset += offset; + else if (header_is_backward(h)) { + if (offset < pointed_offset) + error("Offset too large (%lu) at %lu with mask %lu.", pointed_offset, + offset, mask); + pointed_offset = offset - pointed_offset; + } else { + error("Offset are corrupted."); + } + + /* set offset after current record. */ + map += header_get_record_size_from_mask(h, mask); + + if (pointed_offset == offset || pointed_offset == 0) + return (size_t)(map - file_init); + + /* read mask of the pointed record. */ + size_t pointed_mask = 0; + logger_loader_io_read_mask(h, file_init + pointed_offset, &pointed_mask, + NULL); + + /* check if not mixing time stamp and particles. */ + if ((pointed_mask != 128 && mask == 128) || + (pointed_mask == 128 && mask != 128)) + error("Error in the offset (mask %lu at %lu != %lu at %lu).", mask, offset, + pointed_mask, pointed_offset); + + if (pointed_mask == 128) return (size_t)(map - file_init); + + struct logger_particle part; + logger_particle_read(&part, reader, offset, 0, logger_reader_const); + + size_t id = part.id; + logger_particle_read(&part, reader, pointed_offset, 0, logger_reader_const); + + if (id != part.id) + error("Offset wrong, id incorrect (%lu != %lu) at %lu.", id, part.id, + pointed_offset); + + return (size_t)(map - file_init); +} diff --git a/logger/logger_tools.h b/logger/logger_tools.h new file mode 100644 index 0000000000000000000000000000000000000000..21a59e42fca144a0381b15e8771ca14ceed46b33 --- /dev/null +++ b/logger/logger_tools.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +/** + * @brief This file contains functions that help to navigate in the logs. + */ +#ifndef LOGGER_LOGGER_TOOLS_H +#define LOGGER_LOGGER_TOOLS_H + +#include "../config.h" + +/* Swift include */ +#include "../src/dimension.h" +#include "../src/error.h" +#include "../src/inline.h" +#include "../src/logger.h" +#include "../src/part_type.h" + +#ifdef HAVE_PYTHON +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include <Python.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define STRING_SIZE 200 + +struct header; +struct logger_reader; + +int tools_get_next_record(const struct header *h, void *map, size_t *offset, + size_t file_size); +int _tools_get_next_record_backward(const struct header *h, void *map, + size_t *offset, size_t file_size); +int _tools_get_next_record_forward(const struct header *h, void *map, + size_t *offset); +size_t tools_reverse_offset(const struct header *h, void *map, size_t offset); +size_t tools_check_record_consistency(const struct logger_reader *reader, + size_t offset); + +#endif // LOGGER_LOGGER_TOOLS_H diff --git a/logger/python/reader_example.py b/logger/python/reader_example.py new file mode 100644 index 0000000000000000000000000000000000000000..6ace309c5b68b4fc4f1088b6206cd1ae3ccd69a5 --- /dev/null +++ b/logger/python/reader_example.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +""" +Read a logger file by using an index. +Example: ./reader_example.py ../../examples/SedovBlast_3D/index.dump ../../examples/SedovBlast_3D/index_0005.hdf5 +""" +import sys +from h5py import File +import numpy as np +import matplotlib.pyplot as plt +sys.path.append("../.libs/") + +import liblogger as logger + +# Get filenames +if len(sys.argv) != 3: + print("WARNING missing arguments. Will use the default ones") + index = "../../examples/HydroTests/SedovBlast_3D/index_0002.hdf5" + dump = "../../examples/HydroTests/SedovBlast_3D/index.dump" +else: + index = sys.argv[-1] + dump = sys.argv[-2] + +# constant +offset_name = "PartType0/Offset" +header = "Header" +time_name = "Time Offset" + +# Read index file +with File(index, "r") as f: + if offset_name not in f: + raise Exception("Unable to find the offset dataset") + offset = f[offset_name][:] + + if header not in f: + raise Exception("Unable to find the header") + if time_name not in f[header].attrs: + raise Exception("Unable to find the time offset") + time_offset = f[header].attrs[time_name] + +# read dump +data = logger.loadFromIndex(offset, dump, time_offset) + +# Compute distance from center +pos = data["positions"] +center = pos.mean() +r2 = np.sum((pos - center)**2, axis=1) + +# plot entropy vs distance +plt.plot(np.sqrt(r2), data["entropy"], '.') + +plt.xlim(0., 0.5) +plt.ylim(-5, 50) +plt.xlabel("Radius") +plt.ylabel("Entropy") +plt.show() diff --git a/logger/tests/Makefile.am b/logger/tests/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..dd94462b8b98b0a089d0f959b81c603c29911a76 --- /dev/null +++ b/logger/tests/Makefile.am @@ -0,0 +1,37 @@ +# This file is part of SWIFT. +# Copyright (c) 2019 loic.hausammann@epfl.ch. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU 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 General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Add the source directory and the non-standard paths to the included library headers to CFLAGS +AM_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/logger $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) + +AM_LDFLAGS = ../../src/.libs/libswiftsim.a ../.libs/liblogger.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) + +# List of programs and scripts to run in the test suite +TESTS = testLogfileHeader testLogfileReader testTimeArray + +# List of test programs to compile +check_PROGRAMS = testLogfileHeader testLogfileReader testTimeArray + +# Rebuild tests when SWIFT is updated. +$(check_PROGRAMS): ../../src/.libs/libswiftsim.a ../.libs/liblogger.a + +# Sources for the individual programs +testLogfileHeader_SOURCES = testLogfileHeader.c +testLogfileReader_SOURCES = testLogfileReader.c +testTimeArray_SOURCES = testTimeArray.c + +# Files necessary for distribution +EXTRA_DIST = testLogfileHeader.yml testLogfileReader.yml diff --git a/logger/tests/testLogfileHeader.c b/logger/tests/testLogfileHeader.c new file mode 100644 index 0000000000000000000000000000000000000000..0f2c8a5df7942d50cbb641b99e3173a05fe1d539 --- /dev/null +++ b/logger/tests/testLogfileHeader.c @@ -0,0 +1,95 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2019 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/>. + * + ******************************************************************************/ + +#include "logger_header.h" +#include "logger_logfile.h" +#include "logger_reader.h" +#include "swift.h" + +int main(int argc, char *argv[]) { + + /* + First generate the file. + */ + + message("Generating the dump."); + /* Create required structures. */ + struct logger_writer log; + struct swift_params params; + char filename[200] = "testLogfileHeader.yml"; + + /* Read parameters. */ + parser_read_file(filename, ¶ms); + + /* Initialize the logger. */ + logger_init(&log, ¶ms); + + /* get dump filename. */ + char dump_filename[PARSER_MAX_LINE_SIZE]; + strcpy(dump_filename, log.base_name); + strcat(dump_filename, ".dump"); + + /* Write file header. */ + logger_write_file_header(&log); + + /* clean memory. */ + logger_free(&log); + /* + Then read the file. + */ + + message("Reading the header."); + /* Generate required structure for reading. */ + struct logger_reader reader; + struct logger_logfile *logfile = &reader.log; + logfile->reader = &reader; + + /* Set verbose level. */ + reader.verbose = 1; + + /* Read the header */ + logger_logfile_init_from_file(logfile, dump_filename, &reader, + /* only_header */ 1); + /* + Finally check everything. + */ + + struct header *h = &logfile->header; + message("Checking versions."); + assert(h->major_version == logger_major_version); + assert(h->minor_version == logger_minor_version); + + message("Checking offset of first record"); + assert(h->offset_first_record == logfile->log.file_size); + + message("Checking number of masks"); + assert(h->number_mask == logger_count_mask); + + message("Checking masks"); + for (int i = 0; i < logger_count_mask; i++) { + assert(logger_mask_data[i].size == h->masks[i].size); + assert(logger_mask_data[i].mask == h->masks[i].mask); + assert(strcmp(logger_mask_data[i].name, h->masks[i].name) == 0); + } + + message("Checking offset direction"); + assert(h->offset_direction == logger_offset_backward); + + return 0; +} diff --git a/logger/tests/testLogfileHeader.yml b/logger/tests/testLogfileHeader.yml new file mode 100644 index 0000000000000000000000000000000000000000..b97c513fa9ee1c3d9816b54afed38f4124dc3957 --- /dev/null +++ b/logger/tests/testLogfileHeader.yml @@ -0,0 +1,6 @@ +# Parameter file for the tests +Logger: + delta_step: 10 + initial_buffer_size: 0.1 # in GB + buffer_scale: 10 + basename: test_header \ No newline at end of file diff --git a/logger/tests/testLogfileReader.c b/logger/tests/testLogfileReader.c new file mode 100644 index 0000000000000000000000000000000000000000..751c6b7d628fcd1191e8deba9135cddd8cd04bf8 --- /dev/null +++ b/logger/tests/testLogfileReader.c @@ -0,0 +1,311 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2019 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/>. + * + ******************************************************************************/ + +#include "logger_header.h" +#include "logger_loader_io.h" +#include "logger_particle.h" +#include "logger_reader.h" +#include "swift.h" + +#define number_parts 100 +/* Not all the fields are written at every step. + * Here we define how often a few fields are written. + */ +#define period_rho 2 +#define period_h 4 + +/** + * @brief Initialize the particles. + * + * @param p The array of #part. + * @param xp The array of #xpart. + */ +void init_particles(struct part *p, struct xpart *xp) { + struct hydro_space hs; + + for (int i = 0; i < number_parts; i++) { + /* Set internal energy. */ + hydro_set_init_internal_energy(&p[i], 100); + + /* Initialize particle. */ + hydro_first_init_part(&p[i], &xp[i]); + hydro_init_part(&p[i], &hs); + + for (int j = 0; j < 3; j++) { + p[i].x[j] = i; + p[i].v[j] = (j == 0) ? -1 : 0; + p[i].a_hydro[j] = (j == 1) ? 1e-2 : 0; + } + p[i].h = 15; + p[i].rho = 50; + p[i].id = i; + hydro_set_mass(&p[i], 1.5); + xp[i].logger_data.last_offset = 0; + + /* Add time bin in order to skip particles. */ + p[i].time_bin = (i % 10) + 1; + } +} + +/** Provides a integer time given the step number.*/ +integertime_t get_integer_time(int step) { return step; } + +/** Provides a double time given the step number. */ +double get_double_time(int step) { + const double time_base = 1e-4; + return step * time_base; +} + +/** + * @brief Write a few particles during multiple time steps. + * + * As only the logger is tested, there is no need to really + * evolve the particles. + */ +void write_particles(struct logger_writer *log, struct part *parts, + struct xpart *xparts) { + + const int number_steps = 100; + + /* Loop over all the steps. */ + for (int i = 0; i < number_steps; i++) { + integertime_t ti_int = get_integer_time(i); + double ti_double = get_double_time(i); + + /* Mark the current time step in the particle logger file. */ + logger_log_timestamp(log, ti_int, ti_double, &log->timestamp_offset); + /* Make sure that we have enough space in the particle logger file + * to store the particles in current time step. */ + logger_ensure_size(log, number_parts, /* number gpart */ 0, 0); + + /* Loop over all the particles. */ + for (int j = 0; j < number_parts; j++) { + + /* Skip some particles. */ + if (i % parts[j].time_bin != 0) continue; + + /* Write a time information to check that the correct particle is read. */ + parts[j].x[0] = i; + + /* Write this particle. */ + unsigned int mask = + logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask | + logger_mask_data[logger_a].mask | logger_mask_data[logger_u].mask | + logger_mask_data[logger_consts].mask; + + int number_particle_step = i / parts[j].time_bin; + + if (number_particle_step % period_h == 0) + mask |= logger_mask_data[logger_h].mask; + if (number_particle_step % period_rho == 0) + mask |= logger_mask_data[logger_rho].mask; + + logger_log_part(log, &parts[j], mask, &xparts[j].logger_data.last_offset); + } + + // TODO write index files. + } + + /* Mark the current time step in the particle logger file. */ + integertime_t ti_int = get_integer_time(number_steps); + double ti_double = get_double_time(number_steps); + logger_log_timestamp(log, ti_int, ti_double, &log->timestamp_offset); +} + +/** Count the number of active particles. */ +int get_number_active_particles(int step, struct part *p) { + int count = 0; + for (int i = 0; i < number_parts; i++) { + if (step % p[i].time_bin == 0) count += 1; + } + return count; +} +/** + * @brief Check that the reader contains the correct data + * + * @param reader The #logger_reader. + */ +void check_data(struct logger_reader *reader, struct part *parts, + struct xpart *xparts) { + + /* No need to check the header, this is already done in testHeader.c */ + + /* Get required structures. */ + struct logger_logfile *logfile = &reader->log; + + struct logger_particle lp; + logger_particle_init(&lp); + + /* Define a few variables */ + double time = get_double_time(0); + int is_particle = 0; + int step = -1; + + /* Number of particle found during this time step. */ + int count = 0; + /* Set it to an impossible value in order to flag it. */ + const size_t id_flag = 5 * number_parts; + size_t previous_id = id_flag; + + /* Loop over each record. */ + for (size_t offset = reader_read_record(reader, &lp, &time, &is_particle, + logfile->header.offset_first_record); + offset < logfile->log.file_size; + offset = reader_read_record(reader, &lp, &time, &is_particle, offset)) { + + /* Do the particle case */ + if (is_particle) { + count += 1; + + /* + Check that we are really increasing the id in the logfile. + See the writing part to see that we are always increasing the id. + */ + if (previous_id != id_flag && previous_id >= lp.id) { + error("Wrong particle found"); + previous_id = lp.id; + } + + /* Get the corresponding particle */ + if (lp.id >= number_parts) error("Wrong id %zi", lp.id); + + struct part *p = &parts[lp.id]; + + /* Check the record's data. */ + for (int i = 0; i < 3; i++) { + /* in the first index, we are storing the step information. */ + if (i == 0) + assert(step == lp.pos[i]); + else + assert(p->x[i] == lp.pos[i]); + assert(p->v[i] == lp.vel[i]); + assert(p->a_hydro[i] == lp.acc[i]); + } + + assert(p->entropy == lp.entropy); + assert(p->mass == lp.mass); + + /* Check optional fields. */ + int number_steps = step / p->time_bin; + if (number_steps % period_h == 0) { + assert(p->h == lp.h); + } else { + assert(-1 == lp.h); + } + if (number_steps % period_rho == 0) { + assert(p->rho == lp.density); + } else { + assert(-1 == lp.density); + } + } + /* Time stamp case. */ + else { + + /* Check if we have the current amount of particles in previous step. */ + if (step != -1 && count != get_number_active_particles(step, parts)) + error( + "The reader did not find the correct number of particles during " + "step %i", + step); + + step += 1; + + /* Reset some variables. */ + previous_id = id_flag; + count = 0; + + /* Check the record's data. */ + assert(time == get_double_time(step)); + } + } +} + +int main(int argc, char *argv[]) { + + /* + First generate the file. + */ + + message("Generating the dump."); + + /* Create required structures. */ + struct logger_writer log; + struct swift_params params; + char filename[200] = "testLogfileReader.yml"; + + /* Read parameters. */ + parser_read_file(filename, ¶ms); + + /* Initialize the particles. */ + struct part *parts; + if ((parts = (struct part *)malloc(sizeof(struct part) * number_parts)) == + NULL) + error("Failed to allocate particles array."); + + struct xpart *xparts; + if ((xparts = (struct xpart *)malloc(sizeof(struct xpart) * number_parts)) == + NULL) + error("Failed to allocate xparticles array."); + + init_particles(parts, xparts); + + /* Initialize the logger. */ + logger_init(&log, ¶ms); + + /* get dump filename. */ + char dump_filename[PARSER_MAX_LINE_SIZE]; + message("%s", log.base_name); + strcpy(dump_filename, log.base_name); + strcat(dump_filename, ".dump"); + + /* Write file header. */ + logger_write_file_header(&log); + + /* Write particles. */ + write_particles(&log, parts, xparts); + + /* clean memory */ + logger_free(&log); + /* + Then read the file. + */ + + message("Reading the header."); + + /* Generate required structure for reading. */ + struct logger_reader reader; + + /* Set verbose level. */ + reader.verbose = 1; + + /* Read the header. */ + logger_reader_init(&reader, dump_filename, /* verbose */ 1); + + /* + Finally check everything. + */ + + check_data(&reader, parts, xparts); + + /* Do some cleanup. */ + free(parts); + free(xparts); + + return 0; +} diff --git a/logger/tests/testLogfileReader.yml b/logger/tests/testLogfileReader.yml new file mode 100644 index 0000000000000000000000000000000000000000..1ac5e2da909f1fe53cba052bbd24c5c3ce98dfed --- /dev/null +++ b/logger/tests/testLogfileReader.yml @@ -0,0 +1,6 @@ +# Parameter file for the tests +Logger: + delta_step: 10 + initial_buffer_size: 0.01 # in GB + buffer_scale: 10 + basename: test_reader \ No newline at end of file diff --git a/logger/tests/testTimeArray.c b/logger/tests/testTimeArray.c new file mode 100644 index 0000000000000000000000000000000000000000..929a7124baa8ab05fd3452f87076d95c88c2f3b2 --- /dev/null +++ b/logger/tests/testTimeArray.c @@ -0,0 +1,78 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2019 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/>. + * + ******************************************************************************/ + +#include <stdlib.h> +#include <time.h> +#include "logger_time.h" + +#define NUMBER_OF_ELEMENT 10000 +#define TIME_BASE 0.04 +#define OFFSET_BASE 1000 + +int main(int argc, char *argv[]) { + + /* Check that we are really testing the reallocation */ + if (NUMBER_OF_ELEMENT < LOGGER_TIME_INIT_SIZE) { + error("Not testing the reallocation."); + } + + /* Fix the random seed in order to reproduce the results */ + srand(100); + + /* Initialize the time array */ + struct time_array times; + time_array_init(×); + + /* Add elements */ + for (size_t i = 0; i < NUMBER_OF_ELEMENT; i++) { + integertime_t int_time = i; + double time = i * TIME_BASE; + size_t offset = i * OFFSET_BASE; + + time_array_append(×, int_time, time, offset); + } + + /* Check the elements */ + for (size_t i = 0; i < NUMBER_OF_ELEMENT; i++) { + integertime_t int_time = i; + double time = i * TIME_BASE; + size_t offset = i * OFFSET_BASE; + + /* Ensure that we can get the correct offset when looking + in between the records. */ + int r = rand() % OFFSET_BASE; + size_t read_offset = offset + r; + + /* The offset cannot be larger than the largest one */ + if (i == NUMBER_OF_ELEMENT - 1) { + read_offset = offset; + } + + /* Get the index from the offset */ + size_t ind = time_array_get_index(×, read_offset); + + /* Check the values obtained */ + assert(i == ind); + assert(int_time == times.records[ind].int_time); + assert(time == times.records[ind].time); + assert(offset == times.records[ind].offset); + } + + return 0; +} diff --git a/m4/ax_cc_maxopt.m4 b/m4/ax_cc_maxopt.m4 index 7523d9d09ee741914fe9fe1cb22d3e0550763233..72407f3b6a5f29758fbb7b41afda9357ce4210ec 100644 --- a/m4/ax_cc_maxopt.m4 +++ b/m4/ax_cc_maxopt.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_cc_maxopt.html +# https://www.gnu.org/software/autoconf-archive/ax_cc_maxopt.html # =========================================================================== # # SYNOPSIS @@ -40,7 +40,7 @@ # Public License for more details. # # You should have received a copy of the GNU General Public License along -# with this program. If not, see <http://www.gnu.org/licenses/>. +# with this program. If not, see <https://www.gnu.org/licenses/>. # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure @@ -55,7 +55,7 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 16 +#serial 18 AC_DEFUN([AX_CC_MAXOPT], [ @@ -68,19 +68,18 @@ AC_ARG_ENABLE(portable-binary, [AS_HELP_STRING([--enable-portable-binary], [disa # Try to determine "good" native compiler flags if none specified via CFLAGS if test "$ac_test_CFLAGS" != "set"; then - CFLAGS="" case $ax_cv_c_compiler_vendor in - dec) CFLAGS="-newc -w0 -O5 -ansi_alias -ansi_args -fp_reorder -tune host" + dec) CFLAGS="$CFLAGS -newc -w0 -O5 -ansi_alias -ansi_args -fp_reorder -tune host" if test "x$acx_maxopt_portable" = xno; then CFLAGS="$CFLAGS -arch host" fi;; - sun) CFLAGS="-native -fast -xO5 -dalign" + sun) CFLAGS="$CFLAGS -native -fast -xO5 -dalign" if test "x$acx_maxopt_portable" = xyes; then CFLAGS="$CFLAGS -xarch=generic" fi;; - hp) CFLAGS="+Oall +Optrs_ansi +DSnative" + hp) CFLAGS="$CFLAGS +Oall +Optrs_ansi +DSnative" if test "x$acx_maxopt_portable" = xyes; then CFLAGS="$CFLAGS +DAportable" fi;; @@ -91,8 +90,8 @@ if test "$ac_test_CFLAGS" != "set"; then xlc_opt="-qtune=auto" fi AX_CHECK_COMPILE_FLAG($xlc_opt, - CFLAGS="-O3 -qansialias -w $xlc_opt", - [CFLAGS="-O3 -qansialias -w" + CFLAGS="$CFLAGS -O3 -qansialias -w $xlc_opt", + [CFLAGS="$CFLAGS -O3 -qansialias -w" echo "******************************************************" echo "* You seem to have the IBM C compiler. It is *" echo "* recommended for best performance that you use: *" @@ -105,7 +104,7 @@ if test "$ac_test_CFLAGS" != "set"; then echo "******************************************************"]) ;; - intel) CFLAGS="-O3 -ansi-alias" + intel) CFLAGS="$CFLAGS -O3 -ansi-alias" if test "x$acx_maxopt_portable" = xno; then icc_archflag=unknown icc_flags="" @@ -151,7 +150,7 @@ if test "$ac_test_CFLAGS" != "set"; then clang) # default optimization flags for clang on all systems - CFLAGS="-O3 -fomit-frame-pointer" + CFLAGS="$CFLAGS -O3 -fomit-frame-pointer" # Always good optimisation to have AX_CHECK_COMPILE_FLAG(-fstrict-aliasing, CFLAGS="$CFLAGS -fstrict-aliasing") @@ -167,7 +166,7 @@ if test "$ac_test_CFLAGS" != "set"; then gnu) # default optimization flags for gcc on all systems - CFLAGS="-O3 -fomit-frame-pointer" + CFLAGS="$CFLAGS -O3 -fomit-frame-pointer" # -malign-double for x86 systems AX_CHECK_COMPILE_FLAG(-malign-double, CFLAGS="$CFLAGS -malign-double") @@ -187,7 +186,7 @@ if test "$ac_test_CFLAGS" != "set"; then microsoft) # default optimization flags for MSVC opt builds - CFLAGS="-O2" + CFLAGS="$CFLAGS -O2" ;; esac @@ -199,7 +198,7 @@ if test "$ac_test_CFLAGS" != "set"; then echo "* (otherwise, a default of CFLAGS=-O3 will be used) *" echo "********************************************************" echo "" - CFLAGS="-O3" + CFLAGS="$CFLAGS -O3" fi AX_CHECK_COMPILE_FLAG($CFLAGS, [], [ @@ -210,7 +209,6 @@ if test "$ac_test_CFLAGS" != "set"; then echo "* Use ./configure CFLAGS=... to specify your own flags *" echo "********************************************************" echo "" - CFLAGS="" ]) fi diff --git a/src/Makefile.am b/src/Makefile.am index 7556089b828cb901f0ba2b1e576cf4793e3e1e8e..665aa4b24c94162fb8f772edd346f3c95a1d7ddb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,14 +44,15 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ common_io.h single_io.h multipole.h map.h tools.h partition.h partition_fixed_costs.h \ clocks.h parser.h physical_constants.h physical_constants_cgs.h potential.h version.h \ hydro_properties.h riemann.h threadpool.h cooling_io.h cooling.h cooling_struct.h \ - statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h entropy_floor.h \ + statistics.h memswap.h cache.h runner_doiact_hydro_vec.h profiler.h entropy_floor.h \ dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h \ gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h \ chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h space_getsid.h utilities.h \ mesh_gravity.h cbrt.h exp10.h velociraptor_interface.h swift_velociraptor_part.h outputlist.h \ - logger_io.h tracers_io.h tracers.h tracers_struct.h star_formation_io.h fof.h \ - star_formation_struct.h star_formation.h star_formation_iact.h \ + logger_io.h tracers_io.h tracers.h tracers_struct.h star_formation_io.h fof.h fof_struct.h fof_io.h \ + star_formation_struct.h star_formation.h \ star_formation_logger.h star_formation_logger_struct.h \ + pressure_floor.h pressure_floor_struct.h pressure_floor_iact.h \ velociraptor_struct.h velociraptor_io.h random.h memuse.h black_holes.h black_holes_io.h \ black_holes_properties.h black_holes_struct.h feedback.h feedback_struct.h feedback_properties.h @@ -68,24 +69,31 @@ EAGLE_FEEDBACK_SOURCES += feedback/EAGLE/feedback.c endif # Common source files -AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c engine_maketasks.c \ - engine_marktasks.c engine_drift.c serial_io.c timers.c debug.c scheduler.c \ +AM_SOURCES = space.c runner_main.c runner_doiact_hydro.c runner_doiact_grav.c \ + runner_doiact_stars.c runner_doiact_black_holes.c runner_ghost.c runner_recv.c \ + runner_sort.c runner_drift.c runner_black_holes.c runner_time_integration.c \ + runner_doiact_hydro_vec.c runner_others.c\ + queue.c task.c cell.c engine.c engine_maketasks.c \ + engine_marktasks.c engine_drift.c engine_unskip.c engine_collect_end_of_step.c \ + engine_redistribute.c engine_fof.c serial_io.c timers.c debug.c scheduler.c \ proxy.c parallel_io.c units.c common_io.c single_io.c multipole.c version.c map.c \ kernel_hydro.c tools.c part.c partition.c clocks.c parser.c \ physical_constants.c potential.c hydro_properties.c \ threadpool.c cooling.c star_formation.c \ - statistics.c runner_doiact_vec.c profiler.c dump.c logger.c \ + statistics.c profiler.c dump.c logger.c \ part_type.c xmf.c gravity_properties.c gravity.c \ collectgroup.c hydro_space.c equation_of_state.c \ chemistry.c cosmology.c restart.c mesh_gravity.c velociraptor_interface.c \ outputlist.c velociraptor_dummy.c logger_io.c memuse.c fof.c \ - hashmap.c \ + hashmap.c pressure_floor.c \ $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES) # 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 runner_doiact_black_holes.h units.h intrinsics.h minmax.h \ + gravity_iact.h kernel_long_gravity.h vector.h cache.h \ + runner_doiact_nosort.h runner_doiact_hydro.h runner_doiact_stars.h runner_doiact_black_holes.h runner_doiact_grav.h \ + runner_doiact_functions_hydro.h runner_doiact_functions_stars.h runner_doiact_functions_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 hashmap.h \ gravity.h gravity_io.h gravity_cache.h \ @@ -93,33 +101,35 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h gravity/Default/gravity_debug.h gravity/Default/gravity_part.h \ gravity/Potential/gravity.h gravity/Potential/gravity_iact.h gravity/Potential/gravity_io.h \ gravity/Potential/gravity_debug.h gravity/Potential/gravity_part.h \ + gravity/MultiSoftening/gravity.h gravity/MultiSoftening/gravity_iact.h gravity/MultiSoftening/gravity_io.h \ + gravity/MultiSoftening/gravity_debug.h gravity/MultiSoftening/gravity_part.h \ equation_of_state.h \ equation_of_state/ideal_gas/equation_of_state.h equation_of_state/isothermal/equation_of_state.h \ hydro.h hydro_io.h hydro_parameters.h \ hydro/Minimal/hydro.h hydro/Minimal/hydro_iact.h hydro/Minimal/hydro_io.h \ hydro/Minimal/hydro_debug.h hydro/Minimal/hydro_part.h \ - hydro/Minimal/hydro_parameters.h \ + hydro/Minimal/hydro_parameters.h \ hydro/Default/hydro.h hydro/Default/hydro_iact.h hydro/Default/hydro_io.h \ hydro/Default/hydro_debug.h hydro/Default/hydro_part.h \ - hydro/Default/hydro_parameters.h \ + hydro/Default/hydro_parameters.h \ hydro/Gadget2/hydro.h hydro/Gadget2/hydro_iact.h hydro/Gadget2/hydro_io.h \ hydro/Gadget2/hydro_debug.h hydro/Gadget2/hydro_part.h \ - hydro/Gadget2/hydro_parameters.h \ + hydro/Gadget2/hydro_parameters.h \ hydro/PressureEntropy/hydro.h hydro/PressureEntropy/hydro_iact.h hydro/PressureEntropy/hydro_io.h \ hydro/PressureEntropy/hydro_debug.h hydro/PressureEntropy/hydro_part.h \ - hydro/PressureEntropy/hydro_parameters.h \ + hydro/PressureEntropy/hydro_parameters.h \ hydro/PressureEnergy/hydro.h hydro/PressureEnergy/hydro_iact.h hydro/PressureEnergy/hydro_io.h \ hydro/PressureEnergy/hydro_debug.h hydro/PressureEnergy/hydro_part.h \ - hydro/PressureEnergy/hydro_parameters.h \ + hydro/PressureEnergy/hydro_parameters.h \ hydro/PressureEnergyMorrisMonaghanAV/hydro.h hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h \ hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h \ - hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h \ + hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h \ hydro/AnarchyPU/hydro.h hydro/PressureEnergy/hydro_iact.h hydro/PressureEnergy/hydro_io.h \ hydro/AnarchyPU/hydro_debug.h hydro/PressureEnergy/hydro_part.h \ - hydro/AnarchyPU/hydro_parameters.h \ + hydro/AnarchyPU/hydro_parameters.h \ hydro/AnarchyDU/hydro.h hydro/PressureEnergy/hydro_iact.h hydro/PressureEnergy/hydro_io.h \ hydro/AnarchyDU/hydro_debug.h hydro/PressureEnergy/hydro_part.h \ - hydro/AnarchyDU/hydro_parameters.h \ + hydro/AnarchyDU/hydro_parameters.h \ hydro/GizmoMFV/hydro.h hydro/GizmoMFV/hydro_iact.h \ hydro/GizmoMFV/hydro_io.h hydro/GizmoMFV/hydro_debug.h \ hydro/GizmoMFV/hydro_part.h \ @@ -131,7 +141,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h hydro/GizmoMFV/hydro_slope_limiters.h \ hydro/GizmoMFV/hydro_unphysical.h \ hydro/GizmoMFV/hydro_velocities.h \ - hydro/GizmoMFV/hydro_parameters.h \ + hydro/GizmoMFV/hydro_parameters.h \ hydro/GizmoMFM/hydro.h hydro/GizmoMFM/hydro_iact.h \ hydro/GizmoMFM/hydro_io.h hydro/GizmoMFM/hydro_debug.h \ hydro/GizmoMFM/hydro_part.h \ @@ -142,7 +152,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h hydro/GizmoMFM/hydro_slope_limiters_face.h \ hydro/GizmoMFM/hydro_slope_limiters.h \ hydro/GizmoMFM/hydro_unphysical.h \ - hydro/GizmoMFM/hydro_parameters.h \ + hydro/GizmoMFM/hydro_parameters.h \ hydro/Shadowswift/hydro_debug.h \ hydro/Shadowswift/hydro_gradients.h hydro/Shadowswift/hydro.h \ hydro/Shadowswift/hydro_iact.h \ @@ -172,11 +182,11 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h potential/isothermal/potential.h potential/disc_patch/potential.h \ potential/sine_wave/potential.h \ star_formation/none/star_formation.h star_formation/none/star_formation_struct.h \ - star_formation/none/star_formation_io.h star_formation/none/star_formation_iact.h \ + star_formation/none/star_formation_io.h \ star_formation/EAGLE/star_formation.h star_formation/EAGLE/star_formation_struct.h \ - star_formation/EAGLE/star_formation_io.h star_formation/EAGLE/star_formation_iact.h \ + star_formation/EAGLE/star_formation_io.h \ star_formation/GEAR/star_formation.h star_formation/GEAR/star_formation_struct.h \ - star_formation/GEAR/star_formation_io.h star_formation/GEAR/star_formation_iact.h \ + star_formation/GEAR/star_formation_io.h \ star_formation/EAGLE/star_formation_logger.h star_formation/EAGLE/star_formation_logger_struct.h \ star_formation/GEAR/star_formation_logger.h star_formation/GEAR/star_formation_logger_struct.h \ star_formation/none/star_formation_logger.h star_formation/none/star_formation_logger_struct.h \ @@ -190,7 +200,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h cooling/const_lambda/cooling_io.h \ cooling/grackle/cooling.h cooling/grackle/cooling_struct.h \ cooling/grackle/cooling_io.h \ - cooling/EAGLE/cooling.h cooling/EAGLE/cooling_struct.h \ + cooling/EAGLE/cooling.h cooling/EAGLE/cooling_struct.h cooling/EAGLE/cooling_tables.h \ cooling/EAGLE/cooling_io.h cooling/EAGLE/interpolate.h cooling/EAGLE/cooling_rates.h \ chemistry/none/chemistry.h \ chemistry/none/chemistry_io.h \ @@ -221,7 +231,11 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h black_holes/Default/black_holes_struct.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 + black_holes/EAGLE/black_holes_properties.h \ + black_holes/EAGLE/black_holes_struct.h \ + pressure_floor/GEAR/pressure_floor.h pressure_floor/none/pressure_floor.h \ + pressure_floor/GEAR/pressure_floor_iact.h pressure_floor/none/pressure_floor_iact.h \ + pressure_floor/GEAR/pressure_floor_struct.h pressure_floor/none/pressure_floor_struct.h # Sources and flags for regular library diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h index de7c3871cfb6e42739edf45a7d5b4882547d3cc2..d64968d736df0f0539a568632e8cf9c50a85145e 100644 --- a/src/adiabatic_index.h +++ b/src/adiabatic_index.h @@ -545,4 +545,38 @@ pow_three_gamma_minus_five_over_two(float x) { #endif } +/** + * @brief Return the argument to the power three (adiabatic index - 1). + * + * Computes \f$x^{3(\gamma - 1)}\f$. + * + * @param x Argument + */ +__attribute__((always_inline, const)) INLINE static float +pow_three_gamma_minus_one(float x) { + +#if defined(HYDRO_GAMMA_5_3) + + return x * x; /* x^(2) */ + +#elif defined(HYDRO_GAMMA_7_5) + + return powf(x, 1.2f); /* x^(6/5) */ + +#elif defined(HYDRO_GAMMA_4_3) + + return x; /* x^(1) */ + +#elif defined(HYDRO_GAMMA_2_1) + + return x * x * x; /* x^(3) */ + +#else + + error("The adiabatic index is not defined !"); + return 0.f; + +#endif +} + #endif /* SWIFT_ADIABATIC_INDEX_H */ diff --git a/src/black_holes/Default/black_holes.h b/src/black_holes/Default/black_holes.h index ebc5a36841e07fe96d81319914f065516ef43517..082134cc78f5a7f5e0aa9adeb231c92b9d66c233 100644 --- a/src/black_holes/Default/black_holes.h +++ b/src/black_holes/Default/black_holes.h @@ -157,39 +157,17 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part( } /** - * @brief Update a given #part's BH data field to mark the particle has - * not yet been swallowed. - * - * @param p_data The #part's #black_holes_part_data structure. - */ -__attribute__((always_inline)) INLINE static void -black_holes_mark_as_not_swallowed(struct black_holes_part_data* p_data) { - - /* Nothing to do here: No swallowing in the default model */ -} - -/** - * @brief Update a given #part's BH data field to mark the particle has - * having been been swallowed. - * - * @param p_data The #part's #black_holes_part_data structure. - */ -__attribute__((always_inline)) INLINE static void black_holes_mark_as_swallowed( - struct black_holes_part_data* p_data) { - - /* Nothing to do here: No swallowing in the default model */ -} - -/** - * @brief Return the ID of the BH that should swallow this #part. + * @brief Update the properties of a black hole particles by swallowing + * a BH particle. * - * @param p_data The #part's #black_holes_part_data structure. + * @param bpi The #bpart to update. + * @param bpj The #bpart that is swallowed. + * @param cosmo The current cosmological model. */ -__attribute__((always_inline)) INLINE static long long -black_holes_get_swallow_id(struct black_holes_part_data* p_data) { +__attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( + struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo) { - /* Return a non-existing ID */ - return -1; + /* Nothing to do here: No merging in the default model */ } /** @@ -209,6 +187,20 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const struct phys_const* constants, const struct cosmology* cosmo, const double dt) {} +/** + * @brief Finish the calculation of the new BH position. + * + * 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. + */ +__attribute__((always_inline)) INLINE static void black_holes_end_reposition( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo) {} + /** * @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 index 37e3facc4a14a8eaa1b87e1c60dfeae22dfecf44..b26b8a50f40599402f4ea9886dbbd7495e8efd59 100644 --- a/src/black_holes/Default/black_holes_iact.h +++ b/src/black_holes/Default/black_holes_iact.h @@ -30,13 +30,15 @@ * @param pj Second particle (gas, not updated). * @param xpj The extended data of the second particle (not updated). * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). * @param ti_current Current integer time value (for random numbers). */ -__attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_density( +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_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) { + const struct gravity_props *grav_props, const integertime_t ti_current) { float wi, wi_dx; @@ -77,13 +79,40 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_density( * @param pj Second particle (gas) * @param xpj The extended data of the second particle. * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). * @param ti_current Current integer time value (for random numbers). */ -__attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_swallow( +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_swallow( const float r2, const float *dx, const float hi, const float hj, - struct bpart *restrict bi, struct part *restrict pj, + const struct bpart *restrict bi, struct part *restrict pj, struct xpart *restrict xpj, const struct cosmology *cosmo, - const integertime_t ti_current) {} + const struct gravity_props *grav_props, const integertime_t ti_current) {} + +/** + * @brief Swallowing interaction between two BH particles (non-symmetric). + * + * Function used to flag the BH particles that will be swallowed + * by the black hole particle. + * + * @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 bj Second particle (black hole) + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, + const float hi, const float hj, + const struct bpart *restrict bi, + struct bpart *restrict bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const integertime_t ti_current) {} /** * @brief Feedback interaction between two particles (non-symmetric). @@ -96,15 +125,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_swallow( * @param pj Second particle (gas) * @param xpj The extended data of the second particle. * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). * @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) { +runner_iact_nonsym_bh_gas_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 struct gravity_props *grav_props, const integertime_t ti_current) { #ifdef DEBUG_INTERACTIONS_BH /* Update ngb counters */ if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_BH) diff --git a/src/black_holes/Default/black_holes_io.h b/src/black_holes/Default/black_holes_io.h index 41ca7c1cd0b3542959d8a7372179c7e0cb285285..53322da1dd90a98fb0f1f2529774a20dc718b30c 100644 --- a/src/black_holes/Default/black_holes_io.h +++ b/src/black_holes/Default/black_holes_io.h @@ -22,20 +22,6 @@ #include "black_holes_part.h" #include "io_properties.h" -INLINE static void convert_bpart_pos(const struct engine *e, - const struct bpart *bp, double *ret) { - - if (e->s->periodic) { - ret[0] = box_wrap(bp->x[0], 0.0, e->s->dim[0]); - ret[1] = box_wrap(bp->x[1], 0.0, e->s->dim[1]); - ret[2] = box_wrap(bp->x[2], 0.0, e->s->dim[2]); - } else { - ret[0] = bp->x[0]; - ret[1] = bp->x[1]; - ret[2] = bp->x[2]; - } -} - /** * @brief Specifies which b-particle fields to read from a dataset * @@ -43,17 +29,16 @@ INLINE static void convert_bpart_pos(const struct engine *e, * @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) { +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 = 5; /* List what we want to read */ - list[0] = io_make_output_field_convert_bpart( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, bparts, convert_bpart_pos); - + 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, @@ -64,31 +49,88 @@ INLINE static void black_holes_read_particles(struct bpart *bparts, UNIT_CONV_LENGTH, bparts, h); } +INLINE static void convert_bpart_pos(const struct engine* e, + const struct bpart* bp, double* ret) { + + if (e->s->periodic) { + ret[0] = box_wrap(bp->x[0], 0.0, e->s->dim[0]); + ret[1] = box_wrap(bp->x[1], 0.0, e->s->dim[1]); + ret[2] = box_wrap(bp->x[2], 0.0, e->s->dim[2]); + } else { + ret[0] = bp->x[0]; + ret[1] = bp->x[1]; + ret[2] = bp->x[2]; + } +} + +INLINE static void convert_bpart_vel(const struct engine* e, + const struct bpart* bp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, bp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, bp->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + const struct gpart* gp = bp->gpart; + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + /** * @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. + * @param with_cosmology Are we running a cosmological simulation? */ -INLINE static void black_holes_write_particles(const struct bpart *bparts, - struct io_props *list, - int *num_fields) { +INLINE static void black_holes_write_particles(const struct bpart* bparts, + struct io_props* list, + int* num_fields, + int with_cosmology) { /* Say how much we want to write */ *num_fields = 5; /* 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[0] = io_make_output_field_convert_bpart( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, bparts, + convert_bpart_pos, "Co-moving position of the particles"); + + list[1] = io_make_output_field_convert_bpart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, convert_bpart_vel, + "Peculiar velocities of the particles. This is a * dx/dt where x is the " + "co-moving position of the particles."); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, mass, "Masses of the particles"); + 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); + 0.f, bparts, id, "Unique ID of the particles"); + + list[4] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, bparts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); #ifdef DEBUG_INTERACTIONS_BLACK_HOLES diff --git a/src/black_holes/Default/black_holes_part.h b/src/black_holes/Default/black_holes_part.h index db8c9b18cb8bac72cd7b3b239930aa2306e5171d..1b7d4ac37caf100c48b504a3836166ce33c24169 100644 --- a/src/black_holes/Default/black_holes_part.h +++ b/src/black_holes/Default/black_holes_part.h @@ -21,6 +21,8 @@ #include "chemistry_struct.h" +#include "timeline.h" + /** * @brief Particle fields for the black hole particles. * @@ -66,6 +68,9 @@ struct bpart { * content, etc.) */ struct chemistry_bpart_data chemistry_data; + /*! Black holes merger information (e.g. merging ID) */ + struct black_holes_bpart_data merger_data; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/black_holes/Default/black_holes_struct.h b/src/black_holes/Default/black_holes_struct.h index 3fdb0ee882b075d40a8725fa52b221d48dcbd722..0b06ded1a7ac3114363f1e49fc527a18dd5a1cf9 100644 --- a/src/black_holes/Default/black_holes_struct.h +++ b/src/black_holes/Default/black_holes_struct.h @@ -24,4 +24,81 @@ */ struct black_holes_part_data {}; +/** + * @brief Black holes-related fields carried by each *BH* particle. + */ +struct black_holes_bpart_data {}; + +/** + * @brief Update a given #part's BH data field to mark the particle has + * not yet been swallowed. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_part_as_not_swallowed(struct black_holes_part_data* p_data) { + + /* Nothing to do here: No swallowing in the default model */ +} + +/** + * @brief Update a given #part's BH data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_part_as_swallowed(struct black_holes_part_data* p_data) { + + /* Nothing to do here: No swallowing in the default model */ +} + +/** + * @brief Return the ID of the BH that should swallow this #part. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static long long +black_holes_get_part_swallow_id(struct black_holes_part_data* p_data) { + + /* Return a non-existing ID */ + return -1; +} + +/** + * @brief Update a given #bpart's BH data field to mark the particle has + * not yet been swallowed. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_bpart_as_not_swallowed(struct black_holes_bpart_data* p_data) { + + /* Nothing to do here: No merging in the default model */ +} + +/** + * @brief Update a given #bpart's BH data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_bpart_as_merged(struct black_holes_bpart_data* p_data) { + + /* Nothing to do here: No merging in the default model */ +} + +/** + * @brief Return the ID of the BH that should swallow this #bpart. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static long long +black_holes_get_bpart_swallow_id(struct black_holes_bpart_data* p_data) { + + /* Return a non-existing ID */ + return -1; +} + #endif /* SWIFT_BLACK_HOLES_STRUCT_DEFAULT_H */ diff --git a/src/black_holes/EAGLE/black_holes.h b/src/black_holes/EAGLE/black_holes.h index a9a40b1cfb9604045913c90a25ed5b3771b0f3e4..a1b41f0954e1bec890329779d0edb7fcd31265f1 100644 --- a/src/black_holes/EAGLE/black_holes.h +++ b/src/black_holes/EAGLE/black_holes.h @@ -24,6 +24,7 @@ #include "black_holes_struct.h" #include "cosmology.h" #include "dimension.h" +#include "gravity.h" #include "kernel_hydro.h" #include "minmax.h" #include "physical_constants.h" @@ -59,6 +60,8 @@ __attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( bp->total_accreted_mass = 0.f; bp->accretion_rate = 0.f; bp->formation_time = -1.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; } /** @@ -87,16 +90,43 @@ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( bp->circular_velocity_gas[2] = 0.f; bp->ngb_mass = 0.f; bp->num_ngbs = 0; + bp->reposition.x[0] = -FLT_MAX; + bp->reposition.x[1] = -FLT_MAX; + bp->reposition.x[2] = -FLT_MAX; + bp->reposition.min_potential = FLT_MAX; } /** * @brief Predict additional particle fields forward in time when drifting * + * The fields do not get predicted but we move the BH to its new position + * if a new one was calculated in the repositioning loop. + * * @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) {} + struct bpart* restrict bp, float dt_drift) { + + /* Are we doing some repositioning? */ + if (bp->reposition.min_potential != FLT_MAX) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->reposition.x[0] == -FLT_MAX || bp->reposition.x[1] == -FLT_MAX || + bp->reposition.x[2] == -FLT_MAX) { + error("Something went wrong with the new repositioning position"); + } +#endif + + bp->x[0] = bp->reposition.x[0]; + bp->x[1] = bp->reposition.x[1]; + bp->x[2] = bp->reposition.x[2]; + + bp->gpart->x[0] = bp->reposition.x[0]; + bp->gpart->x[1] = bp->reposition.x[1]; + bp->gpart->x[2] = bp->reposition.x[2]; + } +} /** * @brief Sets the values to be predicted in the drifts to their values at a @@ -235,38 +265,45 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part( } /** - * @brief Update a given #part's BH data field to mark the particle has - * not yet been swallowed. + * @brief Update the properties of a black hole particles by swallowing + * a BH particle. * - * @param p_data The #part's #black_holes_part_data structure. + * @param bpi The #bpart to update. + * @param bpj The #bpart that is swallowed. + * @param cosmo The current cosmological model. */ -__attribute__((always_inline)) INLINE static void -black_holes_mark_as_not_swallowed(struct black_holes_part_data* p_data) { +__attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( + struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo) { - p_data->swallow_id = -1; -} + /* Get the current dynamical masses */ + const float bpi_dyn_mass = bpi->mass; + const float bpj_dyn_mass = bpj->mass; -/** - * @brief Update a given #part's BH data field to mark the particle has - * having been been swallowed. - * - * @param p_data The #part's #black_holes_part_data structure. - */ -__attribute__((always_inline)) INLINE static void black_holes_mark_as_swallowed( - struct black_holes_part_data* p_data) { + /* Increase the masses of the BH. */ + bpi->mass += bpj->mass; + bpi->gpart->mass += bpj->mass; + bpi->subgrid_mass += bpj->subgrid_mass; - p_data->swallow_id = -2; -} + /* Update the BH momentum */ + const float BH_mom[3] = {bpi_dyn_mass * bpi->v[0] + bpj_dyn_mass * bpj->v[0], + bpi_dyn_mass * bpi->v[1] + bpj_dyn_mass * bpj->v[1], + bpi_dyn_mass * bpi->v[2] + bpj_dyn_mass * bpj->v[2]}; -/** - * @brief Return the ID of the BH that should swallow this #part. - * - * @param p_data The #part's #black_holes_part_data structure. - */ -__attribute__((always_inline)) INLINE static long long -black_holes_get_swallow_id(struct black_holes_part_data* p_data) { + bpi->v[0] = BH_mom[0] / bpi->mass; + bpi->v[1] = BH_mom[1] / bpi->mass; + bpi->v[2] = BH_mom[2] / bpi->mass; + bpi->gpart->v_full[0] = bpi->v[0]; + bpi->gpart->v_full[1] = bpi->v[1]; + bpi->gpart->v_full[2] = bpi->v[2]; + + /* Update the energy reservoir */ + bpi->energy_reservoir += bpj->energy_reservoir; - return p_data->swallow_id; + /* Add up all the BH seeds */ + bpi->cumulative_number_seeds += bpj->cumulative_number_seeds; + + /* We had another merger */ + bpi->number_of_mergers++; } /** @@ -421,6 +458,35 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( } } +/** + * @brief Finish the calculation of the new BH position. + * + * Here, we check that the BH should indeed be moved in the next drift. + * + * @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. + */ +__attribute__((always_inline)) INLINE static void black_holes_end_reposition( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo) { + + const float potential = gravity_get_comoving_potential(bp->gpart); + + /* Is the potential lower (i.e. the BH is at the bottom already) + * OR is the BH massive enough that we don't reposition? */ + if (potential < bp->reposition.min_potential || + bp->subgrid_mass > props->max_reposition_mass) { + + /* No need to reposition */ + bp->reposition.min_potential = FLT_MAX; + bp->reposition.x[0] = -FLT_MAX; + bp->reposition.x[1] = -FLT_MAX; + bp->reposition.x[2] = -FLT_MAX; + } +} + /** * @brief Reset acceleration fields of a particle * @@ -469,6 +535,8 @@ INLINE static void black_holes_create_from_gas( /* We haven't accreted anything yet */ bp->total_accreted_mass = 0.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; /* Initial metal masses */ const float gas_mass = hydro_get_mass(p); @@ -494,6 +562,8 @@ INLINE static void black_holes_create_from_gas( /* First initialisation */ black_holes_init_bpart(bp); + + black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); } #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 index cba68a999433bd0eedc1050cbdf00e5e17a535d7..3e2238dc2a05f73dc9020b66f754e26a5976572a 100644 --- a/src/black_holes/EAGLE/black_holes_iact.h +++ b/src/black_holes/EAGLE/black_holes_iact.h @@ -20,6 +20,7 @@ #define SWIFT_EAGLE_BH_IACT_H /* Local includes */ +#include "gravity.h" #include "hydro.h" #include "random.h" #include "space.h" @@ -35,13 +36,15 @@ * @param pj Second particle (gas, not updated). * @param xpj The extended data of the second particle (not updated). * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). * @param ti_current Current integer time value (for random numbers). */ -__attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_density( +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_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) { + const struct gravity_props *grav_props, const integertime_t ti_current) { float wi, wi_dx; @@ -114,13 +117,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_density( * @param pj Second particle (gas) * @param xpj The extended data of the second particle. * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). * @param ti_current Current integer time value (for random numbers). */ -__attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_swallow( +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_swallow( 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) { + const struct gravity_props *grav_props, const integertime_t ti_current) { float wi; @@ -134,6 +139,44 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_swallow( const float ui = r * hi_inv; kernel_eval(ui, &wi); + /* Start by checking the repositioning criteria */ + + /* Note the factor 9 is taken from EAGLE. Will be turned into a parameter */ + const float max_dist_repos2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * 9.f * + grav_props->epsilon_baryon_cur * grav_props->epsilon_baryon_cur; + + /* This gas neighbour is close enough that we can consider it's potential + for repositioning */ + if (r2 < max_dist_repos2) { + + /* Compute relative peculiar velocity between the two BHs + * Recall that in SWIFT v is (v_pec * a) */ + const float delta_v[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1], + bi->v[2] - pj->v[2]}; + const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] + + delta_v[2] * delta_v[2]; + + const float v2_pec = v2 * cosmo->a2_inv; + + /* Check the velocity criterion */ + if (v2_pec < 0.25f * bi->sound_speed_gas * bi->sound_speed_gas) { + + const float potential = gravity_get_comoving_potential(pj->gpart); + + /* Is the potential lower? */ + if (potential < bi->reposition.min_potential) { + + /* Store this as our new best */ + bi->reposition.min_potential = potential; + bi->reposition.x[0] = pj->x[0]; + bi->reposition.x[1] = pj->x[1]; + bi->reposition.x[2] = pj->x[2]; + } + } + } + /* Is the BH hungry? */ if (bi->subgrid_mass > bi->mass) { @@ -169,6 +212,117 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_swallow( } } +/** + * @brief Swallowing interaction between two BH particles (non-symmetric). + * + * Function used to flag the BH particles that will be swallowed + * by the black hole particle. + * + * @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 bj Second particle (black hole) + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, + const float hi, const float hj, + struct bpart *restrict bi, + struct bpart *restrict bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const integertime_t ti_current) { + + /* Compute relative peculiar velocity between the two BHs + * Recall that in SWIFT v is (v_pec * a) */ + const float delta_v[3] = {bi->v[0] - bj->v[0], bi->v[1] - bj->v[1], + bi->v[2] - bj->v[2]}; + const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] + + delta_v[2] * delta_v[2]; + + const float v2_pec = v2 * cosmo->a2_inv; + + /* Note the factor 9 is taken from EAGLE. Will be turned into a parameter */ + const float max_dist_repos2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * 9.f * + grav_props->epsilon_baryon_cur * grav_props->epsilon_baryon_cur; + + /* This gas neighbour is close enough that we can consider it's potential + for repositioning */ + if (r2 < max_dist_repos2) { + + /* Check the velocity criterion */ + if (v2_pec < 0.25f * bi->sound_speed_gas * bi->sound_speed_gas) { + + const float potential = gravity_get_comoving_potential(bj->gpart); + + /* Is the potential lower? */ + if (potential < bi->reposition.min_potential) { + + /* Store this as our new best */ + bi->reposition.min_potential = potential; + bi->reposition.x[0] = bj->x[0]; + bi->reposition.x[1] = bj->x[1]; + bi->reposition.x[2] = bj->x[2]; + } + } + } + + /* Find the most massive of the two BHs */ + float M = bi->subgrid_mass; + float h = hi; + if (bj->subgrid_mass > M) { + M = bj->subgrid_mass; + h = hj; + } + + /* Note the factor 9 is taken from EAGLE. Will be turned into a parameter */ + const float max_dist_merge2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * 9.f * + grav_props->epsilon_baryon_cur * grav_props->epsilon_baryon_cur; + + const float G_Newton = grav_props->G_Newton; + + /* The BH with the smaller mass will be merged onto the one with the + * larger mass. + * To avoid rounding issues, we additionally check for IDs if the BHs + * have the exact same mass. */ + if ((bj->subgrid_mass < bi->subgrid_mass) || + (bj->subgrid_mass == bi->subgrid_mass && bj->id < bi->id)) { + + /* Merge if gravitationally bound AND if within max distance + * Note that we use the kernel support here as the size and not just the + * smoothing length */ + if (v2_pec < G_Newton * M / (kernel_gamma * h) && (r2 < max_dist_merge2)) { + + /* This particle is swallowed by the BH with the largest ID of all the + * candidates wanting to swallow it */ + if ((bj->merger_data.swallow_mass < bi->subgrid_mass) || + (bj->merger_data.swallow_mass == bi->subgrid_mass && + bj->merger_data.swallow_id < bi->id)) { + + message("BH %lld wants to swallow BH particle %lld", bi->id, bj->id); + + bj->merger_data.swallow_id = bi->id; + bj->merger_data.swallow_mass = bi->subgrid_mass; + + } else { + + message( + "BH %lld wants to swallow gas particle %lld BUT CANNOT (old " + "swallow id=%lld)", + bi->id, bj->id, bj->merger_data.swallow_id); + } + } + } +} + /** * @brief Feedback interaction between two particles (non-symmetric). * @@ -180,15 +334,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_swallow( * @param pj Second particle (gas) * @param xpj The extended data of the second particle. * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). * @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) { +runner_iact_nonsym_bh_gas_feedback( + const float r2, const float *dx, const float hi, const float hj, + const struct bpart *restrict bi, struct part *restrict pj, + struct xpart *restrict xpj, const struct cosmology *cosmo, + const struct gravity_props *grav_props, const integertime_t ti_current) { /* Get the heating probability */ const float prob = bi->to_distribute.AGN_heating_probability; diff --git a/src/black_holes/EAGLE/black_holes_io.h b/src/black_holes/EAGLE/black_holes_io.h index f5a8da344ffe0d37564710b9ce9bc3e0d8f3d705..cd6c4599f93b3a324ed2be16e6e3fb4ee06ec02c 100644 --- a/src/black_holes/EAGLE/black_holes_io.h +++ b/src/black_holes/EAGLE/black_holes_io.h @@ -22,20 +22,6 @@ #include "black_holes_part.h" #include "io_properties.h" -INLINE static void convert_bpart_pos(const struct engine *e, - const struct bpart *bp, double *ret) { - - if (e->s->periodic) { - ret[0] = box_wrap(bp->x[0], 0.0, e->s->dim[0]); - ret[1] = box_wrap(bp->x[1], 0.0, e->s->dim[1]); - ret[2] = box_wrap(bp->x[2], 0.0, e->s->dim[2]); - } else { - ret[0] = bp->x[0]; - ret[1] = bp->x[1]; - ret[2] = bp->x[2]; - } -} - /** * @brief Specifies which b-particle fields to read from a dataset * @@ -43,9 +29,9 @@ INLINE static void convert_bpart_pos(const struct engine *e, * @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) { +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; @@ -65,47 +51,132 @@ INLINE static void black_holes_read_particles(struct bpart *bparts, UNIT_CONV_ENERGY, bparts, energy_reservoir); } +INLINE static void convert_bpart_pos(const struct engine* e, + const struct bpart* bp, double* ret) { + + if (e->s->periodic) { + ret[0] = box_wrap(bp->x[0], 0.0, e->s->dim[0]); + ret[1] = box_wrap(bp->x[1], 0.0, e->s->dim[1]); + ret[2] = box_wrap(bp->x[2], 0.0, e->s->dim[2]); + } else { + ret[0] = bp->x[0]; + ret[1] = bp->x[1]; + ret[2] = bp->x[2]; + } +} + +INLINE static void convert_bpart_vel(const struct engine* e, + const struct bpart* bp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, bp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, bp->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + const struct gpart* gp = bp->gpart; + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + /** * @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. + * @param with_cosmology Are we running a cosmological simulation? */ -INLINE static void black_holes_write_particles(const struct bpart *bparts, - struct io_props *list, - int *num_fields) { +INLINE static void black_holes_write_particles(const struct bpart* bparts, + struct io_props* list, + int* num_fields, + int with_cosmology) { /* Say how much we want to write */ *num_fields = 12; /* List what we want to write */ list[0] = io_make_output_field_convert_bpart( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, bparts, convert_bpart_pos); - list[1] = - io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, bparts, v); + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, bparts, + convert_bpart_pos, "Co-moving position of the particles"); + + list[1] = io_make_output_field_convert_bpart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, convert_bpart_vel, + "Peculiar velocities of the particles. This is a * dx/dt where x is the " + "co-moving position of the particles."); + list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, bparts, mass); + io_make_output_field("DynamicalMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, mass, "Dynamical masses of the particles"); + 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); + 0.f, bparts, id, "Unique ID of the particles"); + + list[4] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, bparts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[5] = io_make_output_field("SubgridMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, subgrid_mass, + "Subgrid masses of the particles"); + + if (with_cosmology) { + list[6] = io_make_output_field( + "FormationScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + formation_scale_factor, "Scale-factors at which the BHs were formed"); + } else { + list[6] = io_make_output_field("FormationTimes", FLOAT, 1, UNIT_CONV_TIME, + 0.f, bparts, formation_time, + "Times at which the BHs were formed"); + } + + list[7] = io_make_output_field( + "GasDensities", FLOAT, 1, UNIT_CONV_DENSITY, 0.f, bparts, rho_gas, + "Co-moving densities of the gas around the particles"); + + list[8] = io_make_output_field( + "GasSoundSpeeds", FLOAT, 1, UNIT_CONV_SPEED, 1.5f * hydro_gamma_minus_one, + bparts, sound_speed_gas, + "Co-moving sound-speeds of the gas around the particles"); + + list[9] = io_make_output_field( + "EnergyReservoirs", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, + energy_reservoir, + "Physcial energy contained in the feedback reservoir of the particles"); + + list[10] = io_make_output_field( + "AccretionRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, bparts, + accretion_rate, + "Physical instantaneous accretion rates of the particles"); + + list[11] = io_make_output_field( + "TotalAccretedMasses", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, + bparts, total_accreted_mass, + "Total mass accreted onto the particles since its birth"); + + list[12] = io_make_output_field( + "CumulativeNumberSeeds", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + cumulative_number_seeds, + "Total number of BH seeds that have merged into this black hole"); #ifdef DEBUG_INTERACTIONS_BLACK_HOLES diff --git a/src/black_holes/EAGLE/black_holes_part.h b/src/black_holes/EAGLE/black_holes_part.h index 598edffecae228a47f57224be9727ee50ccc4074..4d8a9c8e177cc64d7e3191c046ff95c374b2d4a4 100644 --- a/src/black_holes/EAGLE/black_holes_part.h +++ b/src/black_holes/EAGLE/black_holes_part.h @@ -19,7 +19,9 @@ #ifndef SWIFT_EAGLE_BLACK_HOLE_PART_H #define SWIFT_EAGLE_BLACK_HOLE_PART_H +#include "black_holes_struct.h" #include "chemistry_struct.h" +#include "timeline.h" /** * @brief Particle fields for the black hole particles. @@ -103,6 +105,12 @@ struct bpart { /*! Integer number of neighbours */ int num_ngbs; + /*! Number of seeds in this BH (i.e. itself + the merged ones) */ + int cumulative_number_seeds; + + /*! Total number of BH merger events (i.e. not including all progenies) */ + int number_of_mergers; + /*! Properties used in the feedback loop to distribute to gas neighbours. */ struct { @@ -114,10 +122,23 @@ struct bpart { } to_distribute; + struct { + + /*! Value of the minimum potential across all neighbours. */ + float min_potential; + + /*! New position of the BH following the reposition procedure */ + double x[3]; + + } reposition; + /*! Chemistry information (e.g. metal content at birth, swallowed metal * content, etc.) */ struct chemistry_bpart_data chemistry_data; + /*! Black holes merger information (e.g. merging ID) */ + struct black_holes_bpart_data merger_data; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/black_holes/EAGLE/black_holes_properties.h b/src/black_holes/EAGLE/black_holes_properties.h index 50981b43e1ec23927d9958c95ec396fe4a4e4d3a..db50e5b3e08b1a66497aaf75da9d3acdea3287ee 100644 --- a/src/black_holes/EAGLE/black_holes_properties.h +++ b/src/black_holes/EAGLE/black_holes_properties.h @@ -74,6 +74,11 @@ struct black_holes_props { /*! Number of gas neighbours to heat in a feedback event */ float num_ngbs_to_heat; + /* ---- Properties of the repositioning model --- */ + + /*! Maximal mass of BH to reposition */ + float max_reposition_mass; + /* ---- Common conversion factors --------------- */ /*! Conversion factor from temperature to internal energy */ @@ -157,6 +162,14 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->num_ngbs_to_heat = parser_get_param_float(params, "EAGLEAGN:AGN_num_ngb_to_heat"); + /* Reposition parameters --------------------------------- */ + + bp->max_reposition_mass = + parser_get_param_float(params, "EAGLEAGN:max_reposition_mass"); + + /* Convert to internal units */ + bp->max_reposition_mass *= phys_const->const_solar_mass; + /* Common conversion factors ----------------------------- */ /* Calculate temperature to internal energy conversion factor (all internal diff --git a/src/black_holes/EAGLE/black_holes_struct.h b/src/black_holes/EAGLE/black_holes_struct.h index cf0e9c3975c1a1d9bda0065a5072465ae886297f..177a54b921bec24f357756e4f777b0ecf1781c30 100644 --- a/src/black_holes/EAGLE/black_holes_struct.h +++ b/src/black_holes/EAGLE/black_holes_struct.h @@ -19,6 +19,8 @@ #ifndef SWIFT_BLACK_HOLES_STRUCT_EAGLE_H #define SWIFT_BLACK_HOLES_STRUCT_EAGLE_H +#include "inline.h" + /** * @brief Black holes-related fields carried by each *gas* particle. */ @@ -28,4 +30,88 @@ struct black_holes_part_data { long long swallow_id; }; +/** + * @brief Black holes-related fields carried by each *BH* particle. + */ +struct black_holes_bpart_data { + + /*! ID of the black-hole that will swallow this #bpart. */ + long long swallow_id; + + /*! Mass of the black-hole that will swallow this #bpart. */ + float swallow_mass; +}; + +/** + * @brief Update a given #part's BH data field to mark the particle has + * not yet been swallowed. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_part_as_not_swallowed(struct black_holes_part_data* p_data) { + + p_data->swallow_id = -1; +} + +/** + * @brief Update a given #part's BH data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_part_as_swallowed(struct black_holes_part_data* p_data) { + + p_data->swallow_id = -2; +} + +/** + * @brief Return the ID of the BH that should swallow this #part. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static long long +black_holes_get_part_swallow_id(struct black_holes_part_data* p_data) { + + return p_data->swallow_id; +} + +/** + * @brief Update a given #bpart's BH data field to mark the particle has + * not yet been swallowed. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_bpart_as_not_swallowed(struct black_holes_bpart_data* p_data) { + + p_data->swallow_id = -1; + p_data->swallow_mass = 0.f; +} + +/** + * @brief Update a given #bpart's BH data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_bpart_as_merged(struct black_holes_bpart_data* p_data) { + + p_data->swallow_id = -2; + p_data->swallow_mass = -1.f; +} + +/** + * @brief Return the ID of the BH that should swallow this #bpart. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static long long +black_holes_get_bpart_swallow_id(struct black_holes_bpart_data* p_data) { + + return p_data->swallow_id; +} + #endif /* SWIFT_BLACK_HOLES_STRUCT_EAGLE_H */ diff --git a/src/cell.c b/src/cell.c index 2913b9c3b9479bb7fb40e614c36d1acc6dd79352..92f53d7ca8499457248baa6b1bd0fb86380e4159 100644 --- a/src/cell.c +++ b/src/cell.c @@ -61,6 +61,7 @@ #include "hydro_properties.h" #include "memswap.h" #include "minmax.h" +#include "pressure_floor.h" #include "scheduler.h" #include "space.h" #include "space_getsid.h" @@ -70,6 +71,8 @@ #include "tools.h" #include "tracers.h" +extern int engine_star_resort_task_depth; + /* Global variables. */ int cell_next_tag = 0; @@ -612,6 +615,28 @@ void cell_unpack_part_swallow(struct cell *c, } } +void cell_pack_bpart_swallow(const struct cell *c, + struct black_holes_bpart_data *data) { + + const size_t count = c->black_holes.count; + const struct bpart *bparts = c->black_holes.parts; + + for (size_t i = 0; i < count; ++i) { + data[i] = bparts[i].merger_data; + } +} + +void cell_unpack_bpart_swallow(struct cell *c, + const struct black_holes_bpart_data *data) { + + const size_t count = c->black_holes.count; + struct bpart *bparts = c->black_holes.parts; + + for (size_t i = 0; i < count; ++i) { + bparts[i].merger_data = data[i]; + } +} + /** * @brief Unpack the data of a given cell and its sub-cells. * @@ -1988,7 +2013,8 @@ void cell_clean_links(struct cell *c, void *data) { c->stars.feedback = NULL; c->black_holes.density = NULL; c->black_holes.swallow = NULL; - c->black_holes.do_swallow = NULL; + c->black_holes.do_gas_swallow = NULL; + c->black_holes.do_bh_swallow = NULL; c->black_holes.feedback = NULL; } @@ -2152,8 +2178,11 @@ void cell_reset_task_counters(struct cell *c) { * * @param c The #cell. * @param ti_current The current integer time. + * @param grav_props The properties of the gravity scheme. */ -void cell_make_multipoles(struct cell *c, integertime_t ti_current) { +void cell_make_multipoles(struct cell *c, integertime_t ti_current, + const struct gravity_props *const grav_props) { + /* Reset everything */ gravity_reset(c->grav.multipole); @@ -2161,7 +2190,7 @@ void cell_make_multipoles(struct cell *c, integertime_t ti_current) { /* Start by recursing */ for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) - cell_make_multipoles(c->progeny[k], ti_current); + cell_make_multipoles(c->progeny[k], ti_current, grav_props); } /* Compute CoM of all progenies */ @@ -2222,7 +2251,7 @@ void cell_make_multipoles(struct cell *c, integertime_t ti_current) { } else { if (c->grav.count > 0) { - gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count); + gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count, grav_props); const double dx = c->grav.multipole->CoM[0] > c->loc[0] + c->width[0] * 0.5 ? c->grav.multipole->CoM[0] - c->loc[0] @@ -2298,8 +2327,11 @@ void cell_check_foreign_multipole(const struct cell *c) { * recursively computed one. * * @param c Cell to act upon + * @param grav_props The properties of the gravity scheme. */ -void cell_check_multipole(struct cell *c) { +void cell_check_multipole(struct cell *c, + const struct gravity_props *const grav_props) { + #ifdef SWIFT_DEBUG_CHECKS struct gravity_tensors ma; const double tolerance = 1e-3; /* Relative */ @@ -2307,11 +2339,12 @@ void cell_check_multipole(struct cell *c) { /* First recurse */ if (c->split) for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) cell_check_multipole(c->progeny[k]); + if (c->progeny[k] != NULL) + cell_check_multipole(c->progeny[k], grav_props); if (c->grav.count > 0) { /* Brute-force calculation */ - gravity_P2M(&ma, c->grav.parts, c->grav.count); + gravity_P2M(&ma, c->grav.parts, c->grav.count, grav_props); /* Now compare the multipole expansion */ if (!gravity_multipole_equal(&ma, c->grav.multipole, tolerance)) { @@ -2374,6 +2407,122 @@ void cell_clear_limiter_flags(struct cell *c, void *data) { cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); } +/** + * @brief Recursively clear the stars_resort flag in a cell hierarchy. + * + * @param c The #cell to act on. + */ +void cell_set_star_resort_flag(struct cell *c) { + + cell_set_flag(c, cell_flag_do_stars_resort); + + /* Abort if we reched the level where the resorting task lives */ + if (c->depth == engine_star_resort_task_depth || c->hydro.super == c) return; + + if (c->split) { + for (int k = 0; k < 8; ++k) + if (c->progeny[k] != NULL) cell_set_star_resort_flag(c->progeny[k]); + } +} + +/** + * @brief Recurses in a cell hierarchy down to the level where the + * star resort tasks are and activates them. + * + * The function will fail if called *below* the super-level + * + * @param c The #cell to recurse into. + * @param s The #scheduler. + */ +void cell_activate_star_resort_tasks(struct cell *c, struct scheduler *s) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super != NULL && c->hydro.super != c) + error("Function called below the super level!"); +#endif + + /* The resort tasks are at either the chosen depth or the super level, + * whichever comes first. */ + if ((c->depth == engine_star_resort_task_depth || c->hydro.super == c) && + c->hydro.count > 0) { + scheduler_activate(s, c->hydro.stars_resort); + } else { + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) { + cell_activate_star_resort_tasks(c->progeny[k], s); + } + } + } +} + +/** + * @brief Activate the star formation task as well as the resorting of stars + * + * Must be called at the top-level in the tree (where the SF task is...) + * + * @param c The (top-level) #cell. + * @param s The #scheduler. + */ +void cell_activate_star_formation_tasks(struct cell *c, struct scheduler *s) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->depth != 0) error("Function should be called at the top-level only"); +#endif + + /* Have we already unskipped that task? */ + if (c->hydro.star_formation->skip == 0) return; + + /* Activate the star formation task */ + scheduler_activate(s, c->hydro.star_formation); + + /* Activate the star resort tasks at whatever level they are */ + cell_activate_star_resort_tasks(c, s); +} + +/** + * @brief Recursively activate the hydro ghosts (and implicit links) in a cell + * hierarchy. + * + * @param c The #cell. + * @param s The #scheduler. + * @param e The #engine. + */ +void cell_recursively_activate_hydro_ghosts(struct cell *c, struct scheduler *s, + const struct engine *e) { + /* Early abort? */ + if ((c->hydro.count == 0) || !cell_is_active_hydro(c, e)) return; + + /* Is the ghost at this level? */ + if (c->hydro.ghost != NULL) { + scheduler_activate(s, c->hydro.ghost); + } else { + +#ifdef SWIFT_DEBUG_CHECKS + if (!c->split) + error("Reached the leaf level without finding a hydro ghost!"); +#endif + + /* Keep recursing */ + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) + cell_recursively_activate_hydro_ghosts(c->progeny[k], s, e); + } +} + +/** + * @brief Activate the hydro ghosts (and implicit links) in a cell hierarchy. + * + * @param c The #cell. + * @param s The #scheduler. + * @param e The #engine. + */ +void cell_activate_hydro_ghosts(struct cell *c, struct scheduler *s, + const struct engine *e) { + scheduler_activate(s, c->hydro.ghost_in); + scheduler_activate(s, c->hydro.ghost_out); + cell_recursively_activate_hydro_ghosts(c, s, e); +} + /** * @brief Recurse down in a cell hierarchy until the hydro.super level is * reached and activate the spart drift at that level. @@ -2382,6 +2531,10 @@ void cell_clear_limiter_flags(struct cell *c, void *data) { * @param s The #scheduler. */ void cell_activate_super_spart_drifts(struct cell *c, struct scheduler *s) { + + /* Early abort? */ + if (c->hydro.count == 0) return; + if (c == c->hydro.super) { cell_activate_drift_spart(c, s); } else { @@ -3396,20 +3549,18 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { if (c->hydro.extra_ghost != NULL) scheduler_activate(s, c->hydro.extra_ghost); - if (c->hydro.ghost_in != NULL) scheduler_activate(s, c->hydro.ghost_in); - if (c->hydro.ghost_out != NULL) scheduler_activate(s, c->hydro.ghost_out); - if (c->hydro.ghost != NULL) scheduler_activate(s, c->hydro.ghost); + if (c->hydro.ghost_in != NULL) cell_activate_hydro_ghosts(c, s, e); 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->hydro.end_force != NULL) scheduler_activate(s, c->hydro.end_force); if (c->hydro.cooling != NULL) scheduler_activate(s, c->hydro.cooling); +#ifdef WITH_LOGGER if (c->logger != NULL) scheduler_activate(s, c->logger); +#endif if (c->top->hydro.star_formation != NULL) { - scheduler_activate(s, c->top->hydro.star_formation); - scheduler_activate(s, c->top->hydro.stars_resort); - cell_activate_drift_spart(c, s); + cell_activate_star_formation_tasks(c->top, s); } } @@ -3561,7 +3712,9 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) { if (c->grav.mesh != NULL) scheduler_activate(s, c->grav.mesh); if (c->grav.long_range != NULL) scheduler_activate(s, c->grav.long_range); if (c->grav.end_force != NULL) scheduler_activate(s, c->grav.end_force); +#ifdef WITH_LOGGER if (c->logger != NULL) scheduler_activate(s, c->logger); +#endif } return rebuild; @@ -3803,7 +3956,9 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s, 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); +#ifdef WITH_LOGGER if (c->logger != NULL) scheduler_activate(s, c->logger); +#endif } } @@ -3995,7 +4150,30 @@ int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s) { } /* Un-skip the swallow tasks involved with this cell. */ - for (struct link *l = c->black_holes.do_swallow; l != NULL; l = l->next) { + for (struct link *l = c->black_holes.do_gas_swallow; 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 + + /* Only activate tasks that involve a local active cell. */ + if ((ci_active || cj_active) && + (ci_nodeID == nodeID || cj_nodeID == nodeID)) { + + scheduler_activate(s, t); + } + } + + /* Un-skip the swallow tasks involved with this cell. */ + for (struct link *l = c->black_holes.do_bh_swallow; l != NULL; l = l->next) { struct task *t = l->t; struct cell *ci = t->ci; struct cell *cj = t->cj; @@ -4043,23 +4221,14 @@ int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s) { /* Unskip all the other task types. */ if (c->nodeID == nodeID && cell_is_active_black_holes(c, e)) { - /* for (struct link *l = c->black_holes.swallow; l != NULL; l = l->next) - */ - /* scheduler_activate(s, l->t); */ - /* for (struct link *l = c->black_holes.do_swallow; l != NULL; l = - * l->next) - */ - /* scheduler_activate(s, l->t); */ - /* for (struct link *l = c->black_holes.feedback; l != NULL; l = l->next) - */ - /* scheduler_activate(s, l->t); */ - if (c->black_holes.density_ghost != NULL) scheduler_activate(s, c->black_holes.density_ghost); if (c->black_holes.swallow_ghost[0] != NULL) scheduler_activate(s, c->black_holes.swallow_ghost[0]); if (c->black_holes.swallow_ghost[1] != NULL) scheduler_activate(s, c->black_holes.swallow_ghost[1]); + if (c->black_holes.swallow_ghost[2] != NULL) + scheduler_activate(s, c->black_holes.swallow_ghost[2]); 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) @@ -4067,7 +4236,9 @@ int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s) { 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); +#ifdef WITH_LOGGER if (c->logger != NULL) scheduler_activate(s, c->logger); +#endif } return rebuild; @@ -4342,13 +4513,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { hydro_remove_part(p, xp); /* Remove the particle entirely */ - struct gpart *gp = p->gpart; cell_remove_part(e, c, p, xp); - - /* and it's gravity friend */ - if (gp != NULL) { - cell_remove_gpart(e, c, gp); - } } if (lock_unlock(&e->s->lock) != 0) @@ -4376,13 +4541,13 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { cell_h_max = max(cell_h_max, p->h); /* Mark the particle has not being swallowed */ - black_holes_mark_as_not_swallowed(&p->black_holes_data); + black_holes_mark_part_as_not_swallowed(&p->black_holes_data); /* Get ready for a density calculation */ if (part_is_active(p, e)) { hydro_init_part(p, &e->s->hs); chemistry_init_part(p, e->chemistry); - star_formation_init_part(p, e->star_formation); + pressure_floor_init_part(p, xp); tracers_after_init(p, xp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, e->hydro_properties, e->cooling_func, e->time); @@ -4510,7 +4675,9 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { if (!gpart_is_inhibited(gp, e)) { /* Remove the particle entirely */ - cell_remove_gpart(e, c, gp); + if (gp->type == swift_type_dark_matter) { + cell_remove_gpart(e, c, gp); + } } if (lock_unlock(&e->s->lock) != 0) @@ -4651,11 +4818,7 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { if (!spart_is_inhibited(sp, e)) { /* Remove the particle entirely */ - struct gpart *gp = sp->gpart; cell_remove_spart(e, c, sp); - - /* and it's gravity friend */ - cell_remove_gpart(e, c, gp); } if (lock_unlock(&e->s->lock) != 0) @@ -4826,11 +4989,7 @@ void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { if (!bpart_is_inhibited(bp, e)) { /* Remove the particle entirely */ - struct gpart *gp = bp->gpart; cell_remove_bpart(e, c, bp); - - /* and it's gravity friend */ - cell_remove_gpart(e, c, gp); } if (lock_unlock(&e->s->lock) != 0) @@ -4853,6 +5012,9 @@ void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { /* Maximal smoothing length */ cell_h_max = max(cell_h_max, bp->h); + /* Mark the particle has not being swallowed */ + black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); + /* Get ready for a density calculation */ if (bpart_is_active(bp, e)) { black_holes_init_bpart(bp); @@ -5134,10 +5296,17 @@ void cell_recursively_shift_sparts(struct cell *c, /* When directly above the leaf with the new particle: increase the particle * count */ /* When after the leaf with the new particle: shift by one position */ - if (main_branch) + if (main_branch) { c->stars.count++; - else + + /* Indicate that the cell is not sorted and cancel the pointer sorting + * arrays. */ + c->stars.sorted = 0; + cell_free_stars_sorts(c); + + } else { c->stars.parts++; + } } /** @@ -5287,6 +5456,9 @@ void cell_remove_part(const struct engine *e, struct cell *c, struct part *p, if (c->nodeID != e->nodeID) error("Can't remove a particle in a foreign cell."); + /* Don't remove a particle twice */ + if (p->time_bin == time_bin_inhibited) return; + /* Mark the particle as inhibited */ p->time_bin = time_bin_inhibited; @@ -5297,12 +5469,15 @@ void cell_remove_part(const struct engine *e, struct cell *c, struct part *p, p->gpart->type = swift_type_dark_matter; } - /* Un-link the part */ - p->gpart = NULL; - /* Update the space-wide counters */ const size_t one = 1; atomic_add(&e->s->nr_inhibited_parts, one); + if (p->gpart) { + atomic_add(&e->s->nr_inhibited_gparts, one); + } + + /* Un-link the part */ + p->gpart = NULL; } /** @@ -5317,10 +5492,21 @@ void cell_remove_part(const struct engine *e, struct cell *c, struct part *p, */ void cell_remove_gpart(const struct engine *e, struct cell *c, struct gpart *gp) { + + /* Quick cross-check */ + if (c->nodeID != e->nodeID) + error("Can't remove a particle in a foreign cell."); + + /* Don't remove a particle twice */ + if (gp->time_bin == time_bin_inhibited) return; + /* Quick cross-check */ if (c->nodeID != e->nodeID) error("Can't remove a particle in a foreign cell."); + if (gp->type == swift_type_dark_matter_background) + error("Can't remove a DM background particle!"); + /* Mark the particle as inhibited */ gp->time_bin = time_bin_inhibited; @@ -5345,6 +5531,9 @@ void cell_remove_spart(const struct engine *e, struct cell *c, if (c->nodeID != e->nodeID) error("Can't remove a particle in a foreign cell."); + /* Don't remove a particle twice */ + if (sp->time_bin == time_bin_inhibited) return; + /* Mark the particle as inhibited and stand-alone */ sp->time_bin = time_bin_inhibited; if (sp->gpart) { @@ -5353,12 +5542,15 @@ void cell_remove_spart(const struct engine *e, struct cell *c, sp->gpart->type = swift_type_dark_matter; } - /* Un-link the spart */ - sp->gpart = NULL; - /* Update the space-wide counters */ const size_t one = 1; atomic_add(&e->s->nr_inhibited_sparts, one); + if (sp->gpart) { + atomic_add(&e->s->nr_inhibited_gparts, one); + } + + /* Un-link the spart */ + sp->gpart = NULL; } /** @@ -5378,6 +5570,9 @@ void cell_remove_bpart(const struct engine *e, struct cell *c, if (c->nodeID != e->nodeID) error("Can't remove a particle in a foreign cell."); + /* Don't remove a particle twice */ + if (bp->time_bin == time_bin_inhibited) return; + /* Mark the particle as inhibited and stand-alone */ bp->time_bin = time_bin_inhibited; if (bp->gpart) { @@ -5386,12 +5581,15 @@ void cell_remove_bpart(const struct engine *e, struct cell *c, bp->gpart->type = swift_type_dark_matter; } - /* Un-link the bpart */ - bp->gpart = NULL; - /* Update the space-wide counters */ const size_t one = 1; atomic_add(&e->s->nr_inhibited_bparts, one); + if (bp->gpart) { + atomic_add(&e->s->nr_inhibited_gparts, one); + } + + /* Un-link the bpart */ + bp->gpart = NULL; } /** @@ -5759,7 +5957,11 @@ int cell_can_use_pair_mm(const struct cell *ci, const struct cell *cj, } const double r2 = dx * dx + dy * dy + dz * dz; - return gravity_M2L_accept(multi_i->r_max, multi_j->r_max, theta_crit2, r2); + const double epsilon_i = multi_i->m_pole.max_softening; + const double epsilon_j = multi_j->m_pole.max_softening; + + return gravity_M2L_accept(multi_i->r_max, multi_j->r_max, theta_crit2, r2, + epsilon_i, epsilon_j); } /** @@ -5821,6 +6023,9 @@ int cell_can_use_pair_mm_rebuild(const struct cell *ci, const struct cell *cj, } const double r2 = dx * dx + dy * dy + dz * dz; + const double epsilon_i = multi_i->m_pole.max_softening; + const double epsilon_j = multi_j->m_pole.max_softening; + return gravity_M2L_accept(multi_i->r_max_rebuild, multi_j->r_max_rebuild, - theta_crit2, r2); + theta_crit2, r2, epsilon_i, epsilon_j); } diff --git a/src/cell.h b/src/cell.h index b58f41fd710251c4caa7ef0536fbcf2af65d7932..10a3e2bddfebd907b8efaffa472f94c421e4966c 100644 --- a/src/cell.h +++ b/src/cell.h @@ -273,8 +273,10 @@ struct pcell_sf { } stars; }; -/** Bitmasks for the cell flags. Beware when adding flags that you don't exceed - the size of the flags variable in the struct cell. */ +/** + * @brief Bitmasks for the cell flags. Beware when adding flags that you don't + * exceed the size of the flags variable in the struct cell. + */ enum cell_flags { cell_flag_split = (1UL << 0), cell_flag_do_hydro_drift = (1UL << 1), @@ -289,7 +291,8 @@ enum cell_flags { cell_flag_do_stars_sub_drift = (1UL << 10), cell_flag_do_bh_drift = (1UL << 11), cell_flag_do_bh_sub_drift = (1UL << 12), - cell_flag_do_stars_resort = (1UL << 13) + cell_flag_do_stars_resort = (1UL << 13), + cell_flag_has_tasks = (1UL << 14), }; /** @@ -655,7 +658,7 @@ struct cell { struct task *density_ghost; /*! The star ghost task itself */ - struct task *swallow_ghost[2]; + struct task *swallow_ghost[3]; /*! Linked list of the tasks computing this cell's BH density. */ struct link *density; @@ -665,7 +668,10 @@ struct cell { struct link *swallow; /*! Linked list of the tasks processing the particles to swallow */ - struct link *do_swallow; + struct link *do_gas_swallow; + + /*! Linked list of the tasks processing the particles to swallow */ + struct link *do_bh_swallow; /*! Linked list of the tasks computing this cell's BH feedback. */ struct link *feedback; @@ -752,8 +758,10 @@ struct cell { /*! The task to limit the time-step of inactive particles */ struct task *timestep_limiter; +#ifdef WITH_LOGGER /*! The logger task */ struct task *logger; +#endif /*! Minimum dimension, i.e. smallest edge of this cell (min(width)). */ float dmin; @@ -819,6 +827,10 @@ void cell_pack_part_swallow(const struct cell *c, struct black_holes_part_data *data); void cell_unpack_part_swallow(struct cell *c, const struct black_holes_part_data *data); +void cell_pack_bpart_swallow(const struct cell *c, + struct black_holes_bpart_data *data); +void cell_unpack_bpart_swallow(struct cell *c, + const struct black_holes_bpart_data *data); int cell_pack_tags(const struct cell *c, int *tags); int cell_unpack_tags(const int *tags, struct cell *c); int cell_pack_end_step_hydro(struct cell *c, struct pcell_step_hydro *pcell); @@ -845,8 +857,10 @@ int cell_link_foreign_gparts(struct cell *c, struct gpart *gparts); int cell_count_parts_for_tasks(const struct cell *c); int cell_count_gparts_for_tasks(const struct cell *c); void cell_clean_links(struct cell *c, void *data); -void cell_make_multipoles(struct cell *c, integertime_t ti_current); -void cell_check_multipole(struct cell *c); +void cell_make_multipoles(struct cell *c, integertime_t ti_current, + const struct gravity_props *const grav_props); +void cell_check_multipole(struct cell *c, + const struct gravity_props *const grav_props); void cell_check_foreign_multipole(const struct cell *c); void cell_clean(struct cell *c); void cell_check_part_drift_point(struct cell *c, void *data); @@ -867,6 +881,8 @@ void cell_drift_multipole(struct cell *c, const struct engine *e); void cell_drift_all_multipoles(struct cell *c, const struct engine *e); void cell_check_timesteps(struct cell *c); void cell_store_pre_drift_values(struct cell *c); +void cell_set_star_resort_flag(struct cell *c); +void cell_activate_star_formation_tasks(struct cell *c, struct scheduler *s); void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj, struct scheduler *s); void cell_activate_subcell_grav_tasks(struct cell *ci, struct cell *cj, diff --git a/src/chemistry/EAGLE/chemistry.h b/src/chemistry/EAGLE/chemistry.h index a04d7f94e7340a68d25914828de40bb2e1e8020c..e53050884a443de6dbcd79981fcd60cdcaff8c40 100644 --- a/src/chemistry/EAGLE/chemistry.h +++ b/src/chemistry/EAGLE/chemistry.h @@ -170,9 +170,10 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( p->chemistry_data.metal_mass_fraction_total = data->initial_metal_mass_fraction_total; - for (int elem = 0; elem < chemistry_element_count; ++elem) + for (int elem = 0; elem < chemistry_element_count; ++elem) { p->chemistry_data.metal_mass_fraction[elem] = data->initial_metal_mass_fraction[elem]; + } } chemistry_init_part(p, data); } @@ -196,6 +197,16 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_spart( sp->chemistry_data.metal_mass_fraction[elem] = data->initial_metal_mass_fraction[elem]; } + + /* Initialize mass fractions for total metals and each metal individually */ + if (data->initial_metal_mass_fraction_total != -1) { + sp->chemistry_data.smoothed_metal_mass_fraction_total = + data->initial_metal_mass_fraction_total; + + for (int elem = 0; elem < chemistry_element_count; ++elem) + sp->chemistry_data.smoothed_metal_mass_fraction[elem] = + data->initial_metal_mass_fraction[elem]; + } } /** @@ -225,6 +236,41 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, data->initial_metal_mass_fraction[elem] = parser_get_param_float(parameter_file, buffer); } + + /* Let's check that things make sense (broadly) */ + + /* H + He + Z should be ~1 */ + float total_frac = data->initial_metal_mass_fraction[chemistry_element_H] + + data->initial_metal_mass_fraction[chemistry_element_He] + + data->initial_metal_mass_fraction_total; + + if (total_frac < 0.98 || total_frac > 1.02) + error("The abundances provided seem odd! H + He + Z = %f =/= 1.", + total_frac); + + /* Sum of metal elements should be <= Z */ + total_frac = 0.f; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + if (elem != chemistry_element_H && elem != chemistry_element_He) { + total_frac += data->initial_metal_mass_fraction[elem]; + } + } + + if (total_frac > 1.02 * data->initial_metal_mass_fraction_total) + error( + "The abundances provided seem odd! \\sum metal elements (%f) > Z " + "(%f)", + total_frac, data->initial_metal_mass_fraction_total); + + /* Sum of all elements should be <= 1 */ + total_frac = 0.f; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + total_frac += data->initial_metal_mass_fraction[elem]; + } + + if (total_frac > 1.02) + error("The abundances provided seem odd! \\sum elements (%f) > 1", + total_frac); } } @@ -241,4 +287,125 @@ static INLINE void chemistry_print_backend( chemistry_element_count); } +/** + * @brief Updates to the chemistry data after the hydro force loop. + * + * Nothing to do here in EAGLE. + * + * @param p The particle to act upon. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_force( + struct part* restrict p, const struct cosmology* cosmo) {} + +/** + * @brief Computes the chemistry-related time-step constraint. + * + * No constraints in the EAGLE model (no diffusion etc.) --> FLT_MAX + * + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param us The internal system of units. + * @param hydro_props The properties of the hydro scheme. + * @param cd The global properties of the chemistry scheme. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float chemistry_timestep( + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct chemistry_global_data* cd, const struct part* restrict p) { + return FLT_MAX; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * star particle to be used in feedback/enrichment related routines. + * + * EAGLE uses smooth abundances for everything. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return sp->chemistry_data.smoothed_metal_mass_fraction_total; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * star particle to be used in feedback/enrichment related routines. + * + * EAGLE uses smooth abundances for everything. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return sp->chemistry_data.smoothed_metal_mass_fraction; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in cooling related routines. + * + * EAGLE uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_cooling( + const struct part* restrict p) { + + return p->chemistry_data.smoothed_metal_mass_fraction_total; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in cooling related routines. + * + * EAGLE uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_cooling(const struct part* restrict p) { + + return p->chemistry_data.smoothed_metal_mass_fraction; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in star formation related routines. + * + * EAGLE uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return p->chemistry_data.smoothed_metal_mass_fraction_total; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in star formation related routines. + * + * EAGLE uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return p->chemistry_data.smoothed_metal_mass_fraction; +} + #endif /* SWIFT_CHEMISTRY_EAGLE_H */ diff --git a/src/chemistry/EAGLE/chemistry_io.h b/src/chemistry/EAGLE/chemistry_io.h index 023791133d6fee78497f27ad741bcfcd1eeec5ce..5e671e4c74dafc515812bdbf7516b03c6bcff08e 100644 --- a/src/chemistry/EAGLE/chemistry_io.h +++ b/src/chemistry/EAGLE/chemistry_io.h @@ -58,50 +58,72 @@ INLINE static int chemistry_write_particles(const struct part* parts, struct io_props* list) { /* List what we want to write */ - list[0] = io_make_output_field("ElementAbundance", FLOAT, - chemistry_element_count, UNIT_CONV_NO_UNITS, - parts, chemistry_data.metal_mass_fraction); + list[0] = io_make_output_field( + "ElementMassFractions", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, parts, chemistry_data.metal_mass_fraction, + "Fractions of the particles' masses that are in the given element"); list[1] = io_make_output_field( - "SmoothedElementAbundance", FLOAT, chemistry_element_count, - UNIT_CONV_NO_UNITS, parts, chemistry_data.smoothed_metal_mass_fraction); + "SmoothedElementMassFractions", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.smoothed_metal_mass_fraction, + "Smoothed fractions of the particles' masses that are " + "in the given element"); - list[2] = - io_make_output_field("Metallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, parts, - chemistry_data.metal_mass_fraction_total); + list[2] = io_make_output_field( + "MetalMassFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.metal_mass_fraction_total, + "Fractions of the particles' masses that are in metals"); list[3] = io_make_output_field( - "SmoothedMetallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, parts, - chemistry_data.smoothed_metal_mass_fraction_total); - - list[4] = io_make_output_field("TotalMassFromSNIa", FLOAT, 1, UNIT_CONV_MASS, - parts, chemistry_data.mass_from_SNIa); - - list[5] = io_make_output_field("MetalMassFracFromSNIa", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - chemistry_data.metal_mass_fraction_from_SNIa); - - list[6] = io_make_output_field("TotalMassFromAGB", FLOAT, 1, UNIT_CONV_MASS, - parts, chemistry_data.mass_from_AGB); - - list[7] = - io_make_output_field("MetalMassFracFromAGB", FLOAT, 1, UNIT_CONV_NO_UNITS, - parts, chemistry_data.metal_mass_fraction_from_AGB); - - list[8] = io_make_output_field("TotalMassFromSNII", FLOAT, 1, UNIT_CONV_MASS, - parts, chemistry_data.mass_from_SNII); - - list[9] = io_make_output_field("MetalMassFracFromSNII", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - chemistry_data.metal_mass_fraction_from_SNII); - - list[10] = - io_make_output_field("IronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, - parts, chemistry_data.iron_mass_fraction_from_SNIa); + "SmoothedMetalMassFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.smoothed_metal_mass_fraction_total, + "Smoothed fractions of the particles masses that are in metals"); + + list[4] = io_make_output_field( + "MassesFromSNIa", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + chemistry_data.mass_from_SNIa, + "Masses of gas that have been produced by SNIa stars"); + + list[5] = io_make_output_field("MetalMassFractionsFromSNIa", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.metal_mass_fraction_from_SNIa, + "Fractions of the particles' masses that are " + "in metals produced by SNIa stars"); + + list[6] = io_make_output_field( + "MassesFromAGB", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + chemistry_data.mass_from_AGB, + "Masses of gas that have been produced by AGN stars"); + + list[7] = io_make_output_field("MetalMassFractionsFromAGB", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0., parts, + chemistry_data.metal_mass_fraction_from_AGB, + "Fractions of the particles' masses that are " + "in metals produced by AGB stars"); + + list[8] = io_make_output_field( + "MassesFromSNII", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + chemistry_data.mass_from_SNII, + "Masses of gas that have been produced by SNII stars"); + + list[9] = io_make_output_field("MetalMassFractionsFromSNII", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.metal_mass_fraction_from_SNII, + "Fractions of the particles' masses that are " + "in metals produced by SNII stars"); + + list[10] = io_make_output_field("IronMassFractionsFromSNIa", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.iron_mass_fraction_from_SNIa, + "Fractions of the particles' masses that are " + "in iron produced by SNIa stars"); list[11] = io_make_output_field( - "SmoothedIronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, parts, - chemistry_data.smoothed_iron_mass_fraction_from_SNIa); + "SmoothedIronMassFractionsFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, chemistry_data.smoothed_iron_mass_fraction_from_SNIa, + "Smoothed fractions of the particles' masses that are " + "in iron produced by SNIa stars"); return 12; } @@ -118,50 +140,72 @@ INLINE static int chemistry_write_sparticles(const struct spart* sparts, struct io_props* list) { /* List what we want to write */ - list[0] = io_make_output_field("ElementAbundance", FLOAT, - chemistry_element_count, UNIT_CONV_NO_UNITS, - sparts, chemistry_data.metal_mass_fraction); + list[0] = io_make_output_field( + "ElementMassFractions", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, sparts, chemistry_data.metal_mass_fraction, + "Fractions of the particles' masses that are in the given element"); list[1] = io_make_output_field( - "SmoothedElementAbundance", FLOAT, chemistry_element_count, - UNIT_CONV_NO_UNITS, sparts, chemistry_data.smoothed_metal_mass_fraction); + "SmoothedElementMassFractions", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.smoothed_metal_mass_fraction, + "Smoothed fractions of the particles' masses that are " + "in the given element"); - list[2] = - io_make_output_field("Metallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, - chemistry_data.metal_mass_fraction_total); + list[2] = io_make_output_field( + "MetalMassFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.metal_mass_fraction_total, + "Fractions of the particles' masses that are in metals"); list[3] = io_make_output_field( - "SmoothedMetallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, - chemistry_data.smoothed_metal_mass_fraction_total); - - list[4] = io_make_output_field("TotalMassFromSNIa", FLOAT, 1, UNIT_CONV_MASS, - sparts, chemistry_data.mass_from_SNIa); - - list[5] = io_make_output_field("MetalMassFracFromSNIa", FLOAT, 1, - UNIT_CONV_NO_UNITS, sparts, - chemistry_data.metal_mass_fraction_from_SNIa); - - list[6] = io_make_output_field("TotalMassFromAGB", FLOAT, 1, UNIT_CONV_MASS, - sparts, chemistry_data.mass_from_AGB); - - list[7] = - io_make_output_field("MetalMassFracFromAGB", FLOAT, 1, UNIT_CONV_NO_UNITS, - sparts, chemistry_data.metal_mass_fraction_from_AGB); - - list[8] = io_make_output_field("TotalMassFromSNII", FLOAT, 1, UNIT_CONV_MASS, - sparts, chemistry_data.mass_from_SNII); - - list[9] = io_make_output_field("MetalMassFracFromSNII", FLOAT, 1, - UNIT_CONV_NO_UNITS, sparts, - chemistry_data.metal_mass_fraction_from_SNII); - - list[10] = - io_make_output_field("IronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, - sparts, chemistry_data.iron_mass_fraction_from_SNIa); + "SmoothedMetalMassFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.smoothed_metal_mass_fraction_total, + "Smoothed fractions of the particles masses that are in metals"); + + list[4] = io_make_output_field( + "MassesFromSNIa", FLOAT, 1, UNIT_CONV_MASS, 0.f, sparts, + chemistry_data.mass_from_SNIa, + "Masses of gas that have been produced by SNIa stars"); + + list[5] = io_make_output_field("MetalMassFractionsFromSNIa", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.metal_mass_fraction_from_SNIa, + "Fractions of the particles' masses that are " + "in metals produced by SNIa stars"); + + list[6] = io_make_output_field( + "MassesFromAGB", FLOAT, 1, UNIT_CONV_MASS, 0.f, sparts, + chemistry_data.mass_from_AGB, + "Masses of gas that have been produced by AGN stars"); + + list[7] = io_make_output_field("MetalMassFractionsFromAGB", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0., sparts, + chemistry_data.metal_mass_fraction_from_AGB, + "Fractions of the particles' masses that are " + "in metals produced by AGB stars"); + + list[8] = io_make_output_field( + "MassesFromSNII", FLOAT, 1, UNIT_CONV_MASS, 0.f, sparts, + chemistry_data.mass_from_SNII, + "Masses of gas that have been produced by SNII stars"); + + list[9] = io_make_output_field("MetalMassFractionsFromSNII", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.metal_mass_fraction_from_SNII, + "Fractions of the particles' masses that are " + "in metals produced by SNII stars"); + + list[10] = io_make_output_field("IronMassFractionsFromSNIa", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.iron_mass_fraction_from_SNIa, + "Fractions of the particles' masses that are " + "in iron produced by SNIa stars"); list[11] = io_make_output_field( - "SmoothedIronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, - chemistry_data.smoothed_iron_mass_fraction_from_SNIa); + "SmoothedIronMassFractionsFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + sparts, chemistry_data.smoothed_iron_mass_fraction_from_SNIa, + "Smoothed fractions of the particles' masses that are " + "in iron produced by SNIa stars"); return 12; } @@ -178,34 +222,54 @@ INLINE static int chemistry_write_bparticles(const struct bpart* bparts, struct io_props* list) { /* List what we want to write */ - list[0] = - io_make_output_field("ElementMass", FLOAT, chemistry_element_count, - UNIT_CONV_MASS, bparts, chemistry_data.metal_mass); - - list[1] = io_make_output_field("MetalMass", FLOAT, chemistry_element_count, - UNIT_CONV_MASS, bparts, - chemistry_data.metal_mass_total); - - list[2] = io_make_output_field("MassFromSNIa", FLOAT, 1, UNIT_CONV_MASS, - bparts, chemistry_data.mass_from_SNIa); + list[0] = io_make_output_field( + "ElementMasses", FLOAT, chemistry_element_count, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.metal_mass, + "Mass contents of the BH particles in a given element"); - list[3] = io_make_output_field("MassFromSNII", FLOAT, 1, UNIT_CONV_MASS, - bparts, chemistry_data.mass_from_SNII); - - list[4] = io_make_output_field("MassFromAGB", FLOAT, 1, UNIT_CONV_MASS, - bparts, chemistry_data.mass_from_AGB); - - list[5] = io_make_output_field("MetalMassFromSNIa", FLOAT, 1, UNIT_CONV_MASS, - bparts, chemistry_data.metal_mass_from_SNIa); + list[1] = io_make_output_field( + "MetalMasses", FLOAT, chemistry_element_count, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.metal_mass_total, + "Mass contents of the BH particles in a metals"); - list[6] = io_make_output_field("MetalMassFromSNII", FLOAT, 1, UNIT_CONV_MASS, - bparts, chemistry_data.metal_mass_from_SNII); + list[2] = io_make_output_field( + "MassesFromSNIa", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, + chemistry_data.mass_from_SNIa, + "Masses of the BH particles that have been produced by SNIa stars"); - list[7] = io_make_output_field("MetalMassFromAGB", FLOAT, 1, UNIT_CONV_MASS, - bparts, chemistry_data.metal_mass_from_AGB); + list[3] = io_make_output_field( + "MassesFromSNII", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, + chemistry_data.mass_from_SNII, + "Masses of the BH particles that have been produced by SNII stars"); + + list[4] = io_make_output_field( + "MassesFromAGB", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, + chemistry_data.mass_from_AGB, + "Masses of the BH particles that have been produced by AGB stars"); + + list[5] = + io_make_output_field("MetalMassesFromSNIa", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.metal_mass_from_SNIa, + "Masses of the BH particles in metals that have " + "been produced by SNIa stars"); + + list[6] = + io_make_output_field("MetalMassesFromSNII", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.metal_mass_from_SNII, + "Masses of the BH particles in metals that have " + "been produced by SNII stars"); - list[8] = io_make_output_field("IronMassFromSNIa", FLOAT, 1, UNIT_CONV_MASS, - bparts, chemistry_data.iron_mass_from_SNIa); + list[7] = + io_make_output_field("MetalMassesFromAGB", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.metal_mass_from_AGB, + "Masses of the BH particles in metals that have " + "been produced by AGB stars"); + + list[8] = + io_make_output_field("IronMassesFromSNIa", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.iron_mass_from_SNIa, + "Masses of the BH particles in iron that have been " + "produced by SNIa stars"); return 9; } diff --git a/src/chemistry/GEAR/chemistry.h b/src/chemistry/GEAR/chemistry.h index 951d565337eae39bec05c0e142a020e6647811fe..34c4d10e5fb2f491b029c4c8d76cf04ca7a5429a 100644 --- a/src/chemistry/GEAR/chemistry.h +++ b/src/chemistry/GEAR/chemistry.h @@ -135,6 +135,15 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( } } +/** + * @brief Updates to the chemistry data after the hydro force loop. + * + * @param p The particle to act upon. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_force( + struct part* restrict p, const struct cosmology* cosmo) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -151,6 +160,27 @@ chemistry_part_has_no_neighbours(struct part* restrict p, error("Needs implementing!"); } +/** + * @brief Computes the chemistry-related time-step constraint. + * + * No constraints in the GEAR model (no diffusion) --> FLT_MAX + * + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param us The internal system of units. + * @param hydro_props The properties of the hydro scheme. + * @param cd The global properties of the chemistry scheme. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float chemistry_timestep( + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct chemistry_global_data* cd, const struct part* restrict p) { + return FLT_MAX; +} + /** * @brief Sets the chemistry properties of the (x-)particles to a valid start * state. diff --git a/src/chemistry/GEAR/chemistry_io.h b/src/chemistry/GEAR/chemistry_io.h index 7d21be4d138f40b626c29a9711166496f34b75d3..008268657fc89ae20208585cb983f4d436122201 100644 --- a/src/chemistry/GEAR/chemistry_io.h +++ b/src/chemistry/GEAR/chemistry_io.h @@ -73,16 +73,19 @@ INLINE static int chemistry_write_particles(const struct part* parts, struct io_props* list) { /* List what we want to write */ - list[0] = io_make_output_field( - "SmoothedElementAbundance", FLOAT, chemistry_element_count, - UNIT_CONV_NO_UNITS, parts, chemistry_data.smoothed_metal_mass_fraction); + list[0] = + io_make_output_field("SmoothedElementAbundances", FLOAT, + chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f, + parts, chemistry_data.smoothed_metal_mass_fraction, + "Element abundances smoothed over the neighbors"); - list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, parts, - chemistry_data.Z); + list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.Z, "Temporary field"); - list[2] = io_make_output_field("ElementAbundance", FLOAT, + list[2] = io_make_output_field("ElementAbundances", FLOAT, chemistry_element_count, UNIT_CONV_NO_UNITS, - parts, chemistry_data.metal_mass_fraction); + 0.f, parts, chemistry_data.metal_mass_fraction, + "Mass fraction of each element"); return 3; } @@ -99,16 +102,19 @@ INLINE static int chemistry_write_sparticles(const struct spart* sparts, struct io_props* list) { /* List what we want to write */ - list[0] = io_make_output_field( - "SmoothedElementAbundance", FLOAT, chemistry_element_count, - UNIT_CONV_NO_UNITS, sparts, chemistry_data.smoothed_metal_mass_fraction); - - list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, - chemistry_data.Z); - - list[2] = io_make_output_field("ElementAbundance", FLOAT, - chemistry_element_count, UNIT_CONV_NO_UNITS, - sparts, chemistry_data.metal_mass_fraction); + list[0] = + io_make_output_field("SmoothedElementAbundances", FLOAT, + chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f, + sparts, chemistry_data.smoothed_metal_mass_fraction, + "Element abundances smoothed over the neighbors"); + + list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.Z, "Temporary field"); + + list[2] = io_make_output_field( + "ElementAbundance", FLOAT, chemistry_element_count, UNIT_CONV_NO_UNITS, + 0.f, sparts, chemistry_data.metal_mass_fraction, + "Mass fraction of each element"); return 3; } diff --git a/src/chemistry/none/chemistry.h b/src/chemistry/none/chemistry.h index 543a5e77eea245da9ec18de210c781d5be07d7fb..7c89e682570484737fe93a82690ebcb1e09b03e8 100644 --- a/src/chemistry/none/chemistry.h +++ b/src/chemistry/none/chemistry.h @@ -87,6 +87,34 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( struct part* restrict p, const struct chemistry_global_data* cd, const struct cosmology* cosmo) {} +/** + * @brief Updates to the chemistry data after the hydro force loop. + * + * @param p The particle to act upon. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_force( + struct part* restrict p, const struct cosmology* cosmo) {} + +/** + * @brief Computes the chemistry-related time-step constraint. + * + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param us The internal system of units. + * @param hydro_props The properties of the hydro scheme. + * @param cd The global properties of the chemistry scheme. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float chemistry_timestep( + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct chemistry_global_data* cd, const struct part* restrict p) { + return FLT_MAX; +} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -146,4 +174,88 @@ __attribute__((always_inline)) INLINE static void chemistry_init_part( */ __attribute__((always_inline)) INLINE static void chemistry_first_init_spart( const struct chemistry_global_data* data, struct spart* restrict sp) {} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * star particle to be used in feedback/enrichment related routines. + * + * No metallicity treatment here -> return 0. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return 0.f; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * star particle to be used in feedback/enrichment related routines. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return NULL; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in cooling related routines. + * + * No metallicity treatment here -> return 0. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_cooling( + const struct part* restrict p) { + + return 0.f; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in cooling related routines. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_cooling(const struct part* restrict p) { + + return NULL; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in star formation related routines. + * + * No metallicity treatment here -> return 0. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return 0.f; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in star formation related routines. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return NULL; +} + #endif /* SWIFT_CHEMISTRY_NONE_H */ diff --git a/src/collectgroup.c b/src/collectgroup.c index d17608d67a291ae412728b3fcd9ea80c192802bf..8dd9e974f41c979f4c50e84fb555f9b80db19e25 100644 --- a/src/collectgroup.c +++ b/src/collectgroup.c @@ -122,7 +122,8 @@ void collectgroup1_apply(const struct collectgroup1 *grp1, struct engine *e) { e->total_nr_cells = grp1->total_nr_cells; e->total_nr_tasks = grp1->total_nr_tasks; e->tasks_per_cell_max = grp1->tasks_per_cell_max; - e->sfh = grp1->sfh; + + star_formation_logger_add_to_accumulator(&e->sfh, &grp1->sfh); } /** diff --git a/src/common_io.c b/src/common_io.c index 1c5befb8698109d7d4f6711c84e5fb4e7496c37d..25abb187a7ac1ae86feab50bfd5cdbd0633d17ac 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -24,10 +24,16 @@ /* This object's header. */ #include "common_io.h" +/* Pre-inclusion as needed in other headers */ +#include "engine.h" + /* Local includes. */ +#include "black_holes_io.h" #include "chemistry_io.h" -#include "engine.h" +#include "const.h" +#include "cooling_io.h" #include "error.h" +#include "fof_io.h" #include "gravity_io.h" #include "hydro.h" #include "hydro_io.h" @@ -35,9 +41,12 @@ #include "kernel_hydro.h" #include "part.h" #include "part_type.h" +#include "star_formation_io.h" #include "stars_io.h" #include "threadpool.h" +#include "tracers_io.h" #include "units.h" +#include "velociraptor_io.h" #include "version.h" /* Some standard headers. */ @@ -130,6 +139,85 @@ void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, H5Aclose(h_attr); } +/** + * @brief Reads an attribute from a given HDF5 group. + * + * @param grp The group from which to read. + * @param name The name of the attribute to read. + * @param type The #IO_DATA_TYPE of the attribute. + * @param data (output) The attribute read from the HDF5 group. + * + * Exits gracefully (i.e. does not read the attribute at all) if + * it is not present, unless debugging checks are activated. If they are, + * and the read fails, we print a warning. + */ +void io_read_attribute_graceful(hid_t grp, const char* name, + enum IO_DATA_TYPE type, void* data) { + + /* First, we need to check if this attribute exists to avoid raising errors + * within the HDF5 library if we attempt to access an attribute that does + * not exist. */ + const htri_t h_exists = H5Aexists(grp, name); + + if (h_exists <= 0) { + /* Attribute either does not exist (0) or function failed (-ve) */ +#ifdef SWIFT_DEBUG_CHECKS + message("WARNING: attribute '%s' does not exist.", name); +#endif + } else { + /* Ok, now we know that it exists we can read it. */ + const hid_t h_attr = H5Aopen(grp, name, H5P_DEFAULT); + + if (h_attr >= 0) { + const hid_t h_err = H5Aread(h_attr, io_hdf5_type(type), data); + if (h_err < 0) { + /* Explicitly do nothing unless debugging checks are activated */ +#ifdef SWIFT_DEBUG_CHECKS + message("WARNING: unable to read attribute '%s'", name); +#endif + } + } else { +#ifdef SWIFT_DEBUG_CHECKS + if (h_attr < 0) { + message("WARNING: was unable to open attribute '%s'", name); + } +#endif + } + + H5Aclose(h_attr); + } +} + +/** + * @brief Asserts that the redshift in the initial conditions and the one + * specified by the parameter file match. + * + * @param h_grp The Header group from the ICs + * @param a Current scale factor as specified by parameter file + */ +void io_assert_valid_header_cosmology(hid_t h_grp, double a) { + + double redshift_from_snapshot = -1.0; + io_read_attribute_graceful(h_grp, "Redshift", DOUBLE, + &redshift_from_snapshot); + + /* If the Header/Redshift value is not present, then we skip this check */ + if (redshift_from_snapshot == -1.0) { + return; + } + + const double current_redshift = 1.0 / a - 1.0; + const double redshift_fractional_difference = + fabs(redshift_from_snapshot - current_redshift) / current_redshift; + + if (redshift_fractional_difference >= io_redshift_tolerance) { + error( + "Initial redshift specified in parameter file (%lf) and redshift " + "read from initial conditions (%lf) are inconsistent.", + current_redshift, redshift_from_snapshot); + } +} + /** * @brief Write an attribute to a given HDF5 group. * @@ -392,8 +480,8 @@ static long long cell_count_non_inhibited_gas(const struct cell* c) { struct part* parts = c->hydro.parts; long long count = 0; for (int i = 0; i < total_count; ++i) { - if (!(parts[i].time_bin != time_bin_inhibited) && - !(parts[i].time_bin != time_bin_not_created)) { + if ((parts[i].time_bin != time_bin_inhibited) && + (parts[i].time_bin != time_bin_not_created)) { ++count; } } @@ -405,8 +493,8 @@ static long long cell_count_non_inhibited_dark_matter(const struct cell* c) { struct gpart* gparts = c->grav.parts; long long count = 0; for (int i = 0; i < total_count; ++i) { - if (!(gparts[i].time_bin != time_bin_inhibited) && - !(gparts[i].time_bin != time_bin_not_created) && + if ((gparts[i].time_bin != time_bin_inhibited) && + (gparts[i].time_bin != time_bin_not_created) && (gparts[i].type == swift_type_dark_matter)) { ++count; } @@ -414,13 +502,28 @@ static long long cell_count_non_inhibited_dark_matter(const struct cell* c) { return count; } +static long long cell_count_non_inhibited_background_dark_matter( + const struct cell* c) { + const int total_count = c->grav.count; + struct gpart* gparts = c->grav.parts; + long long count = 0; + for (int i = 0; i < total_count; ++i) { + if ((gparts[i].time_bin != time_bin_inhibited) && + (gparts[i].time_bin != time_bin_not_created) && + (gparts[i].type == swift_type_dark_matter_background)) { + ++count; + } + } + return count; +} + static long long cell_count_non_inhibited_stars(const struct cell* c) { const int total_count = c->stars.count; struct spart* sparts = c->stars.parts; long long count = 0; for (int i = 0; i < total_count; ++i) { - if (!(sparts[i].time_bin != time_bin_inhibited) && - !(sparts[i].time_bin != time_bin_not_created)) { + if ((sparts[i].time_bin != time_bin_inhibited) && + (sparts[i].time_bin != time_bin_not_created)) { ++count; } } @@ -432,8 +535,8 @@ static long long cell_count_non_inhibited_black_holes(const struct cell* c) { struct bpart* bparts = c->black_holes.parts; long long count = 0; for (int i = 0; i < total_count; ++i) { - if (!(bparts[i].time_bin != time_bin_inhibited) && - !(bparts[i].time_bin != time_bin_not_created)) { + if ((bparts[i].time_bin != time_bin_inhibited) && + (bparts[i].time_bin != time_bin_not_created)) { ++count; } } @@ -455,30 +558,36 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], centres = (double*)malloc(3 * nr_cells * sizeof(double)); /* Count of particles in each cell */ - long long *count_part = NULL, *count_gpart = NULL, *count_spart = NULL, + long long *count_part = NULL, *count_gpart = NULL, + *count_background_gpart = NULL, *count_spart = NULL, *count_bpart = NULL; count_part = (long long*)malloc(nr_cells * sizeof(long long)); count_gpart = (long long*)malloc(nr_cells * sizeof(long long)); + count_background_gpart = (long long*)malloc(nr_cells * sizeof(long long)); count_spart = (long long*)malloc(nr_cells * sizeof(long long)); count_bpart = (long long*)malloc(nr_cells * sizeof(long long)); /* Global offsets of particles in each cell */ - long long *offset_part = NULL, *offset_gpart = NULL, *offset_spart = NULL, + long long *offset_part = NULL, *offset_gpart = NULL, + *offset_background_gpart = NULL, *offset_spart = NULL, *offset_bpart = NULL; offset_part = (long long*)malloc(nr_cells * sizeof(long long)); offset_gpart = (long long*)malloc(nr_cells * sizeof(long long)); + offset_background_gpart = (long long*)malloc(nr_cells * sizeof(long long)); offset_spart = (long long*)malloc(nr_cells * sizeof(long long)); offset_bpart = (long long*)malloc(nr_cells * sizeof(long long)); /* Offsets of the 0^th element */ offset_part[0] = 0; offset_gpart[0] = 0; + offset_background_gpart[0] = 0; offset_spart[0] = 0; offset_bpart[0] = 0; /* Collect the cell information of *local* cells */ long long local_offset_part = 0; long long local_offset_gpart = 0; + long long local_offset_background_gpart = 0; long long local_offset_spart = 0; long long local_offset_bpart = 0; for (int i = 0; i < nr_cells; ++i) { @@ -493,6 +602,8 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], /* Count real particles that will be written */ count_part[i] = cell_count_non_inhibited_gas(&cells_top[i]); count_gpart[i] = cell_count_non_inhibited_dark_matter(&cells_top[i]); + count_background_gpart[i] = + cell_count_non_inhibited_background_dark_matter(&cells_top[i]); count_spart[i] = cell_count_non_inhibited_stars(&cells_top[i]); count_bpart[i] = cell_count_non_inhibited_black_holes(&cells_top[i]); @@ -501,12 +612,16 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], offset_part[i] = local_offset_part + global_offsets[swift_type_gas]; offset_gpart[i] = local_offset_gpart + global_offsets[swift_type_dark_matter]; + offset_background_gpart[i] = + local_offset_background_gpart + + global_offsets[swift_type_dark_matter_background]; offset_spart[i] = local_offset_spart + global_offsets[swift_type_stars]; offset_bpart[i] = local_offset_bpart + global_offsets[swift_type_black_hole]; local_offset_part += count_part[i]; local_offset_gpart += count_gpart[i]; + local_offset_background_gpart += count_background_gpart[i]; local_offset_spart += count_spart[i]; local_offset_bpart += count_bpart[i]; @@ -520,11 +635,13 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], count_part[i] = 0; count_gpart[i] = 0; + count_background_gpart[i] = 0; count_spart[i] = 0; count_bpart[i] = 0; offset_part[i] = 0; offset_gpart[i] = 0; + offset_background_gpart[i] = 0; offset_spart[i] = 0; offset_bpart[i] = 0; } @@ -540,7 +657,6 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], MPI_Reduce(count_part, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); } - if (nodeID == 0) { MPI_Reduce(MPI_IN_PLACE, count_gpart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); @@ -548,6 +664,13 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], MPI_Reduce(count_gpart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, count_background_gpart, nr_cells, + MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(count_background_gpart, NULL, nr_cells, MPI_LONG_LONG_INT, + MPI_BOR, 0, MPI_COMM_WORLD); + } if (nodeID == 0) { MPI_Reduce(MPI_IN_PLACE, count_spart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); @@ -577,6 +700,13 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], MPI_Reduce(offset_gpart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, offset_background_gpart, nr_cells, + MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(offset_background_gpart, NULL, nr_cells, MPI_LONG_LONG_INT, + MPI_BOR, 0, MPI_COMM_WORLD); + } if (nodeID == 0) { MPI_Reduce(MPI_IN_PLACE, offset_spart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); @@ -692,6 +822,28 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], H5Sclose(h_space); } + if (global_counts[swift_type_dark_matter_background] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) + error("Error while creating data space for background DM offsets"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error( + "Error while changing shape of background DM offsets data space."); + h_data = H5Dcreate(h_subgrp, "PartType2", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) + error("Error while creating dataspace for background DM offsets."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, offset_background_gpart); + if (h_err < 0) error("Error while writing background DM offsets."); + H5Dclose(h_data); + H5Sclose(h_space); + } + if (global_counts[swift_type_stars] > 0) { shape[0] = nr_cells; @@ -778,6 +930,27 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], H5Sclose(h_space); } + if (global_counts[swift_type_dark_matter_background] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) + error("Error while creating data space for background DM counts"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of background DM counts data space."); + h_data = H5Dcreate(h_subgrp, "PartType2", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) + error("Error while creating dataspace for background DM counts."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, count_background_gpart); + if (h_err < 0) error("Error while writing background DM counts."); + H5Dclose(h_data); + H5Sclose(h_space); + } + if (global_counts[swift_type_stars] > 0) { shape[0] = nr_cells; @@ -826,10 +999,12 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], free(centres); free(count_part); free(count_gpart); + free(count_background_gpart); free(count_spart); free(count_bpart); free(offset_part); free(offset_gpart); + free(offset_background_gpart); free(offset_spart); free(offset_bpart); } @@ -1497,6 +1672,56 @@ void io_prepare_dm_gparts(struct threadpool* tp, struct gpart* const gparts, sizeof(struct gpart), 0, NULL); } +void io_prepare_dm_background_gparts_mapper(void* restrict data, int Ndm, + void* dummy) { + + struct gpart* restrict gparts = (struct gpart*)data; + + /* Let's give all these gparts a negative id */ + for (int i = 0; i < Ndm; ++i) { + + /* Negative ids are not allowed */ + if (gparts[i].id_or_neg_offset < 0) + error("Negative ID for DM particle %i: ID=%lld", i, + gparts[i].id_or_neg_offset); + + /* Set gpart type */ + gparts[i].type = swift_type_dark_matter_background; + } +} + +/** + * @brief Prepare the DM backgorund particles (in gparts) read in + * for the addition of the other particle types + * + * This function assumes that the DM particles are all at the start of the + * gparts array and that the background particles directly follow them. + * + * @param tp The current #threadpool. + * @param gparts The array of #gpart freshly read in. + * @param Ndm The number of DM particles read in. + */ +void io_prepare_dm_background_gparts(struct threadpool* tp, + struct gpart* const gparts, size_t Ndm) { + + threadpool_map(tp, io_prepare_dm_background_gparts_mapper, gparts, Ndm, + sizeof(struct gpart), 0, NULL); +} + +size_t io_count_dm_background_gparts(const struct gpart* const gparts, + const size_t Ndm) { + + swift_declare_aligned_ptr(const struct gpart, gparts_array, gparts, + SWIFT_STRUCT_ALIGNMENT); + + size_t count = 0; + for (size_t i = 0; i < Ndm; ++i) { + if (gparts_array[i].type == swift_type_dark_matter_background) ++count; + } + + return count; +} + struct duplication_data { struct part* parts; @@ -1792,7 +2017,8 @@ void io_collect_bparts_to_write(const struct bpart* restrict bparts, } /** - * @brief Copy every non-inhibited DM #gpart into the gparts_written array. + * @brief Copy every non-inhibited regulat DM #gpart into the gparts_written + * array. * * @param gparts The array of #gpart containing all particles. * @param vr_data The array of gpart-related VELOCIraptor output. @@ -1834,6 +2060,50 @@ void io_collect_gparts_to_write( count, Ngparts_written); } +/** + * @brief Copy every non-inhibited background DM #gpart into the gparts_written + * array. + * + * @param gparts The array of #gpart containing all particles. + * @param vr_data The array of gpart-related VELOCIraptor output. + * @param gparts_written The array of #gpart to fill with particles we want to + * write. + * @param vr_data_written The array of gpart-related VELOCIraptor with particles + * we want to write. + * @param Ngparts The total number of #part. + * @param Ngparts_written The total number of #part to write. + * @param with_stf Are we running with STF? i.e. do we want to collect vr data? + */ +void io_collect_gparts_background_to_write( + const struct gpart* restrict gparts, + const struct velociraptor_gpart_data* restrict vr_data, + struct gpart* restrict gparts_written, + struct velociraptor_gpart_data* restrict vr_data_written, + const size_t Ngparts, const size_t Ngparts_written, const int with_stf) { + + size_t count = 0; + + /* Loop over all parts */ + for (size_t i = 0; i < Ngparts; ++i) { + + /* And collect the ones that have not been removed */ + if ((gparts[i].time_bin != time_bin_inhibited) && + (gparts[i].time_bin != time_bin_not_created) && + (gparts[i].type == swift_type_dark_matter_background)) { + + if (with_stf) vr_data_written[count] = vr_data[i]; + + gparts_written[count] = gparts[i]; + count++; + } + } + + /* Check that everything is fine */ + if (count != Ngparts_written) + error("Collected the wrong number of g-particles (%zu vs. %zu expected)", + count, Ngparts_written); +} + /** * @brief Verify the io parameter file * @@ -1841,17 +2111,7 @@ void io_collect_gparts_to_write( * @param N_total The total number of each particle type. */ void io_check_output_fields(const struct swift_params* params, - const long long N_total[3]) { - - /* Create some fake particles as arguments for the writing routines */ - struct part p; - struct xpart xp; - struct spart sp; - struct gpart gp; - - /* Copy N_total to array with length == 6 */ - const long long nr_total[swift_type_count] = {N_total[0], N_total[1], 0, - 0, N_total[2], 0}; + const long long N_total[swift_type_count]) { /* Loop over all particle types to check the fields */ for (int ptype = 0; ptype < swift_type_count; ptype++) { @@ -1860,22 +2120,51 @@ void io_check_output_fields(const struct swift_params* params, struct io_props list[100]; /* Don't do anything if no particle of this kind */ - if (nr_total[ptype] == 0) continue; + if (N_total[ptype] == 0) continue; /* Gather particle fields from the particle structures */ switch (ptype) { case swift_type_gas: - hydro_write_particles(&p, &xp, list, &num_fields); - num_fields += chemistry_write_particles(&p, list + num_fields); + hydro_write_particles(NULL, NULL, list, &num_fields); + num_fields += chemistry_write_particles(NULL, list + num_fields); + num_fields += + cooling_write_particles(NULL, NULL, list + num_fields, NULL); + num_fields += tracers_write_particles(NULL, NULL, list + num_fields, + /*with_cosmology=*/1); + num_fields += + star_formation_write_particles(NULL, NULL, list + num_fields); + num_fields += fof_write_parts(NULL, NULL, list + num_fields); + num_fields += velociraptor_write_parts(NULL, NULL, list + num_fields); break; case swift_type_dark_matter: - darkmatter_write_particles(&gp, list, &num_fields); + darkmatter_write_particles(NULL, list, &num_fields); + num_fields += fof_write_gparts(NULL, list + num_fields); + num_fields += velociraptor_write_gparts(NULL, list + num_fields); + break; + + case swift_type_dark_matter_background: + darkmatter_write_particles(NULL, list, &num_fields); + num_fields += fof_write_gparts(NULL, list + num_fields); + num_fields += velociraptor_write_gparts(NULL, list + num_fields); break; case swift_type_stars: - stars_write_particles(&sp, list, &num_fields); + stars_write_particles(NULL, list, &num_fields, /*with_cosmology=*/1); + num_fields += chemistry_write_sparticles(NULL, list + num_fields); + num_fields += tracers_write_sparticles(NULL, list + num_fields, + /*with_cosmology=*/1); + num_fields += fof_write_sparts(NULL, list + num_fields); + num_fields += velociraptor_write_sparts(NULL, list + num_fields); + break; + + case swift_type_black_hole: + black_holes_write_particles(NULL, list, &num_fields, + /*with_cosmology=*/1); + num_fields += chemistry_write_bparticles(NULL, list + num_fields); + num_fields += fof_write_bparts(NULL, list + num_fields); + num_fields += velociraptor_write_bparts(NULL, list + num_fields); break; default: @@ -1901,8 +2190,8 @@ void io_check_output_fields(const struct swift_params* params, /* loop over each possible output field */ for (int field_id = 0; field_id < num_fields; field_id++) { char field_name[PARSER_MAX_LINE_SIZE]; - sprintf(field_name, "SelectOutput:%s_%s", list[field_id].name, - part_type_names[ptype]); + sprintf(field_name, "SelectOutput:%.*s_%s", FIELD_BUFFER_SIZE, + list[field_id].name, part_type_names[ptype]); if (strcmp(param_name, field_name) == 0) { found = 1; @@ -1934,13 +2223,17 @@ void io_check_output_fields(const struct swift_params* params, /** * @brief Write the output field parameters file * - * @param filename The file to write + * @param filename The file to write. */ void io_write_output_field_parameter(const char* filename) { FILE* file = fopen(filename, "w"); if (file == NULL) error("Error opening file '%s'", filename); + /* Create a fake unit system for the snapshots */ + struct unit_system snapshot_units; + units_init_cgs(&snapshot_units); + /* Loop over all particle types */ fprintf(file, "SelectOutput:\n"); for (int ptype = 0; ptype < swift_type_count; ptype++) { @@ -1954,14 +2247,43 @@ void io_write_output_field_parameter(const char* filename) { case swift_type_gas: hydro_write_particles(NULL, NULL, list, &num_fields); num_fields += chemistry_write_particles(NULL, list + num_fields); + num_fields += + cooling_write_particles(NULL, NULL, list + num_fields, NULL); + num_fields += tracers_write_particles(NULL, NULL, list + num_fields, + /*with_cosmology=*/1); + num_fields += + star_formation_write_particles(NULL, NULL, list + num_fields); + num_fields += fof_write_parts(NULL, NULL, list + num_fields); + num_fields += velociraptor_write_parts(NULL, NULL, list + num_fields); break; case swift_type_dark_matter: darkmatter_write_particles(NULL, list, &num_fields); + num_fields += fof_write_gparts(NULL, list + num_fields); + num_fields += velociraptor_write_gparts(NULL, list + num_fields); + break; + + case swift_type_dark_matter_background: + darkmatter_write_particles(NULL, list, &num_fields); + num_fields += fof_write_gparts(NULL, list + num_fields); + num_fields += velociraptor_write_gparts(NULL, list + num_fields); break; case swift_type_stars: - stars_write_particles(NULL, list, &num_fields); + stars_write_particles(NULL, list, &num_fields, /*with_cosmology=*/1); + num_fields += chemistry_write_sparticles(NULL, list + num_fields); + num_fields += tracers_write_sparticles(NULL, list + num_fields, + /*with_cosmology=*/1); + num_fields += fof_write_sparts(NULL, list + num_fields); + num_fields += velociraptor_write_sparts(NULL, list + num_fields); + break; + + case swift_type_black_hole: + black_holes_write_particles(NULL, list, &num_fields, + /*with_cosmology=*/1); + num_fields += chemistry_write_bparticles(NULL, list + num_fields); + num_fields += fof_write_bparts(NULL, list + num_fields); + num_fields += velociraptor_write_bparts(NULL, list + num_fields); break; default: @@ -1974,8 +2296,17 @@ void io_write_output_field_parameter(const char* filename) { fprintf(file, " # Particle Type %s\n", part_type_names[ptype]); /* Write all the fields of this particle type */ - for (int i = 0; i < num_fields; ++i) - fprintf(file, " %s_%s: 1\n", list[i].name, part_type_names[ptype]); + for (int i = 0; i < num_fields; ++i) { + + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, &snapshot_units, list[i].units, + list[i].scale_factor_exponent); + + fprintf(file, + " %s_%s: %*d \t # %s. ::: Conversion to physical CGS: %s\n", + list[i].name, part_type_names[ptype], + (int)(28 - strlen(list[i].name)), 1, list[i].description, buffer); + } fprintf(file, "\n"); } diff --git a/src/common_io.h b/src/common_io.h index c93414d1ffca8f7ead7a1ff29d387967f82a9cb2..b4a846dc4f683cb4e18e3bba03c4cc95235a8854 100644 --- a/src/common_io.h +++ b/src/common_io.h @@ -27,7 +27,8 @@ #include "part_type.h" #include "units.h" -#define FIELD_BUFFER_SIZE 200 +#define FIELD_BUFFER_SIZE 64 +#define DESCRIPTION_BUFFER_SIZE 256 #define PARTICLE_GROUP_BUFFER_SIZE 50 #define FILENAME_BUFFER_SIZE 150 #define IO_BUFFER_ALIGNMENT 1024 @@ -67,6 +68,9 @@ hid_t io_hdf5_type(enum IO_DATA_TYPE type); void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data); +void io_read_attribute_graceful(hid_t grp, const char* name, + enum IO_DATA_TYPE type, void* data); +void io_assert_valid_header_cosmology(hid_t h_grp, double a); void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, const void* data, int num); @@ -124,8 +128,18 @@ void io_collect_gparts_to_write(const struct gpart* restrict gparts, struct velociraptor_gpart_data* vr_data_written, const size_t Ngparts, const size_t Ngparts_written, int with_stf); +void io_collect_gparts_background_to_write( + const struct gpart* restrict gparts, + const struct velociraptor_gpart_data* vr_data, + struct gpart* restrict gparts_written, + struct velociraptor_gpart_data* vr_data_written, const size_t Ngparts, + const size_t Ngparts_written, int with_stf); void io_prepare_dm_gparts(struct threadpool* tp, struct gpart* const gparts, size_t Ndm); +void io_prepare_dm_background_gparts(struct threadpool* tp, + struct gpart* const gparts, size_t Ndm); +size_t io_count_dm_background_gparts(const struct gpart* const gparts, + size_t Ndm); void io_duplicate_hydro_gparts(struct threadpool* tp, struct part* const parts, struct gpart* const gparts, size_t Ngas, size_t Ndm); diff --git a/src/const.h b/src/const.h index d7017699cff56b1de545e6e0ae78013f000b76d0..521d094f51e61fde1e1f983caa7194bc0242214b 100644 --- a/src/const.h +++ b/src/const.h @@ -26,6 +26,11 @@ /* Time-step limiter maximal difference in signal velocity */ #define const_limiter_max_v_sig_ratio 4.1f +/* I/O Constant; this determines the relative tolerance between the value of + * redshift read from the snapshot, and the value from the parameter file. This + * current value asserts that they must match within 0.1%. */ +#define io_redshift_tolerance 1e-3f + /* Type of gradients to use (GIZMO_SPH only) */ /* If no option is chosen, no gradients are used (first order scheme) */ //#define GRADIENTS_SPH diff --git a/src/cooling/Compton/cooling_io.h b/src/cooling/Compton/cooling_io.h index 8fa3944ff78e7592da3978ee9465451c96e1d533..1f137e18ca3c62c1083a8139cbb636b0b934d81d 100644 --- a/src/cooling/Compton/cooling_io.h +++ b/src/cooling/Compton/cooling_io.h @@ -64,9 +64,9 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, - UNIT_CONV_TEMPERATURE, parts, - xparts, convert_part_T); + list[0] = io_make_output_field_convert_part( + "Temperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, + convert_part_T, "Temperatures of the gas particles"); return 1; } diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c index 3fda0c80040b6ad139b9100064b937e695e6030f..c5595ef514d5a145dd6e9c326c1ff1f2bc9da196 100644 --- a/src/cooling/EAGLE/cooling.c +++ b/src/cooling/EAGLE/cooling.c @@ -207,7 +207,7 @@ INLINE static double bisection_iter( const float d_He, const double Lambda_He_reion_cgs, const double ratefact_cgs, const struct cooling_function_data *restrict cooling, - const float abundance_ratio[chemistry_element_count + 2], + const float abundance_ratio[eagle_cooling_N_abundances], const double dt_cgs, const long long ID) { /* Bracketing */ @@ -419,14 +419,14 @@ void cooling_cool_part(const struct phys_const *phys_const, * Note that we need to add S and Ca that are in the tables but not tracked * by the particles themselves. * The order is [H, He, C, N, O, Ne, Mg, Si, S, Ca, Fe] */ - float abundance_ratio[chemistry_element_count + 2]; + float abundance_ratio[eagle_cooling_N_abundances]; abundance_ratio_to_solar(p, cooling, abundance_ratio); /* Get the Hydrogen and Helium mass fractions */ - const float XH = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H]; - const float XHe = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_He]; + const float *const metal_fraction = + chemistry_get_metal_mass_fraction_for_cooling(p); + const float XH = metal_fraction[chemistry_element_H]; + const float XHe = metal_fraction[chemistry_element_He]; /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a * metal-free Helium mass fraction as per the Wiersma+08 definition */ @@ -600,10 +600,10 @@ float cooling_get_temperature( const double u_cgs = u * cooling->internal_energy_to_cgs; /* Get the Hydrogen and Helium mass fractions */ - const float XH = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H]; - const float XHe = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_He]; + const float *const metal_fraction = + chemistry_get_metal_mass_fraction_for_cooling(p); + const float XH = metal_fraction[chemistry_element_H]; + const float XHe = metal_fraction[chemistry_element_He]; /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a * metal-free Helium mass fraction as per the Wiersma+08 definition */ diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h index 5508153afc094d84383893f55ac0362a6d427b24..a57e4451d8e350e1f16a64318b510495ed77e811 100644 --- a/src/cooling/EAGLE/cooling_io.h +++ b/src/cooling/EAGLE/cooling_io.h @@ -63,9 +63,9 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, - UNIT_CONV_TEMPERATURE, parts, - xparts, convert_part_T); + list[0] = io_make_output_field_convert_part( + "Temperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, + convert_part_T, "Temperatures of the gas particles"); return 1; } diff --git a/src/cooling/EAGLE/cooling_rates.h b/src/cooling/EAGLE/cooling_rates.h index 6707a9a9d70b58eb218cfc8ebeca9b1f08fdcb12..39978c9bfa8cf2fbea25e8856c2939a8d6ee69e4 100644 --- a/src/cooling/EAGLE/cooling_rates.h +++ b/src/cooling/EAGLE/cooling_rates.h @@ -50,57 +50,50 @@ * @param cooling #cooling_function_data struct. * @param ratio_solar (return) Array of ratios to solar abundances. */ -__attribute__((always_inline)) INLINE void abundance_ratio_to_solar( +__attribute__((always_inline)) INLINE static void abundance_ratio_to_solar( const struct part *p, const struct cooling_function_data *cooling, - float ratio_solar[chemistry_element_count + 2]) { + float ratio_solar[eagle_cooling_N_abundances]) { - ratio_solar[0] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H] * - cooling->SolarAbundances_inv[0 /* H */]; + /* Get the individual metal mass fractions from the particle */ + const float *const metal_fraction = + chemistry_get_metal_mass_fraction_for_cooling(p); - ratio_solar[1] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_He] * - cooling->SolarAbundances_inv[1 /* He */]; + ratio_solar[0] = metal_fraction[chemistry_element_H] * + cooling->SolarAbundances_inv[0 /* H */]; - ratio_solar[2] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_C] * - cooling->SolarAbundances_inv[2 /* C */]; + ratio_solar[1] = metal_fraction[chemistry_element_He] * + cooling->SolarAbundances_inv[1 /* He */]; - ratio_solar[3] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_N] * - cooling->SolarAbundances_inv[3 /* N */]; + ratio_solar[2] = metal_fraction[chemistry_element_C] * + cooling->SolarAbundances_inv[2 /* C */]; - ratio_solar[4] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_O] * - cooling->SolarAbundances_inv[4 /* O */]; + ratio_solar[3] = metal_fraction[chemistry_element_N] * + cooling->SolarAbundances_inv[3 /* N */]; - ratio_solar[5] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Ne] * - cooling->SolarAbundances_inv[5 /* Ne */]; + ratio_solar[4] = metal_fraction[chemistry_element_O] * + cooling->SolarAbundances_inv[4 /* O */]; - ratio_solar[6] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Mg] * - cooling->SolarAbundances_inv[6 /* Mg */]; + ratio_solar[5] = metal_fraction[chemistry_element_Ne] * + cooling->SolarAbundances_inv[5 /* Ne */]; - ratio_solar[7] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Si] * - cooling->SolarAbundances_inv[7 /* Si */]; + ratio_solar[6] = metal_fraction[chemistry_element_Mg] * + cooling->SolarAbundances_inv[6 /* Mg */]; + + ratio_solar[7] = metal_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */]; /* For S, we use the same ratio as Si */ - ratio_solar[8] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Si] * - cooling->SolarAbundances_inv[7 /* Si */] * - cooling->S_over_Si_ratio_in_solar; + ratio_solar[8] = metal_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */] * + cooling->S_over_Si_ratio_in_solar; /* For Ca, we use the same ratio as Si */ - ratio_solar[9] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Si] * - cooling->SolarAbundances_inv[7 /* Si */] * - cooling->Ca_over_Si_ratio_in_solar; - - ratio_solar[10] = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Fe] * - cooling->SolarAbundances_inv[10 /* Fe */]; + ratio_solar[9] = metal_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */] * + cooling->Ca_over_Si_ratio_in_solar; + + ratio_solar[10] = metal_fraction[chemistry_element_Fe] * + cooling->SolarAbundances_inv[10 /* Fe */]; } /** @@ -313,7 +306,7 @@ __attribute__((always_inline)) INLINE double eagle_Compton_cooling_rate( */ INLINE static double eagle_metal_cooling_rate( const double log10_u_cgs, const double redshift, const double n_H_cgs, - const float solar_ratio[chemistry_element_count + 2], const int n_H_index, + const float solar_ratio[eagle_cooling_N_abundances], const int n_H_index, const float d_n_H, const int He_index, const float d_He, const struct cooling_function_data *cooling, double *element_lambda) { @@ -537,7 +530,7 @@ INLINE static double eagle_metal_cooling_rate( */ INLINE static double eagle_cooling_rate( const double log10_u_cgs, const double redshift, const double n_H_cgs, - const float abundance_ratio[chemistry_element_count + 2], + const float abundance_ratio[eagle_cooling_N_abundances], const int n_H_index, const float d_n_H, const int He_index, const float d_He, const struct cooling_function_data *cooling) { diff --git a/src/cooling/EAGLE/cooling_tables.c b/src/cooling/EAGLE/cooling_tables.c index cddc8d50e0c2f00c30846ebaf65b2bf250e9cc8a..1de3265df6298eeb955758e272c7e17afb64de00 100644 --- a/src/cooling/EAGLE/cooling_tables.c +++ b/src/cooling/EAGLE/cooling_tables.c @@ -213,10 +213,6 @@ void read_cooling_header(const char *fname, if (N_SolarAbundances != eagle_cooling_N_abundances) error("Invalid solar abundances array length."); - /* Check value */ - if (N_SolarAbundances != chemistry_element_count + 2) - error("Number of abundances not compatible with the chemistry model."); - dataset = H5Dopen(tempfile_id, "/Header/Number_of_metals", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &N_Elements); @@ -297,7 +293,10 @@ void read_cooling_header(const char *fname, cooling->nH[i] = log10(cooling->nH[i]); } - /* Compute inverse of solar mass fractions */ + /* Compute inverse of solar mass fractions */ +#if defined(__ICC) +#pragma novector +#endif for (int i = 0; i < N_SolarAbundances; ++i) { cooling->SolarAbundances_inv[i] = 1.f / cooling->SolarAbundances[i]; } diff --git a/src/cooling/EAGLE/newton_cooling.c b/src/cooling/EAGLE/newton_cooling.c index c68a77614425f85da011ab45d2ec488710e068dc..50af3294c31dfc2d44a679c26a78629d53d03420 100644 --- a/src/cooling/EAGLE/newton_cooling.c +++ b/src/cooling/EAGLE/newton_cooling.c @@ -214,7 +214,7 @@ __attribute__((always_inline)) INLINE double eagle_convert_u_to_temp( */ INLINE static double eagle_metal_cooling_rate( double log10_u_cgs, double redshift, double n_H_cgs, - const float solar_ratio[chemistry_element_count + 2], int n_H_index, + const float solar_ratio[eagle_cooling_N_abundances], int n_H_index, float d_n_H, int He_index, float d_He, const struct cooling_function_data *restrict cooling, double *dlambda_du, double *element_lambda) { @@ -569,7 +569,7 @@ INLINE static double eagle_metal_cooling_rate( */ INLINE static double eagle_cooling_rate( double log_u_cgs, double redshift, double n_H_cgs, - const float abundance_ratio[chemistry_element_count + 2], int n_H_index, + const float abundance_ratio[eagle_cooling_N_abundances], int n_H_index, float d_n_H, int He_index, float d_He, const struct cooling_function_data *restrict cooling, double *dLambdaNet_du) { @@ -609,7 +609,7 @@ INLINE static double eagle_metal_cooling_rate( const struct cosmology *restrict cosmo, const struct cooling_function_data *restrict cooling, const struct phys_const *restrict phys_const, - const float abundance_ratio[chemistry_element_count + 2], + const float abundance_ratio[eagle_cooling_N_abundances], float dt, int *bisection_flag) { double logu, logu_old; @@ -621,8 +621,9 @@ INLINE static double eagle_metal_cooling_rate( const float log_table_bound_low = (cooling->Therm[0] + 0.05) / M_LOG10E; /* convert Hydrogen mass fraction in Hydrogen number density */ - const float XH = - p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H]; + float const *metal_fraction = + chemistry_get_metal_mass_fraction_for_cooling(p); + const float XH = metal_fraction[chemistry_element_H]; const double n_H = hydro_get_physical_density(p, cosmo) * XH / phys_const->const_proton_mass; const double n_H_cgs = n_H * cooling->number_density_to_cgs; diff --git a/src/cooling/const_du/cooling_io.h b/src/cooling/const_du/cooling_io.h index a60aa5d282d0a244f206f74827f0c1979d3bcb75..8c82d0e3f7b134fc1fa1ee12f81474e1223912cb 100644 --- a/src/cooling/const_du/cooling_io.h +++ b/src/cooling/const_du/cooling_io.h @@ -73,9 +73,9 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, - UNIT_CONV_TEMPERATURE, parts, - xparts, convert_part_T); + list[0] = io_make_output_field_convert_part( + "Temperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, + convert_part_T, "Temperatures of the gas particles"); return 1; } diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h index 2e2ba799ab51a73c610701499ef61f1b398e42c5..5a8c2e8241e1ffea9a936df59e8f42f954a724b1 100644 --- a/src/cooling/const_lambda/cooling_io.h +++ b/src/cooling/const_lambda/cooling_io.h @@ -72,12 +72,14 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, - UNIT_CONV_TEMPERATURE, parts, - xparts, convert_part_T); + list[0] = io_make_output_field_convert_part( + "Temperature", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, + convert_part_T, "Temperatures of the gas particles"); - list[1] = io_make_output_field("RadiatedEnergy", FLOAT, 1, UNIT_CONV_ENERGY, - xparts, cooling_data.radiated_energy); + list[1] = io_make_output_field( + "RadiatedEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, xparts, + cooling_data.radiated_energy, + "Thermal energies radiated by the cooling mechanism"); return 2; } diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h index 1abbe5d3827726bde34502cd2b6a1d18cd309950..98d34dc630aa0c83198ddd60b6ee9e2775778d0f 100644 --- a/src/cooling/grackle/cooling.h +++ b/src/cooling/grackle/cooling.h @@ -52,7 +52,7 @@ static gr_float cooling_time( const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, - const struct part* restrict p, const struct xpart* restrict xp); + const struct part* restrict p, struct xpart* restrict xp); static gr_float cooling_new_energy( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, @@ -573,7 +573,7 @@ __attribute__((always_inline)) INLINE static gr_float cooling_time( const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, - const struct part* restrict p, const struct xpart* restrict xp) { + const struct part* restrict p, struct xpart* restrict xp) { /* set current time */ code_units units = cooling->units; @@ -697,6 +697,17 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( xp->cooling_data.radiated_energy -= hydro_get_mass(p) * cooling_du_dt * dt; } +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ static INLINE float cooling_get_temperature( const struct phys_const* restrict phys_const, const struct hydro_props* restrict hydro_props, @@ -704,9 +715,30 @@ static INLINE float cooling_get_temperature( const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, const struct part* restrict p, const struct xpart* restrict xp) { + // TODO use the grackle library - error("This function needs implementing!!"); - return 0.; + /* Physical constants */ + const double m_H = phys_const->const_proton_mass; + const double k_B = phys_const->const_boltzmann_k; + + /* Gas properties */ + const double T_transition = hydro_props->hydrogen_ionization_temperature; + const double mu_neutral = hydro_props->mu_neutral; + const double mu_ionised = hydro_props->mu_ionised; + + /* Particle temperature */ + const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* Temperature over mean molecular weight */ + const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; + + /* Are we above or below the HII -> HI transition? */ + if (T_over_mu > (T_transition + 1.) / mu_ionised) + return T_over_mu * mu_ionised; + else if (T_over_mu < (T_transition - 1.) / mu_neutral) + return T_over_mu * mu_neutral; + else + return T_transition; } /** diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h index 3905cafd05fb8e15ddf33f4ea688d6144698df73..18712f4cf6c74253bfed3bd36f1a4484b9e50943 100644 --- a/src/cooling/grackle/cooling_io.h +++ b/src/cooling/grackle/cooling_io.h @@ -63,23 +63,29 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( #if COOLING_GRACKLE_MODE >= 1 /* List what we want to write */ - list[0] = io_make_output_field("HI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.HI_frac); + list[0] = + io_make_output_field("HI", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.HI_frac, "HI mass fraction"); - list[1] = io_make_output_field("HII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.HII_frac); + list[1] = + io_make_output_field("HII", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.HII_frac, "HII mass fraction"); - list[2] = io_make_output_field("HeI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.HeI_frac); + list[2] = + io_make_output_field("HeI", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.HeI_frac, "HeI mass fraction"); - list[3] = io_make_output_field("HeII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.HeII_frac); + list[3] = + io_make_output_field("HeII", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.HeII_frac, "HeII mass fraction"); - list[4] = io_make_output_field("HeIII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.HeIII_frac); + list[4] = + io_make_output_field("HeIII", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.HeIII_frac, "HeIII mass fraction"); - list[5] = io_make_output_field("e", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.e_frac); + list[5] = + io_make_output_field("e", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.e_frac, "free electron mass fraction"); num += 6; #endif @@ -87,14 +93,17 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( #if COOLING_GRACKLE_MODE >= 2 list += num; - list[0] = io_make_output_field("HM", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.HM_frac); + list[0] = + io_make_output_field("HM", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.HM_frac, "H- mass fraction"); - list[1] = io_make_output_field("H2I", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.H2I_frac); + list[1] = + io_make_output_field("H2I", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.H2I_frac, "H2I mass fraction"); - list[2] = io_make_output_field("H2II", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.H2II_frac); + list[2] = + io_make_output_field("H2II", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.H2II_frac, "H2II mass fraction"); num += 3; #endif @@ -102,14 +111,17 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( #if COOLING_GRACKLE_MODE >= 3 list += num; - list[0] = io_make_output_field("DI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.DI_frac); + list[0] = + io_make_output_field("DI", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.DI_frac, "DI mass fraction"); - list[1] = io_make_output_field("DII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.DII_frac); + list[1] = + io_make_output_field("DII", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.DII_frac, "DII mass fraction"); - list[2] = io_make_output_field("HDI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts, - cooling_data.HDI_frac); + list[2] = + io_make_output_field("HDI", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + cooling_data.HDI_frac, "HDI mass fraction"); num += 3; #endif diff --git a/src/cooling/none/cooling_io.h b/src/cooling/none/cooling_io.h index 16b4b4ca29f8ebd325decc25420d7db617e1e4ef..0c551f0f6617a57b85672654dd633e4f90afc8dd 100644 --- a/src/cooling/none/cooling_io.h +++ b/src/cooling/none/cooling_io.h @@ -62,9 +62,9 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, - UNIT_CONV_TEMPERATURE, parts, - xparts, convert_part_T); + list[0] = io_make_output_field_convert_part( + "Temperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, + convert_part_T, "Temperature of the particles"); return 1; } diff --git a/src/debug.c b/src/debug.c index f8509af805fed90cbdbdfd58849cb7b6352062e9..8e3c581163bb404b8013790ef32549503c93fa38 100644 --- a/src/debug.c +++ b/src/debug.c @@ -73,6 +73,8 @@ #include "./gravity/Default/gravity_debug.h" #elif defined(POTENTIAL_GRAVITY) #include "./gravity/Potential/gravity_debug.h" +#elif defined(MULTI_SOFTENING_GRAVITY) +#include "./gravity/MultiSoftening/gravity_debug.h" #else #error "Invalid choice of gravity variant" #endif diff --git a/src/engine.c b/src/engine.c index 322866ebed7e2536b8dfc62986754f43e495d05d..d0011f4a294fccb212337f13a8eaec752d4f155e 100644 --- a/src/engine.c +++ b/src/engine.c @@ -44,11 +44,6 @@ #include <numa.h> #endif -/* Load the profiler header, if needed. */ -#ifdef WITH_PROFILER -#include <gperftools/profiler.h> -#endif - /* This object's header. */ #include "engine.h" @@ -72,7 +67,6 @@ #include "logger.h" #include "logger_io.h" #include "map.h" -#include "memswap.h" #include "memuse.h" #include "minmax.h" #include "outputlist.h" @@ -130,21 +124,8 @@ int engine_rank; /** The current step of the engine as a global variable (for messages). */ int engine_current_step; -/** - * @brief Data collected from the cells at the end of a time-step - */ -struct end_of_step_data { - - size_t updated, g_updated, s_updated, b_updated; - size_t inhibited, g_inhibited, s_inhibited, b_inhibited; - integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max; - integertime_t ti_gravity_end_min, ti_gravity_end_max, ti_gravity_beg_max; - integertime_t ti_stars_end_min, ti_stars_end_max, ti_stars_beg_max; - integertime_t ti_black_holes_end_min, ti_black_holes_end_max, - ti_black_holes_beg_max; - struct engine *e; - struct star_formation_history sfh; -}; +extern int engine_max_parts_per_ghost; +extern int engine_max_sparts_per_ghost; /** * @brief Link a density/force task to a cell. @@ -177,1092 +158,6 @@ void engine_addlink(struct engine *e, struct link **l, struct task *t) { res->next = atomic_swap(l, res); } -#ifdef WITH_MPI -/** - * Do the exchange of one type of particles with all the other nodes. - * - * @param label a label for the memory allocations of this particle type. - * @param counts 2D array with the counts of particles to exchange with - * each other node. - * @param parts the particle data to exchange - * @param new_nr_parts the number of particles this node will have after all - * exchanges have completed. - * @param sizeofparts sizeof the particle struct. - * @param alignsize the memory alignment required for this particle type. - * @param mpi_type the MPI_Datatype for these particles. - * @param nr_nodes the number of nodes to exchange with. - * @param nodeID the id of this node. - * @param syncredist whether to use slower more memory friendly synchronous - * exchanges. - * - * @result new particle data constructed from all the exchanges with the - * given alignment. - */ -static void *engine_do_redistribute(const char *label, int *counts, char *parts, - size_t new_nr_parts, size_t sizeofparts, - size_t alignsize, MPI_Datatype mpi_type, - int nr_nodes, int nodeID, int syncredist) { - - /* Allocate a new particle array with some extra margin */ - char *parts_new = NULL; - if (swift_memalign( - label, (void **)&parts_new, alignsize, - sizeofparts * new_nr_parts * engine_redistribute_alloc_margin) != 0) - error("Failed to allocate new particle data."); - - if (syncredist) { - - /* Slow synchronous redistribute,. */ - size_t offset_send = 0, offset_recv = 0; - - /* Only send and receive only "chunk" particles per request. - * Fixing the message size to 2GB. */ - const int chunk = INT_MAX / sizeofparts; - int res = 0; - for (int k = 0; k < nr_nodes; k++) { - int kk = k; - - /* Rank 0 decides the index of sending node */ - MPI_Bcast(&kk, 1, MPI_INT, 0, MPI_COMM_WORLD); - - int ind_recv = kk * nr_nodes + nodeID; - - if ( nodeID == kk ) { - - /* Send out our particles. */ - offset_send = 0; - for (int j = 0; j < nr_nodes; j++) { - - int ind_send = kk * nr_nodes + j; - - /* Just copy our own parts */ - if ( counts[ind_send] > 0 ) { - if ( j == nodeID ) { - memcpy(&parts_new[offset_recv * sizeofparts], - &parts[offset_send * sizeofparts], - sizeofparts * counts[ind_recv]); - offset_send += counts[ind_send]; - offset_recv += counts[ind_recv]; - } - else { - for (int i = 0, n = 0; i < counts[ind_send]; n++) { - - /* Count and index, with chunk parts at most. */ - size_t sendc = min(chunk, counts[ind_send] - i); - size_t sendo = offset_send + i; - - res = MPI_Send(&parts[sendo * sizeofparts], sendc, mpi_type, j, - n, MPI_COMM_WORLD); - if ( res != MPI_SUCCESS ) { - mpi_error(res, "Failed to send parts to node %i from %i.", - j, nodeID); - } - i += sendc; - } - offset_send += counts[ind_send]; - } - } - } - } - else { - /* Listen for sends from kk. */ - if (counts[ind_recv] > 0) { - for (int i = 0, n = 0; i < counts[ind_recv]; n++) { - /* Count and index, with +chunk parts at most. */ - size_t recvc = min(chunk, counts[ind_recv] - i); - size_t recvo = offset_recv + i; - - MPI_Status status; - res = MPI_Recv(&parts_new[recvo * sizeofparts], recvc, mpi_type, kk, - n, MPI_COMM_WORLD, &status); - if ( res != MPI_SUCCESS ) { - mpi_error(res,"Failed to recv of parts from node %i to %i.", - kk, nodeID); - } - i += recvc; - } - offset_recv += counts[ind_recv]; - } - } - } - - } else { - /* Asynchronous redistribute, can take a lot of memory. */ - - /* Prepare MPI requests for the asynchronous communications */ - MPI_Request *reqs; - if ((reqs = (MPI_Request *)malloc(sizeof(MPI_Request) * 2 * nr_nodes)) == - NULL) - error("Failed to allocate MPI request list."); - - /* Only send and receive only "chunk" particles per request. So we need to - * loop as many times as necessary here. Make 2Gb/sizeofparts so we only - * send 2Gb packets. */ - const int chunk = INT_MAX / sizeofparts; - int sent = 0; - int recvd = 0; - - int activenodes = 1; - while (activenodes) { - - for (int k = 0; k < 2 * nr_nodes; k++) reqs[k] = MPI_REQUEST_NULL; - - /* Emit the sends and recvs for the data. */ - size_t offset_send = sent; - size_t offset_recv = recvd; - activenodes = 0; - - for (int k = 0; k < nr_nodes; k++) { - - /* Indices in the count arrays of the node of interest */ - const int ind_send = nodeID * nr_nodes + k; - const int ind_recv = k * nr_nodes + nodeID; - - /* Are we sending any data this loop? */ - int sending = counts[ind_send] - sent; - if (sending > 0) { - activenodes++; - if (sending > chunk) sending = chunk; - - /* If the send and receive is local then just copy. */ - if (k == nodeID) { - int receiving = counts[ind_recv] - recvd; - if (receiving > chunk) receiving = chunk; - memcpy(&parts_new[offset_recv * sizeofparts], - &parts[offset_send * sizeofparts], sizeofparts * receiving); - } else { - /* Otherwise send it. */ - int res = - MPI_Isend(&parts[offset_send * sizeofparts], sending, mpi_type, k, - ind_send, MPI_COMM_WORLD, &reqs[2 * k + 0]); - if (res != MPI_SUCCESS) - mpi_error(res, "Failed to isend parts to node %i.", k); - } - } - - /* If we're sending to this node, then move past it to next. */ - if (counts[ind_send] > 0) offset_send += counts[ind_send]; - - /* Are we receiving any data from this node? Note already done if coming - * from this node. */ - if (k != nodeID) { - int receiving = counts[ind_recv] - recvd; - if (receiving > 0) { - activenodes++; - if (receiving > chunk) receiving = chunk; - int res = MPI_Irecv(&parts_new[offset_recv * sizeofparts], receiving, - mpi_type, k, ind_recv, MPI_COMM_WORLD, - &reqs[2 * k + 1]); - if (res != MPI_SUCCESS) - mpi_error(res, "Failed to emit irecv of parts from node %i.", k); - } - } - - /* If we're receiving from this node, then move past it to next. */ - if (counts[ind_recv] > 0) offset_recv += counts[ind_recv]; - } - - /* Wait for all the sends and recvs to tumble in. */ - MPI_Status stats[2 * nr_nodes]; - int res; - if ((res = MPI_Waitall(2 * nr_nodes, reqs, stats)) != MPI_SUCCESS) { - for (int k = 0; k < 2 * nr_nodes; k++) { - char buff[MPI_MAX_ERROR_STRING]; - MPI_Error_string(stats[k].MPI_ERROR, buff, &res); - message("request from source %i, tag %i has error '%s'.", - stats[k].MPI_SOURCE, stats[k].MPI_TAG, buff); - } - error("Failed during waitall for part data."); - } - - /* Move to next chunks. */ - sent += chunk; - recvd += chunk; - } - - /* Free temps. */ - free(reqs); - } - - /* And return new memory. */ - return parts_new; -} -#endif - -#ifdef WITH_MPI /* redist_mapper */ - -/* Support for engine_redistribute threadpool dest mappers. */ -struct redist_mapper_data { - int *counts; - int *dest; - int nodeID; - int nr_nodes; - struct cell *cells; - struct space *s; - void *base; -}; - -/* Generic function for accumulating counts for TYPE parts. Note - * we use a local counts array to avoid the atomic_add in the parts - * loop. */ -#define ENGINE_REDISTRIBUTE_DEST_MAPPER(TYPE) \ - engine_redistribute_dest_mapper_##TYPE(void *map_data, int num_elements, \ - void *extra_data) { \ - struct TYPE *parts = (struct TYPE *)map_data; \ - struct redist_mapper_data *mydata = \ - (struct redist_mapper_data *)extra_data; \ - struct space *s = mydata->s; \ - int *dest = \ - mydata->dest + (ptrdiff_t)(parts - (struct TYPE *)mydata->base); \ - int *lcounts = NULL; \ - if ((lcounts = (int *)calloc( \ - sizeof(int), mydata->nr_nodes * mydata->nr_nodes)) == NULL) \ - error("Failed to allocate counts thread-specific buffer"); \ - for (int k = 0; k < num_elements; k++) { \ - for (int j = 0; j < 3; j++) { \ - if (parts[k].x[j] < 0.0) \ - parts[k].x[j] += s->dim[j]; \ - else if (parts[k].x[j] >= s->dim[j]) \ - parts[k].x[j] -= s->dim[j]; \ - } \ - const int cid = cell_getid(s->cdim, parts[k].x[0] * s->iwidth[0], \ - parts[k].x[1] * s->iwidth[1], \ - parts[k].x[2] * s->iwidth[2]); \ - dest[k] = s->cells_top[cid].nodeID; \ - size_t ind = mydata->nodeID * mydata->nr_nodes + dest[k]; \ - lcounts[ind] += 1; \ - } \ - for (int k = 0; k < (mydata->nr_nodes * mydata->nr_nodes); k++) \ - atomic_add(&mydata->counts[k], lcounts[k]); \ - free(lcounts); \ - } - -/** - * @brief Accumulate the counts of particles per cell. - * Threadpool helper for accumulating the counts of particles per cell. - * - * part version. - */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(part); - -/** - * @brief Accumulate the counts of star particles per cell. - * Threadpool helper for accumulating the counts of particles per cell. - * - * spart version. - */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(spart); - -/** - * @brief Accumulate the counts of gravity particles per cell. - * Threadpool helper for accumulating the counts of particles per cell. - * - * gpart version. - */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(gpart); - -/** - * @brief Accumulate the counts of black holes particles per cell. - * Threadpool helper for accumulating the counts of particles per cell. - * - * bpart version. - */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(bpart); - -#endif /* redist_mapper_data */ - -#ifdef WITH_MPI /* savelink_mapper_data */ - -/* Support for saving the linkage between gparts and parts/sparts. */ -struct savelink_mapper_data { - int nr_nodes; - int *counts; - void *parts; - int nodeID; -}; - -/** - * @brief Save the offset of each gravity partner of a part or spart. - * - * The offset is from the start of the sorted particles to be sent to a node. - * This is possible as parts without gravity partners have a positive id. - * These offsets are used to restore the pointers on the receiving node. - * - * CHECKS should be eliminated as dead code when optimizing. - */ -#define ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(TYPE, CHECKS) \ - engine_redistribute_savelink_mapper_##TYPE(void *map_data, int num_elements, \ - void *extra_data) { \ - int *nodes = (int *)map_data; \ - struct savelink_mapper_data *mydata = \ - (struct savelink_mapper_data *)extra_data; \ - int nodeID = mydata->nodeID; \ - int nr_nodes = mydata->nr_nodes; \ - int *counts = mydata->counts; \ - struct TYPE *parts = (struct TYPE *)mydata->parts; \ - \ - for (int j = 0; j < num_elements; j++) { \ - int node = nodes[j]; \ - int count = 0; \ - size_t offset = 0; \ - for (int i = 0; i < node; i++) offset += counts[nodeID * nr_nodes + i]; \ - \ - for (int k = 0; k < counts[nodeID * nr_nodes + node]; k++) { \ - if (parts[k + offset].gpart != NULL) { \ - if (CHECKS) \ - if (parts[k + offset].gpart->id_or_neg_offset > 0) \ - error("Trying to link a partnerless " #TYPE "!"); \ - parts[k + offset].gpart->id_or_neg_offset = -count; \ - count++; \ - } \ - } \ - } \ - } - -/** - * @brief Save position of part-gpart links. - * Threadpool helper for accumulating the counts of particles per cell. - */ -#ifdef SWIFT_DEBUG_CHECKS -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 1); -#else -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 0); -#endif - -/** - * @brief Save position of spart-gpart links. - * Threadpool helper for accumulating the counts of particles per cell. - */ -#ifdef SWIFT_DEBUG_CHECKS -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 1); -#else -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 0); -#endif - -/** - * @brief Save position of bpart-gpart links. - * Threadpool helper for accumulating the counts of particles per cell. - */ -#ifdef SWIFT_DEBUG_CHECKS -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 1); -#else -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 0); -#endif - -#endif /* savelink_mapper_data */ - -#ifdef WITH_MPI /* relink_mapper_data */ - -/* Support for relinking parts, gparts, sparts and bparts after moving between - * nodes. */ -struct relink_mapper_data { - int nodeID; - int nr_nodes; - int *counts; - int *s_counts; - int *g_counts; - int *b_counts; - struct space *s; -}; - -/** - * @brief Restore the part/gpart and spart/gpart links for a list of nodes. - * - * @param map_data address of nodes to process. - * @param num_elements the number nodes to process. - * @param extra_data additional data defining the context (a - * relink_mapper_data). - */ -static void engine_redistribute_relink_mapper(void *map_data, int num_elements, - void *extra_data) { - - int *nodes = (int *)map_data; - struct relink_mapper_data *mydata = (struct relink_mapper_data *)extra_data; - - int nodeID = mydata->nodeID; - int nr_nodes = mydata->nr_nodes; - int *counts = mydata->counts; - int *g_counts = mydata->g_counts; - int *s_counts = mydata->s_counts; - int *b_counts = mydata->b_counts; - struct space *s = mydata->s; - - for (int i = 0; i < num_elements; i++) { - - int node = nodes[i]; - - /* Get offsets to correct parts of the counts arrays for this node. */ - size_t offset_parts = 0; - size_t offset_gparts = 0; - size_t offset_sparts = 0; - size_t offset_bparts = 0; - for (int n = 0; n < node; n++) { - int ind_recv = n * nr_nodes + nodeID; - offset_parts += counts[ind_recv]; - offset_gparts += g_counts[ind_recv]; - offset_sparts += s_counts[ind_recv]; - offset_bparts += b_counts[ind_recv]; - } - - /* Number of gparts sent from this node. */ - int ind_recv = node * nr_nodes + nodeID; - const size_t count_gparts = g_counts[ind_recv]; - - /* Loop over the gparts received from this node */ - for (size_t k = offset_gparts; k < offset_gparts + count_gparts; k++) { - - /* Does this gpart have a gas partner ? */ - if (s->gparts[k].type == swift_type_gas) { - - const ptrdiff_t partner_index = - offset_parts - s->gparts[k].id_or_neg_offset; - - /* Re-link */ - s->gparts[k].id_or_neg_offset = -partner_index; - s->parts[partner_index].gpart = &s->gparts[k]; - } - - /* Does this gpart have a star partner ? */ - else if (s->gparts[k].type == swift_type_stars) { - - const ptrdiff_t partner_index = - offset_sparts - s->gparts[k].id_or_neg_offset; - - /* Re-link */ - s->gparts[k].id_or_neg_offset = -partner_index; - s->sparts[partner_index].gpart = &s->gparts[k]; - } - - /* Does this gpart have a black hole partner ? */ - else if (s->gparts[k].type == swift_type_black_hole) { - - const ptrdiff_t partner_index = - offset_bparts - s->gparts[k].id_or_neg_offset; - - /* Re-link */ - s->gparts[k].id_or_neg_offset = -partner_index; - s->bparts[partner_index].gpart = &s->gparts[k]; - } - } - } -} - -#endif /* relink_mapper_data */ - -/** - * @brief Redistribute the particles amongst the nodes according - * to their cell's node IDs. - * - * The strategy here is as follows: - * 1) Each node counts the number of particles it has to send to each other - * node. - * 2) The number of particles of each type is then exchanged. - * 3) The particles to send are placed in a temporary buffer in which the - * part-gpart links are preserved. - * 4) Each node allocates enough space for the new particles. - * 5) Asynchronous or synchronous communications are issued to transfer the data. - * - * - * @param e The #engine. - */ -void engine_redistribute(struct engine *e) { - -#ifdef WITH_MPI - - const int nr_nodes = e->nr_nodes; - const int nodeID = e->nodeID; - struct space *s = e->s; - struct cell *cells = s->cells_top; - const int nr_cells = s->nr_cells; - struct xpart *xparts = s->xparts; - struct part *parts = s->parts; - struct gpart *gparts = s->gparts; - struct spart *sparts = s->sparts; - struct bpart *bparts = s->bparts; - ticks tic = getticks(); - - size_t nr_parts = s->nr_parts; - size_t nr_gparts = s->nr_gparts; - size_t nr_sparts = s->nr_sparts; - size_t nr_bparts = s->nr_bparts; - - /* Start by moving inhibited particles to the end of the arrays */ - for (size_t k = 0; k < nr_parts; /* void */) { - if (parts[k].time_bin == time_bin_inhibited || - parts[k].time_bin == time_bin_not_created) { - nr_parts -= 1; - - /* Swap the particle */ - memswap(&parts[k], &parts[nr_parts], sizeof(struct part)); - - /* Swap the xpart */ - memswap(&xparts[k], &xparts[nr_parts], sizeof(struct xpart)); - - /* Swap the link with the gpart */ - if (parts[k].gpart != NULL) { - parts[k].gpart->id_or_neg_offset = -k; - } - if (parts[nr_parts].gpart != NULL) { - parts[nr_parts].gpart->id_or_neg_offset = -nr_parts; - } - } else { - k++; - } - } - - /* Now move inhibited star particles to the end of the arrays */ - for (size_t k = 0; k < nr_sparts; /* void */) { - if (sparts[k].time_bin == time_bin_inhibited || - sparts[k].time_bin == time_bin_not_created) { - nr_sparts -= 1; - - /* Swap the particle */ - memswap(&s->sparts[k], &s->sparts[nr_sparts], sizeof(struct spart)); - - /* Swap the link with the gpart */ - if (s->sparts[k].gpart != NULL) { - s->sparts[k].gpart->id_or_neg_offset = -k; - } - if (s->sparts[nr_sparts].gpart != NULL) { - s->sparts[nr_sparts].gpart->id_or_neg_offset = -nr_sparts; - } - } else { - k++; - } - } - - /* Now move inhibited black hole particles to the end of the arrays */ - for (size_t k = 0; k < nr_bparts; /* void */) { - if (bparts[k].time_bin == time_bin_inhibited || - bparts[k].time_bin == time_bin_not_created) { - nr_bparts -= 1; - - /* Swap the particle */ - memswap(&s->bparts[k], &s->bparts[nr_bparts], sizeof(struct bpart)); - - /* Swap the link with the gpart */ - if (s->bparts[k].gpart != NULL) { - s->bparts[k].gpart->id_or_neg_offset = -k; - } - if (s->bparts[nr_bparts].gpart != NULL) { - s->bparts[nr_bparts].gpart->id_or_neg_offset = -nr_bparts; - } - } else { - k++; - } - } - - /* Finally do the same with the gravity particles */ - for (size_t k = 0; k < nr_gparts; /* void */) { - if (gparts[k].time_bin == time_bin_inhibited || - gparts[k].time_bin == time_bin_not_created) { - nr_gparts -= 1; - - /* Swap the particle */ - memswap(&s->gparts[k], &s->gparts[nr_gparts], sizeof(struct gpart)); - - /* Swap the link with part/spart */ - if (s->gparts[k].type == swift_type_gas) { - s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; - } else if (s->gparts[k].type == swift_type_stars) { - s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; - } else if (s->gparts[k].type == swift_type_black_hole) { - s->bparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; - } - - if (s->gparts[nr_gparts].type == swift_type_gas) { - s->parts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = - &s->gparts[nr_gparts]; - } else if (s->gparts[nr_gparts].type == swift_type_stars) { - s->sparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = - &s->gparts[nr_gparts]; - } else if (s->gparts[nr_gparts].type == swift_type_black_hole) { - s->bparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = - &s->gparts[nr_gparts]; - } - } else { - k++; - } - } - - /* Now we are ready to deal with real particles and can start the exchange. */ - - /* Allocate temporary arrays to store the counts of particles to be sent - * and the destination of each particle */ - int *counts; - if ((counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) - error("Failed to allocate counts temporary buffer."); - - int *dest; - if ((dest = (int *)swift_malloc("dest", sizeof(int) * nr_parts)) == NULL) - error("Failed to allocate dest temporary buffer."); - - /* Simple index of node IDs, used for mappers over nodes. */ - int *nodes = NULL; - if ((nodes = (int *)malloc(sizeof(int) * nr_nodes)) == NULL) - error("Failed to allocate nodes temporary buffer."); - for (int k = 0; k < nr_nodes; k++) nodes[k] = k; - - /* Get destination of each particle */ - struct redist_mapper_data redist_data; - redist_data.s = s; - redist_data.nodeID = nodeID; - redist_data.nr_nodes = nr_nodes; - - redist_data.counts = counts; - redist_data.dest = dest; - redist_data.base = (void *)parts; - - threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_part, parts, - nr_parts, sizeof(struct part), 0, &redist_data); - - /* Sort the particles according to their cell index. */ - if (nr_parts > 0) - space_parts_sort(s->parts, s->xparts, dest, &counts[nodeID * nr_nodes], - nr_nodes, 0); - -#ifdef SWIFT_DEBUG_CHECKS - /* Verify that the part have been sorted correctly. */ - for (size_t k = 0; k < nr_parts; k++) { - const struct part *p = &s->parts[k]; - - if (p->time_bin == time_bin_inhibited) - error("Inhibited particle found after sorting!"); - - if (p->time_bin == time_bin_not_created) - error("Inhibited particle found after sorting!"); - - /* New cell index */ - const int new_cid = - cell_getid(s->cdim, p->x[0] * s->iwidth[0], p->x[1] * s->iwidth[1], - p->x[2] * s->iwidth[2]); - - /* New cell of this part */ - const struct cell *c = &s->cells_top[new_cid]; - const int new_node = c->nodeID; - - if (dest[k] != new_node) - error("part's new node index not matching sorted index."); - - if (p->x[0] < c->loc[0] || p->x[0] > c->loc[0] + c->width[0] || - p->x[1] < c->loc[1] || p->x[1] > c->loc[1] + c->width[1] || - p->x[2] < c->loc[2] || p->x[2] > c->loc[2] + c->width[2]) - error("part not sorted into the right top-level cell!"); - } -#endif - - /* We will need to re-link the gpart partners of parts, so save their - * relative positions in the sent lists. */ - if (nr_parts > 0 && nr_gparts > 0) { - - struct savelink_mapper_data savelink_data; - savelink_data.nr_nodes = nr_nodes; - savelink_data.counts = counts; - savelink_data.parts = (void *)parts; - savelink_data.nodeID = nodeID; - threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_part, - nodes, nr_nodes, sizeof(int), 0, &savelink_data); - } - swift_free("dest", dest); - - /* Get destination of each s-particle */ - int *s_counts; - if ((s_counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) - error("Failed to allocate s_counts temporary buffer."); - - int *s_dest; - if ((s_dest = (int *)swift_malloc("s_dest", sizeof(int) * nr_sparts)) == NULL) - error("Failed to allocate s_dest temporary buffer."); - - redist_data.counts = s_counts; - redist_data.dest = s_dest; - redist_data.base = (void *)sparts; - - threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_spart, sparts, - nr_sparts, sizeof(struct spart), 0, &redist_data); - - /* Sort the particles according to their cell index. */ - if (nr_sparts > 0) - space_sparts_sort(s->sparts, s_dest, &s_counts[nodeID * nr_nodes], nr_nodes, - 0); - -#ifdef SWIFT_DEBUG_CHECKS - /* Verify that the spart have been sorted correctly. */ - for (size_t k = 0; k < nr_sparts; k++) { - const struct spart *sp = &s->sparts[k]; - - if (sp->time_bin == time_bin_inhibited) - error("Inhibited particle found after sorting!"); - - if (sp->time_bin == time_bin_not_created) - error("Inhibited particle found after sorting!"); - - /* New cell index */ - const int new_cid = - cell_getid(s->cdim, sp->x[0] * s->iwidth[0], sp->x[1] * s->iwidth[1], - sp->x[2] * s->iwidth[2]); - - /* New cell of this spart */ - const struct cell *c = &s->cells_top[new_cid]; - const int new_node = c->nodeID; - - if (s_dest[k] != new_node) - error("spart's new node index not matching sorted index."); - - if (sp->x[0] < c->loc[0] || sp->x[0] > c->loc[0] + c->width[0] || - sp->x[1] < c->loc[1] || sp->x[1] > c->loc[1] + c->width[1] || - sp->x[2] < c->loc[2] || sp->x[2] > c->loc[2] + c->width[2]) - error("spart not sorted into the right top-level cell!"); - } -#endif - - /* We need to re-link the gpart partners of sparts. */ - if (nr_sparts > 0) { - - struct savelink_mapper_data savelink_data; - savelink_data.nr_nodes = nr_nodes; - savelink_data.counts = s_counts; - savelink_data.parts = (void *)sparts; - savelink_data.nodeID = nodeID; - threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_spart, - nodes, nr_nodes, sizeof(int), 0, &savelink_data); - } - swift_free("s_dest", s_dest); - - /* Get destination of each b-particle */ - int *b_counts; - if ((b_counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) - error("Failed to allocate b_counts temporary buffer."); - - int *b_dest; - if ((b_dest = (int *)swift_malloc("b_dest", sizeof(int) * nr_bparts)) == NULL) - error("Failed to allocate b_dest temporary buffer."); - - redist_data.counts = b_counts; - redist_data.dest = b_dest; - redist_data.base = (void *)bparts; - - threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_bpart, bparts, - nr_bparts, sizeof(struct bpart), 0, &redist_data); - - /* Sort the particles according to their cell index. */ - if (nr_bparts > 0) - space_bparts_sort(s->bparts, b_dest, &b_counts[nodeID * nr_nodes], nr_nodes, - 0); - -#ifdef SWIFT_DEBUG_CHECKS - /* Verify that the bpart have been sorted correctly. */ - for (size_t k = 0; k < nr_bparts; k++) { - const struct bpart *bp = &s->bparts[k]; - - if (bp->time_bin == time_bin_inhibited) - error("Inhibited particle found after sorting!"); - - if (bp->time_bin == time_bin_not_created) - error("Inhibited particle found after sorting!"); - - /* New cell index */ - const int new_cid = - cell_getid(s->cdim, bp->x[0] * s->iwidth[0], bp->x[1] * s->iwidth[1], - bp->x[2] * s->iwidth[2]); - - /* New cell of this bpart */ - const struct cell *c = &s->cells_top[new_cid]; - const int new_node = c->nodeID; - - if (b_dest[k] != new_node) - error("bpart's new node index not matching sorted index."); - - if (bp->x[0] < c->loc[0] || bp->x[0] > c->loc[0] + c->width[0] || - bp->x[1] < c->loc[1] || bp->x[1] > c->loc[1] + c->width[1] || - bp->x[2] < c->loc[2] || bp->x[2] > c->loc[2] + c->width[2]) - error("bpart not sorted into the right top-level cell!"); - } -#endif - - /* We need to re-link the gpart partners of bparts. */ - if (nr_bparts > 0) { - - struct savelink_mapper_data savelink_data; - savelink_data.nr_nodes = nr_nodes; - savelink_data.counts = b_counts; - savelink_data.parts = (void *)bparts; - savelink_data.nodeID = nodeID; - threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_bpart, - nodes, nr_nodes, sizeof(int), 0, &savelink_data); - } - swift_free("b_dest", b_dest); - - /* Get destination of each g-particle */ - int *g_counts; - if ((g_counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) - error("Failed to allocate g_gcount temporary buffer."); - - int *g_dest; - if ((g_dest = (int *)swift_malloc("g_dest", sizeof(int) * nr_gparts)) == NULL) - error("Failed to allocate g_dest temporary buffer."); - - redist_data.counts = g_counts; - redist_data.dest = g_dest; - redist_data.base = (void *)gparts; - - threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_gpart, gparts, - nr_gparts, sizeof(struct gpart), 0, &redist_data); - - /* Sort the gparticles according to their cell index. */ - if (nr_gparts > 0) - space_gparts_sort(s->gparts, s->parts, s->sparts, s->bparts, g_dest, - &g_counts[nodeID * nr_nodes], nr_nodes); - -#ifdef SWIFT_DEBUG_CHECKS - /* Verify that the gpart have been sorted correctly. */ - for (size_t k = 0; k < nr_gparts; k++) { - const struct gpart *gp = &s->gparts[k]; - - if (gp->time_bin == time_bin_inhibited) - error("Inhibited particle found after sorting!"); - - if (gp->time_bin == time_bin_not_created) - error("Inhibited particle found after sorting!"); - - /* New cell index */ - const int new_cid = - cell_getid(s->cdim, gp->x[0] * s->iwidth[0], gp->x[1] * s->iwidth[1], - gp->x[2] * s->iwidth[2]); - - /* New cell of this gpart */ - const struct cell *c = &s->cells_top[new_cid]; - const int new_node = c->nodeID; - - if (g_dest[k] != new_node) - error("gpart's new node index not matching sorted index (%d != %d).", - g_dest[k], new_node); - - if (gp->x[0] < c->loc[0] || gp->x[0] > c->loc[0] + c->width[0] || - gp->x[1] < c->loc[1] || gp->x[1] > c->loc[1] + c->width[1] || - gp->x[2] < c->loc[2] || gp->x[2] > c->loc[2] + c->width[2]) - error("gpart not sorted into the right top-level cell!"); - } -#endif - - swift_free("g_dest", g_dest); - - /* Get all the counts from all the nodes. */ - if (MPI_Allreduce(MPI_IN_PLACE, counts, nr_nodes * nr_nodes, MPI_INT, MPI_SUM, - MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to allreduce particle transfer counts."); - - /* Get all the g_counts from all the nodes. */ - if (MPI_Allreduce(MPI_IN_PLACE, g_counts, nr_nodes * nr_nodes, MPI_INT, - MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to allreduce gparticle transfer counts."); - - /* Get all the s_counts from all the nodes. */ - if (MPI_Allreduce(MPI_IN_PLACE, s_counts, nr_nodes * nr_nodes, MPI_INT, - MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to allreduce sparticle transfer counts."); - - /* Get all the b_counts from all the nodes. */ - if (MPI_Allreduce(MPI_IN_PLACE, b_counts, nr_nodes * nr_nodes, MPI_INT, - MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to allreduce bparticle transfer counts."); - - /* Report how many particles will be moved. */ - if (e->verbose) { - if (e->nodeID == 0) { - size_t total = 0, g_total = 0, s_total = 0, b_total = 0; - size_t unmoved = 0, g_unmoved = 0, s_unmoved = 0, b_unmoved = 0; - for (int p = 0, r = 0; p < nr_nodes; p++) { - for (int n = 0; n < nr_nodes; n++) { - total += counts[r]; - g_total += g_counts[r]; - s_total += s_counts[r]; - b_total += b_counts[r]; - if (p == n) { - unmoved += counts[r]; - g_unmoved += g_counts[r]; - s_unmoved += s_counts[r]; - b_unmoved += b_counts[r]; - } - r++; - } - } - if (total > 0) - message("%zu of %zu (%.2f%%) of particles moved", total - unmoved, - total, 100.0 * (double)(total - unmoved) / (double)total); - if (g_total > 0) - message("%zu of %zu (%.2f%%) of g-particles moved", g_total - g_unmoved, - g_total, - 100.0 * (double)(g_total - g_unmoved) / (double)g_total); - if (s_total > 0) - message("%zu of %zu (%.2f%%) of s-particles moved", s_total - s_unmoved, - s_total, - 100.0 * (double)(s_total - s_unmoved) / (double)s_total); - if (b_total > 0) - message("%ld of %ld (%.2f%%) of b-particles moved", b_total - b_unmoved, - b_total, - 100.0 * (double)(b_total - b_unmoved) / (double)b_total); - } - } - - /* Now each node knows how many parts, sparts, bparts, and gparts will be - * transferred to every other node. Get the new numbers of particles for this - * node. */ - size_t nr_parts_new = 0, nr_gparts_new = 0, nr_sparts_new = 0, - nr_bparts_new = 0; - for (int k = 0; k < nr_nodes; k++) - nr_parts_new += counts[k * nr_nodes + nodeID]; - for (int k = 0; k < nr_nodes; k++) - nr_gparts_new += g_counts[k * nr_nodes + nodeID]; - for (int k = 0; k < nr_nodes; k++) - nr_sparts_new += s_counts[k * nr_nodes + nodeID]; - for (int k = 0; k < nr_nodes; k++) - nr_bparts_new += b_counts[k * nr_nodes + nodeID]; - - /* Now exchange the particles, type by type to keep the memory required - * under control. */ - - /* SPH particles. */ - void *new_parts = engine_do_redistribute( - "parts", counts, (char *)s->parts, nr_parts_new, sizeof(struct part), - part_align, part_mpi_type, nr_nodes, nodeID, e->syncredist); - swift_free("parts", s->parts); - s->parts = (struct part *)new_parts; - s->nr_parts = nr_parts_new; - s->size_parts = engine_redistribute_alloc_margin * nr_parts_new; - - /* Extra SPH particle properties. */ - new_parts = engine_do_redistribute( - "xparts", counts, (char *)s->xparts, nr_parts_new, sizeof(struct xpart), - xpart_align, xpart_mpi_type, nr_nodes, nodeID, e->syncredist); - swift_free("xparts", s->xparts); - s->xparts = (struct xpart *)new_parts; - - /* Gravity particles. */ - new_parts = engine_do_redistribute( - "gparts", g_counts, (char *)s->gparts, nr_gparts_new, - sizeof(struct gpart), gpart_align, gpart_mpi_type, nr_nodes, nodeID, - e->syncredist); - swift_free("gparts", s->gparts); - s->gparts = (struct gpart *)new_parts; - s->nr_gparts = nr_gparts_new; - s->size_gparts = engine_redistribute_alloc_margin * nr_gparts_new; - - /* Star particles. */ - new_parts = engine_do_redistribute( - "sparts", s_counts, (char *)s->sparts, nr_sparts_new, - sizeof(struct spart), spart_align, spart_mpi_type, nr_nodes, nodeID, - e->syncredist); - swift_free("sparts", s->sparts); - s->sparts = (struct spart *)new_parts; - s->nr_sparts = nr_sparts_new; - s->size_sparts = engine_redistribute_alloc_margin * nr_sparts_new; - - /* Black holes particles. */ - new_parts = engine_do_redistribute( - "bparts", b_counts, (char *)s->bparts, nr_bparts_new, - sizeof(struct bpart), bpart_align, bpart_mpi_type, nr_nodes, nodeID, - e->syncredist); - swift_free("bparts", s->bparts); - s->bparts = (struct bpart *)new_parts; - s->nr_bparts = nr_bparts_new; - s->size_bparts = engine_redistribute_alloc_margin * nr_bparts_new; - - /* All particles have now arrived. Time for some final operations on the - stuff we just received */ - - /* Restore the part<->gpart and spart<->gpart links. - * Generate indices and counts for threadpool tasks. Note we process a node - * at a time. */ - struct relink_mapper_data relink_data; - relink_data.s = s; - relink_data.counts = counts; - relink_data.g_counts = g_counts; - relink_data.s_counts = s_counts; - relink_data.b_counts = b_counts; - relink_data.nodeID = nodeID; - relink_data.nr_nodes = nr_nodes; - - threadpool_map(&e->threadpool, engine_redistribute_relink_mapper, nodes, - nr_nodes, sizeof(int), 1, &relink_data); - free(nodes); - - /* Clean up the counts now we are done. */ - free(counts); - free(g_counts); - free(s_counts); - free(b_counts); - -#ifdef SWIFT_DEBUG_CHECKS - /* Verify that all parts are in the right place. */ - for (size_t k = 0; k < nr_parts_new; k++) { - const int cid = cell_getid(s->cdim, s->parts[k].x[0] * s->iwidth[0], - s->parts[k].x[1] * s->iwidth[1], - s->parts[k].x[2] * s->iwidth[2]); - if (cells[cid].nodeID != nodeID) - error("Received particle (%zu) that does not belong here (nodeID=%i).", k, - cells[cid].nodeID); - } - for (size_t k = 0; k < nr_gparts_new; k++) { - const int cid = cell_getid(s->cdim, s->gparts[k].x[0] * s->iwidth[0], - s->gparts[k].x[1] * s->iwidth[1], - s->gparts[k].x[2] * s->iwidth[2]); - if (cells[cid].nodeID != nodeID) - error("Received g-particle (%zu) that does not belong here (nodeID=%i).", - k, cells[cid].nodeID); - } - for (size_t k = 0; k < nr_sparts_new; k++) { - const int cid = cell_getid(s->cdim, s->sparts[k].x[0] * s->iwidth[0], - s->sparts[k].x[1] * s->iwidth[1], - s->sparts[k].x[2] * s->iwidth[2]); - if (cells[cid].nodeID != nodeID) - error("Received s-particle (%zu) that does not belong here (nodeID=%i).", - k, cells[cid].nodeID); - } - for (size_t k = 0; k < nr_bparts_new; k++) { - const int cid = cell_getid(s->cdim, s->bparts[k].x[0] * s->iwidth[0], - s->bparts[k].x[1] * s->iwidth[1], - s->bparts[k].x[2] * s->iwidth[2]); - if (cells[cid].nodeID != nodeID) - error("Received b-particle (%zu) that does not belong here (nodeID=%i).", - k, cells[cid].nodeID); - } - - /* Verify that the links are correct */ - part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, nr_parts_new, - nr_gparts_new, nr_sparts_new, nr_bparts_new, e->verbose); - -#endif - - /* Be verbose about what just happened. */ - if (e->verbose) { - int my_cells = 0; - for (int k = 0; k < nr_cells; k++) - if (cells[k].nodeID == nodeID) my_cells += 1; - message( - "node %i now has %zu parts, %zu sparts, %zu bparts and %zu gparts in " - "%i cells.", - nodeID, nr_parts_new, nr_sparts_new, nr_bparts_new, nr_gparts_new, - my_cells); - } - - /* Flag that we do not have any extra particles any more */ - s->nr_extra_parts = 0; - s->nr_extra_gparts = 0; - s->nr_extra_sparts = 0; - s->nr_extra_bparts = 0; - - /* Flag that a redistribute has taken place */ - e->step_props |= engine_step_prop_redistribute; - - if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); -#else - error("SWIFT was not compiled with MPI support."); -#endif -} - /** * @brief Repartition the cells amongst the nodes. * @@ -2318,6 +1213,30 @@ void engine_allocate_foreign_particles(struct engine *e) { #endif } +void engine_do_tasks_count_mapper(void *map_data, int num_elements, + void *extra_data) { + + const struct task *tasks = (struct task *)map_data; + int *const global_counts = (int *)extra_data; + + /* Local accumulator copy */ + int local_counts[task_type_count + 1]; + for (int k = 0; k <= task_type_count; k++) local_counts[k] = 0; + + /* Add task counts locally */ + for (int k = 0; k < num_elements; k++) { + if (tasks[k].skip) + local_counts[task_type_count] += 1; + else + local_counts[(int)tasks[k].type] += 1; + } + + /* Update the global counts */ + for (int k = 0; k <= task_type_count; k++) { + if (local_counts[k]) atomic_add(global_counts + k, local_counts[k]); + } +} + /** * @brief Prints the number of tasks in the engine * @@ -2360,14 +1279,11 @@ void engine_print_task_counts(const struct engine *e) { fflush(stdout); /* Count and print the number of each task type. */ - int counts[task_type_count + 1]; - for (int k = 0; k <= task_type_count; k++) counts[k] = 0; - for (int k = 0; k < nr_tasks; k++) { - if (tasks[k].skip) - counts[task_type_count] += 1; - else - counts[(int)tasks[k].type] += 1; - } + int counts[task_type_count + 1]; + for (int k = 0; k <= task_type_count; k++) counts[k] = 0; + threadpool_map((struct threadpool *)&e->threadpool, + engine_do_tasks_count_mapper, (void *)tasks, nr_tasks, + sizeof(struct task), 0, counts); #ifdef WITH_MPI printf("[%04i] %s engine_print_task_counts: task counts are [ %s=%i", @@ -2777,544 +1693,6 @@ void engine_barrier(struct engine *e) { swift_barrier_wait(&e->run_barrier); } -/** - * @brief Recursive function gathering end-of-step data. - * - * We recurse until we encounter a timestep or time-step MPI recv task - * as the values will have been set at that level. We then bring these - * values upwards. - * - * @param c The #cell to recurse into. - * @param e The #engine. - */ -void engine_collect_end_of_step_recurse_hydro(struct cell *c, - const struct engine *e) { - - /* Skip super-cells (Their values are already set) */ - if (c->timestep != NULL) return; -#ifdef WITH_MPI - if (cell_get_recv(c, task_subtype_tend_part) != NULL) return; -#endif /* WITH_MPI */ - -#ifdef SWIFT_DEBUG_CHECKS - /* if (!c->split) error("Reached a leaf without finding a time-step task! - * c->depth=%d c->maxdepth=%d c->count=%d c->node=%d", */ - /* c->depth, c->maxdepth, c->hydro.count, c->nodeID); */ -#endif - - /* Counters for the different quantities. */ - size_t updated = 0; - integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, - ti_hydro_beg_max = 0; - - /* Local Star formation history properties */ - struct star_formation_history sfh_updated; - - /* Initialize the star formation structs */ - star_formation_logger_init(&sfh_updated); - - /* Collect the values from the progeny. */ - for (int k = 0; k < 8; k++) { - struct cell *cp = c->progeny[k]; - if (cp != NULL && cp->hydro.count > 0) { - - /* Recurse */ - engine_collect_end_of_step_recurse_hydro(cp, e); - - /* And update */ - 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); - - updated += cp->hydro.updated; - - /* Check if the cell is inactive and in that case reorder the SFH */ - if (!cell_is_starting_hydro(cp, e)) { - star_formation_logger_log_inactive_cell(&cp->stars.sfh); - } - - /* Add the star formation history in this cell to sfh_updated */ - star_formation_logger_add(&sfh_updated, &cp->stars.sfh); - - /* Collected, so clear for next time. */ - cp->hydro.updated = 0; - } - } - - /* Store the collected values in the cell. */ - c->hydro.ti_end_min = ti_hydro_end_min; - c->hydro.ti_end_max = ti_hydro_end_max; - c->hydro.ti_beg_max = ti_hydro_beg_max; - c->hydro.updated = updated; - // c->hydro.inhibited = inhibited; - - /* Store the star formation history in the parent cell */ - star_formation_logger_add(&c->stars.sfh, &sfh_updated); -} - -/** - * @brief Recursive function gathering end-of-step data. - * - * We recurse until we encounter a timestep or time-step MPI recv task - * as the values will have been set at that level. We then bring these - * values upwards. - * - * @param c The #cell to recurse into. - * @param e The #engine. - */ -void engine_collect_end_of_step_recurse_grav(struct cell *c, - const struct engine *e) { - - /* Skip super-cells (Their values are already set) */ - if (c->timestep != NULL) return; -#ifdef WITH_MPI - if (cell_get_recv(c, task_subtype_tend_gpart) != NULL) return; -#endif /* WITH_MPI */ - -#ifdef SWIFT_DEBUG_CHECKS - // if (!c->split) error("Reached a leaf without finding a time-step - // task!"); -#endif - - /* Counters for the different quantities. */ - size_t updated = 0; - integertime_t ti_grav_end_min = max_nr_timesteps, ti_grav_end_max = 0, - ti_grav_beg_max = 0; - - /* Collect the values from the progeny. */ - for (int k = 0; k < 8; k++) { - struct cell *cp = c->progeny[k]; - if (cp != NULL && cp->grav.count > 0) { - - /* Recurse */ - engine_collect_end_of_step_recurse_grav(cp, e); - - /* And update */ - ti_grav_end_min = min(ti_grav_end_min, cp->grav.ti_end_min); - ti_grav_end_max = max(ti_grav_end_max, cp->grav.ti_end_max); - ti_grav_beg_max = max(ti_grav_beg_max, cp->grav.ti_beg_max); - - updated += cp->grav.updated; - - /* Collected, so clear for next time. */ - cp->grav.updated = 0; - } - } - - /* Store the collected values in the cell. */ - c->grav.ti_end_min = ti_grav_end_min; - c->grav.ti_end_max = ti_grav_end_max; - c->grav.ti_beg_max = ti_grav_beg_max; - c->grav.updated = updated; -} - -/** - * @brief Recursive function gathering end-of-step data. - * - * We recurse until we encounter a timestep or time-step MPI recv task - * as the values will have been set at that level. We then bring these - * values upwards. - * - * @param c The #cell to recurse into. - * @param e The #engine. - */ -void engine_collect_end_of_step_recurse_stars(struct cell *c, - const struct engine *e) { - - /* Skip super-cells (Their values are already set) */ - if (c->timestep != NULL) return; -#ifdef WITH_MPI - if (cell_get_recv(c, task_subtype_tend_spart) != NULL) return; -#endif /* WITH_MPI */ - -#ifdef SWIFT_DEBUG_CHECKS - // if (!c->split) error("Reached a leaf without finding a time-step task!"); -#endif - - /* Counters for the different quantities. */ - size_t updated = 0; - integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, - ti_stars_beg_max = 0; - - /* Collect the values from the progeny. */ - for (int k = 0; k < 8; k++) { - struct cell *cp = c->progeny[k]; - if (cp != NULL && cp->stars.count > 0) { - - /* Recurse */ - engine_collect_end_of_step_recurse_stars(cp, e); - - /* And update */ - ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); - ti_stars_end_max = max(ti_stars_end_max, cp->stars.ti_end_max); - ti_stars_beg_max = max(ti_stars_beg_max, cp->stars.ti_beg_max); - - updated += cp->stars.updated; - - /* Collected, so clear for next time. */ - cp->stars.updated = 0; - } - } - - /* Store the collected values in the cell. */ - c->stars.ti_end_min = ti_stars_end_min; - c->stars.ti_end_max = ti_stars_end_max; - c->stars.ti_beg_max = ti_stars_beg_max; - c->stars.updated = updated; -} - -/** - * @brief Recursive function gathering end-of-step data. - * - * We recurse until we encounter a timestep or time-step MPI recv task - * as the values will have been set at that level. We then bring these - * values upwards. - * - * @param c The #cell to recurse into. - * @param e The #engine. - */ -void engine_collect_end_of_step_recurse_black_holes(struct cell *c, - const struct engine *e) { - - /* 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 - // if (!c->split) error("Reached a leaf without finding a time-step task!"); -#endif - - /* Counters for the different quantities. */ - size_t updated = 0; - integertime_t ti_black_holes_end_min = max_nr_timesteps, - ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; - - /* Collect the values from the progeny. */ - for (int k = 0; k < 8; k++) { - struct cell *cp = c->progeny[k]; - if (cp != NULL && cp->black_holes.count > 0) { - - /* Recurse */ - engine_collect_end_of_step_recurse_black_holes(cp, e); - - /* And update */ - ti_black_holes_end_min = - min(ti_black_holes_end_min, cp->black_holes.ti_end_min); - ti_black_holes_end_max = - max(ti_black_holes_end_max, cp->black_holes.ti_end_max); - ti_black_holes_beg_max = - max(ti_black_holes_beg_max, cp->black_holes.ti_beg_max); - - updated += cp->black_holes.updated; - - /* Collected, so clear for next time. */ - cp->black_holes.updated = 0; - } - } - - /* Store the collected values in the cell. */ - c->black_holes.ti_end_min = ti_black_holes_end_min; - c->black_holes.ti_end_max = ti_black_holes_end_max; - c->black_holes.ti_beg_max = ti_black_holes_beg_max; - c->black_holes.updated = updated; -} - -/** - * @brief Mapping function to collect the data from the end of the step - * - * This function will call a recursive function on all the top-level cells - * to collect the information we are after. - * - * @param map_data The list of cells with tasks on this node. - * @param num_elements The number of elements in the list this thread will work - * on. - * @param extra_data The #engine. - */ -void engine_collect_end_of_step_mapper(void *map_data, int num_elements, - void *extra_data) { - - struct end_of_step_data *data = (struct end_of_step_data *)extra_data; - const struct engine *e = data->e; - const int with_hydro = (e->policy & engine_policy_hydro); - const int with_self_grav = (e->policy & engine_policy_self_gravity); - const int with_ext_grav = (e->policy & engine_policy_external_gravity); - const int with_grav = (with_self_grav || with_ext_grav); - 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; - int *local_cells = (int *)map_data; - struct star_formation_history *sfh_top = &data->sfh; - - /* Local collectible */ - size_t updated = 0, g_updated = 0, s_updated = 0, b_updated = 0; - integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, - ti_hydro_beg_max = 0; - integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, - ti_gravity_beg_max = 0; - integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, - ti_stars_beg_max = 0; - integertime_t ti_black_holes_end_min = max_nr_timesteps, - ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; - - /* Local Star formation history properties */ - struct star_formation_history sfh_updated; - - /* Initialize the star formation structs for this engine to zero */ - star_formation_logger_init(&sfh_updated); - - for (int ind = 0; ind < num_elements; ind++) { - struct cell *c = &s->cells_top[local_cells[ind]]; - - if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0 || - c->black_holes.count > 0) { - - /* Make the top-cells recurse */ - if (with_hydro) { - engine_collect_end_of_step_recurse_hydro(c, e); - } - if (with_grav) { - engine_collect_end_of_step_recurse_grav(c, e); - } - if (with_stars) { - engine_collect_end_of_step_recurse_stars(c, e); - } - if (with_black_holes) { - engine_collect_end_of_step_recurse_black_holes(c, e); - } - - /* And aggregate */ - if (c->hydro.ti_end_min > e->ti_current) - ti_hydro_end_min = min(ti_hydro_end_min, c->hydro.ti_end_min); - ti_hydro_end_max = max(ti_hydro_end_max, c->hydro.ti_end_max); - ti_hydro_beg_max = max(ti_hydro_beg_max, c->hydro.ti_beg_max); - - if (c->grav.ti_end_min > e->ti_current) - ti_gravity_end_min = min(ti_gravity_end_min, c->grav.ti_end_min); - ti_gravity_end_max = max(ti_gravity_end_max, c->grav.ti_end_max); - ti_gravity_beg_max = max(ti_gravity_beg_max, c->grav.ti_beg_max); - - if (c->stars.ti_end_min > e->ti_current) - ti_stars_end_min = min(ti_stars_end_min, c->stars.ti_end_min); - ti_stars_end_max = max(ti_stars_end_max, c->stars.ti_end_max); - ti_stars_beg_max = max(ti_stars_beg_max, c->stars.ti_beg_max); - - if (c->black_holes.ti_end_min > e->ti_current) - ti_black_holes_end_min = - min(ti_black_holes_end_min, c->black_holes.ti_end_min); - ti_black_holes_end_max = - max(ti_black_holes_end_max, c->black_holes.ti_end_max); - ti_black_holes_beg_max = - max(ti_black_holes_beg_max, c->black_holes.ti_beg_max); - - updated += c->hydro.updated; - g_updated += c->grav.updated; - s_updated += c->stars.updated; - b_updated += c->black_holes.updated; - - /* Check if the cell is inactive and in that case reorder the SFH */ - if (!cell_is_starting_hydro(c, e)) { - star_formation_logger_log_inactive_cell(&c->stars.sfh); - } - - /* Get the star formation history from the current cell and store it in - * the star formation history struct */ - star_formation_logger_add(&sfh_updated, &c->stars.sfh); - - /* Collected, so clear for next time. */ - c->hydro.updated = 0; - c->grav.updated = 0; - c->stars.updated = 0; - c->black_holes.updated = 0; - } - } - - /* Let's write back to the global data. - * We use the space lock to garanty single access*/ - if (lock_lock(&s->lock) == 0) { - data->updated += updated; - data->g_updated += g_updated; - data->s_updated += s_updated; - data->b_updated += b_updated; - - /* Add the SFH information from this engine to the global data */ - star_formation_logger_add(sfh_top, &sfh_updated); - - if (ti_hydro_end_min > e->ti_current) - data->ti_hydro_end_min = min(ti_hydro_end_min, data->ti_hydro_end_min); - data->ti_hydro_end_max = max(ti_hydro_end_max, data->ti_hydro_end_max); - data->ti_hydro_beg_max = max(ti_hydro_beg_max, data->ti_hydro_beg_max); - - if (ti_gravity_end_min > e->ti_current) - data->ti_gravity_end_min = - min(ti_gravity_end_min, data->ti_gravity_end_min); - data->ti_gravity_end_max = - max(ti_gravity_end_max, data->ti_gravity_end_max); - data->ti_gravity_beg_max = - max(ti_gravity_beg_max, data->ti_gravity_beg_max); - - if (ti_stars_end_min > e->ti_current) - data->ti_stars_end_min = min(ti_stars_end_min, data->ti_stars_end_min); - data->ti_stars_end_max = max(ti_stars_end_max, data->ti_stars_end_max); - data->ti_stars_beg_max = max(ti_stars_beg_max, data->ti_stars_beg_max); - - if (ti_black_holes_end_min > e->ti_current) - data->ti_black_holes_end_min = - min(ti_black_holes_end_min, data->ti_black_holes_end_min); - data->ti_black_holes_end_max = - max(ti_black_holes_end_max, data->ti_black_holes_end_max); - data->ti_black_holes_beg_max = - max(ti_black_holes_beg_max, data->ti_black_holes_beg_max); - } - - if (lock_unlock(&s->lock) != 0) error("Failed to unlock the space"); -} - -/** - * @brief Collects the next time-step and rebuild flag. - * - * The next time-step is determined by making each super-cell recurse to - * collect the minimal of ti_end and the number of updated particles. When in - * MPI mode this routines reduces these across all nodes and also collects the - * forcerebuild flag -- this is so that we only use a single collective MPI - * call per step for all these values. - * - * Note that the results are stored in e->collect_group1 struct not in the - * engine fields, unless apply is true. These can be applied field-by-field - * or all at once using collectgroup1_copy(); - * - * @param e The #engine. - * @param apply whether to apply the results to the engine or just keep in the - * group1 struct. - */ -void engine_collect_end_of_step(struct engine *e, int apply) { - - const ticks tic = getticks(); - struct space *s = e->s; - struct end_of_step_data data; - data.updated = 0, data.g_updated = 0, data.s_updated = 0, data.b_updated = 0; - data.ti_hydro_end_min = max_nr_timesteps, data.ti_hydro_end_max = 0, - data.ti_hydro_beg_max = 0; - data.ti_gravity_end_min = max_nr_timesteps, data.ti_gravity_end_max = 0, - data.ti_gravity_beg_max = 0; - data.ti_stars_end_min = max_nr_timesteps, data.ti_stars_end_max = 0, - data.ti_stars_beg_max = 0; - data.ti_black_holes_end_min = max_nr_timesteps, - data.ti_black_holes_end_max = 0, data.ti_black_holes_beg_max = 0; - data.e = e; - - /* Initialize the total SFH of the simulation to zero */ - star_formation_logger_init(&data.sfh); - - /* Collect information from the local top-level cells */ - threadpool_map(&e->threadpool, engine_collect_end_of_step_mapper, - s->local_cells_with_tasks_top, s->nr_local_cells_with_tasks, - sizeof(int), 0, &data); - - /* Get the number of inhibited particles from the space-wide counters - * since these have been updated atomically during the time-steps. */ - data.inhibited = s->nr_inhibited_parts; - data.g_inhibited = s->nr_inhibited_gparts; - data.s_inhibited = s->nr_inhibited_sparts; - data.b_inhibited = s->nr_inhibited_bparts; - - /* Store these in the temporary collection group. */ - collectgroup1_init( - &e->collect_group1, data.updated, data.g_updated, data.s_updated, - data.b_updated, data.inhibited, data.g_inhibited, data.s_inhibited, - data.b_inhibited, data.ti_hydro_end_min, data.ti_hydro_end_max, - data.ti_hydro_beg_max, data.ti_gravity_end_min, data.ti_gravity_end_max, - data.ti_gravity_beg_max, data.ti_stars_end_min, data.ti_stars_end_max, - data.ti_stars_beg_max, data.ti_black_holes_end_min, - data.ti_black_holes_end_max, data.ti_black_holes_beg_max, e->forcerebuild, - e->s->tot_cells, e->sched.nr_tasks, - (float)e->sched.nr_tasks / (float)e->s->tot_cells, data.sfh); - -/* Aggregate collective data from the different nodes for this step. */ -#ifdef WITH_MPI - collectgroup1_reduce(&e->collect_group1); - -#ifdef SWIFT_DEBUG_CHECKS - { - /* Check the above using the original MPI calls. */ - integertime_t in_i[2], out_i[2]; - in_i[0] = 0; - in_i[1] = 0; - out_i[0] = data.ti_hydro_end_min; - out_i[1] = data.ti_gravity_end_min; - if (MPI_Allreduce(out_i, in_i, 2, MPI_LONG_LONG_INT, MPI_MIN, - MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to aggregate ti_end_min."); - if (in_i[0] != (long long)e->collect_group1.ti_hydro_end_min) - error("Failed to get same ti_hydro_end_min, is %lld, should be %lld", - in_i[0], e->collect_group1.ti_hydro_end_min); - if (in_i[1] != (long long)e->collect_group1.ti_gravity_end_min) - error("Failed to get same ti_gravity_end_min, is %lld, should be %lld", - in_i[1], e->collect_group1.ti_gravity_end_min); - - long long in_ll[4], out_ll[4]; - out_ll[0] = data.updated; - out_ll[1] = data.g_updated; - out_ll[2] = data.s_updated; - out_ll[3] = data.b_updated; - if (MPI_Allreduce(out_ll, in_ll, 4, MPI_LONG_LONG_INT, MPI_SUM, - MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to aggregate particle counts."); - if (in_ll[0] != (long long)e->collect_group1.updated) - error("Failed to get same updated, is %lld, should be %lld", in_ll[0], - e->collect_group1.updated); - if (in_ll[1] != (long long)e->collect_group1.g_updated) - error("Failed to get same g_updated, is %lld, should be %lld", in_ll[1], - e->collect_group1.g_updated); - if (in_ll[2] != (long long)e->collect_group1.s_updated) - error("Failed to get same s_updated, is %lld, should be %lld", in_ll[2], - e->collect_group1.s_updated); - if (in_ll[3] != (long long)e->collect_group1.b_updated) - error("Failed to get same b_updated, is %lld, should be %lld", in_ll[3], - e->collect_group1.b_updated); - - out_ll[0] = data.inhibited; - out_ll[1] = data.g_inhibited; - out_ll[2] = data.s_inhibited; - out_ll[3] = data.b_inhibited; - if (MPI_Allreduce(out_ll, in_ll, 4, MPI_LONG_LONG_INT, MPI_SUM, - MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to aggregate particle counts."); - if (in_ll[0] != (long long)e->collect_group1.inhibited) - error("Failed to get same inhibited, is %lld, should be %lld", in_ll[0], - e->collect_group1.inhibited); - if (in_ll[1] != (long long)e->collect_group1.g_inhibited) - error("Failed to get same g_inhibited, is %lld, should be %lld", in_ll[1], - e->collect_group1.g_inhibited); - if (in_ll[2] != (long long)e->collect_group1.s_inhibited) - error("Failed to get same s_inhibited, is %lld, should be %lld", in_ll[2], - e->collect_group1.s_inhibited); - if (in_ll[3] != (long long)e->collect_group1.b_inhibited) - error("Failed to get same b_inhibited, is %lld, should be %lld", in_ll[3], - e->collect_group1.b_inhibited); - - int buff = 0; - if (MPI_Allreduce(&e->forcerebuild, &buff, 1, MPI_INT, MPI_MAX, - MPI_COMM_WORLD) != MPI_SUCCESS) - error("Failed to aggregate the rebuild flag across nodes."); - if (!!buff != !!e->collect_group1.forcerebuild) - error( - "Failed to get same rebuild flag from all nodes, is %d," - "should be %d", - buff, e->collect_group1.forcerebuild); - } -#endif -#endif - - /* Apply to the engine, if requested. */ - if (apply) collectgroup1_apply(&e->collect_group1, e); - - if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); -} - /** * @brief Print the conserved quantities statistics to a log file * @@ -3412,16 +1790,19 @@ void engine_skip_force_and_kick(struct engine *e) { t->type == task_type_drift_gpart_out || t->type == task_type_cooling || t->type == task_type_stars_in || t->type == task_type_stars_out || t->type == task_type_star_formation || - t->type == task_type_extra_ghost || + t->type == task_type_stars_resort || t->type == task_type_extra_ghost || t->type == task_type_bh_swallow_ghost1 || t->type == task_type_bh_swallow_ghost2 || + t->type == task_type_bh_swallow_ghost3 || t->subtype == task_subtype_gradient || t->subtype == task_subtype_stars_feedback || t->subtype == task_subtype_bh_feedback || t->subtype == task_subtype_bh_swallow || - t->subtype == task_subtype_do_swallow || + t->subtype == task_subtype_do_gas_swallow || + t->subtype == task_subtype_do_bh_swallow || t->subtype == task_subtype_bpart_rho || t->subtype == task_subtype_part_swallow || + t->subtype == task_subtype_bpart_merger || t->subtype == task_subtype_bpart_swallow || t->subtype == task_subtype_bpart_feedback || t->subtype == task_subtype_tend_part || @@ -3639,7 +2020,6 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, #endif scheduler_write_dependencies(&e->sched, e->verbose); - space_write_cell_hierarchy(e->s); if (e->nodeID == 0) scheduler_write_task_level(&e->sched); /* Run the 0th time-step */ @@ -4214,64 +2594,6 @@ int engine_is_done(struct engine *e) { return !(e->ti_current < max_nr_timesteps); } -/** - * @brief Unskip all the tasks that act on active cells at this time. - * - * @param e The #engine. - */ -void engine_unskip(struct engine *e) { - - const ticks tic = getticks(); - struct space *s = e->s; - const int nodeID = e->nodeID; - - const int with_hydro = e->policy & engine_policy_hydro; - const int with_self_grav = e->policy & engine_policy_self_gravity; - 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; - char filename[100]; - sprintf(filename, "/tmp/swift_runner_do_usnkip_mapper_%06i.prof", count++); - ProfilerStart(filename); -#endif // WITH_PROFILER - - /* Move the active local cells to the top of the list. */ - int *local_cells = e->s->local_cells_with_tasks_top; - int num_active_cells = 0; - for (int k = 0; k < s->nr_local_cells_with_tasks; k++) { - struct cell *c = &s->cells_top[local_cells[k]]; - - if ((with_hydro && cell_is_active_hydro(c, e)) || - (with_self_grav && cell_is_active_gravity(c, 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_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)); - num_active_cells += 1; - } - } - - /* Activate all the regular tasks */ - threadpool_map(&e->threadpool, runner_do_unskip_mapper, local_cells, - num_active_cells, sizeof(int), 1, e); - -#ifdef WITH_PROFILER - ProfilerStop(); -#endif // WITH_PROFILER - - if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); -} - void engine_do_reconstruct_multipoles_mapper(void *map_data, int num_elements, void *extra_data) { @@ -4283,7 +2605,7 @@ void engine_do_reconstruct_multipoles_mapper(void *map_data, int num_elements, if (c != NULL && c->nodeID == e->nodeID) { /* Construct the multipoles in this cell hierarchy */ - cell_make_multipoles(c, e->ti_current); + cell_make_multipoles(c, e->ti_current, e->gravity_properties); } } } @@ -4490,19 +2812,26 @@ void engine_makeproxies(struct engine *e) { sqrt(min_dist_centres2) - 2. * delta_CoM; const double min_dist_CoM2 = min_dist_CoM * min_dist_CoM; + /* We also assume that the softening is negligible compared + to the cell size */ + const double epsilon_i = 0.; + const double epsilon_j = 0.; + /* Are we beyond the distance where the truncated forces are 0 * but not too far such that M2L can be used? */ if (periodic) { if ((min_dist_CoM2 < max_mesh_dist2) && (!gravity_M2L_accept(r_max, r_max, theta_crit2, - min_dist_CoM2))) + min_dist_CoM2, epsilon_i, + epsilon_j))) proxy_type |= (int)proxy_cell_type_gravity; } else { if (!gravity_M2L_accept(r_max, r_max, theta_crit2, - min_dist_CoM2)) + min_dist_CoM2, epsilon_i, + epsilon_j)) proxy_type |= (int)proxy_cell_type_gravity; } } @@ -4867,7 +3196,7 @@ void engine_dump_snapshot(struct engine *e) { */ void engine_dump_index(struct engine *e) { -#if defined(WITH_LOGGER) +#if defined(WITH_LOGGER) && !defined(WITH_MPI) struct clocks_time time1, time2; clocks_gettime(&time1); @@ -4965,6 +3294,7 @@ void engine_unpin(void) { * @param Ngparts total number of gravity particles in the simulation. * @param Nstars total number of star particles in the simulation. * @param Nblackholes total number of black holes in the simulation. + * @param Nbackground_gparts Total number of background DM particles. * @param policy The queuing policy to use. * @param verbose Is this #engine talkative ? * @param reparttype What type of repartition algorithm are we using ? @@ -4986,8 +3316,8 @@ void engine_unpin(void) { */ void engine_init(struct engine *e, struct space *s, struct swift_params *params, long long Ngas, long long Ngparts, long long Nstars, - long long Nblackholes, int policy, int verbose, - struct repartition *reparttype, + long long Nblackholes, long long Nbackground_gparts, + int policy, int verbose, struct repartition *reparttype, const struct unit_system *internal_units, const struct phys_const *physical_constants, struct cosmology *cosmo, struct hydro_props *hydro, @@ -5012,6 +3342,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, e->total_nr_gparts = Ngparts; e->total_nr_sparts = Nstars; e->total_nr_bparts = Nblackholes; + e->total_nr_DM_background_gparts = Nbackground_gparts; e->proxy_ind = NULL; e->nr_proxies = 0; e->reparttype = reparttype; @@ -5030,7 +3361,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, e->time_first_snapshot = parser_get_opt_param_double(params, "Snapshots:time_first", 0.); e->delta_time_snapshot = - parser_get_param_double(params, "Snapshots:delta_time"); + parser_get_opt_param_double(params, "Snapshots:delta_time", -1.); e->ti_next_snapshot = 0; parser_get_param_string(params, "Snapshots:basename", e->snapshot_base_name); e->snapshot_compression = @@ -5082,7 +3413,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, e->total_nr_tasks = 0; #if defined(WITH_LOGGER) - e->logger = (struct logger *)malloc(sizeof(struct logger)); + e->logger = (struct logger_writer *)malloc(sizeof(struct logger_writer)); logger_init(e->logger, params); #endif @@ -5147,6 +3478,11 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, parser_get_opt_param_double(params, "FOF:delta_time", -1.); } + /* Initialize the star formation history structure */ + if (e->policy & engine_policy_star_formation) { + star_formation_logger_accumulator_init(&e->sfh); + } + engine_init_output_lists(e, params); } @@ -5665,8 +4001,10 @@ void engine_config(int restart, int fof, struct engine *e, stats_create_mpi_type(); proxy_create_mpi_type(); task_create_mpi_comms(); +#ifdef WITH_FOF fof_create_mpi_types(); -#endif +#endif /* WITH_FOF */ +#endif /* WITH_MPI */ if (!fof) { @@ -5711,6 +4049,51 @@ void engine_config(int restart, int fof, struct engine *e, e->sched.mpi_message_limit = parser_get_opt_param_int(params, "Scheduler:mpi_message_limit", 4) * 1024; + if (restart) { + + /* Overwrite the constants for the scheduler */ + space_maxsize = parser_get_opt_param_int(params, "Scheduler:cell_max_size", + space_maxsize_default); + space_subsize_pair_hydro = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_pair_hydro", + space_subsize_pair_hydro_default); + space_subsize_self_hydro = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_self_hydro", + space_subsize_self_hydro_default); + space_subsize_pair_stars = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_pair_stars", + space_subsize_pair_stars_default); + space_subsize_self_stars = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_self_stars", + space_subsize_self_stars_default); + space_subsize_pair_grav = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_pair_grav", + space_subsize_pair_grav_default); + space_subsize_self_grav = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_self_grav", + space_subsize_self_grav_default); + space_splitsize = parser_get_opt_param_int( + params, "Scheduler:cell_split_size", space_splitsize_default); + space_subdepth_diff_grav = + parser_get_opt_param_int(params, "Scheduler:cell_subdepth_diff_grav", + space_subdepth_diff_grav_default); + space_extra_parts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_parts", space_extra_parts_default); + space_extra_sparts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_sparts", space_extra_sparts_default); + space_extra_gparts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_gparts", space_extra_gparts_default); + space_extra_bparts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_bparts", space_extra_bparts_default); + + engine_max_parts_per_ghost = + parser_get_opt_param_int(params, "Scheduler:engine_max_parts_per_ghost", + engine_max_parts_per_ghost_default); + engine_max_sparts_per_ghost = parser_get_opt_param_int( + params, "Scheduler:engine_max_sparts_per_ghost", + engine_max_sparts_per_ghost_default); + } + /* Allocate and init the threads. */ if (swift_memalign("runners", (void **)&e->runners, SWIFT_CACHE_ALIGNMENT, e->nr_threads * sizeof(struct runner)) != 0) @@ -5778,7 +4161,7 @@ void engine_config(int restart, int fof, struct engine *e, #ifdef WITH_LOGGER /* Write the particle logger header */ - logger_write_file_header(e->logger, e); + logger_write_file_header(e->logger); #endif /* Initialise the structure finder */ @@ -6190,11 +4573,17 @@ void engine_recompute_displacement_constraint(struct engine *e) { #endif /* Get the counts of each particle types */ + const long long total_nr_baryons = + e->total_nr_parts + e->total_nr_sparts + e->total_nr_bparts; const long long total_nr_dm_gparts = - e->total_nr_gparts - e->total_nr_parts - e->total_nr_sparts; + e->total_nr_gparts - e->total_nr_DM_background_gparts - total_nr_baryons; float count_parts[swift_type_count] = { - (float)e->total_nr_parts, (float)total_nr_dm_gparts, 0.f, 0.f, - (float)e->total_nr_sparts, (float)e->total_nr_bparts}; + (float)e->total_nr_parts, + (float)total_nr_dm_gparts, + (float)e->total_nr_DM_background_gparts, + 0.f, + (float)e->total_nr_sparts, + (float)e->total_nr_bparts}; /* Count of particles for the two species */ const float N_dm = count_parts[1]; @@ -6290,7 +4679,7 @@ void engine_clean(struct engine *e, const int fof) { swift_free("links", e->links); #if defined(WITH_LOGGER) - logger_clean(e->logger); + logger_free(e->logger); free(e->logger); #endif scheduler_clean(&e->sched); @@ -6346,7 +4735,9 @@ void engine_struct_dump(struct engine *e, FILE *stream) { feedback_struct_dump(e->feedback_props, stream); black_holes_struct_dump(e->black_holes_properties, stream); chemistry_struct_dump(e->chemistry, stream); +#ifdef WITH_FOF fof_struct_dump(e->fof_properties, stream); +#endif parser_struct_dump(e->parameter_file, stream); if (e->output_list_snapshots) output_list_struct_dump(e->output_list_snapshots, stream); @@ -6464,10 +4855,12 @@ void engine_struct_restore(struct engine *e, FILE *stream) { chemistry_struct_restore(chemistry, stream); e->chemistry = chemistry; +#ifdef WITH_FOF struct fof_props *fof_props = (struct fof_props *)malloc(sizeof(struct fof_props)); fof_struct_restore(fof_props, stream); e->fof_properties = fof_props; +#endif struct swift_params *parameter_file = (struct swift_params *)malloc(sizeof(struct swift_params)); @@ -6503,141 +4896,3 @@ void engine_struct_restore(struct engine *e, FILE *stream) { e->forcerebuild = 1; e->forcerepart = 0; } - -/** - * @brief Activate all the #gpart communications in preparation - * fof a call to FOF. - * - * @param e The #engine to act on. - */ -void engine_activate_gpart_comms(struct engine *e) { - -#ifdef WITH_MPI - - const ticks tic = getticks(); - - struct scheduler *s = &e->sched; - const int nr_tasks = s->nr_tasks; - struct task *tasks = s->tasks; - - for (int k = 0; k < nr_tasks; ++k) { - - struct task *t = &tasks[k]; - - if ((t->type == task_type_send) && (t->subtype == task_subtype_gpart)) { - scheduler_activate(s, t); - } else if ((t->type == task_type_recv) && - (t->subtype == task_subtype_gpart)) { - scheduler_activate(s, t); - } else { - t->skip = 1; - } - } - - if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); - -#else - error("Calling an MPI function in non-MPI mode."); -#endif -} - -/** - * @brief Activate all the FOF tasks. - * - * Marks all the other task types to be skipped. - * - * @param e The #engine to act on. - */ -void engine_activate_fof_tasks(struct engine *e) { - - const ticks tic = getticks(); - - struct scheduler *s = &e->sched; - const int nr_tasks = s->nr_tasks; - struct task *tasks = s->tasks; - - for (int k = 0; k < nr_tasks; k++) { - - struct task *t = &tasks[k]; - - if (t->type == task_type_fof_self || t->type == task_type_fof_pair) - scheduler_activate(s, t); - else - t->skip = 1; - } - - if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); -} - -/** - * @brief Run a FOF search. - * - * @param e the engine - * @param dump_results Are we writing group catalogues to output files? - * @param seed_black_holes Are we seeding black holes? - */ -void engine_fof(struct engine *e, const int dump_results, - const int seed_black_holes) { - -#ifdef WITH_MPI - MPI_Barrier(MPI_COMM_WORLD); -#endif - ticks tic = getticks(); - - /* Compute number of DM particles */ - const long long total_nr_baryons = - e->total_nr_parts + e->total_nr_sparts + e->total_nr_bparts; - const long long total_nr_dmparts = e->total_nr_gparts - total_nr_baryons; - - /* Initialise FOF parameters and allocate FOF arrays. */ - fof_allocate(e->s, total_nr_dmparts, e->fof_properties); - - message("FOF allocate (FOF SCALING) took: %.3f %s.", - clocks_from_ticks(getticks() - tic), clocks_getunit()); - - /* Make FOF tasks */ - engine_make_fof_tasks(e); - - /* and activate them. */ - engine_activate_fof_tasks(e); - - ticks tic_local_fof = getticks(); - - /* Perform local FOF tasks. */ - engine_launch(e); - - message("Local FOF search (tasks FOF SCALING) took: %.3f %s.", - clocks_from_ticks(getticks() - tic_local_fof), clocks_getunit()); - - message( - "FOF allocate + task creation + local search (FOF SCALING) took: %.3f " - "%s.", - clocks_from_ticks(getticks() - tic), clocks_getunit()); - - /* Perform FOF search over foreign particles and - * find groups which require black hole seeding. */ - fof_search_tree(e->fof_properties, e->black_holes_properties, - e->physical_constants, e->cosmology, e->s, dump_results, - seed_black_holes); - - /* Reset flag. */ - e->run_fof = 0; - - /* Flag that a FOF has taken place */ - e->step_props |= engine_step_prop_fof; - - /* ... and find the next FOF time */ - if (seed_black_holes) engine_compute_next_fof_time(e); - -#ifdef WITH_MPI - MPI_Barrier(MPI_COMM_WORLD); -#endif - - if (engine_rank == 0) - message("Complete FOF search took: %.3f %s.", - clocks_from_ticks(getticks() - tic), clocks_getunit()); -} diff --git a/src/engine.h b/src/engine.h index 77e010cd40d6d75529e44967b0475525b62fce4f..68a4df10c08325d5d810b361dab863bf9ee68ea6 100644 --- a/src/engine.h +++ b/src/engine.h @@ -113,6 +113,7 @@ enum engine_step_properties { #define engine_default_timesteps_file_name "timesteps" #define engine_max_parts_per_ghost_default 1000 #define engine_max_sparts_per_ghost_default 1000 +#define engine_star_resort_task_depth_default 2 #define engine_tasks_per_cell_margin 1.2 /** @@ -237,7 +238,7 @@ struct engine { long long b_updates_since_rebuild; /* Star formation logger information */ - struct star_formation_history sfh; + struct star_formation_history_accumulator sfh; /* Properties of the previous step */ int step_props; @@ -247,6 +248,7 @@ struct engine { long long total_nr_gparts; long long total_nr_sparts; long long total_nr_bparts; + long long total_nr_DM_background_gparts; /* Total numbers of cells (top-level and sub-cells) in the system. */ long long total_nr_cells; @@ -381,7 +383,7 @@ struct engine { struct repartition *reparttype; #ifdef WITH_LOGGER - struct logger *logger; + struct logger_writer *logger; #endif /* How many steps have we done with the same set of tasks? */ @@ -492,12 +494,13 @@ void engine_reconstruct_multipoles(struct engine *e); void engine_allocate_foreign_particles(struct engine *e); void engine_print_stats(struct engine *e); void engine_check_for_dumps(struct engine *e); +void engine_collect_end_of_step(struct engine *e, int apply); void engine_dump_snapshot(struct engine *e); void engine_init_output_lists(struct engine *e, struct swift_params *params); void engine_init(struct engine *e, struct space *s, struct swift_params *params, long long Ngas, long long Ngparts, long long Nstars, - long long Nblackholes, int policy, int verbose, - struct repartition *reparttype, + long long Nblackholes, long long Nbackground_gparts, + int policy, int verbose, struct repartition *reparttype, const struct unit_system *internal_units, const struct phys_const *physical_constants, struct cosmology *cosmo, struct hydro_props *hydro, diff --git a/src/engine_collect_end_of_step.c b/src/engine_collect_end_of_step.c new file mode 100644 index 0000000000000000000000000000000000000000..ec02acfefdf65aca13d44a7cf90d48f31b99778f --- /dev/null +++ b/src/engine_collect_end_of_step.c @@ -0,0 +1,584 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "engine.h" + +/* Local headers. */ +#include "active.h" +#include "timeline.h" + +/** + * @brief Data collected from the cells at the end of a time-step + */ +struct end_of_step_data { + + size_t updated, g_updated, s_updated, b_updated; + size_t inhibited, g_inhibited, s_inhibited, b_inhibited; + integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max; + integertime_t ti_gravity_end_min, ti_gravity_end_max, ti_gravity_beg_max; + integertime_t ti_stars_end_min, ti_stars_end_max, ti_stars_beg_max; + integertime_t ti_black_holes_end_min, ti_black_holes_end_max, + ti_black_holes_beg_max; + struct engine *e; + struct star_formation_history sfh; +}; + +/** + * @brief Recursive function gathering end-of-step data. + * + * We recurse until we encounter a timestep or time-step MPI recv task + * as the values will have been set at that level. We then bring these + * values upwards. + * + * @param c The #cell to recurse into. + * @param e The #engine. + */ +void engine_collect_end_of_step_recurse_hydro(struct cell *c, + const struct engine *e) { + + /* Skip super-cells (Their values are already set) */ + if (c->timestep != NULL) return; +#ifdef WITH_MPI + if (cell_get_recv(c, task_subtype_tend_part) != NULL) return; +#endif /* WITH_MPI */ + +#ifdef SWIFT_DEBUG_CHECKS + /* if (!c->split) error("Reached a leaf without finding a time-step task! + * c->depth=%d c->maxdepth=%d c->count=%d c->node=%d", */ + /* c->depth, c->maxdepth, c->hydro.count, c->nodeID); */ +#endif + + /* Counters for the different quantities. */ + size_t updated = 0; + integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, + ti_hydro_beg_max = 0; + + /* Local Star formation history properties */ + struct star_formation_history sfh_updated; + + /* Initialize the star formation structs */ + star_formation_logger_init(&sfh_updated); + + /* Collect the values from the progeny. */ + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL && cp->hydro.count > 0) { + + /* Recurse */ + engine_collect_end_of_step_recurse_hydro(cp, e); + + /* And update */ + 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); + + updated += cp->hydro.updated; + + /* Check if the cell is inactive and in that case reorder the SFH */ + if (!cell_is_starting_hydro(cp, e)) { + star_formation_logger_log_inactive_cell(&cp->stars.sfh); + } + + /* Add the star formation history in this cell to sfh_updated */ + star_formation_logger_add(&sfh_updated, &cp->stars.sfh); + + /* Collected, so clear for next time. */ + cp->hydro.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->hydro.ti_end_min = ti_hydro_end_min; + c->hydro.ti_end_max = ti_hydro_end_max; + c->hydro.ti_beg_max = ti_hydro_beg_max; + c->hydro.updated = updated; + // c->hydro.inhibited = inhibited; + + /* Store the star formation history in the parent cell */ + star_formation_logger_add(&c->stars.sfh, &sfh_updated); +} + +/** + * @brief Recursive function gathering end-of-step data. + * + * We recurse until we encounter a timestep or time-step MPI recv task + * as the values will have been set at that level. We then bring these + * values upwards. + * + * @param c The #cell to recurse into. + * @param e The #engine. + */ +void engine_collect_end_of_step_recurse_grav(struct cell *c, + const struct engine *e) { + + /* Skip super-cells (Their values are already set) */ + if (c->timestep != NULL) return; +#ifdef WITH_MPI + if (cell_get_recv(c, task_subtype_tend_gpart) != NULL) return; +#endif /* WITH_MPI */ + +#ifdef SWIFT_DEBUG_CHECKS + // if (!c->split) error("Reached a leaf without finding a time-step + // task!"); +#endif + + /* Counters for the different quantities. */ + size_t updated = 0; + integertime_t ti_grav_end_min = max_nr_timesteps, ti_grav_end_max = 0, + ti_grav_beg_max = 0; + + /* Collect the values from the progeny. */ + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL && cp->grav.count > 0) { + + /* Recurse */ + engine_collect_end_of_step_recurse_grav(cp, e); + + /* And update */ + ti_grav_end_min = min(ti_grav_end_min, cp->grav.ti_end_min); + ti_grav_end_max = max(ti_grav_end_max, cp->grav.ti_end_max); + ti_grav_beg_max = max(ti_grav_beg_max, cp->grav.ti_beg_max); + + updated += cp->grav.updated; + + /* Collected, so clear for next time. */ + cp->grav.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->grav.ti_end_min = ti_grav_end_min; + c->grav.ti_end_max = ti_grav_end_max; + c->grav.ti_beg_max = ti_grav_beg_max; + c->grav.updated = updated; +} + +/** + * @brief Recursive function gathering end-of-step data. + * + * We recurse until we encounter a timestep or time-step MPI recv task + * as the values will have been set at that level. We then bring these + * values upwards. + * + * @param c The #cell to recurse into. + * @param e The #engine. + */ +void engine_collect_end_of_step_recurse_stars(struct cell *c, + const struct engine *e) { + + /* Skip super-cells (Their values are already set) */ + if (c->timestep != NULL) return; +#ifdef WITH_MPI + if (cell_get_recv(c, task_subtype_tend_spart) != NULL) return; +#endif /* WITH_MPI */ + +#ifdef SWIFT_DEBUG_CHECKS + // if (!c->split) error("Reached a leaf without finding a time-step task!"); +#endif + + /* Counters for the different quantities. */ + size_t updated = 0; + integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, + ti_stars_beg_max = 0; + + /* Collect the values from the progeny. */ + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL && cp->stars.count > 0) { + + /* Recurse */ + engine_collect_end_of_step_recurse_stars(cp, e); + + /* And update */ + ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); + ti_stars_end_max = max(ti_stars_end_max, cp->stars.ti_end_max); + ti_stars_beg_max = max(ti_stars_beg_max, cp->stars.ti_beg_max); + + updated += cp->stars.updated; + + /* Collected, so clear for next time. */ + cp->stars.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->stars.ti_end_min = ti_stars_end_min; + c->stars.ti_end_max = ti_stars_end_max; + c->stars.ti_beg_max = ti_stars_beg_max; + c->stars.updated = updated; +} + +/** + * @brief Recursive function gathering end-of-step data. + * + * We recurse until we encounter a timestep or time-step MPI recv task + * as the values will have been set at that level. We then bring these + * values upwards. + * + * @param c The #cell to recurse into. + * @param e The #engine. + */ +void engine_collect_end_of_step_recurse_black_holes(struct cell *c, + const struct engine *e) { + + /* 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 + // if (!c->split) error("Reached a leaf without finding a time-step task!"); +#endif + + /* Counters for the different quantities. */ + size_t updated = 0; + integertime_t ti_black_holes_end_min = max_nr_timesteps, + ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; + + /* Collect the values from the progeny. */ + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL && cp->black_holes.count > 0) { + + /* Recurse */ + engine_collect_end_of_step_recurse_black_holes(cp, e); + + /* And update */ + ti_black_holes_end_min = + min(ti_black_holes_end_min, cp->black_holes.ti_end_min); + ti_black_holes_end_max = + max(ti_black_holes_end_max, cp->black_holes.ti_end_max); + ti_black_holes_beg_max = + max(ti_black_holes_beg_max, cp->black_holes.ti_beg_max); + + updated += cp->black_holes.updated; + + /* Collected, so clear for next time. */ + cp->black_holes.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->black_holes.ti_end_min = ti_black_holes_end_min; + c->black_holes.ti_end_max = ti_black_holes_end_max; + c->black_holes.ti_beg_max = ti_black_holes_beg_max; + c->black_holes.updated = updated; +} + +/** + * @brief Mapping function to collect the data from the end of the step + * + * This function will call a recursive function on all the top-level cells + * to collect the information we are after. + * + * @param map_data The list of cells with tasks on this node. + * @param num_elements The number of elements in the list this thread will work + * on. + * @param extra_data The #engine. + */ +void engine_collect_end_of_step_mapper(void *map_data, int num_elements, + void *extra_data) { + + struct end_of_step_data *data = (struct end_of_step_data *)extra_data; + const struct engine *e = data->e; + const int with_hydro = (e->policy & engine_policy_hydro); + const int with_self_grav = (e->policy & engine_policy_self_gravity); + const int with_ext_grav = (e->policy & engine_policy_external_gravity); + const int with_grav = (with_self_grav || with_ext_grav); + 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; + int *local_cells = (int *)map_data; + struct star_formation_history *sfh_top = &data->sfh; + + /* Local collectible */ + size_t updated = 0, g_updated = 0, s_updated = 0, b_updated = 0; + integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, + ti_hydro_beg_max = 0; + integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, + ti_gravity_beg_max = 0; + integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, + ti_stars_beg_max = 0; + integertime_t ti_black_holes_end_min = max_nr_timesteps, + ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; + + /* Local Star formation history properties */ + struct star_formation_history sfh_updated; + + /* Initialize the star formation structs for this engine to zero */ + star_formation_logger_init(&sfh_updated); + + for (int ind = 0; ind < num_elements; ind++) { + struct cell *c = &s->cells_top[local_cells[ind]]; + + if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0 || + c->black_holes.count > 0) { + + /* Make the top-cells recurse */ + if (with_hydro) { + engine_collect_end_of_step_recurse_hydro(c, e); + } + if (with_grav) { + engine_collect_end_of_step_recurse_grav(c, e); + } + if (with_stars) { + engine_collect_end_of_step_recurse_stars(c, e); + } + if (with_black_holes) { + engine_collect_end_of_step_recurse_black_holes(c, e); + } + + /* And aggregate */ + if (c->hydro.ti_end_min > e->ti_current) + ti_hydro_end_min = min(ti_hydro_end_min, c->hydro.ti_end_min); + ti_hydro_end_max = max(ti_hydro_end_max, c->hydro.ti_end_max); + ti_hydro_beg_max = max(ti_hydro_beg_max, c->hydro.ti_beg_max); + + if (c->grav.ti_end_min > e->ti_current) + ti_gravity_end_min = min(ti_gravity_end_min, c->grav.ti_end_min); + ti_gravity_end_max = max(ti_gravity_end_max, c->grav.ti_end_max); + ti_gravity_beg_max = max(ti_gravity_beg_max, c->grav.ti_beg_max); + + if (c->stars.ti_end_min > e->ti_current) + ti_stars_end_min = min(ti_stars_end_min, c->stars.ti_end_min); + ti_stars_end_max = max(ti_stars_end_max, c->stars.ti_end_max); + ti_stars_beg_max = max(ti_stars_beg_max, c->stars.ti_beg_max); + + if (c->black_holes.ti_end_min > e->ti_current) + ti_black_holes_end_min = + min(ti_black_holes_end_min, c->black_holes.ti_end_min); + ti_black_holes_end_max = + max(ti_black_holes_end_max, c->black_holes.ti_end_max); + ti_black_holes_beg_max = + max(ti_black_holes_beg_max, c->black_holes.ti_beg_max); + + updated += c->hydro.updated; + g_updated += c->grav.updated; + s_updated += c->stars.updated; + b_updated += c->black_holes.updated; + + /* Check if the cell is inactive and in that case reorder the SFH */ + if (!cell_is_starting_hydro(c, e)) { + star_formation_logger_log_inactive_cell(&c->stars.sfh); + } + + /* Get the star formation history from the current cell and store it in + * the star formation history struct */ + star_formation_logger_add(&sfh_updated, &c->stars.sfh); + + /* Collected, so clear for next time. */ + c->hydro.updated = 0; + c->grav.updated = 0; + c->stars.updated = 0; + c->black_holes.updated = 0; + } + } + + /* Let's write back to the global data. + * We use the space lock to garanty single access*/ + if (lock_lock(&s->lock) == 0) { + data->updated += updated; + data->g_updated += g_updated; + data->s_updated += s_updated; + data->b_updated += b_updated; + + /* Add the SFH information from this engine to the global data */ + star_formation_logger_add(sfh_top, &sfh_updated); + + if (ti_hydro_end_min > e->ti_current) + data->ti_hydro_end_min = min(ti_hydro_end_min, data->ti_hydro_end_min); + data->ti_hydro_end_max = max(ti_hydro_end_max, data->ti_hydro_end_max); + data->ti_hydro_beg_max = max(ti_hydro_beg_max, data->ti_hydro_beg_max); + + if (ti_gravity_end_min > e->ti_current) + data->ti_gravity_end_min = + min(ti_gravity_end_min, data->ti_gravity_end_min); + data->ti_gravity_end_max = + max(ti_gravity_end_max, data->ti_gravity_end_max); + data->ti_gravity_beg_max = + max(ti_gravity_beg_max, data->ti_gravity_beg_max); + + if (ti_stars_end_min > e->ti_current) + data->ti_stars_end_min = min(ti_stars_end_min, data->ti_stars_end_min); + data->ti_stars_end_max = max(ti_stars_end_max, data->ti_stars_end_max); + data->ti_stars_beg_max = max(ti_stars_beg_max, data->ti_stars_beg_max); + + if (ti_black_holes_end_min > e->ti_current) + data->ti_black_holes_end_min = + min(ti_black_holes_end_min, data->ti_black_holes_end_min); + data->ti_black_holes_end_max = + max(ti_black_holes_end_max, data->ti_black_holes_end_max); + data->ti_black_holes_beg_max = + max(ti_black_holes_beg_max, data->ti_black_holes_beg_max); + } + + if (lock_unlock(&s->lock) != 0) error("Failed to unlock the space"); +} + +/** + * @brief Collects the next time-step and rebuild flag. + * + * The next time-step is determined by making each super-cell recurse to + * collect the minimal of ti_end and the number of updated particles. When in + * MPI mode this routines reduces these across all nodes and also collects the + * forcerebuild flag -- this is so that we only use a single collective MPI + * call per step for all these values. + * + * Note that the results are stored in e->collect_group1 struct not in the + * engine fields, unless apply is true. These can be applied field-by-field + * or all at once using collectgroup1_copy(); + * + * @param e The #engine. + * @param apply whether to apply the results to the engine or just keep in the + * group1 struct. + */ +void engine_collect_end_of_step(struct engine *e, int apply) { + + const ticks tic = getticks(); + struct space *s = e->s; + struct end_of_step_data data; + data.updated = 0, data.g_updated = 0, data.s_updated = 0, data.b_updated = 0; + data.ti_hydro_end_min = max_nr_timesteps, data.ti_hydro_end_max = 0, + data.ti_hydro_beg_max = 0; + data.ti_gravity_end_min = max_nr_timesteps, data.ti_gravity_end_max = 0, + data.ti_gravity_beg_max = 0; + data.ti_stars_end_min = max_nr_timesteps, data.ti_stars_end_max = 0, + data.ti_stars_beg_max = 0; + data.ti_black_holes_end_min = max_nr_timesteps, + data.ti_black_holes_end_max = 0, data.ti_black_holes_beg_max = 0; + data.e = e; + + /* Initialize the total SFH of the simulation to zero */ + star_formation_logger_init(&data.sfh); + + /* Collect information from the local top-level cells */ + threadpool_map(&e->threadpool, engine_collect_end_of_step_mapper, + s->local_cells_with_tasks_top, s->nr_local_cells_with_tasks, + sizeof(int), 0, &data); + + /* Get the number of inhibited particles from the space-wide counters + * since these have been updated atomically during the time-steps. */ + data.inhibited = s->nr_inhibited_parts; + data.g_inhibited = s->nr_inhibited_gparts; + data.s_inhibited = s->nr_inhibited_sparts; + data.b_inhibited = s->nr_inhibited_bparts; + + /* Store these in the temporary collection group. */ + collectgroup1_init( + &e->collect_group1, data.updated, data.g_updated, data.s_updated, + data.b_updated, data.inhibited, data.g_inhibited, data.s_inhibited, + data.b_inhibited, data.ti_hydro_end_min, data.ti_hydro_end_max, + data.ti_hydro_beg_max, data.ti_gravity_end_min, data.ti_gravity_end_max, + data.ti_gravity_beg_max, data.ti_stars_end_min, data.ti_stars_end_max, + data.ti_stars_beg_max, data.ti_black_holes_end_min, + data.ti_black_holes_end_max, data.ti_black_holes_beg_max, e->forcerebuild, + e->s->tot_cells, e->sched.nr_tasks, + (float)e->sched.nr_tasks / (float)e->s->tot_cells, data.sfh); + +/* Aggregate collective data from the different nodes for this step. */ +#ifdef WITH_MPI + collectgroup1_reduce(&e->collect_group1); + +#ifdef SWIFT_DEBUG_CHECKS + { + /* Check the above using the original MPI calls. */ + integertime_t in_i[2], out_i[2]; + in_i[0] = 0; + in_i[1] = 0; + out_i[0] = data.ti_hydro_end_min; + out_i[1] = data.ti_gravity_end_min; + if (MPI_Allreduce(out_i, in_i, 2, MPI_LONG_LONG_INT, MPI_MIN, + MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to aggregate ti_end_min."); + if (in_i[0] != (long long)e->collect_group1.ti_hydro_end_min) + error("Failed to get same ti_hydro_end_min, is %lld, should be %lld", + in_i[0], e->collect_group1.ti_hydro_end_min); + if (in_i[1] != (long long)e->collect_group1.ti_gravity_end_min) + error("Failed to get same ti_gravity_end_min, is %lld, should be %lld", + in_i[1], e->collect_group1.ti_gravity_end_min); + + long long in_ll[4], out_ll[4]; + out_ll[0] = data.updated; + out_ll[1] = data.g_updated; + out_ll[2] = data.s_updated; + out_ll[3] = data.b_updated; + if (MPI_Allreduce(out_ll, in_ll, 4, MPI_LONG_LONG_INT, MPI_SUM, + MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to aggregate particle counts."); + if (in_ll[0] != (long long)e->collect_group1.updated) + error("Failed to get same updated, is %lld, should be %lld", in_ll[0], + e->collect_group1.updated); + if (in_ll[1] != (long long)e->collect_group1.g_updated) + error("Failed to get same g_updated, is %lld, should be %lld", in_ll[1], + e->collect_group1.g_updated); + if (in_ll[2] != (long long)e->collect_group1.s_updated) + error("Failed to get same s_updated, is %lld, should be %lld", in_ll[2], + e->collect_group1.s_updated); + if (in_ll[3] != (long long)e->collect_group1.b_updated) + error("Failed to get same b_updated, is %lld, should be %lld", in_ll[3], + e->collect_group1.b_updated); + + out_ll[0] = data.inhibited; + out_ll[1] = data.g_inhibited; + out_ll[2] = data.s_inhibited; + out_ll[3] = data.b_inhibited; + if (MPI_Allreduce(out_ll, in_ll, 4, MPI_LONG_LONG_INT, MPI_SUM, + MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to aggregate particle counts."); + if (in_ll[0] != (long long)e->collect_group1.inhibited) + error("Failed to get same inhibited, is %lld, should be %lld", in_ll[0], + e->collect_group1.inhibited); + if (in_ll[1] != (long long)e->collect_group1.g_inhibited) + error("Failed to get same g_inhibited, is %lld, should be %lld", in_ll[1], + e->collect_group1.g_inhibited); + if (in_ll[2] != (long long)e->collect_group1.s_inhibited) + error("Failed to get same s_inhibited, is %lld, should be %lld", in_ll[2], + e->collect_group1.s_inhibited); + if (in_ll[3] != (long long)e->collect_group1.b_inhibited) + error("Failed to get same b_inhibited, is %lld, should be %lld", in_ll[3], + e->collect_group1.b_inhibited); + + int buff = 0; + if (MPI_Allreduce(&e->forcerebuild, &buff, 1, MPI_INT, MPI_MAX, + MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to aggregate the rebuild flag across nodes."); + if (!!buff != !!e->collect_group1.forcerebuild) + error( + "Failed to get same rebuild flag from all nodes, is %d," + "should be %d", + buff, e->collect_group1.forcerebuild); + } +#endif +#endif + + /* Apply to the engine, if requested. */ + if (apply) collectgroup1_apply(&e->collect_group1, e); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} diff --git a/src/engine_fof.c b/src/engine_fof.c new file mode 100644 index 0000000000000000000000000000000000000000..f1bb5b452104642f68b4a9987a1ab8d8e3b0162b --- /dev/null +++ b/src/engine_fof.c @@ -0,0 +1,150 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "engine.h" + +/** + * @brief Activate all the #gpart communications in preparation + * fof a call to FOF. + * + * @param e The #engine to act on. + */ +void engine_activate_gpart_comms(struct engine *e) { + +#ifdef WITH_MPI + + const ticks tic = getticks(); + + struct scheduler *s = &e->sched; + const int nr_tasks = s->nr_tasks; + struct task *tasks = s->tasks; + + for (int k = 0; k < nr_tasks; ++k) { + + struct task *t = &tasks[k]; + + if ((t->type == task_type_send) && (t->subtype == task_subtype_gpart)) { + scheduler_activate(s, t); + } else if ((t->type == task_type_recv) && + (t->subtype == task_subtype_gpart)) { + scheduler_activate(s, t); + } else { + t->skip = 1; + } + } + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); + +#else + error("Calling an MPI function in non-MPI mode."); +#endif +} + +/** + * @brief Activate all the FOF tasks. + * + * Marks all the other task types to be skipped. + * + * @param e The #engine to act on. + */ +void engine_activate_fof_tasks(struct engine *e) { + + const ticks tic = getticks(); + + struct scheduler *s = &e->sched; + const int nr_tasks = s->nr_tasks; + struct task *tasks = s->tasks; + + for (int k = 0; k < nr_tasks; k++) { + + struct task *t = &tasks[k]; + + if (t->type == task_type_fof_self || t->type == task_type_fof_pair) + scheduler_activate(s, t); + else + t->skip = 1; + } + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Run a FOF search. + * + * @param e the engine + * @param dump_results Are we writing group catalogues to output files? + * @param seed_black_holes Are we seeding black holes? + */ +void engine_fof(struct engine *e, const int dump_results, + const int seed_black_holes) { + +#ifdef WITH_FOF + + ticks tic = getticks(); + + /* Compute number of DM particles */ + const long long total_nr_baryons = + e->total_nr_parts + e->total_nr_sparts + e->total_nr_bparts; + const long long total_nr_dmparts = + e->total_nr_gparts - e->total_nr_DM_background_gparts - total_nr_baryons; + + /* Initialise FOF parameters and allocate FOF arrays. */ + fof_allocate(e->s, total_nr_dmparts, e->fof_properties); + + /* Make FOF tasks */ + engine_make_fof_tasks(e); + + /* and activate them. */ + engine_activate_fof_tasks(e); + + /* Perform local FOF tasks. */ + engine_launch(e); + + /* Perform FOF search over foreign particles and + * find groups which require black hole seeding. */ + fof_search_tree(e->fof_properties, e->black_holes_properties, + e->physical_constants, e->cosmology, e->s, dump_results, + seed_black_holes); + + /* Reset flag. */ + e->run_fof = 0; + + /* Flag that a FOF has taken place */ + e->step_props |= engine_step_prop_fof; + + /* ... and find the next FOF time */ + if (seed_black_holes) engine_compute_next_fof_time(e); + + if (engine_rank == 0) + message("Complete FOF search took: %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); +#else + error("SWIFT was not compiled with FOF enabled!"); +#endif +} diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c index 04ec7baed73a1ab42de2aabcac1113a224a774e4..b9851b0280aaf2eaaf1e78f3b7db65547eb6bd07 100644 --- a/src/engine_maketasks.c +++ b/src/engine_maketasks.c @@ -54,6 +54,7 @@ extern int engine_max_parts_per_ghost; extern int engine_max_sparts_per_ghost; +extern int engine_star_resort_task_depth; /** * @brief Add send tasks for the gravity pairs to a hierarchy of cells. @@ -73,6 +74,9 @@ void engine_addtasks_send_gravity(struct engine *e, struct cell *ci, struct scheduler *s = &e->sched; const int nodeID = cj->nodeID; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(ci, cell_flag_has_tasks)) return; + /* Check if any of the gravity tasks are for the target node. */ for (l = ci->grav.grav; l != NULL; l = l->next) if (l->t->ci->nodeID == nodeID || @@ -140,6 +144,9 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, struct scheduler *s = &e->sched; const int nodeID = cj->nodeID; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(ci, cell_flag_has_tasks)) return; + /* Check if any of the density tasks are for the target node. */ for (l = ci->hydro.density; l != NULL; l = l->next) if (l->t->ci->nodeID == nodeID || @@ -247,6 +254,9 @@ void engine_addtasks_send_stars(struct engine *e, struct cell *ci, struct scheduler *s = &e->sched; const int nodeID = cj->nodeID; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(ci, cell_flag_has_tasks)) return; + if (t_sf_counts == NULL && with_star_formation && ci->hydro.count > 0) { #ifdef SWIFT_DEBUG_CHECKS if (ci->depth != 0) @@ -319,7 +329,7 @@ void engine_addtasks_send_stars(struct engine *e, struct cell *ci, * @param ci The sending #cell. * @param cj Dummy cell containing the nodeID of the receiving node. * @param t_rho The density comm. task, if it has already been created. - * @param t_swallow The BH swallow comm. task, if it has already been created. + * @param t_bh_merger The BH swallow comm. task, if it has already been created. * @param t_gas_swallow The gas swallow comm. task, if it has already been * created. * @param t_feedback The send_feed #task, if it has already been created. @@ -327,7 +337,7 @@ void engine_addtasks_send_stars(struct engine *e, struct cell *ci, */ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, struct cell *cj, struct task *t_rho, - struct task *t_swallow, + struct task *t_bh_merger, struct task *t_gas_swallow, struct task *t_feedback, struct task *t_ti) { @@ -338,6 +348,9 @@ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, struct scheduler *s = &e->sched; const int nodeID = cj->nodeID; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(ci, cell_flag_has_tasks)) return; + /* 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 || @@ -356,9 +369,8 @@ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, t_rho = scheduler_addtask(s, task_type_send, task_subtype_bpart_rho, ci->mpi.tag, 0, ci, cj); - /* t_swallow = */ - /* scheduler_addtask(s, task_type_send, task_subtype_bpart_swallow, */ - /* ci->mpi.tag, 0, ci, cj); */ + t_bh_merger = scheduler_addtask( + s, task_type_send, task_subtype_bpart_merger, ci->mpi.tag, 0, ci, cj); t_gas_swallow = scheduler_addtask( s, task_type_send, task_subtype_part_swallow, ci->mpi.tag, 0, ci, cj); @@ -375,20 +387,19 @@ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, scheduler_addunlock(s, t_feedback, ci->hydro.super->black_holes.black_holes_out); - scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost[1], + scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost[2], t_feedback); /* Ghost before you send */ + scheduler_addunlock(s, ci->hydro.super->black_holes.drift, t_rho); scheduler_addunlock(s, ci->hydro.super->black_holes.density_ghost, t_rho); scheduler_addunlock(s, t_rho, ci->hydro.super->black_holes.swallow_ghost[0]); - /* Drift before you send */ - /* scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost[0], - */ - /* t_swallow); */ - /* scheduler_addunlock(s, t_swallow, */ - /* ci->hydro.super->black_holes.swallow_ghost[1]); */ + scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost[0], + t_bh_merger); + scheduler_addunlock(s, t_bh_merger, + ci->hydro.super->black_holes.swallow_ghost[2]); scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost[0], t_gas_swallow); @@ -399,7 +410,7 @@ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, } engine_addlink(e, &ci->mpi.send, t_rho); - // engine_addlink(e, &ci->mpi.send, t_swallow); + engine_addlink(e, &ci->mpi.send, t_bh_merger); engine_addlink(e, &ci->mpi.send, t_gas_swallow); engine_addlink(e, &ci->mpi.send, t_feedback); engine_addlink(e, &ci->mpi.send, t_ti); @@ -410,7 +421,7 @@ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, for (int k = 0; k < 8; k++) if (ci->progeny[k] != NULL) engine_addtasks_send_black_holes(e, ci->progeny[k], cj, t_rho, - t_swallow, t_gas_swallow, t_feedback, + t_bh_merger, t_gas_swallow, t_feedback, t_ti); #else @@ -435,6 +446,9 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c, #ifdef WITH_MPI struct scheduler *s = &e->sched; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + /* Have we reached a level where there are any hydro tasks ? */ if (t_xv == NULL && c->hydro.density != NULL) { @@ -534,6 +548,9 @@ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, #ifdef WITH_MPI struct scheduler *s = &e->sched; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + if (t_sf_counts == NULL && with_star_formation && c->hydro.count > 0) { #ifdef SWIFT_DEBUG_CHECKS if (c->depth != 0) @@ -609,7 +626,7 @@ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, * @param e The #engine. * @param c The foreign #cell. * @param t_rho The density comm. task, if it has already been created. - * @param t_swallow The BH swallow comm. task, if it has already been created. + * @param t_bh_merger The BH swallow comm. task, if it has already been created. * @param t_gas_swallow The gas swallow comm. task, if it has already been * created. * @param t_feedback The recv_feed #task, if it has already been created. @@ -617,7 +634,7 @@ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, */ void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c, struct task *t_rho, - struct task *t_swallow, + struct task *t_bh_merger, struct task *t_gas_swallow, struct task *t_feedback, struct task *t_ti) { @@ -625,6 +642,9 @@ void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c, #ifdef WITH_MPI struct scheduler *s = &e->sched; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + /* Have we reached a level where there are any black_holes tasks ? */ if (t_rho == NULL && c->black_holes.density != NULL) { @@ -637,9 +657,8 @@ void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c, t_rho = scheduler_addtask(s, task_type_recv, task_subtype_bpart_rho, c->mpi.tag, 0, c, NULL); - /* t_swallow = scheduler_addtask(s, task_type_recv, - * task_subtype_bpart_swallow, */ - /* c->mpi.tag, 0, c, NULL); */ + t_bh_merger = scheduler_addtask( + s, task_type_recv, task_subtype_bpart_merger, c->mpi.tag, 0, c, NULL); t_gas_swallow = scheduler_addtask( s, task_type_recv, task_subtype_part_swallow, c->mpi.tag, 0, c, NULL); @@ -653,7 +672,7 @@ void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c, if (t_rho != NULL) { engine_addlink(e, &c->mpi.recv, t_rho); - // engine_addlink(e, &c->mpi.recv, t_swallow); + engine_addlink(e, &c->mpi.recv, t_bh_merger); engine_addlink(e, &c->mpi.recv, t_gas_swallow); engine_addlink(e, &c->mpi.recv, t_feedback); engine_addlink(e, &c->mpi.recv, t_ti); @@ -672,12 +691,16 @@ void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c, for (struct link *l = c->black_holes.swallow; l != NULL; l = l->next) { scheduler_addunlock(s, t_rho, l->t); - // scheduler_addunlock(s, l->t, t_swallow); scheduler_addunlock(s, l->t, t_gas_swallow); + scheduler_addunlock(s, l->t, t_bh_merger); } - for (struct link *l = c->black_holes.do_swallow; l != NULL; l = l->next) { - // scheduler_addunlock(s, t_swallow, l->t); + for (struct link *l = c->black_holes.do_gas_swallow; l != NULL; + l = l->next) { scheduler_addunlock(s, t_gas_swallow, l->t); + } + for (struct link *l = c->black_holes.do_bh_swallow; l != NULL; + l = l->next) { + scheduler_addunlock(s, t_bh_merger, l->t); scheduler_addunlock(s, l->t, t_feedback); } for (struct link *l = c->black_holes.feedback; l != NULL; l = l->next) { @@ -690,7 +713,7 @@ void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c, 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_rho, t_swallow, + engine_addtasks_recv_black_holes(e, c->progeny[k], t_rho, t_bh_merger, t_gas_swallow, t_feedback, t_ti); #else @@ -712,6 +735,9 @@ void engine_addtasks_recv_gravity(struct engine *e, struct cell *c, #ifdef WITH_MPI struct scheduler *s = &e->sched; + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + /* Have we reached a level where there are any gravity tasks ? */ if (t_grav == NULL && c->grav.grav != NULL) { @@ -774,9 +800,6 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { if (with_star_formation && c->hydro.count > 0) { c->hydro.star_formation = scheduler_addtask( s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL); - c->hydro.stars_resort = scheduler_addtask( - s, task_type_stars_resort, task_subtype_none, 0, 0, c, NULL); - scheduler_addunlock(s, c->hydro.star_formation, c->hydro.stars_resort); } } @@ -790,24 +813,33 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { c->kick1 = scheduler_addtask(s, task_type_kick1, task_subtype_none, 0, 0, c, NULL); + c->kick2 = scheduler_addtask(s, task_type_kick2, task_subtype_none, 0, 0, + c, NULL); + #if defined(WITH_LOGGER) + /* Add the hydro logger task. */ c->logger = scheduler_addtask(s, task_type_logger, task_subtype_none, 0, 0, c, NULL); -#endif - c->kick2 = scheduler_addtask(s, task_type_kick2, task_subtype_none, 0, 0, - c, NULL); + /* Add the kick2 dependency */ + scheduler_addunlock(s, c->kick2, c->logger); + + /* Create a variable in order to avoid to many ifdef */ + struct task *kick2_or_logger = c->logger; +#else + struct task *kick2_or_logger = c->kick2; +#endif /* Add the time-step calculation task and its dependency */ c->timestep = scheduler_addtask(s, task_type_timestep, task_subtype_none, 0, 0, c, NULL); - scheduler_addunlock(s, c->kick2, c->timestep); + scheduler_addunlock(s, kick2_or_logger, c->timestep); scheduler_addunlock(s, c->timestep, c->kick1); /* Subgrid tasks: star formation */ if (with_star_formation && c->hydro.count > 0) { - scheduler_addunlock(s, c->kick2, c->top->hydro.star_formation); + scheduler_addunlock(s, kick2_or_logger, c->top->hydro.star_formation); scheduler_addunlock(s, c->top->hydro.star_formation, c->timestep); } @@ -820,10 +852,6 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { scheduler_addunlock(s, c->timestep, c->timestep_limiter); scheduler_addunlock(s, c->timestep_limiter, c->kick1); } - -#if defined(WITH_LOGGER) - scheduler_addunlock(s, c->kick1, c->logger); -#endif } } else { /* We are above the super-cell so need to go deeper */ @@ -978,8 +1006,11 @@ void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in, * * @param e The #engine. * @param c The #cell. + * @param star_resort_cell Pointer to the cell where the star_resort task has + * been created. NULL above that level or if not running with star formation. */ -void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { +void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, + struct cell *star_resort_cell) { struct scheduler *s = &e->sched; const int with_stars = (e->policy & engine_policy_stars); @@ -988,6 +1019,25 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { const int with_star_formation = (e->policy & engine_policy_star_formation); const int with_black_holes = (e->policy & engine_policy_black_holes); + /* Are we are the level where we create the stars' resort tasks? + * If the tree is shallow, we need to do this at the super-level if the + * super-level is above the level we want */ + if ((c->nodeID == e->nodeID) && (star_resort_cell == NULL) && + (c->depth == engine_star_resort_task_depth || c->hydro.super == c)) { + + if (with_star_formation && c->hydro.count > 0) { + + /* Record this is the level where we re-sort */ + star_resort_cell = c; + + c->hydro.stars_resort = scheduler_addtask( + s, task_type_stars_resort, task_subtype_none, 0, 0, c, NULL); + + scheduler_addunlock(s, c->top->hydro.star_formation, + c->hydro.stars_resort); + } + } + /* Are we in a super-cell ? */ if (c->hydro.super == c) { @@ -1073,11 +1123,16 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { c->stars.ghost = scheduler_addtask(s, task_type_stars_ghost, task_subtype_none, 0, 0, c, NULL); +#ifdef WITH_LOGGER + scheduler_addunlock(s, c->super->logger, c->stars.stars_in); +#else scheduler_addunlock(s, c->super->kick2, c->stars.stars_in); +#endif scheduler_addunlock(s, c->stars.stars_out, c->super->timestep); if (with_star_formation && c->hydro.count > 0) { - scheduler_addunlock(s, c->top->hydro.stars_resort, c->stars.stars_in); + scheduler_addunlock(s, star_resort_cell->hydro.stars_resort, + c->stars.stars_in); } } @@ -1095,10 +1150,18 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { c->black_holes.density_ghost = scheduler_addtask( s, task_type_bh_density_ghost, task_subtype_none, 0, 0, c, NULL); - c->black_holes.swallow_ghost[1] = scheduler_addtask( - s, task_type_bh_swallow_ghost2, task_subtype_none, 0, 0, c, NULL); + c->black_holes.swallow_ghost[1] = + scheduler_addtask(s, task_type_bh_swallow_ghost2, task_subtype_none, + 0, /* implicit =*/1, c, NULL); + c->black_holes.swallow_ghost[2] = scheduler_addtask( + s, task_type_bh_swallow_ghost3, task_subtype_none, 0, 0, c, NULL); + +#ifdef WITH_LOGGER + scheduler_addunlock(s, c->super->logger, c->black_holes.black_holes_in); +#else scheduler_addunlock(s, c->super->kick2, c->black_holes.black_holes_in); +#endif scheduler_addunlock(s, c->black_holes.black_holes_out, c->super->timestep); } @@ -1109,7 +1172,8 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) - engine_make_hierarchical_tasks_hydro(e, c->progeny[k]); + engine_make_hierarchical_tasks_hydro(e, c->progeny[k], + star_resort_cell); } } @@ -1126,7 +1190,8 @@ void engine_make_hierarchical_tasks_mapper(void *map_data, int num_elements, /* Make the common tasks (time integration) */ engine_make_hierarchical_tasks_common(e, c); /* Add the hydro stuff */ - if (with_hydro) engine_make_hierarchical_tasks_hydro(e, c); + if (with_hydro) + engine_make_hierarchical_tasks_hydro(e, c, /*star_resort_cell=*/NULL); /* And the gravity stuff */ if (with_self_gravity || with_ext_gravity) engine_make_hierarchical_tasks_gravity(e, c); @@ -1720,7 +1785,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, struct task *t_star_feedback = NULL; struct task *t_bh_density = NULL; struct task *t_bh_swallow = NULL; - struct task *t_do_swallow = NULL; + struct task *t_do_gas_swallow = NULL; + struct task *t_do_bh_swallow = NULL; struct task *t_bh_feedback = NULL; for (int ind = 0; ind < num_elements; ind++) { @@ -1729,8 +1795,21 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, const enum task_types t_type = t->type; const enum task_subtypes t_subtype = t->subtype; const long long flags = t->flags; - struct cell *ci = t->ci; - struct cell *cj = t->cj; + struct cell *const ci = t->ci; + struct cell *const cj = t->cj; + + /* Escape early */ + if (t->type == task_type_none) continue; + +#ifdef WITH_LOGGER + struct task *const ci_super_kick2_or_logger = ci->super->logger; + struct task *const cj_super_kick2_or_logger = + (cj == NULL) ? NULL : cj->super->logger; +#else + struct task *const ci_super_kick2_or_logger = ci->super->kick2; + struct task *const cj_super_kick2_or_logger = + (cj == NULL) ? NULL : cj->super->kick2; +#endif /* Sort tasks depend on the drift of the cell (gas version). */ if (t_type == task_type_sort && ci->nodeID == nodeID) { @@ -1776,8 +1855,12 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, sched, task_type_self, task_subtype_bh_density, flags, 0, ci, NULL); t_bh_swallow = scheduler_addtask( sched, task_type_self, task_subtype_bh_swallow, flags, 0, ci, NULL); - t_do_swallow = scheduler_addtask( - sched, task_type_self, task_subtype_do_swallow, flags, 0, ci, NULL); + t_do_gas_swallow = + scheduler_addtask(sched, task_type_self, + task_subtype_do_gas_swallow, flags, 0, ci, NULL); + t_do_bh_swallow = + scheduler_addtask(sched, task_type_self, task_subtype_do_bh_swallow, + flags, 0, ci, NULL); t_bh_feedback = scheduler_addtask(sched, task_type_self, task_subtype_bh_feedback, flags, 0, ci, NULL); @@ -1795,7 +1878,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, if (with_black_holes && bcount_i > 0) { engine_addlink(e, &ci->black_holes.density, t_bh_density); engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow); - engine_addlink(e, &ci->black_holes.do_swallow, t_do_swallow); + engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow); + engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow); engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); } @@ -1853,20 +1937,27 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, scheduler_addunlock(sched, t_bh_swallow, ci->hydro.super->black_holes.swallow_ghost[0]); - scheduler_addunlock( - sched, ci->hydro.super->black_holes.swallow_ghost[0], t_do_swallow); - scheduler_addunlock(sched, t_do_swallow, + scheduler_addunlock(sched, + ci->hydro.super->black_holes.swallow_ghost[0], + t_do_gas_swallow); + scheduler_addunlock(sched, t_do_gas_swallow, ci->hydro.super->black_holes.swallow_ghost[1]); scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost[1], + t_do_bh_swallow); + scheduler_addunlock(sched, t_do_bh_swallow, + ci->hydro.super->black_holes.swallow_ghost[2]); + + scheduler_addunlock(sched, + ci->hydro.super->black_holes.swallow_ghost[2], 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, ci_super_kick2_or_logger, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter); } @@ -1918,8 +2009,12 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, sched, task_type_pair, task_subtype_bh_density, flags, 0, ci, cj); t_bh_swallow = scheduler_addtask( sched, task_type_pair, task_subtype_bh_swallow, flags, 0, ci, cj); - t_do_swallow = scheduler_addtask( - sched, task_type_pair, task_subtype_do_swallow, flags, 0, ci, cj); + t_do_gas_swallow = + scheduler_addtask(sched, task_type_pair, + task_subtype_do_gas_swallow, flags, 0, ci, cj); + t_do_bh_swallow = + scheduler_addtask(sched, task_type_pair, task_subtype_do_bh_swallow, + flags, 0, ci, cj); t_bh_feedback = scheduler_addtask( sched, task_type_pair, task_subtype_bh_feedback, flags, 0, ci, cj); } @@ -1941,8 +2036,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &cj->black_holes.density, t_bh_density); engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow); engine_addlink(e, &cj->black_holes.swallow, t_bh_swallow); - engine_addlink(e, &ci->black_holes.do_swallow, t_do_swallow); - engine_addlink(e, &cj->black_holes.do_swallow, t_do_swallow); + engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow); + engine_addlink(e, &cj->black_holes.do_gas_swallow, t_do_gas_swallow); + engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow); + engine_addlink(e, &cj->black_holes.do_bh_swallow, t_do_bh_swallow); engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback); } @@ -2032,19 +2129,25 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost[0], - t_do_swallow); - scheduler_addunlock(sched, t_do_swallow, + t_do_gas_swallow); + scheduler_addunlock(sched, t_do_gas_swallow, ci->hydro.super->black_holes.swallow_ghost[1]); scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost[1], + t_do_bh_swallow); + scheduler_addunlock(sched, t_do_bh_swallow, + ci->hydro.super->black_holes.swallow_ghost[2]); + + scheduler_addunlock(sched, + ci->hydro.super->black_holes.swallow_ghost[2], 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, ci_super_kick2_or_logger, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter); } @@ -2104,19 +2207,25 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, scheduler_addunlock(sched, cj->hydro.super->black_holes.swallow_ghost[0], - t_do_swallow); - scheduler_addunlock(sched, t_do_swallow, + t_do_gas_swallow); + scheduler_addunlock(sched, t_do_gas_swallow, cj->hydro.super->black_holes.swallow_ghost[1]); scheduler_addunlock(sched, cj->hydro.super->black_holes.swallow_ghost[1], + t_do_bh_swallow); + scheduler_addunlock(sched, t_do_bh_swallow, + cj->hydro.super->black_holes.swallow_ghost[2]); + + scheduler_addunlock(sched, + cj->hydro.super->black_holes.swallow_ghost[2], 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, cj_super_kick2_or_logger, t_limiter); scheduler_addunlock(sched, t_limiter, cj->super->timestep); scheduler_addunlock(sched, t_limiter, cj->super->timestep_limiter); } @@ -2174,9 +2283,13 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, scheduler_addtask(sched, task_type_sub_self, task_subtype_bh_swallow, flags, 0, ci, NULL); - t_do_swallow = + t_do_gas_swallow = + scheduler_addtask(sched, task_type_sub_self, + task_subtype_do_gas_swallow, flags, 0, ci, NULL); + + t_do_bh_swallow = scheduler_addtask(sched, task_type_sub_self, - task_subtype_do_swallow, flags, 0, ci, NULL); + task_subtype_do_bh_swallow, flags, 0, ci, NULL); t_bh_feedback = scheduler_addtask(sched, task_type_sub_self, @@ -2195,7 +2308,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, if (with_black_holes && bcount_i > 0) { engine_addlink(e, &ci->black_holes.density, t_bh_density); engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow); - engine_addlink(e, &ci->black_holes.do_swallow, t_do_swallow); + engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow); + engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow); engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); } @@ -2259,20 +2373,27 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, scheduler_addunlock(sched, t_bh_swallow, ci->hydro.super->black_holes.swallow_ghost[0]); - scheduler_addunlock( - sched, ci->hydro.super->black_holes.swallow_ghost[0], t_do_swallow); - scheduler_addunlock(sched, t_do_swallow, + scheduler_addunlock(sched, + ci->hydro.super->black_holes.swallow_ghost[0], + t_do_gas_swallow); + scheduler_addunlock(sched, t_do_gas_swallow, ci->hydro.super->black_holes.swallow_ghost[1]); scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost[1], + t_do_bh_swallow); + scheduler_addunlock(sched, t_do_bh_swallow, + ci->hydro.super->black_holes.swallow_ghost[2]); + + scheduler_addunlock(sched, + ci->hydro.super->black_holes.swallow_ghost[2], 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, ci_super_kick2_or_logger, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter); } @@ -2328,9 +2449,12 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, t_bh_swallow = scheduler_addtask(sched, task_type_sub_pair, task_subtype_bh_swallow, flags, 0, ci, cj); - t_do_swallow = + t_do_gas_swallow = + scheduler_addtask(sched, task_type_sub_pair, + task_subtype_do_gas_swallow, flags, 0, ci, cj); + t_do_bh_swallow = scheduler_addtask(sched, task_type_sub_pair, - task_subtype_do_swallow, flags, 0, ci, cj); + task_subtype_do_bh_swallow, flags, 0, ci, cj); t_bh_feedback = scheduler_addtask(sched, task_type_sub_pair, task_subtype_bh_feedback, flags, 0, ci, cj); @@ -2353,8 +2477,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &cj->black_holes.density, t_bh_density); engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow); engine_addlink(e, &cj->black_holes.swallow, t_bh_swallow); - engine_addlink(e, &ci->black_holes.do_swallow, t_do_swallow); - engine_addlink(e, &cj->black_holes.do_swallow, t_do_swallow); + engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow); + engine_addlink(e, &cj->black_holes.do_gas_swallow, t_do_gas_swallow); + engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow); + engine_addlink(e, &cj->black_holes.do_bh_swallow, t_do_bh_swallow); engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback); } @@ -2443,19 +2569,25 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost[0], - t_do_swallow); - scheduler_addunlock(sched, t_do_swallow, + t_do_gas_swallow); + scheduler_addunlock(sched, t_do_gas_swallow, ci->hydro.super->black_holes.swallow_ghost[1]); scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost[1], + t_do_bh_swallow); + scheduler_addunlock(sched, t_do_bh_swallow, + ci->hydro.super->black_holes.swallow_ghost[2]); + + scheduler_addunlock(sched, + ci->hydro.super->black_holes.swallow_ghost[2], 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, ci_super_kick2_or_logger, t_limiter); scheduler_addunlock(sched, t_limiter, ci->super->timestep); scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter); } @@ -2517,19 +2649,25 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, scheduler_addunlock(sched, cj->hydro.super->black_holes.swallow_ghost[0], - t_do_swallow); - scheduler_addunlock(sched, t_do_swallow, + t_do_gas_swallow); + scheduler_addunlock(sched, t_do_gas_swallow, cj->hydro.super->black_holes.swallow_ghost[1]); scheduler_addunlock(sched, cj->hydro.super->black_holes.swallow_ghost[1], + t_do_bh_swallow); + scheduler_addunlock(sched, t_do_bh_swallow, + cj->hydro.super->black_holes.swallow_ghost[2]); + + scheduler_addunlock(sched, + cj->hydro.super->black_holes.swallow_ghost[2], 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, cj_super_kick2_or_logger, t_limiter); scheduler_addunlock(sched, t_limiter, cj->super->timestep); scheduler_addunlock(sched, t_limiter, cj->super->timestep_limiter); } @@ -2874,7 +3012,7 @@ void engine_make_fof_tasks(struct engine *e) { tic = getticks(); /* Split the tasks. */ - scheduler_splittasks(sched, /*fof_tasks=*/1); + scheduler_splittasks(sched, /*fof_tasks=*/1, e->verbose); if (e->verbose) message("Splitting FOF tasks took %.3f %s.", @@ -2954,7 +3092,7 @@ void engine_maketasks(struct engine *e) { tic2 = getticks(); /* Split the tasks. */ - scheduler_splittasks(sched, /*fof_tasks=*/0); + scheduler_splittasks(sched, /*fof_tasks=*/0, e->verbose); MPI_Barrier(MPI_COMM_WORLD); diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c index 0189b47c60ace9033454e23975c713ca8932aef2..7f4e2d4c4b4ae4865eb2c2320d021be534cc57c3 100644 --- a/src/engine_marktasks.c +++ b/src/engine_marktasks.c @@ -207,14 +207,26 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } else if (t_type == task_type_self && - t_subtype == task_subtype_do_swallow) { + t_subtype == task_subtype_do_gas_swallow) { if (ci_active_black_holes) { scheduler_activate(s, t); } } else if (t_type == task_type_sub_self && - t_subtype == task_subtype_do_swallow) { + t_subtype == task_subtype_do_gas_swallow) { + if (ci_active_black_holes) scheduler_activate(s, t); + } + + else if (t_type == task_type_self && + t_subtype == task_subtype_do_bh_swallow) { + if (ci_active_black_holes) { + scheduler_activate(s, t); + } + } + + else if (t_type == task_type_sub_self && + t_subtype == task_subtype_do_bh_swallow) { if (ci_active_black_holes) scheduler_activate(s, t); } @@ -404,14 +416,15 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Black_Holes density */ else if ((t_subtype == task_subtype_bh_density || t_subtype == task_subtype_bh_swallow || - t_subtype == task_subtype_do_swallow || + t_subtype == task_subtype_do_gas_swallow || + t_subtype == task_subtype_do_bh_swallow || t_subtype == task_subtype_bh_feedback) && (ci_active_black_holes || cj_active_black_holes) && (ci_nodeID == nodeID || cj_nodeID == nodeID)) { scheduler_activate(s, t); - /* Set the correct sorting flags */ + /* Set the correct drifting flags */ if (t_type == task_type_pair && t_subtype == task_subtype_bh_density) { if (ci_nodeID == nodeID) cell_activate_drift_bpart(ci, s); if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); @@ -674,6 +687,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, * swallowing */ scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rho); scheduler_activate_recv(s, ci->mpi.recv, task_subtype_part_swallow); + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_bpart_merger); /* Send the local BHs to tag the particles to swallow and to do * feedback */ @@ -701,6 +715,8 @@ void engine_marktasks_mapper(void *map_data, int num_elements, scheduler_activate_send(s, cj->mpi.send, task_subtype_rho, ci_nodeID); scheduler_activate_send(s, cj->mpi.send, task_subtype_part_swallow, ci_nodeID); + scheduler_activate_send(s, cj->mpi.send, task_subtype_bpart_merger, + ci_nodeID); /* Drift the cell which will be sent; note that not all sent particles will be drifted, only those that are needed. */ @@ -712,6 +728,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, * swallowing */ scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rho); scheduler_activate_recv(s, cj->mpi.recv, task_subtype_part_swallow); + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_bpart_merger); /* Send the local BHs to tag the particles to swallow and to do * feedback */ @@ -739,6 +756,8 @@ void engine_marktasks_mapper(void *map_data, int num_elements, scheduler_activate_send(s, ci->mpi.send, task_subtype_rho, cj_nodeID); scheduler_activate_send(s, ci->mpi.send, task_subtype_part_swallow, cj_nodeID); + scheduler_activate_send(s, ci->mpi.send, task_subtype_bpart_merger, + cj_nodeID); /* Drift the cell which will be sent; note that not all sent particles will be drifted, only those that are needed. */ @@ -892,7 +911,8 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Black hole ghost tasks ? */ else if (t_type == task_type_bh_density_ghost || t_type == task_type_bh_swallow_ghost1 || - t_type == task_type_bh_swallow_ghost2) { + t_type == task_type_bh_swallow_ghost2 || + t_type == task_type_bh_swallow_ghost3) { if (cell_is_active_black_holes(t->ci, e)) scheduler_activate(s, t); } @@ -919,10 +939,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } /* Subgrid tasks: star formation */ - else if (t_type == task_type_star_formation || - t_type == task_type_stars_resort) { + else if (t_type == task_type_star_formation) { if (cell_is_active_hydro(t->ci, e)) { - scheduler_activate(s, t); + cell_activate_star_formation_tasks(t->ci, s); cell_activate_super_spart_drifts(t->ci, s); } } diff --git a/src/engine_redistribute.c b/src/engine_redistribute.c new file mode 100644 index 0000000000000000000000000000000000000000..3132ad2665c67cd244ae1ec9ece75726788c1506 --- /dev/null +++ b/src/engine_redistribute.c @@ -0,0 +1,1031 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "engine.h" + +/* Local headers. */ +#include "memswap.h" + +#ifdef WITH_MPI + +/** + * Do the exchange of one type of particles with all the other nodes. + * + * @param label a label for the memory allocations of this particle type. + * @param counts 2D array with the counts of particles to exchange with + * each other node. + * @param parts the particle data to exchange + * @param new_nr_parts the number of particles this node will have after all + * exchanges have completed. + * @param sizeofparts sizeof the particle struct. + * @param alignsize the memory alignment required for this particle type. + * @param mpi_type the MPI_Datatype for these particles. + * @param nr_nodes the number of nodes to exchange with. + * @param nodeID the id of this node. + * + * @result new particle data constructed from all the exchanges with the + * given alignment. + */ +static void *engine_do_redistribute(const char *label, int *counts, char *parts, + size_t new_nr_parts, size_t sizeofparts, + size_t alignsize, MPI_Datatype mpi_type, + int nr_nodes, int nodeID) { + + /* Allocate a new particle array with some extra margin */ + char *parts_new = NULL; + if (swift_memalign( + label, (void **)&parts_new, alignsize, + sizeofparts * new_nr_parts * engine_redistribute_alloc_margin) != 0) + error("Failed to allocate new particle data."); + + /* Prepare MPI requests for the asynchronous communications */ + MPI_Request *reqs; + if ((reqs = (MPI_Request *)malloc(sizeof(MPI_Request) * 2 * nr_nodes)) == + NULL) + error("Failed to allocate MPI request list."); + + /* Only send and receive only "chunk" particles per request. So we need to + * loop as many times as necessary here. Make 2Gb/sizeofparts so we only + * send 2Gb packets. */ + const int chunk = INT_MAX / sizeofparts; + int sent = 0; + int recvd = 0; + + int activenodes = 1; + while (activenodes) { + + for (int k = 0; k < 2 * nr_nodes; k++) reqs[k] = MPI_REQUEST_NULL; + + /* Emit the sends and recvs for the data. */ + size_t offset_send = sent; + size_t offset_recv = recvd; + activenodes = 0; + + for (int k = 0; k < nr_nodes; k++) { + + /* Indices in the count arrays of the node of interest */ + const int ind_send = nodeID * nr_nodes + k; + const int ind_recv = k * nr_nodes + nodeID; + + /* Are we sending any data this loop? */ + int sending = counts[ind_send] - sent; + if (sending > 0) { + activenodes++; + if (sending > chunk) sending = chunk; + + /* If the send and receive is local then just copy. */ + if (k == nodeID) { + int receiving = counts[ind_recv] - recvd; + if (receiving > chunk) receiving = chunk; + memcpy(&parts_new[offset_recv * sizeofparts], + &parts[offset_send * sizeofparts], sizeofparts * receiving); + } else { + /* Otherwise send it. */ + int res = + MPI_Isend(&parts[offset_send * sizeofparts], sending, mpi_type, k, + ind_send, MPI_COMM_WORLD, &reqs[2 * k + 0]); + if (res != MPI_SUCCESS) + mpi_error(res, "Failed to isend parts to node %i.", k); + } + } + + /* If we're sending to this node, then move past it to next. */ + if (counts[ind_send] > 0) offset_send += counts[ind_send]; + + /* Are we receiving any data from this node? Note already done if coming + * from this node. */ + if (k != nodeID) { + int receiving = counts[ind_recv] - recvd; + if (receiving > 0) { + activenodes++; + if (receiving > chunk) receiving = chunk; + int res = MPI_Irecv(&parts_new[offset_recv * sizeofparts], receiving, + mpi_type, k, ind_recv, MPI_COMM_WORLD, + &reqs[2 * k + 1]); + if (res != MPI_SUCCESS) + mpi_error(res, "Failed to emit irecv of parts from node %i.", k); + } + } + + /* If we're receiving from this node, then move past it to next. */ + if (counts[ind_recv] > 0) offset_recv += counts[ind_recv]; + } + + /* Wait for all the sends and recvs to tumble in. */ + MPI_Status stats[2 * nr_nodes]; + int res; + if ((res = MPI_Waitall(2 * nr_nodes, reqs, stats)) != MPI_SUCCESS) { + for (int k = 0; k < 2 * nr_nodes; k++) { + char buff[MPI_MAX_ERROR_STRING]; + MPI_Error_string(stats[k].MPI_ERROR, buff, &res); + message("request from source %i, tag %i has error '%s'.", + stats[k].MPI_SOURCE, stats[k].MPI_TAG, buff); + } + error("Failed during waitall for part data."); + } + + /* Move to next chunks. */ + sent += chunk; + recvd += chunk; + } + + /* Free temps. */ + free(reqs); + + /* And return new memory. */ + return parts_new; +} +#endif + +#ifdef WITH_MPI /* redist_mapper */ + +/* Support for engine_redistribute threadpool dest mappers. */ +struct redist_mapper_data { + int *counts; + int *dest; + int nodeID; + int nr_nodes; + struct cell *cells; + struct space *s; + void *base; +}; + +/* Generic function for accumulating counts for TYPE parts. Note + * we use a local counts array to avoid the atomic_add in the parts + * loop. */ +#define ENGINE_REDISTRIBUTE_DEST_MAPPER(TYPE) \ + engine_redistribute_dest_mapper_##TYPE(void *map_data, int num_elements, \ + void *extra_data) { \ + struct TYPE *parts = (struct TYPE *)map_data; \ + struct redist_mapper_data *mydata = \ + (struct redist_mapper_data *)extra_data; \ + struct space *s = mydata->s; \ + int *dest = \ + mydata->dest + (ptrdiff_t)(parts - (struct TYPE *)mydata->base); \ + int *lcounts = NULL; \ + if ((lcounts = (int *)calloc( \ + sizeof(int), mydata->nr_nodes * mydata->nr_nodes)) == NULL) \ + error("Failed to allocate counts thread-specific buffer"); \ + for (int k = 0; k < num_elements; k++) { \ + for (int j = 0; j < 3; j++) { \ + if (parts[k].x[j] < 0.0) \ + parts[k].x[j] += s->dim[j]; \ + else if (parts[k].x[j] >= s->dim[j]) \ + parts[k].x[j] -= s->dim[j]; \ + } \ + const int cid = cell_getid(s->cdim, parts[k].x[0] * s->iwidth[0], \ + parts[k].x[1] * s->iwidth[1], \ + parts[k].x[2] * s->iwidth[2]); \ + dest[k] = s->cells_top[cid].nodeID; \ + size_t ind = mydata->nodeID * mydata->nr_nodes + dest[k]; \ + lcounts[ind] += 1; \ + } \ + for (int k = 0; k < (mydata->nr_nodes * mydata->nr_nodes); k++) \ + atomic_add(&mydata->counts[k], lcounts[k]); \ + free(lcounts); \ + } + +/** + * @brief Accumulate the counts of particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. + * + * part version. + */ +static void ENGINE_REDISTRIBUTE_DEST_MAPPER(part); + +/** + * @brief Accumulate the counts of star particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. + * + * spart version. + */ +static void ENGINE_REDISTRIBUTE_DEST_MAPPER(spart); + +/** + * @brief Accumulate the counts of gravity particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. + * + * gpart version. + */ +static void ENGINE_REDISTRIBUTE_DEST_MAPPER(gpart); + +/** + * @brief Accumulate the counts of black holes particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. + * + * bpart version. + */ +static void ENGINE_REDISTRIBUTE_DEST_MAPPER(bpart); + +#endif /* redist_mapper_data */ + +#ifdef WITH_MPI /* savelink_mapper_data */ + +/* Support for saving the linkage between gparts and parts/sparts. */ +struct savelink_mapper_data { + int nr_nodes; + int *counts; + void *parts; + int nodeID; +}; + +/** + * @brief Save the offset of each gravity partner of a part or spart. + * + * The offset is from the start of the sorted particles to be sent to a node. + * This is possible as parts without gravity partners have a positive id. + * These offsets are used to restore the pointers on the receiving node. + * + * CHECKS should be eliminated as dead code when optimizing. + */ +#define ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(TYPE, CHECKS) \ + engine_redistribute_savelink_mapper_##TYPE(void *map_data, int num_elements, \ + void *extra_data) { \ + int *nodes = (int *)map_data; \ + struct savelink_mapper_data *mydata = \ + (struct savelink_mapper_data *)extra_data; \ + int nodeID = mydata->nodeID; \ + int nr_nodes = mydata->nr_nodes; \ + int *counts = mydata->counts; \ + struct TYPE *parts = (struct TYPE *)mydata->parts; \ + \ + for (int j = 0; j < num_elements; j++) { \ + int node = nodes[j]; \ + int count = 0; \ + size_t offset = 0; \ + for (int i = 0; i < node; i++) offset += counts[nodeID * nr_nodes + i]; \ + \ + for (int k = 0; k < counts[nodeID * nr_nodes + node]; k++) { \ + if (parts[k + offset].gpart != NULL) { \ + if (CHECKS) \ + if (parts[k + offset].gpart->id_or_neg_offset > 0) \ + error("Trying to link a partnerless " #TYPE "!"); \ + parts[k + offset].gpart->id_or_neg_offset = -count; \ + count++; \ + } \ + } \ + } \ + } + +/** + * @brief Save position of part-gpart links. + * Threadpool helper for accumulating the counts of particles per cell. + */ +#ifdef SWIFT_DEBUG_CHECKS +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 1); +#else +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 0); +#endif + +/** + * @brief Save position of spart-gpart links. + * Threadpool helper for accumulating the counts of particles per cell. + */ +#ifdef SWIFT_DEBUG_CHECKS +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 1); +#else +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 0); +#endif + +/** + * @brief Save position of bpart-gpart links. + * Threadpool helper for accumulating the counts of particles per cell. + */ +#ifdef SWIFT_DEBUG_CHECKS +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 1); +#else +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 0); +#endif + +#endif /* savelink_mapper_data */ + +#ifdef WITH_MPI /* relink_mapper_data */ + +/* Support for relinking parts, gparts, sparts and bparts after moving between + * nodes. */ +struct relink_mapper_data { + int nodeID; + int nr_nodes; + int *counts; + int *s_counts; + int *g_counts; + int *b_counts; + struct space *s; +}; + +/** + * @brief Restore the part/gpart and spart/gpart links for a list of nodes. + * + * @param map_data address of nodes to process. + * @param num_elements the number nodes to process. + * @param extra_data additional data defining the context (a + * relink_mapper_data). + */ +static void engine_redistribute_relink_mapper(void *map_data, int num_elements, + void *extra_data) { + + int *nodes = (int *)map_data; + struct relink_mapper_data *mydata = (struct relink_mapper_data *)extra_data; + + int nodeID = mydata->nodeID; + int nr_nodes = mydata->nr_nodes; + int *counts = mydata->counts; + int *g_counts = mydata->g_counts; + int *s_counts = mydata->s_counts; + int *b_counts = mydata->b_counts; + struct space *s = mydata->s; + + for (int i = 0; i < num_elements; i++) { + + int node = nodes[i]; + + /* Get offsets to correct parts of the counts arrays for this node. */ + size_t offset_parts = 0; + size_t offset_gparts = 0; + size_t offset_sparts = 0; + size_t offset_bparts = 0; + for (int n = 0; n < node; n++) { + int ind_recv = n * nr_nodes + nodeID; + offset_parts += counts[ind_recv]; + offset_gparts += g_counts[ind_recv]; + offset_sparts += s_counts[ind_recv]; + offset_bparts += b_counts[ind_recv]; + } + + /* Number of gparts sent from this node. */ + int ind_recv = node * nr_nodes + nodeID; + const size_t count_gparts = g_counts[ind_recv]; + + /* Loop over the gparts received from this node */ + for (size_t k = offset_gparts; k < offset_gparts + count_gparts; k++) { + + /* Does this gpart have a gas partner ? */ + if (s->gparts[k].type == swift_type_gas) { + + const ptrdiff_t partner_index = + offset_parts - s->gparts[k].id_or_neg_offset; + + /* Re-link */ + s->gparts[k].id_or_neg_offset = -partner_index; + s->parts[partner_index].gpart = &s->gparts[k]; + } + + /* Does this gpart have a star partner ? */ + else if (s->gparts[k].type == swift_type_stars) { + + const ptrdiff_t partner_index = + offset_sparts - s->gparts[k].id_or_neg_offset; + + /* Re-link */ + s->gparts[k].id_or_neg_offset = -partner_index; + s->sparts[partner_index].gpart = &s->gparts[k]; + } + + /* Does this gpart have a black hole partner ? */ + else if (s->gparts[k].type == swift_type_black_hole) { + + const ptrdiff_t partner_index = + offset_bparts - s->gparts[k].id_or_neg_offset; + + /* Re-link */ + s->gparts[k].id_or_neg_offset = -partner_index; + s->bparts[partner_index].gpart = &s->gparts[k]; + } + } + } +} + +#endif /* relink_mapper_data */ + +/** + * @brief Redistribute the particles amongst the nodes according + * to their cell's node IDs. + * + * The strategy here is as follows: + * 1) Each node counts the number of particles it has to send to each other + * node. + * 2) The number of particles of each type is then exchanged. + * 3) The particles to send are placed in a temporary buffer in which the + * part-gpart links are preserved. + * 4) Each node allocates enough space for the new particles. + * 5) (Asynchronous) communications are issued to transfer the data. + * + * + * @param e The #engine. + */ +void engine_redistribute(struct engine *e) { + +#ifdef WITH_MPI + + const int nr_nodes = e->nr_nodes; + const int nodeID = e->nodeID; + struct space *s = e->s; + struct cell *cells = s->cells_top; + const int nr_cells = s->nr_cells; + struct xpart *xparts = s->xparts; + struct part *parts = s->parts; + struct gpart *gparts = s->gparts; + struct spart *sparts = s->sparts; + struct bpart *bparts = s->bparts; + ticks tic = getticks(); + + size_t nr_parts = s->nr_parts; + size_t nr_gparts = s->nr_gparts; + size_t nr_sparts = s->nr_sparts; + size_t nr_bparts = s->nr_bparts; + + /* Start by moving inhibited particles to the end of the arrays */ + for (size_t k = 0; k < nr_parts; /* void */) { + if (parts[k].time_bin == time_bin_inhibited || + parts[k].time_bin == time_bin_not_created) { + nr_parts -= 1; + + /* Swap the particle */ + memswap(&parts[k], &parts[nr_parts], sizeof(struct part)); + + /* Swap the xpart */ + memswap(&xparts[k], &xparts[nr_parts], sizeof(struct xpart)); + + /* Swap the link with the gpart */ + if (parts[k].gpart != NULL) { + parts[k].gpart->id_or_neg_offset = -k; + } + if (parts[nr_parts].gpart != NULL) { + parts[nr_parts].gpart->id_or_neg_offset = -nr_parts; + } + } else { + k++; + } + } + + /* Now move inhibited star particles to the end of the arrays */ + for (size_t k = 0; k < nr_sparts; /* void */) { + if (sparts[k].time_bin == time_bin_inhibited || + sparts[k].time_bin == time_bin_not_created) { + nr_sparts -= 1; + + /* Swap the particle */ + memswap(&s->sparts[k], &s->sparts[nr_sparts], sizeof(struct spart)); + + /* Swap the link with the gpart */ + if (s->sparts[k].gpart != NULL) { + s->sparts[k].gpart->id_or_neg_offset = -k; + } + if (s->sparts[nr_sparts].gpart != NULL) { + s->sparts[nr_sparts].gpart->id_or_neg_offset = -nr_sparts; + } + } else { + k++; + } + } + + /* Now move inhibited black hole particles to the end of the arrays */ + for (size_t k = 0; k < nr_bparts; /* void */) { + if (bparts[k].time_bin == time_bin_inhibited || + bparts[k].time_bin == time_bin_not_created) { + nr_bparts -= 1; + + /* Swap the particle */ + memswap(&s->bparts[k], &s->bparts[nr_bparts], sizeof(struct bpart)); + + /* Swap the link with the gpart */ + if (s->bparts[k].gpart != NULL) { + s->bparts[k].gpart->id_or_neg_offset = -k; + } + if (s->bparts[nr_bparts].gpart != NULL) { + s->bparts[nr_bparts].gpart->id_or_neg_offset = -nr_bparts; + } + } else { + k++; + } + } + + /* Finally do the same with the gravity particles */ + for (size_t k = 0; k < nr_gparts; /* void */) { + if (gparts[k].time_bin == time_bin_inhibited || + gparts[k].time_bin == time_bin_not_created) { + nr_gparts -= 1; + + /* Swap the particle */ + memswap(&s->gparts[k], &s->gparts[nr_gparts], sizeof(struct gpart)); + + /* Swap the link with part/spart */ + if (s->gparts[k].type == swift_type_gas) { + s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; + } else if (s->gparts[k].type == swift_type_stars) { + s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; + } else if (s->gparts[k].type == swift_type_black_hole) { + s->bparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; + } + + if (s->gparts[nr_gparts].type == swift_type_gas) { + s->parts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = + &s->gparts[nr_gparts]; + } else if (s->gparts[nr_gparts].type == swift_type_stars) { + s->sparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = + &s->gparts[nr_gparts]; + } else if (s->gparts[nr_gparts].type == swift_type_black_hole) { + s->bparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = + &s->gparts[nr_gparts]; + } + } else { + k++; + } + } + + /* Now we are ready to deal with real particles and can start the exchange. */ + + /* Allocate temporary arrays to store the counts of particles to be sent + * and the destination of each particle */ + int *counts; + if ((counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) + error("Failed to allocate counts temporary buffer."); + + int *dest; + if ((dest = (int *)swift_malloc("dest", sizeof(int) * nr_parts)) == NULL) + error("Failed to allocate dest temporary buffer."); + + /* Simple index of node IDs, used for mappers over nodes. */ + int *nodes = NULL; + if ((nodes = (int *)malloc(sizeof(int) * nr_nodes)) == NULL) + error("Failed to allocate nodes temporary buffer."); + for (int k = 0; k < nr_nodes; k++) nodes[k] = k; + + /* Get destination of each particle */ + struct redist_mapper_data redist_data; + redist_data.s = s; + redist_data.nodeID = nodeID; + redist_data.nr_nodes = nr_nodes; + + redist_data.counts = counts; + redist_data.dest = dest; + redist_data.base = (void *)parts; + + threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_part, parts, + nr_parts, sizeof(struct part), 0, &redist_data); + + /* Sort the particles according to their cell index. */ + if (nr_parts > 0) + space_parts_sort(s->parts, s->xparts, dest, &counts[nodeID * nr_nodes], + nr_nodes, 0); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that the part have been sorted correctly. */ + for (size_t k = 0; k < nr_parts; k++) { + const struct part *p = &s->parts[k]; + + if (p->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (p->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + + /* New cell index */ + const int new_cid = + cell_getid(s->cdim, p->x[0] * s->iwidth[0], p->x[1] * s->iwidth[1], + p->x[2] * s->iwidth[2]); + + /* New cell of this part */ + const struct cell *c = &s->cells_top[new_cid]; + const int new_node = c->nodeID; + + if (dest[k] != new_node) + error("part's new node index not matching sorted index."); + + if (p->x[0] < c->loc[0] || p->x[0] > c->loc[0] + c->width[0] || + p->x[1] < c->loc[1] || p->x[1] > c->loc[1] + c->width[1] || + p->x[2] < c->loc[2] || p->x[2] > c->loc[2] + c->width[2]) + error("part not sorted into the right top-level cell!"); + } +#endif + + /* We will need to re-link the gpart partners of parts, so save their + * relative positions in the sent lists. */ + if (nr_parts > 0 && nr_gparts > 0) { + + struct savelink_mapper_data savelink_data; + savelink_data.nr_nodes = nr_nodes; + savelink_data.counts = counts; + savelink_data.parts = (void *)parts; + savelink_data.nodeID = nodeID; + threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_part, + nodes, nr_nodes, sizeof(int), 0, &savelink_data); + } + swift_free("dest", dest); + + /* Get destination of each s-particle */ + int *s_counts; + if ((s_counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) + error("Failed to allocate s_counts temporary buffer."); + + int *s_dest; + if ((s_dest = (int *)swift_malloc("s_dest", sizeof(int) * nr_sparts)) == NULL) + error("Failed to allocate s_dest temporary buffer."); + + redist_data.counts = s_counts; + redist_data.dest = s_dest; + redist_data.base = (void *)sparts; + + threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_spart, sparts, + nr_sparts, sizeof(struct spart), 0, &redist_data); + + /* Sort the particles according to their cell index. */ + if (nr_sparts > 0) + space_sparts_sort(s->sparts, s_dest, &s_counts[nodeID * nr_nodes], nr_nodes, + 0); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that the spart have been sorted correctly. */ + for (size_t k = 0; k < nr_sparts; k++) { + const struct spart *sp = &s->sparts[k]; + + if (sp->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (sp->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + + /* New cell index */ + const int new_cid = + cell_getid(s->cdim, sp->x[0] * s->iwidth[0], sp->x[1] * s->iwidth[1], + sp->x[2] * s->iwidth[2]); + + /* New cell of this spart */ + const struct cell *c = &s->cells_top[new_cid]; + const int new_node = c->nodeID; + + if (s_dest[k] != new_node) + error("spart's new node index not matching sorted index."); + + if (sp->x[0] < c->loc[0] || sp->x[0] > c->loc[0] + c->width[0] || + sp->x[1] < c->loc[1] || sp->x[1] > c->loc[1] + c->width[1] || + sp->x[2] < c->loc[2] || sp->x[2] > c->loc[2] + c->width[2]) + error("spart not sorted into the right top-level cell!"); + } +#endif + + /* We need to re-link the gpart partners of sparts. */ + if (nr_sparts > 0) { + + struct savelink_mapper_data savelink_data; + savelink_data.nr_nodes = nr_nodes; + savelink_data.counts = s_counts; + savelink_data.parts = (void *)sparts; + savelink_data.nodeID = nodeID; + threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_spart, + nodes, nr_nodes, sizeof(int), 0, &savelink_data); + } + swift_free("s_dest", s_dest); + + /* Get destination of each b-particle */ + int *b_counts; + if ((b_counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) + error("Failed to allocate b_counts temporary buffer."); + + int *b_dest; + if ((b_dest = (int *)swift_malloc("b_dest", sizeof(int) * nr_bparts)) == NULL) + error("Failed to allocate b_dest temporary buffer."); + + redist_data.counts = b_counts; + redist_data.dest = b_dest; + redist_data.base = (void *)bparts; + + threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_bpart, bparts, + nr_bparts, sizeof(struct bpart), 0, &redist_data); + + /* Sort the particles according to their cell index. */ + if (nr_bparts > 0) + space_bparts_sort(s->bparts, b_dest, &b_counts[nodeID * nr_nodes], nr_nodes, + 0); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that the bpart have been sorted correctly. */ + for (size_t k = 0; k < nr_bparts; k++) { + const struct bpart *bp = &s->bparts[k]; + + if (bp->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (bp->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + + /* New cell index */ + const int new_cid = + cell_getid(s->cdim, bp->x[0] * s->iwidth[0], bp->x[1] * s->iwidth[1], + bp->x[2] * s->iwidth[2]); + + /* New cell of this bpart */ + const struct cell *c = &s->cells_top[new_cid]; + const int new_node = c->nodeID; + + if (b_dest[k] != new_node) + error("bpart's new node index not matching sorted index."); + + if (bp->x[0] < c->loc[0] || bp->x[0] > c->loc[0] + c->width[0] || + bp->x[1] < c->loc[1] || bp->x[1] > c->loc[1] + c->width[1] || + bp->x[2] < c->loc[2] || bp->x[2] > c->loc[2] + c->width[2]) + error("bpart not sorted into the right top-level cell!"); + } +#endif + + /* We need to re-link the gpart partners of bparts. */ + if (nr_bparts > 0) { + + struct savelink_mapper_data savelink_data; + savelink_data.nr_nodes = nr_nodes; + savelink_data.counts = b_counts; + savelink_data.parts = (void *)bparts; + savelink_data.nodeID = nodeID; + threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_bpart, + nodes, nr_nodes, sizeof(int), 0, &savelink_data); + } + swift_free("b_dest", b_dest); + + /* Get destination of each g-particle */ + int *g_counts; + if ((g_counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) + error("Failed to allocate g_gcount temporary buffer."); + + int *g_dest; + if ((g_dest = (int *)swift_malloc("g_dest", sizeof(int) * nr_gparts)) == NULL) + error("Failed to allocate g_dest temporary buffer."); + + redist_data.counts = g_counts; + redist_data.dest = g_dest; + redist_data.base = (void *)gparts; + + threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_gpart, gparts, + nr_gparts, sizeof(struct gpart), 0, &redist_data); + + /* Sort the gparticles according to their cell index. */ + if (nr_gparts > 0) + space_gparts_sort(s->gparts, s->parts, s->sparts, s->bparts, g_dest, + &g_counts[nodeID * nr_nodes], nr_nodes); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that the gpart have been sorted correctly. */ + for (size_t k = 0; k < nr_gparts; k++) { + const struct gpart *gp = &s->gparts[k]; + + if (gp->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (gp->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + + /* New cell index */ + const int new_cid = + cell_getid(s->cdim, gp->x[0] * s->iwidth[0], gp->x[1] * s->iwidth[1], + gp->x[2] * s->iwidth[2]); + + /* New cell of this gpart */ + const struct cell *c = &s->cells_top[new_cid]; + const int new_node = c->nodeID; + + if (g_dest[k] != new_node) + error("gpart's new node index not matching sorted index (%d != %d).", + g_dest[k], new_node); + + if (gp->x[0] < c->loc[0] || gp->x[0] > c->loc[0] + c->width[0] || + gp->x[1] < c->loc[1] || gp->x[1] > c->loc[1] + c->width[1] || + gp->x[2] < c->loc[2] || gp->x[2] > c->loc[2] + c->width[2]) + error("gpart not sorted into the right top-level cell!"); + } +#endif + + swift_free("g_dest", g_dest); + + /* Get all the counts from all the nodes. */ + if (MPI_Allreduce(MPI_IN_PLACE, counts, nr_nodes * nr_nodes, MPI_INT, MPI_SUM, + MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to allreduce particle transfer counts."); + + /* Get all the g_counts from all the nodes. */ + if (MPI_Allreduce(MPI_IN_PLACE, g_counts, nr_nodes * nr_nodes, MPI_INT, + MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to allreduce gparticle transfer counts."); + + /* Get all the s_counts from all the nodes. */ + if (MPI_Allreduce(MPI_IN_PLACE, s_counts, nr_nodes * nr_nodes, MPI_INT, + MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to allreduce sparticle transfer counts."); + + /* Get all the b_counts from all the nodes. */ + if (MPI_Allreduce(MPI_IN_PLACE, b_counts, nr_nodes * nr_nodes, MPI_INT, + MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to allreduce bparticle transfer counts."); + + /* Report how many particles will be moved. */ + if (e->verbose) { + if (e->nodeID == 0) { + size_t total = 0, g_total = 0, s_total = 0, b_total = 0; + size_t unmoved = 0, g_unmoved = 0, s_unmoved = 0, b_unmoved = 0; + for (int p = 0, r = 0; p < nr_nodes; p++) { + for (int n = 0; n < nr_nodes; n++) { + total += counts[r]; + g_total += g_counts[r]; + s_total += s_counts[r]; + b_total += b_counts[r]; + if (p == n) { + unmoved += counts[r]; + g_unmoved += g_counts[r]; + s_unmoved += s_counts[r]; + b_unmoved += b_counts[r]; + } + r++; + } + } + if (total > 0) + message("%zu of %zu (%.2f%%) of particles moved", total - unmoved, + total, 100.0 * (double)(total - unmoved) / (double)total); + if (g_total > 0) + message("%zu of %zu (%.2f%%) of g-particles moved", g_total - g_unmoved, + g_total, + 100.0 * (double)(g_total - g_unmoved) / (double)g_total); + if (s_total > 0) + message("%zu of %zu (%.2f%%) of s-particles moved", s_total - s_unmoved, + s_total, + 100.0 * (double)(s_total - s_unmoved) / (double)s_total); + if (b_total > 0) + message("%ld of %ld (%.2f%%) of b-particles moved", b_total - b_unmoved, + b_total, + 100.0 * (double)(b_total - b_unmoved) / (double)b_total); + } + } + + /* Now each node knows how many parts, sparts, bparts, and gparts will be + * transferred to every other node. Get the new numbers of particles for this + * node. */ + size_t nr_parts_new = 0, nr_gparts_new = 0, nr_sparts_new = 0, + nr_bparts_new = 0; + for (int k = 0; k < nr_nodes; k++) + nr_parts_new += counts[k * nr_nodes + nodeID]; + for (int k = 0; k < nr_nodes; k++) + nr_gparts_new += g_counts[k * nr_nodes + nodeID]; + for (int k = 0; k < nr_nodes; k++) + nr_sparts_new += s_counts[k * nr_nodes + nodeID]; + for (int k = 0; k < nr_nodes; k++) + nr_bparts_new += b_counts[k * nr_nodes + nodeID]; + + /* Now exchange the particles, type by type to keep the memory required + * under control. */ + + /* SPH particles. */ + void *new_parts = engine_do_redistribute( + "parts", counts, (char *)s->parts, nr_parts_new, sizeof(struct part), + part_align, part_mpi_type, nr_nodes, nodeID); + swift_free("parts", s->parts); + s->parts = (struct part *)new_parts; + s->nr_parts = nr_parts_new; + s->size_parts = engine_redistribute_alloc_margin * nr_parts_new; + + /* Extra SPH particle properties. */ + new_parts = engine_do_redistribute( + "xparts", counts, (char *)s->xparts, nr_parts_new, sizeof(struct xpart), + xpart_align, xpart_mpi_type, nr_nodes, nodeID); + swift_free("xparts", s->xparts); + s->xparts = (struct xpart *)new_parts; + + /* Gravity particles. */ + new_parts = engine_do_redistribute( + "gparts", g_counts, (char *)s->gparts, nr_gparts_new, + sizeof(struct gpart), gpart_align, gpart_mpi_type, nr_nodes, nodeID); + swift_free("gparts", s->gparts); + s->gparts = (struct gpart *)new_parts; + s->nr_gparts = nr_gparts_new; + s->size_gparts = engine_redistribute_alloc_margin * nr_gparts_new; + + /* Star particles. */ + new_parts = engine_do_redistribute( + "sparts", s_counts, (char *)s->sparts, nr_sparts_new, + sizeof(struct spart), spart_align, spart_mpi_type, nr_nodes, nodeID); + swift_free("sparts", s->sparts); + s->sparts = (struct spart *)new_parts; + s->nr_sparts = nr_sparts_new; + s->size_sparts = engine_redistribute_alloc_margin * nr_sparts_new; + + /* Black holes particles. */ + new_parts = engine_do_redistribute( + "bparts", b_counts, (char *)s->bparts, nr_bparts_new, + sizeof(struct bpart), bpart_align, bpart_mpi_type, nr_nodes, nodeID); + swift_free("bparts", s->bparts); + s->bparts = (struct bpart *)new_parts; + s->nr_bparts = nr_bparts_new; + s->size_bparts = engine_redistribute_alloc_margin * nr_bparts_new; + + /* All particles have now arrived. Time for some final operations on the + stuff we just received */ + + /* Restore the part<->gpart and spart<->gpart links. + * Generate indices and counts for threadpool tasks. Note we process a node + * at a time. */ + struct relink_mapper_data relink_data; + relink_data.s = s; + relink_data.counts = counts; + relink_data.g_counts = g_counts; + relink_data.s_counts = s_counts; + relink_data.b_counts = b_counts; + relink_data.nodeID = nodeID; + relink_data.nr_nodes = nr_nodes; + + threadpool_map(&e->threadpool, engine_redistribute_relink_mapper, nodes, + nr_nodes, sizeof(int), 1, &relink_data); + free(nodes); + + /* Clean up the counts now we are done. */ + free(counts); + free(g_counts); + free(s_counts); + free(b_counts); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that all parts are in the right place. */ + for (size_t k = 0; k < nr_parts_new; k++) { + const int cid = cell_getid(s->cdim, s->parts[k].x[0] * s->iwidth[0], + s->parts[k].x[1] * s->iwidth[1], + s->parts[k].x[2] * s->iwidth[2]); + if (cells[cid].nodeID != nodeID) + error("Received particle (%zu) that does not belong here (nodeID=%i).", k, + cells[cid].nodeID); + } + for (size_t k = 0; k < nr_gparts_new; k++) { + const int cid = cell_getid(s->cdim, s->gparts[k].x[0] * s->iwidth[0], + s->gparts[k].x[1] * s->iwidth[1], + s->gparts[k].x[2] * s->iwidth[2]); + if (cells[cid].nodeID != nodeID) + error("Received g-particle (%zu) that does not belong here (nodeID=%i).", + k, cells[cid].nodeID); + } + for (size_t k = 0; k < nr_sparts_new; k++) { + const int cid = cell_getid(s->cdim, s->sparts[k].x[0] * s->iwidth[0], + s->sparts[k].x[1] * s->iwidth[1], + s->sparts[k].x[2] * s->iwidth[2]); + if (cells[cid].nodeID != nodeID) + error("Received s-particle (%zu) that does not belong here (nodeID=%i).", + k, cells[cid].nodeID); + } + for (size_t k = 0; k < nr_bparts_new; k++) { + const int cid = cell_getid(s->cdim, s->bparts[k].x[0] * s->iwidth[0], + s->bparts[k].x[1] * s->iwidth[1], + s->bparts[k].x[2] * s->iwidth[2]); + if (cells[cid].nodeID != nodeID) + error("Received b-particle (%zu) that does not belong here (nodeID=%i).", + k, cells[cid].nodeID); + } + + /* Verify that the links are correct */ + part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, nr_parts_new, + nr_gparts_new, nr_sparts_new, nr_bparts_new, e->verbose); + +#endif + + /* Be verbose about what just happened. */ + if (e->verbose) { + int my_cells = 0; + for (int k = 0; k < nr_cells; k++) + if (cells[k].nodeID == nodeID) my_cells += 1; + message( + "node %i now has %zu parts, %zu sparts, %zu bparts and %zu gparts in " + "%i cells.", + nodeID, nr_parts_new, nr_sparts_new, nr_bparts_new, nr_gparts_new, + my_cells); + } + + /* Flag that we do not have any extra particles any more */ + s->nr_extra_parts = 0; + s->nr_extra_gparts = 0; + s->nr_extra_sparts = 0; + s->nr_extra_bparts = 0; + + /* Flag that a redistribute has taken place */ + e->step_props |= engine_step_prop_redistribute; + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +#else + error("SWIFT was not compiled with MPI support."); +#endif +} diff --git a/src/engine_unskip.c b/src/engine_unskip.c new file mode 100644 index 0000000000000000000000000000000000000000..dfadfa5ca1a6aebd0d7a277164eca9707ac97a62 --- /dev/null +++ b/src/engine_unskip.c @@ -0,0 +1,400 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "engine.h" + +/* Local headers. */ +#include "active.h" +#include "cell.h" +#include "memswap.h" + +/* Load the profiler header, if needed. */ +#ifdef WITH_PROFILER +#include <gperftools/profiler.h> +#endif + +/** + * @brief Broad categories of tasks. + * + * Each category is unskipped independently + * of the others. + */ +enum task_broad_types { + task_broad_types_hydro = 1, + task_broad_types_gravity, + task_broad_types_stars, + task_broad_types_black_holes, + task_broad_types_count, +}; + +/** + * @brief Meta-data for the unskipping + */ +struct unskip_data { + + /*! The #engine */ + struct engine *e; + + /*! Pointer to the start of the list of cells to unskip */ + int *list_base; + + /*! Number of times the list has been duplicated */ + int multiplier; + + /*! The number of active cells (without dulication) */ + int num_active_cells; + + /*! The #task_broad_types corresponding to each copy of the list */ + enum task_broad_types task_types[task_broad_types_count]; +}; + +/** + * @brief Unskip any hydro tasks associated with active cells. + * + * @param c The cell. + * @param e The engine. + */ +static void engine_do_unskip_hydro(struct cell *c, struct engine *e) { + + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + + /* Ignore empty cells. */ + if (c->hydro.count == 0) return; + + /* Skip inactive cells. */ + if (!cell_is_active_hydro(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]; + engine_do_unskip_hydro(cp, e); + } + } + } + + /* Unskip any active tasks. */ + const int forcerebuild = cell_unskip_hydro_tasks(c, &e->sched); + if (forcerebuild) atomic_inc(&e->forcerebuild); +} + +/** + * @brief Unskip any stars tasks associated with active cells. + * + * @param c The cell. + * @param e The engine. + * @param with_star_formation Are we running with star formation switched on? + */ +static void engine_do_unskip_stars(struct cell *c, struct engine *e, + const int with_star_formation) { + + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + + const int non_empty = + c->stars.count > 0 || (with_star_formation && c->hydro.count > 0); + + /* Ignore empty cells. */ + if (!non_empty) return; + + const int ci_active = cell_is_active_stars(c, e) || + (with_star_formation && cell_is_active_hydro(c, e)); + + /* Skip inactive cells. */ + if (!ci_active) return; + + /* Recurse */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *cp = c->progeny[k]; + engine_do_unskip_stars(cp, e, with_star_formation); + } + } + } + + /* Unskip any active tasks. */ + const int forcerebuild = + cell_unskip_stars_tasks(c, &e->sched, with_star_formation); + 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 engine_do_unskip_black_holes(struct cell *c, struct engine *e) { + + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + + /* 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]; + engine_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. + * + * @param c The cell. + * @param e The engine. + */ +static void engine_do_unskip_gravity(struct cell *c, struct engine *e) { + + /* Early abort (are we below the level where tasks are)? */ + if (!cell_get_flag(c, cell_flag_has_tasks)) return; + + /* Ignore empty cells. */ + if (c->grav.count == 0) return; + + /* Skip inactive cells. */ + if (!cell_is_active_gravity(c, e)) return; + + /* Recurse */ + if (c->split && ((c->maxdepth - c->depth) >= space_subdepth_diff_grav)) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *cp = c->progeny[k]; + engine_do_unskip_gravity(cp, e); + } + } + } + + /* Unskip any active tasks. */ + cell_unskip_gravity_tasks(c, &e->sched); +} + +/** + * @brief Mapper function to unskip active tasks. + * + * @param map_data An array of #cell%s. + * @param num_elements Chunk size. + * @param extra_data Pointer to an unskip_data structure. + */ +void engine_do_unskip_mapper(void *map_data, int num_elements, + void *extra_data) { + + /* Unpack the meta data */ + struct unskip_data *data = (struct unskip_data *)extra_data; + const int num_active_cells = data->num_active_cells; + const enum task_broad_types *const task_types = data->task_types; + const int *const list_base = data->list_base; + struct engine *e = data->e; + struct cell *const cells_top = e->s->cells_top; + + /* What policies are we running? */ + const int with_star_formation = e->policy & engine_policy_star_formation; + + /* The current chunk of active cells */ + const int *const local_cells = (int *)map_data; + + /* Loop over this thread's chunk of cells to unskip */ + for (int ind = 0; ind < num_elements; ind++) { + + /* Handle on the cell */ + struct cell *const c = &cells_top[local_cells[ind]]; + + /* In what copy of the global list are we? + * This gives us the broad type of task we are working on. */ + const ptrdiff_t delta = &local_cells[ind] - list_base; + const int type = delta / num_active_cells; + +#ifdef SWIFT_DEBUG_CHECKS + if (type >= data->multiplier) error("Invalid broad task type!"); + if (c == NULL) error("Got an invalid cell index!"); +#endif + + /* What broad type of tasks are we unskipping? */ + switch (task_types[type]) { + case task_broad_types_hydro: +#ifdef SWIFT_DEBUG_CHECKS + if (!(e->policy & engine_policy_hydro)) + error("Trying to unskip hydro tasks in a non-hydro run!"); +#endif + engine_do_unskip_hydro(c, e); + break; + case task_broad_types_gravity: +#ifdef SWIFT_DEBUG_CHECKS + if (!(e->policy & engine_policy_self_gravity) && + !(e->policy & engine_policy_external_gravity)) + error("Trying to unskip gravity tasks in a non-gravity run!"); +#endif + engine_do_unskip_gravity(c, e); + break; + case task_broad_types_stars: +#ifdef SWIFT_DEBUG_CHECKS + if (!(e->policy & engine_policy_stars)) + error("Trying to unskip star tasks in a non-stars run!"); +#endif + engine_do_unskip_stars(c, e, with_star_formation); + break; + case task_broad_types_black_holes: +#ifdef SWIFT_DEBUG_CHECKS + if (!(e->policy & engine_policy_black_holes)) + error("Trying to unskip black holes tasks in a non-BH run!"); +#endif + engine_do_unskip_black_holes(c, e); + break; + default: +#ifdef SWIFT_DEBUG_CHECKS + error("Invalid broad task type!"); +#endif + continue; + } + } +} + +/** + * @brief Unskip all the tasks that act on active cells at this time. + * + * @param e The #engine. + */ +void engine_unskip(struct engine *e) { + + const ticks tic = getticks(); + struct space *s = e->s; + const int nodeID = e->nodeID; + + const int with_hydro = e->policy & engine_policy_hydro; + const int with_self_grav = e->policy & engine_policy_self_gravity; + 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; + char filename[100]; + sprintf(filename, "/tmp/swift_engine_do_usnkip_mapper_%06i.prof", count++); + ProfilerStart(filename); +#endif // WITH_PROFILER + + /* Move the active local cells to the top of the list. */ + int *local_cells = e->s->local_cells_with_tasks_top; + int num_active_cells = 0; + for (int k = 0; k < s->nr_local_cells_with_tasks; k++) { + struct cell *c = &s->cells_top[local_cells[k]]; + + if ((with_hydro && cell_is_active_hydro(c, e)) || + (with_self_grav && cell_is_active_gravity(c, 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_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)); + num_active_cells += 1; + } + } + + /* What kind of tasks do we have? */ + struct unskip_data data; + bzero(&data, sizeof(struct unskip_data)); + int multiplier = 0; + if (with_hydro) { + data.task_types[multiplier] = task_broad_types_hydro; + multiplier++; + } + if (with_self_grav || with_ext_grav) { + data.task_types[multiplier] = task_broad_types_gravity; + multiplier++; + } + if (with_feedback || with_stars) { + data.task_types[multiplier] = task_broad_types_stars; + multiplier++; + } + if (with_black_holes) { + data.task_types[multiplier] = task_broad_types_black_holes; + multiplier++; + } + + /* Should we duplicate the list of active cells to better parallelise the + unskip over the threads ? */ + int *local_active_cells; + if (multiplier > 1) { + + /* Make space for copies of the list */ + local_active_cells = + (int *)malloc(multiplier * num_active_cells * sizeof(int)); + if (local_active_cells == NULL) + error( + "Couldn't allocate memory for duplicated list of local active " + "cells."); + + /* Make blind copies of the list */ + for (int m = 0; m < multiplier; m++) { + memcpy(local_active_cells + m * num_active_cells, local_cells, + num_active_cells * sizeof(int)); + } + } else { + local_active_cells = local_cells; + } + + /* We now have a list of local active cells duplicated as many times as + * we have broad task types. We can now release all the threads on the list */ + + data.e = e; + data.list_base = local_active_cells; + data.num_active_cells = num_active_cells; + data.multiplier = multiplier; + + /* Activate all the regular tasks */ + threadpool_map(&e->threadpool, engine_do_unskip_mapper, local_active_cells, + num_active_cells * multiplier, sizeof(int), 1, &data); + +#ifdef WITH_PROFILER + ProfilerStop(); +#endif // WITH_PROFILER + + /* Free stuff? */ + if (multiplier > 1) { + free(local_active_cells); + } + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} diff --git a/src/equation_of_state/planetary/tillotson.h b/src/equation_of_state/planetary/tillotson.h index 7522ba7b17e0e1bf734b853a89aada0375072ddc..9e2fa9dd9eb50ca8e8d3ba2e62ddb765f29cb2b1 100644 --- a/src/equation_of_state/planetary/tillotson.h +++ b/src/equation_of_state/planetary/tillotson.h @@ -55,9 +55,9 @@ INLINE static void set_Til_iron(struct Til_params *mat, mat->b = 1.5f; mat->A = 1.28e11f; mat->B = 1.05e11f; - mat->u_0 = 9.5e9f; - mat->u_iv = 2.4e9f; - mat->u_cv = 8.67e9f; + mat->u_0 = 9.5e6f; + mat->u_iv = 2.4e6f; + mat->u_cv = 8.67e6f; mat->alpha = 5.0f; mat->beta = 5.0f; mat->eta_min = 0.0f; @@ -72,9 +72,9 @@ INLINE static void set_Til_granite(struct Til_params *mat, mat->b = 1.3f; mat->A = 1.8e10f; mat->B = 1.8e10f; - mat->u_0 = 1.6e10f; - mat->u_iv = 3.5e9f; - mat->u_cv = 1.8e10f; + mat->u_0 = 1.6e7f; + mat->u_iv = 3.5e6f; + mat->u_cv = 1.8e7f; mat->alpha = 5.0f; mat->beta = 5.0f; mat->eta_min = 0.0f; @@ -89,9 +89,9 @@ INLINE static void set_Til_water(struct Til_params *mat, mat->b = 0.15f; mat->A = 2.18e9f; mat->B = 1.325e10f; - mat->u_0 = 7.0e9f; - mat->u_iv = 4.19e8f; - mat->u_cv = 2.69e9f; + mat->u_0 = 7.0e6f; + mat->u_iv = 4.19e5f; + mat->u_cv = 2.69e6f; mat->alpha = 10.0f; mat->beta = 5.0f; mat->eta_min = 0.925f; diff --git a/src/feedback/EAGLE/feedback.c b/src/feedback/EAGLE/feedback.c index cf6848760fb9827718befe48e43ab53cfe77f8ef..c280d6cd829d819bede91bf17df0b8861ad9fbbc 100644 --- a/src/feedback/EAGLE/feedback.c +++ b/src/feedback/EAGLE/feedback.c @@ -63,12 +63,12 @@ double eagle_feedback_number_of_SNII(const struct spart* sp, * * We follow Foerster et al. 2006, MNRAS, 368 * - * @param sp The #spart. + * @param M_init The inital mass of the star particle in internal units. * @param t0 The initial time (in Gyr). * @param t1 The final time (in Gyr). * @param props The properties of the stellar model. */ -double eagle_feedback_number_of_SNIa(const struct spart* sp, const double t0, +double eagle_feedback_number_of_SNIa(const double M_init, const double t0, const double t1, const struct feedback_props* props) { @@ -78,7 +78,7 @@ double eagle_feedback_number_of_SNIa(const struct spart* sp, const double t0, const double nu = props->SNIa_efficiency; const double num_SNIa_per_Msun = nu * (exp(-t0 * tau) - exp(-t1 * tau)); - return num_SNIa_per_Msun * sp->mass_init * props->mass_to_solar_mass; + return num_SNIa_per_Msun * M_init * props->mass_to_solar_mass; } /** @@ -105,15 +105,15 @@ double eagle_feedback_energy_fraction(const struct spart* sp, /* Star properties */ - /* Smoothed metallicity (metal mass fraction) at birth time of the star */ - const double Z_smooth = sp->chemistry_data.smoothed_metal_mass_fraction_total; + /* Metallicity (metal mass fraction) at birth time of the star */ + const double Z = chemistry_get_total_metal_mass_fraction_for_feedback(sp); /* Physical density of the gas at the star's birth time */ const double rho_birth = sp->birth_density; const double n_birth = rho_birth * props->rho_to_n_cgs; /* Calculate f_E */ - const double Z_term = pow(max(Z_smooth, 1e-6) / Z_0, n_Z); + const double Z_term = pow(max(Z, 1e-6) / Z_0, n_Z); const double n_term = pow(n_birth / n_0, -n_n); const double denonimator = 1. + Z_term * n_term; @@ -199,107 +199,58 @@ INLINE static void compute_SNII_feedback( /** * @brief Find the bins and offset along the metallicity dimension of the - * AGB yields table. + * yields table. + * + * Note for Matthieu: In the normal EAGLE case, the arrays are of length 3 and + * 5 for the AGB and SNII channels respectivly. Can we simplify this? * - * @param iz_low (return) Lower index along the metallicity dimension. - * @param iz_high (return) High index along the metallicity dimension. - * @param dz (return) Offset between the metallicity bin and Z. + * @param index_Z_low (return) Lower index along the metallicity dimension. + * @param index_Z_high (return) High index along the metallicity dimension. + * @param dZ (return) Offset between the metallicity bin and Z. * @param log10_Z log10 of the star metallicity (metal mass fraction). - * @param props The properties of the feedback model. + * @param log_Z_bins bin of log10 of the metallicities in the table for this + * enrichment channel. + * @param N_bins The number of metallicity bins for this enrichment channel. */ -INLINE static void determine_bin_yield_AGB(int* iz_low, int* iz_high, float* dz, - const float log10_Z, - const struct feedback_props* props) { - - const double* AGB_Z = props->yield_AGB.metallicity; - const int N_bins = eagle_feedback_AGB_N_metals; +INLINE static void determine_bin_yields(int* index_Z_low, int* index_Z_high, + float* dZ, const float log10_Z, + const double* const log_Z_bins, + const int N_bins) { if (log10_Z > log10_min_metallicity) { /* Find metallicity bin which contains the star's metallicity */ - int j; - for (j = 0; j < (N_bins - 1) && log10_Z > AGB_Z[j + 1]; j++) - ; + int j = 0; + while (j < (N_bins - 1) && log10_Z > log_Z_bins[j + 1]) { + j++; + } /* Store the indices */ - *iz_low = j; - *iz_high = *iz_low + 1; - - *iz_high = min(*iz_high, N_bins - 1); + *index_Z_low = j; + *index_Z_high = j + 1; - /* Compute offset */ - if ((log10_Z >= AGB_Z[0]) && (log10_Z <= AGB_Z[N_bins - 1])) { + *index_Z_high = min(*index_Z_high, N_bins - 1); - *dz = log10_Z - AGB_Z[*iz_low]; + /* Compute offset from index_Z_low in the table*/ + if ((log10_Z >= log_Z_bins[0]) && (log10_Z <= log_Z_bins[N_bins - 1])) { + *dZ = log10_Z - log_Z_bins[*index_Z_low]; } else { - *dz = 0.f; + *dZ = 0.f; } /* Normalize offset */ - const float delta_Z = AGB_Z[*iz_high] - AGB_Z[*iz_low]; - - if (delta_Z > 0.f) - *dz /= delta_Z; - else - *dz = 0.f; - - } else { - *iz_low = 0; - *iz_high = 0; - *dz = 0.f; - } -} - -/** - * @brief Find the bins and offset along the metallicity dimension of the - * SNII yields table. - * - * @param iz_low (return) Lower index along the metallicity dimension. - * @param iz_high (return) High index along the metallicity dimension. - * @param dz (return) Offset between the metallicity bin and Z. - * @param log10_Z log10 of the star metallicity (metal mass fraction). - * @param props The properties of the feedback model. - */ -INLINE static void determine_bin_yield_SNII( - int* iz_low, int* iz_high, float* dz, const float log10_Z, - const struct feedback_props* props) { - - const double* SNII_Z = props->yield_SNII.metallicity; - const int N_bins = eagle_feedback_SNII_N_metals; - - if (log10_Z > log10_min_metallicity) { - - /* Find metallicity bin which contains the star's metallicity */ - int j; - for (j = 0; j < (N_bins - 1) && log10_Z > SNII_Z[j + 1]; j++) - ; - - /* Store the indices */ - *iz_low = j; - *iz_high = *iz_low + 1; - - *iz_high = min(*iz_high, N_bins - 1); + const float delta_Z = log_Z_bins[*index_Z_high] - log_Z_bins[*index_Z_low]; - /* Compute offset */ - if ((log10_Z >= SNII_Z[0]) && (log10_Z <= SNII_Z[N_bins - 1])) { - - *dz = log10_Z - SNII_Z[*iz_low]; + if (delta_Z > 0.f) { + *dZ /= delta_Z; } else { - *dz = 0.f; + *dZ = 0.f; } - /* Normalize offset */ - const float delta_Z = SNII_Z[*iz_high] - SNII_Z[*iz_low]; - - if (delta_Z > 0.f) - *dz = *dz / delta_Z; - else - *dz = 0.f; - } else { - *iz_low = 0; - *iz_high = 0; - *dz = 0.f; + *index_Z_low = 0; + *index_Z_high = 0; + *dZ = 0.f; } } @@ -310,16 +261,19 @@ INLINE static void determine_bin_yield_SNII( * * @param log10_min_mass log10 mass at the end of step * @param log10_max_mass log10 mass at the beginning of step - * @param props properties of the feedback model - * @param sp #spart we are computing feedback from + * @param M_init The initial mass of the star particle (in internal units). + * @param Z The total metallicity of the star (metal mass fraction). + * @param props Properties of the feedback model. * @param star_age_Gyr age of star in Gyr * @param dt_Gyr timestep dt in Gyr + * @param feedback_data (return) The #feedback_spart_data to fill with things to + * distribute to the gas. */ -INLINE static void evolve_SNIa(const float log10_min_mass, - const float log10_max_mass, - const struct feedback_props* props, - struct spart* sp, double star_age_Gyr, - double dt_Gyr) { +INLINE static void evolve_SNIa( + const double log10_min_mass, const double log10_max_mass, + const double M_init, const double Z, const struct feedback_props* props, + double star_age_Gyr, double dt_Gyr, + struct feedback_spart_data* const feedback_data) { /* Check if we're outside the mass range for SNIa */ if (log10_min_mass >= props->log10_SNIa_max_mass_msun) return; @@ -333,9 +287,8 @@ INLINE static void evolve_SNIa(const float log10_min_mass, * and use updated values for the star's age and timestep in this function */ if (log10_max_mass > props->log10_SNIa_max_mass_msun) { - const float Z = sp->chemistry_data.metal_mass_fraction_total; - const float max_mass = exp10f(props->log10_SNIa_max_mass_msun); - const float lifetime_Gyr = lifetime_in_Gyr(max_mass, Z, props); + const double max_mass = props->SNIa_max_mass_msun; + const double lifetime_Gyr = lifetime_in_Gyr(max_mass, Z, props); dt_Gyr = max(star_age_Gyr + dt_Gyr - lifetime_Gyr, 0.); star_age_Gyr = lifetime_Gyr; @@ -343,40 +296,40 @@ INLINE static void evolve_SNIa(const float log10_min_mass, /* Compute the number of SNIa */ const float num_SNIa = eagle_feedback_number_of_SNIa( - sp, star_age_Gyr, star_age_Gyr + dt_Gyr, props); + M_init, star_age_Gyr, star_age_Gyr + dt_Gyr, props); - /* compute mass of each metal */ + /* Compute mass of each metal */ for (int i = 0; i < chemistry_element_count; i++) { - sp->feedback_data.to_distribute.metal_mass[i] += + feedback_data->to_distribute.metal_mass[i] += num_SNIa * props->yield_SNIa_IMF_resampled[i] * props->solar_mass_to_mass; } /* Update the metallicity of the material released */ - sp->feedback_data.to_distribute.metal_mass_from_SNIa += + feedback_data->to_distribute.metal_mass_from_SNIa += num_SNIa * props->yield_SNIa_total_metals_IMF_resampled * props->solar_mass_to_mass; /* Update the metal mass produced */ - sp->feedback_data.to_distribute.total_metal_mass += + feedback_data->to_distribute.total_metal_mass += num_SNIa * props->yield_SNIa_total_metals_IMF_resampled * props->solar_mass_to_mass; /* Compute the mass produced by SNIa * Note: SNIa do not inject H or He so the mass injected is the same * as the metal mass injected. */ - sp->feedback_data.to_distribute.mass_from_SNIa += + feedback_data->to_distribute.mass_from_SNIa += num_SNIa * props->yield_SNIa_total_metals_IMF_resampled * props->solar_mass_to_mass; /* Compute the iron mass produced */ - sp->feedback_data.to_distribute.Fe_mass_from_SNIa += + feedback_data->to_distribute.Fe_mass_from_SNIa += num_SNIa * props->yield_SNIa_IMF_resampled[chemistry_element_Fe] * props->solar_mass_to_mass; /* Compute the energy to be injected */ if (props->with_SNIa_feedback) { - sp->feedback_data.to_distribute.energy += num_SNIa * props->E_SNIa; + feedback_data->to_distribute.energy += num_SNIa * props->E_SNIa; } } @@ -385,21 +338,33 @@ INLINE static void evolve_SNIa(const float log10_min_mass, * IMF weighted by the yields read from tables for each of the quantities of * interest. * - * Note for Matthieu: This function is poorly written and needs improving. - * * @param log10_min_mass log10 mass at the end of step * @param log10_max_mass log10 mass at the beginning of step - * @param stellar_yields array to store calculated yields for passing to - * integrate_imf - * @param props properties of the feedback model. - * @param sp spart we are computing feedback from + * @param M_init The initial mass of the star particle (in internal units). + * @param Z The total metallicity of the star (metal mass fraction). + * @param abundances The individual metal abundances (mass fractions) of the + * star. + * @param props Properties of the feedback model. + * @param feedback_data (return) The #feedback_spart_data to fill with things to + * distribute to the gas. */ -INLINE static void evolve_SNII(float log10_min_mass, float log10_max_mass, - float* stellar_yields, - const struct feedback_props* props, - struct spart* sp) { +INLINE static void evolve_SNII( + double log10_min_mass, double log10_max_mass, const double M_init, + const double Z, const float* const abundances, + const struct feedback_props* props, + struct feedback_spart_data* const feedback_data) { - int low_imf_mass_bin_index, high_imf_mass_bin_index, mass_bin_index; + /* Pull out common arrays */ + + /* Metal mass produced by the star */ + const double* const total_yields = + props->yield_SNII.total_metals_IMF_resampled; + + /* Individual elements produced by the star */ + const double* const metal_yields = props->yield_SNII.yield_IMF_resampled; + + /* Elements already in the stars that are ejected */ + const double* const ejecta = props->yield_SNII.ejecta_IMF_resampled; /* If mass at beginning of step is less than tabulated lower bound for IMF, * limit it.*/ @@ -416,124 +381,128 @@ INLINE static void evolve_SNII(float log10_min_mass, float log10_max_mass, if (log10_min_mass >= log10_max_mass) return; /* determine which IMF mass bins contribute to the integral */ + int low_imf_mass_bin_index, high_imf_mass_bin_index; determine_imf_bins(log10_min_mass, log10_max_mass, &low_imf_mass_bin_index, &high_imf_mass_bin_index, props); /* determine which metallicity bin and offset this star belongs to */ - int iz_low = 0, iz_high = 0, low_index_3d, high_index_3d, low_index_2d, - high_index_2d; - float dz = 0.; - determine_bin_yield_SNII(&iz_low, &iz_high, &dz, - log10(sp->chemistry_data.metal_mass_fraction_total), - props); - - /* compute metals produced */ - float metal_mass_released[chemistry_element_count], metal_mass_released_total; + int index_Z_lo = 0, index_Z_hi = 0; + float dZ = 0.; + determine_bin_yields(&index_Z_lo, &index_Z_hi, &dZ, log10(Z), + props->yield_SNII.metallicity, + eagle_feedback_SNII_N_metals); + + /* Allocate temporary array for calculating imf weights */ + double stellar_yields[eagle_feedback_N_imf_bins]; + + /******************************* + * Compute metal mass produced * + *******************************/ + double metal_mass_released[chemistry_element_count]; + + /* Loop over all the elements */ for (int elem = 0; elem < chemistry_element_count; elem++) { - for (mass_bin_index = low_imf_mass_bin_index; + + /* Loop over all the relevent IMF mass bins */ + for (int mass_bin_index = low_imf_mass_bin_index; mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - low_index_3d = row_major_index_3d( - iz_low, elem, mass_bin_index, eagle_feedback_SNII_N_metals, + + const int lo_index_3d = row_major_index_3d( + index_Z_lo, elem, mass_bin_index, eagle_feedback_SNII_N_metals, chemistry_element_count, eagle_feedback_N_imf_bins); - high_index_3d = row_major_index_3d( - iz_high, elem, mass_bin_index, eagle_feedback_SNII_N_metals, + const int hi_index_3d = row_major_index_3d( + index_Z_hi, elem, mass_bin_index, eagle_feedback_SNII_N_metals, chemistry_element_count, eagle_feedback_N_imf_bins); - low_index_2d = row_major_index_2d(iz_low, mass_bin_index, - eagle_feedback_SNII_N_metals, - eagle_feedback_N_imf_bins); - high_index_2d = row_major_index_2d(iz_high, mass_bin_index, - eagle_feedback_SNII_N_metals, - eagle_feedback_N_imf_bins); + + const int lo_index_2d = row_major_index_2d(index_Z_lo, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + const int hi_index_2d = row_major_index_2d(index_Z_hi, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); stellar_yields[mass_bin_index] = - (1 - dz) * - (props->yield_SNII.yield_IMF_resampled[low_index_3d] + - sp->chemistry_data.metal_mass_fraction[elem] * - props->yield_SNII.ejecta_IMF_resampled[low_index_2d]) + - dz * (props->yield_SNII.yield_IMF_resampled[high_index_3d] + - sp->chemistry_data.metal_mass_fraction[elem] * - props->yield_SNII.ejecta_IMF_resampled[high_index_2d]); + (1.f - dZ) * (metal_yields[lo_index_3d] + + abundances[elem] * ejecta[lo_index_2d]) + + (0.f + dZ) * (metal_yields[hi_index_3d] + + abundances[elem] * ejecta[hi_index_2d]); } - metal_mass_released[elem] = integrate_imf( log10_min_mass, log10_max_mass, eagle_imf_integration_yield_weight, stellar_yields, props); } - /* Compute mass produced */ - for (mass_bin_index = low_imf_mass_bin_index; + /************************************* + * Compute total metal mass produced * + *************************************/ + for (int mass_bin_index = low_imf_mass_bin_index; mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - low_index_2d = - row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_SNII_N_metals, - eagle_feedback_N_imf_bins); - high_index_2d = row_major_index_2d(iz_high, mass_bin_index, - eagle_feedback_SNII_N_metals, - eagle_feedback_N_imf_bins); + + const int lo_index_2d = row_major_index_2d(index_Z_lo, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + const int hi_index_2d = row_major_index_2d(index_Z_hi, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = - (1 - dz) * (props->yield_SNII.total_metals_IMF_resampled[low_index_2d] + - sp->chemistry_data.metal_mass_fraction_total * - props->yield_SNII.ejecta_IMF_resampled[low_index_2d]) + - dz * (props->yield_SNII.total_metals_IMF_resampled[high_index_2d] + - sp->chemistry_data.metal_mass_fraction_total * - props->yield_SNII.ejecta_IMF_resampled[high_index_2d]); + (1.f - dZ) * (total_yields[lo_index_2d] + Z * ejecta[lo_index_2d]) + + (0.f + dZ) * (total_yields[hi_index_2d] + Z * ejecta[hi_index_2d]); } - - metal_mass_released_total = + double metal_mass_released_total = integrate_imf(log10_min_mass, log10_max_mass, eagle_imf_integration_yield_weight, stellar_yields, props); - /* yield normalization */ - float mass_ejected, mass_released; - - /* zero all negative values */ - for (int i = 0; i < chemistry_element_count; i++) - metal_mass_released[i] = max(metal_mass_released[i], 0.f); + /************************************************ + * Compute the total mass ejected from the star * + ************************************************/ + for (int mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - metal_mass_released_total = max(metal_mass_released_total, 0.f); + const int lo_index_2d = row_major_index_2d(index_Z_lo, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + const int hi_index_2d = row_major_index_2d(index_Z_hi, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); - /* compute the total metal mass ejected from the star*/ - for (mass_bin_index = low_imf_mass_bin_index; - mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - low_index_2d = - row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_SNII_N_metals, - eagle_feedback_N_imf_bins); - high_index_2d = row_major_index_2d(iz_high, mass_bin_index, - eagle_feedback_SNII_N_metals, - eagle_feedback_N_imf_bins); stellar_yields[mass_bin_index] = - (1 - dz) * props->yield_SNII.ejecta_IMF_resampled[low_index_2d] + - dz * props->yield_SNII.ejecta_IMF_resampled[high_index_2d]; + (1 - dZ) * ejecta[lo_index_2d] + dZ * ejecta[hi_index_2d]; } - - mass_ejected = + const double mass_ejected = integrate_imf(log10_min_mass, log10_max_mass, eagle_imf_integration_yield_weight, stellar_yields, props); + /* Zero all negative values */ + for (int i = 0; i < chemistry_element_count; i++) + metal_mass_released[i] = max(metal_mass_released[i], 0.f); + metal_mass_released_total = max(metal_mass_released_total, 0.f); + /* compute the total mass released */ - mass_released = metal_mass_released_total + - metal_mass_released[chemistry_element_H] + - metal_mass_released[chemistry_element_He]; - - /* normalize the yields */ - if (mass_released > 0) { - /* Set normalisation factor. Note additional multiplication by the star - * initial mass as tables are per initial mass */ - const float norm_factor = sp->mass_init * mass_ejected / mass_released; - - for (int i = 0; i < chemistry_element_count; i++) { - sp->feedback_data.to_distribute.metal_mass[i] += - metal_mass_released[i] * norm_factor; - } - for (int i = 0; i < chemistry_element_count; i++) { - sp->feedback_data.to_distribute.mass_from_SNII += - sp->feedback_data.to_distribute.metal_mass[i]; - } - sp->feedback_data.to_distribute.total_metal_mass += - metal_mass_released_total * norm_factor; - sp->feedback_data.to_distribute.metal_mass_from_SNII += - metal_mass_released_total * norm_factor; - } else { + const double mass_released = metal_mass_released_total + + metal_mass_released[chemistry_element_H] + + metal_mass_released[chemistry_element_He]; + +#ifdef SWIFT_DEBUG_CHECKS + if (mass_released <= 0) { error("wrong normalization!!!! mass_released = %e\n", mass_released); } +#endif + + /* Set normalisation factor. Note additional multiplication by the star + * initial mass as tables are per initial mass */ + const double norm_factor = M_init * mass_ejected / mass_released; + + /* Store what we want to distribute */ + for (int i = 0; i < chemistry_element_count; i++) { + feedback_data->to_distribute.metal_mass[i] += + metal_mass_released[i] * norm_factor; + feedback_data->to_distribute.mass_from_SNII += + metal_mass_released[i] * norm_factor; + } + feedback_data->to_distribute.total_metal_mass += + metal_mass_released_total * norm_factor; + feedback_data->to_distribute.metal_mass_from_SNII += + metal_mass_released_total * norm_factor; } /** @@ -541,21 +510,33 @@ INLINE static void evolve_SNII(float log10_min_mass, float log10_max_mass, * IMF weighted by the yields read from tables for each of the quantities of * interest. * - * Note for Matthieu: This function is poorly written and needs improving. - * * @param log10_min_mass log10 mass at the end of step * @param log10_max_mass log10 mass at the beginning of step - * @param stellar_yields array to store calculated yields for passing to - * integrate_imf + * @param M_init The initial mass of the star particle (in internal units). + * @param Z The total metallicity of the star (metal mass fraction). + * @param abundances The individual metal abundances (mass fractions) of the + * star. * @param props Properties of the feedback model. - * @param sp spart we are computing feedback for. + * @param feedback_data (return) The #feedback_spart_data to fill with things to + * distribute to the gas. */ -INLINE static void evolve_AGB(const float log10_min_mass, float log10_max_mass, - float* stellar_yields, +INLINE static void evolve_AGB(const double log10_min_mass, + double log10_max_mass, const double M_init, + const double Z, const float* const abundances, const struct feedback_props* props, - struct spart* sp) { + struct feedback_spart_data* const feedback_data) { + + /* Pull out common arrays */ + + /* Metal mass produced by the star */ + const double* const total_yields = + props->yield_AGB.total_metals_IMF_resampled; + + /* Individual elements produced by the star */ + const double* const metal_yields = props->yield_AGB.yield_IMF_resampled; - int low_imf_mass_bin_index, high_imf_mass_bin_index, mass_bin_index; + /* Elements already in the stars that are ejected */ + const double* const ejecta = props->yield_AGB.ejecta_IMF_resampled; /* If mass at end of step is greater than tabulated lower bound for IMF, limit * it.*/ @@ -567,41 +548,50 @@ INLINE static void evolve_AGB(const float log10_min_mass, float log10_max_mass, if (log10_min_mass >= log10_max_mass) return; /* determine which IMF mass bins contribute to the integral */ + int low_imf_mass_bin_index, high_imf_mass_bin_index; determine_imf_bins(log10_min_mass, log10_max_mass, &low_imf_mass_bin_index, &high_imf_mass_bin_index, props); /* determine which metallicity bin and offset this star belongs to */ - int iz_low = 0, iz_high = 0, low_index_3d, high_index_3d, low_index_2d, - high_index_2d; - float dz = 0.f; - determine_bin_yield_AGB(&iz_low, &iz_high, &dz, - log10(sp->chemistry_data.metal_mass_fraction_total), - props); - - /* compute metals produced */ - float metal_mass_released[chemistry_element_count], metal_mass_released_total; + int index_Z_lo = 0, index_Z_hi = 0; + float dZ = 0.f; + determine_bin_yields(&index_Z_lo, &index_Z_hi, &dZ, log10(Z), + props->yield_AGB.metallicity, + eagle_feedback_AGB_N_metals); + + /* Allocate temporary array for calculating imf weights */ + double stellar_yields[eagle_feedback_N_imf_bins]; + + /******************************* + * Compute metal mass produced * + *******************************/ + double metal_mass_released[chemistry_element_count]; + + /* Loop over all the elements */ for (int elem = 0; elem < chemistry_element_count; elem++) { - for (mass_bin_index = low_imf_mass_bin_index; + + /* Loop over all the relevent IMF mass bins */ + for (int mass_bin_index = low_imf_mass_bin_index; mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - low_index_3d = row_major_index_3d( - iz_low, elem, mass_bin_index, eagle_feedback_AGB_N_metals, + + const int lo_index_3d = row_major_index_3d( + index_Z_lo, elem, mass_bin_index, eagle_feedback_AGB_N_metals, chemistry_element_count, eagle_feedback_N_imf_bins); - high_index_3d = row_major_index_3d( - iz_high, elem, mass_bin_index, eagle_feedback_AGB_N_metals, + const int hi_index_3d = row_major_index_3d( + index_Z_hi, elem, mass_bin_index, eagle_feedback_AGB_N_metals, chemistry_element_count, eagle_feedback_N_imf_bins); - low_index_2d = row_major_index_2d(iz_low, mass_bin_index, - eagle_feedback_AGB_N_metals, - eagle_feedback_N_imf_bins); - high_index_2d = row_major_index_2d(iz_high, mass_bin_index, - eagle_feedback_AGB_N_metals, - eagle_feedback_N_imf_bins); + + const int lo_index_2d = row_major_index_2d(index_Z_lo, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + const int hi_index_2d = row_major_index_2d(index_Z_hi, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); stellar_yields[mass_bin_index] = - (1 - dz) * (props->yield_AGB.yield_IMF_resampled[low_index_3d] + - sp->chemistry_data.metal_mass_fraction[elem] * - props->yield_AGB.ejecta_IMF_resampled[low_index_2d]) + - dz * (props->yield_AGB.yield_IMF_resampled[high_index_3d] + - sp->chemistry_data.metal_mass_fraction[elem] * - props->yield_AGB.ejecta_IMF_resampled[high_index_2d]); + (1.f - dZ) * (metal_yields[lo_index_3d] + + abundances[elem] * ejecta[lo_index_2d]) + + (0.f + dZ) * (metal_yields[hi_index_3d] + + abundances[elem] * ejecta[hi_index_2d]); } metal_mass_released[elem] = integrate_imf( @@ -609,80 +599,79 @@ INLINE static void evolve_AGB(const float log10_min_mass, float log10_max_mass, stellar_yields, props); } - /* Compute mass produced */ - for (mass_bin_index = low_imf_mass_bin_index; + /************************************* + * Compute total metal mass produced * + *************************************/ + for (int mass_bin_index = low_imf_mass_bin_index; mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - low_index_2d = - row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_AGB_N_metals, - eagle_feedback_N_imf_bins); - high_index_2d = - row_major_index_2d(iz_high, mass_bin_index, eagle_feedback_AGB_N_metals, - eagle_feedback_N_imf_bins); + + const int lo_index_2d = row_major_index_2d(index_Z_lo, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + const int hi_index_2d = row_major_index_2d(index_Z_hi, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = - (1 - dz) * (props->yield_AGB.total_metals_IMF_resampled[low_index_2d] + - sp->chemistry_data.metal_mass_fraction_total * - props->yield_AGB.ejecta_IMF_resampled[low_index_2d]) + - dz * (props->yield_AGB.total_metals_IMF_resampled[high_index_2d] + - sp->chemistry_data.metal_mass_fraction_total * - props->yield_AGB.ejecta_IMF_resampled[high_index_2d]); + (1.f - dZ) * (total_yields[lo_index_2d] + Z * ejecta[lo_index_2d]) + + (0.f + dZ) * (total_yields[hi_index_2d] + Z * ejecta[hi_index_2d]); } - metal_mass_released_total = + double metal_mass_released_total = integrate_imf(log10_min_mass, log10_max_mass, eagle_imf_integration_yield_weight, stellar_yields, props); - /* yield normalization */ - float mass_ejected, mass_released; + /************************************************ + * Compute the total mass ejected from the star * + ************************************************/ - /* zero all negative values */ - for (int i = 0; i < chemistry_element_count; i++) - metal_mass_released[i] = max(metal_mass_released[i], 0.f); + for (int mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - metal_mass_released_total = max(metal_mass_released_total, 0.f); + const int lo_index_2d = row_major_index_2d(index_Z_lo, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + const int hi_index_2d = row_major_index_2d(index_Z_hi, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); - /* compute the total metal mass ejected from the star */ - for (mass_bin_index = low_imf_mass_bin_index; - mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { - low_index_2d = - row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_AGB_N_metals, - eagle_feedback_N_imf_bins); - high_index_2d = - row_major_index_2d(iz_high, mass_bin_index, eagle_feedback_AGB_N_metals, - eagle_feedback_N_imf_bins); stellar_yields[mass_bin_index] = - (1 - dz) * props->yield_AGB.ejecta_IMF_resampled[low_index_2d] + - dz * props->yield_AGB.ejecta_IMF_resampled[high_index_2d]; + (1.f - dZ) * ejecta[lo_index_2d] + dZ * ejecta[hi_index_2d]; } - - mass_ejected = + const double mass_ejected = integrate_imf(log10_min_mass, log10_max_mass, eagle_imf_integration_yield_weight, stellar_yields, props); + /* Zero all negative values */ + for (int i = 0; i < chemistry_element_count; i++) + metal_mass_released[i] = max(metal_mass_released[i], 0.f); + metal_mass_released_total = max(metal_mass_released_total, 0.f); + /* compute the total mass released */ - mass_released = metal_mass_released_total + - metal_mass_released[chemistry_element_H] + - metal_mass_released[chemistry_element_He]; - - /* normalize the yields */ - if (mass_released > 0) { - - /* Set normalisation factor. Note additional multiplication by the stellar - * initial mass as tables are per initial mass */ - const float norm_factor = sp->mass_init * mass_ejected / mass_released; - - for (int i = 0; i < chemistry_element_count; i++) { - sp->feedback_data.to_distribute.metal_mass[i] += - metal_mass_released[i] * norm_factor; - sp->feedback_data.to_distribute.mass_from_AGB += - metal_mass_released[i] * norm_factor; - } - sp->feedback_data.to_distribute.total_metal_mass += - metal_mass_released_total * norm_factor; - sp->feedback_data.to_distribute.metal_mass_from_AGB += - metal_mass_released_total * norm_factor; - } else { + const double mass_released = metal_mass_released_total + + metal_mass_released[chemistry_element_H] + + metal_mass_released[chemistry_element_He]; + +#ifdef SWIFT_DEBUG_CHECKS + if (mass_released <= 0) { error("wrong normalization!!!! mass_released = %e\n", mass_released); } +#endif + + /* Set normalisation factor. Note additional multiplication by the stellar + * initial mass as tables are per initial mass */ + const double norm_factor = M_init * mass_ejected / mass_released; + + for (int i = 0; i < chemistry_element_count; i++) { + feedback_data->to_distribute.metal_mass[i] += + metal_mass_released[i] * norm_factor; + feedback_data->to_distribute.mass_from_AGB += + metal_mass_released[i] * norm_factor; + } + feedback_data->to_distribute.total_metal_mass += + metal_mass_released_total * norm_factor; + feedback_data->to_distribute.metal_mass_from_AGB += + metal_mass_released_total * norm_factor; } /** @@ -707,9 +696,6 @@ void compute_stellar_evolution(const struct feedback_props* feedback_props, if (age < 0.f) error("Negative age for a star."); #endif - /* Allocate temporary array for calculating imf weights */ - float stellar_yields[eagle_feedback_N_imf_bins]; - /* Convert dt and stellar age from internal units to Gyr. */ const double Gyr_in_cgs = 1e9 * 365. * 24. * 3600.; const double time_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TIME); @@ -717,8 +703,17 @@ void compute_stellar_evolution(const struct feedback_props* feedback_props, const double dt_Gyr = dt * conversion_factor; const double star_age_Gyr = age * conversion_factor; - /* Get the metallicity */ - const float Z = sp->chemistry_data.metal_mass_fraction_total; + /* Get the birth mass of the star */ + const double M_init = sp->mass_init; + + /* Get the total metallicity (metal mass fraction) at birth time and impose a + * minimum */ + const double Z = max(chemistry_get_total_metal_mass_fraction_for_feedback(sp), + exp10(log10_min_metallicity)); + + /* Get the individual abundances (mass fractions at birth time) */ + const float* const abundances = + chemistry_get_metal_mass_fraction_for_feedback(sp); /* Properties collected in the stellar density loop. */ const float ngb_gas_mass = sp->feedback_data.to_collect.ngb_mass; @@ -743,9 +738,9 @@ void compute_stellar_evolution(const struct feedback_props* feedback_props, /* Calculate mass of stars that has died from the star's birth up to the * beginning and end of timestep */ - const float max_dying_mass_Msun = + const double max_dying_mass_Msun = dying_mass_msun(star_age_Gyr, Z, feedback_props); - const float min_dying_mass_Msun = + const double min_dying_mass_Msun = dying_mass_msun(star_age_Gyr + dt_Gyr, Z, feedback_props); #ifdef SWIFT_DEBUG_CHECK @@ -761,22 +756,22 @@ void compute_stellar_evolution(const struct feedback_props* feedback_props, if (min_dying_mass_Msun == max_dying_mass_Msun) return; /* Life is better in log */ - const float log10_max_dying_mass_Msun = log10f(max_dying_mass_Msun); - const float log10_min_dying_mass_Msun = log10f(min_dying_mass_Msun); + const double log10_max_dying_mass_Msun = log10(max_dying_mass_Msun); + const double log10_min_dying_mass_Msun = log10(min_dying_mass_Msun); /* Compute elements, energy and momentum to distribute from the * three channels SNIa, SNII, AGB */ if (feedback_props->with_SNIa_enrichment) { - evolve_SNIa(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, - feedback_props, sp, star_age_Gyr, dt_Gyr); + evolve_SNIa(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, M_init, Z, + feedback_props, star_age_Gyr, dt_Gyr, &sp->feedback_data); } if (feedback_props->with_SNII_enrichment) { - evolve_SNII(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, - stellar_yields, feedback_props, sp); + evolve_SNII(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, M_init, Z, + abundances, feedback_props, &sp->feedback_data); } if (feedback_props->with_AGB_enrichment) { - evolve_AGB(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, - stellar_yields, feedback_props, sp); + evolve_AGB(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, M_init, Z, + abundances, feedback_props, &sp->feedback_data); } #ifdef SWIFT_DEBUG_CHECKS @@ -925,9 +920,9 @@ void feedback_props_init(struct feedback_props* fp, /* Properties of the SNIa enrichment model -------------------------------- */ - const double SNIa_max_mass_msun = + fp->SNIa_max_mass_msun = parser_get_param_double(params, "EAGLEFeedback:SNIa_max_mass_Msun"); - fp->log10_SNIa_max_mass_msun = log10(SNIa_max_mass_msun); + fp->log10_SNIa_max_mass_msun = log10(fp->SNIa_max_mass_msun); /* Read SNIa timescale model parameters */ fp->SNIa_efficiency = diff --git a/src/feedback/EAGLE/feedback_iact.h b/src/feedback/EAGLE/feedback_iact.h index fa5507df74777077ebcbf9cab6de0750768460eb..2576e0f05947d0c33230b3140885b0a4fbcaa58e 100644 --- a/src/feedback/EAGLE/feedback_iact.h +++ b/src/feedback/EAGLE/feedback_iact.h @@ -164,15 +164,11 @@ runner_iact_nonsym_feedback_apply(const float r2, const float *dx, pj->chemistry_data.iron_mass_fraction_from_SNIa = new_iron_from_SNIa_mass * new_mass_inv; - /* Update mass fraction from SNIa */ - const double current_mass_from_SNIa = - pj->chemistry_data.mass_from_SNIa * current_mass; + /* Update mass from SNIa */ const double delta_mass_from_SNIa = si->feedback_data.to_distribute.mass_from_SNIa * Omega_frac; - const double new_mass_from_SNIa = - current_mass_from_SNIa + delta_mass_from_SNIa; - pj->chemistry_data.mass_from_SNIa = new_mass_from_SNIa * new_mass_inv; + pj->chemistry_data.mass_from_SNIa += delta_mass_from_SNIa; /* Update metal mass fraction from SNIa */ const double current_metal_mass_from_SNIa = @@ -185,15 +181,11 @@ runner_iact_nonsym_feedback_apply(const float r2, const float *dx, pj->chemistry_data.metal_mass_fraction_from_SNIa = new_metal_mass_from_SNIa * new_mass_inv; - /* Update mass fraction from SNII */ - const double current_mass_from_SNII = - pj->chemistry_data.mass_from_SNII * current_mass; + /* Update mass from SNII */ const double delta_mass_from_SNII = si->feedback_data.to_distribute.mass_from_SNII * Omega_frac; - const double new_mass_from_SNII = - current_mass_from_SNII + delta_mass_from_SNII; - pj->chemistry_data.mass_from_SNII = new_mass_from_SNII * new_mass_inv; + pj->chemistry_data.mass_from_SNII += delta_mass_from_SNII; /* Update metal mass fraction from SNII */ const double current_metal_mass_from_SNII = @@ -206,14 +198,11 @@ runner_iact_nonsym_feedback_apply(const float r2, const float *dx, pj->chemistry_data.metal_mass_fraction_from_SNII = new_metal_mass_from_SNII * new_mass_inv; - /* Update mass fraction from AGB */ - const double current_mass_from_AGB = - pj->chemistry_data.mass_from_AGB * current_mass; + /* Update mass from AGB */ const double delta_mass_from_AGB = si->feedback_data.to_distribute.mass_from_AGB * Omega_frac; - const double new_mass_from_AGB = current_mass_from_AGB + delta_mass_from_AGB; - pj->chemistry_data.mass_from_AGB = new_mass_from_AGB * new_mass_inv; + pj->chemistry_data.mass_from_AGB += delta_mass_from_AGB; /* Update metal mass fraction from AGB */ const double current_metal_mass_from_AGB = diff --git a/src/feedback/EAGLE/feedback_properties.h b/src/feedback/EAGLE/feedback_properties.h index 931d5146b6d648a6b083830662bd6b1e57635b2a..63928b59c153951747af0dcc5151bb7b118814d7 100644 --- a/src/feedback/EAGLE/feedback_properties.h +++ b/src/feedback/EAGLE/feedback_properties.h @@ -27,25 +27,28 @@ */ struct yield_table { - /* Yield table mass bins */ + /*! Yield table mass bins */ double *mass; - /* Yield table metallicity bins */ + /*! Yield table metallicity (metal mass fractions) bins */ double *metallicity; - /* Array to store yield table resampled by IMF mass bins */ + /*! Array to store yield table (individual metals produced by the star) + resampled by IMF mass bins */ double *yield_IMF_resampled; - /* Array to store yield table being read in */ + /*! Array to store yield table being read in */ double *yield; - /* Array to store table of ejecta resampled by IMF mass bins */ + /*! Array to store table of ejecta (metals alredy in the stars that are + ejected) resampled by IMF mass bins */ double *ejecta_IMF_resampled; - /* Array to store table of ejecta being read in */ + /*! Array to store table of ejecta being read in */ double *ejecta; - /* Array to store table of total mass released resampled by IMF mass bins */ + /*! Array to store table of total mass released ( metals produced by the star) + resampled by IMF mass bins */ double *total_metals_IMF_resampled; /* Array to store table of total mass released being read in */ @@ -133,8 +136,11 @@ struct feedback_props { /*! Inverse of time-scale of the SNIa decay function in Giga-years */ float SNIa_timescale_Gyr_inv; + /*! Maximal mass used for SNIa feedback (in solar masses) */ + double SNIa_max_mass_msun; + /*! Log 10 of the maximal mass used for SNIa feedback (in solar masses) */ - float log10_SNIa_max_mass_msun; + double log10_SNIa_max_mass_msun; /*! Energy released by one supernova type II in cgs units */ double E_SNIa_cgs; @@ -165,37 +171,37 @@ struct feedback_props { /* ------------- Parameters for IMF --------------- */ /*! Array to store calculated IMF */ - float *imf; + double *imf; /*! Arrays to store IMF mass bins */ - float *imf_mass_bin; + double *imf_mass_bin; /*! Arrays to store IMF mass bins (log10)*/ - float *imf_mass_bin_log10; + double *imf_mass_bin_log10; /*! Minimal stellar mass considered by the IMF (in solar masses) */ - float imf_min_mass_msun; + double imf_min_mass_msun; /*! Maximal stellar mass considered by the IMF (in solar masses) */ - float imf_max_mass_msun; + double imf_max_mass_msun; /*! Log 10 of the minimal stellar mass considered by the IMF (in solar masses) */ - float log10_imf_min_mass_msun; + double log10_imf_min_mass_msun; /*! Log 10 of the maximal stellar mass considered by the IMF (in solar masses) */ - float log10_imf_max_mass_msun; + double log10_imf_max_mass_msun; /* ------------ SNe feedback properties ------------ */ /*! Log 10 of the minimal stellar mass considered for SNII feedback (in solar * masses) */ - float log10_SNII_min_mass_msun; + double log10_SNII_min_mass_msun; /*! Log 10 of the maximal stellar mass considered for SNII feedback (in solar * masses) */ - float log10_SNII_max_mass_msun; + double log10_SNII_max_mass_msun; /*! Number of type II supernovae per solar mass */ float num_SNII_per_msun; diff --git a/src/feedback/EAGLE/imf.h b/src/feedback/EAGLE/imf.h index 767b2c312bfc0a5041da0c6be1db9e356bc829ad..52c6e25e4ef3ac4c6d4fefbd5ec7bbd8d1256bdb 100644 --- a/src/feedback/EAGLE/imf.h +++ b/src/feedback/EAGLE/imf.h @@ -58,7 +58,7 @@ INLINE static void determine_imf_bins( #endif const int N_bins = eagle_feedback_N_imf_bins; - const float *imf_bins_log10 = feedback_props->imf_mass_bin_log10; + const double *const imf_bins_log10 = feedback_props->imf_mass_bin_log10; /* Check whether lower mass is within the IMF mass bin range */ log10_min_mass = max(log10_min_mass, imf_bins_log10[0]); @@ -91,19 +91,19 @@ INLINE static void determine_imf_bins( * yield-weighted integration. * @param feedback_props the #feedback_props data structure */ -INLINE static float integrate_imf(const float log10_min_mass, - const float log10_max_mass, - const enum eagle_imf_integration_type mode, - const float *stellar_yields, - const struct feedback_props *feedback_props) { +INLINE static double integrate_imf( + const double log10_min_mass, const double log10_max_mass, + const enum eagle_imf_integration_type mode, + const double stellar_yields[eagle_feedback_N_imf_bins], + const struct feedback_props *feedback_props) { /* Pull out some common terms */ - const float *imf = feedback_props->imf; - const float *imf_mass_bin = feedback_props->imf_mass_bin; - const float *imf_mass_bin_log10 = feedback_props->imf_mass_bin_log10; + const double *imf = feedback_props->imf; + const double *imf_mass_bin = feedback_props->imf_mass_bin; + const double *imf_mass_bin_log10 = feedback_props->imf_mass_bin_log10; /* IMF mass bin spacing in log10 space. Assumes uniform spacing. */ - const float imf_log10_mass_bin_size = + const double imf_log10_mass_bin_size = imf_mass_bin_log10[1] - imf_mass_bin_log10[0]; /* Determine bins to integrate over based on integration bounds */ @@ -112,7 +112,7 @@ INLINE static float integrate_imf(const float log10_min_mass, feedback_props); /* Array for the integrand */ - float integrand[eagle_feedback_N_imf_bins]; + double integrand[eagle_feedback_N_imf_bins]; /* Add up the contribution from each of the IMF mass bins */ switch (mode) { @@ -153,7 +153,7 @@ INLINE static float integrate_imf(const float log10_min_mass, } /* Integrate using trapezoidal rule */ - float result = 0.f; + double result = 0.; for (int i = i_min; i < i_max + 1; i++) { result += integrand[i]; } @@ -163,31 +163,31 @@ INLINE static float integrate_imf(const float log10_min_mass, result -= 0.5 * (integrand[i_min] + integrand[i_max]); /* Correct first bin */ - const float first_bin_offset = + const double first_bin_offset = (log10_min_mass - imf_mass_bin_log10[i_min]) / imf_log10_mass_bin_size; - if (first_bin_offset < 0.5f) { + if (first_bin_offset < 0.5) { result -= first_bin_offset * integrand[i_min]; } else { - result -= 0.5f * integrand[i_min]; - result -= (first_bin_offset - 0.5f) * integrand[i_min + 1]; + result -= 0.5 * integrand[i_min]; + result -= (first_bin_offset - 0.5) * integrand[i_min + 1]; } /* Correct last bin */ - const float last_bin_offset = + const double last_bin_offset = (log10_max_mass - imf_mass_bin_log10[i_max - 1]) / imf_log10_mass_bin_size; if (last_bin_offset < 0.5) { - result -= 0.5f * integrand[i_max]; - result -= (0.5f - last_bin_offset) * integrand[i_max - 1]; + result -= 0.5 * integrand[i_max]; + result -= (0.5 - last_bin_offset) * integrand[i_max - 1]; } else { - result -= (1.f - last_bin_offset) * integrand[i_max]; + result -= (1.0 - last_bin_offset) * integrand[i_max]; } /* The IMF is tabulated in log10, multiply by log10(mass bin size) to get * result of integrating IMF */ - return result * imf_log10_mass_bin_size * ((float)M_LN10); + return result * imf_log10_mass_bin_size * M_LN10; } /** @@ -207,19 +207,19 @@ INLINE static void init_imf(struct feedback_props *feedback_props) { /* Allocate IMF array */ if (swift_memalign("imf-tables", (void **)&feedback_props->imf, SWIFT_STRUCT_ALIGNMENT, - eagle_feedback_N_imf_bins * sizeof(float)) != 0) + eagle_feedback_N_imf_bins * sizeof(double)) != 0) error("Failed to allocate IMF bins table"); /* Allocate array to store IMF mass bins */ if (swift_memalign("imf-tables", (void **)&feedback_props->imf_mass_bin, SWIFT_STRUCT_ALIGNMENT, - eagle_feedback_N_imf_bins * sizeof(float)) != 0) + eagle_feedback_N_imf_bins * sizeof(double)) != 0) error("Failed to allocate IMF bins table"); /* Allocate array to store IMF mass bins in log10 space */ if (swift_memalign("imf-tables", (void **)&feedback_props->imf_mass_bin_log10, SWIFT_STRUCT_ALIGNMENT, - eagle_feedback_N_imf_bins * sizeof(float)) != 0) + eagle_feedback_N_imf_bins * sizeof(double)) != 0) error("Failed to allocate IMF bins table"); /* Set IMF from Chabrier 2003, PASP, 115, 763 @@ -271,12 +271,10 @@ INLINE static void init_imf(struct feedback_props *feedback_props) { * @param feedback_props the #feedback_props data structure. * @return Mass of stars died up to that age in solar masses. */ -INLINE static float dying_mass_msun( - const float age_Gyr, const float Z, +INLINE static double dying_mass_msun( + const double age_Gyr, const double Z, const struct feedback_props *feedback_props) { - // MATTHIEU check this!!! - /* Pull out some common terms */ const double *lifetime_Z = feedback_props->lifetimes.metallicity; const double *lifetime_m = feedback_props->lifetimes.mass; @@ -285,62 +283,69 @@ INLINE static float dying_mass_msun( const int n_m = eagle_feedback_lifetime_N_masses; /* Early abort? */ - if (age_Gyr <= 0.f) { + if (age_Gyr <= 0.) { return feedback_props->imf_max_mass_msun; } - const float log10_age_yr = log10f(age_Gyr * 1e9f); + const double log10_age_yr = log10(age_Gyr * 1.0e9); /* Calculate index along the metallicity axis */ int Z_index; - float Z_offset; + double Z_offset; if (Z <= lifetime_Z[0]) { /* Before start of the table */ Z_index = 0; - Z_offset = 0.f; + Z_offset = 0.; } else if (Z >= lifetime_Z[n_Z - 1]) { /* After end of the table */ Z_index = n_Z - 2; - Z_offset = 1.f; + Z_offset = 1.; } else { /* Normal case: Somewhere inside the table */ - for (Z_index = 0; Z_index < n_Z - 1; Z_index++) { - if (lifetime_Z[Z_index + 1] > Z) break; + Z_index = 0; + while (Z_index < n_Z - 1 && lifetime_Z[Z_index + 1] <= Z) { + Z_index++; } +#ifdef SWIFT_DEBUG_CHECKS + if (Z_index >= n_Z) error("Z_index is beyond the range of the table"); +#endif + Z_offset = (Z - lifetime_Z[Z_index]) / (lifetime_Z[Z_index + 1] - lifetime_Z[Z_index]); } - /* Check whether we are not beyond the table */ + /* Check whether we are not beyond the age table for the low metallicity end + */ int time_index_lowZ = -1; - float time_offset_lowZ = 0.f; + double time_offset_lowZ = 0.; if (log10_age_yr >= dying_times[Z_index][0]) { /* Before start of the table */ time_index_lowZ = 0; - time_offset_lowZ = 0.f; + time_offset_lowZ = 0.; } else if (log10_age_yr <= dying_times[Z_index][n_m - 1]) { /* After end of the table */ time_index_lowZ = n_m - 2; - time_offset_lowZ = 1.0; + time_offset_lowZ = 1.; } - /* Check whether we are not beyond the table */ + /* Check whether we are not beyond the age table for the high metallicity end + */ int time_index_highZ = -1; - float time_offset_highZ = 0.f; + double time_offset_highZ = 0.; if (log10_age_yr >= dying_times[Z_index + 1][0]) { /* Before start of the table */ time_index_highZ = 0; - time_offset_highZ = 0.f; + time_offset_highZ = 0.; } else if (log10_age_yr <= dying_times[Z_index + 1][n_m - 1]) { @@ -350,11 +355,9 @@ INLINE static float dying_mass_msun( } /* Search the table starting from the largest times until we reach - a solution for the two bounds */ - int i = n_m; - while (i >= 0 && (time_index_lowZ == -1 || time_index_highZ == -1)) { - - i--; + a solution for the low-metallicity bound */ + int i = n_m - 1; + while (i >= 0 && time_index_lowZ == -1) { if (dying_times[Z_index][i] >= log10_age_yr && time_index_lowZ == -1) { @@ -366,7 +369,20 @@ INLINE static float dying_mass_msun( (log10_age_yr - dying_times[Z_index][time_index_lowZ]) / (dying_times[Z_index][time_index_lowZ + 1] - dying_times[Z_index][time_index_lowZ]); + + break; } + i--; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (time_index_lowZ == -1) error("Could not find low-metallicity bound!"); +#endif + + /* Search the table starting from the largest times until we reach + a solution for the high-metallicity bound */ + i = n_m - 1; + while (i >= 0 && time_index_highZ == -1) { if (dying_times[Z_index + 1][i] >= log10_age_yr && time_index_highZ == -1) { @@ -378,16 +394,23 @@ INLINE static float dying_mass_msun( (log10_age_yr - dying_times[Z_index + 1][time_index_highZ]) / (dying_times[Z_index + 1][time_index_highZ + 1] - dying_times[Z_index + 1][time_index_highZ]); + + break; } + i--; } +#ifdef SWIFT_DEBUG_CHECKS + if (time_index_highZ == -1) error("Could not find high-metallicity bound!"); +#endif + /* And now interpolate the solution */ - const float mass_low_Z = + const double mass_low_Z = interpolate_1d(lifetime_m, time_index_lowZ, time_offset_lowZ); - const float mass_high_Z = + const double mass_high_Z = interpolate_1d(lifetime_m, time_index_highZ, time_offset_highZ); - float mass = (1.f - Z_offset) * mass_low_Z + Z_offset * mass_high_Z; + double mass = (1. - Z_offset) * mass_low_Z + Z_offset * mass_high_Z; /* Check that we haven't killed too many stars */ mass = min(mass, feedback_props->imf_max_mass_msun); diff --git a/src/feedback/EAGLE/interpolate.h b/src/feedback/EAGLE/interpolate.h index 2da39c3d200694f9f60fd57904e5254838cb4863..115d75a71d28d1071766a73788a387a3d82bbbe2 100644 --- a/src/feedback/EAGLE/interpolate.h +++ b/src/feedback/EAGLE/interpolate.h @@ -68,9 +68,9 @@ __attribute__((always_inline)) static INLINE int row_major_index_3d( * @param dx offset within cell to interpolate */ __attribute__((always_inline)) static INLINE double interpolate_1d( - const double* table, const int i, const float dx) { + const double* const table, const int i, const double dx) { - const float tx = 1.f - dx; + const double tx = 1. - dx; return tx * table[i] + dx * table[i + 1]; } diff --git a/src/fof.c b/src/fof.c index fa6f13cfe9bd061bd3ca45dc3c4fb5e02313cb74..0895008af74dfe8662c81315caa684cfc03db841 100644 --- a/src/fof.c +++ b/src/fof.c @@ -20,6 +20,8 @@ /* Config parameters. */ #include "../config.h" +#ifdef WITH_FOF + /* Some standard headers. */ #include <errno.h> #include <libgen.h> @@ -207,7 +209,7 @@ void fof_set_initial_group_id_mapper(void *map_data, int num_elements, const size_t group_id_default = *((size_t *)extra_data); for (int i = 0; i < num_elements; ++i) { - gparts[i].group_id = group_id_default; + gparts[i].fof_data.group_id = group_id_default; } } @@ -267,12 +269,12 @@ void fof_allocate(const struct space *s, const long long total_nr_DM_particles, error("Failed to allocate list of group size for FOF search."); /* Set initial group ID of the gparts */ - size_t group_id_default = props->group_id_default; + const size_t group_id_default = props->group_id_default; ticks tic = getticks(); threadpool_map(&s->e->threadpool, fof_set_initial_group_id_mapper, s->gparts, - s->nr_gparts, sizeof(struct gpart), 0, &group_id_default); + s->nr_gparts, sizeof(struct gpart), 0, (void *)&group_id_default); message("Setting initial group ID took: %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); @@ -1019,7 +1021,7 @@ void fof_search_pair_cells_foreign( /* Check that the links have not already been added to the list. */ for (int l = 0; l < local_link_count; l++) { if ((local_group_links)[l].group_i == root_i && - (local_group_links)[l].group_j == pj->group_id) { + (local_group_links)[l].group_j == pj->fof_data.group_id) { found = 1; break; } @@ -1049,8 +1051,9 @@ void fof_search_pair_cells_foreign( local_group_links[local_link_count].group_i_size = group_size[root_i - node_offset]; - local_group_links[local_link_count].group_j = pj->group_id; - local_group_links[local_link_count].group_j_size = pj->group_size; + local_group_links[local_link_count].group_j = pj->fof_data.group_id; + local_group_links[local_link_count].group_j_size = + pj->fof_data.group_size; local_link_count++; } @@ -1321,9 +1324,9 @@ void fof_calc_group_mass_mapper(void *map_data, int num_elements, for (int ind = 0; ind < num_elements; ind++) { /* Only check groups above the minimum size. */ - if (gparts[ind].group_id != group_id_default) { + if (gparts[ind].fof_data.group_id != group_id_default) { - hashmap_key_t index = gparts[ind].group_id - group_id_offset; + hashmap_key_t index = gparts[ind].fof_data.group_id - group_id_offset; hashmap_value_t *data = hashmap_get(&map, index); /* Update group mass */ @@ -1412,7 +1415,7 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, for (size_t i = 0; i < nr_gparts; i++) { /* Check if the particle is in a group above the threshold. */ - if (gparts[i].group_id != group_id_default) { + if (gparts[i].fof_data.group_id != group_id_default) { const size_t root = fof_find_global(i, group_index, nr_gparts); @@ -1420,7 +1423,7 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, if (is_local(root, nr_gparts)) { const size_t index = - gparts[i].group_id - group_id_offset - num_groups_prev; + gparts[i].fof_data.group_id - group_id_offset - num_groups_prev; /* Update group mass */ group_mass[index] += gparts[i].mass; @@ -1467,7 +1470,7 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, for (size_t i = 0; i < nr_gparts; i++) { /* Only check groups above the minimum size and mass threshold. */ - if (gparts[i].group_id != group_id_default) { + if (gparts[i].fof_data.group_id != group_id_default) { size_t root = fof_find_global(i, group_index, nr_gparts); @@ -1475,7 +1478,7 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, if (is_local(root, nr_gparts)) { const size_t index = - gparts[i].group_id - group_id_offset - num_groups_prev; + gparts[i].fof_data.group_id - group_id_offset - num_groups_prev; /* Only seed groups above the mass threshold. */ if (group_mass[index] > seed_halo_mass) { @@ -1567,7 +1570,8 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, (fof_mass_recv[i].global_root >= node_offset + nr_gparts)) { error("Received global root index out of range!"); } - group_mass[gparts[fof_mass_recv[i].global_root - node_offset].group_id - + group_mass[gparts[fof_mass_recv[i].global_root - node_offset] + .fof_data.group_id - group_id_offset - num_groups_prev] += fof_mass_recv[i].group_mass; } @@ -1577,7 +1581,7 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, for (size_t i = 0; i < nrecv; i++) { const int offset = - gparts[fof_mass_recv[i].global_root - node_offset].group_id - + gparts[fof_mass_recv[i].global_root - node_offset].fof_data.group_id - group_id_offset - num_groups_prev; /* Only seed groups above the mass threshold. */ @@ -1614,7 +1618,7 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, } const int offset = - gparts[fof_mass_recv[i].global_root - node_offset].group_id - + gparts[fof_mass_recv[i].global_root - node_offset].fof_data.group_id - group_id_offset - num_groups_prev; /* If the densest particle found locally is not the global max, make sure we @@ -1714,10 +1718,10 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, /* JSW TODO: Parallelise with threadpool*/ for (size_t i = 0; i < nr_gparts; i++) { - const size_t index = gparts[i].group_id - group_id_offset; + const size_t index = gparts[i].fof_data.group_id - group_id_offset; /* Only check groups above the minimum mass threshold. */ - if (gparts[i].group_id != group_id_default) { + if (gparts[i].fof_data.group_id != group_id_default) { if (group_mass[index] > seed_halo_mass) { @@ -2003,14 +2007,14 @@ void fof_dump_group_data(const struct fof_props *props, : -1; #ifdef WITH_MPI fprintf(file, " %8zu %12zu %12e %12e %18lld %18lld\n", - gparts[group_offset - node_offset].group_id, + (size_t)gparts[group_offset - node_offset].fof_data.group_id, group_size[group_offset - node_offset], group_mass[i], max_part_density[i], max_part_density_index[i], part_id); #else fprintf(file, " %8zu %12zu %12e %12e %18lld %18lld\n", - gparts[group_offset].group_id, group_size[group_offset], - group_mass[i], max_part_density[i], max_part_density_index[i], - part_id); + (size_t)gparts[group_offset].fof_data.group_id, + group_size[group_offset], group_mass[i], max_part_density[i], + max_part_density_index[i], part_id); #endif } @@ -2061,8 +2065,8 @@ void fof_set_outgoing_root_mapper(void *map_data, int num_elements, const size_t root = fof_find_global(offset[k] - node_offset, group_index, nr_gparts); - gparts[k].group_id = root; - gparts[k].group_size = group_size[root - node_offset]; + gparts[k].fof_data.group_id = root; + gparts[k].fof_data.group_size = group_size[root - node_offset]; } } @@ -2703,17 +2707,18 @@ void fof_search_tree(struct fof_props *props, clocks_getunit()); /* Set default group ID for all particles */ - for (size_t i = 0; i < nr_gparts; i++) gparts[i].group_id = group_id_default; + for (size_t i = 0; i < nr_gparts; i++) + gparts[i].fof_data.group_id = group_id_default; /* Assign final group IDs to local root particles where the global root is * on this node and the group is large enough. Within a node IDs are * assigned in descending order of particle number. */ for (size_t i = 0; i < num_groups_local; i++) { #ifdef WITH_MPI - gparts[high_group_sizes[i].index - node_offset].group_id = + gparts[high_group_sizes[i].index - node_offset].fof_data.group_id = group_id_offset + i + num_groups_prev; #else - gparts[high_group_sizes[i].index].group_id = group_id_offset + i; + gparts[high_group_sizes[i].index].fof_data.group_id = group_id_offset + i; #endif } @@ -2803,7 +2808,7 @@ void fof_search_tree(struct fof_props *props, error("Received global root index out of range!"); } fof_index_recv[i].global_root = - gparts[fof_index_recv[i].global_root - node_offset].group_id; + gparts[fof_index_recv[i].global_root - node_offset].fof_data.group_id; } /* Send the result back */ @@ -2817,7 +2822,7 @@ void fof_search_tree(struct fof_props *props, (fof_index_send[i].local_root >= node_offset + nr_gparts)) { error("Sent local root index out of range!"); } - gparts[fof_index_send[i].local_root - node_offset].group_id = + gparts[fof_index_send[i].local_root - node_offset].fof_data.group_id = fof_index_send[i].global_root; } @@ -2833,7 +2838,7 @@ void fof_search_tree(struct fof_props *props, /* Assign every particle the group_id of its local root. */ for (size_t i = 0; i < nr_gparts; i++) { const size_t root = fof_find_local(i, nr_gparts, group_index); - gparts[i].group_id = gparts[root].group_id; + gparts[i].fof_data.group_id = gparts[root].fof_data.group_id; } if (verbose) @@ -2929,3 +2934,5 @@ void fof_struct_restore(struct fof_props *props, FILE *stream) { restart_read_blocks((void *)props, sizeof(struct fof_props), 1, stream, NULL, "fof_props"); } + +#endif /* WITH_FOF */ diff --git a/src/fof_io.h b/src/fof_io.h new file mode 100644 index 0000000000000000000000000000000000000000..24edefed3f0e04d40b9d6a1d444c8942426e4f41 --- /dev/null +++ b/src/fof_io.h @@ -0,0 +1,143 @@ +/******************************************************************************* + * 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_FOF_IO_H +#define SWIFT_FOF_IO_H + +/* Config parameters. */ +#include "../config.h" + +#ifdef WITH_FOF + +INLINE static void convert_part_group_id(const struct engine* e, + const struct part* p, + const struct xpart* xp, + long long* ret) { + ret[0] = p->gpart->fof_data.group_id; +} + +INLINE static void convert_spart_group_id(const struct engine* e, + const struct spart* sp, + long long* ret) { + ret[0] = sp->gpart->fof_data.group_id; +} + +INLINE static void convert_bpart_group_id(const struct engine* e, + const struct bpart* bp, + long long* ret) { + ret[0] = bp->gpart->fof_data.group_id; +} + +#endif /* WITH_FOF */ + +/** + * @brief Specifies which FoF-related particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extended particle array. + * @param list The list of i/o properties to write. + * + * @return The number of fields to write. + */ +INLINE static int fof_write_parts(const struct part* parts, + const struct xpart* xparts, + struct io_props* list) { + +#ifdef WITH_FOF + + list[0] = io_make_output_field_convert_part( + "FOFGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_part_group_id, + "Friends-Of-Friends ID of the group the particles belong to"); + return 1; +#else + return 0; +#endif +} + +/** + * @brief Specifies which FoF-related g-particle fields to write to a dataset + * + * @param gparts The g-particle array. + * @param list The list of i/o properties to write. + * + * @return The number of fields to write. + */ +INLINE static int fof_write_gparts(const struct gpart* gparts, + struct io_props* list) { + +#ifdef WITH_FOF + + list[0] = io_make_output_field( + "FOFGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, gparts, + fof_data.group_id, + "Friends-Of-Friends ID of the group the particles belong to"); + + return 1; +#else + return 0; +#endif +} + +/** + * @brief Specifies which FoF-related s-particle fields to write to a dataset + * + * @param sparts The s-particle array. + * @param list The list of i/o properties to write. + * + * @return The number of fields to write. + */ +INLINE static int fof_write_sparts(const struct spart* sparts, + struct io_props* list) { + +#ifdef WITH_FOF + + list[0] = io_make_output_field_convert_spart( + "FOFGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + convert_spart_group_id, + "Friends-Of-Friends ID of the group the particles belong to"); + return 1; +#else + return 0; +#endif +} + +/** + * @brief Specifies which FoF-related b-particle fields to write to a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to write. + * + * @return The number of fields to write. + */ +INLINE static int fof_write_bparts(const struct bpart* bparts, + struct io_props* list) { + +#ifdef WITH_FOF + + list[0] = io_make_output_field_convert_bpart( + "FOFGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + convert_bpart_group_id, + "Friends-Of-Friends ID of the group the particles belong to"); + return 1; +#else + return 0; +#endif +} + +#endif /* SWIFT_FOF_IO_H */ diff --git a/src/star_formation_iact.h b/src/fof_struct.h similarity index 59% rename from src/star_formation_iact.h rename to src/fof_struct.h index ef457214a23102bc33385705db41c89dc29d8b8f..38022b56db7d273291e2fa783c48adb7887f516f 100644 --- a/src/star_formation_iact.h +++ b/src/fof_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * 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 @@ -16,26 +16,33 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_STAR_FORMATION_IACT_H -#define SWIFT_STAR_FORMATION_IACT_H +#ifndef SWIFT_FOF_STRUCT_H +#define SWIFT_FOF_STRUCT_H + +/* Config parameters. */ +#include "../config.h" + +#ifdef WITH_FOF /** - * @file src/star_formation_iact.h - * @brief Branches between the different star formation iact. + * @brief Particle-carried fields for the FoF scheme. */ +struct fof_gpart_data { -/* Config parameters. */ -#include "../config.h" + /*! Particle group ID */ + size_t group_id; + + /*! Size of the FOF group of this particle */ + size_t group_size; +}; -/* Import the right star formation law definition */ -#if defined(STAR_FORMATION_NONE) -#include "./star_formation/none/star_formation_iact.h" -#elif defined(STAR_FORMATION_EAGLE) -#include "./star_formation/EAGLE/star_formation_iact.h" -#elif defined(STAR_FORMATION_GEAR) -#include "./star_formation/GEAR/star_formation_iact.h" #else -#error "Invalid choice of star formation law" + +/** + * @brief Particle-carried fields for the FoF scheme. + */ +struct fof_gpart_data {}; + #endif -#endif /* SWIFT_STAR_FORMATION_IACT_H */ +#endif /* SWIFT_FOF_STRUCT_H */ diff --git a/src/gravity.h b/src/gravity.h index 57cad8eba5f0772f85affd031a450c3ecb4dde59..4e255d2cbaaae012ab1282c82c8f6f15410e6b4b 100644 --- a/src/gravity.h +++ b/src/gravity.h @@ -34,6 +34,9 @@ #elif defined(POTENTIAL_GRAVITY) #include "./gravity/Potential/gravity.h" #define GRAVITY_IMPLEMENTATION "With potential calculation" +#elif defined(MULTI_SOFTENING_GRAVITY) +#include "./gravity/MultiSoftening/gravity.h" +#define GRAVITY_IMPLEMENTATION "With per-particle softening" #else #error "Invalid choice of gravity variant" #endif diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h index 6d1270c952100f3a25202fcdb22be09f9acaa8d9..a8ace72d2b2bb8cb92d2d386ae3ed5402c4f76e0 100644 --- a/src/gravity/Default/gravity.h +++ b/src/gravity/Default/gravity.h @@ -42,13 +42,16 @@ __attribute__((always_inline)) INLINE static float gravity_get_mass( /** * @brief Returns the current co-moving softening of a particle * + * Note that in this basic gravity scheme, all particles have + * the same softening length. + * * @param gp The particle of interest * @param grav_props The global gravity properties. */ __attribute__((always_inline)) INLINE static float gravity_get_softening( const struct gpart* gp, const struct gravity_props* restrict grav_props) { - return grav_props->epsilon_cur; + return grav_props->epsilon_DM_cur; } /** diff --git a/src/gravity/Default/gravity_iact.h b/src/gravity/Default/gravity_iact.h index 6fce3ddd512018e9ea4be21111c75904c77cb925..1fa789032bb464b8887c6fc48d643bff35f6b6d0 100644 --- a/src/gravity/Default/gravity_iact.h +++ b/src/gravity/Default/gravity_iact.h @@ -144,13 +144,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated( */ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( const float r_x, const float r_y, const float r_z, const float r2, - const float h, const float h_inv, const struct multipole *m, float *f_x, - float *f_y, float *f_z, float *pot) { + const float h, const float h_inv, const struct multipole *m, + float *restrict f_x, float *restrict f_y, float *restrict f_z, + float *restrict pot) { -/* In the case where the order is < 3, then there is only a monopole term left. +/* In the case where the order is < 2, then there is only a monopole term left. * We can default to the normal P-P interaction with the mass of the multipole * and its CoM as the "particle" property */ -#if SELF_GRAVITY_MULTIPOLE_ORDER < 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER < 2 float f_ij, pot_ij; runner_iact_grav_pp_full(r2, h * h, h_inv, h_inv * h_inv * h_inv, m->M_000, @@ -166,8 +167,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( /* Compute the derivatives of the potential */ struct potential_derivatives_M2P d; - potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, 0, 0.f, - &d); + potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, + /*periodic=*/0, /*r_s_inv=*/0.f, &d); /* 0th order contributions */ *f_x = m->M_000 * d.D_100; @@ -185,8 +186,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( /* *f_z = m->M_001 * d.D_002 + m->M_010 * d.D_011 + m->M_100 * d.D_101 ; */ #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 /* 2nd order contributions */ *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 + @@ -197,8 +197,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( m->M_101 * d.D_102 + m->M_110 * d.D_111 + m->M_200 * d.D_201; #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 /* 3rd order contributions */ *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 + @@ -214,6 +213,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( m->M_120 * d.D_121 + m->M_201 * d.D_202 + m->M_210 * d.D_211 + m->M_300 * d.D_301; +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + + /* 4th order contributions */ + *f_x += m->M_004 * d.D_104 + m->M_013 * d.D_113 + m->M_022 * d.D_122 + + m->M_031 * d.D_131 + m->M_040 * d.D_140 + m->M_103 * d.D_203 + + m->M_112 * d.D_212 + m->M_121 * d.D_221 + m->M_130 * d.D_230 + + m->M_202 * d.D_302 + m->M_211 * d.D_311 + m->M_220 * d.D_320 + + m->M_301 * d.D_401 + m->M_310 * d.D_410 + m->M_400 * d.D_500; + *f_y += m->M_004 * d.D_014 + m->M_013 * d.D_023 + m->M_022 * d.D_032 + + m->M_031 * d.D_041 + m->M_040 * d.D_050 + m->M_103 * d.D_113 + + m->M_112 * d.D_122 + m->M_121 * d.D_131 + m->M_130 * d.D_140 + + m->M_202 * d.D_212 + m->M_211 * d.D_221 + m->M_220 * d.D_230 + + m->M_301 * d.D_311 + m->M_310 * d.D_320 + m->M_400 * d.D_410; + *f_z += m->M_004 * d.D_005 + m->M_013 * d.D_014 + m->M_022 * d.D_023 + + m->M_031 * d.D_032 + m->M_040 * d.D_041 + m->M_103 * d.D_104 + + m->M_112 * d.D_113 + m->M_121 * d.D_122 + m->M_130 * d.D_131 + + m->M_202 * d.D_203 + m->M_211 * d.D_212 + m->M_220 * d.D_221 + + m->M_301 * d.D_302 + m->M_310 * d.D_311 + m->M_400 * d.D_401; #endif /* Take care of the the sign convention */ @@ -250,12 +268,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( const float r_x, const float r_y, const float r_z, const float r2, const float h, const float h_inv, const float r_s_inv, - const struct multipole *m, float *f_x, float *f_y, float *f_z, float *pot) { + const struct multipole *m, float *restrict f_x, float *restrict f_y, + float *restrict f_z, float *restrict pot) { -/* In the case where the order is < 3, then there is only a monopole term left. +/* In the case where the order is < 2, then there is only a monopole term left. * We can default to the normal P-P interaction with the mass of the multipole * and its CoM as the "particle" property */ -#if SELF_GRAVITY_MULTIPOLE_ORDER < 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER < 2 float f_ij, pot_ij; runner_iact_grav_pp_truncated(r2, h * h, h_inv, h_inv * h_inv * h_inv, @@ -271,7 +290,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( /* Compute the derivatives of the potential */ struct potential_derivatives_M2P d; - potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, 1, + potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, /*periodic=*/1, r_s_inv, &d); /* 0th order contributions */ @@ -290,8 +309,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( /* *f_z = m->M_001 * d.D_002 + m->M_010 * d.D_011 + m->M_100 * d.D_101 ; */ #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 /* 2nd order contributions */ *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 + @@ -302,8 +320,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( m->M_101 * d.D_102 + m->M_110 * d.D_111 + m->M_200 * d.D_201; #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 /* 3rd order contributions */ *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 + @@ -319,7 +336,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( m->M_120 * d.D_121 + m->M_201 * d.D_202 + m->M_210 * d.D_211 + m->M_300 * d.D_301; #endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + /* 4th order contributions */ + *f_x += m->M_004 * d.D_104 + m->M_013 * d.D_113 + m->M_022 * d.D_122 + + m->M_031 * d.D_131 + m->M_040 * d.D_140 + m->M_103 * d.D_203 + + m->M_112 * d.D_212 + m->M_121 * d.D_221 + m->M_130 * d.D_230 + + m->M_202 * d.D_302 + m->M_211 * d.D_311 + m->M_220 * d.D_320 + + m->M_301 * d.D_401 + m->M_310 * d.D_410 + m->M_400 * d.D_500; + *f_y += m->M_004 * d.D_014 + m->M_013 * d.D_023 + m->M_022 * d.D_032 + + m->M_031 * d.D_041 + m->M_040 * d.D_050 + m->M_103 * d.D_113 + + m->M_112 * d.D_122 + m->M_121 * d.D_131 + m->M_130 * d.D_140 + + m->M_202 * d.D_212 + m->M_211 * d.D_221 + m->M_220 * d.D_230 + + m->M_301 * d.D_311 + m->M_310 * d.D_320 + m->M_400 * d.D_410; + *f_z += m->M_004 * d.D_005 + m->M_013 * d.D_014 + m->M_022 * d.D_023 + + m->M_031 * d.D_032 + m->M_040 * d.D_041 + m->M_103 * d.D_104 + + m->M_112 * d.D_113 + m->M_121 * d.D_122 + m->M_130 * d.D_131 + + m->M_202 * d.D_203 + m->M_211 * d.D_212 + m->M_220 * d.D_221 + + m->M_301 * d.D_302 + m->M_310 * d.D_311 + m->M_400 * d.D_401; +#endif /* Take care of the the sign convention */ *f_x *= -1.f; *f_y *= -1.f; diff --git a/src/gravity/Default/gravity_io.h b/src/gravity/Default/gravity_io.h index 2e443e8fdc2479f3f2feff30281dccad9a7b6519..fea193e92cfe738880ef146b199253c7300501b4 100644 --- a/src/gravity/Default/gravity_io.h +++ b/src/gravity/Default/gravity_io.h @@ -104,19 +104,24 @@ INLINE static void darkmatter_write_particles(const struct gpart* gparts, int* num_fields) { /* Say how much we want to write */ - *num_fields = 5; + *num_fields = 4; /* List what we want to write */ list[0] = io_make_output_field_convert_gpart( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, gparts, convert_gpart_pos); + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, gparts, + convert_gpart_pos, "Co-moving position of the particles"); + list[1] = io_make_output_field_convert_gpart( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, gparts, convert_gpart_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, gparts, mass); - list[3] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, gparts, id_or_neg_offset); - list[4] = io_make_output_field("GroupIDs", INT, 1, UNIT_CONV_NO_UNITS, gparts, - group_id); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, gparts, convert_gpart_vel, + "Peculiar velocities of the stars. This is a * dx/dt where x is the " + "co-moving position of the particles."); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + gparts, mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, gparts, + id_or_neg_offset, "Unique ID of the particles"); } #endif /* SWIFT_DEFAULT_GRAVITY_IO_H */ diff --git a/src/gravity/Default/gravity_part.h b/src/gravity/Default/gravity_part.h index bc3df222b99b3899d3cfa3b563b67253214fd6be..a49a8cff0d27b56734e9ae7302dbf38b0bb520af 100644 --- a/src/gravity/Default/gravity_part.h +++ b/src/gravity/Default/gravity_part.h @@ -19,34 +19,32 @@ #ifndef SWIFT_DEFAULT_GRAVITY_PART_H #define SWIFT_DEFAULT_GRAVITY_PART_H -/* Gravity particle. */ +#include "fof_struct.h" + +/** + * @brief Gravity particle. + */ struct gpart { /*! Particle ID. If negative, it is the negative offset of the #part with which this gpart is linked. */ long long id_or_neg_offset; - /* Particle group ID and size in the FOF. */ - size_t group_id, group_size; - /*! Particle position. */ double x[3]; /*! Particle mass. */ float mass; + /*! Particle FoF properties (group ID, group size, ...) */ + struct fof_gpart_data fof_data; + /*! Time-step length */ timebin_t time_bin; /*! Type of the #gpart (DM, gas, star, ...) */ enum part_type type; - /*! Particle velocity. */ - char v_full[3]; - - /*! Particle acceleration. */ - char a_grav[3]; - #ifdef SWIFT_DEBUG_CHECKS /* Numer of gparts this gpart interacted with */ diff --git a/src/gravity/MultiSoftening/gravity.h b/src/gravity/MultiSoftening/gravity.h new file mode 100644 index 0000000000000000000000000000000000000000..893a835011c244a99b443edbd0f095aa5961cb2e --- /dev/null +++ b/src/gravity/MultiSoftening/gravity.h @@ -0,0 +1,250 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 Tom Theuns (tom.theuns@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_MULTI_SOFTENING_GRAVITY_H +#define SWIFT_MULTI_SOFTENING_GRAVITY_H + +#include <float.h> + +/* Local includes. */ +#include "cosmology.h" +#include "error.h" +#include "gravity_properties.h" +#include "kernel_gravity.h" +#include "minmax.h" + +/** + * @brief Returns the mass of a particle + * + * @param gp The particle of interest + */ +__attribute__((always_inline)) INLINE static float gravity_get_mass( + const struct gpart* restrict gp) { + + return gp->mass; +} + +/** + * @brief Returns the current co-moving softening of a particle + * + * @param gp The particle of interest + * @param grav_props The global gravity properties. + */ +__attribute__((always_inline)) INLINE static float gravity_get_softening( + const struct gpart* gp, const struct gravity_props* restrict grav_props) { + + switch (gp->type) { + case swift_type_dark_matter: + return grav_props->epsilon_DM_cur; + case swift_type_stars: + return grav_props->epsilon_baryon_cur; + case swift_type_gas: + return grav_props->epsilon_baryon_cur; + case swift_type_black_hole: + return grav_props->epsilon_baryon_cur; + case swift_type_dark_matter_background: + return grav_props->epsilon_background_fac * cbrtf(gp->mass); + default: +#ifdef SWIFT_DEBUG_CHECKS + error("Invalid gpart type!"); +#endif + return 0.f; + } +} + +/** + * @brief Add a contribution to this particle's potential. + * + * @param gp The particle. + * @param pot The contribution to add. + */ +__attribute__((always_inline)) INLINE static void +gravity_add_comoving_potential(struct gpart* restrict gp, float pot) { + + gp->potential += pot; +} + +/** + * @brief Returns the comoving potential of a particle. + * + * @param gp The particle of interest + */ +__attribute__((always_inline)) INLINE static float +gravity_get_comoving_potential(const struct gpart* restrict gp) { + + return gp->potential; +} + +/** + * @brief Returns the physical potential of a particle + * + * @param gp The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +gravity_get_physical_potential(const struct gpart* restrict gp, + const struct cosmology* cosmo) { + + return gp->potential * cosmo->a_inv; +} + +/** + * @brief Computes the gravity time-step of a given particle due to self-gravity + * + * We use Gadget-2's type 0 time-step criterion. + * + * @param gp Pointer to the g-particle data. + * @param a_hydro The accelerations coming from the hydro scheme (can be 0). + * @param grav_props Constants used in the gravity scheme. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static float +gravity_compute_timestep_self(const struct gpart* const gp, + const float a_hydro[3], + const struct gravity_props* restrict grav_props, + const struct cosmology* cosmo) { + + /* Get physical acceleration (gravity contribution) */ + float a_phys_x = gp->a_grav[0] * cosmo->a_factor_grav_accel; + float a_phys_y = gp->a_grav[1] * cosmo->a_factor_grav_accel; + float a_phys_z = gp->a_grav[2] * cosmo->a_factor_grav_accel; + + /* Get physical acceleration (hydro contribution) */ + a_phys_x += a_hydro[0] * cosmo->a_factor_hydro_accel; + a_phys_y += a_hydro[1] * cosmo->a_factor_hydro_accel; + a_phys_z += a_hydro[2] * cosmo->a_factor_hydro_accel; + + const float ac2 = + a_phys_x * a_phys_x + a_phys_y * a_phys_y + a_phys_z * a_phys_z; + + const float ac_inv = (ac2 > 0.f) ? 1.f / sqrtf(ac2) : FLT_MAX; + + const float epsilon = gravity_get_softening(gp, grav_props); + + const float dt = sqrtf(2. * kernel_gravity_softening_plummer_equivalent_inv * + cosmo->a * grav_props->eta * epsilon * ac_inv); + + return dt; +} + +/** + * @brief Prepares a g-particle for the gravity calculation + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the variaous tasks + * + * @param gp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void gravity_init_gpart( + struct gpart* gp) { + + /* Zero the acceleration */ + gp->a_grav[0] = 0.f; + gp->a_grav[1] = 0.f; + gp->a_grav[2] = 0.f; + gp->potential = 0.f; + +#ifdef SWIFT_GRAVITY_FORCE_CHECKS + gp->potential_PM = 0.f; + gp->a_grav_PM[0] = 0.f; + gp->a_grav_PM[1] = 0.f; + gp->a_grav_PM[2] = 0.f; +#endif + +#ifdef SWIFT_DEBUG_CHECKS + gp->num_interacted = 0; + gp->initialised = 1; +#endif +} + +/** + * @brief Finishes the gravity calculation. + * + * Multiplies the forces and accelerations by the appropiate constants. + * Applies cosmological correction for periodic BCs. + * + * No need to apply the potential normalisation correction for periodic + * BCs here since we do not compute the potential. + * + * @param gp The particle to act upon + * @param const_G Newton's constant in internal units. + * @param potential_normalisation Term to be added to all the particles. + * @param periodic Are we using periodic BCs? + */ +__attribute__((always_inline)) INLINE static void gravity_end_force( + struct gpart* gp, float const_G, const float potential_normalisation, + const int periodic) { + + /* Apply the periodic correction to the peculiar potential */ + if (periodic) gp->potential += potential_normalisation; + + /* Let's get physical... */ + gp->a_grav[0] *= const_G; + gp->a_grav[1] *= const_G; + gp->a_grav[2] *= const_G; + gp->potential *= const_G; + +#ifdef SWIFT_GRAVITY_FORCE_CHECKS + gp->potential_PM *= const_G; + gp->a_grav_PM[0] *= const_G; + gp->a_grav_PM[1] *= const_G; + gp->a_grav_PM[2] *= const_G; +#endif + +#ifdef SWIFT_DEBUG_CHECKS + gp->initialised = 0; /* Ready for next step */ +#endif +} + +/** + * @brief Kick the additional variables + * + * @param gp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void gravity_kick_extra( + struct gpart* gp, float dt) {} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param gp The particle. + */ +__attribute__((always_inline)) INLINE static void +gravity_reset_predicted_values(struct gpart* gp) {} + +/** + * @brief Initialises the g-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param gp The particle to act upon + * @param grav_props The global properties of the gravity calculation. + */ +__attribute__((always_inline)) INLINE static void gravity_first_init_gpart( + struct gpart* gp, const struct gravity_props* grav_props) { + + gp->time_bin = 0; + + gravity_init_gpart(gp); +} + +#endif /* SWIFT_MULTI_SOFTENING_GRAVITY_H */ diff --git a/src/gravity/MultiSoftening/gravity_debug.h b/src/gravity/MultiSoftening/gravity_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..7deea93f385d77871ed4bb28b881d7e93ce59436 --- /dev/null +++ b/src/gravity/MultiSoftening/gravity_debug.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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_MULTI_SOFTENING_GRAVITY_DEBUG_H +#define SWIFT_MULTI_SOFTENING_GRAVITY_DEBUG_H + +__attribute__((always_inline)) INLINE static void gravity_debug_particle( + const struct gpart* p) { + printf( + "mass=%.3e time_bin=%d\n" + "x=[%.5e,%.5e,%.5e], v_full=[%.5e,%.5e,%.5e], type=%d, " + "a=[%.5e,%.5e,%.5e], pot=%.5e\n", + p->mass, p->time_bin, p->x[0], p->x[1], p->x[2], p->v_full[0], + p->v_full[1], p->v_full[2], (int)p->type, p->a_grav[0], p->a_grav[1], + p->a_grav[2], p->potential); +#ifdef SWIFT_DEBUG_CHECKS + printf("num_interacted=%lld ti_drift=%lld ti_kick=%lld\n", p->num_interacted, + p->ti_drift, p->ti_kick); +#endif +} + +#endif /* SWIFT_MULTI_SOFTENING_GRAVITY_DEBUG_H */ diff --git a/src/gravity/MultiSoftening/gravity_iact.h b/src/gravity/MultiSoftening/gravity_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..f5020646e1bdc693005d01476735f9ad8485c843 --- /dev/null +++ b/src/gravity/MultiSoftening/gravity_iact.h @@ -0,0 +1,396 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * 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_MULTI_SOFTENING_GRAVITY_IACT_H +#define SWIFT_MULTI_SOFTENING_GRAVITY_IACT_H + +/* Includes. */ +#include "kernel_gravity.h" +#include "kernel_long_gravity.h" +#include "multipole.h" + +/* Standard headers */ +#include <float.h> + +/** + * @brief Computes the intensity of the force at a point generated by a + * point-mass. + * + * The returned quantity needs to be multiplied by the distance vector to obtain + * the force vector. + * + * @param r2 Square of the distance to the point-mass. + * @param h2 Square of the softening length. + * @param h_inv Inverse of the softening length. + * @param h_inv3 Cube of the inverse of the softening length. + * @param mass Mass of the point-mass. + * @param f_ij (return) The force intensity. + * @param pot_ij (return) The potential. + */ +__attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full( + const float r2, const float h2, const float h_inv, const float h_inv3, + const float mass, float *f_ij, float *pot_ij) { + + /* Get the inverse distance */ + const float r_inv = 1.f / sqrtf(r2 + FLT_MIN); + + /* Should we soften ? */ + if (r2 >= h2) { + + /* Get Newtonian gravity */ + *f_ij = mass * r_inv * r_inv * r_inv; + *pot_ij = -mass * r_inv; + + } else { + + const float r = r2 * r_inv; + const float ui = r * h_inv; + + float W_f_ij, W_pot_ij; + kernel_grav_force_eval(ui, &W_f_ij); + kernel_grav_pot_eval(ui, &W_pot_ij); + + /* Get softened gravity */ + *f_ij = mass * h_inv3 * W_f_ij; + *pot_ij = mass * h_inv * W_pot_ij; + } +} + +/** + * @brief Computes the intensity of the force at a point generated by a + * point-mass truncated for long-distance periodicity. + * + * The returned quantity needs to be multiplied by the distance vector to obtain + * the force vector. + * + * @param r2 Square of the distance to the point-mass. + * @param h2 Square of the softening length. + * @param h_inv Inverse of the softening length. + * @param h_inv3 Cube of the inverse of the softening length. + * @param mass Mass of the point-mass. + * @param r_s_inv Inverse of the mesh smoothing scale. + * @param f_ij (return) The force intensity. + * @param pot_ij (return) The potential. + */ +__attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated( + const float r2, const float h2, const float h_inv, const float h_inv3, + const float mass, const float r_s_inv, float *f_ij, float *pot_ij) { + + /* Get the inverse distance */ + const float r_inv = 1.f / sqrtf(r2 + FLT_MIN); + const float r = r2 * r_inv; + + /* Should we soften ? */ + if (r2 >= h2) { + + /* Get Newtonian gravity */ + *f_ij = mass * r_inv * r_inv * r_inv; + *pot_ij = -mass * r_inv; + + } else { + + const float ui = r * h_inv; + float W_f_ij, W_pot_ij; + + kernel_grav_force_eval(ui, &W_f_ij); + kernel_grav_pot_eval(ui, &W_pot_ij); + + /* Get softened gravity */ + *f_ij = mass * h_inv3 * W_f_ij; + *pot_ij = mass * h_inv * W_pot_ij; + } + + /* Get long-range correction */ + const float u_lr = r * r_s_inv; + float corr_f_lr, corr_pot_lr; + kernel_long_grav_force_eval(u_lr, &corr_f_lr); + kernel_long_grav_pot_eval(u_lr, &corr_pot_lr); + *f_ij *= corr_f_lr; + *pot_ij *= corr_pot_lr; +} + +/** + * @brief Computes the forces at a point generated by a multipole. + * + * This assumes M_100 == M_010 == M_001 == 0. + * This uses the quadrupole and trace of the octupole terms only and defaults to + * the monopole if the code is compiled with low-order gravity only. + * + * @param r_x x-component of the distance vector to the multipole. + * @param r_y y-component of the distance vector to the multipole. + * @param r_z z-component of the distance vector to the multipole. + * @param r2 Square of the distance vector to the multipole. + * @param h The softening length. + * @param h_inv Inverse of the softening length. + * @param m The multipole. + * @param f_x (return) The x-component of the acceleration. + * @param f_y (return) The y-component of the acceleration. + * @param f_z (return) The z-component of the acceleration. + * @param pot (return) The potential. + */ +__attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( + const float r_x, const float r_y, const float r_z, const float r2, + const float h, const float h_inv, const struct multipole *m, + float *restrict f_x, float *restrict f_y, float *restrict f_z, + float *restrict pot) { + +/* In the case where the order is < 2, then there is only a monopole term left. + * We can default to the normal P-P interaction with the mass of the multipole + * and its CoM as the "particle" property */ +#if SELF_GRAVITY_MULTIPOLE_ORDER < 2 + + float f_ij, pot_ij; + runner_iact_grav_pp_full(r2, h * h, h_inv, h_inv * h_inv * h_inv, m->M_000, + &f_ij, &pot_ij); + *f_x = f_ij * r_x; + *f_y = f_ij * r_y; + *f_z = f_ij * r_z; + *pot = pot_ij; + +#else + + /* Get the inverse distance */ + const float r_inv = 1.f / sqrtf(r2); + + /* Compute the derivatives of the potential */ + struct potential_derivatives_M2P d; + potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, + /*periodic=*/0, /*r_s_inv=*/0.f, &d); + + /* 0th order contributions */ + *f_x = m->M_000 * d.D_100; + *f_y = m->M_000 * d.D_010; + *f_z = m->M_000 * d.D_001; + *pot = m->M_000 * d.D_000; + +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 + + /* 1st order contributions */ + + /* 1st order contributions are all 0 since the dipole is 0 */ + + /* *f_x = m->M_001 * d.D_101 + m->M_010 * d.D_110 + m->M_100 * d.D_200 ; */ + /* *f_y = m->M_001 * d.D_011 + m->M_010 * d.D_020 + m->M_100 * d.D_110 ; */ + /* *f_z = m->M_001 * d.D_002 + m->M_010 * d.D_011 + m->M_100 * d.D_101 ; */ + /* *pot = m->M_001 * d.D_001 + m->M_010 * d.D_010 + m->M_100 * d.D_100 ; */ + +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 + + /* 2nd order contributions */ + *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 + + m->M_101 * d.D_201 + m->M_110 * d.D_210 + m->M_200 * d.D_300; + *f_y += m->M_002 * d.D_012 + m->M_011 * d.D_021 + m->M_020 * d.D_030 + + m->M_101 * d.D_111 + m->M_110 * d.D_120 + m->M_200 * d.D_210; + *f_z += m->M_002 * d.D_003 + m->M_011 * d.D_012 + m->M_020 * d.D_021 + + m->M_101 * d.D_102 + m->M_110 * d.D_111 + m->M_200 * d.D_201; + *pot += m->M_002 * d.D_002 + m->M_011 * d.D_011 + m->M_020 * d.D_020 + + m->M_101 * d.D_101 + m->M_110 * d.D_110 + m->M_200 * d.D_200; + +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 + + /* 3rd order contributions */ + *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 + + m->M_030 * d.D_130 + m->M_102 * d.D_202 + m->M_111 * d.D_211 + + m->M_120 * d.D_220 + m->M_201 * d.D_301 + m->M_210 * d.D_310 + + m->M_300 * d.D_400; + *f_y += m->M_003 * d.D_013 + m->M_012 * d.D_022 + m->M_021 * d.D_031 + + m->M_030 * d.D_040 + m->M_102 * d.D_112 + m->M_111 * d.D_121 + + m->M_120 * d.D_130 + m->M_201 * d.D_211 + m->M_210 * d.D_220 + + m->M_300 * d.D_310; + *f_z += m->M_003 * d.D_004 + m->M_012 * d.D_013 + m->M_021 * d.D_022 + + m->M_030 * d.D_031 + m->M_102 * d.D_103 + m->M_111 * d.D_112 + + m->M_120 * d.D_121 + m->M_201 * d.D_202 + m->M_210 * d.D_211 + + m->M_300 * d.D_301; + *pot += m->M_003 * d.D_003 + m->M_012 * d.D_012 + m->M_021 * d.D_021 + + m->M_030 * d.D_030 + m->M_102 * d.D_102 + m->M_111 * d.D_111 + + m->M_120 * d.D_120 + m->M_201 * d.D_201 + m->M_210 * d.D_210 + + m->M_300 * d.D_300; + +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + + /* 4th order contributions */ + *f_x += m->M_004 * d.D_104 + m->M_013 * d.D_113 + m->M_022 * d.D_122 + + m->M_031 * d.D_131 + m->M_040 * d.D_140 + m->M_103 * d.D_203 + + m->M_112 * d.D_212 + m->M_121 * d.D_221 + m->M_130 * d.D_230 + + m->M_202 * d.D_302 + m->M_211 * d.D_311 + m->M_220 * d.D_320 + + m->M_301 * d.D_401 + m->M_310 * d.D_410 + m->M_400 * d.D_500; + *f_y += m->M_004 * d.D_014 + m->M_013 * d.D_023 + m->M_022 * d.D_032 + + m->M_031 * d.D_041 + m->M_040 * d.D_050 + m->M_103 * d.D_113 + + m->M_112 * d.D_122 + m->M_121 * d.D_131 + m->M_130 * d.D_140 + + m->M_202 * d.D_212 + m->M_211 * d.D_221 + m->M_220 * d.D_230 + + m->M_301 * d.D_311 + m->M_310 * d.D_320 + m->M_400 * d.D_410; + *f_z += m->M_004 * d.D_005 + m->M_013 * d.D_014 + m->M_022 * d.D_023 + + m->M_031 * d.D_032 + m->M_040 * d.D_041 + m->M_103 * d.D_104 + + m->M_112 * d.D_113 + m->M_121 * d.D_122 + m->M_130 * d.D_131 + + m->M_202 * d.D_203 + m->M_211 * d.D_212 + m->M_220 * d.D_221 + + m->M_301 * d.D_302 + m->M_310 * d.D_311 + m->M_400 * d.D_401; + *pot += m->M_004 * d.D_004 + m->M_013 * d.D_013 + m->M_022 * d.D_022 + + m->M_031 * d.D_031 + m->M_040 * d.D_040 + m->M_103 * d.D_103 + + m->M_112 * d.D_112 + m->M_121 * d.D_121 + m->M_130 * d.D_130 + + m->M_202 * d.D_202 + m->M_211 * d.D_211 + m->M_220 * d.D_220 + + m->M_301 * d.D_301 + m->M_310 * d.D_310 + m->M_400 * d.D_400; + +#endif + /* Take care of the the sign convention */ + *f_x *= -1.f; + *f_y *= -1.f; + *f_z *= -1.f; + *pot *= -1.f; +#endif +} + +/** + * @brief Computes the forces at a point generated by a multipole, truncated for + * long-range periodicity. + * + * This assumes M_100 == M_010 == M_001 == 0. + * This uses the quadrupole term and trace of the octupole terms only and + * defaults to the monopole if the code is compiled with low-order gravity only. + * + * @param r_x x-component of the distance vector to the multipole. + * @param r_y y-component of the distance vector to the multipole. + * @param r_z z-component of the distance vector to the multipole. + * @param r2 Square of the distance vector to the multipole. + * @param h The softening length. + * @param h_inv Inverse of the softening length. + * @param r_s_inv The inverse of the gravity mesh-smoothing scale. + * @param m The multipole. + * @param f_x (return) The x-component of the acceleration. + * @param f_y (return) The y-component of the acceleration. + * @param f_z (return) The z-component of the acceleration. + * @param pot (return) The potential. + */ +__attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( + const float r_x, const float r_y, const float r_z, const float r2, + const float h, const float h_inv, const float r_s_inv, + const struct multipole *m, float *restrict f_x, float *restrict f_y, + float *restrict f_z, float *restrict pot) { + +/* In the case where the order is < 2, then there is only a monopole term left. + * We can default to the normal P-P interaction with the mass of the multipole + * and its CoM as the "particle" property */ +#if SELF_GRAVITY_MULTIPOLE_ORDER < 2 + + float f_ij, pot_ij; + runner_iact_grav_pp_truncated(r2, h * h, h_inv, h_inv * h_inv * h_inv, + m->M_000, r_s_inv, &f_ij, &pot_ij); + *f_x = f_ij * r_x; + *f_y = f_ij * r_y; + *f_z = f_ij * r_z; + *pot = -pot_ij; + +#else + + /* Get the inverse distance */ + const float r_inv = 1.f / sqrtf(r2); + + /* Compute the derivatives of the potential */ + struct potential_derivatives_M2P d; + potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, /*periodic=*/1, + r_s_inv, &d); + + /* 0th order contributions */ + *f_x = m->M_000 * d.D_100; + *f_y = m->M_000 * d.D_010; + *f_z = m->M_000 * d.D_001; + *pot = m->M_000 * d.D_000; + +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 + + /* 1st order contributions */ + + /* 1st order contributions are all 0 since the dipole is 0 */ + + /* *f_x = m->M_001 * d.D_101 + m->M_010 * d.D_110 + m->M_100 * d.D_200 ; */ + /* *f_y = m->M_001 * d.D_011 + m->M_010 * d.D_020 + m->M_100 * d.D_110 ; */ + /* *f_z = m->M_001 * d.D_002 + m->M_010 * d.D_011 + m->M_100 * d.D_101 ; */ + /* *pot = m->M_001 * d.D_001 + m->M_010 * d.D_010 + m->M_100 * d.D_100 ; */ + +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 + + /* 2nd order contributions */ + *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 + + m->M_101 * d.D_201 + m->M_110 * d.D_210 + m->M_200 * d.D_300; + *f_y += m->M_002 * d.D_012 + m->M_011 * d.D_021 + m->M_020 * d.D_030 + + m->M_101 * d.D_111 + m->M_110 * d.D_120 + m->M_200 * d.D_210; + *f_z += m->M_002 * d.D_003 + m->M_011 * d.D_012 + m->M_020 * d.D_021 + + m->M_101 * d.D_102 + m->M_110 * d.D_111 + m->M_200 * d.D_201; + *pot += m->M_002 * d.D_002 + m->M_011 * d.D_011 + m->M_020 * d.D_020 + + m->M_101 * d.D_101 + m->M_110 * d.D_110 + m->M_200 * d.D_200; + +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 + + /* 3rd order contributions */ + *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 + + m->M_030 * d.D_130 + m->M_102 * d.D_202 + m->M_111 * d.D_211 + + m->M_120 * d.D_220 + m->M_201 * d.D_301 + m->M_210 * d.D_310 + + m->M_300 * d.D_400; + *f_y += m->M_003 * d.D_013 + m->M_012 * d.D_022 + m->M_021 * d.D_031 + + m->M_030 * d.D_040 + m->M_102 * d.D_112 + m->M_111 * d.D_121 + + m->M_120 * d.D_130 + m->M_201 * d.D_211 + m->M_210 * d.D_220 + + m->M_300 * d.D_310; + *f_z += m->M_003 * d.D_004 + m->M_012 * d.D_013 + m->M_021 * d.D_022 + + m->M_030 * d.D_031 + m->M_102 * d.D_103 + m->M_111 * d.D_112 + + m->M_120 * d.D_121 + m->M_201 * d.D_202 + m->M_210 * d.D_211 + + m->M_300 * d.D_301; + *pot += m->M_003 * d.D_003 + m->M_012 * d.D_012 + m->M_021 * d.D_021 + + m->M_030 * d.D_030 + m->M_102 * d.D_102 + m->M_111 * d.D_111 + + m->M_120 * d.D_120 + m->M_201 * d.D_201 + m->M_210 * d.D_210 + + m->M_300 * d.D_300; + +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + + /* 4th order contributions */ + *f_x += m->M_004 * d.D_104 + m->M_013 * d.D_113 + m->M_022 * d.D_122 + + m->M_031 * d.D_131 + m->M_040 * d.D_140 + m->M_103 * d.D_203 + + m->M_112 * d.D_212 + m->M_121 * d.D_221 + m->M_130 * d.D_230 + + m->M_202 * d.D_302 + m->M_211 * d.D_311 + m->M_220 * d.D_320 + + m->M_301 * d.D_401 + m->M_310 * d.D_410 + m->M_400 * d.D_500; + *f_y += m->M_004 * d.D_014 + m->M_013 * d.D_023 + m->M_022 * d.D_032 + + m->M_031 * d.D_041 + m->M_040 * d.D_050 + m->M_103 * d.D_113 + + m->M_112 * d.D_122 + m->M_121 * d.D_131 + m->M_130 * d.D_140 + + m->M_202 * d.D_212 + m->M_211 * d.D_221 + m->M_220 * d.D_230 + + m->M_301 * d.D_311 + m->M_310 * d.D_320 + m->M_400 * d.D_410; + *f_z += m->M_004 * d.D_005 + m->M_013 * d.D_014 + m->M_022 * d.D_023 + + m->M_031 * d.D_032 + m->M_040 * d.D_041 + m->M_103 * d.D_104 + + m->M_112 * d.D_113 + m->M_121 * d.D_122 + m->M_130 * d.D_131 + + m->M_202 * d.D_203 + m->M_211 * d.D_212 + m->M_220 * d.D_221 + + m->M_301 * d.D_302 + m->M_310 * d.D_311 + m->M_400 * d.D_401; + *pot += m->M_004 * d.D_004 + m->M_013 * d.D_013 + m->M_022 * d.D_022 + + m->M_031 * d.D_031 + m->M_040 * d.D_040 + m->M_103 * d.D_103 + + m->M_112 * d.D_112 + m->M_121 * d.D_121 + m->M_130 * d.D_130 + + m->M_202 * d.D_202 + m->M_211 * d.D_211 + m->M_220 * d.D_220 + + m->M_301 * d.D_301 + m->M_310 * d.D_310 + m->M_400 * d.D_400; + +#endif + /* Take care of the the sign convention */ + *f_x *= -1.f; + *f_y *= -1.f; + *f_z *= -1.f; + *pot *= -1.f; +#endif +} + +#endif /* SWIFT_MULTI_SOFTENING_GRAVITY_IACT_H */ diff --git a/src/gravity/MultiSoftening/gravity_io.h b/src/gravity/MultiSoftening/gravity_io.h new file mode 100644 index 0000000000000000000000000000000000000000..412a270afef5d3e7ab75530184782479aaa46b82 --- /dev/null +++ b/src/gravity/MultiSoftening/gravity_io.h @@ -0,0 +1,138 @@ +/******************************************************************************* + * 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_MULTI_SOFTENING_GRAVITY_IO_H +#define SWIFT_MULTI_SOFTENING_GRAVITY_IO_H + +#include "io_properties.h" + +INLINE static void convert_gpart_pos(const struct engine* e, + const struct gpart* gp, double* ret) { + + if (e->s->periodic) { + ret[0] = box_wrap(gp->x[0], 0.0, e->s->dim[0]); + ret[1] = box_wrap(gp->x[1], 0.0, e->s->dim[1]); + ret[2] = box_wrap(gp->x[2], 0.0, e->s->dim[2]); + } else { + ret[0] = gp->x[0]; + ret[1] = gp->x[1]; + ret[2] = gp->x[2]; + } +} + +INLINE static void convert_gpart_vel(const struct engine* e, + const struct gpart* gp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, gp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, gp->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + +INLINE static void convert_gpart_soft(const struct engine* e, + const struct gpart* gp, float* ret) { + + ret[0] = kernel_gravity_softening_plummer_equivalent_inv * + gravity_get_softening(gp, e->gravity_properties); +} + +/** + * @brief Specifies which g-particle fields to read from a dataset + * + * @param gparts The g-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 darkmatter_read_particles(struct gpart* gparts, + struct io_props* list, + int* num_fields) { + + /* Say how much we want to read */ + *num_fields = 4; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, gparts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, gparts, v_full); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + gparts, mass); + list[3] = io_make_input_field("ParticleIDs", ULONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, gparts, id_or_neg_offset); +} + +/** + * @brief Specifies which g-particle fields to write to a dataset + * + * @param gparts The g-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 darkmatter_write_particles(const struct gpart* gparts, + struct io_props* list, + int* num_fields) { + + /* Say how much we want to write */ + *num_fields = 5; + + /* List what we want to write */ + list[0] = io_make_output_field_convert_gpart( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, gparts, + convert_gpart_pos, "Co-moving position of the particles"); + + list[1] = io_make_output_field_convert_gpart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, gparts, convert_gpart_vel, + "Peculiar velocities of the stars. This is a * dx/dt where x is the " + "co-moving position of the particles."); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + gparts, mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, gparts, + id_or_neg_offset, "Unique ID of the particles"); + + list[4] = io_make_output_field_convert_gpart( + "Softenings", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, gparts, convert_gpart_soft, + "Co-moving Plummer-equivalent softening lengths of the particles."); +} + +#endif /* SWIFT_MULTI_SOFTENING_GRAVITY_IO_H */ diff --git a/src/gravity/MultiSoftening/gravity_part.h b/src/gravity/MultiSoftening/gravity_part.h new file mode 100644 index 0000000000000000000000000000000000000000..de410fbbb988136af46c6f9085bdafee863b9f99 --- /dev/null +++ b/src/gravity/MultiSoftening/gravity_part.h @@ -0,0 +1,88 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@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_MULTI_SOFTENING_GRAVITY_PART_H +#define SWIFT_MULTI_SOFTENING_GRAVITY_PART_H + +#include "fof_struct.h" + +/* Gravity particle. */ +struct gpart { + + /*! Particle ID. If negative, it is the negative offset of the #part with + which this gpart is linked. */ + long long id_or_neg_offset; + + /*! Particle position. */ + double x[3]; + + /*! Particle velocity. */ + float v_full[3]; + + /*! Particle acceleration. */ + float a_grav[3]; + + /*! Gravitational potential */ + float potential; + + /*! Particle mass. */ + float mass; + + /*! Particle FoF properties (group ID, group size, ...) */ + struct fof_gpart_data fof_data; + + /*! Time-step length */ + timebin_t time_bin; + + /*! Type of the #gpart (DM, gas, star, ...) */ + enum part_type type; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Numer of gparts this gpart interacted with */ + long long num_interacted; + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + + /* Has this particle been initialised? */ + int initialised; + +#endif + +#ifdef SWIFT_GRAVITY_FORCE_CHECKS + + /*! Acceleration taken from the mesh only */ + float a_grav_PM[3]; + + /*! Potential taken from the mesh only */ + float potential_PM; + + /* Brute-force particle acceleration. */ + double a_grav_exact[3]; + + /* Brute-force particle potential. */ + double potential_exact; +#endif + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_MULTI_SOFTENING_GRAVITY_PART_H */ diff --git a/src/gravity/Potential/gravity.h b/src/gravity/Potential/gravity.h index 7d38e9126f1a313b169092b080ebc312c4bbe1bc..3a3e9680016ef51c554691c6f43f8279cde8caf9 100644 --- a/src/gravity/Potential/gravity.h +++ b/src/gravity/Potential/gravity.h @@ -22,6 +22,7 @@ #include <float.h> +/* Local includes. */ #include "cosmology.h" #include "gravity_properties.h" #include "kernel_gravity.h" @@ -41,13 +42,16 @@ __attribute__((always_inline)) INLINE static float gravity_get_mass( /** * @brief Returns the current co-moving softening of a particle * + * Note that in this basic gravity scheme, all particles have + * the same softening length. + * * @param gp The particle of interest * @param grav_props The global gravity properties. */ __attribute__((always_inline)) INLINE static float gravity_get_softening( const struct gpart* gp, const struct gravity_props* restrict grav_props) { - return grav_props->epsilon_cur; + return grav_props->epsilon_DM_cur; } /** diff --git a/src/gravity/Potential/gravity_iact.h b/src/gravity/Potential/gravity_iact.h index f2094f6ecd5b31b94ebfe7a64f42fbd289a0c81c..8d36ab666650a5ae6239657b247a0f2dad1f7756 100644 --- a/src/gravity/Potential/gravity_iact.h +++ b/src/gravity/Potential/gravity_iact.h @@ -146,13 +146,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated( */ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( const float r_x, const float r_y, const float r_z, const float r2, - const float h, const float h_inv, const struct multipole *m, float *f_x, - float *f_y, float *f_z, float *pot) { + const float h, const float h_inv, const struct multipole *m, + float *restrict f_x, float *restrict f_y, float *restrict f_z, + float *restrict pot) { -/* In the case where the order is < 3, then there is only a monopole term left. +/* In the case where the order is < 2, then there is only a monopole term left. * We can default to the normal P-P interaction with the mass of the multipole * and its CoM as the "particle" property */ -#if SELF_GRAVITY_MULTIPOLE_ORDER < 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER < 2 float f_ij, pot_ij; runner_iact_grav_pp_full(r2, h * h, h_inv, h_inv * h_inv * h_inv, m->M_000, @@ -169,8 +170,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( /* Compute the derivatives of the potential */ struct potential_derivatives_M2P d; - potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, 0, 0.f, - &d); + potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, /*periodic=*/0, + /*r_s_inv=*/0.f, &d); /* 0th order contributions */ *f_x = m->M_000 * d.D_100; @@ -190,8 +191,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( /* *pot = m->M_001 * d.D_001 + m->M_010 * d.D_010 + m->M_100 * d.D_100 ; */ #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 /* 2nd order contributions */ *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 + @@ -204,8 +204,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( m->M_101 * d.D_101 + m->M_110 * d.D_110 + m->M_200 * d.D_200; #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 /* 3rd order contributions */ *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 + @@ -226,7 +225,31 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( m->M_300 * d.D_300; #endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + /* 4th order contributions */ + *f_x += m->M_004 * d.D_104 + m->M_013 * d.D_113 + m->M_022 * d.D_122 + + m->M_031 * d.D_131 + m->M_040 * d.D_140 + m->M_103 * d.D_203 + + m->M_112 * d.D_212 + m->M_121 * d.D_221 + m->M_130 * d.D_230 + + m->M_202 * d.D_302 + m->M_211 * d.D_311 + m->M_220 * d.D_320 + + m->M_301 * d.D_401 + m->M_310 * d.D_410 + m->M_400 * d.D_500; + *f_y += m->M_004 * d.D_014 + m->M_013 * d.D_023 + m->M_022 * d.D_032 + + m->M_031 * d.D_041 + m->M_040 * d.D_050 + m->M_103 * d.D_113 + + m->M_112 * d.D_122 + m->M_121 * d.D_131 + m->M_130 * d.D_140 + + m->M_202 * d.D_212 + m->M_211 * d.D_221 + m->M_220 * d.D_230 + + m->M_301 * d.D_311 + m->M_310 * d.D_320 + m->M_400 * d.D_410; + *f_z += m->M_004 * d.D_005 + m->M_013 * d.D_014 + m->M_022 * d.D_023 + + m->M_031 * d.D_032 + m->M_040 * d.D_041 + m->M_103 * d.D_104 + + m->M_112 * d.D_113 + m->M_121 * d.D_122 + m->M_130 * d.D_131 + + m->M_202 * d.D_203 + m->M_211 * d.D_212 + m->M_220 * d.D_221 + + m->M_301 * d.D_302 + m->M_310 * d.D_311 + m->M_400 * d.D_401; + *pot += m->M_004 * d.D_004 + m->M_013 * d.D_013 + m->M_022 * d.D_022 + + m->M_031 * d.D_031 + m->M_040 * d.D_040 + m->M_103 * d.D_103 + + m->M_112 * d.D_112 + m->M_121 * d.D_121 + m->M_130 * d.D_130 + + m->M_202 * d.D_202 + m->M_211 * d.D_211 + m->M_220 * d.D_220 + + m->M_301 * d.D_301 + m->M_310 * d.D_310 + m->M_400 * d.D_400; + +#endif /* Take care of the the sign convention */ *f_x *= -1.f; *f_y *= -1.f; @@ -259,12 +282,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full( __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( const float r_x, const float r_y, const float r_z, const float r2, const float h, const float h_inv, const float r_s_inv, - const struct multipole *m, float *f_x, float *f_y, float *f_z, float *pot) { + const struct multipole *m, float *restrict f_x, float *restrict f_y, + float *restrict f_z, float *restrict pot) { -/* In the case where the order is < 3, then there is only a monopole term left. +/* In the case where the order is < 2, then there is only a monopole term left. * We can default to the normal P-P interaction with the mass of the multipole * and its CoM as the "particle" property */ -#if SELF_GRAVITY_MULTIPOLE_ORDER < 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER < 2 float f_ij, pot_ij; runner_iact_grav_pp_truncated(r2, h * h, h_inv, h_inv * h_inv * h_inv, @@ -281,7 +305,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( /* Compute the derivatives of the potential */ struct potential_derivatives_M2P d; - potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, 1, + potential_derivatives_compute_M2P(r_x, r_y, r_z, r2, r_inv, h, /*periodic=*/1, r_s_inv, &d); /* 0th order contributions */ @@ -302,8 +326,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( /* *pot = m->M_001 * d.D_001 + m->M_010 * d.D_010 + m->M_100 * d.D_100 ; */ #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 /* 2nd order contributions */ *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 + @@ -316,8 +339,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( m->M_101 * d.D_101 + m->M_110 * d.D_110 + m->M_200 * d.D_200; #endif - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 /* 3rd order contributions */ *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 + @@ -338,7 +360,31 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated( m->M_300 * d.D_300; #endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + /* 4th order contributions */ + *f_x += m->M_004 * d.D_104 + m->M_013 * d.D_113 + m->M_022 * d.D_122 + + m->M_031 * d.D_131 + m->M_040 * d.D_140 + m->M_103 * d.D_203 + + m->M_112 * d.D_212 + m->M_121 * d.D_221 + m->M_130 * d.D_230 + + m->M_202 * d.D_302 + m->M_211 * d.D_311 + m->M_220 * d.D_320 + + m->M_301 * d.D_401 + m->M_310 * d.D_410 + m->M_400 * d.D_500; + *f_y += m->M_004 * d.D_014 + m->M_013 * d.D_023 + m->M_022 * d.D_032 + + m->M_031 * d.D_041 + m->M_040 * d.D_050 + m->M_103 * d.D_113 + + m->M_112 * d.D_122 + m->M_121 * d.D_131 + m->M_130 * d.D_140 + + m->M_202 * d.D_212 + m->M_211 * d.D_221 + m->M_220 * d.D_230 + + m->M_301 * d.D_311 + m->M_310 * d.D_320 + m->M_400 * d.D_410; + *f_z += m->M_004 * d.D_005 + m->M_013 * d.D_014 + m->M_022 * d.D_023 + + m->M_031 * d.D_032 + m->M_040 * d.D_041 + m->M_103 * d.D_104 + + m->M_112 * d.D_113 + m->M_121 * d.D_122 + m->M_130 * d.D_131 + + m->M_202 * d.D_203 + m->M_211 * d.D_212 + m->M_220 * d.D_221 + + m->M_301 * d.D_302 + m->M_310 * d.D_311 + m->M_400 * d.D_401; + *pot += m->M_004 * d.D_004 + m->M_013 * d.D_013 + m->M_022 * d.D_022 + + m->M_031 * d.D_031 + m->M_040 * d.D_040 + m->M_103 * d.D_103 + + m->M_112 * d.D_112 + m->M_121 * d.D_121 + m->M_130 * d.D_130 + + m->M_202 * d.D_202 + m->M_211 * d.D_211 + m->M_220 * d.D_220 + + m->M_301 * d.D_301 + m->M_310 * d.D_310 + m->M_400 * d.D_400; + +#endif /* Take care of the the sign convention */ *f_x *= -1.f; *f_y *= -1.f; diff --git a/src/gravity/Potential/gravity_io.h b/src/gravity/Potential/gravity_io.h index 6aa4cbb4786af99ac372564ed67f4ce77c08f25c..d0d31139fb89fe4015f968ab43bdaef72ba796c5 100644 --- a/src/gravity/Potential/gravity_io.h +++ b/src/gravity/Potential/gravity_io.h @@ -108,15 +108,24 @@ INLINE static void darkmatter_write_particles(const struct gpart* gparts, /* List what we want to write */ list[0] = io_make_output_field_convert_gpart( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, gparts, convert_gpart_pos); + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, gparts, + convert_gpart_pos, "Co-moving position of the particles"); + list[1] = io_make_output_field_convert_gpart( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, gparts, convert_gpart_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, gparts, mass); - list[3] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, gparts, id_or_neg_offset); - list[4] = io_make_output_field("Potential", FLOAT, 1, UNIT_CONV_POTENTIAL, - gparts, potential); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, gparts, convert_gpart_vel, + "Peculiar velocities of the stars. This is a * dx/dt where x is the " + "co-moving position of the particles."); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + gparts, mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, gparts, + id_or_neg_offset, "Unique ID of the particles"); + + list[4] = io_make_output_field( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, gparts, potential, + "Co-moving gravitational potential at position of the particles"); } #endif /* SWIFT_POTENTIAL_GRAVITY_IO_H */ diff --git a/src/gravity/Potential/gravity_part.h b/src/gravity/Potential/gravity_part.h index 7b47a6198333edf7e6acbd0daf37c6b9c1eb6ce1..97126731f0dda7484600b05c0fd04faf72165d63 100644 --- a/src/gravity/Potential/gravity_part.h +++ b/src/gravity/Potential/gravity_part.h @@ -19,7 +19,11 @@ #ifndef SWIFT_POTENTIAL_GRAVITY_PART_H #define SWIFT_POTENTIAL_GRAVITY_PART_H -/* Gravity particle. */ +#include "fof_struct.h" + +/** + * @brief Gravity particle. + */ struct gpart { /*! Particle ID. If negative, it is the negative offset of the #part with @@ -41,15 +45,15 @@ struct gpart { /*! Gravitational potential */ float potential; + /*! Particle FoF properties (group ID, group size, ...) */ + struct fof_gpart_data fof_data; + /*! Time-step length */ timebin_t time_bin; /*! Type of the #gpart (DM, gas, star, ...) */ enum part_type type; - /* Particle group ID and size in the FOF. */ - size_t group_id, group_size; - #ifdef SWIFT_DEBUG_CHECKS /* Numer of gparts this gpart interacted with */ diff --git a/src/gravity_cache.h b/src/gravity_cache.h index 6d073db60171a9d30d6f397213849e4c3d5314a1..912961d2e06dc748039f54c72bd9b33fd2547df2 100644 --- a/src/gravity_cache.h +++ b/src/gravity_cache.h @@ -255,7 +255,8 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate( const float r2 = dx * dx + dy * dy + dz * dz; /* Check whether we can use the multipole instead of P-P */ - use_mpole[i] = allow_mpole && gravity_M2P_accept(r_max2, theta_crit2, r2); + use_mpole[i] = + allow_mpole && gravity_M2P_accept(r_max2, theta_crit2, r2, epsilon[i]); } #ifdef SWIFT_DEBUG_CHECKS @@ -436,7 +437,7 @@ gravity_cache_populate_all_mpole(const timebin_t max_active_bin, } const float r2 = dx * dx + dy * dy + dz * dz; - if (!gravity_M2P_accept(r_max2, theta_crit2, r2)) + if (!gravity_M2P_accept(r_max2, theta_crit2, r2, epsilon[i])) error("Using m-pole where the test fails"); #endif } diff --git a/src/gravity_derivatives.h b/src/gravity_derivatives.h index 3dcffe1cc04c5e10d3b2353b7e21c532747c1475..674779b54b8594fe80ed3595972b3f9950fb0f9d 100644 --- a/src/gravity_derivatives.h +++ b/src/gravity_derivatives.h @@ -102,9 +102,13 @@ struct potential_derivatives_M2P { /* 1st order terms */ float D_100, D_010, D_001; +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 + /* 2nd order terms */ float D_200, D_020, D_002; float D_110, D_101, D_011; +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 /* 3rd order terms */ float D_300, D_030, D_003; @@ -112,8 +116,8 @@ struct potential_derivatives_M2P { float D_120, D_021; float D_102, D_012; float D_111; - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 /* 4th order terms */ float D_400, D_040, D_004; @@ -123,6 +127,17 @@ struct potential_derivatives_M2P { float D_220, D_202, D_022; float D_211, D_121, D_112; #endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + + /* 5th order terms */ + float D_005, D_014, D_023; + float D_032, D_041, D_050; + float D_104, D_113, D_122; + float D_131, D_140, D_203; + float D_212, D_221, D_230; + float D_302, D_311, D_320; + float D_401, D_410, D_500; +#endif }; /** @@ -194,7 +209,6 @@ potential_derivatives_flip_signs(struct potential_derivatives_M2L *pot) { * @param r2 Square norm of distance vector * @param r_inv Inverse norm of distance vector * @param eps Softening length. - * @param eps_inv Inverse of softening length. * @param periodic Is the calculation periodic ? * @param r_s_inv Inverse of the long-range gravity mesh smoothing length. * @param pot (return) The structure containing all the derivatives. @@ -203,29 +217,33 @@ __attribute__((always_inline)) INLINE static void potential_derivatives_compute_M2L(const float r_x, const float r_y, const float r_z, const float r2, const float r_inv, const float eps, - const float eps_inv, const int periodic, - const float r_s_inv, + const int periodic, const float r_s_inv, struct potential_derivatives_M2L *pot) { - float Dt_1; +#ifdef SWIFT_DEBUG_CHECKS + if (r2 < 0.99f * eps * eps) + error("Computing M2L derivatives below softening length"); +#endif + + float Dt_1 = 0.f; #if SELF_GRAVITY_MULTIPOLE_ORDER > 0 - float Dt_3; + float Dt_3 = 0.f; #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 1 - float Dt_5; + float Dt_5 = 0.f; #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 2 - float Dt_7; + float Dt_7 = 0.f; #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 3 - float Dt_9; + float Dt_9 = 0.f; #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 4 - float Dt_11; + float Dt_11 = 0.f; #endif - /* Un-softened un-truncated case (Newtonian potential) */ - if (!periodic && r2 > eps * eps) { + /* Un-truncated case (Newtonian potential) */ + if (!periodic) { Dt_1 = r_inv; #if SELF_GRAVITY_MULTIPOLE_ORDER > 0 @@ -248,8 +266,8 @@ potential_derivatives_compute_M2L(const float r_x, const float r_y, #error "Missing implementation for order >5" #endif - /* Un-softened truncated case */ - } else if (periodic && r2 > eps * eps) { + /* Truncated case */ + } else { /* Get the derivatives of the truncated potential */ const float r = r2 * r_inv; @@ -291,38 +309,6 @@ potential_derivatives_compute_M2L(const float r_x, const float r_y, #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 5 #error "Missing implementation for order >5" -#endif - - /* Softened case */ - } else { - const float r = r2 * r_inv; - const float u = r * eps_inv; - const float u_inv = r_inv * eps; - - Dt_1 = eps_inv * D_soft_1(u, u_inv); -#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 - const float eps_inv2 = eps_inv * eps_inv; - const float eps_inv3 = eps_inv * eps_inv2; - Dt_3 = -eps_inv3 * D_soft_3(u, u_inv); -#endif -#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 - const float eps_inv5 = eps_inv3 * eps_inv2; - Dt_5 = eps_inv5 * D_soft_5(u, u_inv); -#endif -#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 - const float eps_inv7 = eps_inv5 * eps_inv2; - Dt_7 = -eps_inv7 * D_soft_7(u, u_inv); -#endif -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 - const float eps_inv9 = eps_inv7 * eps_inv2; - Dt_9 = eps_inv9 * D_soft_9(u, u_inv); -#endif -#if SELF_GRAVITY_MULTIPOLE_ORDER > 4 - const float eps_inv11 = eps_inv9 * eps_inv2; - Dt_11 = -eps_inv11 * D_soft_11(u, u_inv); -#endif -#if SELF_GRAVITY_MULTIPOLE_ORDER > 5 -#error "Missing implementation for order >5" #endif } @@ -444,13 +430,15 @@ potential_derivatives_compute_M2L(const float r_x, const float r_y, * @brief Compute all the relevent derivatives of the softened and truncated * gravitational potential for the M2P kernel. * + * For M2P, we compute the derivatives to one order higher than + * SELF_GRAVITY_MULTIPOLE_ORDER, as these are needed for the accelerations. + * * @param r_x x-component of distance vector * @param r_y y-component of distance vector * @param r_z z-component of distance vector * @param r2 Square norm of distance vector * @param r_inv Inverse norm of distance vector * @param eps Softening length. - * @param eps_inv Inverse of softening length. * @param periodic Is the calculation using periodic BCs? * @param r_s_inv The inverse of the gravity mesh-smoothing scale. * @param pot (return) The structure containing all the derivatives. @@ -459,99 +447,114 @@ __attribute__((always_inline)) INLINE static void potential_derivatives_compute_M2P(const float r_x, const float r_y, const float r_z, const float r2, const float r_inv, const float eps, - const float eps_inv, const int periodic, - const float r_s_inv, + const int periodic, const float r_s_inv, struct potential_derivatives_M2P *pot) { +#ifdef SWIFT_DEBUG_CHECKS + if (r2 < 0.99f * eps * eps) + error("Computing M2P derivatives below softening length"); +#endif + float Dt_1; float Dt_3; +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 float Dt_5; +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 float Dt_7; -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 float Dt_9; #endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + float Dt_11; +#endif - /* Un-softened un-truncated case (Newtonian potential) */ - if (!periodic && r2 > eps * eps) { + /* Un-truncated case (Newtonian potential) */ + if (!periodic) { const float r_inv2 = r_inv * r_inv; - Dt_1 = r_inv; Dt_3 = -1.f * Dt_1 * r_inv2; /* -1 / r^3 */ +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 Dt_5 = -3.f * Dt_3 * r_inv2; /* 3 / r^5 */ +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 Dt_7 = -5.f * Dt_5 * r_inv2; /* -15 / r^7 */ +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 + Dt_9 = -7.f * Dt_7 * r_inv2; /* 105 / r^9 */ +#endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 3 - Dt_9 = -7.f * Dt_7 * r_inv2; /* -105 / r^9 */ + Dt_11 = -9.f * Dt_9 * r_inv2; /* -945 / r^11 */ #endif - /* Un-softened truncated case */ - } else if (periodic && r2 > eps * eps) { + /* Truncated case */ + } else { /* Get the derivatives of the truncated potential */ const float r = r2 * r_inv; - struct chi_derivatives d; - kernel_long_grav_derivatives(r, r_s_inv, &d); + struct chi_derivatives derivs; + kernel_long_grav_derivatives(r, r_s_inv, &derivs); const float r_inv2 = r_inv * r_inv; - Dt_1 = d.chi_0 * r_inv; - const float r_inv3 = r_inv2 * r_inv; - Dt_3 = (r * d.chi_1 - d.chi_0) * r_inv3; - + Dt_1 = derivs.chi_0 * r_inv; + Dt_3 = (r * derivs.chi_1 - derivs.chi_0) * r_inv3; +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 const float r_inv5 = r_inv2 * r_inv3; - Dt_5 = (r * r * d.chi_2 - 3.f * r * d.chi_1 + 3.f * d.chi_0) * r_inv5; - + Dt_5 = + (r * r * derivs.chi_2 - 3.f * r * derivs.chi_1 + 3.f * derivs.chi_0) * + r_inv5; +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 const float r_inv7 = r_inv2 * r_inv5; - Dt_7 = (r * r * r * d.chi_3 - 6.f * r * r * d.chi_2 + 15.f * r * d.chi_1 - - 15.f * d.chi_0) * + Dt_7 = (r * r * r * derivs.chi_3 - 6.f * r * r * derivs.chi_2 + + 15.f * r * derivs.chi_1 - 15.f * derivs.chi_0) * r_inv7; - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 const float r_inv9 = r_inv2 * r_inv7; - Dt_9 = (r * r * r * r * d.chi_4 - 10.f * r * r * r * d.chi_3 + - 45.f * r * r * d.chi_2 - 105.f * r * d.chi_1 + 105.f * d.chi_0) * + Dt_9 = (r * r * r * r * derivs.chi_4 - 10.f * r * r * r * derivs.chi_3 + + 45.f * r * r * derivs.chi_2 - 105.f * r * derivs.chi_1 + + 105.f * derivs.chi_0) * r_inv9; #endif - - /* Softened case */ - } else { - - const float r = r2 * r_inv; - const float u = r * eps_inv; - const float u_inv = r_inv * eps; - const float eps_inv2 = eps_inv * eps_inv; - - Dt_1 = eps_inv * D_soft_1(u, u_inv); - - const float eps_inv3 = eps_inv * eps_inv2; - Dt_3 = -eps_inv3 * D_soft_3(u, u_inv); - - const float eps_inv5 = eps_inv3 * eps_inv2; - Dt_5 = eps_inv5 * D_soft_5(u, u_inv); - - const float eps_inv7 = eps_inv5 * eps_inv2; - Dt_7 = -eps_inv7 * D_soft_7(u, u_inv); - #if SELF_GRAVITY_MULTIPOLE_ORDER > 3 - const float eps_inv9 = eps_inv7 * eps_inv2; - Dt_9 = eps_inv9 * D_soft_9(u, u_inv); + const float r_inv11 = r_inv2 * r_inv9; + Dt_11 = (r * r * r * r * r * derivs.chi_5 - + 15.f * r * r * r * r * derivs.chi_4 + + 105.f * r * r * r * derivs.chi_3 - 420.f * r * r * derivs.chi_2 + + 945.f * r * derivs.chi_1 - 945.f * derivs.chi_0) * + r_inv11; #endif } - /* Compute some powers of r_x, r_y and r_z */ +/* Alright, let's get the full terms */ + +/* Compute some powers of r_x, r_y and r_z */ +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 const float r_x2 = r_x * r_x; const float r_y2 = r_y * r_y; const float r_z2 = r_z * r_z; +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 const float r_x3 = r_x2 * r_x; const float r_y3 = r_y2 * r_y; const float r_z3 = r_z2 * r_z; -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 const float r_x4 = r_x3 * r_x; const float r_y4 = r_y3 * r_y; const float r_z4 = r_z3 * r_z; #endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + const float r_x5 = r_x4 * r_x; + const float r_y5 = r_y4 * r_y; + const float r_z5 = r_z4 * r_z; +#endif - /* 0th order derivative */ + /* Get the 0th order term */ pot->D_000 = Dt_1; /* 1st order derivatives */ @@ -559,6 +562,7 @@ potential_derivatives_compute_M2P(const float r_x, const float r_y, pot->D_010 = r_y * Dt_3; pot->D_001 = r_z * Dt_3; +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 /* 2nd order derivatives */ pot->D_200 = r_x2 * Dt_5 + Dt_3; pot->D_020 = r_y2 * Dt_5 + Dt_3; @@ -566,7 +570,8 @@ potential_derivatives_compute_M2P(const float r_x, const float r_y, pot->D_110 = r_x * r_y * Dt_5; pot->D_101 = r_x * r_z * Dt_5; pot->D_011 = r_y * r_z * Dt_5; - +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 /* 3rd order derivatives */ pot->D_300 = r_x3 * Dt_7 + 3.f * r_x * Dt_5; pot->D_030 = r_y3 * Dt_7 + 3.f * r_y * Dt_5; @@ -578,8 +583,8 @@ potential_derivatives_compute_M2P(const float r_x, const float r_y, pot->D_102 = r_z2 * r_x * Dt_7 + r_x * Dt_5; pot->D_012 = r_z2 * r_y * Dt_7 + r_y * Dt_5; pot->D_111 = r_x * r_y * r_z * Dt_7; - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 /* 4th order derivatives */ pot->D_400 = r_x4 * Dt_9 + 6.f * r_x2 * Dt_7 + 3.f * Dt_5; pot->D_040 = r_y4 * Dt_9 + 6.f * r_y2 * Dt_7 + 3.f * Dt_5; @@ -597,6 +602,39 @@ potential_derivatives_compute_M2P(const float r_x, const float r_y, pot->D_121 = r_y2 * r_x * r_z * Dt_9 + r_x * r_z * Dt_7; pot->D_112 = r_z2 * r_x * r_y * Dt_9 + r_x * r_y * Dt_7; #endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + /* 5th order derivatives */ + pot->D_500 = r_x5 * Dt_11 + 10.f * r_x3 * Dt_9 + 15.f * r_x * Dt_7; + pot->D_050 = r_y5 * Dt_11 + 10.f * r_y3 * Dt_9 + 15.f * r_y * Dt_7; + pot->D_005 = r_z5 * Dt_11 + 10.f * r_z3 * Dt_9 + 15.f * r_z * Dt_7; + pot->D_410 = r_x4 * r_y * Dt_11 + 6.f * r_x2 * r_y * Dt_9 + 3.f * r_y * Dt_7; + pot->D_401 = r_x4 * r_z * Dt_11 + 6.f * r_x2 * r_z * Dt_9 + 3.f * r_z * Dt_7; + pot->D_140 = r_y4 * r_x * Dt_11 + 6.f * r_y2 * r_x * Dt_9 + 3.f * r_x * Dt_7; + pot->D_041 = r_y4 * r_z * Dt_11 + 6.f * r_y2 * r_z * Dt_9 + 3.f * r_z * Dt_7; + pot->D_104 = r_z4 * r_x * Dt_11 + 6.f * r_z2 * r_x * Dt_9 + 3.f * r_x * Dt_7; + pot->D_014 = r_z4 * r_y * Dt_11 + 6.f * r_z2 * r_y * Dt_9 + 3.f * r_y * Dt_7; + pot->D_320 = r_x3 * r_y2 * Dt_11 + r_x3 * Dt_9 + 3.f * r_x * r_y2 * Dt_9 + + 3.f * r_x * Dt_7; + pot->D_302 = r_x3 * r_z2 * Dt_11 + r_x3 * Dt_9 + 3.f * r_x * r_z2 * Dt_9 + + 3.f * r_x * Dt_7; + pot->D_230 = r_y3 * r_x2 * Dt_11 + r_y3 * Dt_9 + 3.f * r_y * r_x2 * Dt_9 + + 3.f * r_y * Dt_7; + pot->D_032 = r_y3 * r_z2 * Dt_11 + r_y3 * Dt_9 + 3.f * r_y * r_z2 * Dt_9 + + 3.f * r_y * Dt_7; + pot->D_203 = r_z3 * r_x2 * Dt_11 + r_z3 * Dt_9 + 3.f * r_z * r_x2 * Dt_9 + + 3.f * r_z * Dt_7; + pot->D_023 = r_z3 * r_y2 * Dt_11 + r_z3 * Dt_9 + 3.f * r_z * r_y2 * Dt_9 + + 3.f * r_z * Dt_7; + pot->D_311 = r_x3 * r_y * r_z * Dt_11 + 3.f * r_x * r_y * r_z * Dt_9; + pot->D_131 = r_y3 * r_x * r_z * Dt_11 + 3.f * r_x * r_y * r_z * Dt_9; + pot->D_113 = r_z3 * r_x * r_y * Dt_11 + 3.f * r_x * r_y * r_z * Dt_9; + pot->D_122 = r_x * r_y2 * r_z2 * Dt_11 + r_x * r_y2 * Dt_9 + + r_x * r_z2 * Dt_9 + r_x * Dt_7; + pot->D_212 = r_y * r_x2 * r_z2 * Dt_11 + r_y * r_x2 * Dt_9 + + r_y * r_z2 * Dt_9 + r_y * Dt_7; + pot->D_221 = r_z * r_x2 * r_y2 * Dt_11 + r_z * r_x2 * Dt_9 + + r_z * r_y2 * Dt_9 + r_z * Dt_7; +#endif } #endif /* SWIFT_GRAVITY_DERIVATIVE_H */ diff --git a/src/gravity_iact.h b/src/gravity_iact.h index 2aaf7219d0851058cba92d4686ed989572dbcaa4..4c8d707fe33d0955809f8cb394d75c3a2f580fb7 100644 --- a/src/gravity_iact.h +++ b/src/gravity_iact.h @@ -32,6 +32,8 @@ #include "./gravity/Default/gravity_iact.h" #elif defined(POTENTIAL_GRAVITY) #include "./gravity/Potential/gravity_iact.h" +#elif defined(MULTI_SOFTENING_GRAVITY) +#include "./gravity/MultiSoftening/gravity_iact.h" #else #error "Invalid choice of gravity variant" #endif diff --git a/src/gravity_io.h b/src/gravity_io.h index 752ee906081d706865e62bae7c8f505e9ca64347..44f392ee06d43228f7aa46faa5c7d9015fde790b 100644 --- a/src/gravity_io.h +++ b/src/gravity_io.h @@ -30,6 +30,8 @@ #include "./gravity/Default/gravity_io.h" #elif defined(POTENTIAL_GRAVITY) #include "./gravity/Potential/gravity_io.h" +#elif defined(MULTI_SOFTENING_GRAVITY) +#include "./gravity/MultiSoftening/gravity_io.h" #else #error "Invalid choice of gravity variant" #endif diff --git a/src/gravity_properties.c b/src/gravity_properties.c index 60c0bd05ba8cb2234284154b4383bec07f30756d..d1654a1e7830d31c20731f130389a04d69b148b3 100644 --- a/src/gravity_properties.c +++ b/src/gravity_properties.c @@ -39,8 +39,10 @@ #define gravity_props_default_rebuild_frequency 0.01f void gravity_props_init(struct gravity_props *p, struct swift_params *params, - const struct cosmology *cosmo, int with_cosmology, - int periodic) { + const struct phys_const *phys_const, + const struct cosmology *cosmo, const int with_cosmology, + const int has_baryons, const int has_DM, + const int is_zoom_simulation, const int periodic) { /* Tree updates */ p->rebuild_frequency = @@ -88,16 +90,63 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, /* Softening parameters */ if (with_cosmology) { - p->epsilon_comoving = - parser_get_param_double(params, "Gravity:comoving_softening"); - p->epsilon_max_physical = - parser_get_param_double(params, "Gravity:max_physical_softening"); + + if (has_DM) { + /* Maximal physical softening taken straight from the parameter file */ + p->epsilon_DM_max_physical = + parser_get_param_double(params, "Gravity:max_physical_DM_softening"); + + /* Co-moving softenings taken straight from the parameter file */ + p->epsilon_DM_comoving = + parser_get_param_double(params, "Gravity:comoving_DM_softening"); + } + + if (has_baryons) { + /* Maximal physical softening taken straight from the parameter file */ + p->epsilon_baryon_max_physical = parser_get_param_double( + params, "Gravity:max_physical_baryon_softening"); + + /* Co-moving softenings taken straight from the parameter file */ + p->epsilon_baryon_comoving = + parser_get_param_double(params, "Gravity:comoving_baryon_softening"); + } + + if (is_zoom_simulation) { + + /* Compute the comoving softening length for background particles as + * a fraction of the mean inter-particle density of the background DM + * particles Since they have variable masses the mass factor will be + * multiplied in later on. Note that we already multiply in the conversion + * from Plummer -> real softening length */ + const double ratio_background = + parser_get_param_double(params, "Gravity:softening_ratio_background"); + + const double mean_matter_density = + cosmo->Omega_m * cosmo->critical_density_0; + + p->epsilon_background_fac = kernel_gravity_softening_plummer_equivalent * + ratio_background * + cbrt(1. / mean_matter_density); + } + } else { - p->epsilon_max_physical = - parser_get_param_double(params, "Gravity:max_physical_softening"); - p->epsilon_comoving = p->epsilon_max_physical; + + if (has_DM) { + p->epsilon_DM_max_physical = + parser_get_param_double(params, "Gravity:max_physical_DM_softening"); + } + if (has_baryons) { + p->epsilon_baryon_max_physical = parser_get_param_double( + params, "Gravity:max_physical_baryon_softening"); + } + + p->epsilon_DM_comoving = p->epsilon_DM_max_physical; + p->epsilon_baryon_comoving = p->epsilon_baryon_max_physical; } + /* Copy over the gravitational constant */ + p->G_Newton = phys_const->const_newton_G; + /* Set the softening to the current time */ gravity_props_update(p, cosmo); } @@ -105,23 +154,26 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, void gravity_props_update(struct gravity_props *p, const struct cosmology *cosmo) { - /* Current softening lengths */ - double softening; - if (p->epsilon_comoving * cosmo->a > p->epsilon_max_physical) - softening = p->epsilon_max_physical / cosmo->a; + /* Current softening length for the high-res. DM particles. */ + double DM_softening, baryon_softening; + if (p->epsilon_DM_comoving * cosmo->a > p->epsilon_DM_max_physical) + DM_softening = p->epsilon_DM_max_physical / cosmo->a; else - softening = p->epsilon_comoving; + DM_softening = p->epsilon_DM_comoving; + + /* Current softening length for the high-res. baryon particles. */ + if (p->epsilon_baryon_comoving * cosmo->a > p->epsilon_baryon_max_physical) + baryon_softening = p->epsilon_baryon_max_physical / cosmo->a; + else + baryon_softening = p->epsilon_baryon_comoving; /* Plummer equivalent -> internal */ - softening *= kernel_gravity_softening_plummer_equivalent; + DM_softening *= kernel_gravity_softening_plummer_equivalent; + baryon_softening *= kernel_gravity_softening_plummer_equivalent; /* Store things */ - p->epsilon_cur = softening; - - /* Other factors */ - p->epsilon_cur2 = softening * softening; - p->epsilon_cur_inv = 1. / softening; - p->epsilon_cur_inv3 = 1. / (softening * softening * softening); + p->epsilon_DM_cur = DM_softening; + p->epsilon_baryon_cur = baryon_softening; } void gravity_props_print(const struct gravity_props *p) { @@ -139,16 +191,31 @@ void gravity_props_print(const struct gravity_props *p) { kernel_gravity_softening_name); message( - "Self-gravity comoving softening: epsilon=%.4f (Plummer equivalent: " - "%.4f)", - p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent, - p->epsilon_comoving); + "Self-gravity DM comoving softening: epsilon=%.6f (Plummer equivalent: " + "%.6f)", + p->epsilon_DM_comoving * kernel_gravity_softening_plummer_equivalent, + p->epsilon_DM_comoving); message( - "Self-gravity maximal physical softening: epsilon=%.4f (Plummer " - "equivalent: %.4f)", - p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent, - p->epsilon_max_physical); + "Self-gravity DM maximal physical softening: epsilon=%.6f (Plummer " + "equivalent: %.6f)", + p->epsilon_DM_max_physical * kernel_gravity_softening_plummer_equivalent, + p->epsilon_DM_max_physical); + + message( + "Self-gravity baryon comoving softening: epsilon=%.6f (Plummer " + "equivalent: " + "%.6f)", + p->epsilon_baryon_comoving * kernel_gravity_softening_plummer_equivalent, + p->epsilon_baryon_comoving); + + message( + "Self-gravity baryon maximal physical softening: epsilon=%.6f " + "(Plummer " + "equivalent: %.6f)", + p->epsilon_baryon_max_physical * + kernel_gravity_softening_plummer_equivalent, + p->epsilon_baryon_max_physical); message("Self-gravity mesh side-length: N=%d", p->mesh_size); message("Self-gravity mesh smoothing-scale: a_smooth=%f", p->a_smooth); @@ -170,20 +237,40 @@ void gravity_props_print_snapshot(hid_t h_grpgrav, io_write_attribute_f(h_grpgrav, "Time integration eta", p->eta); io_write_attribute_s(h_grpgrav, "Softening style", kernel_gravity_softening_name); + + io_write_attribute_f( + h_grpgrav, "Comoving DM softening length [internal units]", + p->epsilon_DM_comoving * kernel_gravity_softening_plummer_equivalent); + io_write_attribute_f( + h_grpgrav, + "Comoving DM softening length (Plummer equivalent) [internal units]", + p->epsilon_DM_comoving); + io_write_attribute_f( - h_grpgrav, "Comoving softening length [internal units]", - p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent); + h_grpgrav, "Maximal physical DM softening length [internal units]", + p->epsilon_DM_max_physical * kernel_gravity_softening_plummer_equivalent); + io_write_attribute_f(h_grpgrav, + "Maximal physical DM softening length (Plummer " + "equivalent) [internal units]", + p->epsilon_DM_max_physical); + + io_write_attribute_f( + h_grpgrav, "Comoving baryon softening length [internal units]", + p->epsilon_baryon_comoving * kernel_gravity_softening_plummer_equivalent); io_write_attribute_f( h_grpgrav, - "Comoving Softening length (Plummer equivalent) [internal units]", - p->epsilon_comoving); + "Comoving baryon softening length (Plummer equivalent) [internal units]", + p->epsilon_baryon_comoving); + io_write_attribute_f( - h_grpgrav, "Maximal physical softening length [internal units]", - p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent); + h_grpgrav, "Maximal physical baryon softening length [internal units]", + p->epsilon_baryon_max_physical * + kernel_gravity_softening_plummer_equivalent); io_write_attribute_f(h_grpgrav, - "Maximal physical softening length (Plummer equivalent) " - " [internal units]", - p->epsilon_max_physical); + "Maximal physical baryon softening length (Plummer " + "equivalent) [internal units]", + p->epsilon_baryon_max_physical); + io_write_attribute_f(h_grpgrav, "Opening angle", p->theta_crit); io_write_attribute_s(h_grpgrav, "Scheme", GRAVITY_IMPLEMENTATION); io_write_attribute_i(h_grpgrav, "MM order", SELF_GRAVITY_MULTIPOLE_ORDER); diff --git a/src/gravity_properties.h b/src/gravity_properties.h index 09c8ef8ffa1d6cc4effa4895614106217a2861a9..d0a68ceb14d13f618dc628fb7e0a0dbf37d67cfb 100644 --- a/src/gravity_properties.h +++ b/src/gravity_properties.h @@ -27,10 +27,12 @@ #endif /* Local includes. */ +#include "kernel_gravity.h" #include "restart.h" /* Forward declarations */ struct cosmology; +struct phys_const; struct swift_params; /** @@ -38,25 +40,23 @@ struct swift_params; */ struct gravity_props { - /*! Frequency of tree-rebuild in units of #gpart updates. */ - float rebuild_frequency; + /* -------------- Softening for the regular particles ---------------- */ - /*! Periodic long-range mesh side-length */ - int mesh_size; + /*! Co-moving softening length for high-res. DM particles at the current + * redshift. */ + float epsilon_DM_cur; - /*! Mesh smoothing scale in units of top-level cell size */ - float a_smooth; + /*! Co-moving softening length for high-res. baryon particles at the current + * redshift. */ + float epsilon_baryon_cur; - /*! Distance below which the truncated mesh force is Newtonian in units of - * a_smooth */ - float r_cut_min_ratio; + /* -------------- Softening for the background DM -------------------- */ - /*! Distance above which the truncated mesh force is negligible in units of - * a_smooth */ - float r_cut_max_ratio; + /*! Conversion factor from cbrt of particle mass to softening assuming + * a constant fraction of the mean inter-particle separation at that mass. */ + float epsilon_background_fac; - /*! Time integration dimensionless multiplier */ - float eta; + /* -------------- Properties of the FFM gravity ---------------------- */ /*! Tree opening angle (Multipole acceptance criterion) */ double theta_crit; @@ -67,29 +67,61 @@ struct gravity_props { /*! Inverse of opening angle */ double theta_crit_inv; - /*! Comoving softening */ - double epsilon_comoving; + /* ------------- Properties of the softened gravity ------------------ */ + + /*! Co-moving softening length for for high-res. DM particles */ + float epsilon_DM_comoving; - /*! Maxium physical softening */ - double epsilon_max_physical; + /*! Maximal softening length in physical coordinates for the high-res. + * DM particles */ + float epsilon_DM_max_physical; - /*! Current softening length */ - float epsilon_cur; + /*! Co-moving softening length for for high-res. baryon particles */ + float epsilon_baryon_comoving; - /*! Square of current softening length */ - float epsilon_cur2; + /*! Maximal softening length in physical coordinates for the high-res. + * baryon particles */ + float epsilon_baryon_max_physical; - /*! Inverse of current softening length */ - float epsilon_cur_inv; + /*! Fraction of the mean inter particle separation corresponding to the + * co-moving softening length of the low-res. particles (DM + baryons) */ + float mean_inter_particle_fraction_high_res; + + /* ------------- Properties of the time integration ----------------- */ + + /*! Frequency of tree-rebuild in units of #gpart updates. */ + float rebuild_frequency; + + /*! Time integration dimensionless multiplier */ + float eta; + + /* ------------- Properties of the mesh-based gravity ---------------- */ + + /*! Periodic long-range mesh side-length */ + int mesh_size; + + /*! Mesh smoothing scale in units of top-level cell size */ + float a_smooth; + + /*! Distance below which the truncated mesh force is Newtonian in units of + * a_smooth */ + float r_cut_min_ratio; + + /*! Distance above which the truncated mesh force is negligible in units of + * a_smooth */ + float r_cut_max_ratio; - /*! Cube of the inverse of current oftening length */ - float epsilon_cur_inv3; + /*! Gravitational constant (in internal units, copied from the physical + * constants) */ + float G_Newton; }; void gravity_props_print(const struct gravity_props *p); void gravity_props_init(struct gravity_props *p, struct swift_params *params, - const struct cosmology *cosmo, int with_cosmology, - int periodic); + const struct phys_const *phys_const, + const struct cosmology *cosmo, const int with_cosmology, + const int has_baryons, const int has_DM, + const int is_zoom_simulation, const int periodic); void gravity_props_update(struct gravity_props *p, const struct cosmology *cosmo); diff --git a/src/hydro/AnarchyDU/hydro.h b/src/hydro/AnarchyDU/hydro.h index 53c66e316136b172f7610dd6f066c6e841421ced..ff8eac12510cce6f5e5a578fbf91479aa67b3f5c 100644 --- a/src/hydro/AnarchyDU/hydro.h +++ b/src/hydro/AnarchyDU/hydro.h @@ -398,17 +398,23 @@ hydro_set_drifted_physical_internal_energy(struct part *p, const struct cosmology *cosmo, const float u) { + /* There is no need to use the floor here as this function is called in the + * feedback, so the new value of the internal energy should be strictly + * higher than the old value. */ + p->u = u / cosmo->a_factor_internal_energy; /* Now recompute the extra quantities */ /* Compute the sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); - const float pressure = hydro_get_comoving_pressure(p); + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); /* Update variables. */ p->force.soundspeed = soundspeed; p->force.pressure = pressure; + + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -616,6 +622,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( struct part *restrict p) { p->viscosity.v_sig = 2.f * p->force.soundspeed; + p->force.alpha_visc_max_ngb = p->viscosity.alpha; } /** @@ -774,11 +781,23 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( new_diffusion_alpha += alpha_diff_dt * dt_alpha; /* Consistency checks to ensure min < alpha < max */ - new_diffusion_alpha = - min(new_diffusion_alpha, hydro_props->diffusion.alpha_max); new_diffusion_alpha = max(new_diffusion_alpha, hydro_props->diffusion.alpha_min); + /* Now we limit in viscous flows; remove diffusion there. If we + * don't do that, then we end up diffusing energy away in supernovae. + * This is an EAGLE-specific fix. We limit based on the maximal + * viscous alpha over our neighbours in an attempt to keep diffusion + * low near to supernovae sites. */ + + /* This also enforces alpha_diff < alpha_diff_max */ + + const float viscous_diffusion_limit = + hydro_props->diffusion.alpha_max * + (1.f - p->force.alpha_visc_max_ngb / hydro_props->viscosity.alpha_max); + + new_diffusion_alpha = min(new_diffusion_alpha, viscous_diffusion_limit); + p->diffusion.alpha = new_diffusion_alpha; } @@ -809,9 +828,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -827,6 +848,9 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( p->force.pressure = pressure; p->force.soundspeed = soundspeed; + + /* Update the signal velocity, if we need to. */ + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -888,10 +912,13 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( /* Compute the new sound speed */ const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); - const float soundspeed = hydro_get_comoving_soundspeed(p); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); p->force.pressure = pressure; p->force.soundspeed = soundspeed; + + /* Update signal velocity if we need to */ + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** diff --git a/src/hydro/AnarchyDU/hydro_iact.h b/src/hydro/AnarchyDU/hydro_iact.h index cba945ecae8cbf2971b3d0d810cd565cb8ecc8eb..09ecde01d44cfef1c0edbfe0d3f1aa909d553a95 100644 --- a/src/hydro/AnarchyDU/hydro_iact.h +++ b/src/hydro/AnarchyDU/hydro_iact.h @@ -227,6 +227,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float delta_u_factor = (pi->u - pj->u) * r_inv; pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; pj->diffusion.laplace_u -= pi->mass * delta_u_factor * wj_dx / pi->rho; + + /* Set the maximal alpha from the previous step over the neighbours + * (this is used to limit the diffusion in hydro_prepare_force) */ + const float alpha_i = pi->viscosity.alpha; + const float alpha_j = pj->viscosity.alpha; + pi->force.alpha_visc_max_ngb = max(pi->force.alpha_visc_max_ngb, alpha_j); + pj->force.alpha_visc_max_ngb = max(pj->force.alpha_visc_max_ngb, alpha_i); } /** @@ -289,6 +296,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float delta_u_factor = (pi->u - pj->u) * r_inv; pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; + + /* Set the maximal alpha from the previous step over the neighbours + * (this is used to limit the diffusion in hydro_prepare_force) */ + const float alpha_j = pj->viscosity.alpha; + pi->force.alpha_visc_max_ngb = max(pi->force.alpha_visc_max_ngb, alpha_j); } /** @@ -398,9 +410,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Diffusion term */ const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha); - const float v_diff = - alpha_diff * sqrtf(0.5f * fabsf(pressurei - pressurej) / rho_ij) + - fabsf(fac_mu * r_inv * dvdr_Hubble); + const float v_diff = alpha_diff * 0.5f * + (sqrtf(2.f * fabsf(pressurei - pressurej) / rho_ij) + + fabsf(fac_mu * r_inv * dvdr_Hubble)); /* wi_dx + wj_dx / 2 is F_ij */ const float diff_du_term = v_diff * (pi->u - pj->u) * (wi_dr / rhoi + wj_dr / rhoj); @@ -416,6 +428,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for h. */ pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; + + /* Update if we need to; this should be guaranteed by the gradient loop but + * due to some possible synchronisation problems this is here as a _quick + * fix_. Added: 14th August 2019. To be removed by 1st Jan 2020. (JB) */ + pi->viscosity.v_sig = max(pi->viscosity.v_sig, v_sig); + pj->viscosity.v_sig = max(pj->viscosity.v_sig, v_sig); } /** @@ -520,9 +538,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Diffusion term */ const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha); - const float v_diff = - alpha_diff * sqrtf(0.5f * fabsf(pressurei - pressurej) / rho_ij) + - fabsf(fac_mu * r_inv * dvdr_Hubble); + const float v_diff = alpha_diff * 0.5f * + (sqrtf(2.f * fabsf(pressurei - pressurej) / rho_ij) + + fabsf(fac_mu * r_inv * dvdr_Hubble)); /* wi_dx + wj_dx / 2 is F_ij */ const float diff_du_term = v_diff * (pi->u - pj->u) * (wi_dr / rhoi + wj_dr / rhoj); @@ -535,6 +553,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for h. */ pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + + /* Update if we need to; this should be guaranteed by the gradient loop but + * due to some possible synchronisation problems this is here as a _quick + * fix_. Added: 14th August 2019. To be removed by 1st Jan 2020. (JB) */ + pi->viscosity.v_sig = max(pi->viscosity.v_sig, v_sig); } /** diff --git a/src/hydro/AnarchyDU/hydro_io.h b/src/hydro/AnarchyDU/hydro_io.h index db9995c3fe8a5089415dc9152a44a655ec97f0f3..4044a0c1f1cdbf4263de03071b046cd4c0884d2e 100644 --- a/src/hydro/AnarchyDU/hydro_io.h +++ b/src/hydro/AnarchyDU/hydro_io.h @@ -161,38 +161,53 @@ INLINE static void hydro_write_particles(const struct part* parts, struct io_props* list, int* num_fields) { - *num_fields = 12; - + *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + list[7] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); - list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1, - UNIT_CONV_ENTROPY_PER_UNIT_MASS, - parts, xparts, convert_S); - list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); - list[10] = io_make_output_field_convert_part("Viscosity", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - xparts, convert_viscosity); - list[11] = io_make_output_field_convert_part("Diffusion", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - xparts, convert_diffusion); + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Co-moving entropies per unit mass of the particles"); + + list[8] = io_make_output_field_convert_part( + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + xparts, convert_P, "Co-moving pressures of the particles"); + + list[9] = io_make_output_field_convert_part( + "ViscosityParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_viscosity, "Visosity coefficient (alpha_visc) of the particles"); + + list[10] = io_make_output_field_convert_part( + "DiffusionParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_diffusion, "Diffusion coefficient (alpha_diff) of the particles"); } /** diff --git a/src/hydro/AnarchyDU/hydro_part.h b/src/hydro/AnarchyDU/hydro_part.h index c30f778b80d91d576052082c6e849fa9d1a4f38e..4b4cc187a96f5111389cde8c50f535a545e9e7f5 100644 --- a/src/hydro/AnarchyDU/hydro_part.h +++ b/src/hydro/AnarchyDU/hydro_part.h @@ -185,6 +185,9 @@ struct part { /*! Balsara switch */ float balsara; + /*! Maximal alpha (viscosity) over neighbours */ + float alpha_visc_max_ngb; + } force; }; diff --git a/src/hydro/AnarchyPU/hydro.h b/src/hydro/AnarchyPU/hydro.h index 9ba58828dd84df205539550c221746dfac37ed21..ca154374a07a99bcd94cee481d1effce33787d2f 100644 --- a/src/hydro/AnarchyPU/hydro.h +++ b/src/hydro/AnarchyPU/hydro.h @@ -219,9 +219,7 @@ hydro_get_comoving_soundspeed(const struct part *restrict p) { /* Compute the sound speed -- see theory section for justification */ /* IDEAL GAS ONLY -- P-U does not work with generic EoS. */ - const float square_rooted = sqrtf(hydro_gamma * p->pressure_bar / p->rho); - - return square_rooted; + return gas_soundspeed_from_pressure(p->rho, p->pressure_bar); } /** @@ -408,15 +406,29 @@ hydro_set_drifted_physical_internal_energy(struct part *p, const struct cosmology *cosmo, const float u) { + /* Store ratio of new internal energy to old internal energy, as we use this + * in the drifting of the pressure. */ + float internal_energy_ratio = 1.f / p->u; + + /* Update the internal energy */ p->u = u / cosmo->a_factor_internal_energy; + internal_energy_ratio *= p->u; + + /* Now we can use this to 'update' the value of the smoothed pressure. To + * truly update this variable, we would need another loop over neighbours + * using the new internal energies of everyone, but that's not feasible. */ + p->pressure_bar *= internal_energy_ratio; /* Now recompute the extra quantities */ /* Compute the sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, p->pressure_bar); /* Update variables. */ p->force.soundspeed = soundspeed; + + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -467,10 +479,7 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / (cosmo->a_factor_sound_speed * p->viscosity.v_sig); - const float dt_u_change = - (p->u_dt != 0.0f) ? fabsf(const_max_u_change * p->u / p->u_dt) : FLT_MAX; - - return fminf(dt_cfl, dt_u_change); + return dt_cfl; } /** @@ -828,9 +837,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -869,8 +880,18 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { + /* Store ratio of new internal energy to old internal energy, as we use this + * in the drifting of the pressure. */ + float internal_energy_ratio = 1.f / p->u; + /* Predict the internal energy */ p->u += p->u_dt * dt_therm; + internal_energy_ratio *= p->u; + + /* Now we can use this to 'update' the value of the smoothed pressure. To + * truly update this variable, we would need another loop over neighbours + * using the new internal energies of everyone, but that's not feasible. */ + p->pressure_bar *= internal_energy_ratio; /* Check against entropy floor */ const float floor_A = entropy_floor(p, cosmo, floor_props); @@ -906,9 +927,12 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( } /* Compute the new sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); - + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, p->pressure_bar); p->force.soundspeed = soundspeed; + + /* Update the signal velocity */ + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** diff --git a/src/hydro/AnarchyPU/hydro_io.h b/src/hydro/AnarchyPU/hydro_io.h index e1525cc99db8074a99c8d28a73918adb7b7b5319..7dfe61f198163c458ea8ecc22c1cbaa7eb4b99dd 100644 --- a/src/hydro/AnarchyPU/hydro_io.h +++ b/src/hydro/AnarchyPU/hydro_io.h @@ -165,35 +165,55 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 12; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[7] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, - parts, pressure_bar); - list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1, - UNIT_CONV_ENTROPY_PER_UNIT_MASS, - parts, xparts, convert_S); - list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); - list[10] = io_make_output_field_convert_part("Viscosity", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - xparts, convert_viscosity); - list[11] = io_make_output_field_convert_part("Diffusion", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - xparts, convert_diffusion); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + + list[7] = io_make_output_field( + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + pressure_bar, "Co-moving smoothed pressures of the particles"); + + list[8] = io_make_output_field_convert_part( + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Co-moving entropies per unit mass of the particles"); + + list[9] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, "Gravitational potentials of the particles"); + + list[10] = io_make_output_field_convert_part( + "ViscosityParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_viscosity, "Visosity coefficient (alpha_visc) of the particles"); + + list[11] = io_make_output_field_convert_part( + "DiffusionParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_diffusion, "Diffusion coefficient (alpha_diff) of the particles"); } /** diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h index 558143f88d07225f5e6bfe5666d345a7891a7534..5a62770d9c4ddc1381ee9b390c06d0e8e607d32b 100644 --- a/src/hydro/Default/hydro.h +++ b/src/hydro/Default/hydro.h @@ -403,12 +403,14 @@ hydro_set_drifted_physical_internal_energy(struct part *p, /* Now recompute the extra quantities */ /* Compute the sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); - const float pressure = hydro_get_comoving_pressure(p); + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); /* Update variables. */ p->force.soundspeed = soundspeed; p->force.pressure = pressure; + + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -766,9 +768,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -780,10 +784,12 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( /* Compute the sound speed */ const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); - const float soundspeed = hydro_get_comoving_soundspeed(p); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); p->force.pressure = pressure; p->force.soundspeed = soundspeed; + + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -845,10 +851,12 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( /* Compute the new sound speed */ const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); - const float soundspeed = hydro_get_comoving_soundspeed(p); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); p->force.pressure = pressure; p->force.soundspeed = soundspeed; + + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** diff --git a/src/hydro/Default/hydro_io.h b/src/hydro/Default/hydro_io.h index 7b668fac1700738fb83199900dbb171ac913084f..1944f677ff16c5f002aa4ee2f830c84e808ffa63 100644 --- a/src/hydro/Default/hydro_io.h +++ b/src/hydro/Default/hydro_io.h @@ -164,35 +164,56 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 12; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + list[7] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); - list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1, - UNIT_CONV_ENTROPY_PER_UNIT_MASS, - parts, xparts, convert_S); - list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); - list[10] = io_make_output_field_convert_part("Viscosity", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - xparts, convert_viscosity); - list[11] = io_make_output_field_convert_part("Diffusion", FLOAT, 1, - UNIT_CONV_NO_UNITS, parts, - xparts, convert_diffusion); + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + xparts, convert_P, "Co-moving pressures of the particles"); + + list[8] = io_make_output_field_convert_part( + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Co-moving entropies per unit mass of the particles"); + + list[9] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, + "Co-moving gravitational potential at position of the particles"); + + list[10] = io_make_output_field_convert_part( + "ViscosityParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_viscosity, "Visosity coefficient (alpha_visc) of the particles"); + + list[11] = io_make_output_field_convert_part( + "DiffusionParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_diffusion, "Diffusion coefficient (alpha_diff) of the particles"); } /** diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index afc9c17d5b6ed691685bf42df2562ecb6f4409d8..00fc59e70c2f421f39a647d104e1369e6c6f626b 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -41,6 +41,7 @@ #include "hydro_space.h" #include "kernel_hydro.h" #include "minmax.h" +#include "pressure_floor.h" #include "./hydro_parameters.h" @@ -388,17 +389,22 @@ hydro_set_drifted_physical_internal_energy(struct part *p, const float rho_inv = 1.f / p->rho; /* Compute the pressure */ - const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + float comoving_pressure = gas_pressure_from_entropy(p->rho, p->entropy); + comoving_pressure = + pressure_floor_get_comoving_pressure(p, comoving_pressure, cosmo); /* Compute the sound speed */ - const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, comoving_pressure); /* Divide the pressure by the density squared to get the SPH term */ - const float P_over_rho2 = pressure * rho_inv * rho_inv; + const float P_over_rho2 = comoving_pressure * rho_inv * rho_inv; /* Update variables. */ p->force.P_over_rho2 = P_over_rho2; p->force.soundspeed = soundspeed; + + p->force.v_sig = max(p->force.v_sig, 2.f * soundspeed); } /** @@ -459,7 +465,7 @@ __attribute__((always_inline)) INLINE static void hydro_timestep_extra( * @brief Prepares a particle for the density calculation. * * Zeroes all the relevant arrays in preparation for the sums taking place in - * the variaous density tasks + * the various density tasks * * @param p The particle to act upon * @param hs #hydro_space containing hydro specific space information. @@ -591,13 +597,16 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( const float abs_div_physical_v = fabsf(div_physical_v); /* Compute the pressure */ - const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + float comoving_pressure = gas_pressure_from_entropy(p->rho, p->entropy); + comoving_pressure = + pressure_floor_get_comoving_pressure(p, comoving_pressure, cosmo); /* Compute the sound speed */ - const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, comoving_pressure); /* Divide the pressure by the density squared to get the SPH term */ - const float P_over_rho2 = pressure * rho_inv * rho_inv; + const float P_over_rho2 = comoving_pressure * rho_inv * rho_inv; /* Compute the Balsara switch */ /* Pre-multiply in the AV factor; hydro_props are not passed to the iact @@ -643,7 +652,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( p->force.h_dt = 0.0f; /* Reset maximal signal velocity */ - p->force.v_sig = p->force.soundspeed; + p->force.v_sig = 2.f * p->force.soundspeed; } /** @@ -652,9 +661,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -665,14 +676,17 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( p->entropy = xp->entropy_full; /* Re-compute the pressure */ - const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + float comoving_pressure = gas_pressure_from_entropy(p->rho, p->entropy); + comoving_pressure = + pressure_floor_get_comoving_pressure(p, comoving_pressure, cosmo); /* Compute the new sound speed */ - const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, comoving_pressure); /* Divide the pressure by the density squared to get the SPH term */ const float rho_inv = 1.f / p->rho; - const float P_over_rho2 = pressure * rho_inv * rho_inv; + const float P_over_rho2 = comoving_pressure * rho_inv * rho_inv; /* Update variables */ p->force.soundspeed = soundspeed; @@ -730,18 +744,23 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->rho *= expf(w2); /* Re-compute the pressure */ - const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + float comoving_pressure = gas_pressure_from_entropy(p->rho, p->entropy); + comoving_pressure = + pressure_floor_get_comoving_pressure(p, comoving_pressure, cosmo); /* Compute the new sound speed */ - const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, comoving_pressure); /* Divide the pressure by the density squared to get the SPH term */ const float rho_inv = 1.f / p->rho; - const float P_over_rho2 = pressure * rho_inv * rho_inv; + const float P_over_rho2 = comoving_pressure * rho_inv * rho_inv; /* Update variables */ p->force.soundspeed = soundspeed; p->force.P_over_rho2 = P_over_rho2; + + p->force.v_sig = max(p->force.v_sig, 2.f * soundspeed); } /** @@ -840,14 +859,17 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( } /* Compute the pressure */ - const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + float comoving_pressure = gas_pressure_from_entropy(p->rho, p->entropy); + comoving_pressure = + pressure_floor_get_comoving_pressure(p, comoving_pressure, cosmo); /* Compute the sound speed */ - const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, comoving_pressure); /* Divide the pressure by the density squared to get the SPH term */ const float rho_inv = 1.f / p->rho; - const float P_over_rho2 = pressure * rho_inv * rho_inv; + const float P_over_rho2 = comoving_pressure * rho_inv * rho_inv; p->force.soundspeed = soundspeed; p->force.P_over_rho2 = P_over_rho2; diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h index 5d68dc5d9f0f4ac0d12f7829f2c2a12d9964fe4a..20ad8e2d0c15094101e44999ff643f9d21619622 100644 --- a/src/hydro/Gadget2/hydro_io.h +++ b/src/hydro/Gadget2/hydro_io.h @@ -130,16 +130,6 @@ INLINE static void convert_part_potential(const struct engine* e, ret[0] = 0.f; } -INLINE static void convert_part_group_id(const struct engine* e, - const struct part* p, - const struct xpart* xp, int* ret) { - - if (p->gpart != NULL) - ret[0] = p->gpart->group_id; - else - ret[0] = 0; -} - /** * @brief Specifies which particle fields to write to a dataset * @@ -153,36 +143,51 @@ INLINE static void hydro_write_particles(const struct part* parts, struct io_props* list, int* num_fields) { - *num_fields = 11; + *num_fields = 10; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + list[4] = io_make_output_field( - "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[7] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, xparts, convert_part_u); + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + entropy, "Co-moving entropies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + + list[7] = io_make_output_field_convert_part( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, xparts, convert_part_u, + "Co-moving thermal energies per unit mass of the particles"); + list[8] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_part_P); - - list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); - list[10] = - io_make_output_field_convert_part("GroupIDs", INT, 1, UNIT_CONV_NO_UNITS, - parts, xparts, convert_part_group_id); + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + xparts, convert_part_P, "Co-moving pressures of the particles"); + + list[9] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, + "Co-moving gravitational potential at position of the particles"); #ifdef DEBUG_INTERACTIONS_SPH diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h index 7a8844d560561dae80c676a0b5bb72b34416d080..5f4cca366b101c2341863f47d7a9adce51b860a2 100644 --- a/src/hydro/Gadget2/hydro_part.h +++ b/src/hydro/Gadget2/hydro_part.h @@ -35,6 +35,7 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "logger.h" +#include "pressure_floor_struct.h" #include "star_formation_struct.h" #include "tracers_struct.h" @@ -155,6 +156,9 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /* Additional data used by the pressure floor */ + struct pressure_floor_part_data pressure_floor_data; + /* Time-step length */ timebin_t time_bin; diff --git a/src/hydro/GizmoMFM/hydro.h b/src/hydro/GizmoMFM/hydro.h index eb3d9044baecadeaba9165061fae33c816446042..890c7f4c113e289e167247bb4978c1a362b8ff5d 100644 --- a/src/hydro/GizmoMFM/hydro.h +++ b/src/hydro/GizmoMFM/hydro.h @@ -497,9 +497,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part* restrict p, const struct xpart* restrict xp) { + struct part* restrict p, const struct xpart* restrict xp, + const struct cosmology* cosmo) { // MATTHIEU: Do we need something here? } diff --git a/src/hydro/GizmoMFM/hydro_io.h b/src/hydro/GizmoMFM/hydro_io.h index 1f956edf3fdc31990c6aba254603ea69a98238eb..711eee9e3117c220e6ab5a27a6d8f3557ec7ce13 100644 --- a/src/hydro/GizmoMFM/hydro_io.h +++ b/src/hydro/GizmoMFM/hydro_io.h @@ -188,33 +188,52 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - - list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, - conserved.mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, xparts, convert_u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 1.f, parts, + conserved.mass, "Co-moving masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field_convert_part( + "InternalEnergy", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + 3.f * hydro_gamma_minus_one, parts, xparts, convert_u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + list[7] = io_make_output_field_convert_part( - "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, xparts, convert_A); - list[8] = - io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, P); + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY, 0.f, parts, xparts, convert_A, + "Co-moving entropies of the particles"); + + list[8] = io_make_output_field("Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, + -3.f * hydro_gamma, parts, P, + "Co-moving pressures of the particles"); + list[9] = io_make_output_field_convert_part( - "TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY, parts, xparts, convert_Etot); + "TotalEnergies", FLOAT, 1, UNIT_CONV_ENERGY, -3.f * hydro_gamma_minus_one, + parts, xparts, convert_Etot, "Total (co-moving) energy of the particles"); - list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); + list[10] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, "Gravitational potentials of the particles"); } /** diff --git a/src/hydro/GizmoMFV/hydro.h b/src/hydro/GizmoMFV/hydro.h index e6a87b9a86fc7ec03a4bfe055e151ee8c7c580e4..58a8a19dccd2dd102beb2803ec04f1f555cdcec2 100644 --- a/src/hydro/GizmoMFV/hydro.h +++ b/src/hydro/GizmoMFV/hydro.h @@ -539,9 +539,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part* restrict p, const struct xpart* restrict xp) { + struct part* restrict p, const struct xpart* restrict xp, + const struct cosmology* cosmo) { // MATTHIEU: Apply the entropy floor here. } diff --git a/src/hydro/GizmoMFV/hydro_io.h b/src/hydro/GizmoMFV/hydro_io.h index 92e4378f071cb71678929716be86588a3405f40e..11b8e95867495ccbc9df6a67b3045a09052fb161 100644 --- a/src/hydro/GizmoMFV/hydro_io.h +++ b/src/hydro/GizmoMFV/hydro_io.h @@ -184,37 +184,55 @@ INLINE static void hydro_write_particles(const struct part* parts, const struct xpart* xparts, struct io_props* list, int* num_fields) { - *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - - list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, - conserved.mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, xparts, convert_u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, - primitives.rho); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 1.f, parts, + conserved.mass, "Co-moving masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field_convert_part( + "InternalEnergy", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + 3.f * hydro_gamma_minus_one, parts, xparts, convert_u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, primitives.rho, + "Co-moving mass densities of the particles"); + list[7] = io_make_output_field_convert_part( - "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, xparts, convert_A); - list[8] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, - parts, primitives.P); + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY, 0.f, parts, xparts, convert_A, + "Co-moving entropies of the particles"); + + list[8] = io_make_output_field("Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, + -3.f * hydro_gamma, parts, primitives.P, + "Co-moving pressures of the particles"); + list[9] = io_make_output_field_convert_part( - "TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY, parts, xparts, convert_Etot); + "TotalEnergies", FLOAT, 1, UNIT_CONV_ENERGY, -3.f * hydro_gamma_minus_one, + parts, xparts, convert_Etot, "Total (co-moving) energy of the particles"); - list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); + list[10] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, "Gravitational potentials of the particles"); } /** diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index 0cac1de40f258ff9921ea98ddd59a848d4bb7a67..cdfbdbf3bcf3790b8cba84ce74084d3885cf75bf 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -390,10 +390,14 @@ hydro_set_drifted_physical_internal_energy(struct part *p, /* Now recompute the extra quantities */ /* Compute the sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); /* Update variables. */ + p->force.pressure = pressure; p->force.soundspeed = soundspeed; + + p->force.v_sig = max(p->force.v_sig, 2.f * soundspeed); } /** @@ -633,7 +637,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( /* Reset the time derivatives. */ p->u_dt = 0.0f; p->force.h_dt = 0.0f; - p->force.v_sig = p->force.soundspeed; + p->force.v_sig = 2.f * p->force.soundspeed; } /** @@ -642,9 +646,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -663,6 +669,8 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( /* Update variables */ p->force.pressure = pressure; p->force.soundspeed = soundspeed; + + p->force.v_sig = max(p->force.v_sig, 2.f * soundspeed); } /** @@ -726,6 +734,8 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->force.pressure = pressure; p->force.soundspeed = soundspeed; + + p->force.v_sig = max(p->force.v_sig, 2.f * soundspeed); } /** diff --git a/src/hydro/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h index c6e36e32d6176c6968e70c7ba689b6651a2d1c18..ba62489bcc8a7bff3b9d3cd45d9b9d0881772194 100644 --- a/src/hydro/Minimal/hydro_io.h +++ b/src/hydro/Minimal/hydro_io.h @@ -157,33 +157,46 @@ INLINE static void hydro_write_particles(const struct part* parts, struct io_props* list, int* num_fields) { - *num_fields = 10; + *num_fields = 9; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[7] = io_make_output_field_convert_part("Entropy", FLOAT, 1, - UNIT_CONV_ENTROPY_PER_UNIT_MASS, - parts, xparts, convert_S); - list[8] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Co-moving thermal energies per unit mass of the particles"); - list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + + list[7] = io_make_output_field_convert_part( + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Co-moving entropies per unit mass of the particles"); + + list[8] = io_make_output_field_convert_part( + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + xparts, convert_P, "Co-moving pressures of the particles"); } /** diff --git a/src/hydro/Planetary/hydro.h b/src/hydro/Planetary/hydro.h index 7139f811a6e868d9eafdbbf4628b03c156aa459e..3891bf3df8245b63ec40985910aa7d827b0c00b7 100644 --- a/src/hydro/Planetary/hydro.h +++ b/src/hydro/Planetary/hydro.h @@ -601,8 +601,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Compute the "grad h" term */ const float rho_inv = 1.f / p->rho; float rho_dh = p->density.rho_dh; - /* Ignore changing-kernel effects when h is h_max */ - if (p->h == hydro_props->h_max) { + /* Ignore changing-kernel effects when h ~= h_max */ + if (p->h > 0.9999f * hydro_props->h_max) { rho_dh = 0.f; } const float grad_h_term = @@ -652,9 +652,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -869,7 +871,9 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { __attribute__((always_inline)) INLINE static void hydro_remove_part( const struct part *p, const struct xpart *xp) { + printf("Removed particle id=%lld \n", p->id); printParticle_single(p, xp); + fflush(stdout); } #endif /* SWIFT_PLANETARY_HYDRO_H */ diff --git a/src/hydro/Planetary/hydro_debug.h b/src/hydro/Planetary/hydro_debug.h index 306f7526404599a051f83dc1b61886ed2aa5b69e..6d0a226f49ab9d1b57f00cba646c7cb38eae180e 100644 --- a/src/hydro/Planetary/hydro_debug.h +++ b/src/hydro/Planetary/hydro_debug.h @@ -42,7 +42,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "v_full=[%.3g, %.3g, %.3g], a=[%.3g, %.3g, %.3g], \n " "m=%.3g, u=%.3g, du/dt=%.3g, P=%.3g, c_s=%.3g, \n " "v_sig=%.3g, h=%.3g, dh/dt=%.3g, wcount=%.3g, rho=%.3g, \n " - "dh_drho=%.3g, time_bin=%d, wakeup=%d mat_id=%d \n", + "dh_drho=%.3g, time_bin=%d, wakeup=%d, mat_id=%d \n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->mass, p->u, p->u_dt, hydro_get_comoving_pressure(p), diff --git a/src/hydro/Planetary/hydro_io.h b/src/hydro/Planetary/hydro_io.h index 64f229be3087fcb225b2b934b613f2eda0d84eba..782e4de6dc05c8d5b485f9f4d2dadd685e44e3a7 100644 --- a/src/hydro/Planetary/hydro_io.h +++ b/src/hydro/Planetary/hydro_io.h @@ -71,7 +71,7 @@ INLINE static void hydro_read_particles(struct part* parts, UNIT_CONV_ACCELERATION, parts, a_hydro); list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL, UNIT_CONV_DENSITY, parts, rho); - list[8] = io_make_input_field("MaterialID", INT, 1, COMPULSORY, + list[8] = io_make_input_field("MaterialIDs", INT, 1, COMPULSORY, UNIT_CONV_NO_UNITS, parts, mat_id); } @@ -163,31 +163,38 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Positions of the particles"); list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[7] = io_make_output_field_convert_part("Entropy", FLOAT, 1, - UNIT_CONV_ENTROPY_PER_UNIT_MASS, - parts, xparts, convert_S); - list[8] = io_make_output_field("MaterialID", INT, 1, UNIT_CONV_NO_UNITS, - parts, mat_id); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, "Velocities of the particles"); + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Smoothing lengths (FWHM of the kernel) of the particles"); + list[4] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Thermal energies per unit mass of the particles"); + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, "Densities of the particles"); + list[7] = io_make_output_field_convert_part( + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Entropies per unit mass of the particles"); + list[8] = + io_make_output_field("MaterialIDs", INT, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, mat_id, "Material IDs of the particles"); list[9] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); - list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + xparts, convert_P, "Pressures of the particles"); + list[10] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, 0.f, parts, xparts, + convert_part_potential, "Gravitational potentials of the particles"); } /** diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h index e3a4f98160150717e50651760d8af68af2a70662..6ce6c5c526ca016884f13d88044ed3a19248de4c 100644 --- a/src/hydro/PressureEnergy/hydro.h +++ b/src/hydro/PressureEnergy/hydro.h @@ -46,6 +46,7 @@ #include "hydro_space.h" #include "kernel_hydro.h" #include "minmax.h" +#include "pressure_floor.h" #include "./hydro_parameters.h" @@ -212,18 +213,33 @@ hydro_get_drifted_physical_entropy(const struct part *restrict p, } /** - * @brief Returns the comoving sound speed of a particle + * @brief Update the sound speed of a particle * - * @param p The particle of interest + * @param p The particle of interest. + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float -hydro_get_comoving_soundspeed(const struct part *restrict p) { +__attribute__((always_inline)) INLINE static void hydro_update_soundspeed( + struct part *restrict p, const struct cosmology *cosmo) { /* Compute the sound speed -- see theory section for justification */ /* IDEAL GAS ONLY -- P-U does not work with generic EoS. */ - const float square_rooted = sqrtf(hydro_gamma * p->pressure_bar / p->rho); + const float comoving_pressure = + pressure_floor_get_comoving_pressure(p, p->pressure_bar, cosmo); + p->force.soundspeed = gas_soundspeed_from_pressure(p->rho, comoving_pressure); + + /* Also update the signal velocity; this could be a huge change! */ + p->force.v_sig = max(p->force.v_sig, 2.f * p->force.soundspeed); +} + +/** + * @brief Returns the comoving sound speed of a particle + * + * @param p The particle of interest. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { - return square_rooted; + return p->force.soundspeed; } /** @@ -236,6 +252,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_soundspeed(const struct part *restrict p, const struct cosmology *cosmo) { + /* The pressure floor is already included in p->force.soundspeed */ return cosmo->a_factor_sound_speed * p->force.soundspeed; } @@ -410,15 +427,21 @@ hydro_set_drifted_physical_internal_energy(struct part *p, const struct cosmology *cosmo, const float u) { - p->u = u / cosmo->a_factor_internal_energy; + /* Store ratio of new internal energy to old internal energy, as we use this + * in the drifting of the pressure. */ + float internal_energy_ratio = 1.f / p->u; - /* Now recompute the extra quantities */ + /* Update the internal energy */ + p->u = u / cosmo->a_factor_internal_energy; + internal_energy_ratio *= p->u; - /* Compute the sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); + /* Now we can use this to 'update' the value of the smoothed pressure. To + * truly update this variable, we would need another loop over neighbours + * using the new internal energies of everyone, but that's not feasible. */ + p->pressure_bar *= internal_energy_ratio; /* Update variables. */ - p->force.soundspeed = soundspeed; + hydro_update_soundspeed(p, cosmo); } /** @@ -465,10 +488,7 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / (cosmo->a_factor_sound_speed * p->force.v_sig); - const float dt_u_change = - (p->u_dt != 0.0f) ? fabsf(const_max_u_change * p->u / p->u_dt) : FLT_MAX; - - return fminf(dt_cfl, dt_u_change); + return dt_cfl; } /** @@ -497,7 +517,6 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount = 0.f; p->density.wcount_dh = 0.f; p->rho = 0.f; - p->density.rho_dh = 0.f; p->pressure_bar = 0.f; p->density.pressure_bar_dh = 0.f; @@ -531,7 +550,6 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Final operation on the density (add self-contribution). */ p->rho += p->mass * kernel_root; - p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; p->pressure_bar += p->mass * p->u * kernel_root; p->density.pressure_bar_dh -= hydro_dimension * p->mass * p->u * kernel_root; p->density.wcount += kernel_root; @@ -539,7 +557,6 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Finish the calculation by inserting the missing h-factors */ p->rho *= h_inv_dim; - p->density.rho_dh *= h_inv_dim_plus_one; p->pressure_bar *= (h_inv_dim * hydro_gamma_minus_one); p->density.pressure_bar_dh *= (h_inv_dim_plus_one * hydro_gamma_minus_one); p->density.wcount *= h_inv_dim; @@ -584,7 +601,6 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->pressure_bar = p->mass * p->u * hydro_gamma_minus_one * kernel_root * h_inv_dim; p->density.wcount = kernel_root * h_inv_dim; - p->density.rho_dh = 0.f; p->density.wcount_dh = 0.f; p->density.pressure_bar_dh = 0.f; @@ -627,6 +643,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( const float abs_div_v = fabsf(p->density.div_v); /* Compute the sound speed -- see theory section for justification */ + hydro_update_soundspeed(p, cosmo); const float soundspeed = hydro_get_comoving_soundspeed(p); /* Compute the Balsara switch */ @@ -640,10 +657,14 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( hydro_one_over_gamma_minus_one) / (1.f + common_factor * p->density.wcount_dh); + /* Get the pressures */ + const float comoving_pressure_with_floor = + pressure_floor_get_comoving_pressure(p, p->pressure_bar, cosmo); + /* Update variables. */ p->force.f = grad_h_term; - p->force.soundspeed = soundspeed; p->force.balsara = balsara; + p->force.pressure_bar_with_floor = comoving_pressure_with_floor; } /** @@ -665,7 +686,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( /* Reset the time derivatives. */ p->u_dt = 0.0f; p->force.h_dt = 0.0f; - p->force.v_sig = p->force.soundspeed; + p->force.v_sig = 2.f * p->force.soundspeed; } /** @@ -674,9 +695,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -687,9 +710,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( p->u = xp->u_full; /* Compute the sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); - - p->force.soundspeed = soundspeed; + hydro_update_soundspeed(p, cosmo); } /** @@ -715,8 +736,18 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { + /* Store ratio of new internal energy to old internal energy, as we use this + * in the drifting of the pressure. */ + float internal_energy_ratio = 1.f / p->u; + /* Predict the internal energy */ p->u += p->u_dt * dt_therm; + internal_energy_ratio *= p->u; + + /* Now we can use this to 'update' the value of the smoothed pressure. To + * truly update this variable, we would need another loop over neighbours + * using the new internal energies of everyone, but that's not feasible. */ + p->pressure_bar *= internal_energy_ratio; /* Check against entropy floor */ const float floor_A = entropy_floor(p, cosmo, floor_props); @@ -752,9 +783,12 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( } /* Compute the new sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); + hydro_update_soundspeed(p, cosmo); - p->force.soundspeed = soundspeed; + /* update the required variables */ + const float comoving_pressure_with_floor = + pressure_floor_get_comoving_pressure(p, p->pressure_bar, cosmo); + p->force.pressure_bar_with_floor = comoving_pressure_with_floor; } /** diff --git a/src/hydro/PressureEnergy/hydro_debug.h b/src/hydro/PressureEnergy/hydro_debug.h index 7ffc370ed4d6abd273fc3d8d5b887f5ccf8e001c..861b16299f824abbee759c80e79d99d449a348c5 100644 --- a/src/hydro/PressureEnergy/hydro_debug.h +++ b/src/hydro/PressureEnergy/hydro_debug.h @@ -30,13 +30,13 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "x=[%.3e,%.3e,%.3e], " "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e], " "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n" - "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, \n" + "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, rho=%.3e, \n" "p_dh=%.3e, p_bar=%.3e \n" "time_bin=%d wakeup=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, p->force.v_sig, hydro_get_comoving_pressure(p), p->h, - p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, + p->force.h_dt, (int)p->density.wcount, p->mass, p->rho, p->density.pressure_bar_dh, p->pressure_bar, p->time_bin, p->wakeup); } diff --git a/src/hydro/PressureEnergy/hydro_iact.h b/src/hydro/PressureEnergy/hydro_iact.h index b59fdbbe418d0442fd290b3596fd08531fae88e6..62106bb35cd09a0d67d56fb7b8ab58ba7cac7491 100644 --- a/src/hydro/PressureEnergy/hydro_iact.h +++ b/src/hydro/PressureEnergy/hydro_iact.h @@ -69,7 +69,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( kernel_deval(ui, &wi, &wi_dx); pi->rho += mj * wi; - pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); pi->pressure_bar += mj * wi * pj->u; pi->density.pressure_bar_dh -= @@ -83,7 +82,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( kernel_deval(uj, &wj, &wj_dx); pj->rho += mi * wj; - pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); pj->pressure_bar += mi * wj * pi->u; pj->density.pressure_bar_dh -= mi * pi->u * (hydro_dimension * wj + uj * wj_dx); @@ -149,7 +147,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( kernel_deval(ui, &wi, &wi_dx); pi->rho += mj * wi; - pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); pi->pressure_bar += mj * wi * pj->u; @@ -259,11 +256,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Convolve with the kernel */ const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; + /* Compute the ratio of pressures */ + const float pressure_inverse_i = + pi->force.pressure_bar_with_floor / (pi->pressure_bar * pi->pressure_bar); + const float pressure_inverse_j = + pj->force.pressure_bar_with_floor / (pj->pressure_bar * pj->pressure_bar); + /* SPH acceleration term */ - const float sph_acc_term = - pj->u * pi->u * hydro_gamma_minus_one * hydro_gamma_minus_one * - ((f_ij / pi->pressure_bar) * wi_dr + (f_ji / pj->pressure_bar) * wj_dr) * - r_inv; + const float sph_acc_term = pj->u * pi->u * hydro_gamma_minus_one * + hydro_gamma_minus_one * + ((f_ij * pressure_inverse_i) * wi_dr + + (f_ji * pressure_inverse_j) * wj_dr) * + r_inv; /* Assemble the acceleration */ const float acc = sph_acc_term + visc_acc_term; @@ -278,11 +282,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->a_hydro[2] += mi * acc * dx[2]; /* Get the time derivative for u. */ + const float sph_du_term_i = hydro_gamma_minus_one * hydro_gamma_minus_one * - pj->u * pi->u * (f_ij / pi->pressure_bar) * + pj->u * pi->u * (f_ij * pressure_inverse_i) * wi_dr * dvdr * r_inv; + const float sph_du_term_j = hydro_gamma_minus_one * hydro_gamma_minus_one * - pi->u * pj->u * (f_ji / pj->pressure_bar) * + pi->u * pj->u * (f_ji * pressure_inverse_j) * wj_dr * dvdr * r_inv; /* Viscosity term */ @@ -386,11 +392,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Convolve with the kernel */ const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; + /* Compute the ratio of pressures */ + const float pressure_inverse_i = + pi->force.pressure_bar_with_floor / (pi->pressure_bar * pi->pressure_bar); + const float pressure_inverse_j = + pj->force.pressure_bar_with_floor / (pj->pressure_bar * pj->pressure_bar); + /* SPH acceleration term */ - const float sph_acc_term = - pj->u * pi->u * hydro_gamma_minus_one * hydro_gamma_minus_one * - ((f_ij / pi->pressure_bar) * wi_dr + (f_ji / pj->pressure_bar) * wj_dr) * - r_inv; + const float sph_acc_term = pj->u * pi->u * hydro_gamma_minus_one * + hydro_gamma_minus_one * + ((f_ij * pressure_inverse_i) * wi_dr + + (f_ji * pressure_inverse_j) * wj_dr) * + r_inv; /* Assemble the acceleration */ const float acc = sph_acc_term + visc_acc_term; @@ -402,7 +415,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for u. */ const float sph_du_term_i = hydro_gamma_minus_one * hydro_gamma_minus_one * - pj->u * pi->u * (f_ij / pi->pressure_bar) * + pj->u * pi->u * (f_ij * pressure_inverse_i) * wi_dr * dvdr * r_inv; /* Viscosity term */ diff --git a/src/hydro/PressureEnergy/hydro_io.h b/src/hydro/PressureEnergy/hydro_io.h index 3e645803ebc3cbdbc361e73d776824e2ea7906fa..896d0137ca7aae2d17159e4dbf335a9263dce3a7 100644 --- a/src/hydro/PressureEnergy/hydro_io.h +++ b/src/hydro/PressureEnergy/hydro_io.h @@ -156,29 +156,47 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 10; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[7] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, - parts, pressure_bar); - list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1, - UNIT_CONV_ENTROPY_PER_UNIT_MASS, - parts, xparts, convert_S); - list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + + list[7] = io_make_output_field( + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + pressure_bar, "Co-moving smoothed pressures of the particles"); + + list[8] = io_make_output_field_convert_part( + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Co-moving entropies per unit mass of the particles"); + + list[9] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, "Gravitational potentials of the particles"); } /** diff --git a/src/hydro/PressureEnergy/hydro_part.h b/src/hydro/PressureEnergy/hydro_part.h index ded83d70176409332b669517ed203fdf95ecfd5e..6231ad4e1356b91c220dad5e992d68c77975e950 100644 --- a/src/hydro/PressureEnergy/hydro_part.h +++ b/src/hydro/PressureEnergy/hydro_part.h @@ -34,6 +34,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "pressure_floor_struct.h" #include "star_formation_struct.h" #include "tracers_struct.h" @@ -132,9 +133,6 @@ struct part { /*! Derivative of the neighbour number with respect to h. */ float wcount_dh; - /*! Derivative of density with respect to h */ - float rho_dh; - /*! Derivative of the weighted pressure with respect to h */ float pressure_bar_dh; @@ -168,6 +166,9 @@ struct part { /*! Balsara switch */ float balsara; + + /*! Pressure term including the pressure floor */ + float pressure_bar_with_floor; } force; }; @@ -177,6 +178,9 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /* Additional data used by the pressure floor */ + struct pressure_floor_part_data pressure_floor_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h index c23b3467fd0dee575aacf92c108508be7d44ad32..ae5da6bfb7d8495cfb2b5298861aebdad31f7b75 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h @@ -164,9 +164,8 @@ hydro_get_comoving_soundspeed(const struct part *restrict p) { /* Compute the sound speed -- see theory section for justification */ /* IDEAL GAS ONLY -- P-U does not work with generic EoS. */ - const float square_rooted = sqrtf(hydro_gamma * p->pressure_bar / p->rho); - return square_rooted; + return gas_soundspeed_from_pressure(p->rho, p->pressure_bar); } /** @@ -407,15 +406,28 @@ hydro_set_drifted_physical_internal_energy(struct part *p, const struct cosmology *cosmo, const float u) { + /* Store ratio of new internal energy to old internal energy, as we use this + * in the drifting of the pressure. */ + float internal_energy_ratio = 1.f / p->u; + + /* Update the internal energy */ p->u = u / cosmo->a_factor_internal_energy; + internal_energy_ratio *= p->u; + + /* Now we can use this to 'update' the value of the smoothed pressure. To + * truly update this variable, we would need another loop over neighbours + * using the new internal energies of everyone, but that's not feasible. */ + p->pressure_bar *= internal_energy_ratio; /* Now recompute the extra quantities */ /* Compute the sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, p->pressure_bar); /* Update variables. */ p->force.soundspeed = soundspeed; + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -463,10 +475,7 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / (cosmo->a_factor_sound_speed * p->force.v_sig); - const float dt_u_change = - (p->u_dt != 0.0f) ? fabsf(const_max_u_change * p->u / p->u_dt) : FLT_MAX; - - return fminf(dt_cfl, dt_u_change); + return dt_cfl; } /** @@ -640,14 +649,14 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Artificial viscosity updates */ - /* TODO: Actually work out why this cosmology factor is correct - * and update the SPH / cosmology theory documents. */ + /* We perform all of this in physical space. */ + const float h_inv_physical = cosmo->a_inv * h_inv; + const float soundspeed_physical = cosmo->a_factor_sound_speed * soundspeed; - /* We divide by a^2 here to make this transform under cosmology the - * same as the velocity (which in SWIFT has an extra 1/a^2 factor. - * See the cosmology theory documents for more information. */ + /* Decay rate */ const float inverse_tau = - (hydro_props->viscosity.length * cosmo->a2_inv) * soundspeed * h_inv; + hydro_props->viscosity.length * soundspeed_physical * h_inv_physical; + /* Source term (div v) is already in physical co-ordinates for this scheme */ const float source_term = -1.f * min(p->density.div_v, 0.f); /* Compute da/dt */ @@ -680,7 +689,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( /* Reset the time derivatives. */ p->u_dt = 0.0f; p->force.h_dt = 0.0f; - p->force.v_sig = p->force.soundspeed; + p->force.v_sig = 2.f * p->force.soundspeed; } /** @@ -689,9 +698,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; @@ -730,8 +741,18 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { + /* Store ratio of new internal energy to old internal energy, as we use this + * in the drifting of the pressure. */ + float internal_energy_ratio = 1.f / p->u; + /* Predict the internal energy */ p->u += p->u_dt * dt_therm; + internal_energy_ratio *= p->u; + + /* Now we can use this to 'update' the value of the smoothed pressure. To + * truly update this variable, we would need another loop over neighbours + * using the new internal energies of everyone, but that's not feasible. */ + p->pressure_bar *= internal_energy_ratio; /* Check against entropy floor */ const float floor_A = entropy_floor(p, cosmo, floor_props); @@ -767,9 +788,11 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( } /* Compute the new sound speed */ - const float soundspeed = hydro_get_comoving_soundspeed(p); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, p->pressure_bar); p->force.soundspeed = soundspeed; + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h index fe44864b2c360373a3eff676775de6e9fac96266..7da369847a5d684ebe30dbf7428ea3da9a8c216f 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h @@ -157,31 +157,51 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[7] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, - parts, pressure_bar); - list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1, - UNIT_CONV_ENTROPY_PER_UNIT_MASS, - parts, xparts, convert_S); - list[9] = io_make_output_field("Viscosity", FLOAT, 1, UNIT_CONV_NO_UNITS, - parts, alpha); - list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + + list[7] = io_make_output_field( + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + pressure_bar, "Co-moving smoothed pressures of the particles"); + + list[8] = io_make_output_field_convert_part( + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Co-moving entropies per unit mass of the particles"); + + list[9] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, "Gravitational potentials of the particles"); + + list[10] = io_make_output_field( + "ViscosityParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, alpha, + "Visosity coefficient (alpha_visc) of the particles"); } /** diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h index 66b8e186c82ab1646ffb862ff2d401f0362886c3..83234a7ea145c426767cf4c6d36dc1af8ae490e4 100644 --- a/src/hydro/PressureEntropy/hydro.h +++ b/src/hydro/PressureEntropy/hydro.h @@ -628,9 +628,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part *restrict p, const struct xpart *restrict xp) { + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Re-set the predicted velocities */ p->v[0] = xp->v_full[0]; diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h index 5c0bb71d3dcfe77619d18115e27fbafbac3facec..8ac77f31efde3b708cbf631c792404b135e4c112 100644 --- a/src/hydro/PressureEntropy/hydro_io.h +++ b/src/hydro/PressureEntropy/hydro_io.h @@ -157,31 +157,51 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + list[1] = io_make_output_field_convert_part( - "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + list[4] = io_make_output_field( - "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = - io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[7] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, xparts, convert_u); + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + entropy, "Co-moving entropies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + + list[7] = io_make_output_field_convert_part( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, xparts, convert_u, + "Co-moving thermal energies per unit mass of the particles"); + list[8] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); - list[9] = io_make_output_field("WeightedDensity", FLOAT, 1, UNIT_CONV_DENSITY, - parts, rho_bar); - list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1, - UNIT_CONV_POTENTIAL, parts, - xparts, convert_part_potential); + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + xparts, convert_P, "Co-moving smoothed pressures of the particles"); + + list[9] = io_make_output_field( + "WeightedDensity", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, rho_bar, + "Co-moving pressure-weighted densities of the particles"); + + list[10] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, "Gravitational potentials of the particles"); } /** diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h index 955a3db1d4480c43dd2fd57c6d976689d36a5652..8c0454025180598baaeafb4032699cddb44b26d1 100644 --- a/src/hydro/Shadowswift/hydro.h +++ b/src/hydro/Shadowswift/hydro.h @@ -397,9 +397,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( * * @param p The particle. * @param xp The extended data of this particle. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( - struct part* restrict p, const struct xpart* restrict xp) {} + struct part* restrict p, const struct xpart* restrict xp, + const struct cosmology* cosmo) {} /** * @brief Converts the hydrodynamic variables from the initial condition file to @@ -797,6 +799,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_density( */ __attribute__((always_inline)) INLINE static float hydro_get_physical_internal_energy(const struct part* restrict p, + const struct xpart* restrict xp, const struct cosmology* cosmo) { return cosmo->a_factor_internal_energy * @@ -874,6 +877,38 @@ hydro_set_drifted_physical_internal_energy(struct part* p, error("Need implementing"); } +/** + * @brief Gets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * + * @return The physical internal energy + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_internal_energy(const struct part* p, + const struct cosmology* cosmo) { + error("Need implementing"); + + return 0; +} + +/** + * @brief Gets the drifted physical entropy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * + * @return The physical entropy + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_entropy(const struct part* p, + const struct cosmology* cosmo) { + error("Need implementing"); + + return 0; +} + /** * @brief Update the value of the viscosity alpha for the scheme. * diff --git a/src/hydro/Shadowswift/hydro_io.h b/src/hydro/Shadowswift/hydro_io.h index 1f6bb86e62c6a3359d1242328775c6e4067ef8f2..31840a05ee436d5b535ae022bd79118851ef4e5b 100644 --- a/src/hydro/Shadowswift/hydro_io.h +++ b/src/hydro/Shadowswift/hydro_io.h @@ -138,34 +138,59 @@ INLINE static void hydro_write_particles(const struct part* parts, *num_fields = 13; /* List what we want to write */ - list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, - UNIT_CONV_LENGTH, parts, xparts, - convert_part_pos); - list[1] = io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, - primitives.v); - list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, - conserved.mass); - list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - parts, h); - list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, xparts, convert_u); - list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); - list[6] = io_make_output_field("Acceleration", FLOAT, 3, - UNIT_CONV_ACCELERATION, parts, a_hydro); - list[7] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, - primitives.rho); - list[8] = io_make_output_field("Volume", FLOAT, 1, UNIT_CONV_VOLUME, parts, - cell.volume); - list[9] = io_make_output_field("GradDensity", FLOAT, 3, UNIT_CONV_DENSITY, - parts, primitives.gradients.rho); + list[0] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + + list[1] = io_make_output_field( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, primitives.v, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + conserved.mass, "Masses of the particles"); + + list[3] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[4] = io_make_output_field_convert_part( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, xparts, convert_u, + "Co-moving thermal energies per unit mass of the particles"); + + list[5] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "Unique IDs of the particles"); + + list[6] = io_make_output_field("Accelerations", FLOAT, 3, + UNIT_CONV_ACCELERATION, 1.f, parts, a_hydro, + "Accelerations of the particles(does not " + "work in non-cosmological runs)."); + + list[7] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, primitives.rho, + "Co-moving mass densities of the particles"); + + list[8] = + io_make_output_field("Volumes", FLOAT, 1, UNIT_CONV_VOLUME, -3.f, parts, + cell.volume, "Co-moving volumes of the particles"); + + list[9] = io_make_output_field("GradDensities", FLOAT, 3, UNIT_CONV_DENSITY, + 1.f, parts, primitives.gradients.rho, + "Gradient densities of the particles"); + list[10] = io_make_output_field_convert_part( - "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, xparts, convert_A); - list[11] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, - parts, primitives.P); + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY, 1.f, parts, xparts, convert_A, + "Co-moving entropies of the particles"); + + list[11] = io_make_output_field("Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, + -3.f * hydro_gamma, parts, primitives.P, + "Co-moving pressures of the particles"); + list[12] = io_make_output_field_convert_part( - "TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY, parts, xparts, convert_Etot); + "TotalEnergies", FLOAT, 1, UNIT_CONV_ENERGY, -3.f * hydro_gamma_minus_one, + parts, xparts, convert_Etot, "Total (co-moving) energy of the particles"); } /** diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 0082546ce14030a37fa63d87ea88bc99153b8213..d28cc1dff363517e72ebb3607ec5aa8121f828b2 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -34,6 +34,7 @@ #include "hydro.h" #include "kernel_hydro.h" #include "parser.h" +#include "pressure_floor.h" #include "units.h" #define hydro_props_default_max_iterations 30 @@ -186,6 +187,9 @@ void hydro_props_print(const struct hydro_props *p) { /* Print equation of state first */ eos_print(&eos); + /* Then the pressure floor */ + pressure_floor_print(&pressure_floor_props); + /* Now SPH */ message("Hydrodynamic scheme: %s in %dD.", SPH_IMPLEMENTATION, (int)hydro_dimension); @@ -238,6 +242,7 @@ void hydro_props_print(const struct hydro_props *p) { void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { eos_print_snapshot(h_grpsph, &eos); + pressure_floor_print_snapshot(h_grpsph); io_write_attribute_i(h_grpsph, "Dimension", (int)hydro_dimension); io_write_attribute_s(h_grpsph, "Scheme", SPH_IMPLEMENTATION); @@ -338,7 +343,7 @@ void hydro_props_update(struct hydro_props *p, const struct gravity_props *gp, * is a fixed fraction of the radius at which the softened forces * recover a Newtonian behaviour (i.e. 2.8 * Plummer equivalent softening * in the case of a cubic spline kernel). */ - p->h_min = p->h_min_ratio * gp->epsilon_cur / kernel_gamma; + p->h_min = p->h_min_ratio * gp->epsilon_baryon_cur / kernel_gamma; } /** diff --git a/src/io_properties.h b/src/io_properties.h index 7ddc8e241f6f114196e25bda2f737253f6fa9338..450b5b6621a05393c80c3aa7f0550588b0ede617 100644 --- a/src/io_properties.h +++ b/src/io_properties.h @@ -24,6 +24,7 @@ /* Local includes. */ #include "common_io.h" +#include "error.h" #include "inline.h" #include "part.h" @@ -85,6 +86,9 @@ struct io_props { /* Name */ char name[FIELD_BUFFER_SIZE]; + /* Description of the variable to write to the field's meta-data */ + char description[DESCRIPTION_BUFFER_SIZE]; + /* Type of the field */ enum IO_DATA_TYPE type; @@ -97,6 +101,9 @@ struct io_props { /* Units of the quantity */ enum unit_conversion_factor units; + /* Scale-factor exponent to apply for unit conversion to physical */ + float scale_factor_exponent; + /* Pointer to the field of the first particle in the array */ char* field; @@ -205,9 +212,10 @@ INLINE static struct io_props io_make_input_field_( /** * @brief Constructs an #io_props from its parameters */ -#define io_make_output_field(name, type, dim, units, part, field) \ - io_make_output_field_(name, type, dim, units, (char*)(&(part[0]).field), \ - sizeof(part[0])) +#define io_make_output_field(name, type, dim, units, a_exponent, part, field, \ + desc) \ + io_make_output_field_(name, type, dim, units, a_exponent, \ + (char*)(&(part[0]).field), sizeof(part[0]), desc) /** * @brief Construct an #io_props from its parameters @@ -216,23 +224,32 @@ INLINE static struct io_props io_make_input_field_( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param field Pointer to the field of the first particle * @param partSize The size in byte of the particle + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, char* field, size_t partSize) { + enum unit_conversion_factor units, float a_exponent, char* field, + size_t partSize, const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.field = field; r.partSize = partSize; r.conversion = 0; @@ -243,10 +260,11 @@ INLINE static struct io_props io_make_output_field_( /** * @brief Constructs an #io_props (with conversion) from its parameters */ -#define io_make_output_field_convert_part(name, type, dim, units, part, xpart, \ - convert) \ - io_make_output_field_convert_part_##type( \ - name, type, dim, units, sizeof(part[0]), part, xpart, convert) +#define io_make_output_field_convert_part(name, type, dim, units, a_exponent, \ + part, xpart, convert, desc) \ + io_make_output_field_convert_part_##type(name, type, dim, units, a_exponent, \ + sizeof(part[0]), part, xpart, \ + convert, desc) /** * @brief Construct an #io_props from its parameters @@ -255,27 +273,36 @@ INLINE static struct io_props io_make_output_field_( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param partSize The size in byte of the particle * @param parts The particle array * @param xparts The xparticle array * @param functionPtr The function used to convert a particle to an int + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_part_INT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t partSize, + enum unit_conversion_factor units, float a_exponent, size_t partSize, const struct part* parts, const struct xpart* xparts, - conversion_func_part_int functionPtr) { + conversion_func_part_int functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = partSize; r.parts = parts; r.xparts = xparts; @@ -292,27 +319,36 @@ INLINE static struct io_props io_make_output_field_convert_part_INT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param partSize The size in byte of the particle * @param parts The particle array * @param xparts The xparticle array * @param functionPtr The function used to convert a particle to a float + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t partSize, + enum unit_conversion_factor units, float a_exponent, size_t partSize, const struct part* parts, const struct xpart* xparts, - conversion_func_part_float functionPtr) { + conversion_func_part_float functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = partSize; r.parts = parts; r.xparts = xparts; @@ -329,27 +365,36 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param partSize The size in byte of the particle * @param parts The particle array * @param xparts The xparticle array * @param functionPtr The function used to convert a particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t partSize, + enum unit_conversion_factor units, float a_exponent, size_t partSize, const struct part* parts, const struct xpart* xparts, - conversion_func_part_double functionPtr) { + conversion_func_part_double functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = partSize; r.parts = parts; r.xparts = xparts; @@ -366,27 +411,36 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param partSize The size in byte of the particle * @param parts The particle array * @param xparts The xparticle array * @param functionPtr The function used to convert a particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_part_LONGLONG( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t partSize, + enum unit_conversion_factor units, float a_exponent, size_t partSize, const struct part* parts, const struct xpart* xparts, - conversion_func_part_long_long functionPtr) { + conversion_func_part_long_long functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = partSize; r.parts = parts; r.xparts = xparts; @@ -399,10 +453,11 @@ INLINE static struct io_props io_make_output_field_convert_part_LONGLONG( /** * @brief Constructs an #io_props (with conversion) from its parameters */ -#define io_make_output_field_convert_gpart(name, type, dim, units, gpart, \ - convert) \ - io_make_output_field_convert_gpart_##type(name, type, dim, units, \ - sizeof(gpart[0]), gpart, convert) +#define io_make_output_field_convert_gpart(name, type, dim, units, a_exponent, \ + gpart, convert, desc) \ + io_make_output_field_convert_gpart_##type(name, type, dim, units, \ + a_exponent, sizeof(gpart[0]), \ + gpart, convert, desc) /** * @brief Construct an #io_props from its parameters @@ -411,25 +466,34 @@ INLINE static struct io_props io_make_output_field_convert_part_LONGLONG( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param gpartSize The size in byte of the particle * @param gparts The particle array * @param functionPtr The function used to convert a g-particle to a float + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_gpart_INT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t gpartSize, - const struct gpart* gparts, conversion_func_gpart_int functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t gpartSize, + const struct gpart* gparts, conversion_func_gpart_int functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = gpartSize; r.gparts = gparts; r.conversion = 1; @@ -445,25 +509,34 @@ INLINE static struct io_props io_make_output_field_convert_gpart_INT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param gpartSize The size in byte of the particle * @param gparts The particle array * @param functionPtr The function used to convert a g-particle to a float + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t gpartSize, - const struct gpart* gparts, conversion_func_gpart_float functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t gpartSize, + const struct gpart* gparts, conversion_func_gpart_float functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = gpartSize; r.gparts = gparts; r.conversion = 1; @@ -479,25 +552,34 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param gpartSize The size in byte of the particle * @param gparts The particle array * @param functionPtr The function used to convert a g-particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t gpartSize, - const struct gpart* gparts, conversion_func_gpart_double functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t gpartSize, + const struct gpart* gparts, conversion_func_gpart_double functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = gpartSize; r.gparts = gparts; r.conversion = 1; @@ -513,25 +595,34 @@ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param gpartSize The size in byte of the particle * @param gparts The particle array * @param functionPtr The function used to convert a g-particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_gpart_LONGLONG( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t gpartSize, - const struct gpart* gparts, conversion_func_gpart_long_long functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t gpartSize, + const struct gpart* gparts, conversion_func_gpart_long_long functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = gpartSize; r.gparts = gparts; r.conversion = 1; @@ -543,10 +634,11 @@ INLINE static struct io_props io_make_output_field_convert_gpart_LONGLONG( /** * @brief Constructs an #io_props (with conversion) from its parameters */ -#define io_make_output_field_convert_spart(name, type, dim, units, spart, \ - convert) \ - io_make_output_field_convert_spart_##type(name, type, dim, units, \ - sizeof(spart[0]), spart, convert) +#define io_make_output_field_convert_spart(name, type, dim, units, a_exponent, \ + spart, convert, desc) \ + io_make_output_field_convert_spart_##type(name, type, dim, units, \ + a_exponent, sizeof(spart[0]), \ + spart, convert, desc) /** * @brief Construct an #io_props from its parameters @@ -555,25 +647,34 @@ INLINE static struct io_props io_make_output_field_convert_gpart_LONGLONG( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param spartSize The size in byte of the particle * @param sparts The particle array - * @param functionPtr The function used to convert a g-particle to a float + * @param functionPtr The function used to convert a s-particle to a float + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_spart_INT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t spartSize, - const struct spart* sparts, conversion_func_spart_int functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t spartSize, + const struct spart* sparts, conversion_func_spart_int functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = spartSize; r.sparts = sparts; r.conversion = 1; @@ -589,25 +690,34 @@ INLINE static struct io_props io_make_output_field_convert_spart_INT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param spartSize The size in byte of the particle * @param sparts The particle array * @param functionPtr The function used to convert a g-particle to a float + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_spart_FLOAT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t spartSize, - const struct spart* sparts, conversion_func_spart_float functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t spartSize, + const struct spart* sparts, conversion_func_spart_float functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = spartSize; r.sparts = sparts; r.conversion = 1; @@ -623,25 +733,34 @@ INLINE static struct io_props io_make_output_field_convert_spart_FLOAT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param spartSize The size in byte of the particle * @param sparts The particle array * @param functionPtr The function used to convert a s-particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_spart_DOUBLE( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t spartSize, - const struct spart* sparts, conversion_func_spart_double functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t spartSize, + const struct spart* sparts, conversion_func_spart_double functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = spartSize; r.sparts = sparts; r.conversion = 1; @@ -657,25 +776,34 @@ INLINE static struct io_props io_make_output_field_convert_spart_DOUBLE( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param spartSize The size in byte of the particle * @param sparts The particle array * @param functionPtr The function used to convert a s-particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_spart_LONGLONG( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t spartSize, - const struct spart* sparts, conversion_func_spart_long_long functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t spartSize, + const struct spart* sparts, conversion_func_spart_long_long functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = spartSize; r.sparts = sparts; r.conversion = 1; @@ -687,10 +815,11 @@ INLINE static struct io_props io_make_output_field_convert_spart_LONGLONG( /** * @brief Constructs an #io_props (with conversion) from its parameters */ -#define io_make_output_field_convert_bpart(name, type, dim, units, bpart, \ - convert) \ - io_make_output_field_convert_bpart_##type(name, type, dim, units, \ - sizeof(bpart[0]), bpart, convert) +#define io_make_output_field_convert_bpart(name, type, dim, units, a_exponent, \ + bpart, convert, desc) \ + io_make_output_field_convert_bpart_##type(name, type, dim, units, \ + a_exponent, sizeof(bpart[0]), \ + bpart, convert, desc) /** * @brief Construct an #io_props from its parameters @@ -699,25 +828,34 @@ INLINE static struct io_props io_make_output_field_convert_spart_LONGLONG( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param bpartSize The size in byte of the particle * @param bparts The particle array - * @param functionPtr The function used to convert a b-particle to a int + * @param functionPtr The function used to convert a b-particle to a float + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_bpart_INT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t bpartSize, - const struct bpart* bparts, conversion_func_bpart_int functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t bpartSize, + const struct bpart* bparts, conversion_func_bpart_int functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = bpartSize; r.bparts = bparts; r.conversion = 1; @@ -733,25 +871,34 @@ INLINE static struct io_props io_make_output_field_convert_bpart_INT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param bpartSize The size in byte of the particle * @param bparts The particle array * @param functionPtr The function used to convert a g-particle to a float + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_bpart_FLOAT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t bpartSize, - const struct bpart* bparts, conversion_func_bpart_float functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t bpartSize, + const struct bpart* bparts, conversion_func_bpart_float functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = bpartSize; r.bparts = bparts; r.conversion = 1; @@ -767,25 +914,34 @@ INLINE static struct io_props io_make_output_field_convert_bpart_FLOAT( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param bpartSize The size in byte of the particle * @param bparts The particle array * @param functionPtr The function used to convert a s-particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_bpart_DOUBLE( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t bpartSize, - const struct bpart* bparts, conversion_func_bpart_double functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t bpartSize, + const struct bpart* bparts, conversion_func_bpart_double functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = bpartSize; r.bparts = bparts; r.conversion = 1; @@ -801,25 +957,34 @@ INLINE static struct io_props io_make_output_field_convert_bpart_DOUBLE( * @param type The type of the data * @param dimension Dataset dimension (1D, 3D, ...) * @param units The units of the dataset + * @param a_exponent Exponent of the scale-factor to convert to physical units. * @param bpartSize The size in byte of the particle * @param bparts The particle array * @param functionPtr The function used to convert a s-particle to a double + * @param description Description of the field added to the meta-data. * * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_bpart_LONGLONG( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, - enum unit_conversion_factor units, size_t bpartSize, - const struct bpart* bparts, conversion_func_bpart_long_long functionPtr) { + enum unit_conversion_factor units, float a_exponent, size_t bpartSize, + const struct bpart* bparts, conversion_func_bpart_long_long functionPtr, + const char description[DESCRIPTION_BUFFER_SIZE]) { struct io_props r; bzero(&r, sizeof(struct io_props)); strcpy(r.name, name); + if (strlen(description) == 0) { + sprintf(r.description, "No description given"); + } else { + strcpy(r.description, description); + } r.type = type; r.dimension = dimension; r.importance = UNUSED; r.units = units; + r.scale_factor_exponent = a_exponent; r.partSize = bpartSize; r.bparts = bparts; r.conversion = 1; diff --git a/src/kick.h b/src/kick.h index 404f7f31170dcec4728b8fbc52717dbf4a35bb0c..0e28e271aad04e9b16a4e988112aa95b36cb4f20 100644 --- a/src/kick.h +++ b/src/kick.h @@ -155,4 +155,41 @@ __attribute__((always_inline)) INLINE static void kick_spart( stars_kick_extra(sp, dt_kick_grav); } +/** + * @brief Perform the 'kick' operation on a #bpart + * + * @param bp The #bpart to kick. + * @param dt_kick_grav The kick time-step for gravity accelerations. + * @param ti_start The starting (integer) time of the kick (for debugging + * checks). + * @param ti_end The ending (integer) time of the kick (for debugging checks). + */ +__attribute__((always_inline)) INLINE static void kick_bpart( + struct bpart *restrict bp, double dt_kick_grav, integertime_t ti_start, + integertime_t ti_end) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->ti_kick != ti_start) + error( + "s-particle has not been kicked to the current time bp->ti_kick=%lld, " + "ti_start=%lld, ti_end=%lld id=%lld", + bp->ti_kick, ti_start, ti_end, bp->id); + + bp->ti_kick = ti_end; +#endif + + /* Kick particles in momentum space */ + bp->v[0] += bp->gpart->a_grav[0] * dt_kick_grav; + bp->v[1] += bp->gpart->a_grav[1] * dt_kick_grav; + bp->v[2] += bp->gpart->a_grav[2] * dt_kick_grav; + + /* Give the gpart friend the same velocity */ + bp->gpart->v_full[0] = bp->v[0]; + bp->gpart->v_full[1] = bp->v[1]; + bp->gpart->v_full[2] = bp->v[2]; + + /* Kick extra variables */ + black_holes_kick_extra(bp, dt_kick_grav); +} + #endif /* SWIFT_KICK_H */ diff --git a/src/logger.c b/src/logger.c index 8be521b27f949ea0d496a5207335f1ec68208489..762eb516077ef82f08b6c34da09cd7bc9eb6a280 100644 --- a/src/logger.c +++ b/src/logger.c @@ -44,44 +44,41 @@ /* * Thoses are definitions from the format and therefore should not be changed! */ -/* number of bytes for a mask */ +/* Number of bytes for a mask. */ // TODO change this to number of bits #define logger_mask_size 1 -/* number of bits for chunk header */ +/* Number of bits for chunk header. */ #define logger_header_bytes 8 -/* number bytes for an offset */ +/* Number bytes for an offset. */ #define logger_offset_size logger_header_bytes - logger_mask_size -/* number of bytes for the version information */ -#define logger_version_size 20 +/* Number of bytes for the file format information. */ +#define logger_format_size 20 -/* number of bytes for the labels in the header */ +/* Number of bytes for the labels in the header. */ #define logger_label_size 20 -/* number of bytes for the number in the header */ -#define logger_number_size 4 - -char logger_version[logger_version_size] = "0.1"; +char logger_file_format[logger_format_size] = "SWIFT_LOGGER"; const struct mask_data logger_mask_data[logger_count_mask] = { - /* Particle's position */ + /* Particle's position. */ {3 * sizeof(double), 1 << logger_x, "positions"}, - /* Particle's velocity */ + /* Particle's velocity. */ {3 * sizeof(float), 1 << logger_v, "velocities"}, - /* Particle's acceleration */ + /* Particle's acceleration. */ {3 * sizeof(float), 1 << logger_a, "accelerations"}, - /* Particle's entropy */ + /* Particle's entropy. */ {sizeof(float), 1 << logger_u, "entropy"}, - /* Particle's smoothing length */ + /* Particle's smoothing length. */ {sizeof(float), 1 << logger_h, "smoothing length"}, - /* Particle's density */ + /* Particle's density. */ {sizeof(float), 1 << logger_rho, "density"}, - /* Particle's constants: mass (float) and ID (long long) */ + /* Particle's constants: mass (float) and ID (long long). */ {sizeof(float) + sizeof(long long), 1 << logger_consts, "consts"}, /* Simulation time stamp: integertime and double time (e.g. scale - factor or time) */ + factor or time). */ {sizeof(integertime_t) + sizeof(double), 1 << logger_timestamp, "timestamp"}}; @@ -99,11 +96,11 @@ const struct mask_data logger_mask_data[logger_count_mask] = { */ char *logger_write_chunk_header(char *buff, const unsigned int *mask, const size_t *offset, const size_t offset_new) { - /* write mask */ + /* write mask. */ memcpy(buff, mask, logger_mask_size); buff += logger_mask_size; - /* write offset */ + /* write offset. */ size_t diff_offset = offset_new - *offset; memcpy(buff, &diff_offset, logger_offset_size); buff += logger_offset_size; @@ -112,7 +109,7 @@ char *logger_write_chunk_header(char *buff, const unsigned int *mask, } /** - * @brief Write to the dump + * @brief Write to the dump. * * @param d #dump file * @param offset (return) offset of the data @@ -121,13 +118,13 @@ char *logger_write_chunk_header(char *buff, const unsigned int *mask, */ void logger_write_data(struct dump *d, size_t *offset, size_t size, const void *p) { - /* get buffer */ + /* get buffer. */ char *buff = dump_get(d, size, offset); - /* write data to the buffer */ + /* write data to the buffer. */ memcpy(buff, p, size); - /* Update offset to end of chunk */ + /* Update offset to end of chunk. */ *offset += size; } @@ -171,15 +168,15 @@ int logger_compute_chunk_size(unsigned int mask) { * @param log The #logger * @param e The #engine */ -void logger_log_all(struct logger *log, const struct engine *e) { +void logger_log_all(struct logger_writer *log, const struct engine *e) { - /* Ensure that enough space is available */ + /* Ensure that enough space is available. */ logger_ensure_size(log, e->total_nr_parts, e->total_nr_gparts, 0); #ifdef SWIFT_DEBUG_CHECKS message("Need to implement stars"); #endif - /* some constants */ + /* some constants. */ const struct space *s = e->s; const unsigned int mask = logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask | @@ -187,17 +184,17 @@ void logger_log_all(struct logger *log, const struct engine *e) { logger_mask_data[logger_h].mask | logger_mask_data[logger_rho].mask | logger_mask_data[logger_consts].mask; - /* loop over all parts */ + /* loop over all parts. */ for (long long i = 0; i < e->total_nr_parts; i++) { logger_log_part(log, &s->parts[i], mask, &s->xparts[i].logger_data.last_offset); s->xparts[i].logger_data.steps_since_last_output = 0; } - /* loop over all gparts */ + /* loop over all gparts. */ if (e->total_nr_gparts > 0) error("Not implemented"); - /* loop over all sparts */ + /* loop over all sparts. */ // TODO } @@ -210,7 +207,7 @@ void logger_log_all(struct logger *log, const struct engine *e) { * @param offset Pointer to the offset of the previous log of this particle; * (return) offset of this log. */ -void logger_log_part(struct logger *log, const struct part *p, +void logger_log_part(struct logger_writer *log, const struct part *p, unsigned int mask, size_t *offset) { /* Make sure we're not writing a timestamp. */ @@ -289,7 +286,7 @@ void logger_log_part(struct logger *log, const struct part *p, * @param offset Pointer to the offset of the previous log of this particle; * (return) offset of this log. */ -void logger_log_gpart(struct logger *log, const struct gpart *p, +void logger_log_gpart(struct logger_writer *log, const struct gpart *p, unsigned int mask, size_t *offset) { /* Make sure we're not writing a timestamp. */ @@ -331,7 +328,7 @@ void logger_log_gpart(struct logger *log, const struct gpart *p, /* Particle constants, which is a bit more complicated. */ if (mask & logger_mask_data[logger_consts].mask) { - // TODO make it dependent of logger_mask_data + // TODO make it dependent of logger_mask_data. memcpy(buff, &p->mass, sizeof(float)); buff += sizeof(float); memcpy(buff, &p->id_or_neg_offset, sizeof(long long)); @@ -351,7 +348,7 @@ void logger_log_gpart(struct logger *log, const struct gpart *p, * @param offset Pointer to the offset of the previous log of this particle; * (return) offset of this log. */ -void logger_log_timestamp(struct logger *log, integertime_t timestamp, +void logger_log_timestamp(struct logger_writer *log, integertime_t timestamp, double time, size_t *offset) { struct dump *dump = &log->dump; @@ -368,11 +365,11 @@ void logger_log_timestamp(struct logger *log, integertime_t timestamp, buff = logger_write_chunk_header(buff, &mask, offset, offset_new); /* Store the timestamp. */ - // TODO make it dependent of logger_mask_data + // TODO make it dependent of logger_mask_data. memcpy(buff, ×tamp, sizeof(integertime_t)); buff += sizeof(integertime_t); - /* Store the time */ + /* Store the time. */ memcpy(buff, &time, sizeof(double)); /* Update the log message offset. */ @@ -390,21 +387,21 @@ void logger_log_timestamp(struct logger *log, integertime_t timestamp, * @param total_nr_gparts total number of gpart * @param total_nr_sparts total number of spart */ -void logger_ensure_size(struct logger *log, size_t total_nr_parts, +void logger_ensure_size(struct logger_writer *log, size_t total_nr_parts, size_t total_nr_gparts, size_t total_nr_sparts) { - /* count part memory */ + /* count part memory. */ size_t limit = log->max_chunk_size; limit *= total_nr_parts; - /* count gpart memory */ + /* count gpart memory. */ if (total_nr_gparts > 0) error("Not implemented"); - /* count spart memory */ + /* count spart memory. */ if (total_nr_sparts > 0) error("Not implemented"); - /* ensure enough space in dump */ + /* ensure enough space in dump. */ dump_ensure(&log->dump, limit, log->buffer_scale * limit); } @@ -414,8 +411,8 @@ void logger_ensure_size(struct logger *log, size_t total_nr_parts, * @param log The #logger * @param params The #swift_params */ -void logger_init(struct logger *log, struct swift_params *params) { - /* read parameters */ +void logger_init(struct logger_writer *log, struct swift_params *params) { + /* read parameters. */ log->delta_step = parser_get_param_int(params, "Logger:delta_step"); size_t buffer_size = parser_get_opt_param_float(params, "Logger:initial_buffer_size", 0.5) * @@ -424,24 +421,24 @@ void logger_init(struct logger *log, struct swift_params *params) { parser_get_opt_param_float(params, "Logger:buffer_scale", 10); parser_get_param_string(params, "Logger:basename", log->base_name); - /* set initial value of parameters */ + /* set initial value of parameters. */ log->timestamp_offset = 0; - /* generate dump filename */ + /* generate dump filename. */ char logger_name_file[PARSER_MAX_LINE_SIZE]; strcpy(logger_name_file, log->base_name); strcat(logger_name_file, ".dump"); - /* Compute max size for a particle chunk */ + /* Compute max size for a particle chunk. */ int max_size = logger_offset_size + logger_mask_size; - /* Loop over all fields except timestamp */ + /* Loop over all fields except timestamp. */ for (int i = 0; i < logger_count_mask - 1; i++) { max_size += logger_mask_data[i].size; } log->max_chunk_size = max_size; - /* init dump */ + /* init dump. */ dump_init(&log->dump, logger_name_file, buffer_size); } @@ -450,18 +447,17 @@ void logger_init(struct logger *log, struct swift_params *params) { * * @param log The #logger */ -void logger_clean(struct logger *log) { dump_close(&log->dump); } +void logger_free(struct logger_writer *log) { dump_close(&log->dump); } /** * @brief Write a file header to a logger file * * @param log The #logger - * @param dump The #dump in which to log the particle data. * */ -void logger_write_file_header(struct logger *log, const struct engine *e) { +void logger_write_file_header(struct logger_writer *log) { - /* get required variables */ + /* get required variables. */ struct dump *dump = &log->dump; size_t file_offset = dump->file_offset; @@ -471,37 +467,46 @@ void logger_write_file_header(struct logger *log, const struct engine *e) { "The logger is not empty." "This function should be called before writing anything in the logger"); - /* Write version information */ - logger_write_data(dump, &file_offset, logger_version_size, &logger_version); + /* Write format information. */ + logger_write_data(dump, &file_offset, logger_format_size, + &logger_file_format); + + /* Write the major version number. */ + int major = logger_major_version; + logger_write_data(dump, &file_offset, sizeof(int), &major); - /* write offset direction */ + /* Write the minor version number. */ + int minor = logger_minor_version; + logger_write_data(dump, &file_offset, sizeof(int), &minor); + + /* write offset direction. */ const int reversed = 0; - logger_write_data(dump, &file_offset, logger_number_size, &reversed); + logger_write_data(dump, &file_offset, sizeof(int), &reversed); - /* placeholder to write the offset of the first log here */ + /* placeholder to write the offset of the first log here. */ char *skip_header = dump_get(dump, logger_offset_size, &file_offset); - /* write number of bytes used for names */ - const int label_size = logger_label_size; - logger_write_data(dump, &file_offset, logger_number_size, &label_size); + /* write number of bytes used for names. */ + const unsigned int label_size = logger_label_size; + logger_write_data(dump, &file_offset, sizeof(unsigned int), &label_size); - /* write number of masks */ - int count_mask = logger_count_mask; - logger_write_data(dump, &file_offset, logger_number_size, &count_mask); + /* write number of masks. */ + const unsigned int count_mask = logger_count_mask; + logger_write_data(dump, &file_offset, sizeof(unsigned int), &count_mask); - /* write masks */ - // loop over all mask type + /* write masks. */ + // loop over all mask type. for (int i = 0; i < logger_count_mask; i++) { - // mask name + // mask name. logger_write_data(dump, &file_offset, logger_label_size, &logger_mask_data[i].name); - // mask size - logger_write_data(dump, &file_offset, logger_number_size, + // mask size. + logger_write_data(dump, &file_offset, sizeof(unsigned int), &logger_mask_data[i].size); } - /* last step: write first offset */ + /* last step: write first offset. */ memcpy(skip_header, &file_offset, logger_offset_size); } @@ -591,7 +596,7 @@ int logger_read_part(struct part *p, size_t *offset, const char *buff) { /* Particle constants, which is a bit more complicated. */ if (mask & logger_mask_data[logger_rho].mask) { - // TODO make it dependent of logger_mask_data + // TODO make it dependent of logger_mask_data. memcpy(&p->mass, buff, sizeof(float)); buff += sizeof(float); memcpy(&p->id, buff, sizeof(long long)); @@ -694,7 +699,7 @@ int logger_read_timestamp(unsigned long long int *t, double *time, error("Timestamp message contains extra fields."); /* Copy the timestamp value from the buffer. */ - // TODO make it dependent of logger_mask_data + // TODO make it dependent of logger_mask_data. memcpy(t, buff, sizeof(unsigned long long int)); buff += sizeof(unsigned long long int); diff --git a/src/logger.h b/src/logger.h index 56e2c8ab94c66b24df1800877bb9cfb129c3e645..ed2d6374fa9031f526e79e790572c89f6176df4b 100644 --- a/src/logger.h +++ b/src/logger.h @@ -28,13 +28,15 @@ #include "timeline.h" #include "units.h" -/* Forward declaration */ +/* Forward declaration. */ struct dump; struct gpart; struct part; -/* TODO remove dependency */ struct engine; +#define logger_major_version 0 +#define logger_minor_version 1 + /** * Logger entries contain messages representing the particle data at a given * point in time during the simulation. @@ -82,16 +84,18 @@ enum logger_masks_number { logger_h = 4, logger_rho = 5, logger_consts = 6, - logger_timestamp = 7, /* expect it to be before count */ - logger_count_mask = 8, /* Need to be the last */ + logger_timestamp = 7, /* expect it to be before count. */ + logger_count_mask = 8, /* Need to be the last. */ } __attribute__((packed)); struct mask_data { - /* Number of bytes for a mask */ + /* Number of bytes for a mask. */ int size; - /* Mask value */ + + /* Mask value. */ unsigned int mask; - /* name of the mask */ + + /* Name of the mask. */ char name[100]; }; @@ -100,51 +104,52 @@ extern const struct mask_data logger_mask_data[logger_count_mask]; /* Size of the strings. */ #define logger_string_length 200 -/* structure containing global data */ -struct logger { - /* Number of particle steps between dumping a chunk of data */ +/* structure containing global data. */ +struct logger_writer { + /* Number of particle steps between dumping a chunk of data. */ short int delta_step; - /* Logger basename */ + /* Logger basename. */ char base_name[logger_string_length]; - /* Dump file */ + /* Dump file (In the reader, the dump is cleaned, therefore it is renamed + * logfile). */ struct dump dump; - /* timestamp offset for logger*/ + /* timestamp offset for logger. */ size_t timestamp_offset; - /* scaling factor when buffer is too small */ + /* scaling factor when buffer is too small. */ float buffer_scale; - /* Size of a chunk if every mask are activated */ + /* Size of a chunk if every mask are activated. */ int max_chunk_size; } SWIFT_STRUCT_ALIGN; -/* required structure for each particle type */ +/* required structure for each particle type. */ struct logger_part_data { - /* Number of particle updates since last output */ + /* Number of particle updates since last output. */ int steps_since_last_output; - /* offset of last particle log entry */ + /* offset of last particle log entry. */ size_t last_offset; }; /* Function prototypes. */ int logger_compute_chunk_size(unsigned int mask); -void logger_log_all(struct logger *log, const struct engine *e); -void logger_log_part(struct logger *log, const struct part *p, +void logger_log_all(struct logger_writer *log, const struct engine *e); +void logger_log_part(struct logger_writer *log, const struct part *p, unsigned int mask, size_t *offset); -void logger_log_gpart(struct logger *log, const struct gpart *p, +void logger_log_gpart(struct logger_writer *log, const struct gpart *p, unsigned int mask, size_t *offset); -void logger_init(struct logger *log, struct swift_params *params); -void logger_clean(struct logger *log); -void logger_log_timestamp(struct logger *log, integertime_t t, double time, - size_t *offset); -void logger_ensure_size(struct logger *log, size_t total_nr_parts, +void logger_init(struct logger_writer *log, struct swift_params *params); +void logger_free(struct logger_writer *log); +void logger_log_timestamp(struct logger_writer *log, integertime_t t, + double time, size_t *offset); +void logger_ensure_size(struct logger_writer *log, size_t total_nr_parts, size_t total_nr_gparts, size_t total_nr_sparts); -void logger_write_file_header(struct logger *log, const struct engine *e); +void logger_write_file_header(struct logger_writer *log); int logger_read_part(struct part *p, size_t *offset, const char *buff); int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff); @@ -164,12 +169,14 @@ INLINE static void logger_part_data_init(struct logger_part_data *logger) { /** * @brief Should this particle write its data now ? * - * @param xp The #xpart. - * @param e The #engine containing information about the current time. - * @return 1 if the #part should write, 0 otherwise. + * @param logger_data The #logger_part_data of a particle. + * @param log The #logger. + * + * @return 1 if the particule should be writen, 0 otherwise. */ __attribute__((always_inline)) INLINE static int logger_should_write( - const struct logger_part_data *logger_data, const struct logger *log) { + const struct logger_part_data *logger_data, + const struct logger_writer *log) { return (logger_data->steps_since_last_output > log->delta_step); } diff --git a/src/logger_io.c b/src/logger_io.c index 3cef3497b2912411cea6763f5418bc76a7f5ece0..c6be1f292434c759e20064542e91caa2cd238a4d 100644 --- a/src/logger_io.c +++ b/src/logger_io.c @@ -21,7 +21,7 @@ /* Config parameters. */ #include "../config.h" -#ifdef WITH_LOGGER +#if defined(WITH_LOGGER) && defined(HAVE_HDF5) && !defined(WITH_MPI) /* Some standard headers. */ #include <hdf5.h> @@ -87,7 +87,7 @@ void write_index_single(struct engine* e, const char* baseName, // struct spart* sparts = e->s->sparts; static int outputCount = 0; - struct logger* log = e->logger; + struct logger_writer* log = e->logger; /* Number of unassociated gparts */ const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0; @@ -296,4 +296,4 @@ void write_index_single(struct engine* e, const char* baseName, ++outputCount; } -#endif /* HAVE_HDF5 */ +#endif /* WITH_LOGGER && HAVE_HDF5 && !WITH_MPI */ diff --git a/src/logger_io.h b/src/logger_io.h index f5b1274fb7b957d5b48bc8425bf784c586ac6a08..a424c5c104b9f1090c69f7e0bb37e72635636f82 100644 --- a/src/logger_io.h +++ b/src/logger_io.h @@ -50,11 +50,13 @@ __attribute__((always_inline)) INLINE static void hydro_write_index( *num_fields = 2; /* List what we want to write */ - list[0] = io_make_output_field("ParticleIDs", ULONGLONG, 1, - UNIT_CONV_NO_UNITS, parts, id); + list[0] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, id, "will be erased"); - list[1] = io_make_output_field("Offset", ULONGLONG, 1, UNIT_CONV_NO_UNITS, - xparts, logger_data.last_offset); + list[1] = + io_make_output_field("Offset", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + xparts, logger_data.last_offset, "will be erased"); } #endif diff --git a/src/multipole.h b/src/multipole.h index e867dfd4e2cc5c9fcd06d7d95dcf76a97689c2b3..bbe8c49db544425f4f35d57884c6036d3f83c905 100644 --- a/src/multipole.h +++ b/src/multipole.h @@ -117,6 +117,9 @@ struct multipole { /*! Minimal velocity along each axis of all #gpart */ float min_delta_vel[3]; + /*! Maximal co-moving softening of all the #gpart in the mulipole */ + float max_softening; + /* 0th order term */ float M_000; @@ -459,6 +462,7 @@ INLINE static void gravity_multipole_init(struct multipole *m) { */ INLINE static void gravity_multipole_print(const struct multipole *m) { + printf("eps_max = %12.5e\n", m->max_softening); printf("Vel= [%12.5e %12.5e %12.5e]\n", m->vel[0], m->vel[1], m->vel[2]); printf("-------------------------\n"); printf("M_000= %12.5e\n", m->M_000); @@ -512,6 +516,9 @@ INLINE static void gravity_multipole_print(const struct multipole *m) { INLINE static void gravity_multipole_add(struct multipole *restrict ma, const struct multipole *restrict mb) { + /* Maximum of both softenings */ + ma->max_softening = max(ma->max_softening, mb->max_softening); + /* Add 0th order term */ ma->M_000 += mb->M_000; @@ -630,6 +637,14 @@ INLINE static int gravity_multipole_equal(const struct gravity_tensors *ga, const double v2 = ma->vel[0] * ma->vel[0] + ma->vel[1] * ma->vel[1] + ma->vel[2] * ma->vel[2]; + /* Check maximal softening */ + if (fabsf(ma->max_softening - mb->max_softening) / + fabsf(ma->max_softening + mb->max_softening) > + tolerance) { + message("max softening different!"); + return 0; + } + /* Check bulk velocity (if non-zero and component > 1% of norm)*/ if (fabsf(ma->vel[0] + mb->vel[0]) > 1e-10 && (ma->vel[0] * ma->vel[0]) > 0.0001 * v2 && @@ -1022,11 +1037,14 @@ INLINE static int gravity_multipole_equal(const struct gravity_tensors *ga, * @param multi The #multipole (content will be overwritten). * @param gparts The #gpart. * @param gcount The number of particles. + * @param grav_props The properties of the gravity scheme. */ INLINE static void gravity_P2M(struct gravity_tensors *multi, - const struct gpart *gparts, int gcount) { + const struct gpart *gparts, const int gcount, + const struct gravity_props *const grav_props) { /* Temporary variables */ + float epsilon_max = 0.f; double mass = 0.0; double com[3] = {0.0, 0.0, 0.0}; double vel[3] = {0.f, 0.f, 0.f}; @@ -1034,12 +1052,14 @@ INLINE static void gravity_P2M(struct gravity_tensors *multi, /* Collect the particle data for CoM. */ for (int k = 0; k < gcount; k++) { const double m = gparts[k].mass; + const float epsilon = gravity_get_softening(&gparts[k], grav_props); #ifdef SWIFT_DEBUG_CHECKS if (gparts[k].time_bin == time_bin_inhibited) error("Inhibited particle in P2M. Should have been removed earlier."); #endif + epsilon_max = max(epsilon_max, epsilon); mass += m; com[0] += gparts[k].x[0] * m; com[1] += gparts[k].x[1] * m; @@ -1203,6 +1223,7 @@ INLINE static void gravity_P2M(struct gravity_tensors *multi, #endif /* Store the data on the multipole. */ + multi->m_pole.max_softening = epsilon_max; multi->m_pole.M_000 = mass; multi->r_max = sqrt(r_max2); multi->CoM[0] = com[0]; @@ -1316,6 +1337,9 @@ INLINE static void gravity_M2M(struct multipole *restrict m_a, const struct multipole *restrict m_b, const double pos_a[3], const double pos_b[3]) { + /* "shift" the softening */ + m_a->max_softening = m_b->max_softening; + /* Shift 0th order term */ m_a->M_000 = m_b->M_000; @@ -1964,8 +1988,7 @@ INLINE static void gravity_M2L_nonsym( const int periodic, const double dim[3], const float rs_inv) { /* Recover some constants */ - const float eps = props->epsilon_cur; - const float eps_inv = props->epsilon_cur_inv; + const float eps = m_a->max_softening; /* Compute distance vector */ float dx = (float)(pos_b[0] - pos_a[0]); @@ -1985,8 +2008,8 @@ INLINE static void gravity_M2L_nonsym( /* Compute all derivatives */ struct potential_derivatives_M2L pot; - potential_derivatives_compute_M2L(dx, dy, dz, r2, r_inv, eps, eps_inv, - periodic, rs_inv, &pot); + potential_derivatives_compute_M2L(dx, dy, dz, r2, r_inv, eps, periodic, + rs_inv, &pot); /* Do the M2L tensor multiplication */ gravity_M2L_apply(l_b, m_a, &pot); @@ -2015,8 +2038,7 @@ INLINE static void gravity_M2L_symmetric( const float rs_inv) { /* Recover some constants */ - const float eps = props->epsilon_cur; - const float eps_inv = props->epsilon_cur_inv; + const float eps = max(m_a->max_softening, m_b->max_softening); /* Compute distance vector */ float dx = (float)(pos_b[0] - pos_a[0]); @@ -2036,8 +2058,8 @@ INLINE static void gravity_M2L_symmetric( /* Compute all derivatives */ struct potential_derivatives_M2L pot; - potential_derivatives_compute_M2L(dx, dy, dz, r2, r_inv, eps, eps_inv, - periodic, rs_inv, &pot); + potential_derivatives_compute_M2L(dx, dy, dz, r2, r_inv, eps, periodic, + rs_inv, &pot); /* Do the first M2L tensor multiplication */ gravity_M2L_apply(l_b, m_a, &pot); @@ -2551,23 +2573,31 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb, * We use the multipole acceptance criterion of Dehnen, 2002, JCoPh, Volume 179, * Issue 1, pp.27-42, equation 10. * + * We also additionally check that the distance between the multipoles + * is larger than the softening lengths (here the distance at which + * the gravity becomes Newtonian again, not the Plummer-equivalent quantity). + * * @param r_crit_a The size of the multipole A. * @param r_crit_b The size of the multipole B. * @param theta_crit2 The square of the critical opening angle. * @param r2 Square of the distance (periodically wrapped) between the * multipoles. + * @param epsilon_a The maximal softening length of any particle in A. + * @param epsilon_b The maximal softening length of any particle in B. */ __attribute__((always_inline, const)) INLINE static int gravity_M2L_accept( const double r_crit_a, const double r_crit_b, const double theta_crit2, - const double r2) { + const double r2, const double epsilon_a, const double epsilon_b) { const double size = r_crit_a + r_crit_b; const double size2 = size * size; + const double epsilon_a2 = epsilon_a * epsilon_a; + const double epsilon_b2 = epsilon_b * epsilon_b; // MATTHIEU: Make this mass-dependent ? /* Multipole acceptance criterion (Dehnen 2002, eq.10) */ - return (r2 * theta_crit2 > size2); + return (r2 * theta_crit2 > size2) && (r2 > epsilon_a2) && (r2 > epsilon_b2); } /** @@ -2577,18 +2607,24 @@ __attribute__((always_inline, const)) INLINE static int gravity_M2L_accept( * We use the multipole acceptance criterion of Dehnen, 2002, JCoPh, Volume 179, * Issue 1, pp.27-42, equation 10. * + * We also additionally check that the distance between the particle and the + * multipole is larger than the softening length (here the distance at which + * the gravity becomes Newtonian again, not the Plummer-equivalent quantity). + * * @param r_max2 The square of the size of the multipole. * @param theta_crit2 The square of the critical opening angle. * @param r2 Square of the distance (periodically wrapped) between the * particle and the multipole. + * @param epsilon The softening length of the particle. */ __attribute__((always_inline, const)) INLINE static int gravity_M2P_accept( - const float r_max2, const float theta_crit2, const float r2) { + const float r_max2, const float theta_crit2, const float r2, + const float epsilon) { // MATTHIEU: Make this mass-dependent ? /* Multipole acceptance criterion (Dehnen 2002, eq.10) */ - return (r2 * theta_crit2 > r_max2); + return (r2 * theta_crit2 > r_max2) && (r2 > epsilon * epsilon); } #endif /* SWIFT_MULTIPOLE_H */ diff --git a/src/parallel_io.c b/src/parallel_io.c index db1fbe2426a3e858dd429aa495641a4e0d4ed631..d469de729bd08c79889b031e9d25d796cabad28e 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -45,6 +45,7 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "fof_io.h" #include "gravity_io.h" #include "gravity_properties.h" #include "hydro_io.h" @@ -426,18 +427,18 @@ void prepareArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, if (h_data < 0) error("Error while creating dataspace '%s'.", props.name); /* Write unit conversion factors for this data set */ - char buffer[FIELD_BUFFER_SIZE]; - units_cgs_conversion_string(buffer, snapshot_units, props.units); + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, snapshot_units, props.units, + props.scale_factor_exponent); float baseUnitsExp[5]; units_get_base_unit_exponents_array(baseUnitsExp, props.units); - const float a_factor_exp = units_a_factor(snapshot_units, props.units); io_write_attribute_f(h_data, "U_M exponent", baseUnitsExp[UNIT_MASS]); io_write_attribute_f(h_data, "U_L exponent", baseUnitsExp[UNIT_LENGTH]); io_write_attribute_f(h_data, "U_t exponent", baseUnitsExp[UNIT_TIME]); io_write_attribute_f(h_data, "U_I exponent", baseUnitsExp[UNIT_CURRENT]); io_write_attribute_f(h_data, "U_T exponent", baseUnitsExp[UNIT_TEMPERATURE]); - io_write_attribute_f(h_data, "h-scale exponent", 0); - io_write_attribute_f(h_data, "a-scale exponent", a_factor_exp); + io_write_attribute_f(h_data, "h-scale exponent", 0.f); + io_write_attribute_f(h_data, "a-scale exponent", props.scale_factor_exponent); io_write_attribute_s(h_data, "Expression for physical CGS units", buffer); /* Write the actual number this conversion factor corresponds to */ @@ -449,8 +450,16 @@ void prepareArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, factor); io_write_attribute_d( h_data, - "Conversion factor to phyical CGS (including cosmological corrections)", - factor * pow(e->cosmology->a, a_factor_exp)); + "Conversion factor to physical CGS (including cosmological corrections)", + factor * pow(e->cosmology->a, props.scale_factor_exponent)); + +#ifdef SWIFT_DEBUG_CHECKS + if (strlen(props.description) == 0) + error("Invalid (empty) description of the field '%s'", props.name); +#endif + + /* Write the full description */ + io_write_attribute_s(h_data, "Description", props.description); /* Add a line to the XMF */ if (xmfFile != NULL) @@ -671,6 +680,8 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, * @param bparts (output) The array of #bpart read from the file. * @param Ngas (output) The number of particles read from the file. * @param Ngparts (output) The number of particles read from the file. + * @param Ngparts_background (output) The number of background DM particles read + * from the file. * @param Nstars (output) The number of particles read from the file. * @param Nblackholes (output) The number of particles read from the file. * @param flag_entropy (output) 1 if the ICs contained Entropy in the @@ -679,6 +690,7 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, * @param with_gravity Are we running with gravity ? * @param with_stars Are we running with stars ? * @param with_black_holes Are we running with black holes ? + * @param with_cosmology Are we running with cosmology ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -695,12 +707,13 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, void read_ic_parallel(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, struct bpart** bparts, - size_t* Ngas, size_t* Ngparts, size_t* Nstars, - size_t* Nblackholes, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, int with_black_holes, - int cleanup_h, int cleanup_sqrt_a, double h, double a, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, - int n_threads, int dry_run) { + size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, + size_t* Nstars, size_t* Nblackholes, int* flag_entropy, + int with_hydro, int with_gravity, int with_stars, + int with_black_holes, int with_cosmology, int cleanup_h, + int cleanup_sqrt_a, double h, double a, int mpi_rank, + int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads, + int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -712,9 +725,11 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, long long offset[swift_type_count] = {0}; int dimension = 3; /* Assume 3D if nothing is specified */ size_t Ndm = 0; + size_t Ndm_background = 0; /* Initialise counters */ - *Ngas = 0, *Ngparts = 0, *Nstars = 0, *Nblackholes = 0; + *Ngas = 0, *Ngparts = 0, *Ngparts_background = 0, *Nstars = 0, + *Nblackholes = 0; /* Open file */ /* message("Opening file '%s' as IC.", fileName); */ @@ -761,6 +776,13 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, io_read_attribute(h_grp, "NumPart_Total_HighWord", LONGLONG, numParticles_highWord); + /* Check that the user is not doing something silly when they e.g. restart + * from a snapshot by asserting that the current scale-factor (from + * parameter file) and the redshift in the header are consistent */ + if (with_cosmology) { + io_assert_valid_header_cosmology(h_grp, a); + } + for (int ptype = 0; ptype < swift_type_count; ++ptype) N_total[ptype] = (numParticles[ptype]) + (numParticles_highWord[ptype] << 32); @@ -866,11 +888,14 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store gravity particles */ if (with_gravity) { - Ndm = N[1]; + Ndm = N[swift_type_dark_matter]; + Ndm_background = N[swift_type_dark_matter_background]; *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + + N[swift_type_dark_matter_background] + (with_stars ? N[swift_type_stars] : 0) + (with_black_holes ? N[swift_type_black_hole] : 0); + *Ngparts_background = Ndm_background; if (swift_memalign("gparts", (void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); @@ -919,6 +944,13 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, } break; + case swift_type_dark_matter_background: + if (with_gravity) { + Nparticles = Ndm_background; + darkmatter_read_particles(*gparts + Ndm, list, &num_fields); + } + break; + case swift_type_stars: if (with_stars) { Nparticles = *Nstars; @@ -959,17 +991,23 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, /* Prepare the DM particles */ io_prepare_dm_gparts(&tp, *gparts, Ndm); + /* Prepare the DM background particles */ + io_prepare_dm_background_gparts(&tp, *gparts + Ndm, Ndm_background); + /* Duplicate the hydro particles into gparts */ - if (with_hydro) io_duplicate_hydro_gparts(&tp, *parts, *gparts, *Ngas, Ndm); + if (with_hydro) + io_duplicate_hydro_gparts(&tp, *parts, *gparts, *Ngas, + Ndm + Ndm_background); /* Duplicate the stars particles into gparts */ if (with_stars) - io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, Ndm + *Ngas); + io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, + Ndm + Ndm_background + *Ngas); /* Duplicate the stars particles into gparts */ if (with_black_holes) io_duplicate_black_holes_gparts(&tp, *bparts, *gparts, *Nblackholes, - Ndm + *Ngas + *Nstars); + Ndm + Ndm_background + *Ngas + *Nstars); threadpool_clean(&tp); } @@ -1008,6 +1046,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; const int with_temperature = e->policy & engine_policy_temperature; + const int with_fof = e->policy & engine_policy_fof; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -1206,6 +1245,9 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], with_cosmology); num_fields += star_formation_write_particles(parts, xparts, list + num_fields); + if (with_fof) { + num_fields += fof_write_parts(parts, xparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_parts(parts, xparts, list + num_fields); @@ -1214,6 +1256,20 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], case swift_type_dark_matter: darkmatter_write_particles(gparts, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts, list + num_fields); + } + if (with_stf) { + num_fields += velociraptor_write_gparts(e->s->gpart_group_data, + list + num_fields); + } + break; + + case swift_type_dark_matter_background: + darkmatter_write_particles(gparts, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_gparts(e->s->gpart_group_data, list + num_fields); @@ -1221,18 +1277,24 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], break; case swift_type_stars: - stars_write_particles(sparts, list, &num_fields); + stars_write_particles(sparts, list, &num_fields, with_cosmology); num_fields += chemistry_write_sparticles(sparts, list + num_fields); num_fields += tracers_write_sparticles(sparts, list + num_fields, with_cosmology); + if (with_fof) { + num_fields += fof_write_sparts(sparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_sparts(sparts, list + num_fields); } break; case swift_type_black_hole: - black_holes_write_particles(bparts, list, &num_fields); + black_holes_write_particles(bparts, list, &num_fields, with_cosmology); num_fields += chemistry_write_bparticles(bparts, list + num_fields); + if (with_fof) { + num_fields += fof_write_bparts(bparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_bparts(bparts, list + num_fields); } @@ -1247,7 +1309,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], /* Did the user cancel this field? */ char field[PARSER_MAX_LINE_SIZE]; - sprintf(field, "SelectOutput:%s_%s", list[i].name, + sprintf(field, "SelectOutput:%.*s_%s", FIELD_BUFFER_SIZE, list[i].name, part_type_names[ptype]); int should_write = parser_get_opt_param_int(params, field, 1); @@ -1306,6 +1368,8 @@ void write_output_parallel(struct engine* e, const char* baseName, const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; const int with_temperature = e->policy & engine_policy_temperature; + const int with_fof = e->policy & engine_policy_fof; + const int with_DM_background = e->s->with_DM_background; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -1321,6 +1385,11 @@ void write_output_parallel(struct engine* e, const char* baseName, // const size_t Nbaryons = Ngas + Nstars; // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; + size_t Ndm_background = 0; + if (with_DM_background) { + Ndm_background = io_count_dm_background_gparts(gparts, Ntot); + } + /* Number of particles that we will write */ const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts; @@ -1333,10 +1402,11 @@ void write_output_parallel(struct engine* e, const char* baseName, const size_t Nbaryons_written = Ngas_written + Nstars_written + Nblackholes_written; const size_t Ndm_written = - Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; + Ntot_written > 0 ? Ntot_written - Nbaryons_written - Ndm_background : 0; /* Compute offset in the file and total number of particles */ - size_t N[swift_type_count] = {Ngas_written, Ndm_written, 0, 0, + size_t N[swift_type_count] = {Ngas_written, Ndm_written, + Ndm_background, 0, Nstars_written, Nblackholes_written}; long long N_total[swift_type_count] = {0}; long long offset[swift_type_count] = {0}; @@ -1535,6 +1605,9 @@ void write_output_parallel(struct engine* e, const char* baseName, num_fields += cooling_write_particles( parts, xparts, list + num_fields, e->cooling_func); } + if (with_fof) { + num_fields += fof_write_parts(parts, xparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_parts(parts, xparts, list + num_fields); @@ -1573,6 +1646,10 @@ void write_output_parallel(struct engine* e, const char* baseName, cooling_write_particles(parts_written, xparts_written, list + num_fields, e->cooling_func); } + if (with_fof) { + num_fields += fof_write_parts(parts_written, xparts_written, + list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_parts( parts_written, xparts_written, list + num_fields); @@ -1590,6 +1667,9 @@ void write_output_parallel(struct engine* e, const char* baseName, /* This is a DM-only run without inhibited particles */ Nparticles = Ntot; darkmatter_write_particles(gparts, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_gparts(e->s->gpart_group_data, list + num_fields); @@ -1622,24 +1702,65 @@ void write_output_parallel(struct engine* e, const char* baseName, /* Select the fields to write */ darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts_written, list + num_fields); + } if (with_stf) { -#ifdef HAVE_VELOCIRAPTOR num_fields += velociraptor_write_gparts(gpart_group_data_written, list + num_fields); -#endif } } } break; + case swift_type_dark_matter_background: { + + /* Ok, we need to fish out the particles we want */ + Nparticles = Ndm_background; + + /* Allocate temporary array */ + if (swift_memalign("gparts_written", (void**)&gparts_written, + gpart_align, + Ndm_background * sizeof(struct gpart)) != 0) + error("Error while allocating temporart memory for gparts"); + + if (with_stf) { + if (swift_memalign( + "gpart_group_written", (void**)&gpart_group_data_written, + gpart_align, + Ndm_background * sizeof(struct velociraptor_gpart_data)) != 0) + error( + "Error while allocating temporart memory for gparts STF " + "data"); + } + + /* Collect the non-inhibited DM particles from gpart */ + io_collect_gparts_background_to_write( + gparts, e->s->gpart_group_data, gparts_written, + gpart_group_data_written, Ntot, Ndm_background, with_stf); + + /* Select the fields to write */ + darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_stf) { +#ifdef HAVE_VELOCIRAPTOR + num_fields += velociraptor_write_gparts(gpart_group_data_written, + list + num_fields); +#endif + } + + } break; + case swift_type_stars: { if (Nstars == Nstars_written) { /* No inhibted particles: easy case */ Nparticles = Nstars; - stars_write_particles(sparts, list, &num_fields); + stars_write_particles(sparts, list, &num_fields, with_cosmology); num_fields += chemistry_write_sparticles(sparts, list + num_fields); num_fields += tracers_write_sparticles(sparts, list + num_fields, with_cosmology); + if (with_fof) { + num_fields += fof_write_sparts(sparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_sparts(sparts, list + num_fields); } @@ -1659,10 +1780,14 @@ void write_output_parallel(struct engine* e, const char* baseName, Nstars_written); /* Select the fields to write */ - stars_write_particles(sparts_written, list, &num_fields); + stars_write_particles(sparts_written, list, &num_fields, + with_cosmology); num_fields += chemistry_write_sparticles(sparts, list + num_fields); num_fields += tracers_write_sparticles(sparts, list + num_fields, with_cosmology); + if (with_fof) { + num_fields += fof_write_sparts(sparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_sparts(sparts_written, list + num_fields); @@ -1675,9 +1800,12 @@ void write_output_parallel(struct engine* e, const char* baseName, /* No inhibted particles: easy case */ Nparticles = Nblackholes; - black_holes_write_particles(bparts, list, &num_fields); + black_holes_write_particles(bparts, list, &num_fields, + with_cosmology); num_fields += chemistry_write_bparticles(bparts, list + num_fields); - + if (with_fof) { + num_fields += fof_write_bparts(bparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_bparts(bparts, list + num_fields); } @@ -1697,9 +1825,12 @@ void write_output_parallel(struct engine* e, const char* baseName, Nblackholes_written); /* Select the fields to write */ - black_holes_write_particles(bparts_written, list, &num_fields); + black_holes_write_particles(bparts_written, list, &num_fields, + with_cosmology); num_fields += chemistry_write_bparticles(bparts, list + num_fields); - + if (with_fof) { + num_fields += fof_write_bparts(bparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_bparts(bparts_written, list + num_fields); @@ -1716,7 +1847,7 @@ void write_output_parallel(struct engine* e, const char* baseName, /* Did the user cancel this field? */ char field[PARSER_MAX_LINE_SIZE]; - sprintf(field, "SelectOutput:%s_%s", list[i].name, + sprintf(field, "SelectOutput:%.*s_%s", FIELD_BUFFER_SIZE, list[i].name, part_type_names[ptype]); int should_write = parser_get_opt_param_int(params, field, 1); diff --git a/src/parallel_io.h b/src/parallel_io.h index 6e6b52b74ebf166068f8dff110ea0ca0fd8464a2..6f1b397ef337c05da99a8017cc562212067e32a5 100644 --- a/src/parallel_io.h +++ b/src/parallel_io.h @@ -36,11 +36,12 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, struct bpart** bparts, - size_t* Ngas, size_t* Ngparts, size_t* Nsparts, - size_t* Nbparts, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, int with_black_holes, - int cleanup_h, int cleanup_sqrt_a, double h, double a, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, + size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, + size_t* Nsparts, size_t* Nbparts, int* flag_entropy, + int with_hydro, int with_gravity, int with_stars, + int with_black_holes, int with_cosmology, int cleanup_h, + int cleanup_sqrt_a, double h, double a, int mpi_rank, + int mpi_size, MPI_Comm comm, MPI_Info info, int nr_threads, int dry_run); void write_output_parallel(struct engine* e, const char* baseName, diff --git a/src/part.c b/src/part.c index 7d967caad18caf0c45d2a422fb4766d34f559d00..d8f57a8b3db3d5f29667dff162fb03d8b117d3a3 100644 --- a/src/part.c +++ b/src/part.c @@ -188,6 +188,15 @@ void part_verify_links(struct part *parts, struct gpart *gparts, error("DM gpart particle linked to something !"); } + /* We have a background DM particle */ + if (gparts[k].type == swift_type_dark_matter_background && + gparts[k].time_bin != time_bin_not_created) { + + /* Check that it's not linked */ + if (gparts[k].id_or_neg_offset <= 0) + error("Background DM gpart particle linked to something !"); + } + /* We have a gas particle */ else if (gparts[k].type == swift_type_gas) { diff --git a/src/part.h b/src/part.h index 820834d652cf53b3dce06192d7c552ec7681f2c7..440e5e1eb3415e65d7d9cc8b611bb98de107a1b2 100644 --- a/src/part.h +++ b/src/part.h @@ -95,6 +95,8 @@ #include "./gravity/Default/gravity_part.h" #elif defined(POTENTIAL_GRAVITY) #include "./gravity/Potential/gravity_part.h" +#elif defined(MULTI_SOFTENING_GRAVITY) +#include "./gravity/MultiSoftening/gravity_part.h" #else #error "Invalid choice of gravity variant" #endif diff --git a/src/part_type.c b/src/part_type.c index 1f96d4ef1db4b35a92d133e91498ea10ce472c70..5a2eee2ee2e4296f4abdd1dd3ae4927748484b15 100644 --- a/src/part_type.c +++ b/src/part_type.c @@ -20,5 +20,5 @@ /* This object's header. */ #include "part_type.h" -const char* part_type_names[swift_type_count] = {"Gas", "DM", "Dummy", - "Dummy", "Stars", "BH"}; +const char* part_type_names[swift_type_count] = { + "Gas", "DM", "DM_background", "Dummy", "Stars", "BH"}; diff --git a/src/part_type.h b/src/part_type.h index 901f47193fa0e72b362c8dce5199a1d0a20526c9..8311f5d17a7b838b9577a029387a5f01fb1a0801 100644 --- a/src/part_type.h +++ b/src/part_type.h @@ -27,6 +27,7 @@ enum part_type { swift_type_gas = 0, swift_type_dark_matter = 1, + swift_type_dark_matter_background = 2, swift_type_stars = 4, swift_type_black_hole = 5, swift_type_count diff --git a/src/potential.h b/src/potential.h index 59567fe92296068f838c39a3eb5ff55c14005d48..9d1418cadafcabe1d66658c43f2c1ef15778fc2b 100644 --- a/src/potential.h +++ b/src/potential.h @@ -46,6 +46,8 @@ #include "./potential/point_mass_ring/potential.h" #elif defined(EXTERNAL_POTENTIAL_POINTMASS_SOFT) #include "./potential/point_mass_softened/potential.h" +#elif defined(EXTERNAL_POTENTIAL_CONSTANT) +#include "./potential/constant/potential.h" #else #error "Invalid choice of external potential" #endif diff --git a/src/potential/constant/potential.h b/src/potential/constant/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..f91d2b3fc8a13792901fee680678c9b93af20cbf --- /dev/null +++ b/src/potential/constant/potential.h @@ -0,0 +1,143 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_CONSTANT_H +#define SWIFT_POTENTIAL_CONSTANT_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Constant acceleration. + */ +struct external_potential { + + /*! Value of the acceleration */ + double g[3]; +}; + +/** + * @brief Computes the time-step due to the acceleration from an constant + * acceleration. + * + * @param time The current time. + * @param potential The #external_potential used in the run. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float external_gravity_timestep( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, + const struct gpart* restrict g) { + + return FLT_MAX; +} + +/** + * @brief Computes the gravitational acceleration from an constant acceleration. + * + * @param time The current time. + * @param potential The #external_potential used in the run. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* potential, + const struct phys_const* const phys_const, struct gpart* g) { + + g->a_grav[0] += potential->g[0]; + g->a_grav[1] += potential->g[1]; + g->a_grav[2] += potential->g[2]; +} + +/** + * @brief Computes the gravitational potential energy of a particle in an + * constant acceleration. + * + * @param time The current time (unused here). + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param g Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + double time, const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* g) { + + const float gh = g->x[0] * potential->g[0] + g->x[1] * potential->g[1] + + g->x[2] * potential->g[2]; + + return g->mass * gh; +} + +/** + * @brief Initialises the external potential properties in the internal system + * of units. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param potential The external potential properties to initialize + */ +static INLINE void potential_init_backend( + struct swift_params* parameter_file, const struct phys_const* phys_const, + const struct unit_system* us, const struct space* s, + struct external_potential* potential) { + + /* Read in the acceleration */ + parser_get_param_double_array(parameter_file, "ConstantPotential:g_cgs", 3, + potential->g); + + /* Change the unit system */ + const double unit_length = units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + const double unit_time = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + const double unit_g = unit_length / (unit_time * unit_time); + + for (int i = 0; i < 3; i++) { + // Need to divide by G due to gravity_end_force + potential->g[i] /= unit_g * phys_const->const_newton_G; + } +} + +/** + * @brief Prints the properties of the external potential to stdout. + * + * @param potential The external potential properties. + */ +static INLINE void potential_print_backend( + const struct external_potential* potential) { + + message( + "External potential is 'Constant' with properties are g = (%e, " + "%e, %e)", + potential->g[0], potential->g[1], potential->g[2]); +} + +#endif /* SWIFT_CONSTANT_H */ diff --git a/src/pressure_floor.c b/src/pressure_floor.c new file mode 100644 index 0000000000000000000000000000000000000000..2901241f1a2ff42d526a0122008dcf6ab2d8ea6f --- /dev/null +++ b/src/pressure_floor.c @@ -0,0 +1,24 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ + +/* This object's header. */ +#include "pressure_floor.h" + +/* Pressure floor for the physics model. */ +struct pressure_floor_properties pressure_floor_props; diff --git a/src/pressure_floor.h b/src/pressure_floor.h new file mode 100644 index 0000000000000000000000000000000000000000..4389dfe0891dd6bfbc1e6730cac1c6ddcc920547 --- /dev/null +++ b/src/pressure_floor.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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_PRESSURE_FLOOR_H +#define SWIFT_PRESSURE_FLOOR_H + +/** + * @file src/pressure_floor.h + * @brief Branches between the different pressure floor models + */ + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "common_io.h" +#include "cosmology.h" +#include "error.h" +#include "inline.h" + +extern struct pressure_floor_properties pressure_floor_props; + +/* Check if pressure floor is implemented in hydro */ +#ifndef PRESSURE_FLOOR_NONE +#if defined(GADGET2_SPH) || defined(HOPKINS_PU_SPH) +/* Implemented */ +#else +#error Pressure floor not implemented with this hydro scheme +#endif + +#endif +/* Import the right pressure floor definition */ +#if defined(PRESSURE_FLOOR_NONE) +#include "./pressure_floor/none/pressure_floor.h" +#elif defined(PRESSURE_FLOOR_GEAR) +#include "./pressure_floor/GEAR/pressure_floor.h" +#endif + +#endif /* SWIFT_PRESSURE_FLOOR_H */ diff --git a/src/pressure_floor/GEAR/pressure_floor.h b/src/pressure_floor/GEAR/pressure_floor.h new file mode 100644 index 0000000000000000000000000000000000000000..de9ad6cf4450005becade48022be1be481fc1cc9 --- /dev/null +++ b/src/pressure_floor/GEAR/pressure_floor.h @@ -0,0 +1,229 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_FLOOR_GEAR_H +#define SWIFT_PRESSURE_FLOOR_GEAR_H + +/* Forward declaration */ +__attribute__((always_inline)) static INLINE float +pressure_floor_get_comoving_pressure(const struct part* p, const float pressure, + const struct cosmology* cosmo); +__attribute__((always_inline)) static INLINE float +pressure_floor_get_physical_pressure(const struct part* p, const float pressure, + const struct cosmology* cosmo); + +#include "adiabatic_index.h" +#include "cosmology.h" +#include "dimension.h" +#include "equation_of_state.h" +#include "hydro.h" +#include "hydro_properties.h" +#include "parser.h" +#include "part.h" +#include "units.h" + +/** + * @file src/pressure_floor/GEAR/pressure_floor.h + * @brief Pressure floor used in the GEAR model + */ + +/** + * @brief Properties of the pressure floor in the GEAR model. + */ +struct pressure_floor_properties { + + /*! Jeans factor */ + float n_jeans; + + /*! The constants in internal units (4 G N_jeans^(2/3) / PI) */ + float constants; +}; + +/** + * @brief Compute the physical pressure floor of a given #part. + * + * Note that the particle is not updated!! + * + * @param p The #part. + * @param pressure_physical The physical pressure without any pressure floor. + * @param cosmo The #cosmology model. + * + * @return The physical pressure with the floor. + */ +__attribute__((always_inline)) static INLINE float +pressure_floor_get_physical_pressure(const struct part* p, + const float pressure_physical, + const struct cosmology* cosmo) { + + const float H_phys = p->h * cosmo->a_inv * kernel_gamma; + const float rho = hydro_get_physical_density(p, cosmo); + + /* Compute the pressure floor */ + float floor = H_phys * H_phys * rho * pressure_floor_props.constants - + p->pressure_floor_data.sigma2; + floor *= rho * hydro_one_over_gamma; + + return fmaxf(pressure_physical, floor); +} + +/** + * @brief Compute the comoving pressure floor of a given #part. + * + * Note that the particle is not updated!! + * + * @param p The #part. + * @param pressure_comoving The comoving pressure without any pressure floor. + * @param cosmo The #cosmology model. + * + * @return The physical or comoving pressure with the floor. + */ +__attribute__((always_inline)) static INLINE float +pressure_floor_get_comoving_pressure(const struct part* p, + const float pressure_comoving, + const struct cosmology* cosmo) { + + const float a_coef = pow_three_gamma_minus_one(cosmo->a); + const float rho = hydro_get_comoving_density(p); + + /* Compute the pressure floor */ + float floor = kernel_gamma * kernel_gamma * p->h * p->h * rho * + pressure_floor_props.constants; + floor -= p->pressure_floor_data.sigma2 * cosmo->a * cosmo->a; + floor *= a_coef * rho * hydro_one_over_gamma; + + return fmaxf(pressure_comoving, floor); +} + +/** + * @brief Initialise the pressure floor by reading the parameters and converting + * to internal units. + * + * The input temperatures and number densities are converted to pressure and + * density assuming a neutral gas of primoridal abundance. + * + * @param params The YAML parameter file. + * @param us The system of units used internally. + * @param phys_const The physical constants. + * @param hydro_props The propoerties of the hydro scheme. + * @param props The pressure floor properties to fill. + */ +__attribute__((always_inline)) static INLINE void pressure_floor_init( + struct pressure_floor_properties* props, + const struct phys_const* phys_const, const struct unit_system* us, + const struct hydro_props* hydro_props, struct swift_params* params) { + + /* Read the Jeans factor */ + props->n_jeans = + parser_get_param_float(params, "GEARPressureFloor:Jeans_factor"); + + /* Compute the constants */ + props->constants = + 4.0 * M_1_PI * phys_const->const_newton_G * pow(props->n_jeans, 2. / 3.); +} + +/** + * @brief Print the properties of the pressure floor to stdout. + * + * @param props The pressure floor properties. + */ +__attribute__((always_inline)) static INLINE void pressure_floor_print( + const struct pressure_floor_properties* props) { + + message("Pressure floor is 'GEAR' with:"); + message("Jeans factor: %g", props->n_jeans); +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of pressure floor to the file + * @param h_grp The HDF5 group in which to write + */ +__attribute__((always_inline)) INLINE static void pressure_floor_print_snapshot( + hid_t h_grp) { + + io_write_attribute_s(h_grp, "Pressure floor", "GEAR"); +} + +/** + * @brief Finishes the density calculation. + * + * @param p The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void pressure_floor_end_density( + struct part* restrict p, const struct cosmology* cosmo) { + + /* To finish the turbulence estimation we devide by the density */ + p->pressure_floor_data.sigma2 /= + pow_dimension(p->h) * hydro_get_comoving_density(p); + + /* Add the cosmological term */ + p->pressure_floor_data.sigma2 *= cosmo->a2_inv; +} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +pressure_floor_part_has_no_neighbours(struct part* restrict p, + struct xpart* restrict xp, + const struct cosmology* cosmo) { + + /* If part has 0 neighbours, the estimation of turbulence is 0 */ + p->pressure_floor_data.sigma2 = 0.f; +} + +/** + * @brief Sets the pressure_floor properties of the (x-)particles to a valid + * start state. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void pressure_floor_init_part( + struct part* restrict p, struct xpart* restrict xp) { + p->pressure_floor_data.sigma2 = 0.f; +} + +/** + * @brief Sets the pressure_floor properties of the (x-)particles to a valid + * start state. + * + * @param phys_const The physical constant in internal units. + * @param us The unit system. + * @param cosmo The current cosmological model. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void +pressure_floor_first_init_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + struct part* restrict p, + struct xpart* restrict xp) { + + pressure_floor_init_part(p, xp); +} + +#endif +#endif /* SWIFT_PRESSURE_FLOOR_GEAR_H */ diff --git a/src/star_formation/EAGLE/star_formation_iact.h b/src/pressure_floor/GEAR/pressure_floor_iact.h similarity index 52% rename from src/star_formation/EAGLE/star_formation_iact.h rename to src/pressure_floor/GEAR/pressure_floor_iact.h index ab917cbe7aa67cad93a92a4b24212c5f1dcf3aeb..5ffb0b0097bb1d117e024f0e01e7babe1f838c40 100644 --- a/src/star_formation/EAGLE/star_formation_iact.h +++ b/src/pressure_floor/GEAR/pressure_floor_iact.h @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 2019 Fabien Jeanquartier (fabien.jeanquartier@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 @@ -16,18 +17,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_EAGLE_STAR_FORMATION_IACT_H -#define SWIFT_EAGLE_STAR_FORMATION_IACT_H +#ifndef SWIFT_GEAR_PRESSURE_FLOOR_IACT_H +#define SWIFT_GEAR_PRESSURE_FLOOR_IACT_H /** - * @file EAGLE/star_formation_iact.h + * @file GEAR/pressure_floor_iact.h * @brief Density computation */ /** - * @brief do star_formation computation after the runner_iact_density (symmetric + * @brief do pressure_floor computation after the runner_iact_density (symmetric * version) * + * Compute the velocity dispersion follow eq. 2 in Revaz & Jablonka 2018. + * * @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. @@ -37,16 +40,33 @@ * @param a Current scale factor. * @param H Current Hubble parameter. */ -__attribute__((always_inline)) INLINE static void runner_iact_star_formation( +__attribute__((always_inline)) INLINE static void runner_iact_pressure_floor( float r2, const float *dx, float hi, float hj, struct part *restrict pi, struct part *restrict pj, float a, float H) { - /* Nothing to do here. We do not need to compute any quantity in the hydro - density loop for the EAGLE star formation model. */ + float wi; + float wj; + /* Evaluation of the SPH kernel */ + kernel_eval(sqrt(r2) / hi, &wi); + kernel_eval(sqrt(r2) / hj, &wj); + + /* Delta v */ + float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; + + /* Compute the velocity dispersion */ + const float a2H = a * a * H; + const float sigma[3] = {dv[0] + a2H * dx[0], dv[1] + a2H * dx[1], + dv[2] + a2H * dx[2]}; + const float sigma2 = + sigma[0] * sigma[0] + sigma[1] * sigma[1] + sigma[2] * sigma[2]; + + /* Compute the velocity dispersion */ + pi->pressure_floor_data.sigma2 += sigma2 * wi * hydro_get_mass(pj); + pj->pressure_floor_data.sigma2 += sigma2 * wj * hydro_get_mass(pi); } /** - * @brief do star_formation computation after the runner_iact_density (non + * @brief do pressure_floor computation after the runner_iact_density (non * symmetric version) * * @param r2 Comoving square distance between the two particles. @@ -59,13 +79,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_star_formation( * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_star_formation(float r2, const float *dx, float hi, float hj, +runner_iact_nonsym_pressure_floor(float r2, const float *dx, float hi, float hj, struct part *restrict pi, const struct part *restrict pj, float a, float H) { + float wi; + /* Evaluation of the SPH kernel */ + kernel_eval(sqrt(r2) / hi, &wi); + + /* Delta v */ + float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; + + /* Compute the velocity dispersion */ + const float a2H = a * a * H; + const float sigma[3] = {dv[0] + a2H * dx[0], dv[1] + a2H * dx[1], + dv[2] + a2H * dx[2]}; + const float sigma2 = + sigma[0] * sigma[0] + sigma[1] * sigma[1] + sigma[2] * sigma[2]; - /* Nothing to do here. We do not need to compute any quantity in the hydro - density loop for the EAGLE star formation model. */ + /* Compute the velocity dispersion */ + pi->pressure_floor_data.sigma2 += sigma2 * wi * hydro_get_mass(pj); } -#endif /* SWIFT_EAGLE_STAR_FORMATION_IACT_H */ +#endif /* SWIFT_GEAR_PRESSURE_FLOOR_IACT_H */ diff --git a/src/pressure_floor/GEAR/pressure_floor_struct.h b/src/pressure_floor/GEAR/pressure_floor_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..1eb70b86dcfb79ce9818e1d7c598d49d5e654efd --- /dev/null +++ b/src/pressure_floor/GEAR/pressure_floor_struct.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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_PRESSURE_FLOOR_PART_GEAR_H +#define SWIFT_PRESSURE_FLOOR_PART_GEAR_H + +/** + * Structure containing the required variables for the pressure + * floor in the density loop. + */ +struct pressure_floor_part_data { + /*! Estimation of local turbulence (squared) + * Units: length^2 / time^2 (physical) */ + float sigma2; +}; + +#endif // SWIFT_PRESSURE_FLOOR_PART_GEAR_H diff --git a/src/pressure_floor/none/pressure_floor.h b/src/pressure_floor/none/pressure_floor.h new file mode 100644 index 0000000000000000000000000000000000000000..84db9cbaf682b36fa68abc7d26273e15cd1da191 --- /dev/null +++ b/src/pressure_floor/none/pressure_floor.h @@ -0,0 +1,161 @@ +/******************************************************************************* + * 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_PRESSURE_FLOOR_NONE_H +#define SWIFT_PRESSURE_FLOOR_NONE_H + +#include "adiabatic_index.h" +#include "cosmology.h" +#include "equation_of_state.h" +#include "hydro_properties.h" +#include "parser.h" +#include "part.h" +#include "units.h" + +/** + * @file src/pressure_floor/none/pressure_floor.h + * @brief Model without pressure floor + */ + +/** + * @brief Properties of the pressure floor in the NONE model. + */ +struct pressure_floor_properties {}; + +/** + * @brief Compute the physical pressure floor of a given #part. + * + * Note that the particle is not updated!! + * + * @param p The #part. + * @param physical_pressure The physical pressure without any pressure floor. + * @param cosmo The #cosmology model. + * + * @return The physical pressure with the floor. + */ +static INLINE float pressure_floor_get_physical_pressure( + const struct part* p, const float physical_pressure, + const struct cosmology* cosmo) { + return physical_pressure; +} + +/** + * @brief Compute the comoving pressure floor of a given #part. + * + * Note that the particle is not updated!! + * + * @param p The #part. + * @param comoving_pressure The comoving pressure without any pressure floor. + * @param cosmo The #cosmology model. + * + * @return The comoving pressure with the floor. + */ +static INLINE float pressure_floor_get_comoving_pressure( + const struct part* p, const float comoving_pressure, + const struct cosmology* cosmo) { + return comoving_pressure; +} + +/** + * @brief Initialise the pressure floor by reading the parameters and converting + * to internal units. + * + * The input temperatures and number densities are converted to pressure and + * density assuming a neutral gas of primoridal abundance. + * + * @param params The YAML parameter file. + * @param us The system of units used internally. + * @param phys_const The physical constants. + * @param hydro_props The propoerties of the hydro scheme. + * @param props The pressure floor properties to fill. + */ +static INLINE void pressure_floor_init(struct pressure_floor_properties* props, + const struct phys_const* phys_const, + const struct unit_system* us, + const struct hydro_props* hydro_props, + struct swift_params* params) {} + +/** + * @brief Print the properties of the pressure floor to stdout. + * + * @param props The pressure floor properties. + */ +static INLINE void pressure_floor_print( + const struct pressure_floor_properties* props) {} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of pressure floor to the file + * @param h_grp The HDF5 group in which to write + */ +INLINE static void pressure_floor_print_snapshot(hid_t h_grp) { + + io_write_attribute_s(h_grp, "Pressure floor", "none"); +} +#endif + +/** + * @brief Finishes the density calculation. + * + * @param p The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void pressure_floor_end_density( + struct part* restrict p, const struct cosmology* cosmo) {} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +pressure_floor_part_has_no_neighbours(struct part* restrict p, + struct xpart* restrict xp, + const struct cosmology* cosmo) {} + +/** + * @brief Sets the pressure_floor properties of the (x-)particles to a valid + * start state. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void pressure_floor_init_part( + struct part* restrict p, struct xpart* restrict xp) {} + +/** + * @brief Sets the pressure_floor properties of the (x-)particles to a valid + * start state. + * + * @param phys_const The physical constant in internal units. + * @param us The unit system. + * @param cosmo The current cosmological model. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void +pressure_floor_first_init_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + struct part* restrict p, + struct xpart* restrict xp) {} + +#endif /* SWIFT_PRESSURE_FLOOR_NONE_H */ diff --git a/src/star_formation/GEAR/star_formation_iact.h b/src/pressure_floor/none/pressure_floor_iact.h similarity index 76% rename from src/star_formation/GEAR/star_formation_iact.h rename to src/pressure_floor/none/pressure_floor_iact.h index 749b608068650a27cbe4c9a0ca4126d2740337f3..fedacfe06be1a6de2a87f14ce2b5a6306f29ac09 100644 --- a/src/star_formation/GEAR/star_formation_iact.h +++ b/src/pressure_floor/none/pressure_floor_iact.h @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 2019 Fabien Jeanquartier (fabien.jeanquartier@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 @@ -16,18 +17,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_GEAR_STAR_FORMATION_IACT_H -#define SWIFT_GEAR_STAR_FORMATION_IACT_H +#ifndef SWIFT_NONE_PRESSURE_FLOOR_IACT_H +#define SWIFT_NONE_PRESSURE_FLOOR_IACT_H /** - * @file GEAR/star_formation_iact.h + * @file NONE/pressure_floor_iact.h * @brief Density computation */ /** - * @brief do star_formation computation after the runner_iact_density (symmetric + * @brief do pressure_floor computation after the runner_iact_density (symmetric * version) * + * Compute the velocity dispersion follow eq. 2 in Revaz & Jablonka 2018. + * * @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. @@ -37,12 +40,12 @@ * @param a Current scale factor. * @param H Current Hubble parameter. */ -__attribute__((always_inline)) INLINE static void runner_iact_star_formation( +__attribute__((always_inline)) INLINE static void runner_iact_pressure_floor( float r2, const float *dx, float hi, float hj, struct part *restrict pi, struct part *restrict pj, float a, float H) {} /** - * @brief do star_formation computation after the runner_iact_density (non + * @brief do pressure_floor computation after the runner_iact_density (non * symmetric version) * * @param r2 Comoving square distance between the two particles. @@ -55,9 +58,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_star_formation( * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_star_formation(float r2, const float *dx, float hi, float hj, +runner_iact_nonsym_pressure_floor(float r2, const float *dx, float hi, float hj, struct part *restrict pi, const struct part *restrict pj, float a, float H) {} -#endif /* SWIFT_GEAR_STAR_FORMATION_IACT_H */ +#endif /* SWIFT_NONE_PRESSURE_FLOOR_IACT_H */ diff --git a/src/pressure_floor/none/pressure_floor_struct.h b/src/pressure_floor/none/pressure_floor_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..d2eee232faff5c74f2f4af6597d919a7901c3473 --- /dev/null +++ b/src/pressure_floor/none/pressure_floor_struct.h @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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_PRESSURE_FLOOR_PART_NONE_H +#define SWIFT_PRESSURE_FLOOR_PART_NONE_H + +/** + * Structure containing the required variables for the pressure + * floor in the density loop. + */ +struct pressure_floor_part_data {}; + +#endif // SWIFT_PRESSURE_FLOOR_PART_NONE_H diff --git a/src/pressure_floor_iact.h b/src/pressure_floor_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..258bf25a5a8429668c30b1d78532159ff32916b9 --- /dev/null +++ b/src/pressure_floor_iact.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_FLOOR_IACT_H +#define SWIFT_PRESSURE_FLOOR_IACT_H + +/** + * @file src/pressure_floor_iact.h + * @brief Branches between the different pressure floor iact. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right pressure floor definition */ +#if defined(PRESSURE_FLOOR_NONE) +#include "./pressure_floor/none/pressure_floor_iact.h" +#elif defined(PRESSURE_FLOOR_GEAR) +#include "./pressure_floor/GEAR/pressure_floor_iact.h" +#else +#error "Invalid choice of pressure floor" +#endif + +#endif /* SWIFT_PRESSURE_FLOOR_IACT_H */ diff --git a/src/pressure_floor_struct.h b/src/pressure_floor_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..e5538b37e03258fab45f9a2cb067f571ff7d16fd --- /dev/null +++ b/src/pressure_floor_struct.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_FLOOR_STRUCT_H +#define SWIFT_PRESSURE_FLOOR_STRUCT_H + +/** + * @file src/pressure_floor_struct.h + * @brief Branches between the different pressure floor data + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right pressure floor definition */ +#if defined(PRESSURE_FLOOR_NONE) +#include "./pressure_floor/none/pressure_floor_struct.h" +#elif defined(PRESSURE_FLOOR_GEAR) +#include "./pressure_floor/GEAR/pressure_floor_struct.h" +#else +#error "Invalid choice of pressure floor structure." +#endif + +#endif /* SWIFT_PRESSURE_FLOOR_STRUCT_H */ diff --git a/src/proxy.c b/src/proxy.c index ec7e8d3d63e6f9f53d6966ccb5a1e555cb6728fa..1104f88c66a61ed1211964be5f266c473cbfaaae 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -72,8 +72,10 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies, /* Run through the cells and get the size of the tags that will be sent off. */ int count_out = 0; - int *offset_out = malloc(s->nr_cells * sizeof(int)); - if (offset_out == NULL) error("Not enough memory to allocate offset_out array"); + int *offset_out = + (int *)swift_malloc("tags_offsets_out", s->nr_cells * sizeof(int)); + if (offset_out == NULL) error("Error allocating memory for tag offsets"); + for (int k = 0; k < s->nr_cells; k++) { offset_out[k] = count_out; if (s->cells_top[k].mpi.sendto) { @@ -86,8 +88,9 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies, /* Run through the proxies and get the count of incoming tags. */ int count_in = 0; - int *offset_in = malloc(s->nr_cells * sizeof(int)); - if (offset_in == NULL) error("Not enough memory to allocate offset_in array"); + int *offset_in = + (int *)swift_malloc("tags_offsets_in", s->nr_cells * sizeof(int)); + if (offset_in == NULL) error("Error allocating memory for tag offsets"); for (int k = 0; k < num_proxies; k++) { for (int j = 0; j < proxies[k].nr_cells_in; j++) { @@ -183,6 +186,8 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies, /* Clean up. */ swift_free("tags_in", tags_in); swift_free("tags_out", tags_out); + swift_free("tags_offsets_in", offset_in); + swift_free("tags_offsets_out", offset_out); free(reqs_in); free(cids_in); @@ -402,9 +407,10 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies, s->nr_cells, sizeof(struct cell), /*chunk=*/0, /*extra_data=*/NULL); int count_out = 0; - int *offset = malloc(s->nr_cells * sizeof(int)); - if (offset == NULL) error("Not enough memory to allocate offset array"); - + int *offset = + (int *)swift_malloc("proxy_cell_offset", s->nr_cells * sizeof(int)); + if (offset == NULL) error("Error allocating memory for proxy cell offsets"); + for (int k = 0; k < s->nr_cells; k++) { offset[k] = count_out; if (s->cells_top[k].mpi.sendto) count_out += s->cells_top[k].mpi.pcell_size; @@ -487,6 +493,7 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies, /* Clean up. */ free(reqs); swift_free("pcells", pcells); + swift_free("proxy_cell_offset", offset); #else error("SWIFT was not compiled with MPI support."); diff --git a/src/random.h b/src/random.h index e7fc77a24136b39584ab16d013b9939708c37b11..832c95a884ae9ae624b3f7723f3b11bc6052f646 100644 --- a/src/random.h +++ b/src/random.h @@ -20,11 +20,14 @@ #ifndef SWIFT_RANDOM_H #define SWIFT_RANDOM_H -/* COde configuration */ +/* Code configuration */ #include "../config.h" /* Standard header */ +#include <stdint.h> #include <stdlib.h> +#include <string.h> +#include <sys/types.h> /** * @brief The categories of random number generated. @@ -49,6 +52,78 @@ enum random_number_type { random_number_BH_swallow = 4947009007LL }; +#ifndef __APPLE__ + +#include <errno.h> +#include <ieee754.h> +#include <limits.h> + +/* Inline the default RNG functions to avoid costly function calls. These + functions are minor modifications, but functional equivalents, of their glibc + counterparts. */ + +INLINE static int inl_rand_r(uint32_t *seed) { + uint32_t next = *seed; + int result; + next *= 1103515245; + next += 12345; + result = (uint32_t)(next / 65536) % 2048; + next *= 1103515245; + next += 12345; + result <<= 10; + result ^= (uint32_t)(next / 65536) % 1024; + next *= 1103515245; + next += 12345; + result <<= 10; + result ^= (uint32_t)(next / 65536) % 1024; + *seed = next; + return result; +} + +INLINE static void inl_drand48_iterate(uint16_t xsubi[3]) { + uint64_t X; + uint64_t result; + const uint64_t __a = 0x5deece66dull; + const uint16_t __c = 0xb; + + /* Do the real work. We choose a data type which contains at least + 48 bits. Because we compute the modulus it does not care how + many bits really are computed. */ + X = (uint64_t)xsubi[2] << 32 | (uint32_t)xsubi[1] << 16 | xsubi[0]; + result = X * __a + __c; + xsubi[0] = result & 0xffff; + xsubi[1] = (result >> 16) & 0xffff; + xsubi[2] = (result >> 32) & 0xffff; +} + +INLINE static double inl_erand48(uint16_t xsubi[3]) { + union ieee754_double temp; + + /* Compute next state. */ + inl_drand48_iterate(xsubi); + + /* Construct a positive double with the 48 random bits distributed over + its fractional part so the resulting FP number is [0.0,1.0). */ + temp.ieee.negative = 0; + temp.ieee.exponent = IEEE754_DOUBLE_BIAS; + temp.ieee.mantissa0 = (xsubi[2] << 4) | (xsubi[1] >> 12); + temp.ieee.mantissa1 = ((xsubi[1] & 0xfff) << 20) | (xsubi[0] << 4); + + /* Please note the lower 4 bits of mantissa1 are always 0. */ + return temp.d - 1.0; +} + +#else + +/* In the case of OSX, we default to the platform's + default implementation. */ + +INLINE static int inl_rand_r(uint32_t *seed) { return rand_r(seed); } + +INLINE static double inl_erand48(uint16_t xsubi[3]) { return erand48(xsubi); } + +#endif + /** * @brief Returns a pseudo-random number in the range [0, 1[. * @@ -62,55 +137,38 @@ enum random_number_type { * @param type The #random_number_type to generate. * @return a random number in the interval [0, 1.[. */ -INLINE static double random_unit_interval(const long long int id, +INLINE static double random_unit_interval(int64_t id, const integertime_t ti_current, const enum random_number_type type) { + /* Start by packing the state into a sequence of 16-bit seeds for rand_r. */ + uint16_t buff[9]; + id += type; + memcpy(&buff[0], &id, 8); + memcpy(&buff[4], &ti_current, 8); + + /* The inputs give 16 bytes of state, but we need a multiple of 6 for the + calls to erand48(), so we add an additional aribrary constant two-byte + value to get 18 bytes of state. */ + buff[8] = 6178; + + /* Shuffle the buffer values, this will be our source of entropy for + the erand48 generator. */ + uint32_t seed16 = 0; + for (int k = 0; k < 9; k++) { + seed16 ^= buff[k]; + inl_rand_r(&seed16); + } + for (int k = 0; k < 9; k++) buff[k] ^= inl_rand_r(&seed16) & 0xffff; + + /* Do three steps of erand48() over the state generated previously. */ + uint16_t seed48[3] = {0, 0, 0}; + for (int k = 0; k < 3; k++) { + for (int j = 0; j < 3; j++) seed48[j] ^= buff[3 * k + j]; + inl_erand48(seed48); + } - /* Range used for the seeds. Best if prime */ - static const long long seed_range = RAND_MAX; - static const double RAND_MAX_inv = 1. / ((double)RAND_MAX); - static const long long mwc_number = (1LL << 32) - 1LL; - - /* Calculate the seed */ - /* WARNING: Only change the math if you really know what you are doing! - * The numbers are carefully chosen prime numbers that prevent correlation - * with either the current integer time or the particle IDs. The current - * method also prevents any correlation between different random number - * types. - * The calculation overflows on purpose. - * 1. The first step is calculating the seed by using a multiply with carry - * (MWC) method, this method depends on the type of random number and - * this therefore also prevents that there is any correlation between - * the different types of random numbers. - * 2. After this we use the 64 bit Xorshift method to randomize the seeds - * even more. - * 3. We calculate a prime multiplication for the id with a quadratic - * term. - * 4. We calculate the seed by using a Quadratic congruential generator, - * in which we use the id part and the current time step bin. - */ - unsigned long long number = ti_current; - - /* Multiply with carry (MWC), (adviced variables by NR) */ - number = 4294957665LL * (number & (mwc_number)) + (number >> 32); - - /* 64-bit Xorshift (adviced variables by NR) */ - number ^= number << 21; - number ^= number >> 35; - number ^= number << 4; - - /* Add constant to ID */ - const unsigned long long idt = id + type; - - /* Nonlinear congruential generator */ - const unsigned long long idpart = - 3457LL * idt + 593LL * idt * ti_current + 5417LL * idt * idt; - unsigned int seed = - (937LL * number + 5171LL * number * number + idpart + 1109LL) % - 9996361LL % seed_range; - - /* Generate a random number between 0 and 1. */ - return rand_r(&seed) * RAND_MAX_inv; + /* Generate one final value, this is our output. */ + return inl_erand48(seed48); } #endif /* SWIFT_RANDOM_H */ diff --git a/src/runner.c b/src/runner.c deleted file mode 100644 index d42ebb4e9f336305d39c5d9109a89b19bf717df3..0000000000000000000000000000000000000000 --- a/src/runner.c +++ /dev/null @@ -1,4788 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) - * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) - * 2016 John A. Regan (john.a.regan@durham.ac.uk) - * Tom Theuns (tom.theuns@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/>. - * - ******************************************************************************/ - -/* Config parameters. */ -#include "../config.h" - -/* Some standard headers. */ -#include <float.h> -#include <limits.h> -#include <stdlib.h> - -/* MPI headers. */ -#ifdef WITH_MPI -#include <mpi.h> -#endif - -/* This object's header. */ -#include "runner.h" - -/* Local headers. */ -#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" -#include "cooling.h" -#include "debug.h" -#include "drift.h" -#include "engine.h" -#include "entropy_floor.h" -#include "error.h" -#include "feedback.h" -#include "gravity.h" -#include "hydro.h" -#include "hydro_properties.h" -#include "kick.h" -#include "logger.h" -#include "memuse.h" -#include "minmax.h" -#include "runner_doiact_vec.h" -#include "scheduler.h" -#include "sort_part.h" -#include "space.h" -#include "space_getsid.h" -#include "star_formation.h" -#include "star_formation_iact.h" -#include "star_formation_logger.h" -#include "stars.h" -#include "task.h" -#include "timers.h" -#include "timestep.h" -#include "timestep_limiter.h" -#include "tracers.h" - -/* Unique identifier of loop types */ -#define TASK_LOOP_DENSITY 0 -#define TASK_LOOP_GRADIENT 1 -#define TASK_LOOP_FORCE 2 -#define TASK_LOOP_LIMITER 3 -#define TASK_LOOP_FEEDBACK 4 -#define TASK_LOOP_SWALLOW 5 - -/* Import the density loop functions. */ -#define FUNCTION density -#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY -#include "runner_doiact.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP - -/* Import the gradient loop functions (if required). */ -#ifdef EXTRA_HYDRO_LOOP -#define FUNCTION gradient -#define FUNCTION_TASK_LOOP TASK_LOOP_GRADIENT -#include "runner_doiact.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP -#endif - -/* Import the force loop functions. */ -#define FUNCTION force -#define FUNCTION_TASK_LOOP TASK_LOOP_FORCE -#include "runner_doiact.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP - -/* Import the limiter loop functions. */ -#define FUNCTION limiter -#define FUNCTION_TASK_LOOP TASK_LOOP_LIMITER -#include "runner_doiact.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP - -/* Import the gravity loop functions. */ -#include "runner_doiact_grav.h" - -/* Import the stars density loop functions. */ -#define FUNCTION density -#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY -#include "runner_doiact_stars.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION - -/* Import the stars feedback loop functions. */ -#define FUNCTION feedback -#define FUNCTION_TASK_LOOP TASK_LOOP_FEEDBACK -#include "runner_doiact_stars.h" -#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 swallow -#define FUNCTION_TASK_LOOP TASK_LOOP_SWALLOW -#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. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { - - struct spart *restrict sparts = c->stars.parts; - const struct engine *e = r->e; - const struct unit_system *us = e->internal_units; - const int with_cosmology = (e->policy & engine_policy_cosmology); - const struct cosmology *cosmo = e->cosmology; - const struct feedback_props *feedback_props = e->feedback_props; - const float stars_h_max = e->hydro_properties->h_max; - const float stars_h_min = e->hydro_properties->h_min; - const float eps = e->stars_properties->h_tolerance; - const float stars_eta_dim = - pow_dimension(e->stars_properties->eta_neighbours); - const int max_smoothing_iter = e->stars_properties->max_smoothing_iterations; - int redo = 0, scount = 0; - - /* Running value of the maximal smoothing length */ - double h_max = c->stars.h_max; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != e->nodeID) - error("Running the star ghost on a foreign node!"); -#endif - - /* Anything to do here? */ - if (c->stars.count == 0) return; - if (!cell_is_active_stars(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - runner_do_stars_ghost(r, c->progeny[k], 0); - - /* Update h_max */ - h_max = max(h_max, c->progeny[k]->stars.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->stars.count)) == NULL) - error("Can't allocate memory for sid."); - if ((h_0 = (float *)malloc(sizeof(float) * c->stars.count)) == NULL) - error("Can't allocate memory for h_0."); - if ((left = (float *)malloc(sizeof(float) * c->stars.count)) == NULL) - error("Can't allocate memory for left."); - if ((right = (float *)malloc(sizeof(float) * c->stars.count)) == NULL) - error("Can't allocate memory for right."); - for (int k = 0; k < c->stars.count; k++) - if (spart_is_active(&sparts[k], e) && - feedback_is_active(&sparts[k], e->time, cosmo, with_cosmology)) { - sid[scount] = k; - h_0[scount] = sparts[k].h; - left[scount] = 0.f; - right[scount] = stars_h_max; - ++scount; - } - - /* While there are particles that need to be updated... */ - for (int num_reruns = 0; scount > 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 < scount; i++) { - - /* Get a direct pointer on the part. */ - struct spart *sp = &sparts[sid[i]]; - -#ifdef SWIFT_DEBUG_CHECKS - /* Is this part within the timestep? */ - if (!spart_is_active(sp, e)) - error("Ghost applied to inactive particle"); -#endif - - /* Get some useful values */ - const float h_init = h_0[i]; - const float h_old = sp->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 (sp->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 */ - stars_end_density(sp, cosmo); - - /* Compute one step of the Newton-Raphson scheme */ - const float n_sum = sp->density.wcount * h_old_dim; - const float n_target = stars_eta_dim; - const float f = n_sum - n_target; - const float f_prime = - sp->density.wcount_dh * h_old_dim + - hydro_dimension * sp->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 (((sp->h >= stars_h_max) && (f < 0.f)) || - ((sp->h <= stars_h_min) && (f > 0.f))) { - - stars_reset_feedback(sp); - - /* Only do feedback if stars have a reasonable birth time */ - if (feedback_do_feedback(sp)) { - - const integertime_t ti_step = get_integer_timestep(sp->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(e->ti_current - 1, sp->time_bin); - - /* Get particle time-step */ - double dt; - if (with_cosmology) { - dt = cosmology_get_delta_time(e->cosmology, ti_begin, - ti_begin + ti_step); - } else { - dt = get_timestep(sp->time_bin, e->time_base); - } - - /* Calculate age of the star at current time */ - double star_age_end_of_step; - if (with_cosmology) { - star_age_end_of_step = - cosmology_get_delta_time_from_scale_factors( - cosmo, (double)sp->birth_scale_factor, cosmo->a); - } else { - star_age_end_of_step = (float)e->time - sp->birth_time; - } - - /* Has this star been around for a while ? */ - if (star_age_end_of_step > 0.) { - - /* Age of the star at the start of the step */ - const double star_age_beg_of_step = - max(star_age_end_of_step - dt, 0.); - - /* Compute the stellar evolution */ - feedback_evolve_spart(sp, feedback_props, cosmo, us, - star_age_beg_of_step, dt); - } else { - - /* Reset the feedback fields of the star particle */ - feedback_reset_feedback(sp, feedback_props); - } - } else { - - feedback_reset_feedback(sp, feedback_props); - } - - /* 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, sp->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 */ - sp->h = pow_inv_dimension( - 0.5f * (pow_dimension(left[i]) + pow_dimension(right[i]))); - - } else { - - /* Normal case */ - sp->h = h_new; - } - - /* If below the absolute maximum, try again */ - if (sp->h < stars_h_max && sp->h > stars_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 */ - stars_init_spart(sp); - feedback_init_spart(sp); - - /* Off we go ! */ - continue; - - } else if (sp->h <= stars_h_min) { - - /* Ok, this particle is a lost cause... */ - sp->h = stars_h_min; - - } else if (sp->h >= stars_h_max) { - - /* Ok, this particle is a lost cause... */ - sp->h = stars_h_max; - - /* Do some damage control if no neighbours at all were found */ - if (has_no_neighbours) { - stars_spart_has_no_neighbours(sp, 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, sp->h); - - stars_reset_feedback(sp); - - /* Only do feedback if stars have a reasonable birth time */ - if (feedback_do_feedback(sp)) { - - const integertime_t ti_step = get_integer_timestep(sp->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(e->ti_current - 1, sp->time_bin); - - /* Get particle time-step */ - double dt; - if (with_cosmology) { - dt = cosmology_get_delta_time(e->cosmology, ti_begin, - ti_begin + ti_step); - } else { - dt = get_timestep(sp->time_bin, e->time_base); - } - - /* Calculate age of the star at current time */ - double star_age_end_of_step; - if (with_cosmology) { - star_age_end_of_step = cosmology_get_delta_time_from_scale_factors( - cosmo, sp->birth_scale_factor, (float)cosmo->a); - } else { - star_age_end_of_step = (float)e->time - sp->birth_time; - } - - /* Has this star been around for a while ? */ - if (star_age_end_of_step > 0.) { - - /* Age of the star at the start of the step */ - const double star_age_beg_of_step = - max(star_age_end_of_step - dt, 0.); - - /* Compute the stellar evolution */ - feedback_evolve_spart(sp, feedback_props, cosmo, us, - star_age_beg_of_step, dt); - } else { - - /* Reset the feedback fields of the star particle */ - feedback_reset_feedback(sp, feedback_props); - } - } else { - - /* Reset the feedback fields of the star particle */ - feedback_reset_feedback(sp, feedback_props); - } - } - - /* We now need to treat the particles whose smoothing length had not - * converged again */ - - /* Re-set the counter for the next loop (potentially). */ - scount = redo; - if (scount > 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->stars.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_stars_density(r, finger, sparts, sid, - scount); - - /* Otherwise, pair interaction? */ - else if (l->t->type == task_type_pair) { - - /* Left or right? */ - if (l->t->ci == finger) - runner_dopair_subset_branch_stars_density( - r, finger, sparts, sid, scount, l->t->cj); - else - runner_dopair_subset_branch_stars_density( - r, finger, sparts, sid, scount, l->t->ci); - } - - /* Otherwise, sub-self interaction? */ - else if (l->t->type == task_type_sub_self) - runner_dosub_subset_stars_density(r, finger, sparts, sid, scount, - 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_stars_density(r, finger, sparts, sid, - scount, l->t->cj, 1); - else - runner_dosub_subset_stars_density(r, finger, sparts, sid, - scount, l->t->ci, 1); - } - } - } - } - } - - if (scount) { - error("Smoothing length failed to converge on %i particles.", scount); - } - - /* Be clean */ - free(left); - free(right); - free(sid); - free(h_0); - } - - /* Update h_max */ - c->stars.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->stars.ghost) { - for (struct cell *tmp = c->parent; tmp != NULL; tmp = tmp->parent) { - atomic_max_d(&tmp->stars.h_max, h_max); - } - } - - 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_density_ghost(struct runner *r, struct cell *c, - int timer) { - - struct bpart *restrict bparts = c->black_holes.parts; - const struct engine *e = r->e; - 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_density_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))) { - - 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 */ - - black_holes_reset_feedback(bp); - - /* Check if h_max has increased */ - h_max = max(h_max, bp->h); - } - - /* 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.density_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 Intermediate task after the BHs have done their swallowing step. - * This is used to update the BH quantities if necessary. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_black_holes_swallow_ghost(struct runner *r, struct cell *c, - int timer) { - - struct bpart *restrict bparts = c->black_holes.parts; - const int count = c->black_holes.count; - const struct engine *e = r->e; - const int with_cosmology = e->policy & engine_policy_cosmology; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) - runner_do_black_holes_swallow_ghost(r, c->progeny[k], 0); - } else { - - /* Loop over the parts in this cell. */ - for (int i = 0; i < count; i++) { - - /* Get a direct pointer on the part. */ - struct bpart *bp = &bparts[i]; - - if (bpart_is_active(bp, e)) { - - /* 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); - } - } - } - - if (timer) TIMER_TOC(timer_do_black_holes_ghost); -} - -/** - * @brief Calculate gravity acceleration from external potential - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void runner_do_grav_external(struct runner *r, struct cell *c, int timer) { - - struct gpart *restrict gparts = c->grav.parts; - const int gcount = c->grav.count; - const struct engine *e = r->e; - const struct external_potential *potential = e->external_potential; - const struct phys_const *constants = e->physical_constants; - const double time = r->e->time; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_gravity(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_grav_external(r, c->progeny[k], 0); - } else { - - /* Loop over the gparts in this cell. */ - for (int i = 0; i < gcount; i++) { - - /* Get a direct pointer on the part. */ - struct gpart *restrict gp = &gparts[i]; - - /* Is this part within the time step? */ - if (gpart_is_active(gp, e)) { - external_gravity_acceleration(time, potential, constants, gp); - } - } - } - - if (timer) TIMER_TOC(timer_dograv_external); -} - -/** - * @brief Calculate gravity accelerations from the periodic mesh - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void runner_do_grav_mesh(struct runner *r, struct cell *c, int timer) { - - struct gpart *restrict gparts = c->grav.parts; - const int gcount = c->grav.count; - const struct engine *e = r->e; - -#ifdef SWIFT_DEBUG_CHECKS - if (!e->s->periodic) error("Calling mesh forces in non-periodic mode."); -#endif - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_gravity(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_grav_mesh(r, c->progeny[k], 0); - } else { - - /* Get the forces from the gravity mesh */ - pm_mesh_interpolate_forces(e->mesh, e, gparts, gcount); - } - - if (timer) TIMER_TOC(timer_dograv_mesh); -} - -/** - * @brief Calculate change in thermal state of particles induced - * by radiative cooling and heating. - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void runner_do_cooling(struct runner *r, struct cell *c, int timer) { - - const struct engine *e = r->e; - const struct cosmology *cosmo = e->cosmology; - const int with_cosmology = (e->policy & engine_policy_cosmology); - const struct cooling_function_data *cooling_func = e->cooling_func; - const struct phys_const *constants = e->physical_constants; - const struct unit_system *us = e->internal_units; - const struct hydro_props *hydro_props = e->hydro_properties; - const struct entropy_floor_properties *entropy_floor_props = e->entropy_floor; - const double time_base = e->time_base; - const integertime_t ti_current = e->ti_current; - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - const int count = c->hydro.count; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_cooling(r, c->progeny[k], 0); - } else { - - /* Loop over the parts in this cell. */ - for (int i = 0; i < count; i++) { - - /* Get a direct pointer on the part. */ - struct part *restrict p = &parts[i]; - struct xpart *restrict xp = &xparts[i]; - - if (part_is_active(p, e)) { - - double dt_cool, dt_therm; - if (with_cosmology) { - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current - 1, p->time_bin); - - dt_cool = - cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); - dt_therm = cosmology_get_therm_kick_factor(e->cosmology, ti_begin, - ti_begin + ti_step); - - } else { - dt_cool = get_timestep(p->time_bin, time_base); - dt_therm = get_timestep(p->time_bin, time_base); - } - - /* Let's cool ! */ - cooling_cool_part(constants, us, cosmo, hydro_props, - entropy_floor_props, cooling_func, p, xp, dt_cool, - dt_therm); - } - } - } - - if (timer) TIMER_TOC(timer_do_cooling); -} - -/** - * - */ -void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { - - struct engine *e = r->e; - const struct cosmology *cosmo = e->cosmology; - const struct star_formation *sf_props = e->star_formation; - const struct phys_const *phys_const = e->physical_constants; - const int count = c->hydro.count; - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - const int with_cosmology = (e->policy & engine_policy_cosmology); - const int with_feedback = (e->policy & engine_policy_feedback); - const struct hydro_props *restrict hydro_props = e->hydro_properties; - const struct unit_system *restrict us = e->internal_units; - struct cooling_function_data *restrict cooling = e->cooling_func; - const struct entropy_floor_properties *entropy_floor = e->entropy_floor; - const double time_base = e->time_base; - const integertime_t ti_current = e->ti_current; - const int current_stars_count = c->stars.count; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != e->nodeID) - error("Running star formation task on a foreign node!"); -#endif - - /* Anything to do here? */ - if (c->hydro.count == 0 || !cell_is_active_hydro(c, e)) { - star_formation_logger_log_inactive_cell(&c->stars.sfh); - return; - } - - /* Reset the SFR */ - star_formation_logger_init(&c->stars.sfh); - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) { - /* Load the child cell */ - struct cell *restrict cp = c->progeny[k]; - - /* Do the recursion */ - runner_do_star_formation(r, cp, 0); - - /* Update current cell using child cells */ - star_formation_logger_add(&c->stars.sfh, &cp->stars.sfh); - } - } else { - - /* Loop over the gas particles in this cell. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - struct xpart *restrict xp = &xparts[k]; - - /* Only work on active particles */ - if (part_is_active(p, e)) { - - /* Is this particle star forming? */ - if (star_formation_is_star_forming(p, xp, sf_props, phys_const, cosmo, - hydro_props, us, cooling, - entropy_floor)) { - - /* Time-step size for this particle */ - double dt_star; - if (with_cosmology) { - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current - 1, p->time_bin); - - dt_star = - cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); - - } else { - dt_star = get_timestep(p->time_bin, time_base); - } - - /* Compute the SF rate of the particle */ - star_formation_compute_SFR(p, xp, sf_props, phys_const, cosmo, - dt_star); - - /* Add the SFR and SFR*dt to the SFH struct of this cell */ - star_formation_logger_log_active_part(p, xp, &c->stars.sfh, dt_star); - - /* Are we forming a star particle from this SF rate? */ - if (star_formation_should_convert_to_star(p, xp, sf_props, e, - dt_star)) { - - /* Convert the gas particle to a star particle */ - struct spart *sp = cell_convert_part_to_spart(e, c, p, xp); - - /* Did we get a star? (Or did we run out of spare ones?) */ - if (sp != NULL) { - - /* message("We formed a star id=%lld cellID=%d", sp->id, - * c->cellID); */ - - /* Copy the properties of the gas particle to the star particle */ - star_formation_copy_properties(p, xp, sp, e, sf_props, cosmo, - with_cosmology, phys_const, - hydro_props, us, cooling); - - /* Update the Star formation history */ - star_formation_logger_log_new_spart(sp, &c->stars.sfh); - } - } - - } else { /* Are we not star-forming? */ - - /* Update the particle to flag it as not star-forming */ - star_formation_update_part_not_SFR(p, xp, e, sf_props, - with_cosmology); - - } /* Not Star-forming? */ - - } else { /* is active? */ - - /* Check if the particle is not inhibited */ - if (!part_is_inhibited(p, e)) { - star_formation_logger_log_inactive_part(p, xp, &c->stars.sfh); - } - } - } /* Loop over particles */ - } - - /* If we formed any stars, the star sorts are now invalid. We need to - * re-compute them. */ - if (with_feedback && (c == c->top) && - (current_stars_count != c->stars.count)) { - cell_set_flag(c, cell_flag_do_stars_resort); - cell_clear_stars_sort_flags(c, /*clear_unused_flags=*/0); - } - - if (timer) TIMER_TOC(timer_do_star_formation); -} - -/** - * @brief Sorts again all the stars in a given cell hierarchy. - * - * This is intended to be used after the star formation task has been run - * to get the cells back into a state where self/pair star tasks can be run. - * - * @param r The thread #runner. - * @param c The top-level cell to run on. - * @param timer Are we timing this? - */ -void runner_do_stars_resort(struct runner *r, struct cell *c, const int timer) { - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != r->e->nodeID) error("Task must be run locally!"); - if (c->depth != 0) error("Task must be run at the top-level"); -#endif - - TIMER_TIC; - - /* Did we demand a recalculation of the stars'sorts? */ - if (cell_get_flag(c, cell_flag_do_stars_resort)) { - runner_do_all_stars_sort(r, c); - cell_clear_flag(c, cell_flag_do_stars_resort); - } - - if (timer) TIMER_TOC(timer_do_stars_resort); -} - -/** - * @brief Sort the entries in ascending order using QuickSort. - * - * @param sort The entries - * @param N The number of entries. - */ -void runner_do_sort_ascending(struct sort_entry *sort, int N) { - - struct { - short int lo, hi; - } qstack[10]; - int qpos, i, j, lo, hi, imin; - struct sort_entry temp; - float pivot; - - /* Sort parts in cell_i in decreasing order with quicksort */ - qstack[0].lo = 0; - qstack[0].hi = N - 1; - qpos = 0; - while (qpos >= 0) { - lo = qstack[qpos].lo; - hi = qstack[qpos].hi; - qpos -= 1; - if (hi - lo < 15) { - for (i = lo; i < hi; i++) { - imin = i; - for (j = i + 1; j <= hi; j++) - if (sort[j].d < sort[imin].d) imin = j; - if (imin != i) { - temp = sort[imin]; - sort[imin] = sort[i]; - sort[i] = temp; - } - } - } else { - pivot = sort[(lo + hi) / 2].d; - i = lo; - j = hi; - while (i <= j) { - while (sort[i].d < pivot) i++; - while (sort[j].d > pivot) j--; - if (i <= j) { - if (i < j) { - temp = sort[i]; - sort[i] = sort[j]; - sort[j] = temp; - } - i += 1; - j -= 1; - } - } - if (j > (lo + hi) / 2) { - if (lo < j) { - qpos += 1; - qstack[qpos].lo = lo; - qstack[qpos].hi = j; - } - if (i < hi) { - qpos += 1; - qstack[qpos].lo = i; - qstack[qpos].hi = hi; - } - } else { - if (i < hi) { - qpos += 1; - qstack[qpos].lo = i; - qstack[qpos].hi = hi; - } - if (lo < j) { - qpos += 1; - qstack[qpos].lo = lo; - qstack[qpos].hi = j; - } - } - } - } -} - -#ifdef SWIFT_DEBUG_CHECKS -/** - * @brief Recursively checks that the flags are consistent in a cell hierarchy. - * - * Debugging function. Exists in two flavours: hydro & stars. - */ -#define RUNNER_CHECK_SORTS(TYPE) \ - void runner_check_sorts_##TYPE(struct cell *c, int flags) { \ - \ - if (flags & ~c->TYPE.sorted) error("Inconsistent sort flags (downward)!"); \ - if (c->split) \ - for (int k = 0; k < 8; k++) \ - if (c->progeny[k] != NULL && c->progeny[k]->TYPE.count > 0) \ - runner_check_sorts_##TYPE(c->progeny[k], c->TYPE.sorted); \ - } -#else -#define RUNNER_CHECK_SORTS(TYPE) \ - void runner_check_sorts_##TYPE(struct cell *c, int flags) { \ - error("Calling debugging code without debugging flag activated."); \ - } -#endif - -RUNNER_CHECK_SORTS(hydro) -RUNNER_CHECK_SORTS(stars) - -/** - * @brief Sort the particles in the given cell along all cardinal directions. - * - * @param r The #runner. - * @param c The #cell. - * @param flags Cell flag. - * @param cleanup If true, re-build the sorts for the selected flags instead - * of just adding them. - * @param clock Flag indicating whether to record the timing or not, needed - * for recursive calls. - */ -void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, - int cleanup, int clock) { - - struct sort_entry *fingers[8]; - const int count = c->hydro.count; - const struct part *parts = c->hydro.parts; - struct xpart *xparts = c->hydro.xparts; - float buff[8]; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->hydro.super == NULL) error("Task called above the super level!!!"); -#endif - - /* We need to do the local sorts plus whatever was requested further up. */ - flags |= c->hydro.do_sort; - if (cleanup) { - c->hydro.sorted = 0; - } else { - flags &= ~c->hydro.sorted; - } - if (flags == 0 && !cell_get_flag(c, cell_flag_do_hydro_sub_sort)) return; - - /* Check that the particles have been moved to the current time */ - if (flags && !cell_are_part_drifted(c, r->e)) - error("Sorting un-drifted cell c->nodeID=%d", c->nodeID); - -#ifdef SWIFT_DEBUG_CHECKS - /* Make sure the sort flags are consistent (downward). */ - runner_check_sorts_hydro(c, c->hydro.sorted); - - /* Make sure the sort flags are consistent (upard). */ - for (struct cell *finger = c->parent; finger != NULL; - finger = finger->parent) { - if (finger->hydro.sorted & ~c->hydro.sorted) - error("Inconsistent sort flags (upward)."); - } - - /* Update the sort timer which represents the last time the sorts - were re-set. */ - if (c->hydro.sorted == 0) c->hydro.ti_sort = r->e->ti_current; -#endif - - /* Allocate memory for sorting. */ - cell_malloc_hydro_sorts(c, flags); - - /* Does this cell have any progeny? */ - if (c->split) { - - /* Fill in the gaps within the progeny. */ - float dx_max_sort = 0.0f; - float dx_max_sort_old = 0.0f; - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - - if (c->progeny[k]->hydro.count > 0) { - - /* Only propagate cleanup if the progeny is stale. */ - runner_do_hydro_sort( - r, c->progeny[k], flags, - cleanup && (c->progeny[k]->hydro.dx_max_sort_old > - space_maxreldx * c->progeny[k]->dmin), - 0); - dx_max_sort = max(dx_max_sort, c->progeny[k]->hydro.dx_max_sort); - dx_max_sort_old = - max(dx_max_sort_old, c->progeny[k]->hydro.dx_max_sort_old); - } else { - - /* We need to clean up the unused flags that were in case the - number of particles in the cell would change */ - cell_clear_hydro_sort_flags(c->progeny[k], /*clear_unused_flags=*/1); - } - } - } - c->hydro.dx_max_sort = dx_max_sort; - c->hydro.dx_max_sort_old = dx_max_sort_old; - - /* Loop over the 13 different sort arrays. */ - for (int j = 0; j < 13; j++) { - - /* Has this sort array been flagged? */ - if (!(flags & (1 << j))) continue; - - /* Init the particle index offsets. */ - int off[8]; - off[0] = 0; - for (int k = 1; k < 8; k++) - if (c->progeny[k - 1] != NULL) - off[k] = off[k - 1] + c->progeny[k - 1]->hydro.count; - else - off[k] = off[k - 1]; - - /* Init the entries and indices. */ - int inds[8]; - for (int k = 0; k < 8; k++) { - inds[k] = k; - if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0) { - fingers[k] = c->progeny[k]->hydro.sort[j]; - buff[k] = fingers[k]->d; - off[k] = off[k]; - } else - buff[k] = FLT_MAX; - } - - /* Sort the buffer. */ - for (int i = 0; i < 7; i++) - for (int k = i + 1; k < 8; k++) - if (buff[inds[k]] < buff[inds[i]]) { - int temp_i = inds[i]; - inds[i] = inds[k]; - inds[k] = temp_i; - } - - /* For each entry in the new sort list. */ - struct sort_entry *finger = c->hydro.sort[j]; - for (int ind = 0; ind < count; ind++) { - - /* Copy the minimum into the new sort array. */ - finger[ind].d = buff[inds[0]]; - finger[ind].i = fingers[inds[0]]->i + off[inds[0]]; - - /* Update the buffer. */ - fingers[inds[0]] += 1; - buff[inds[0]] = fingers[inds[0]]->d; - - /* Find the smallest entry. */ - for (int k = 1; k < 8 && buff[inds[k]] < buff[inds[k - 1]]; k++) { - int temp_i = inds[k - 1]; - inds[k - 1] = inds[k]; - inds[k] = temp_i; - } - - } /* Merge. */ - - /* Add a sentinel. */ - c->hydro.sort[j][count].d = FLT_MAX; - c->hydro.sort[j][count].i = 0; - - /* Mark as sorted. */ - atomic_or(&c->hydro.sorted, 1 << j); - - } /* loop over sort arrays. */ - - } /* progeny? */ - - /* Otherwise, just sort. */ - else { - - /* Reset the sort distance */ - if (c->hydro.sorted == 0) { -#ifdef SWIFT_DEBUG_CHECKS - if (xparts != NULL && c->nodeID != engine_rank) - error("Have non-NULL xparts in foreign cell"); -#endif - - /* And the individual sort distances if we are a local cell */ - if (xparts != NULL) { - for (int k = 0; k < count; k++) { - xparts[k].x_diff_sort[0] = 0.0f; - xparts[k].x_diff_sort[1] = 0.0f; - xparts[k].x_diff_sort[2] = 0.0f; - } - } - c->hydro.dx_max_sort_old = 0.f; - c->hydro.dx_max_sort = 0.f; - } - - /* Fill the sort array. */ - for (int k = 0; k < count; k++) { - const double px[3] = {parts[k].x[0], parts[k].x[1], parts[k].x[2]}; - for (int j = 0; j < 13; j++) - if (flags & (1 << j)) { - c->hydro.sort[j][k].i = k; - c->hydro.sort[j][k].d = px[0] * runner_shift[j][0] + - px[1] * runner_shift[j][1] + - px[2] * runner_shift[j][2]; - } - } - - /* Add the sentinel and sort. */ - for (int j = 0; j < 13; j++) - if (flags & (1 << j)) { - c->hydro.sort[j][count].d = FLT_MAX; - c->hydro.sort[j][count].i = 0; - runner_do_sort_ascending(c->hydro.sort[j], count); - atomic_or(&c->hydro.sorted, 1 << j); - } - } - -#ifdef SWIFT_DEBUG_CHECKS - /* Verify the sorting. */ - for (int j = 0; j < 13; j++) { - if (!(flags & (1 << j))) continue; - struct sort_entry *finger = c->hydro.sort[j]; - for (int k = 1; k < count; k++) { - if (finger[k].d < finger[k - 1].d) - error("Sorting failed, ascending array."); - if (finger[k].i >= count) error("Sorting failed, indices borked."); - } - } - - /* Make sure the sort flags are consistent (downward). */ - runner_check_sorts_hydro(c, flags); - - /* Make sure the sort flags are consistent (upward). */ - for (struct cell *finger = c->parent; finger != NULL; - finger = finger->parent) { - if (finger->hydro.sorted & ~c->hydro.sorted) - error("Inconsistent sort flags."); - } -#endif - - /* Clear the cell's sort flags. */ - c->hydro.do_sort = 0; - cell_clear_flag(c, cell_flag_do_hydro_sub_sort); - c->hydro.requires_sorts = 0; - - if (clock) TIMER_TOC(timer_dosort); -} - -/** - * @brief Sort the stars particles in the given cell along all cardinal - * directions. - * - * @param r The #runner. - * @param c The #cell. - * @param flags Cell flag. - * @param cleanup If true, re-build the sorts for the selected flags instead - * of just adding them. - * @param clock Flag indicating whether to record the timing or not, needed - * for recursive calls. - */ -void runner_do_stars_sort(struct runner *r, struct cell *c, int flags, - int cleanup, int clock) { - - struct sort_entry *fingers[8]; - const int count = c->stars.count; - struct spart *sparts = c->stars.parts; - float buff[8]; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->hydro.super == NULL) error("Task called above the super level!!!"); -#endif - - /* We need to do the local sorts plus whatever was requested further up. */ - flags |= c->stars.do_sort; - if (cleanup) { - c->stars.sorted = 0; - } else { - flags &= ~c->stars.sorted; - } - if (flags == 0 && !cell_get_flag(c, cell_flag_do_stars_sub_sort)) return; - - /* Check that the particles have been moved to the current time */ - if (flags && !cell_are_spart_drifted(c, r->e)) { - error("Sorting un-drifted cell c->nodeID=%d", c->nodeID); - } - -#ifdef SWIFT_DEBUG_CHECKS - /* Make sure the sort flags are consistent (downward). */ - runner_check_sorts_stars(c, c->stars.sorted); - - /* Make sure the sort flags are consistent (upward). */ - for (struct cell *finger = c->parent; finger != NULL; - finger = finger->parent) { - if (finger->stars.sorted & ~c->stars.sorted) - error("Inconsistent sort flags (upward)."); - } - - /* Update the sort timer which represents the last time the sorts - were re-set. */ - if (c->stars.sorted == 0) c->stars.ti_sort = r->e->ti_current; -#endif - - /* start by allocating the entry arrays in the requested dimensions. */ - cell_malloc_stars_sorts(c, flags); - - /* Does this cell have any progeny? */ - if (c->split) { - - /* Fill in the gaps within the progeny. */ - float dx_max_sort = 0.0f; - float dx_max_sort_old = 0.0f; - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - - if (c->progeny[k]->stars.count > 0) { - - /* Only propagate cleanup if the progeny is stale. */ - const int cleanup_prog = - cleanup && (c->progeny[k]->stars.dx_max_sort_old > - space_maxreldx * c->progeny[k]->dmin); - runner_do_stars_sort(r, c->progeny[k], flags, cleanup_prog, 0); - dx_max_sort = max(dx_max_sort, c->progeny[k]->stars.dx_max_sort); - dx_max_sort_old = - max(dx_max_sort_old, c->progeny[k]->stars.dx_max_sort_old); - } else { - - /* We need to clean up the unused flags that were in case the - number of particles in the cell would change */ - cell_clear_stars_sort_flags(c->progeny[k], /*clear_unused_flags=*/1); - } - } - } - c->stars.dx_max_sort = dx_max_sort; - c->stars.dx_max_sort_old = dx_max_sort_old; - - /* Loop over the 13 different sort arrays. */ - for (int j = 0; j < 13; j++) { - - /* Has this sort array been flagged? */ - if (!(flags & (1 << j))) continue; - - /* Init the particle index offsets. */ - int off[8]; - off[0] = 0; - for (int k = 1; k < 8; k++) - if (c->progeny[k - 1] != NULL) - off[k] = off[k - 1] + c->progeny[k - 1]->stars.count; - else - off[k] = off[k - 1]; - - /* Init the entries and indices. */ - int inds[8]; - for (int k = 0; k < 8; k++) { - inds[k] = k; - if (c->progeny[k] != NULL && c->progeny[k]->stars.count > 0) { - fingers[k] = c->progeny[k]->stars.sort[j]; - buff[k] = fingers[k]->d; - off[k] = off[k]; - } else - buff[k] = FLT_MAX; - } - - /* Sort the buffer. */ - for (int i = 0; i < 7; i++) - for (int k = i + 1; k < 8; k++) - if (buff[inds[k]] < buff[inds[i]]) { - int temp_i = inds[i]; - inds[i] = inds[k]; - inds[k] = temp_i; - } - - /* For each entry in the new sort list. */ - struct sort_entry *finger = c->stars.sort[j]; - for (int ind = 0; ind < count; ind++) { - - /* Copy the minimum into the new sort array. */ - finger[ind].d = buff[inds[0]]; - finger[ind].i = fingers[inds[0]]->i + off[inds[0]]; - - /* Update the buffer. */ - fingers[inds[0]] += 1; - buff[inds[0]] = fingers[inds[0]]->d; - - /* Find the smallest entry. */ - for (int k = 1; k < 8 && buff[inds[k]] < buff[inds[k - 1]]; k++) { - int temp_i = inds[k - 1]; - inds[k - 1] = inds[k]; - inds[k] = temp_i; - } - - } /* Merge. */ - - /* Add a sentinel. */ - c->stars.sort[j][count].d = FLT_MAX; - c->stars.sort[j][count].i = 0; - - /* Mark as sorted. */ - atomic_or(&c->stars.sorted, 1 << j); - - } /* loop over sort arrays. */ - - } /* progeny? */ - - /* Otherwise, just sort. */ - else { - - /* Reset the sort distance */ - if (c->stars.sorted == 0) { - - /* And the individual sort distances if we are a local cell */ - for (int k = 0; k < count; k++) { - sparts[k].x_diff_sort[0] = 0.0f; - sparts[k].x_diff_sort[1] = 0.0f; - sparts[k].x_diff_sort[2] = 0.0f; - } - c->stars.dx_max_sort_old = 0.f; - c->stars.dx_max_sort = 0.f; - } - - /* Fill the sort array. */ - for (int k = 0; k < count; k++) { - const double px[3] = {sparts[k].x[0], sparts[k].x[1], sparts[k].x[2]}; - for (int j = 0; j < 13; j++) - if (flags & (1 << j)) { - c->stars.sort[j][k].i = k; - c->stars.sort[j][k].d = px[0] * runner_shift[j][0] + - px[1] * runner_shift[j][1] + - px[2] * runner_shift[j][2]; - } - } - - /* Add the sentinel and sort. */ - for (int j = 0; j < 13; j++) - if (flags & (1 << j)) { - c->stars.sort[j][count].d = FLT_MAX; - c->stars.sort[j][count].i = 0; - runner_do_sort_ascending(c->stars.sort[j], count); - atomic_or(&c->stars.sorted, 1 << j); - } - } - -#ifdef SWIFT_DEBUG_CHECKS - /* Verify the sorting. */ - for (int j = 0; j < 13; j++) { - if (!(flags & (1 << j))) continue; - struct sort_entry *finger = c->stars.sort[j]; - for (int k = 1; k < count; k++) { - if (finger[k].d < finger[k - 1].d) - error("Sorting failed, ascending array."); - if (finger[k].i >= count) error("Sorting failed, indices borked."); - } - } - - /* Make sure the sort flags are consistent (downward). */ - runner_check_sorts_stars(c, flags); - - /* Make sure the sort flags are consistent (upward). */ - for (struct cell *finger = c->parent; finger != NULL; - finger = finger->parent) { - if (finger->stars.sorted & ~c->stars.sorted) - error("Inconsistent sort flags."); - } -#endif - - /* Clear the cell's sort flags. */ - c->stars.do_sort = 0; - cell_clear_flag(c, cell_flag_do_stars_sub_sort); - c->stars.requires_sorts = 0; - - if (clock) TIMER_TOC(timer_do_stars_sort); -} - -/** - * @brief Recurse into a cell until reaching the super level and call - * the hydro sorting function there. - * - * This function must be called at or above the super level! - * - * This function will sort the particles in all 13 directions. - * - * @param r the #runner. - * @param c the #cell. - */ -void runner_do_all_hydro_sort(struct runner *r, struct cell *c) { - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != engine_rank) error("Function called on a foreign cell!"); -#endif - - if (!cell_is_active_hydro(c, r->e)) return; - - /* Shall we sort at this level? */ - if (c->hydro.super == c) { - - /* Sort everything */ - runner_do_hydro_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); - - } else { - -#ifdef SWIFT_DEBUG_CHECKS - if (c->hydro.super != NULL) error("Function called below the super level!"); -#endif - - /* Ok, then, let's try lower */ - if (c->split) { - for (int k = 0; k < 8; ++k) { - if (c->progeny[k] != NULL) runner_do_all_hydro_sort(r, c->progeny[k]); - } - } else { -#ifdef SWIFT_DEBUG_CHECKS - error("Reached a leaf without encountering a hydro super cell!"); -#endif - } - } -} - -/** - * @brief Recurse into a cell until reaching the super level and call - * the star sorting function there. - * - * This function must be called at or above the super level! - * - * This function will sort the particles in all 13 directions. - * - * @param r the #runner. - * @param c the #cell. - */ -void runner_do_all_stars_sort(struct runner *r, struct cell *c) { - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != engine_rank) error("Function called on a foreign cell!"); -#endif - - if (!cell_is_active_stars(c, r->e) && !cell_is_active_hydro(c, r->e)) return; - - /* Shall we sort at this level? */ - if (c->hydro.super == c) { - - /* Sort everything */ - runner_do_stars_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); - - } else { - -#ifdef SWIFT_DEBUG_CHECKS - if (c->hydro.super != NULL) error("Function called below the super level!"); -#endif - - /* Ok, then, let's try lower */ - if (c->split) { - for (int k = 0; k < 8; ++k) { - if (c->progeny[k] != NULL) runner_do_all_stars_sort(r, c->progeny[k]); - } - } else { -#ifdef SWIFT_DEBUG_CHECKS - error("Reached a leaf without encountering a hydro super cell!"); -#endif - } - } -} - -/** - * @brief Initialize the multipoles before the gravity calculation. - * - * @param r The runner thread. - * @param c The cell. - * @param timer 1 if the time is to be recorded. - */ -void runner_do_init_grav(struct runner *r, struct cell *c, int timer) { - - const struct engine *e = r->e; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (!(e->policy & engine_policy_self_gravity)) - error("Grav-init task called outside of self-gravity calculation"); -#endif - - /* Anything to do here? */ - if (!cell_is_active_gravity(c, e)) return; - - /* Reset the gravity acceleration tensors */ - gravity_field_tensors_init(&c->grav.multipole->pot, e->ti_current); - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) runner_do_init_grav(r, c->progeny[k], 0); - } - } - - if (timer) TIMER_TOC(timer_init_grav); -} - -/** - * @brief Intermediate task after the gradient loop that does final operations - * on the gradient quantities and optionally slope limits the gradients - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) { - -#ifdef EXTRA_HYDRO_LOOP - - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - const int count = c->hydro.count; - const struct engine *e = r->e; - const integertime_t ti_current = e->ti_current; - const int with_cosmology = (e->policy & engine_policy_cosmology); - const double time_base = e->time_base; - const struct cosmology *cosmo = e->cosmology; - const struct hydro_props *hydro_props = e->hydro_properties; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k], 0); - } else { - - /* Loop over the parts in this cell. */ - for (int i = 0; i < count; i++) { - - /* Get a direct pointer on the part. */ - struct part *restrict p = &parts[i]; - struct xpart *restrict xp = &xparts[i]; - - if (part_is_active(p, e)) { - - /* Finish the gradient calculation */ - hydro_end_gradient(p); - - /* As of here, particle force variables will be set. */ - - /* Calculate the time-step for passing to hydro_prepare_force. - * This is the physical time between the start and end of the time-step - * without any scale-factor powers. */ - double dt_alpha; - - if (with_cosmology) { - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current - 1, p->time_bin); - - dt_alpha = - cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); - } else { - dt_alpha = get_timestep(p->time_bin, time_base); - } - - /* Compute variables required for the force loop */ - hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); - - /* The particle force values are now set. Do _NOT_ - try to read any particle density variables! */ - - /* Prepare the particle for the force loop over neighbours */ - hydro_reset_acceleration(p); - } - } - } - - if (timer) TIMER_TOC(timer_do_extra_ghost); - -#else - error("SWIFT was not compiled with the extra hydro loop activated."); -#endif -} - -/** - * @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_ghost(struct runner *r, struct cell *c, int timer) { - - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - const struct engine *e = r->e; - const struct space *s = e->s; - const struct hydro_space *hs = &s->hs; - const struct cosmology *cosmo = e->cosmology; - const struct chemistry_global_data *chemistry = e->chemistry; - const struct star_formation *star_formation = e->star_formation; - - const int with_cosmology = (e->policy & engine_policy_cosmology); - - const float hydro_h_max = e->hydro_properties->h_max; - const float hydro_h_min = e->hydro_properties->h_min; - const float eps = e->hydro_properties->h_tolerance; - const float hydro_eta_dim = - pow_dimension(e->hydro_properties->eta_neighbours); - const int max_smoothing_iter = e->hydro_properties->max_smoothing_iterations; - int redo = 0, count = 0; - - /* Running value of the maximal smoothing length */ - double h_max = c->hydro.h_max; - - TIMER_TIC; - - /* Anything to do here? */ - if (c->hydro.count == 0) return; - if (!cell_is_active_hydro(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - runner_do_ghost(r, c->progeny[k], 0); - - /* Update h_max */ - h_max = max(h_max, c->progeny[k]->hydro.h_max); - } - } - } else { - - /* Init the list of active particles that have to be updated and their - * current smoothing lengths. */ - int *pid = NULL; - float *h_0 = NULL; - float *left = NULL; - float *right = NULL; - if ((pid = (int *)malloc(sizeof(int) * c->hydro.count)) == NULL) - error("Can't allocate memory for pid."); - if ((h_0 = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) - error("Can't allocate memory for h_0."); - if ((left = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) - error("Can't allocate memory for left."); - if ((right = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) - error("Can't allocate memory for right."); - for (int k = 0; k < c->hydro.count; k++) - if (part_is_active(&parts[k], e)) { - pid[count] = k; - h_0[count] = parts[k].h; - left[count] = 0.f; - right[count] = hydro_h_max; - ++count; - } - - /* While there are particles that need to be updated... */ - for (int num_reruns = 0; count > 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 < count; i++) { - - /* Get a direct pointer on the part. */ - struct part *p = &parts[pid[i]]; - struct xpart *xp = &xparts[pid[i]]; - -#ifdef SWIFT_DEBUG_CHECKS - /* Is this part within the timestep? */ - if (!part_is_active(p, e)) error("Ghost applied to inactive particle"); -#endif - - /* Get some useful values */ - const float h_init = h_0[i]; - const float h_old = p->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 (p->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 */ - hydro_end_density(p, cosmo); - chemistry_end_density(p, chemistry, cosmo); - star_formation_end_density(p, star_formation, cosmo); - - /* Compute one step of the Newton-Raphson scheme */ - const float n_sum = p->density.wcount * h_old_dim; - const float n_target = hydro_eta_dim; - const float f = n_sum - n_target; - const float f_prime = - p->density.wcount_dh * h_old_dim + - hydro_dimension * p->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 (((p->h >= hydro_h_max) && (f < 0.f)) || - ((p->h <= hydro_h_min) && (f > 0.f))) { - - /* We have a particle whose smoothing length is already set (wants - * to be larger but has already hit the maximum OR wants to be - * smaller but has already reached the minimum). So, just tidy up as - * if the smoothing length had converged correctly */ - -#ifdef EXTRA_HYDRO_LOOP - - /* As of here, particle gradient variables will be set. */ - /* The force variables are set in the extra ghost. */ - - /* Compute variables required for the gradient loop */ - hydro_prepare_gradient(p, xp, cosmo); - - /* The particle gradient values are now set. Do _NOT_ - try to read any particle density variables! */ - - /* Prepare the particle for the gradient loop over neighbours */ - hydro_reset_gradient(p); - -#else - const struct hydro_props *hydro_props = e->hydro_properties; - - /* Calculate the time-step for passing to hydro_prepare_force, used - * for the evolution of alpha factors (i.e. those involved in the - * artificial viscosity and thermal conduction terms) */ - const double time_base = e->time_base; - const integertime_t ti_current = e->ti_current; - double dt_alpha; - - if (with_cosmology) { - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current - 1, p->time_bin); - - dt_alpha = - cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); - } else { - dt_alpha = get_timestep(p->time_bin, time_base); - } - - /* As of here, particle force variables will be set. */ - - /* Compute variables required for the force loop */ - hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); - - /* The particle force values are now set. Do _NOT_ - try to read any particle density variables! */ - - /* Prepare the particle for the force loop over neighbours */ - hydro_reset_acceleration(p); - -#endif /* EXTRA_HYDRO_LOOP */ - - /* 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, p->id, h_init, h_old, h_new, f, f_prime, n_sum, - n_target, left[i], right[i]); - } - -#ifdef SWIFT_DEBUG_CHECKS - if ((f > 0.f && h_new > h_old) || (f < 0.f && h_new < h_old)) - error( - "Smoothing length correction not going in the right direction"); -#endif - - /* 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 */ - p->h = pow_inv_dimension( - 0.5f * (pow_dimension(left[i]) + pow_dimension(right[i]))); - - } else { - - /* Normal case */ - p->h = h_new; - } - - /* If within the allowed range, try again */ - if (p->h < hydro_h_max && p->h > hydro_h_min) { - - /* Flag for another round of fun */ - pid[redo] = pid[i]; - h_0[redo] = h_0[i]; - left[redo] = left[i]; - right[redo] = right[i]; - redo += 1; - - /* Re-initialise everything */ - hydro_init_part(p, hs); - chemistry_init_part(p, chemistry); - star_formation_init_part(p, star_formation); - tracers_after_init(p, xp, e->internal_units, e->physical_constants, - with_cosmology, e->cosmology, - e->hydro_properties, e->cooling_func, e->time); - - /* Off we go ! */ - continue; - - } else if (p->h <= hydro_h_min) { - - /* Ok, this particle is a lost cause... */ - p->h = hydro_h_min; - - } else if (p->h >= hydro_h_max) { - - /* Ok, this particle is a lost cause... */ - p->h = hydro_h_max; - - /* Do some damage control if no neighbours at all were found */ - if (has_no_neighbours) { - hydro_part_has_no_neighbours(p, xp, cosmo); - chemistry_part_has_no_neighbours(p, xp, chemistry, cosmo); - star_formation_part_has_no_neighbours(p, xp, star_formation, - 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 is increased */ - h_max = max(h_max, p->h); - -#ifdef EXTRA_HYDRO_LOOP - - /* As of here, particle gradient variables will be set. */ - /* The force variables are set in the extra ghost. */ - - /* Compute variables required for the gradient loop */ - hydro_prepare_gradient(p, xp, cosmo); - - /* The particle gradient values are now set. Do _NOT_ - try to read any particle density variables! */ - - /* Prepare the particle for the gradient loop over neighbours */ - hydro_reset_gradient(p); - -#else - const struct hydro_props *hydro_props = e->hydro_properties; - - /* Calculate the time-step for passing to hydro_prepare_force, used for - * the evolution of alpha factors (i.e. those involved in the artificial - * viscosity and thermal conduction terms) */ - const double time_base = e->time_base; - const integertime_t ti_current = e->ti_current; - double dt_alpha; - - if (with_cosmology) { - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current - 1, p->time_bin); - - dt_alpha = - cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); - } else { - dt_alpha = get_timestep(p->time_bin, time_base); - } - - /* As of here, particle force variables will be set. */ - - /* Compute variables required for the force loop */ - hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); - - /* The particle force values are now set. Do _NOT_ - try to read any particle density variables! */ - - /* Prepare the particle for the force loop over neighbours */ - hydro_reset_acceleration(p); - -#endif /* EXTRA_HYDRO_LOOP */ - } - - /* We now need to treat the particles whose smoothing length had not - * converged again */ - - /* Re-set the counter for the next loop (potentially). */ - count = redo; - if (count > 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->hydro.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_density(r, finger, parts, pid, count); - - /* Otherwise, pair interaction? */ - else if (l->t->type == task_type_pair) { - - /* Left or right? */ - if (l->t->ci == finger) - runner_dopair_subset_branch_density(r, finger, parts, pid, - count, l->t->cj); - else - runner_dopair_subset_branch_density(r, finger, parts, pid, - count, l->t->ci); - } - - /* Otherwise, sub-self interaction? */ - else if (l->t->type == task_type_sub_self) - runner_dosub_subset_density(r, finger, parts, pid, count, 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_density(r, finger, parts, pid, count, - l->t->cj, 1); - else - runner_dosub_subset_density(r, finger, parts, pid, count, - l->t->ci, 1); - } - } - } - } - } - - if (count) { - error("Smoothing length failed to converge on %i particles.", count); - } - - /* Be clean */ - free(left); - free(right); - free(pid); - free(h_0); - } - - /* Update h_max */ - c->hydro.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->hydro.ghost) { - for (struct cell *tmp = c->parent; tmp != NULL; tmp = tmp->parent) { - atomic_max_d(&tmp->hydro.h_max, h_max); - } - } - - if (timer) TIMER_TOC(timer_do_ghost); -} - -/** - * @brief Unskip any hydro tasks associated with active cells. - * - * @param c The cell. - * @param e The engine. - */ -static void runner_do_unskip_hydro(struct cell *c, struct engine *e) { - - /* Ignore empty cells. */ - if (c->hydro.count == 0) return; - - /* Skip inactive cells. */ - if (!cell_is_active_hydro(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_hydro(cp, e); - } - } - } - - /* Unskip any active tasks. */ - const int forcerebuild = cell_unskip_hydro_tasks(c, &e->sched); - if (forcerebuild) atomic_inc(&e->forcerebuild); -} - -/** - * @brief Unskip any stars tasks associated with active cells. - * - * @param c The cell. - * @param e The engine. - * @param with_star_formation Are we running with star formation switched on? - */ -static void runner_do_unskip_stars(struct cell *c, struct engine *e, - const int with_star_formation) { - - const int non_empty = - c->stars.count > 0 || (with_star_formation && c->hydro.count > 0); - - /* Ignore empty cells. */ - if (!non_empty) return; - - const int ci_active = cell_is_active_stars(c, e) || - (with_star_formation && cell_is_active_hydro(c, e)); - - /* Skip inactive cells. */ - if (!ci_active) 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_stars(cp, e, with_star_formation); - } - } - } - - /* Unskip any active tasks. */ - const int forcerebuild = - cell_unskip_stars_tasks(c, &e->sched, with_star_formation); - 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. - * - * @param c The cell. - * @param e The engine. - */ -static void runner_do_unskip_gravity(struct cell *c, struct engine *e) { - - /* Ignore empty cells. */ - if (c->grav.count == 0) return; - - /* Skip inactive cells. */ - if (!cell_is_active_gravity(c, e)) return; - - /* Recurse */ - if (c->split && ((c->maxdepth - c->depth) >= space_subdepth_diff_grav)) { - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - struct cell *cp = c->progeny[k]; - runner_do_unskip_gravity(cp, e); - } - } - } - - /* Unskip any active tasks. */ - cell_unskip_gravity_tasks(c, &e->sched); -} - -/** - * @brief Mapper function to unskip active tasks. - * - * @param map_data An array of #cell%s. - * @param num_elements Chunk size. - * @param extra_data Pointer to an #engine. - */ -void runner_do_unskip_mapper(void *map_data, int num_elements, - void *extra_data) { - - struct engine *e = (struct engine *)extra_data; - const int with_star_formation = e->policy & engine_policy_star_formation; - const int nodeID = e->nodeID; - struct space *s = e->s; - int *local_cells = (int *)map_data; - - for (int ind = 0; ind < num_elements; ind++) { - struct cell *c = &s->cells_top[local_cells[ind]]; - if (c != NULL) { - - /* Hydro tasks */ - if (e->policy & engine_policy_hydro) runner_do_unskip_hydro(c, e); - - /* All gravity tasks */ - if ((e->policy & engine_policy_self_gravity) || - ((e->policy & engine_policy_external_gravity) && c->nodeID == nodeID)) - runner_do_unskip_gravity(c, e); - - /* Stars tasks */ - if (e->policy & engine_policy_stars) - runner_do_unskip_stars(c, e, with_star_formation); - - /* Black hole tasks */ - if (e->policy & engine_policy_black_holes) - runner_do_unskip_black_holes(c, e); - } - } -} - -/** - * @brief Drift all part in a cell. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_drift_part(struct runner *r, struct cell *c, int timer) { - - TIMER_TIC; - - cell_drift_part(c, r->e, 0); - - if (timer) TIMER_TOC(timer_drift_part); -} - -/** - * @brief Drift all gpart in a cell. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer) { - - TIMER_TIC; - - cell_drift_gpart(c, r->e, 0); - - if (timer) TIMER_TOC(timer_drift_gpart); -} - -/** - * @brief Drift all spart in a cell. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_drift_spart(struct runner *r, struct cell *c, int timer) { - - TIMER_TIC; - - cell_drift_spart(c, r->e, 0); - - if (timer) TIMER_TOC(timer_drift_spart); -} - -/** - * @brief Drift all bpart in a cell. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_drift_bpart(struct runner *r, struct cell *c, int timer) { - - TIMER_TIC; - - cell_drift_bpart(c, r->e, 0); - - if (timer) TIMER_TOC(timer_drift_bpart); -} - -/** - * @brief Perform the first half-kick on all the active particles in a cell. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_kick1(struct runner *r, struct cell *c, int timer) { - - const struct engine *e = r->e; - const struct cosmology *cosmo = e->cosmology; - const struct hydro_props *hydro_props = e->hydro_properties; - const struct entropy_floor_properties *entropy_floor = e->entropy_floor; - const int with_cosmology = (e->policy & engine_policy_cosmology); - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - struct gpart *restrict gparts = c->grav.parts; - struct spart *restrict sparts = c->stars.parts; - const int count = c->hydro.count; - const int gcount = c->grav.count; - const int scount = c->stars.count; - const integertime_t ti_current = e->ti_current; - const double time_base = e->time_base; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_starting_hydro(c, e) && !cell_is_starting_gravity(c, e) && - !cell_is_starting_stars(c, e) && !cell_is_starting_black_holes(c, e)) - return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_kick1(r, c->progeny[k], 0); - } else { - - /* Loop over the parts in this cell. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - struct xpart *restrict xp = &xparts[k]; - - /* If particle needs to be kicked */ - if (part_is_starting(p, e)) { - -#ifdef SWIFT_DEBUG_CHECKS - if (p->wakeup == time_bin_awake) - error("Woken-up particle that has not been processed in kick1"); -#endif - - /* Skip particles that have been woken up and treated by the limiter. */ - if (p->wakeup != time_bin_not_awake) continue; - - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current + 1, p->time_bin); - -#ifdef SWIFT_DEBUG_CHECKS - const integertime_t ti_end = ti_begin + ti_step; - - if (ti_begin != ti_current) - error( - "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " - "ti_step=%lld time_bin=%d wakeup=%d ti_current=%lld", - ti_end, ti_begin, ti_step, p->time_bin, p->wakeup, ti_current); -#endif - - /* Time interval for this half-kick */ - double dt_kick_grav, dt_kick_hydro, dt_kick_therm, dt_kick_corr; - if (with_cosmology) { - dt_kick_hydro = cosmology_get_hydro_kick_factor( - cosmo, ti_begin, ti_begin + ti_step / 2); - dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, - ti_begin + ti_step / 2); - dt_kick_therm = cosmology_get_therm_kick_factor( - cosmo, ti_begin, ti_begin + ti_step / 2); - dt_kick_corr = cosmology_get_corr_kick_factor(cosmo, ti_begin, - ti_begin + ti_step / 2); - } else { - dt_kick_hydro = (ti_step / 2) * time_base; - dt_kick_grav = (ti_step / 2) * time_base; - dt_kick_therm = (ti_step / 2) * time_base; - dt_kick_corr = (ti_step / 2) * time_base; - } - - /* do the kick */ - kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, - dt_kick_corr, cosmo, hydro_props, entropy_floor, ti_begin, - ti_begin + ti_step / 2); - - /* Update the accelerations to be used in the drift for hydro */ - if (p->gpart != NULL) { - - xp->a_grav[0] = p->gpart->a_grav[0]; - xp->a_grav[1] = p->gpart->a_grav[1]; - xp->a_grav[2] = p->gpart->a_grav[2]; - } - } - } - - /* Loop over the gparts in this cell. */ - for (int k = 0; k < gcount; k++) { - - /* Get a handle on the part. */ - struct gpart *restrict gp = &gparts[k]; - - /* If the g-particle has no counterpart and needs to be kicked */ - if (gp->type == swift_type_dark_matter && gpart_is_starting(gp, e)) { - - const integertime_t ti_step = get_integer_timestep(gp->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current + 1, gp->time_bin); - -#ifdef SWIFT_DEBUG_CHECKS - const integertime_t ti_end = - get_integer_time_end(ti_current + 1, gp->time_bin); - - if (ti_begin != ti_current) - error( - "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " - "ti_step=%lld time_bin=%d ti_current=%lld", - ti_end, ti_begin, ti_step, gp->time_bin, ti_current); -#endif - - /* Time interval for this half-kick */ - double dt_kick_grav; - if (with_cosmology) { - dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, - ti_begin + ti_step / 2); - } else { - dt_kick_grav = (ti_step / 2) * time_base; - } - - /* do the kick */ - kick_gpart(gp, dt_kick_grav, ti_begin, ti_begin + ti_step / 2); - } - } - - /* Loop over the stars particles in this cell. */ - for (int k = 0; k < scount; k++) { - - /* Get a handle on the s-part. */ - struct spart *restrict sp = &sparts[k]; - - /* If particle needs to be kicked */ - if (spart_is_starting(sp, e)) { - - const integertime_t ti_step = get_integer_timestep(sp->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current + 1, sp->time_bin); - -#ifdef SWIFT_DEBUG_CHECKS - const integertime_t ti_end = - get_integer_time_end(ti_current + 1, sp->time_bin); - - if (ti_begin != ti_current) - error( - "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " - "ti_step=%lld time_bin=%d ti_current=%lld", - ti_end, ti_begin, ti_step, sp->time_bin, ti_current); -#endif - - /* Time interval for this half-kick */ - double dt_kick_grav; - if (with_cosmology) { - dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, - ti_begin + ti_step / 2); - } else { - dt_kick_grav = (ti_step / 2) * time_base; - } - - /* do the kick */ - kick_spart(sp, dt_kick_grav, ti_begin, ti_begin + ti_step / 2); - } - } - } - - if (timer) TIMER_TOC(timer_kick1); -} - -/** - * @brief Perform the second half-kick on all the active particles in a cell. - * - * Also prepares particles to be drifted. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_kick2(struct runner *r, struct cell *c, int timer) { - - const struct engine *e = r->e; - const struct cosmology *cosmo = e->cosmology; - const struct hydro_props *hydro_props = e->hydro_properties; - const struct entropy_floor_properties *entropy_floor = e->entropy_floor; - const int with_cosmology = (e->policy & engine_policy_cosmology); - const int count = c->hydro.count; - const int gcount = c->grav.count; - const int scount = c->stars.count; - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - struct gpart *restrict gparts = c->grav.parts; - struct spart *restrict sparts = c->stars.parts; - const integertime_t ti_current = e->ti_current; - const double time_base = e->time_base; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && - !cell_is_active_stars(c, e) && !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_kick2(r, c->progeny[k], 0); - } else { - - /* Loop over the particles in this cell. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - struct xpart *restrict xp = &xparts[k]; - - /* If particle needs to be kicked */ - if (part_is_active(p, e)) { - - integertime_t ti_begin, ti_end, ti_step; - -#ifdef SWIFT_DEBUG_CHECKS - if (p->wakeup == time_bin_awake) - error("Woken-up particle that has not been processed in kick1"); -#endif - - if (p->wakeup == time_bin_not_awake) { - - /* Time-step from a regular kick */ - ti_step = get_integer_timestep(p->time_bin); - ti_begin = get_integer_time_begin(ti_current, p->time_bin); - ti_end = ti_begin + ti_step; - - } else { - - /* Time-step that follows a wake-up call */ - ti_begin = get_integer_time_begin(ti_current, p->wakeup); - ti_end = get_integer_time_end(ti_current, p->time_bin); - ti_step = ti_end - ti_begin; - - /* Reset the flag. Everything is back to normal from now on. */ - p->wakeup = time_bin_awake; - } - -#ifdef SWIFT_DEBUG_CHECKS - if (ti_begin + ti_step != ti_current) - error( - "Particle in wrong time-bin, ti_begin=%lld, ti_step=%lld " - "time_bin=%d wakeup=%d ti_current=%lld", - ti_begin, ti_step, p->time_bin, p->wakeup, ti_current); -#endif - /* Time interval for this half-kick */ - double dt_kick_grav, dt_kick_hydro, dt_kick_therm, dt_kick_corr; - if (with_cosmology) { - dt_kick_hydro = cosmology_get_hydro_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_end); - dt_kick_grav = cosmology_get_grav_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_end); - dt_kick_therm = cosmology_get_therm_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_end); - dt_kick_corr = cosmology_get_corr_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_end); - } else { - dt_kick_hydro = (ti_end - (ti_begin + ti_step / 2)) * time_base; - dt_kick_grav = (ti_end - (ti_begin + ti_step / 2)) * time_base; - dt_kick_therm = (ti_end - (ti_begin + ti_step / 2)) * time_base; - dt_kick_corr = (ti_end - (ti_begin + ti_step / 2)) * time_base; - } - - /* Finish the time-step with a second half-kick */ - kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, - dt_kick_corr, cosmo, hydro_props, entropy_floor, - ti_begin + ti_step / 2, ti_end); - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that kick and the drift are synchronized */ - if (p->ti_drift != p->ti_kick) error("Error integrating part in time."); -#endif - - /* Prepare the values to be drifted */ - hydro_reset_predicted_values(p, xp); - } - } - - /* Loop over the g-particles in this cell. */ - for (int k = 0; k < gcount; k++) { - - /* Get a handle on the part. */ - struct gpart *restrict gp = &gparts[k]; - - /* If the g-particle has no counterpart and needs to be kicked */ - if (gp->type == swift_type_dark_matter && gpart_is_active(gp, e)) { - - const integertime_t ti_step = get_integer_timestep(gp->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current, gp->time_bin); - -#ifdef SWIFT_DEBUG_CHECKS - if (ti_begin + ti_step != ti_current) - error("Particle in wrong time-bin"); -#endif - - /* Time interval for this half-kick */ - double dt_kick_grav; - if (with_cosmology) { - dt_kick_grav = cosmology_get_grav_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); - } else { - dt_kick_grav = (ti_step / 2) * time_base; - } - - /* Finish the time-step with a second half-kick */ - kick_gpart(gp, dt_kick_grav, ti_begin + ti_step / 2, - ti_begin + ti_step); - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that kick and the drift are synchronized */ - if (gp->ti_drift != gp->ti_kick) - error("Error integrating g-part in time."); -#endif - - /* Prepare the values to be drifted */ - gravity_reset_predicted_values(gp); - } - } - - /* Loop over the particles in this cell. */ - for (int k = 0; k < scount; k++) { - - /* Get a handle on the part. */ - struct spart *restrict sp = &sparts[k]; - - /* If particle needs to be kicked */ - if (spart_is_active(sp, e)) { - - const integertime_t ti_step = get_integer_timestep(sp->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current, sp->time_bin); - -#ifdef SWIFT_DEBUG_CHECKS - if (ti_begin + ti_step != ti_current) - error("Particle in wrong time-bin"); -#endif - - /* Time interval for this half-kick */ - double dt_kick_grav; - if (with_cosmology) { - dt_kick_grav = cosmology_get_grav_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); - } else { - dt_kick_grav = (ti_step / 2) * time_base; - } - - /* Finish the time-step with a second half-kick */ - kick_spart(sp, dt_kick_grav, ti_begin + ti_step / 2, - ti_begin + ti_step); - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that kick and the drift are synchronized */ - if (sp->ti_drift != sp->ti_kick) - error("Error integrating s-part in time."); -#endif - - /* Prepare the values to be drifted */ - stars_reset_predicted_values(sp); - } - } - } - if (timer) TIMER_TOC(timer_kick2); -} - -/** - * @brief Computes the next time-step of all active particles in this cell - * and update the cell's statistics. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_timestep(struct runner *r, struct cell *c, int timer) { - - const struct engine *e = r->e; - const integertime_t ti_current = e->ti_current; - const int with_cosmology = (e->policy & engine_policy_cosmology); - const int count = c->hydro.count; - const int gcount = c->grav.count; - const int scount = c->stars.count; - const int bcount = c->black_holes.count; - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - struct gpart *restrict gparts = c->grav.parts; - struct spart *restrict sparts = c->stars.parts; - struct bpart *restrict bparts = c->black_holes.parts; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && - !cell_is_active_stars(c, e) && !cell_is_active_black_holes(c, e)) { - c->hydro.updated = 0; - c->grav.updated = 0; - c->stars.updated = 0; - c->black_holes.updated = 0; - return; - } - - int updated = 0, g_updated = 0, s_updated = 0, b_updated = 0; - integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, - ti_hydro_beg_max = 0; - integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, - ti_gravity_beg_max = 0; - integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, - ti_stars_beg_max = 0; - integertime_t ti_black_holes_end_min = max_nr_timesteps, - ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; - - /* No children? */ - if (!c->split) { - - /* Loop over the particles in this cell. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - struct xpart *restrict xp = &xparts[k]; - - /* If particle needs updating */ - if (part_is_active(p, e)) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Current end of time-step */ - const integertime_t ti_end = - get_integer_time_end(ti_current, p->time_bin); - - if (ti_end != ti_current) - error("Computing time-step of rogue particle."); -#endif - - /* Get new time-step */ - const integertime_t ti_new_step = get_part_timestep(p, xp, e); - - /* Update particle */ - p->time_bin = get_time_bin(ti_new_step); - if (p->gpart != NULL) p->gpart->time_bin = p->time_bin; - - /* Update the tracers properties */ - tracers_after_timestep(p, xp, e->internal_units, e->physical_constants, - with_cosmology, e->cosmology, - e->hydro_properties, e->cooling_func, e->time); - - /* Number of updated particles */ - updated++; - if (p->gpart != NULL) g_updated++; - - /* What is the next sync-point ? */ - ti_hydro_end_min = min(ti_current + ti_new_step, ti_hydro_end_min); - ti_hydro_end_max = max(ti_current + ti_new_step, ti_hydro_end_max); - - /* What is the next starting point for this cell ? */ - ti_hydro_beg_max = max(ti_current, ti_hydro_beg_max); - - if (p->gpart != NULL) { - - /* What is the next sync-point ? */ - ti_gravity_end_min = - min(ti_current + ti_new_step, ti_gravity_end_min); - ti_gravity_end_max = - max(ti_current + ti_new_step, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); - } - } - - else { /* part is inactive */ - - if (!part_is_inhibited(p, e)) { - - const integertime_t ti_end = - get_integer_time_end(ti_current, p->time_bin); - - const integertime_t ti_beg = - get_integer_time_begin(ti_current + 1, p->time_bin); - - /* What is the next sync-point ? */ - ti_hydro_end_min = min(ti_end, ti_hydro_end_min); - ti_hydro_end_max = max(ti_end, ti_hydro_end_max); - - /* What is the next starting point for this cell ? */ - ti_hydro_beg_max = max(ti_beg, ti_hydro_beg_max); - - if (p->gpart != NULL) { - - /* What is the next sync-point ? */ - ti_gravity_end_min = min(ti_end, ti_gravity_end_min); - ti_gravity_end_max = max(ti_end, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); - } - } - } - } - - /* Loop over the g-particles in this cell. */ - for (int k = 0; k < gcount; k++) { - - /* Get a handle on the part. */ - struct gpart *restrict gp = &gparts[k]; - - /* If the g-particle has no counterpart */ - if (gp->type == swift_type_dark_matter) { - - /* need to be updated ? */ - if (gpart_is_active(gp, e)) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Current end of time-step */ - const integertime_t ti_end = - get_integer_time_end(ti_current, gp->time_bin); - - if (ti_end != ti_current) - error("Computing time-step of rogue particle."); -#endif - - /* Get new time-step */ - const integertime_t ti_new_step = get_gpart_timestep(gp, e); - - /* Update particle */ - gp->time_bin = get_time_bin(ti_new_step); - - /* Number of updated g-particles */ - g_updated++; - - /* What is the next sync-point ? */ - ti_gravity_end_min = - min(ti_current + ti_new_step, ti_gravity_end_min); - ti_gravity_end_max = - max(ti_current + ti_new_step, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); - - } else { /* gpart is inactive */ - - if (!gpart_is_inhibited(gp, e)) { - - const integertime_t ti_end = - get_integer_time_end(ti_current, gp->time_bin); - - /* What is the next sync-point ? */ - ti_gravity_end_min = min(ti_end, ti_gravity_end_min); - ti_gravity_end_max = max(ti_end, ti_gravity_end_max); - - const integertime_t ti_beg = - get_integer_time_begin(ti_current + 1, gp->time_bin); - - /* What is the next starting point for this cell ? */ - ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); - } - } - } - } - - /* Loop over the star particles in this cell. */ - for (int k = 0; k < scount; k++) { - - /* Get a handle on the part. */ - struct spart *restrict sp = &sparts[k]; - - /* need to be updated ? */ - if (spart_is_active(sp, e)) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Current end of time-step */ - const integertime_t ti_end = - get_integer_time_end(ti_current, sp->time_bin); - - if (ti_end != ti_current) - error("Computing time-step of rogue particle."); -#endif - /* Get new time-step */ - const integertime_t ti_new_step = get_spart_timestep(sp, e); - - /* Update particle */ - sp->time_bin = get_time_bin(ti_new_step); - sp->gpart->time_bin = get_time_bin(ti_new_step); - - /* Number of updated s-particles */ - s_updated++; - g_updated++; - - ti_stars_end_min = min(ti_current + ti_new_step, ti_stars_end_min); - ti_stars_end_max = max(ti_current + ti_new_step, ti_stars_end_max); - ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); - ti_gravity_end_max = max(ti_current + ti_new_step, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_stars_beg_max = max(ti_current, ti_stars_beg_max); - ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); - - /* star particle is inactive but not inhibited */ - } else { - - if (!spart_is_inhibited(sp, e)) { - - const integertime_t ti_end = - get_integer_time_end(ti_current, sp->time_bin); - - const integertime_t ti_beg = - get_integer_time_begin(ti_current + 1, sp->time_bin); - - ti_stars_end_min = min(ti_end, ti_stars_end_min); - ti_stars_end_max = max(ti_end, ti_stars_end_max); - ti_gravity_end_min = min(ti_end, ti_gravity_end_min); - ti_gravity_end_max = max(ti_end, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_stars_beg_max = max(ti_beg, ti_stars_beg_max); - ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); - } - } - } - - /* Loop over the star particles in this cell. */ - for (int k = 0; k < bcount; k++) { - - /* Get a handle on the part. */ - struct bpart *restrict bp = &bparts[k]; - - /* need to be updated ? */ - if (bpart_is_active(bp, e)) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Current end of time-step */ - const integertime_t ti_end = - get_integer_time_end(ti_current, bp->time_bin); - - if (ti_end != ti_current) - error("Computing time-step of rogue particle."); -#endif - /* Get new time-step */ - const integertime_t ti_new_step = get_bpart_timestep(bp, e); - - /* Update particle */ - bp->time_bin = get_time_bin(ti_new_step); - bp->gpart->time_bin = get_time_bin(ti_new_step); - - /* Number of updated s-particles */ - b_updated++; - g_updated++; - - ti_black_holes_end_min = - min(ti_current + ti_new_step, ti_black_holes_end_min); - ti_black_holes_end_max = - max(ti_current + ti_new_step, ti_black_holes_end_max); - ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); - ti_gravity_end_max = max(ti_current + ti_new_step, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_black_holes_beg_max = max(ti_current, ti_black_holes_beg_max); - ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); - - /* star particle is inactive but not inhibited */ - } else { - - if (!bpart_is_inhibited(bp, e)) { - - const integertime_t ti_end = - get_integer_time_end(ti_current, bp->time_bin); - - const integertime_t ti_beg = - get_integer_time_begin(ti_current + 1, bp->time_bin); - - ti_black_holes_end_min = min(ti_end, ti_black_holes_end_min); - ti_black_holes_end_max = max(ti_end, ti_black_holes_end_max); - ti_gravity_end_min = min(ti_end, ti_gravity_end_min); - ti_gravity_end_max = max(ti_end, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_black_holes_beg_max = max(ti_beg, ti_black_holes_beg_max); - ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); - } - } - } - - } else { - - /* Loop over the progeny. */ - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - struct cell *restrict cp = c->progeny[k]; - - /* Recurse */ - runner_do_timestep(r, cp, 0); - - /* And aggregate */ - updated += cp->hydro.updated; - g_updated += cp->grav.updated; - s_updated += cp->stars.updated; - b_updated += cp->black_holes.updated; - - ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); - ti_hydro_end_max = max(cp->hydro.ti_end_max, ti_hydro_end_max); - ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); - - ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); - ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max); - ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); - - ti_stars_end_min = min(cp->stars.ti_end_min, ti_stars_end_min); - ti_stars_end_max = max(cp->grav.ti_end_max, ti_stars_end_max); - ti_stars_beg_max = max(cp->grav.ti_beg_max, ti_stars_beg_max); - - ti_black_holes_end_min = - min(cp->black_holes.ti_end_min, ti_black_holes_end_min); - ti_black_holes_end_max = - max(cp->grav.ti_end_max, ti_black_holes_end_max); - ti_black_holes_beg_max = - max(cp->grav.ti_beg_max, ti_black_holes_beg_max); - } - } - } - - /* Store the values. */ - c->hydro.updated = updated; - c->grav.updated = g_updated; - c->stars.updated = s_updated; - c->black_holes.updated = b_updated; - - c->hydro.ti_end_min = ti_hydro_end_min; - c->hydro.ti_end_max = ti_hydro_end_max; - c->hydro.ti_beg_max = ti_hydro_beg_max; - c->grav.ti_end_min = ti_gravity_end_min; - c->grav.ti_end_max = ti_gravity_end_max; - c->grav.ti_beg_max = ti_gravity_beg_max; - c->stars.ti_end_min = ti_stars_end_min; - c->stars.ti_end_max = ti_stars_end_max; - c->stars.ti_beg_max = ti_stars_beg_max; - c->black_holes.ti_end_min = ti_black_holes_end_min; - c->black_holes.ti_end_max = ti_black_holes_end_max; - c->black_holes.ti_beg_max = ti_black_holes_beg_max; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->hydro.ti_end_min == e->ti_current && - c->hydro.ti_end_min < max_nr_timesteps) - error("End of next hydro step is current time!"); - if (c->grav.ti_end_min == e->ti_current && - c->grav.ti_end_min < max_nr_timesteps) - error("End of next gravity step is current time!"); - if (c->stars.ti_end_min == e->ti_current && - c->stars.ti_end_min < max_nr_timesteps) - error("End of next stars step is current time!"); - if (c->black_holes.ti_end_min == e->ti_current && - c->black_holes.ti_end_min < max_nr_timesteps) - error("End of next black holes step is current time!"); -#endif - - if (timer) TIMER_TOC(timer_timestep); -} - -/** - * @brief Apply the time-step limiter to all awaken particles in a cell - * hierarchy. - * - * @param r The task #runner. - * @param c The #cell. - * @param force Limit the particles irrespective of the #cell flags. - * @param timer Are we timing this ? - */ -void runner_do_limiter(struct runner *r, struct cell *c, int force, int timer) { - - const struct engine *e = r->e; - const integertime_t ti_current = e->ti_current; - const int count = c->hydro.count; - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that we only limit local cells. */ - if (c->nodeID != engine_rank) error("Limiting dt of a foreign cell is nope."); -#endif - - integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, - ti_hydro_beg_max = 0; - integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, - ti_gravity_beg_max = 0; - - /* Limit irrespective of cell flags? */ - force = (force || cell_get_flag(c, cell_flag_do_hydro_limiter)); - - /* Early abort? */ - if (c->hydro.count == 0) { - - /* Clear the limiter flags. */ - cell_clear_flag( - c, cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); - return; - } - - /* Loop over the progeny ? */ - if (c->split && (force || cell_get_flag(c, cell_flag_do_hydro_sub_limiter))) { - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - struct cell *restrict cp = c->progeny[k]; - - /* Recurse */ - runner_do_limiter(r, cp, force, 0); - - /* And aggregate */ - ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); - ti_hydro_end_max = max(cp->hydro.ti_end_max, ti_hydro_end_max); - ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); - ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); - ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max); - ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); - } - } - - /* Store the updated values */ - c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); - c->hydro.ti_end_max = max(c->hydro.ti_end_max, ti_hydro_end_max); - c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); - c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); - c->grav.ti_end_max = max(c->grav.ti_end_max, ti_gravity_end_max); - c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); - - } else if (!c->split && force) { - - ti_hydro_end_min = c->hydro.ti_end_min; - ti_hydro_end_max = c->hydro.ti_end_max; - ti_hydro_beg_max = c->hydro.ti_beg_max; - ti_gravity_end_min = c->grav.ti_end_min; - ti_gravity_end_max = c->grav.ti_end_max; - ti_gravity_beg_max = c->grav.ti_beg_max; - - /* Loop over the gas particles in this cell. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - struct xpart *restrict xp = &xparts[k]; - - /* Avoid inhibited particles */ - if (part_is_inhibited(p, e)) continue; - - /* If the particle will be active no need to wake it up */ - if (part_is_active(p, e) && p->wakeup != time_bin_not_awake) - p->wakeup = time_bin_not_awake; - - /* Bip, bip, bip... wake-up time */ - if (p->wakeup <= time_bin_awake) { - - /* Apply the limiter and get the new time-step size */ - const integertime_t ti_new_step = timestep_limit_part(p, xp, e); - - /* What is the next sync-point ? */ - ti_hydro_end_min = min(ti_current + ti_new_step, ti_hydro_end_min); - ti_hydro_end_max = max(ti_current + ti_new_step, ti_hydro_end_max); - - /* What is the next starting point for this cell ? */ - ti_hydro_beg_max = max(ti_current, ti_hydro_beg_max); - - /* Also limit the gpart counter-part */ - if (p->gpart != NULL) { - - /* Register the time-bin */ - p->gpart->time_bin = p->time_bin; - - /* What is the next sync-point ? */ - ti_gravity_end_min = - min(ti_current + ti_new_step, ti_gravity_end_min); - ti_gravity_end_max = - max(ti_current + ti_new_step, ti_gravity_end_max); - - /* What is the next starting point for this cell ? */ - ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); - } - } - } - - /* Store the updated values */ - c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); - c->hydro.ti_end_max = max(c->hydro.ti_end_max, ti_hydro_end_max); - c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); - c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); - c->grav.ti_end_max = max(c->grav.ti_end_max, ti_gravity_end_max); - c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); - } - - /* Clear the limiter flags. */ - cell_clear_flag(c, - cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); - - if (timer) TIMER_TOC(timer_do_limiter); -} - -/** - * @brief End the hydro force calculation of all active particles in a cell - * by multiplying the acccelerations by the relevant constants - * - * @param r The #runner thread. - * @param c The #cell. - * @param timer Are we timing this ? - */ -void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer) { - - const struct engine *e = r->e; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_end_hydro_force(r, c->progeny[k], 0); - } else { - - const struct cosmology *cosmo = e->cosmology; - const int count = c->hydro.count; - struct part *restrict parts = c->hydro.parts; - - /* Loop over the gas particles in this cell. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - - if (part_is_active(p, e)) { - - /* Finish the force loop */ - hydro_end_force(p, cosmo); - } - } - } - - if (timer) TIMER_TOC(timer_end_hydro_force); -} - -/** - * @brief End the gravity force calculation of all active particles in a cell - * by multiplying the acccelerations by the relevant constants - * - * @param r The #runner thread. - * @param c The #cell. - * @param timer Are we timing this ? - */ -void runner_do_end_grav_force(struct runner *r, struct cell *c, int timer) { - - const struct engine *e = r->e; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_gravity(c, e)) return; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_end_grav_force(r, c->progeny[k], 0); - } else { - - const struct space *s = e->s; - const int periodic = s->periodic; - const float G_newton = e->physical_constants->const_newton_G; - - /* Potential normalisation in the case of periodic gravity */ - float potential_normalisation = 0.; - if (periodic && (e->policy & engine_policy_self_gravity)) { - const double volume = s->dim[0] * s->dim[1] * s->dim[2]; - const double r_s = e->mesh->r_s; - potential_normalisation = 4. * M_PI * e->total_mass * r_s * r_s / volume; - } - - const int gcount = c->grav.count; - struct gpart *restrict gparts = c->grav.parts; - - /* Loop over the g-particles in this cell. */ - for (int k = 0; k < gcount; k++) { - - /* Get a handle on the gpart. */ - struct gpart *restrict gp = &gparts[k]; - - if (gpart_is_active(gp, e)) { - - /* Finish the force calculation */ - gravity_end_force(gp, G_newton, potential_normalisation, periodic); - -#ifdef SWIFT_MAKE_GRAVITY_GLASS - - /* Negate the gravity forces */ - gp->a_grav[0] *= -1.f; - gp->a_grav[1] *= -1.f; - gp->a_grav[2] *= -1.f; -#endif - -#ifdef SWIFT_NO_GRAVITY_BELOW_ID - - /* Get the ID of the gpart */ - long long id = 0; - if (gp->type == swift_type_gas) - id = e->s->parts[-gp->id_or_neg_offset].id; - else if (gp->type == swift_type_stars) - id = e->s->sparts[-gp->id_or_neg_offset].id; - else if (gp->type == swift_type_black_hole) - error("Unexisting type"); - else - id = gp->id_or_neg_offset; - - /* Cancel gravity forces of these particles */ - if (id < SWIFT_NO_GRAVITY_BELOW_ID) { - - /* Don't move ! */ - gp->a_grav[0] = 0.f; - gp->a_grav[1] = 0.f; - gp->a_grav[2] = 0.f; - } -#endif - -#ifdef SWIFT_DEBUG_CHECKS - if ((e->policy & engine_policy_self_gravity) && - !(e->policy & engine_policy_black_holes)) { - - /* Let's add a self interaction to simplify the count */ - gp->num_interacted++; - - /* Check that this gpart has interacted with all the other - * particles (via direct or multipoles) in the box */ - if (gp->num_interacted != - e->total_nr_gparts - e->count_inhibited_gparts) { - - /* Get the ID of the gpart */ - long long my_id = 0; - if (gp->type == swift_type_gas) - my_id = e->s->parts[-gp->id_or_neg_offset].id; - else if (gp->type == swift_type_stars) - my_id = e->s->sparts[-gp->id_or_neg_offset].id; - else if (gp->type == swift_type_black_hole) - error("Unexisting type"); - else - my_id = gp->id_or_neg_offset; - - error( - "g-particle (id=%lld, type=%s) did not interact " - "gravitationally with all other gparts " - "gp->num_interacted=%lld, total_gparts=%lld (local " - "num_gparts=%zd inhibited_gparts=%lld)", - my_id, part_type_names[gp->type], gp->num_interacted, - e->total_nr_gparts, e->s->nr_gparts, e->count_inhibited_gparts); - } - } -#endif - } - } - } - if (timer) TIMER_TOC(timer_end_grav_force); -} - -/** - * @brief Process all the gas particles in a cell that have been flagged for - * swallowing by a black hole. - * - * This is done by recursing down to the leaf-level and skipping the sub-cells - * that have not been drifted as they would not have any particles with - * swallowing flag. We then loop over the particles with a flag and look into - * the space-wide list of black holes for the particle with the corresponding - * ID. If found, the BH swallows the gas particle and the gas particle is - * removed. If the cell is local, we may be looking for a foreign BH, in which - * case, we do not update the BH (that will be done on its node) but just remove - * the gas particle. - * - * @param r The thread #runner. - * @param c The #cell. - * @param timer Are we timing this? - */ -void runner_do_swallow(struct runner *r, struct cell *c, int timer) { - - struct engine *e = r->e; - struct space *s = e->s; - struct bpart *bparts = s->bparts; - const size_t nr_bpart = s->nr_bparts; -#ifdef WITH_MPI - struct bpart *bparts_foreign = s->bparts_foreign; - const size_t nr_bparts_foreign = s->nr_bparts_foreign; -#endif - - struct part *parts = c->hydro.parts; - struct xpart *xparts = c->hydro.xparts; - - /* Early abort? - * (We only want cells for which we drifted the gas as these are - * the only ones that could have gas particles that have been flagged - * for swallowing) */ - if (c->hydro.count == 0 || c->hydro.ti_old_part != e->ti_current) { - return; - } - - /* Loop over the progeny ? */ - if (c->split) { - for (int k = 0; k < 8; k++) { - if (c->progeny[k] != NULL) { - struct cell *restrict cp = c->progeny[k]; - - runner_do_swallow(r, cp, 0); - } - } - } else { - - /* Loop over all the gas particles in the cell - * Note that the cell (and hence the parts) may be local or foreign. */ - const size_t nr_parts = c->hydro.count; - for (size_t k = 0; k < nr_parts; k++) { - - /* Get a handle on the part. */ - struct part *const p = &parts[k]; - struct xpart *const xp = &xparts[k]; - - /* Ignore inhibited particles (they have already been removed!) */ - if (part_is_inhibited(p, e)) continue; - - /* Get the ID of the black holes that will swallow this part */ - const long long swallow_id = - black_holes_get_swallow_id(&p->black_holes_data); - - /* Has this particle been flagged for swallowing? */ - if (swallow_id >= 0) { - -#ifdef SWIFT_DEBUG_CHECKS - if (p->ti_drift != e->ti_current) - error("Trying to swallow an un-drifted particle."); -#endif - - /* ID of the BH swallowing this particle */ - const long long BH_id = swallow_id; - - /* Have we found this particle's BH already? */ - int found = 0; - - /* Let's look for the hungry black hole in the local list */ - for (size_t i = 0; i < nr_bpart; ++i) { - - /* Get a handle on the bpart. */ - struct bpart *bp = &bparts[i]; - - if (bp->id == BH_id) { - - /* Lock the space as we are going to work directly on the bpart list - */ - lock_lock(&s->lock); - - /* Swallow the gas particle (i.e. update the BH properties) */ - black_holes_swallow_part(bp, p, xp, e->cosmology); - - /* Release the space as we are done updating the bpart */ - if (lock_unlock(&s->lock) != 0) - error("Failed to unlock the space."); - - message("BH %lld swallowing particle %lld", bp->id, p->id); - - /* If the gas particle is local, remove it */ - if (c->nodeID == e->nodeID) { - - message("BH %lld removing particle %lld", bp->id, p->id); - - lock_lock(&e->s->lock); - - /* Re-check that the particle has not been removed - * by another thread before we do the deed. */ - if (!part_is_inhibited(p, e)) { - - /* Finally, remove the gas particle from the system */ - struct gpart *gp = p->gpart; - cell_remove_part(e, c, p, xp); - cell_remove_gpart(e, c, gp); - } - - if (lock_unlock(&e->s->lock) != 0) - error("Failed to unlock the space!"); - } - - /* In any case, prevent the particle from being re-swallowed */ - black_holes_mark_as_swallowed(&p->black_holes_data); - - found = 1; - break; - } - - } /* Loop over local BHs */ - -#ifdef WITH_MPI - - /* We could also be in the case of a local gas particle being - * swallowed by a foreign BH. In this case, we won't update the - * BH but just remove the particle from the local list. */ - if (c->nodeID == e->nodeID && !found) { - - /* Let's look for the foreign hungry black hole */ - for (size_t i = 0; i < nr_bparts_foreign; ++i) { - - /* Get a handle on the bpart. */ - struct bpart *bp = &bparts_foreign[i]; - - if (bp->id == BH_id) { - - message("BH %lld removing particle %lld (foreign BH case)", - bp->id, p->id); - - lock_lock(&e->s->lock); - - /* Re-check that the particle has not been removed - * by another thread before we do the deed. */ - if (!part_is_inhibited(p, e)) { - - /* Finally, remove the gas particle from the system */ - struct gpart *gp = p->gpart; - cell_remove_part(e, c, p, xp); - cell_remove_gpart(e, c, gp); - } - - if (lock_unlock(&e->s->lock) != 0) - error("Failed to unlock the space!"); - - found = 1; - break; - } - } /* Loop over foreign BHs */ - } /* Is the cell local? */ -#endif - - /* If we have a local particle, we must have found the BH in one - * of our list of black holes. */ - if (c->nodeID == e->nodeID && !found) { - error("Gas particle %lld could not find BH %lld to be swallowed", - p->id, swallow_id); - } - } /* Part was flagged for swallowing */ - } /* Loop over the parts */ - } /* Cell is not split */ -} - -/** - * @brief Processing of gas particles to swallow - self task case. - * - * @param r The thread #runner. - * @param c The #cell. - * @param timer Are we timing this? - */ -void runner_do_swallow_self(struct runner *r, struct cell *c, int timer) { - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != r->e->nodeID) error("Running self task on foreign node"); - if (!cell_is_active_black_holes(c, r->e)) - error("Running self task on inactive cell"); -#endif - - runner_do_swallow(r, c, timer); -} - -/** - * @brief Processing of gas particles to swallow - pair task case. - * - * @param r The thread #runner. - * @param ci First #cell. - * @param cj Second #cell. - * @param timer Are we timing this? - */ -void runner_do_swallow_pair(struct runner *r, struct cell *ci, struct cell *cj, - int timer) { - - const struct engine *e = r->e; - -#ifdef SWIFT_DEBUG_CHECKS - if (ci->nodeID != e->nodeID && cj->nodeID != e->nodeID) - error("Running pair task on foreign node"); - if (!cell_is_active_black_holes(ci, e) && !cell_is_active_black_holes(cj, e)) - error("Running pair task with two inactive cells"); -#endif - - /* Run the swallowing loop only in the cell that is the neighbour of the - * active BH */ - if (cell_is_active_black_holes(cj, e)) runner_do_swallow(r, ci, timer); - if (cell_is_active_black_holes(ci, e)) runner_do_swallow(r, cj, timer); -} - -/** - * @brief Construct the cell properties from the received #part. - * - * @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_part(struct runner *r, struct cell *c, int clear_sorts, - int timer) { -#ifdef WITH_MPI - - const struct part *restrict parts = c->hydro.parts; - const size_t nr_parts = c->hydro.count; - const integertime_t ti_current = r->e->ti_current; - - TIMER_TIC; - - integertime_t ti_hydro_end_min = max_nr_timesteps; - integertime_t ti_hydro_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 - - /* Clear this cell's sorted mask. */ - if (clear_sorts) c->hydro.sorted = 0; - - /* If this cell is a leaf, collect the particle data. */ - if (!c->split) { - - /* Collect everything... */ - for (size_t k = 0; k < nr_parts; k++) { - if (parts[k].time_bin == time_bin_inhibited) continue; - time_bin_min = min(time_bin_min, parts[k].time_bin); - time_bin_max = max(time_bin_max, parts[k].time_bin); - h_max = max(h_max, parts[k].h); - } - - /* Convert into a time */ - ti_hydro_end_min = get_integer_time_end(ti_current, time_bin_min); - ti_hydro_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]->hydro.count > 0) { - runner_do_recv_part(r, c->progeny[k], clear_sorts, 0); - ti_hydro_end_min = - min(ti_hydro_end_min, c->progeny[k]->hydro.ti_end_min); - ti_hydro_end_max = - max(ti_hydro_end_max, c->progeny[k]->hydro.ti_end_max); - h_max = max(h_max, c->progeny[k]->hydro.h_max); - } - } - } - -#ifdef SWIFT_DEBUG_CHECKS - if (ti_hydro_end_min < ti_current) - error( - "Received a cell at an incorrect time c->ti_end_min=%lld, " - "e->ti_current=%lld.", - ti_hydro_end_min, ti_current); -#endif - - /* ... and store. */ - // c->hydro.ti_end_min = ti_hydro_end_min; - // c->hydro.ti_end_max = ti_hydro_end_max; - c->hydro.ti_old_part = ti_current; - c->hydro.h_max = h_max; - - if (timer) TIMER_TOC(timer_dorecv_part); - -#else - error("SWIFT was not compiled with MPI support."); -#endif -} - -/** - * @brief Construct the cell properties from the received #gpart. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_recv_gpart(struct runner *r, struct cell *c, int timer) { - -#ifdef WITH_MPI - - const struct gpart *restrict gparts = c->grav.parts; - const size_t nr_gparts = c->grav.count; - const integertime_t ti_current = r->e->ti_current; - - TIMER_TIC; - - integertime_t ti_gravity_end_min = max_nr_timesteps; - integertime_t ti_gravity_end_max = 0; - timebin_t time_bin_min = num_time_bins; - timebin_t time_bin_max = 0; - -#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_gparts; k++) { - if (gparts[k].time_bin == time_bin_inhibited) continue; - time_bin_min = min(time_bin_min, gparts[k].time_bin); - time_bin_max = max(time_bin_max, gparts[k].time_bin); - } - - /* Convert into a time */ - ti_gravity_end_min = get_integer_time_end(ti_current, time_bin_min); - ti_gravity_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]->grav.count > 0) { - runner_do_recv_gpart(r, c->progeny[k], 0); - ti_gravity_end_min = - min(ti_gravity_end_min, c->progeny[k]->grav.ti_end_min); - ti_gravity_end_max = - max(ti_gravity_end_max, c->progeny[k]->grav.ti_end_max); - } - } - } - -#ifdef SWIFT_DEBUG_CHECKS - if (ti_gravity_end_min < ti_current) - error( - "Received a cell at an incorrect time c->ti_end_min=%lld, " - "e->ti_current=%lld.", - ti_gravity_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->grav.ti_old_part = ti_current; - - if (timer) TIMER_TOC(timer_dorecv_gpart); - -#else - error("SWIFT was not compiled with MPI support."); -#endif -} - -/** - * @brief Construct the cell properties from the received #spart. - * - * @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_spart(struct runner *r, struct cell *c, int clear_sorts, - int timer) { - -#ifdef WITH_MPI - - struct spart *restrict sparts = c->stars.parts; - const size_t nr_sparts = c->stars.count; - const integertime_t ti_current = r->e->ti_current; - - TIMER_TIC; - - integertime_t ti_stars_end_min = max_nr_timesteps; - integertime_t ti_stars_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 - - /* Clear this cell's sorted mask. */ - if (clear_sorts) c->stars.sorted = 0; - - /* If this cell is a leaf, collect the particle data. */ - if (!c->split) { - - /* Collect everything... */ - for (size_t k = 0; k < nr_sparts; k++) { -#ifdef DEBUG_INTERACTIONS_STARS - sparts[k].num_ngb_force = 0; -#endif - if (sparts[k].time_bin == time_bin_inhibited) continue; - time_bin_min = min(time_bin_min, sparts[k].time_bin); - time_bin_max = max(time_bin_max, sparts[k].time_bin); - h_max = max(h_max, sparts[k].h); - } - - /* Convert into a time */ - ti_stars_end_min = get_integer_time_end(ti_current, time_bin_min); - ti_stars_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]->stars.count > 0) { - runner_do_recv_spart(r, c->progeny[k], clear_sorts, 0); - ti_stars_end_min = - min(ti_stars_end_min, c->progeny[k]->stars.ti_end_min); - ti_stars_end_max = - max(ti_stars_end_max, c->progeny[k]->stars.ti_end_max); - h_max = max(h_max, c->progeny[k]->stars.h_max); - } - } - } - -#ifdef SWIFT_DEBUG_CHECKS - if (ti_stars_end_min < ti_current && - !(r->e->policy & engine_policy_star_formation)) - error( - "Received a cell at an incorrect time c->ti_end_min=%lld, " - "e->ti_current=%lld.", - ti_stars_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->stars.ti_old_part = ti_current; - c->stars.h_max = h_max; - - if (timer) TIMER_TOC(timer_dorecv_spart); - -#else - error("SWIFT was not compiled with MPI support."); -#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]->black_holes.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. - * - * @param data A pointer to this thread's data. - */ -void *runner_main(void *data) { - - struct runner *r = (struct runner *)data; - struct engine *e = r->e; - struct scheduler *sched = &e->sched; - unsigned int seed = r->id; - pthread_setspecific(sched->local_seed_pointer, &seed); - /* Main loop. */ - while (1) { - - /* Wait at the barrier. */ - engine_barrier(e); - - /* Can we go home yet? */ - if (e->step_props & engine_step_prop_done) break; - - /* Re-set the pointer to the previous task, as there is none. */ - struct task *t = NULL; - struct task *prev = NULL; - - /* Loop while there are tasks... */ - while (1) { - - /* If there's no old task, try to get a new one. */ - if (t == NULL) { - - /* Get the task. */ - TIMER_TIC - t = scheduler_gettask(sched, r->qid, prev); - TIMER_TOC(timer_gettask); - - /* Did I get anything? */ - if (t == NULL) break; - } - - /* Get the cells. */ - struct cell *ci = t->ci; - struct cell *cj = t->cj; - -#ifdef SWIFT_DEBUG_TASKS - /* Mark the thread we run on */ - t->rid = r->cpuid; - - /* And recover the pair direction */ - if (t->type == task_type_pair || t->type == task_type_sub_pair) { - struct cell *ci_temp = ci; - struct cell *cj_temp = cj; - double shift[3]; - t->sid = space_getsid(e->s, &ci_temp, &cj_temp, shift); - } else { - t->sid = -1; - } -#endif - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that we haven't scheduled an inactive task */ - t->ti_run = e->ti_current; - /* Store the task that will be running (for debugging only) */ - r->t = t; -#endif - - /* Different types of tasks... */ - switch (t->type) { - case task_type_self: - if (t->subtype == task_subtype_density) - runner_doself1_branch_density(r, ci); -#ifdef EXTRA_HYDRO_LOOP - else if (t->subtype == task_subtype_gradient) - runner_doself1_branch_gradient(r, ci); -#endif - else if (t->subtype == task_subtype_force) - runner_doself2_branch_force(r, ci); - else if (t->subtype == task_subtype_limiter) - runner_doself2_branch_limiter(r, ci); - else if (t->subtype == task_subtype_grav) - runner_doself_recursive_grav(r, ci, 1); - else if (t->subtype == task_subtype_external_grav) - runner_do_grav_external(r, ci, 1); - else if (t->subtype == task_subtype_stars_density) - 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_swallow) - runner_doself_branch_bh_swallow(r, ci); - else if (t->subtype == task_subtype_do_swallow) - runner_do_swallow_self(r, ci, 1); - 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; - - case task_type_pair: - if (t->subtype == task_subtype_density) - runner_dopair1_branch_density(r, ci, cj); -#ifdef EXTRA_HYDRO_LOOP - else if (t->subtype == task_subtype_gradient) - runner_dopair1_branch_gradient(r, ci, cj); -#endif - else if (t->subtype == task_subtype_force) - runner_dopair2_branch_force(r, ci, cj); - else if (t->subtype == task_subtype_limiter) - runner_dopair2_branch_limiter(r, ci, cj); - else if (t->subtype == task_subtype_grav) - runner_dopair_recursive_grav(r, ci, cj, 1); - else if (t->subtype == task_subtype_stars_density) - 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_swallow) - runner_dopair_branch_bh_swallow(r, ci, cj); - else if (t->subtype == task_subtype_do_swallow) { - runner_do_swallow_pair(r, ci, cj, 1); - } 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; - - case task_type_sub_self: - if (t->subtype == task_subtype_density) - runner_dosub_self1_density(r, ci, 1); -#ifdef EXTRA_HYDRO_LOOP - else if (t->subtype == task_subtype_gradient) - runner_dosub_self1_gradient(r, ci, 1); -#endif - else if (t->subtype == task_subtype_force) - runner_dosub_self2_force(r, ci, 1); - else if (t->subtype == task_subtype_limiter) - runner_dosub_self2_limiter(r, ci, 1); - else if (t->subtype == task_subtype_stars_density) - 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_swallow) - runner_dosub_self_bh_swallow(r, ci, 1); - else if (t->subtype == task_subtype_do_swallow) - runner_do_swallow_self(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; - - case task_type_sub_pair: - if (t->subtype == task_subtype_density) - runner_dosub_pair1_density(r, ci, cj, 1); -#ifdef EXTRA_HYDRO_LOOP - else if (t->subtype == task_subtype_gradient) - runner_dosub_pair1_gradient(r, ci, cj, 1); -#endif - else if (t->subtype == task_subtype_force) - runner_dosub_pair2_force(r, ci, cj, 1); - else if (t->subtype == task_subtype_limiter) - runner_dosub_pair2_limiter(r, ci, cj, 1); - else if (t->subtype == task_subtype_stars_density) - 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_swallow) - runner_dosub_pair_bh_swallow(r, ci, cj, 1); - else if (t->subtype == task_subtype_do_swallow) { - runner_do_swallow_pair(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; - - case task_type_sort: - /* Cleanup only if any of the indices went stale. */ - runner_do_hydro_sort( - r, ci, t->flags, - ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, 1); - /* Reset the sort flags as our work here is done. */ - t->flags = 0; - break; - case task_type_stars_sort: - /* Cleanup only if any of the indices went stale. */ - runner_do_stars_sort( - r, ci, t->flags, - ci->stars.dx_max_sort_old > space_maxreldx * ci->dmin, 1); - /* Reset the sort flags as our work here is done. */ - t->flags = 0; - break; - case task_type_init_grav: - runner_do_init_grav(r, ci, 1); - break; - case task_type_ghost: - runner_do_ghost(r, ci, 1); - break; -#ifdef EXTRA_HYDRO_LOOP - case task_type_extra_ghost: - runner_do_extra_ghost(r, ci, 1); - break; -#endif - case task_type_stars_ghost: - runner_do_stars_ghost(r, ci, 1); - break; - case task_type_bh_density_ghost: - runner_do_black_holes_density_ghost(r, ci, 1); - break; - case task_type_bh_swallow_ghost2: - runner_do_black_holes_swallow_ghost(r, ci, 1); - break; - case task_type_drift_part: - runner_do_drift_part(r, ci, 1); - break; - case task_type_drift_spart: - runner_do_drift_spart(r, ci, 1); - break; - case task_type_drift_bpart: - runner_do_drift_bpart(r, ci, 1); - break; - case task_type_drift_gpart: - runner_do_drift_gpart(r, ci, 1); - break; - case task_type_kick1: - runner_do_kick1(r, ci, 1); - break; - case task_type_kick2: - runner_do_kick2(r, ci, 1); - break; - case task_type_end_hydro_force: - runner_do_end_hydro_force(r, ci, 1); - break; - case task_type_end_grav_force: - runner_do_end_grav_force(r, ci, 1); - break; - case task_type_logger: - runner_do_logger(r, ci, 1); - break; - case task_type_timestep: - runner_do_timestep(r, ci, 1); - break; - case task_type_timestep_limiter: - runner_do_limiter(r, ci, 0, 1); - break; -#ifdef WITH_MPI - case task_type_send: - if (t->subtype == task_subtype_tend_part) { - free(t->buff); - } else if (t->subtype == task_subtype_tend_gpart) { - free(t->buff); - } else if (t->subtype == task_subtype_tend_spart) { - free(t->buff); - } else if (t->subtype == task_subtype_tend_bpart) { - free(t->buff); - } else if (t->subtype == task_subtype_sf_counts) { - free(t->buff); - } else if (t->subtype == task_subtype_part_swallow) { - free(t->buff); - } - break; - case task_type_recv: - if (t->subtype == task_subtype_tend_part) { - cell_unpack_end_step_hydro(ci, (struct pcell_step_hydro *)t->buff); - free(t->buff); - } else if (t->subtype == task_subtype_tend_gpart) { - cell_unpack_end_step_grav(ci, (struct pcell_step_grav *)t->buff); - free(t->buff); - } else if (t->subtype == task_subtype_tend_spart) { - cell_unpack_end_step_stars(ci, (struct pcell_step_stars *)t->buff); - free(t->buff); - } else if (t->subtype == task_subtype_tend_bpart) { - cell_unpack_end_step_black_holes( - ci, (struct pcell_step_black_holes *)t->buff); - free(t->buff); - } else if (t->subtype == task_subtype_sf_counts) { - cell_unpack_sf_counts(ci, (struct pcell_sf *)t->buff); - cell_clear_stars_sort_flags(ci, /*clear_unused_flags=*/0); - free(t->buff); - } else if (t->subtype == task_subtype_xv) { - runner_do_recv_part(r, ci, 1, 1); - } else if (t->subtype == task_subtype_rho) { - runner_do_recv_part(r, ci, 0, 1); - } else if (t->subtype == task_subtype_gradient) { - runner_do_recv_part(r, ci, 0, 1); - } else if (t->subtype == task_subtype_part_swallow) { - cell_unpack_part_swallow(ci, - (struct black_holes_part_data *)t->buff); - free(t->buff); - } else if (t->subtype == task_subtype_limiter) { - runner_do_recv_part(r, ci, 0, 1); - } else if (t->subtype == task_subtype_gpart) { - 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_rho) { - runner_do_recv_bpart(r, ci, 1, 1); - } else if (t->subtype == task_subtype_bpart_swallow) { - runner_do_recv_bpart(r, ci, 0, 1); - } else if (t->subtype == task_subtype_bpart_feedback) { - runner_do_recv_bpart(r, ci, 0, 1); - } else if (t->subtype == task_subtype_multipole) { - cell_unpack_multipoles(ci, (struct gravity_tensors *)t->buff); - free(t->buff); - } else { - error("Unknown/invalid task subtype (%d).", t->subtype); - } - break; -#endif - case task_type_grav_down: - runner_do_grav_down(r, t->ci, 1); - break; - case task_type_grav_mesh: - runner_do_grav_mesh(r, t->ci, 1); - break; - case task_type_grav_long_range: - runner_do_grav_long_range(r, t->ci, 1); - break; - case task_type_grav_mm: - runner_dopair_grav_mm_progenies(r, t->flags, t->ci, t->cj); - break; - case task_type_cooling: - runner_do_cooling(r, t->ci, 1); - break; - case task_type_star_formation: - runner_do_star_formation(r, t->ci, 1); - break; - case task_type_stars_resort: - runner_do_stars_resort(r, t->ci, 1); - break; - case task_type_fof_self: - runner_do_fof_self(r, t->ci, 1); - break; - case task_type_fof_pair: - runner_do_fof_pair(r, t->ci, t->cj, 1); - break; - default: - error("Unknown/invalid task type (%d).", t->type); - } - -/* Mark that we have run this task on these cells */ -#ifdef SWIFT_DEBUG_CHECKS - if (ci != NULL) { - ci->tasks_executed[t->type]++; - ci->subtasks_executed[t->subtype]++; - } - if (cj != NULL) { - cj->tasks_executed[t->type]++; - cj->subtasks_executed[t->subtype]++; - } - - /* This runner is not doing a task anymore */ - r->t = NULL; -#endif - - /* We're done with this task, see if we get a next one. */ - prev = t; - t = scheduler_done(sched, t); - - } /* main loop. */ - } - - /* Be kind, rewind. */ - return NULL; -} - -/** - * @brief Write the required particles through the logger. - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_logger(struct runner *r, struct cell *c, int timer) { - -#ifdef WITH_LOGGER - TIMER_TIC; - - const struct engine *e = r->e; - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - const int count = c->hydro.count; - - /* Anything to do here? */ - if (!cell_is_starting_hydro(c, e) && !cell_is_starting_gravity(c, e)) return; - - /* Recurse? Avoid spending too much time in useless cells. */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_logger(r, c->progeny[k], 0); - } else { - - /* Loop over the parts in this cell. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - struct xpart *restrict xp = &xparts[k]; - - /* If particle needs to be log */ - /* This is the same function than part_is_active, except for - * debugging checks */ - if (part_is_starting(p, e)) { - - if (logger_should_write(&xp->logger_data, e->logger)) { - /* Write particle */ - /* Currently writing everything, should adapt it through time */ - logger_log_part(e->logger, p, - logger_mask_data[logger_x].mask | - logger_mask_data[logger_v].mask | - logger_mask_data[logger_a].mask | - logger_mask_data[logger_u].mask | - logger_mask_data[logger_h].mask | - logger_mask_data[logger_rho].mask | - logger_mask_data[logger_consts].mask, - &xp->logger_data.last_offset); - - /* Set counter back to zero */ - xp->logger_data.steps_since_last_output = 0; - } else - /* Update counter */ - xp->logger_data.steps_since_last_output += 1; - } - } - } - - if (c->grav.count > 0) error("gparts not implemented"); - - if (c->stars.count > 0) error("sparts not implemented"); - - if (timer) TIMER_TOC(timer_logger); - -#else - error("Logger disabled, please enable it during configuration"); -#endif -} - -/** - * @brief Recursively search for FOF groups in a single cell. - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void runner_do_fof_self(struct runner *r, struct cell *c, int timer) { - - TIMER_TIC; - - const struct engine *e = r->e; - struct space *s = e->s; - const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; - const int periodic = s->periodic; - const struct gpart *const gparts = s->gparts; - const double search_r2 = e->fof_properties->l_x2; - - rec_fof_search_self(e->fof_properties, dim, search_r2, periodic, gparts, c); - - if (timer) TIMER_TOC(timer_fof_self); -} - -/** - * @brief Recursively search for FOF groups between a pair of cells. - * - * @param r runner task - * @param ci cell i - * @param cj cell j - * @param timer 1 if the time is to be recorded. - */ -void runner_do_fof_pair(struct runner *r, struct cell *ci, struct cell *cj, - int timer) { - - TIMER_TIC; - - const struct engine *e = r->e; - struct space *s = e->s; - const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; - const int periodic = s->periodic; - const struct gpart *const gparts = s->gparts; - const double search_r2 = e->fof_properties->l_x2; - - rec_fof_search_pair(e->fof_properties, dim, search_r2, periodic, gparts, ci, - cj); - - if (timer) TIMER_TOC(timer_fof_pair); -} diff --git a/src/runner.h b/src/runner.h index 1dc62ad6f5dc1c92851cf841a4ab55836d084bac..7e8d0459efb5485ea1301c923e8c7a3396b6fc7e 100644 --- a/src/runner.h +++ b/src/runner.h @@ -26,13 +26,21 @@ /* Config parameters. */ #include "../config.h" -/* Includes. */ +/* Local headers. */ #include "cache.h" #include "gravity_cache.h" -#include "task.h" struct cell; struct engine; +struct task; + +/* Unique identifier of loop types */ +#define TASK_LOOP_DENSITY 0 +#define TASK_LOOP_GRADIENT 1 +#define TASK_LOOP_FORCE 2 +#define TASK_LOOP_LIMITER 3 +#define TASK_LOOP_FEEDBACK 4 +#define TASK_LOOP_SWALLOW 5 /** * @brief A struct representing a runner's thread and its data. @@ -75,6 +83,12 @@ struct runner { /* Function prototypes. */ void runner_do_ghost(struct runner *r, struct cell *c, int timer); void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer); +void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer); +void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c, + int timer); +void runner_do_black_holes_swallow_ghost(struct runner *r, struct cell *c, + int timer); +void runner_do_init_grav(struct runner *r, struct cell *c, int timer); void runner_do_hydro_sort(struct runner *r, struct cell *c, int flag, int cleanup, int clock); void runner_do_stars_sort(struct runner *r, struct cell *c, int flag, @@ -84,21 +98,38 @@ void runner_do_all_stars_sort(struct runner *r, struct cell *c); void runner_do_drift_part(struct runner *r, struct cell *c, int timer); void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer); void runner_do_drift_spart(struct runner *r, struct cell *c, int timer); +void runner_do_drift_bpart(struct runner *r, struct cell *c, int timer); void runner_do_kick1(struct runner *r, struct cell *c, int timer); void runner_do_kick2(struct runner *r, struct cell *c, int timer); +void runner_do_timestep(struct runner *r, struct cell *c, int timer); void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer); +void runner_do_end_grav_force(struct runner *r, struct cell *c, int timer); void runner_do_init(struct runner *r, struct cell *c, int timer); void runner_do_cooling(struct runner *r, struct cell *c, int timer); +void runner_do_limiter(struct runner *r, struct cell *c, int force, int timer); +void runner_do_grav_mesh(struct runner *r, struct cell *c, int timer); void runner_do_grav_external(struct runner *r, struct cell *c, int timer); void runner_do_grav_fft(struct runner *r, int timer); void runner_do_logger(struct runner *r, struct cell *c, int timer); void runner_do_fof_self(struct runner *r, struct cell *c, int timer); void runner_do_fof_pair(struct runner *r, struct cell *ci, struct cell *cj, int timer); +void runner_do_gas_swallow_self(struct runner *r, struct cell *c, int timer); +void runner_do_bh_swallow_self(struct runner *r, struct cell *c, int timer); +void runner_do_gas_swallow_pair(struct runner *r, struct cell *ci, + struct cell *cj, int timer); +void runner_do_bh_swallow_pair(struct runner *r, struct cell *ci, + struct cell *cj, int timer); +void runner_do_star_formation(struct runner *r, struct cell *c, int timer); +void runner_do_stars_resort(struct runner *r, struct cell *c, const int timer); + +void runner_do_recv_gpart(struct runner *r, struct cell *c, int timer); +void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts, + int timer); +void runner_do_recv_spart(struct runner *r, struct cell *c, int clear_sorts, + int timer); +void runner_do_recv_bpart(struct runner *r, struct cell *c, int clear_sorts, + int timer); void *runner_main(void *data); -void runner_do_unskip_mapper(void *map_data, int num_elements, - void *extra_data); -void runner_do_drift_all_mapper(void *map_data, int num_elements, - void *extra_data); #endif /* SWIFT_RUNNER_H */ diff --git a/src/runner_black_holes.c b/src/runner_black_holes.c new file mode 100644 index 0000000000000000000000000000000000000000..d9bb62201d7b087670aef0ce2346a51bf61a3868 --- /dev/null +++ b/src/runner_black_holes.c @@ -0,0 +1,459 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "active.h" +#include "black_holes.h" +#include "cell.h" +#include "engine.h" +#include "timers.h" + +/** + * @brief Process all the gas particles in a cell that have been flagged for + * swallowing by a black hole. + * + * This is done by recursing down to the leaf-level and skipping the sub-cells + * that have not been drifted as they would not have any particles with + * swallowing flag. We then loop over the particles with a flag and look into + * the space-wide list of black holes for the particle with the corresponding + * ID. If found, the BH swallows the gas particle and the gas particle is + * removed. If the cell is local, we may be looking for a foreign BH, in which + * case, we do not update the BH (that will be done on its node) but just remove + * the gas particle. + * + * @param r The thread #runner. + * @param c The #cell. + * @param timer Are we timing this? + */ +void runner_do_gas_swallow(struct runner *r, struct cell *c, int timer) { + + struct engine *e = r->e; + struct space *s = e->s; + struct bpart *bparts = s->bparts; + const size_t nr_bpart = s->nr_bparts; +#ifdef WITH_MPI + struct bpart *bparts_foreign = s->bparts_foreign; + const size_t nr_bparts_foreign = s->nr_bparts_foreign; +#endif + + struct part *parts = c->hydro.parts; + struct xpart *xparts = c->hydro.xparts; + + /* Early abort? + * (We only want cells for which we drifted the gas as these are + * the only ones that could have gas particles that have been flagged + * for swallowing) */ + if (c->hydro.count == 0 || c->hydro.ti_old_part != e->ti_current) { + return; + } + + /* Loop over the progeny ? */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *restrict cp = c->progeny[k]; + + runner_do_gas_swallow(r, cp, 0); + } + } + } else { + + /* Loop over all the gas particles in the cell + * Note that the cell (and hence the parts) may be local or foreign. */ + const size_t nr_parts = c->hydro.count; + for (size_t k = 0; k < nr_parts; k++) { + + /* Get a handle on the part. */ + struct part *const p = &parts[k]; + struct xpart *const xp = &xparts[k]; + + /* Ignore inhibited particles (they have already been removed!) */ + if (part_is_inhibited(p, e)) continue; + + /* Get the ID of the black holes that will swallow this part */ + const long long swallow_id = + black_holes_get_part_swallow_id(&p->black_holes_data); + + /* Has this particle been flagged for swallowing? */ + if (swallow_id >= 0) { + +#ifdef SWIFT_DEBUG_CHECKS + if (p->ti_drift != e->ti_current) + error("Trying to swallow an un-drifted particle."); +#endif + + /* ID of the BH swallowing this particle */ + const long long BH_id = swallow_id; + + /* Have we found this particle's BH already? */ + int found = 0; + + /* Let's look for the hungry black hole in the local list */ + for (size_t i = 0; i < nr_bpart; ++i) { + + /* Get a handle on the bpart. */ + struct bpart *bp = &bparts[i]; + + if (bp->id == BH_id) { + + /* Lock the space as we are going to work directly on the bpart list + */ + lock_lock(&s->lock); + + /* Swallow the gas particle (i.e. update the BH properties) */ + black_holes_swallow_part(bp, p, xp, e->cosmology); + + /* Release the space as we are done updating the bpart */ + if (lock_unlock(&s->lock) != 0) + error("Failed to unlock the space."); + + message("BH %lld swallowing gas particle %lld", bp->id, p->id); + + /* If the gas particle is local, remove it */ + if (c->nodeID == e->nodeID) { + + message("BH %lld removing gas particle %lld", bp->id, p->id); + + lock_lock(&e->s->lock); + + /* Re-check that the particle has not been removed + * by another thread before we do the deed. */ + if (!part_is_inhibited(p, e)) { + + /* Finally, remove the gas particle from the system + * Recall that the gpart associated with it is also removed + * at the same time. */ + cell_remove_part(e, c, p, xp); + } + + if (lock_unlock(&e->s->lock) != 0) + error("Failed to unlock the space!"); + } + + /* In any case, prevent the particle from being re-swallowed */ + black_holes_mark_part_as_swallowed(&p->black_holes_data); + + found = 1; + break; + } + + } /* Loop over local BHs */ + +#ifdef WITH_MPI + + /* We could also be in the case of a local gas particle being + * swallowed by a foreign BH. In this case, we won't update the + * BH but just remove the particle from the local list. */ + if (c->nodeID == e->nodeID && !found) { + + /* Let's look for the foreign hungry black hole */ + for (size_t i = 0; i < nr_bparts_foreign; ++i) { + + /* Get a handle on the bpart. */ + struct bpart *bp = &bparts_foreign[i]; + + if (bp->id == BH_id) { + + message("BH %lld removing gas particle %lld (foreign BH case)", + bp->id, p->id); + + lock_lock(&e->s->lock); + + /* Re-check that the particle has not been removed + * by another thread before we do the deed. */ + if (!part_is_inhibited(p, e)) { + + /* Finally, remove the gas particle from the system */ + cell_remove_part(e, c, p, xp); + } + + if (lock_unlock(&e->s->lock) != 0) + error("Failed to unlock the space!"); + + found = 1; + break; + } + } /* Loop over foreign BHs */ + } /* Is the cell local? */ +#endif + + /* If we have a local particle, we must have found the BH in one + * of our list of black holes. */ + if (c->nodeID == e->nodeID && !found) { + error("Gas particle %lld could not find BH %lld to be swallowed", + p->id, swallow_id); + } + } /* Part was flagged for swallowing */ + } /* Loop over the parts */ + } /* Cell is not split */ +} + +/** + * @brief Processing of gas particles to swallow - self task case. + * + * @param r The thread #runner. + * @param c The #cell. + * @param timer Are we timing this? + */ +void runner_do_gas_swallow_self(struct runner *r, struct cell *c, int timer) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != r->e->nodeID) error("Running self task on foreign node"); + if (!cell_is_active_black_holes(c, r->e)) + error("Running self task on inactive cell"); +#endif + + runner_do_gas_swallow(r, c, timer); +} + +/** + * @brief Processing of gas particles to swallow - pair task case. + * + * @param r The thread #runner. + * @param ci First #cell. + * @param cj Second #cell. + * @param timer Are we timing this? + */ +void runner_do_gas_swallow_pair(struct runner *r, struct cell *ci, + struct cell *cj, int timer) { + + const struct engine *e = r->e; + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != e->nodeID && cj->nodeID != e->nodeID) + error("Running pair task on foreign node"); +#endif + + /* Run the swallowing loop only in the cell that is the neighbour of the + * active BH */ + if (cell_is_active_black_holes(cj, e)) runner_do_gas_swallow(r, ci, timer); + if (cell_is_active_black_holes(ci, e)) runner_do_gas_swallow(r, cj, timer); +} + +/** + * @brief Process all the BH particles in a cell that have been flagged for + * swallowing by a black hole. + * + * This is done by recursing down to the leaf-level and skipping the sub-cells + * that have not been drifted as they would not have any particles with + * swallowing flag. We then loop over the particles with a flag and look into + * the space-wide list of black holes for the particle with the corresponding + * ID. If found, the BH swallows the BH particle and the BH particle is + * removed. If the cell is local, we may be looking for a foreign BH, in which + * case, we do not update the BH (that will be done on its node) but just remove + * the BH particle. + * + * @param r The thread #runner. + * @param c The #cell. + * @param timer Are we timing this? + */ +void runner_do_bh_swallow(struct runner *r, struct cell *c, int timer) { + + struct engine *e = r->e; + struct space *s = e->s; + struct bpart *bparts = s->bparts; + const size_t nr_bpart = s->nr_bparts; +#ifdef WITH_MPI + struct bpart *bparts_foreign = s->bparts_foreign; + const size_t nr_bparts_foreign = s->nr_bparts_foreign; +#endif + + struct bpart *cell_bparts = c->black_holes.parts; + + /* Early abort? + * (We only want cells for which we drifted the BH as these are + * the only ones that could have BH particles that have been flagged + * for swallowing) */ + if (c->black_holes.count == 0 || + c->black_holes.ti_old_part != e->ti_current) { + return; + } + + /* Loop over the progeny ? */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *restrict cp = c->progeny[k]; + + runner_do_bh_swallow(r, cp, 0); + } + } + } else { + + /* Loop over all the gas particles in the cell + * Note that the cell (and hence the bparts) may be local or foreign. */ + const size_t nr_cell_bparts = c->black_holes.count; + for (size_t k = 0; k < nr_cell_bparts; k++) { + + /* Get a handle on the part. */ + struct bpart *const cell_bp = &cell_bparts[k]; + + /* Ignore inhibited particles (they have already been removed!) */ + if (bpart_is_inhibited(cell_bp, e)) continue; + + /* Get the ID of the black holes that will swallow this part */ + const long long swallow_id = + black_holes_get_bpart_swallow_id(&cell_bp->merger_data); + + /* message("OO id=%lld swallow_id = %lld", cell_bp->id, */ + /* swallow_id); */ + + /* Has this particle been flagged for swallowing? */ + if (swallow_id >= 0) { + +#ifdef SWIFT_DEBUG_CHECKS + if (cell_bp->ti_drift != e->ti_current) + error("Trying to swallow an un-drifted particle."); +#endif + + /* ID of the BH swallowing this particle */ + const long long BH_id = swallow_id; + + /* Have we found this particle's BH already? */ + int found = 0; + + /* Let's look for the hungry black hole in the local list */ + for (size_t i = 0; i < nr_bpart; ++i) { + + /* Get a handle on the bpart. */ + struct bpart *bp = &bparts[i]; + + if (bp->id == BH_id) { + + /* Lock the space as we are going to work directly on the bpart list + */ + lock_lock(&s->lock); + + /* Swallow the gas particle (i.e. update the BH properties) */ + black_holes_swallow_bpart(bp, cell_bp, e->cosmology); + + /* Release the space as we are done updating the bpart */ + if (lock_unlock(&s->lock) != 0) + error("Failed to unlock the space."); + + message("BH %lld swallowing BH particle %lld", bp->id, cell_bp->id); + + /* If the gas particle is local, remove it */ + if (c->nodeID == e->nodeID) { + + message("BH %lld removing BH particle %lld", bp->id, cell_bp->id); + + /* Finally, remove the gas particle from the system + * Recall that the gpart associated with it is also removed + * at the same time. */ + cell_remove_bpart(e, c, cell_bp); + } + + /* In any case, prevent the particle from being re-swallowed */ + black_holes_mark_bpart_as_merged(&cell_bp->merger_data); + + found = 1; + break; + } + + } /* Loop over local BHs */ + +#ifdef WITH_MPI + + /* We could also be in the case of a local BH particle being + * swallowed by a foreign BH. In this case, we won't update the + * foreign BH but just remove the particle from the local list. */ + if (c->nodeID == e->nodeID && !found) { + + /* Let's look for the foreign hungry black hole */ + for (size_t i = 0; i < nr_bparts_foreign; ++i) { + + /* Get a handle on the bpart. */ + struct bpart *bp = &bparts_foreign[i]; + + if (bp->id == BH_id) { + + message("BH %lld removing BH particle %lld (foreign BH case)", + bp->id, cell_bp->id); + + /* Finally, remove the gas particle from the system */ + cell_remove_bpart(e, c, cell_bp); + + found = 1; + break; + } + } /* Loop over foreign BHs */ + } /* Is the cell local? */ +#endif + + /* If we have a local particle, we must have found the BH in one + * of our list of black holes. */ + if (c->nodeID == e->nodeID && !found) { + error("BH particle %lld could not find BH %lld to be swallowed", + cell_bp->id, swallow_id); + } + } /* Part was flagged for swallowing */ + } /* Loop over the parts */ + } /* Cell is not split */ +} + +/** + * @brief Processing of bh particles to swallow - self task case. + * + * @param r The thread #runner. + * @param c The #cell. + * @param timer Are we timing this? + */ +void runner_do_bh_swallow_self(struct runner *r, struct cell *c, int timer) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != r->e->nodeID) error("Running self task on foreign node"); + if (!cell_is_active_black_holes(c, r->e)) + error("Running self task on inactive cell"); +#endif + + runner_do_bh_swallow(r, c, timer); +} + +/** + * @brief Processing of bh particles to swallow - pair task case. + * + * @param r The thread #runner. + * @param ci First #cell. + * @param cj Second #cell. + * @param timer Are we timing this? + */ +void runner_do_bh_swallow_pair(struct runner *r, struct cell *ci, + struct cell *cj, int timer) { + + const struct engine *e = r->e; + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != e->nodeID && cj->nodeID != e->nodeID) + error("Running pair task on foreign node"); +#endif + + /* Run the swallowing loop only in the cell that is the neighbour of the + * active BH */ + if (cell_is_active_black_holes(cj, e)) runner_do_bh_swallow(r, ci, timer); + if (cell_is_active_black_holes(ci, e)) runner_do_bh_swallow(r, cj, timer); +} diff --git a/src/runner_doiact_black_holes.c b/src/runner_doiact_black_holes.c new file mode 100644 index 0000000000000000000000000000000000000000..5c139eada6cf7403076194c42261948db5e0f7f4 --- /dev/null +++ b/src/runner_doiact_black_holes.c @@ -0,0 +1,53 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Local headers. */ +#include "active.h" +#include "black_holes.h" +#include "cell.h" +#include "engine.h" +#include "runner.h" +#include "space_getsid.h" +#include "timers.h" + +/* Import the black hole density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_functions_black_holes.h" +#undef FUNCTION_TASK_LOOP +#undef FUNCTION + +/* Import the black hole feedback loop functions. */ +#define FUNCTION swallow +#define FUNCTION_TASK_LOOP TASK_LOOP_SWALLOW +#include "runner_doiact_functions_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_functions_black_holes.h" +#undef FUNCTION_TASK_LOOP +#undef FUNCTION diff --git a/src/runner_doiact_black_holes.h b/src/runner_doiact_black_holes.h index b2232481a027b6d5e51fc061114005a2bf08c527..763e557babb9ca94a05a28d1ea5ed0f1141684ff 100644 --- a/src/runner_doiact_black_holes.h +++ b/src/runner_doiact_black_holes.h @@ -79,700 +79,26 @@ #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) +#define _IACT_BH_GAS(f) PASTE(runner_iact_nonsym_bh_gas, f) +#define IACT_BH_GAS _IACT_BH_GAS(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 bid = 0; bid < bcount; bid++) { - - /* Get a hold of the ith bpart in ci. */ - struct bpart *restrict bi = &bparts[bid]; - - /* Skip inactive particles */ - if (!bpart_is_active(bi, e)) continue; - - const float hi = bi->h; - const float hig2 = hi * hi * kernel_gamma2; - const float bix[3] = {(float)(bi->x[0] - c->loc[0]), - (float)(bi->x[1] - c->loc[1]), - (float)(bi->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] = {bix[0] - pjx[0], bix[1] - pjx[1], bix[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, bi, 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"); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - 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 bid = 0; bid < bcount_i; bid++) { - - /* Get a hold of the ith bpart in ci. */ - struct bpart *restrict bi = &bparts_i[bid]; - - /* Skip inactive particles */ - if (!bpart_is_active(bi, e)) continue; - - const float hi = bi->h; - const float hig2 = hi * hi * kernel_gamma2; - const float bix[3] = {(float)(bi->x[0] - (cj->loc[0] + shift[0])), - (float)(bi->x[1] - (cj->loc[1] + shift[1])), - (float)(bi->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] = {bix[0] - pjx[0], bix[1] - pjx[1], bix[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, bi, 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; +#define _IACT_BH_BH(f) PASTE(runner_iact_nonsym_bh_bh, f) +#define IACT_BH_BH _IACT_BH_BH(FUNCTION) -#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; -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - /* 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; -#else - /* The swallow task is executed on both sides */ - const int do_ci_bh = 1; - const int do_cj_bh = 1; -#endif +void DOSELF1_BRANCH_BH(struct runner *r, struct cell *c); +void DOPAIR1_BRANCH_BH(struct runner *r, struct cell *ci, struct cell *cj); - 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 bid = 0; bid < bcount; bid++) { - - /* Get a hold of the ith part in ci. */ - struct bpart *restrict bi = &bparts_i[ind[bid]]; - - const double bix = bi->x[0] - (shift[0]); - const double biy = bi->x[1] - (shift[1]); - const double biz = bi->x[2] - (shift[2]); - const float hi = bi->h; - const float hig2 = hi * hi * kernel_gamma2; - -#ifdef SWIFT_DEBUG_CHECKS - if (!bpart_is_active(bi, 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)(bix - pjx), (float)(biy - pjy), - (float)(biz - 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, bi, 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 bid = 0; bid < bcount; bid++) { - - /* Get a hold of the ith part in ci. */ - struct bpart *bi = &bparts[ind[bid]]; - const float bix[3] = {(float)(bi->x[0] - ci->loc[0]), - (float)(bi->x[1] - ci->loc[1]), - (float)(bi->x[2] - ci->loc[2])}; - const float hi = bi->h; - const float hig2 = hi * hi * kernel_gamma2; - -#ifdef SWIFT_DEBUG_CHECKS - if (!bpart_is_active(bi, 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] = {bix[0] - pjx[0], bix[1] - pjx[1], bix[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, bi, pj, xpj, cosmo, ti_current); - } - } /* loop over the parts in cj. */ - } /* loop over the parts in ci. */ -} +void DOSUB_SELF1_BH(struct runner *r, struct cell *ci, int gettimer); +void DOSUB_PAIR1_BH(struct runner *r, struct cell *ci, struct cell *cj, + int gettimer); -/** - * @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. - */ + const int bcount); 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); -} + struct cell *restrict cj); 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; -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - /* 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; -#else - /* The swallow task is executed on both sides */ - const int do_ci_bh = 1; - const int do_cj_bh = 1; -#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; -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - /* 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; -#else - const int do_ci_bh = 1; - const int do_cj_bh = 1; -#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); -} + int *ind, const int bcount, struct cell *cj, int gettimer); diff --git a/src/runner_doiact_functions_black_holes.h b/src/runner_doiact_functions_black_holes.h new file mode 100644 index 0000000000000000000000000000000000000000..f8af37c751a9f7a89455ae5c9a7ef72ec55a1c64 --- /dev/null +++ b/src/runner_doiact_functions_black_holes.h @@ -0,0 +1,877 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ + +/* Before including this file, define FUNCTION, which is the + name of the interaction function. This creates the interaction functions + runner_dopair_FUNCTION, runner_dopair_FUNCTION_naive, runner_doself_FUNCTION, + and runner_dosub_FUNCTION calling the pairwise interaction function + runner_iact_FUNCTION. */ + +#include "runner_doiact_black_holes.h" + +/** + * @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->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; + + /* Do we actually have any gas neighbours? */ + if (c->hydro.count != 0) { + + /* Loop over the bparts in ci. */ + for (int bid = 0; bid < bcount; bid++) { + + /* Get a hold of the ith bpart in ci. */ + struct bpart *restrict bi = &bparts[bid]; + + /* Skip inactive particles */ + if (!bpart_is_active(bi, e)) continue; + + const float hi = bi->h; + const float hig2 = hi * hi * kernel_gamma2; + const float bix[3] = {(float)(bi->x[0] - c->loc[0]), + (float)(bi->x[1] - c->loc[1]), + (float)(bi->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] = {bix[0] - pjx[0], bix[1] - pjx[1], bix[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 (bi->ti_drift != e->ti_current) + error("Particle bi not drifted to current time"); + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + if (r2 < hig2) { + IACT_BH_GAS(r2, dx, hi, hj, bi, pj, xpj, cosmo, e->gravity_properties, + ti_current); + } + } /* loop over the parts in ci. */ + } /* loop over the bparts in ci. */ + } /* Do we have gas particles in the cell? */ + + /* When doing BH swallowing, we need a quick loop also over the BH + * neighbours */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + + /* Loop over the bparts in ci. */ + for (int bid = 0; bid < bcount; bid++) { + + /* Get a hold of the ith bpart in ci. */ + struct bpart *restrict bi = &bparts[bid]; + + /* Skip inactive particles */ + if (!bpart_is_active(bi, e)) continue; + + const float hi = bi->h; + const float hig2 = hi * hi * kernel_gamma2; + const float bix[3] = {(float)(bi->x[0] - c->loc[0]), + (float)(bi->x[1] - c->loc[1]), + (float)(bi->x[2] - c->loc[2])}; + + /* Loop over the parts in cj. */ + for (int bjd = 0; bjd < bcount; bjd++) { + + /* Skip self interaction */ + if (bid == bjd) continue; + + /* Get a pointer to the jth particle. */ + struct bpart *restrict bj = &bparts[bjd]; + const float hj = bj->h; + + /* Early abort? */ + if (bpart_is_inhibited(bj, e)) continue; + + /* Compute the pairwise distance. */ + const float bjx[3] = {(float)(bj->x[0] - c->loc[0]), + (float)(bj->x[1] - c->loc[1]), + (float)(bj->x[2] - c->loc[2])}; + float dx[3] = {bix[0] - bjx[0], bix[1] - bjx[1], bix[2] - bjx[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 (bi->ti_drift != e->ti_current) + error("Particle bi not drifted to current time"); + if (bj->ti_drift != e->ti_current) + error("Particle bj not drifted to current time"); +#endif + + if (r2 < hig2) { + IACT_BH_BH(r2, dx, hi, hj, bi, bj, cosmo, e->gravity_properties, + ti_current); + } + } /* loop over the bparts in ci. */ + } /* loop over the bparts in ci. */ + +#endif /* (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) */ + + 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"); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + 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 (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]; + } + + /* Do we actually have any gas neighbours? */ + if (cj->hydro.count != 0) { + + /* Loop over the bparts in ci. */ + for (int bid = 0; bid < bcount_i; bid++) { + + /* Get a hold of the ith bpart in ci. */ + struct bpart *restrict bi = &bparts_i[bid]; + + /* Skip inactive particles */ + if (!bpart_is_active(bi, e)) continue; + + const float hi = bi->h; + const float hig2 = hi * hi * kernel_gamma2; + const float bix[3] = {(float)(bi->x[0] - (cj->loc[0] + shift[0])), + (float)(bi->x[1] - (cj->loc[1] + shift[1])), + (float)(bi->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] = {bix[0] - pjx[0], bix[1] - pjx[1], bix[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 (bi->ti_drift != e->ti_current) + error("Particle bi not drifted to current time"); + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + if (r2 < hig2) { + IACT_BH_GAS(r2, dx, hi, hj, bi, pj, xpj, cosmo, e->gravity_properties, + ti_current); + } + } /* loop over the parts in cj. */ + } /* loop over the bparts in ci. */ + } /* Do we have gas particles in the cell? */ + + /* When doing BH swallowing, we need a quick loop also over the BH + * neighbours */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + + const int bcount_j = cj->black_holes.count; + struct bpart *restrict bparts_j = cj->black_holes.parts; + + /* Loop over the bparts in ci. */ + for (int bid = 0; bid < bcount_i; bid++) { + + /* Get a hold of the ith bpart in ci. */ + struct bpart *restrict bi = &bparts_i[bid]; + + /* Skip inactive particles */ + if (!bpart_is_active(bi, e)) continue; + + const float hi = bi->h; + const float hig2 = hi * hi * kernel_gamma2; + const float bix[3] = {(float)(bi->x[0] - (cj->loc[0] + shift[0])), + (float)(bi->x[1] - (cj->loc[1] + shift[1])), + (float)(bi->x[2] - (cj->loc[2] + shift[2]))}; + + /* Loop over the bparts in cj. */ + for (int bjd = 0; bjd < bcount_j; bjd++) { + + /* Get a pointer to the jth particle. */ + struct bpart *restrict bj = &bparts_j[bjd]; + const float hj = bj->h; + + /* Skip inhibited particles. */ + if (bpart_is_inhibited(bj, e)) continue; + + /* Compute the pairwise distance. */ + const float bjx[3] = {(float)(bj->x[0] - cj->loc[0]), + (float)(bj->x[1] - cj->loc[1]), + (float)(bj->x[2] - cj->loc[2])}; + float dx[3] = {bix[0] - bjx[0], bix[1] - bjx[1], bix[2] - bjx[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 (bi->ti_drift != e->ti_current) + error("Particle bi not drifted to current time"); + if (bj->ti_drift != e->ti_current) + error("Particle bj not drifted to current time"); +#endif + + if (r2 < hig2) { + IACT_BH_BH(r2, dx, hi, hj, bi, bj, cosmo, e->gravity_properties, + ti_current); + } + } /* loop over the bparts in cj. */ + } /* loop over the bparts in ci. */ + +#endif /* (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) */ +} + +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; +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + /* 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; +#else + /* The swallow task is executed on both sides */ + const int do_ci_bh = 1; + const int do_cj_bh = 1; +#endif + + if (do_ci_bh) DO_NONSYM_PAIR1_BH_NAIVE(r, ci, cj); + if (do_cj_bh) 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 bid = 0; bid < bcount; bid++) { + + /* Get a hold of the ith part in ci. */ + struct bpart *restrict bi = &bparts_i[ind[bid]]; + + const double bix = bi->x[0] - (shift[0]); + const double biy = bi->x[1] - (shift[1]); + const double biz = bi->x[2] - (shift[2]); + const float hi = bi->h; + const float hig2 = hi * hi * kernel_gamma2; + +#ifdef SWIFT_DEBUG_CHECKS + if (!bpart_is_active(bi, 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)(bix - pjx), (float)(biy - pjy), + (float)(biz - 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_GAS(r2, dx, hi, hj, bi, pj, xpj, cosmo, e->gravity_properties, + 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 bid = 0; bid < bcount; bid++) { + + /* Get a hold of the ith part in ci. */ + struct bpart *bi = &bparts[ind[bid]]; + const float bix[3] = {(float)(bi->x[0] - ci->loc[0]), + (float)(bi->x[1] - ci->loc[1]), + (float)(bi->x[2] - ci->loc[2])}; + const float hi = bi->h; + const float hig2 = hi * hi * kernel_gamma2; + +#ifdef SWIFT_DEBUG_CHECKS + if (!bpart_is_active(bi, 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] = {bix[0] - pjx[0], bix[1] - pjx[1], bix[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_GAS(r2, dx, hi, pj->h, bi, pj, xpj, cosmo, + e->gravity_properties, 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; +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + /* 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; +#else + /* The swallow task is executed on both sides */ + const int do_ci_bh = 1; + const int do_cj_bh = 1; +#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? + * In the swallow case we care about BH-BH and BH-gas + * interactions. + * In all other cases only BH-gas so we can abort if there is + * is no gas in the cell */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + const int should_do_ci = + ci->black_holes.count != 0 && cell_is_active_black_holes(ci, e); + const int should_do_cj = + cj->black_holes.count != 0 && cell_is_active_black_holes(cj, e); +#else + 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); + +#endif + + 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; +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + /* 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; +#else + /* Here we perform the task on both sides */ + const int do_ci_bh = 1; + const int do_cj_bh = 1; +#endif + + const int do_ci = ci->black_holes.count != 0 && + cell_is_active_black_holes(ci, e) && do_ci_bh; + const int do_cj = cj->black_holes.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 (cj->hydro.count != 0 && !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 (ci->hydro.count != 0 && !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; + + const struct engine *e = r->e; + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != engine_rank) + error("This function should not be called on foreign cells"); +#endif + + /* Should we even bother? + * In the swallow case we care about BH-BH and BH-gas + * interactions. + * In all other cases only BH-gas so we can abort if there is + * is no gas in the cell */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + const int should_do_ci = + ci->black_holes.count != 0 && cell_is_active_black_holes(ci, e); +#else + const int should_do_ci = ci->black_holes.count != 0 && ci->hydro.count != 0 && + cell_is_active_black_holes(ci, e); +#endif + + if (!should_do_ci) 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 { + + /* Check we did drift to the current time */ + if (!cell_are_bpart_drifted(ci, e)) error("Interacting undrifted cell."); + + if (ci->hydro.count != 0 && !cell_are_part_drifted(ci, e)) + error("Interacting undrifted cells (bparts)."); + + DOSELF1_BRANCH_BH(r, ci); + } + + TIMER_TOC(TIMER_DOSUB_SELF_BH); +} diff --git a/src/runner_doiact.h b/src/runner_doiact_functions_hydro.h similarity index 93% rename from src/runner_doiact.h rename to src/runner_doiact_functions_hydro.h index 6caa287cf726f85778ef5abdc184acaf759e8b0e..c324c759b5acc9db75cf0849d0e417b2141978f4 100644 --- a/src/runner_doiact.h +++ b/src/runner_doiact_functions_hydro.h @@ -24,106 +24,7 @@ and runner_dosub_FUNCTION calling the pairwise interaction function runner_iact_FUNCTION. */ -#define PASTE(x, y) x##_##y - -#define _DOPAIR1_BRANCH(f) PASTE(runner_dopair1_branch, f) -#define DOPAIR1_BRANCH _DOPAIR1_BRANCH(FUNCTION) - -#define _DOPAIR1(f) PASTE(runner_dopair1, f) -#define DOPAIR1 _DOPAIR1(FUNCTION) - -#define _DOPAIR2_BRANCH(f) PASTE(runner_dopair2_branch, f) -#define DOPAIR2_BRANCH _DOPAIR2_BRANCH(FUNCTION) - -#define _DOPAIR2(f) PASTE(runner_dopair2, f) -#define DOPAIR2 _DOPAIR2(FUNCTION) - -#define _DOPAIR_SUBSET(f) PASTE(runner_dopair_subset, f) -#define DOPAIR_SUBSET _DOPAIR_SUBSET(FUNCTION) - -#define _DOPAIR_SUBSET_BRANCH(f) PASTE(runner_dopair_subset_branch, f) -#define DOPAIR_SUBSET_BRANCH _DOPAIR_SUBSET_BRANCH(FUNCTION) - -#define _DOPAIR_SUBSET_NOSORT(f) PASTE(runner_dopair_subset_nosort, f) -#define DOPAIR_SUBSET_NOSORT _DOPAIR_SUBSET_NOSORT(FUNCTION) - -#define _DOPAIR_SUBSET_NAIVE(f) PASTE(runner_dopair_subset_naive, f) -#define DOPAIR_SUBSET_NAIVE _DOPAIR_SUBSET_NAIVE(FUNCTION) - -#define _DOPAIR1_NAIVE(f) PASTE(runner_dopair1_naive, f) -#define DOPAIR1_NAIVE _DOPAIR1_NAIVE(FUNCTION) - -#define _DOPAIR2_NAIVE(f) PASTE(runner_dopair2_naive, f) -#define DOPAIR2_NAIVE _DOPAIR2_NAIVE(FUNCTION) - -#define _DOSELF1_NAIVE(f) PASTE(runner_doself1_naive, f) -#define DOSELF1_NAIVE _DOSELF1_NAIVE(FUNCTION) - -#define _DOSELF2_NAIVE(f) PASTE(runner_doself2_naive, f) -#define DOSELF2_NAIVE _DOSELF2_NAIVE(FUNCTION) - -#define _DOSELF1_BRANCH(f) PASTE(runner_doself1_branch, f) -#define DOSELF1_BRANCH _DOSELF1_BRANCH(FUNCTION) - -#define _DOSELF1(f) PASTE(runner_doself1, f) -#define DOSELF1 _DOSELF1(FUNCTION) - -#define _DOSELF2_BRANCH(f) PASTE(runner_doself2_branch, f) -#define DOSELF2_BRANCH _DOSELF2_BRANCH(FUNCTION) - -#define _DOSELF2(f) PASTE(runner_doself2, f) -#define DOSELF2 _DOSELF2(FUNCTION) - -#define _DOSELF_SUBSET(f) PASTE(runner_doself_subset, f) -#define DOSELF_SUBSET _DOSELF_SUBSET(FUNCTION) - -#define _DOSELF_SUBSET_BRANCH(f) PASTE(runner_doself_subset_branch, f) -#define DOSELF_SUBSET_BRANCH _DOSELF_SUBSET_BRANCH(FUNCTION) - -#define _DOSUB_SELF1(f) PASTE(runner_dosub_self1, f) -#define DOSUB_SELF1 _DOSUB_SELF1(FUNCTION) - -#define _DOSUB_PAIR1(f) PASTE(runner_dosub_pair1, f) -#define DOSUB_PAIR1 _DOSUB_PAIR1(FUNCTION) - -#define _DOSUB_SELF2(f) PASTE(runner_dosub_self2, f) -#define DOSUB_SELF2 _DOSUB_SELF2(FUNCTION) - -#define _DOSUB_PAIR2(f) PASTE(runner_dosub_pair2, f) -#define DOSUB_PAIR2 _DOSUB_PAIR2(FUNCTION) - -#define _DOSUB_SUBSET(f) PASTE(runner_dosub_subset, f) -#define DOSUB_SUBSET _DOSUB_SUBSET(FUNCTION) - -#define _IACT_NONSYM(f) PASTE(runner_iact_nonsym, f) -#define IACT_NONSYM _IACT_NONSYM(FUNCTION) - -#define _IACT(f) PASTE(runner_iact, f) -#define IACT _IACT(FUNCTION) - -#define _IACT_NONSYM_VEC(f) PASTE(runner_iact_nonsym_vec, f) -#define IACT_NONSYM_VEC _IACT_NONSYM_VEC(FUNCTION) - -#define _IACT_VEC(f) PASTE(runner_iact_vec, f) -#define IACT_VEC _IACT_VEC(FUNCTION) - -#define _TIMER_DOSELF(f) PASTE(timer_doself, f) -#define TIMER_DOSELF _TIMER_DOSELF(FUNCTION) - -#define _TIMER_DOPAIR(f) PASTE(timer_dopair, f) -#define TIMER_DOPAIR _TIMER_DOPAIR(FUNCTION) - -#define _TIMER_DOSUB_SELF(f) PASTE(timer_dosub_self, f) -#define TIMER_DOSUB_SELF _TIMER_DOSUB_SELF(FUNCTION) - -#define _TIMER_DOSUB_PAIR(f) PASTE(timer_dosub_pair, f) -#define TIMER_DOSUB_PAIR _TIMER_DOSUB_PAIR(FUNCTION) - -#define _TIMER_DOSELF_SUBSET(f) PASTE(timer_doself_subset, f) -#define TIMER_DOSELF_SUBSET _TIMER_DOSELF_SUBSET(FUNCTION) - -#define _TIMER_DOPAIR_SUBSET(f) PASTE(timer_dopair_subset, f) -#define TIMER_DOPAIR_SUBSET _TIMER_DOPAIR_SUBSET(FUNCTION) +#include "runner_doiact_hydro.h" /** * @brief Compute the interactions between a cell pair (non-symmetric case). @@ -213,7 +114,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } if (r2 < hjg2 && pj_active) { @@ -225,7 +126,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } @@ -325,14 +226,14 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, IACT(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (pi_active) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (pj_active) { @@ -343,7 +244,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } @@ -431,14 +332,14 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { IACT(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (doi) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (doj) { @@ -449,7 +350,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } /* loop over the parts in cj. */ @@ -536,14 +437,14 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { IACT(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (doi) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (doj) { @@ -554,7 +455,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } /* loop over the parts in cj. */ @@ -640,7 +541,7 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, IACT_NONSYM(r2, dx, hi, pj->h, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, pj->h, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, pj->h, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, pj->h, pi, pj, a, H); #endif } } /* loop over the parts in cj. */ @@ -733,7 +634,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } } /* loop over the parts in cj. */ @@ -789,7 +690,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } } /* loop over the parts in cj. */ @@ -884,7 +785,6 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, const int count_i = ci->hydro.count; struct part *restrict parts_j = ci->hydro.parts; - /* Loop over the parts in ci. */ for (int pid = 0; pid < count; pid++) { @@ -932,7 +832,7 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } } /* loop over the parts in cj. */ @@ -1096,7 +996,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } } /* loop over the parts in cj. */ @@ -1184,7 +1084,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } /* loop over the parts in ci. */ @@ -1484,7 +1384,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } /* loop over the active parts in cj. */ @@ -1555,13 +1455,13 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, IACT(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } } @@ -1664,7 +1564,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } } /* loop over the active parts in ci. */ @@ -1737,13 +1637,13 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, IACT(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } else { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } @@ -1932,7 +1832,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } /* loop over all other particles. */ @@ -1984,14 +1884,14 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { IACT(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (doi) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else if (doj) { @@ -2001,7 +1901,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } @@ -2128,7 +2028,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); #endif } } /* loop over all other particles. */ @@ -2175,13 +2075,13 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { IACT(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } else { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); #endif } } diff --git a/src/runner_doiact_functions_stars.h b/src/runner_doiact_functions_stars.h new file mode 100644 index 0000000000000000000000000000000000000000..b0d731857e9b4b0474e47c3ac3fca540eecb1cbb --- /dev/null +++ b/src/runner_doiact_functions_stars.h @@ -0,0 +1,1332 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ + +/* Before including this file, define FUNCTION, which is the + name of the interaction function. This creates the interaction functions + runner_dopair_FUNCTION, runner_dopair_FUNCTION_naive, runner_doself_FUNCTION, + and runner_dosub_FUNCTION calling the pairwise interaction function + runner_iact_FUNCTION. */ + +#include "runner_doiact_stars.h" + +/** + * @brief Calculate the number density of #part around the #spart + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void DOSELF1_STARS(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 int with_cosmology = e->policy & engine_policy_cosmology; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + /* Anything to do here? */ + if (c->hydro.count == 0 || c->stars.count == 0) return; + if (!cell_is_active_stars(c, e)) return; + + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + + const int scount = c->stars.count; + const int count = c->hydro.count; + struct spart *restrict sparts = c->stars.parts; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + + /* Loop over the sparts in ci. */ + for (int sid = 0; sid < scount; sid++) { + + /* Get a hold of the ith spart in ci. */ + struct spart *restrict si = &sparts[sid]; + + /* Skip inactive particles */ + if (!spart_is_active(si, e)) continue; + + /* Skip inactive particles */ + if (!feedback_is_active(si, e->time, cosmo, with_cosmology)) 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_STARS(r2, dx, hi, hj, si, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in ci. */ + } /* loop over the sparts in ci. */ + + TIMER_TOC(TIMER_DOSELF_STARS); +} + +/** + * @brief Calculate the number density of cj #part around the ci #spart + * + * @param r runner task + * @param ci The first #cell + * @param cj The second #cell + */ +void DO_NONSYM_PAIR1_STARS_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 int with_cosmology = e->policy & engine_policy_cosmology; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + /* Anything to do here? */ + if (cj->hydro.count == 0 || ci->stars.count == 0) return; + if (!cell_is_active_stars(ci, e)) return; + + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + + const int scount_i = ci->stars.count; + const int count_j = cj->hydro.count; + struct spart *restrict sparts_i = ci->stars.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 sparts in ci. */ + for (int sid = 0; sid < scount_i; sid++) { + + /* Get a hold of the ith spart in ci. */ + struct spart *restrict si = &sparts_i[sid]; + + /* Skip inactive particles */ + if (!spart_is_active(si, e)) continue; + + /* Skip inactive particles */ + if (!feedback_is_active(si, e->time, cosmo, with_cosmology)) 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_STARS(r2, dx, hi, hj, si, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the parts in ci. */ +} + +/** + * @brief Compute the interactions between a cell pair. + * + * @param r The #runner. + * @param ci The first #cell. + * @param cj The second #cell. + * @param sid The direction of the pair. + * @param shift The shift vector to apply to the particles in ci. + */ +void DO_SYM_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, + const int sid, const double *shift) { + + TIMER_TIC; + + const struct engine *e = r->e; + const int with_cosmology = e->policy & engine_policy_cosmology; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + + /* Get the cutoff shift. */ + double rshift = 0.0; + for (int k = 0; k < 3; k++) rshift += shift[k] * runner_shift[sid][k]; + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_stars = (ci->nodeID == e->nodeID) && (ci->stars.count != 0) && + (cj->hydro.count != 0) && cell_is_active_stars(ci, e); + const int do_cj_stars = (cj->nodeID == e->nodeID) && (cj->stars.count != 0) && + (ci->hydro.count != 0) && cell_is_active_stars(cj, e); +#else + /* here we are updating the hydro -> switch ci, cj for local */ + const int do_ci_stars = (cj->nodeID == e->nodeID) && (ci->stars.count != 0) && + (cj->hydro.count != 0) && cell_is_active_stars(ci, e); + const int do_cj_stars = (ci->nodeID == e->nodeID) && (cj->stars.count != 0) && + (ci->hydro.count != 0) && cell_is_active_stars(cj, e); +#endif + + if (do_ci_stars) { + + /* Pick-out the sorted lists. */ + const struct sort_entry *restrict sort_j = cj->hydro.sort[sid]; + const struct sort_entry *restrict sort_i = ci->stars.sort[sid]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Some constants used to checks that the parts are in the right frame */ + const float shift_threshold_x = + 2. * ci->width[0] + + 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); + const float shift_threshold_y = + 2. * ci->width[1] + + 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); + const float shift_threshold_z = + 2. * ci->width[2] + + 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); +#endif /* SWIFT_DEBUG_CHECKS */ + + /* Get some other useful values. */ + const double hi_max = ci->stars.h_max * kernel_gamma - rshift; + const int count_i = ci->stars.count; + const int count_j = cj->hydro.count; + struct spart *restrict sparts_i = ci->stars.parts; + struct part *restrict parts_j = cj->hydro.parts; + struct xpart *restrict xparts_j = cj->hydro.xparts; + const double dj_min = sort_j[0].d; + const float dx_max_rshift = + (ci->stars.dx_max_sort + cj->hydro.dx_max_sort) - rshift; + const float dx_max = (ci->stars.dx_max_sort + cj->hydro.dx_max_sort); + + /* Loop over the sparts in ci. */ + for (int pid = count_i - 1; + pid >= 0 && sort_i[pid].d + hi_max + dx_max > dj_min; pid--) { + + /* Get a hold of the ith part in ci. */ + struct spart *restrict spi = &sparts_i[sort_i[pid].i]; + const float hi = spi->h; + + /* Skip inactive particles */ + if (!spart_is_active(spi, e)) continue; + + /* Skip inactive particles */ + if (!feedback_is_active(spi, e->time, cosmo, with_cosmology)) continue; + + /* Compute distance from the other cell. */ + const double px[3] = {spi->x[0], spi->x[1], spi->x[2]}; + float dist = px[0] * runner_shift[sid][0] + px[1] * runner_shift[sid][1] + + px[2] * runner_shift[sid][2]; + + /* Is there anything we need to interact with ? */ + const double di = dist + hi * kernel_gamma + dx_max_rshift; + if (di < dj_min) continue; + + /* Get some additional information about pi */ + const float hig2 = hi * hi * kernel_gamma2; + const float pix = spi->x[0] - (cj->loc[0] + shift[0]); + const float piy = spi->x[1] - (cj->loc[1] + shift[1]); + const float piz = spi->x[2] - (cj->loc[2] + shift[2]); + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j && sort_j[pjd].d < di; pjd++) { + + /* Recover pj */ + struct part *pj = &parts_j[sort_j[pjd].i]; + struct xpart *xpj = &xparts_j[sort_j[pjd].i]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + + const float hj = pj->h; + const float pjx = pj->x[0] - cj->loc[0]; + const float pjy = pj->x[1] - cj->loc[1]; + const float pjz = pj->x[2] - cj->loc[2]; + + /* Compute the pairwise distance. */ + float dx[3] = {pix - pjx, piy - pjy, piz - pjz}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles are in the correct frame after the shifts */ + if (pix > shift_threshold_x || pix < -shift_threshold_x) + error( + "Invalid particle position in X for pi (pix=%e ci->width[0]=%e)", + pix, ci->width[0]); + if (piy > shift_threshold_y || piy < -shift_threshold_y) + error( + "Invalid particle position in Y for pi (piy=%e ci->width[1]=%e)", + piy, ci->width[1]); + if (piz > shift_threshold_z || piz < -shift_threshold_z) + error( + "Invalid particle position in Z for pi (piz=%e ci->width[2]=%e)", + piz, ci->width[2]); + if (pjx > shift_threshold_x || pjx < -shift_threshold_x) + error( + "Invalid particle position in X for pj (pjx=%e ci->width[0]=%e)", + pjx, ci->width[0]); + if (pjy > shift_threshold_y || pjy < -shift_threshold_y) + error( + "Invalid particle position in Y for pj (pjy=%e ci->width[1]=%e)", + pjy, ci->width[1]); + if (pjz > shift_threshold_z || pjz < -shift_threshold_z) + error( + "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", + pjz, ci->width[2]); + + /* Check that particles have been drifted to the current time */ + if (spi->ti_drift != e->ti_current) + error("Particle spi not drifted to 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_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the parts in ci. */ + } /* do_ci_stars */ + + if (do_cj_stars) { + /* Pick-out the sorted lists. */ + const struct sort_entry *restrict sort_i = ci->hydro.sort[sid]; + const struct sort_entry *restrict sort_j = cj->stars.sort[sid]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Some constants used to checks that the parts are in the right frame */ + const float shift_threshold_x = + 2. * ci->width[0] + + 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); + const float shift_threshold_y = + 2. * ci->width[1] + + 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); + const float shift_threshold_z = + 2. * ci->width[2] + + 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); +#endif /* SWIFT_DEBUG_CHECKS */ + + /* Get some other useful values. */ + const double hj_max = cj->hydro.h_max * kernel_gamma; + const int count_i = ci->hydro.count; + const int count_j = cj->stars.count; + struct part *restrict parts_i = ci->hydro.parts; + struct xpart *restrict xparts_i = ci->hydro.xparts; + struct spart *restrict sparts_j = cj->stars.parts; + const double di_max = sort_i[count_i - 1].d - rshift; + const float dx_max_rshift = + (ci->hydro.dx_max_sort + cj->stars.dx_max_sort) + rshift; + const float dx_max = (ci->hydro.dx_max_sort + cj->stars.dx_max_sort); + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j && sort_j[pjd].d - hj_max - dx_max < di_max; + pjd++) { + + /* Get a hold of the jth part in cj. */ + struct spart *spj = &sparts_j[sort_j[pjd].i]; + const float hj = spj->h; + + /* Skip inactive particles */ + if (!spart_is_active(spj, e)) continue; + + /* Skip inactive particles */ + if (!feedback_is_active(spj, e->time, cosmo, with_cosmology)) continue; + + /* Compute distance from the other cell. */ + const double px[3] = {spj->x[0], spj->x[1], spj->x[2]}; + float dist = px[0] * runner_shift[sid][0] + px[1] * runner_shift[sid][1] + + px[2] * runner_shift[sid][2]; + + /* Is there anything we need to interact with ? */ + const double dj = dist - hj * kernel_gamma - dx_max_rshift; + if (dj - rshift > di_max) continue; + + /* Get some additional information about pj */ + const float hjg2 = hj * hj * kernel_gamma2; + const float pjx = spj->x[0] - cj->loc[0]; + const float pjy = spj->x[1] - cj->loc[1]; + const float pjz = spj->x[2] - cj->loc[2]; + + /* Loop over the parts in ci. */ + for (int pid = count_i - 1; pid >= 0 && sort_i[pid].d > dj; pid--) { + + /* Recover pi */ + struct part *pi = &parts_i[sort_i[pid].i]; + struct xpart *xpi = &xparts_i[sort_i[pid].i]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + + const float hi = pi->h; + const float pix = pi->x[0] - (cj->loc[0] + shift[0]); + const float piy = pi->x[1] - (cj->loc[1] + shift[1]); + const float piz = pi->x[2] - (cj->loc[2] + shift[2]); + + /* Compute the pairwise distance. */ + float dx[3] = {pjx - pix, pjy - piy, pjz - piz}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles are in the correct frame after the shifts */ + if (pix > shift_threshold_x || pix < -shift_threshold_x) + error( + "Invalid particle position in X for pi (pix=%e ci->width[0]=%e)", + pix, ci->width[0]); + if (piy > shift_threshold_y || piy < -shift_threshold_y) + error( + "Invalid particle position in Y for pi (piy=%e ci->width[1]=%e)", + piy, ci->width[1]); + if (piz > shift_threshold_z || piz < -shift_threshold_z) + error( + "Invalid particle position in Z for pi (piz=%e ci->width[2]=%e)", + piz, ci->width[2]); + if (pjx > shift_threshold_x || pjx < -shift_threshold_x) + error( + "Invalid particle position in X for pj (pjx=%e ci->width[0]=%e)", + pjx, ci->width[0]); + if (pjy > shift_threshold_y || pjy < -shift_threshold_y) + error( + "Invalid particle position in Y for pj (pjy=%e ci->width[1]=%e)", + pjy, ci->width[1]); + if (pjz > shift_threshold_z || pjz < -shift_threshold_z) + error( + "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", + pjz, ci->width[2]); + + /* Check that particles have been drifted to the current time */ + if (pi->ti_drift != e->ti_current) + error("Particle pi not drifted to current time"); + if (spj->ti_drift != e->ti_current) + error("Particle spj not drifted to current time"); +#endif + + /* Hit or miss? */ + if (r2 < hjg2) { + + IACT_STARS(r2, dx, hj, hi, spj, pi, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hj, hi, spj, pi, xpi, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hj, hi, spj, pi, xpi, cosmo, + ti_current); +#endif + } + } /* loop over the parts in ci. */ + } /* loop over the parts in cj. */ + } /* Cell cj is active */ + + TIMER_TOC(TIMER_DOPAIR_STARS); +} + +void DOPAIR1_STARS_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_stars = ci->nodeID == r->e->nodeID; + const int do_cj_stars = cj->nodeID == r->e->nodeID; +#else + /* here we are updating the hydro -> switch ci, cj */ + const int do_ci_stars = cj->nodeID == r->e->nodeID; + const int do_cj_stars = ci->nodeID == r->e->nodeID; +#endif + if (do_ci_stars && ci->stars.count != 0 && cj->hydro.count != 0) + DO_NONSYM_PAIR1_STARS_NAIVE(r, ci, cj); + if (do_cj_stars && cj->stars.count != 0 && ci->hydro.count != 0) + DO_NONSYM_PAIR1_STARS_NAIVE(r, cj, ci); + + TIMER_TOC(TIMER_DOPAIR_STARS); +} + +/** + * @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 sparts_i The #part to interact with @c cj. + * @param ind The list of indices of particles in @c ci to interact with. + * @param scount The number of particles in @c ind. + * @param cj The second #cell. + * @param sid The direction of the pair. + * @param flipped Flag to check whether the cells have been flipped or not. + * @param shift The shift vector to apply to the particles in ci. + */ +void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts_i, int *restrict ind, + int scount, struct cell *restrict cj, const int sid, + const int flipped, const double *shift) { + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + + 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; + + /* Pick-out the sorted lists. */ + const struct sort_entry *restrict sort_j = cj->hydro.sort[sid]; + const float dxj = cj->hydro.dx_max_sort; + + /* Sparts are on the left? */ + if (!flipped) { + + /* Loop over the sparts_i. */ + for (int pid = 0; pid < scount; pid++) { + + /* Get a hold of the ith spart in ci. */ + struct spart *restrict spi = &sparts_i[ind[pid]]; + const double pix = spi->x[0] - (shift[0]); + const double piy = spi->x[1] - (shift[1]); + const double piz = spi->x[2] - (shift[2]); + const float hi = spi->h; + const float hig2 = hi * hi * kernel_gamma2; + const double di = hi * kernel_gamma + dxj + pix * runner_shift[sid][0] + + piy * runner_shift[sid][1] + piz * runner_shift[sid][2]; + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j && sort_j[pjd].d < di; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_j[sort_j[pjd].i]; + struct xpart *restrict xpj = &xparts_j[sort_j[pjd].i]; + + /* 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 (spi->ti_drift != e->ti_current) + error("Particle pi not drifted to 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_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the sparts in ci. */ + } + + /* Sparts are on the right. */ + else { + + /* Loop over the sparts_i. */ + for (int pid = 0; pid < scount; pid++) { + + /* Get a hold of the ith spart in ci. */ + struct spart *restrict spi = &sparts_i[ind[pid]]; + const double pix = spi->x[0] - (shift[0]); + const double piy = spi->x[1] - (shift[1]); + const double piz = spi->x[2] - (shift[2]); + const float hi = spi->h; + const float hig2 = hi * hi * kernel_gamma2; + const double di = -hi * kernel_gamma - dxj + pix * runner_shift[sid][0] + + piy * runner_shift[sid][1] + piz * runner_shift[sid][2]; + + /* Loop over the parts in cj. */ + for (int pjd = count_j - 1; pjd >= 0 && di < sort_j[pjd].d; pjd--) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_j[sort_j[pjd].i]; + struct xpart *restrict xpj = &xparts_j[sort_j[pjd].i]; + + /* 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 (spi->ti_drift != e->ti_current) + error("Particle pi not drifted to 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_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the sparts in ci. */ + } +} + +/** + * @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 sparts_i The #part to interact with @c cj. + * @param ind The list of indices of particles in @c ci to interact with. + * @param scount 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_STARS_NAIVE(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts_i, + int *restrict ind, int scount, + 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; + + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + + 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 < scount; pid++) { + + /* Get a hold of the ith part in ci. */ + struct spart *restrict spi = &sparts_i[ind[pid]]; + + const double pix = spi->x[0] - (shift[0]); + const double piy = spi->x[1] - (shift[1]); + const double piz = spi->x[2] - (shift[2]); + const float hi = spi->h; + const float hig2 = hi * hi * kernel_gamma2; + +#ifdef SWIFT_DEBUG_CHECKS + if (!spart_is_active(spi, 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_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif + } + } /* 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 sparts The #spart to interact. + * @param ind The list of indices of particles in @c ci to interact with. + * @param scount The number of particles in @c ind. + */ +void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts, int *restrict ind, + int scount) { + +#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; + + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + + 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 spid = 0; spid < scount; spid++) { + + /* Get a hold of the ith part in ci. */ + struct spart *spi = &sparts[ind[spid]]; + const float spix[3] = {(float)(spi->x[0] - ci->loc[0]), + (float)(spi->x[1] - ci->loc[1]), + (float)(spi->x[2] - ci->loc[2])}; + const float hi = spi->h; + const float hig2 = hi * hi * kernel_gamma2; + +#ifdef SWIFT_DEBUG_CHECKS + if (!spart_is_active(spi, 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] = {spix[0] - pjx[0], spix[1] - pjx[1], spix[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_STARS(r2, dx, hi, pj->h, spi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, pj->h, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, pj->h, spi, pj, xpj, + cosmo, ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the parts in ci. */ +} + +/** + * @brief Determine which version of DOSELF1_SUBSET_STARS needs to be called + * depending on the optimisation level. + * + * @param r The #runner. + * @param ci The first #cell. + * @param sparts The #spart to interact. + * @param ind The list of indices of particles in @c ci to interact with. + * @param scount The number of particles in @c ind. + */ +void DOSELF1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts, + int *restrict ind, int scount) { + + DOSELF1_SUBSET_STARS(r, ci, sparts, ind, scount); +} + +/** + * @brief Determine which version of DOPAIR1_SUBSET_STARS needs to be called + * depending on the orientation of the cells or whether DOPAIR1_SUBSET_STARS + * needs to be called at all. + * + * @param r The #runner. + * @param ci The first #cell. + * @param sparts_i The #spart to interact with @c cj. + * @param ind The list of indices of particles in @c ci to interact with. + * @param scount The number of particles in @c ind. + * @param cj The second #cell. + */ +void DOPAIR1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts_i, + int *restrict ind, int scount, + 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]; + } + +#ifdef SWIFT_USE_NAIVE_INTERACTIONS_STARS + DOPAIR1_SUBSET_STARS_NAIVE(r, ci, sparts_i, ind, scount, cj, shift); +#else + /* Get the sorting index. */ + int sid = 0; + for (int k = 0; k < 3; k++) + sid = 3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0) + ? 0 + : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2 : 1); + + /* Switch the cells around? */ + const int flipped = runner_flip[sid]; + sid = sortlistID[sid]; + + /* Has the cell cj been sorted? */ + if (!(cj->hydro.sorted & (1 << sid)) || + cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin) + error("Interacting unsorted cells."); + + DOPAIR1_SUBSET_STARS(r, ci, sparts_i, ind, scount, cj, sid, flipped, shift); +#endif +} + +void DOSUB_SUBSET_STARS(struct runner *r, struct cell *ci, struct spart *sparts, + int *ind, int scount, struct cell *cj, int gettimer) { + + const struct engine *e = r->e; + struct space *s = e->s; + + /* Should we even bother? */ + if (!cell_is_active_stars(ci, e) && + (cj == NULL || !cell_is_active_stars(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 (&sparts[ind[0]] >= &ci->progeny[k]->stars.parts[0] && + &sparts[ind[0]] < + &ci->progeny[k]->stars.parts[ci->progeny[k]->stars.count]) { + sub = ci->progeny[k]; + break; + } + } + } + } + + /* Is this a single cell? */ + if (cj == NULL) { + + /* Recurse? */ + if (cell_can_recurse_in_self_stars_task(ci)) { + + /* Loop over all progeny. */ + DOSUB_SUBSET_STARS(r, sub, sparts, ind, scount, NULL, 0); + for (int j = 0; j < 8; j++) + if (ci->progeny[j] != sub && ci->progeny[j] != NULL) + DOSUB_SUBSET_STARS(r, sub, sparts, ind, scount, ci->progeny[j], 0); + + } + + /* Otherwise, compute self-interaction. */ + else + DOSELF1_SUBSET_BRANCH_STARS(r, ci, sparts, ind, scount); + } /* self-interaction. */ + + /* Otherwise, it's a pair interaction. */ + else { + + /* Recurse? */ + if (cell_can_recurse_in_pair_stars_task(ci, cj) && + cell_can_recurse_in_pair_stars_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_STARS(r, ci->progeny[pid], sparts, ind, scount, + cj->progeny[pjd], 0); + if (ci->progeny[pid] != NULL && cj->progeny[pjd] == sub) + DOSUB_SUBSET_STARS(r, cj->progeny[pjd], sparts, ind, scount, + ci->progeny[pid], 0); + } + } + + /* Otherwise, compute the pair directly. */ + else if (cell_is_active_stars(ci, e) && cj->hydro.count > 0) { + + /* Do any of the cells need to be drifted first? */ + if (cell_is_active_stars(ci, e)) { + if (!cell_are_spart_drifted(ci, e)) error("Cell should be drifted!"); + if (!cell_are_part_drifted(cj, e)) error("Cell should be drifted!"); + } + + DOPAIR1_SUBSET_BRANCH_STARS(r, ci, sparts, ind, scount, cj); + } + + } /* otherwise, pair interaction. */ +} + +/** + * @brief Determine which version of DOSELF1_STARS needs to be called depending + * on the optimisation level. + * + * @param r #runner + * @param c #cell c + * + */ +void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c) { + + const struct engine *restrict e = r->e; + + /* Anything to do here? */ + if (c->stars.count == 0) return; + + /* Anything to do here? */ + if (!cell_is_active_stars(c, e)) return; + + /* Did we mess up the recursion? */ + if (c->stars.h_max_old * kernel_gamma > c->dmin) + error("Cell smaller than smoothing length"); + + DOSELF1_STARS(r, c, 1); +} + +#define RUNNER_CHECK_SORT(TYPE, PART, cj, ci, sid) \ + ({ \ + const struct sort_entry *restrict sort_j = cj->TYPE.sort[sid]; \ + \ + for (int pjd = 0; pjd < cj->TYPE.count; pjd++) { \ + const struct PART *p = &cj->TYPE.parts[sort_j[pjd].i]; \ + if (PART##_is_inhibited(p, e)) continue; \ + \ + const float d = p->x[0] * runner_shift[sid][0] + \ + p->x[1] * runner_shift[sid][1] + \ + p->x[2] * runner_shift[sid][2]; \ + if ((fabsf(d - sort_j[pjd].d) - cj->TYPE.dx_max_sort) > \ + 1.0e-4 * max(fabsf(d), cj->TYPE.dx_max_sort_old) && \ + (fabsf(d - sort_j[pjd].d) - cj->TYPE.dx_max_sort) > \ + cj->width[0] * 1.0e-10) \ + error( \ + "particle shift diff exceeds dx_max_sort in cell cj. " \ + "cj->nodeID=%d " \ + "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->" #TYPE \ + ".dx_max_sort=%e " \ + "cj->" #TYPE \ + ".dx_max_sort_old=%e, cellID=%i super->cellID=%i" \ + "cj->depth=%d cj->maxdepth=%d", \ + cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->TYPE.dx_max_sort, \ + cj->TYPE.dx_max_sort_old, cj->cellID, cj->hydro.super->cellID, \ + cj->depth, cj->maxdepth); \ + } \ + }) + +/** + * @brief Determine which version of DOPAIR1_STARS needs to be called depending + * on the orientation of the cells or whether DOPAIR1_STARS needs to be called + * at all. + * + * @param r #runner + * @param ci #cell ci + * @param cj #cell cj + * + */ +void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj) { + + const struct engine *restrict e = r->e; + + /* Get the sort ID. */ + double shift[3] = {0.0, 0.0, 0.0}; + const int sid = space_getsid(e->s, &ci, &cj, shift); + + const int ci_active = cell_is_active_stars(ci, e); + const int cj_active = cell_is_active_stars(cj, e); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_stars = ci->nodeID == e->nodeID; + const int do_cj_stars = cj->nodeID == e->nodeID; +#else + /* here we are updating the hydro -> switch ci, cj */ + const int do_ci_stars = cj->nodeID == e->nodeID; + const int do_cj_stars = ci->nodeID == e->nodeID; +#endif + const int do_ci = (ci->stars.count != 0 && cj->hydro.count != 0 && + ci_active && do_ci_stars); + const int do_cj = (cj->stars.count != 0 && ci->hydro.count != 0 && + cj_active && do_cj_stars); + + /* Anything to do here? */ + if (!do_ci && !do_cj) return; + + /* Check that cells are drifted. */ + if (do_ci && + (!cell_are_spart_drifted(ci, e) || !cell_are_part_drifted(cj, e))) + error("Interacting undrifted cells."); + + /* Have the cells been sorted? */ + if (do_ci && (!(ci->stars.sorted & (1 << sid)) || + ci->stars.dx_max_sort_old > space_maxreldx * ci->dmin)) + error("Interacting unsorted cells."); + + if (do_ci && (!(cj->hydro.sorted & (1 << sid)) || + cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)) + error("Interacting unsorted cells."); + + if (do_cj && + (!cell_are_part_drifted(ci, e) || !cell_are_spart_drifted(cj, e))) + error("Interacting undrifted cells."); + + /* Have the cells been sorted? */ + if (do_cj && (!(ci->hydro.sorted & (1 << sid)) || + ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin)) + error("Interacting unsorted cells."); + + if (do_cj && (!(cj->stars.sorted & (1 << sid)) || + cj->stars.dx_max_sort_old > space_maxreldx * cj->dmin)) + error("Interacting unsorted cells."); + +#ifdef SWIFT_DEBUG_CHECKS + if (do_ci) { + // MATTHIEU: This test is faulty. To be fixed... + // RUNNER_CHECK_SORT(hydro, part, cj, ci, sid); + RUNNER_CHECK_SORT(stars, spart, ci, cj, sid); + } + + if (do_cj) { + // MATTHIEU: This test is faulty. To be fixed... + // RUNNER_CHECK_SORT(hydro, part, ci, cj, sid); + RUNNER_CHECK_SORT(stars, spart, cj, ci, sid); + } +#endif /* SWIFT_DEBUG_CHECKS */ + +#ifdef SWIFT_USE_NAIVE_INTERACTIONS_STARS + DOPAIR1_STARS_NAIVE(r, ci, cj, 1); +#else + DO_SYM_PAIR1_STARS(r, ci, cj, sid, shift); +#endif +} + +/** + * @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_STARS(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->stars.count != 0 && cj->hydro.count != 0 && + cell_is_active_stars(ci, e); + const int should_do_cj = cj->stars.count != 0 && ci->hydro.count != 0 && + cell_is_active_stars(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_stars_task(ci, cj) && + cell_can_recurse_in_pair_stars_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_STARS(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_stars = ci->nodeID == e->nodeID; + const int do_cj_stars = cj->nodeID == e->nodeID; +#else + /* here we are updating the hydro -> switch ci, cj */ + const int do_ci_stars = cj->nodeID == e->nodeID; + const int do_cj_stars = ci->nodeID == e->nodeID; +#endif + const int do_ci = ci->stars.count != 0 && cj->hydro.count != 0 && + cell_is_active_stars(ci, e) && do_ci_stars; + const int do_cj = cj->stars.count != 0 && ci->hydro.count != 0 && + cell_is_active_stars(cj, e) && do_cj_stars; + + if (do_ci) { + + /* Make sure both cells are drifted to the current timestep. */ + if (!cell_are_spart_drifted(ci, e)) + error("Interacting undrifted cells (sparts)."); + + if (!cell_are_part_drifted(cj, e)) + error("Interacting undrifted cells (parts)."); + + /* Do any of the cells need to be sorted first? */ + if (!(ci->stars.sorted & (1 << sid)) || + ci->stars.dx_max_sort_old > ci->dmin * space_maxreldx) { + error("Interacting unsorted cell (sparts)."); + } + + if (!(cj->hydro.sorted & (1 << sid)) || + cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx) + error("Interacting unsorted cell (parts). %i", cj->nodeID); + } + + 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_spart_drifted(cj, e)) + error("Interacting undrifted cells (sparts)."); + + /* Do any of the cells need to be sorted first? */ + if (!(ci->hydro.sorted & (1 << sid)) || + ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx) { + error("Interacting unsorted cell (parts)."); + } + + if (!(cj->stars.sorted & (1 << sid)) || + cj->stars.dx_max_sort_old > cj->dmin * space_maxreldx) { + error("Interacting unsorted cell (sparts)."); + } + } + + if (do_ci || do_cj) DOPAIR1_BRANCH_STARS(r, ci, cj); + } + + TIMER_TOC(TIMER_DOSUB_PAIR_STARS); +} + +/** + * @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_STARS(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->stars.count == 0 || + !cell_is_active_stars(ci, r->e)) + return; + + /* Recurse? */ + if (cell_can_recurse_in_self_stars_task(ci)) { + + /* Loop over all progeny. */ + for (int k = 0; k < 8; k++) + if (ci->progeny[k] != NULL) { + DOSUB_SELF1_STARS(r, ci->progeny[k], 0); + for (int j = k + 1; j < 8; j++) + if (ci->progeny[j] != NULL) + DOSUB_PAIR1_STARS(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_spart_drifted(ci, r->e)) error("Interacting undrifted cell."); + + DOSELF1_BRANCH_STARS(r, ci); + } + + TIMER_TOC(TIMER_DOSUB_SELF_STARS); +} diff --git a/src/runner_doiact_grav.c b/src/runner_doiact_grav.c new file mode 100644 index 0000000000000000000000000000000000000000..d4b71b7e94ad1d5731cd81747e296a0aed05e520 --- /dev/null +++ b/src/runner_doiact_grav.c @@ -0,0 +1,1824 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ +#include "../config.h" + +/* This object's header. */ +#include "runner_doiact_grav.h" + +/* Local includes. */ +#include "active.h" +#include "cell.h" +#include "gravity.h" +#include "gravity_cache.h" +#include "gravity_iact.h" +#include "inline.h" +#include "part.h" +#include "space_getsid.h" +#include "timers.h" + +/** + * @brief Recursively propagate the multipoles down the tree by applying the + * L2L and L2P kernels. + * + * @param r The #runner. + * @param c The #cell we are working on. + * @param timer Are we timing this ? + */ +void runner_do_grav_down(struct runner *r, struct cell *c, int timer) { + + /* Some constants */ + const struct engine *e = r->e; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->grav.ti_old_multipole != e->ti_current) + error("c->multipole not drifted."); + if (c->grav.multipole->pot.ti_init != e->ti_current) + error("c->field tensor not initialised"); +#endif + + if (c->split) { + + /* Node case */ + + /* Add the field-tensor to all the 8 progenitors */ + for (int k = 0; k < 8; ++k) { + struct cell *cp = c->progeny[k]; + + /* Do we have a progenitor with any active g-particles ? */ + if (cp != NULL && cell_is_active_gravity(cp, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + if (cp->grav.ti_old_multipole != e->ti_current) + error("cp->multipole not drifted."); + if (cp->grav.multipole->pot.ti_init != e->ti_current) + error("cp->field tensor not initialised"); +#endif + /* If the tensor received any contribution, push it down */ + if (c->grav.multipole->pot.interacted) { + + struct grav_tensor shifted_tensor; + + /* Shift the field tensor */ + gravity_L2L(&shifted_tensor, &c->grav.multipole->pot, + cp->grav.multipole->CoM, c->grav.multipole->CoM); + + /* Add it to this level's tensor */ + gravity_field_tensors_add(&cp->grav.multipole->pot, &shifted_tensor); + } + + /* Recurse */ + runner_do_grav_down(r, cp, 0); + } + } + + } else { + + /* Leaf case */ + + /* We can abort early if no interactions via multipole happened */ + if (!c->grav.multipole->pot.interacted) return; + + if (!cell_are_gpart_drifted(c, e)) error("Un-drifted gparts"); + + /* Cell properties */ + struct gpart *gparts = c->grav.parts; + const int gcount = c->grav.count; + const struct grav_tensor *pot = &c->grav.multipole->pot; + const double CoM[3] = {c->grav.multipole->CoM[0], c->grav.multipole->CoM[1], + c->grav.multipole->CoM[2]}; + + /* Apply accelerations to the particles */ + for (int i = 0; i < gcount; ++i) { + + /* Get a handle on the gpart */ + struct gpart *gp = &gparts[i]; + + /* Update if active */ + if (gpart_is_active(gp, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles have been drifted to the current time */ + if (gp->ti_drift != e->ti_current) + error("gpart not drifted to current time"); + if (c->grav.multipole->pot.ti_init != e->ti_current) + error("c->field tensor not initialised"); + + /* Check that we are not updated an inhibited particle */ + if (gpart_is_inhibited(gp, e)) error("Updating an inhibited particle!"); + + /* Check that the particle was initialised */ + if (gp->initialised == 0) + error("Adding forces to an un-initialised gpart."); +#endif + /* Apply the kernel */ + gravity_L2P(pot, CoM, gp); + } + } + } + + if (timer) TIMER_TOC(timer_dograv_down); +} + +/** + * @brief Compute the non-truncated gravity interactions between all particles + * of a cell and the particles of the other cell. + * + * The calculation is performed non-symmetrically using the pre-filled + * #gravity_cache structures. The loop over the j cache should auto-vectorize. + * + * @param ci_cache #gravity_cache contaning the particles to be updated. + * @param cj_cache #gravity_cache contaning the source particles. + * @param gcount_i The number of particles in the cell i. + * @param gcount_padded_j The number of particles in the cell j padded to the + * vector length. + * @param periodic Is the calculation using periodic BCs ? + * @param dim The size of the simulation volume. + * + * @param e The #engine (for debugging checks only). + * @param gparts_i The #gpart in cell i (for debugging checks only). + * @param gparts_j The #gpart in cell j (for debugging checks only). + * @param gcount_j The number of particles in the cell j (for debugging checks + * only). + */ +static INLINE void runner_dopair_grav_pp_full( + struct gravity_cache *restrict ci_cache, + struct gravity_cache *restrict cj_cache, const int gcount_i, + const int gcount_j, const int gcount_padded_j, const int periodic, + const float dim[3], const struct engine *restrict e, + struct gpart *restrict gparts_i, const struct gpart *restrict gparts_j) { + + /* Loop over all particles in ci... */ + for (int pid = 0; pid < gcount_i; pid++) { + + /* Skip inactive particles */ + if (!ci_cache->active[pid]) continue; + + /* Skip particle that can use the multipole */ + if (ci_cache->use_mpole[pid]) continue; + +#ifdef SWIFT_DEBUG_CHECKS + if (!gpart_is_active(&gparts_i[pid], e)) + error("Inactive particle went through the cache"); +#endif + + const float x_i = ci_cache->x[pid]; + const float y_i = ci_cache->y[pid]; + const float z_i = ci_cache->z[pid]; + const float h_i = ci_cache->epsilon[pid]; + + /* Local accumulators for the acceleration and potential */ + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; + + /* Make the compiler understand we are in happy vectorization land */ + swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->epsilon, SWIFT_CACHE_ALIGNMENT); + swift_assume_size(gcount_padded_j, VEC_SIZE); + + /* Loop over every particle in the other cell. */ + for (int pjd = 0; pjd < gcount_padded_j; pjd++) { + + /* Get info about j */ + const float x_j = cj_cache->x[pjd]; + const float y_j = cj_cache->y[pjd]; + const float z_j = cj_cache->z[pjd]; + const float mass_j = cj_cache->m[pjd]; + const float h_j = cj_cache->epsilon[pjd]; + + /* Compute the pairwise distance. */ + float dx = x_j - x_i; + float dy = y_j - y_i; + float dz = z_j - z_i; + + /* Correct for periodic BCs */ + if (periodic) { + dx = nearestf(dx, dim[0]); + dy = nearestf(dy, dim[1]); + dz = nearestf(dz, dim[2]); + } + + const float r2 = dx * dx + dy * dy + dz * dz; + + /* Pick the maximal softening length of i and j */ + const float h = max(h_i, h_j); + const float h2 = h * h; + const float h_inv = 1.f / h; + const float h_inv_3 = h_inv * h_inv * h_inv; + +#ifdef SWIFT_DEBUG_CHECKS + if (r2 == 0.f && h2 == 0.) + error("Interacting particles with 0 distance and 0 softening."); + + /* Check that particles have been drifted to the current time */ + if (gparts_i[pid].ti_drift != e->ti_current) + error("gpi not drifted to current time"); + if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current && + !gpart_is_inhibited(&gparts_j[pjd], e)) + error("gpj not drifted to current time"); + + /* Check that we are not updated an inhibited particle */ + if (gpart_is_inhibited(&gparts_i[pid], e)) + error("Updating an inhibited particle!"); + + /* Check that the particle we interact with was not inhibited */ + if (pjd < gcount_j && gpart_is_inhibited(&gparts_j[pjd], e) && + mass_j != 0.f) + error("Inhibited particle used as gravity source."); + + /* Check that the particle was initialised */ + if (gparts_i[pid].initialised == 0) + error("Adding forces to an un-initialised gpart."); +#endif + + /* Interact! */ + float f_ij, pot_ij; + runner_iact_grav_pp_full(r2, h2, h_inv, h_inv_3, mass_j, &f_ij, &pot_ij); + + /* Store it back */ + a_x += f_ij * dx; + a_y += f_ij * dy; + a_z += f_ij * dz; + pot += pot_ij; + +#ifdef SWIFT_DEBUG_CHECKS + /* Update the interaction counter if it's not a padded gpart */ + if (pjd < gcount_j && !gpart_is_inhibited(&gparts_j[pjd], e)) + gparts_i[pid].num_interacted++; +#endif + } + + /* Store everything back in cache */ + ci_cache->a_x[pid] += a_x; + ci_cache->a_y[pid] += a_y; + ci_cache->a_z[pid] += a_z; + ci_cache->pot[pid] += pot; + } +} + +/** + * @brief Compute the truncated gravity interactions between all particles + * of a cell and the particles of the other cell. + * + * The calculation is performed non-symmetrically using the pre-filled + * #gravity_cache structures. The loop over the j cache should auto-vectorize. + * + * This function only makes sense in periodic BCs. + * + * @param ci_cache #gravity_cache contaning the particles to be updated. + * @param cj_cache #gravity_cache contaning the source particles. + * @param gcount_i The number of particles in the cell i. + * @param gcount_padded_j The number of particles in the cell j padded to the + * vector length. + * @param dim The size of the simulation volume. + * @param r_s_inv The inverse of the gravity-mesh smoothing-scale. + * + * @param e The #engine (for debugging checks only). + * @param gparts_i The #gpart in cell i (for debugging checks only). + * @param gparts_j The #gpart in cell j (for debugging checks only). + * @param gcount_j The number of particles in the cell j (for debugging checks + * only). + */ +static INLINE void runner_dopair_grav_pp_truncated( + struct gravity_cache *restrict ci_cache, + struct gravity_cache *restrict cj_cache, const int gcount_i, + const int gcount_j, const int gcount_padded_j, const float dim[3], + const float r_s_inv, const struct engine *restrict e, + struct gpart *restrict gparts_i, const struct gpart *restrict gparts_j) { + +#ifdef SWIFT_DEBUG_CHECKS + if (!e->s->periodic) + error("Calling truncated PP function in non-periodic setup."); +#endif + + /* Loop over all particles in ci... */ + for (int pid = 0; pid < gcount_i; pid++) { + + /* Skip inactive particles */ + if (!ci_cache->active[pid]) continue; + + /* Skip particle that can use the multipole */ + if (ci_cache->use_mpole[pid]) continue; + +#ifdef SWIFT_DEBUG_CHECKS + if (!gpart_is_active(&gparts_i[pid], e)) + error("Inactive particle went through the cache"); +#endif + + const float x_i = ci_cache->x[pid]; + const float y_i = ci_cache->y[pid]; + const float z_i = ci_cache->z[pid]; + const float h_i = ci_cache->epsilon[pid]; + + /* Local accumulators for the acceleration and potential */ + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; + + /* Make the compiler understand we are in happy vectorization land */ + swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->epsilon, SWIFT_CACHE_ALIGNMENT); + swift_assume_size(gcount_padded_j, VEC_SIZE); + + /* Loop over every particle in the other cell. */ + for (int pjd = 0; pjd < gcount_padded_j; pjd++) { + + /* Get info about j */ + const float x_j = cj_cache->x[pjd]; + const float y_j = cj_cache->y[pjd]; + const float z_j = cj_cache->z[pjd]; + const float mass_j = cj_cache->m[pjd]; + const float h_j = cj_cache->epsilon[pjd]; + + /* Compute the pairwise distance. */ + float dx = x_j - x_i; + float dy = y_j - y_i; + float dz = z_j - z_i; + + /* Correct for periodic BCs */ + dx = nearestf(dx, dim[0]); + dy = nearestf(dy, dim[1]); + dz = nearestf(dz, dim[2]); + + const float r2 = dx * dx + dy * dy + dz * dz; + + /* Pick the maximal softening length of i and j */ + const float h = max(h_i, h_j); + const float h2 = h * h; + const float h_inv = 1.f / h; + const float h_inv_3 = h_inv * h_inv * h_inv; + +#ifdef SWIFT_DEBUG_CHECKS + if (r2 == 0.f && h2 == 0.) + error("Interacting particles with 0 distance and 0 softening."); + + /* Check that particles have been drifted to the current time */ + if (gparts_i[pid].ti_drift != e->ti_current) + error("gpi not drifted to current time"); + if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current && + !gpart_is_inhibited(&gparts_j[pjd], e)) + error("gpj not drifted to current time"); + + /* Check that we are not updated an inhibited particle */ + if (gpart_is_inhibited(&gparts_i[pid], e)) + error("Updating an inhibited particle!"); + + /* Check that the particle we interact with was not inhibited */ + if (pjd < gcount_j && gpart_is_inhibited(&gparts_j[pjd], e) && + mass_j != 0.f) + error("Inhibited particle used as gravity source."); + + /* Check that the particle was initialised */ + if (gparts_i[pid].initialised == 0) + error("Adding forces to an un-initialised gpart."); +#endif + + /* Interact! */ + float f_ij, pot_ij; + runner_iact_grav_pp_truncated(r2, h2, h_inv, h_inv_3, mass_j, r_s_inv, + &f_ij, &pot_ij); + + /* Store it back */ + a_x += f_ij * dx; + a_y += f_ij * dy; + a_z += f_ij * dz; + pot += pot_ij; + +#ifdef SWIFT_DEBUG_CHECKS + /* Update the interaction counter if it's not a padded gpart */ + if (pjd < gcount_j && !gpart_is_inhibited(&gparts_j[pjd], e)) + gparts_i[pid].num_interacted++; +#endif + } + + /* Store everything back in cache */ + ci_cache->a_x[pid] += a_x; + ci_cache->a_y[pid] += a_y; + ci_cache->a_z[pid] += a_z; + ci_cache->pot[pid] += pot; + } +} + +/** + * @brief Compute the gravity interactions between all particles + * of a cell and the multipole of the other cell. + * + * The calculation is performedusing the pre-filled + * #gravity_cache structure. The loop over the i cache should auto-vectorize. + * + * @param ci_cache #gravity_cache contaning the particles to be updated. + * @param gcount_padded_i The number of particles in the cell i padded to the + * vector length. + * @param CoM_j Position of the #multipole in #cell j. + * @param multi_j The #multipole in #cell j. + * @param periodic Is the calculation using periodic BCs ? + * @param dim The size of the simulation volume. + * + * @param e The #engine (for debugging checks only). + * @param gparts_i The #gpart in cell i (for debugging checks only). + * @param gcount_i The number of particles in the cell i (for debugging checks + * only). + * @param cj The #cell j (for debugging checks only). + */ +static INLINE void runner_dopair_grav_pm_full( + struct gravity_cache *ci_cache, const int gcount_padded_i, + const float CoM_j[3], const struct multipole *restrict multi_j, + const int periodic, const float dim[3], const struct engine *restrict e, + struct gpart *restrict gparts_i, const int gcount_i, + const struct cell *restrict cj) { + + /* Make the compiler understand we are in happy vectorization land */ + swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, y, ci_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, z, ci_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, epsilon, ci_cache->epsilon, + SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, a_x, ci_cache->a_x, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, a_y, ci_cache->a_y, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, a_z, ci_cache->a_z, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, pot, ci_cache->pot, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(int, active, ci_cache->active, + SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(int, use_mpole, ci_cache->use_mpole, + SWIFT_CACHE_ALIGNMENT); + swift_assume_size(gcount_padded_i, VEC_SIZE); + + /* Loop over all particles in ci... */ + for (int pid = 0; pid < gcount_padded_i; pid++) { + + /* Skip inactive particles */ + if (!active[pid]) continue; + + /* Skip particle that cannot use the multipole */ + if (!use_mpole[pid]) continue; + +#ifdef SWIFT_DEBUG_CHECKS + if (pid < gcount_i && !gpart_is_active(&gparts_i[pid], e)) + error("Active particle went through the cache"); + + /* Check that particles have been drifted to the current time */ + if (gparts_i[pid].ti_drift != e->ti_current) + error("gpi not drifted to current time"); + + /* Check that we are not updated an inhibited particle */ + if (gpart_is_inhibited(&gparts_i[pid], e)) + error("Updating an inhibited particle!"); + + /* Check that the particle was initialised */ + if (gparts_i[pid].initialised == 0) + error("Adding forces to an un-initialised gpart."); + + if (pid >= gcount_i) error("Adding forces to padded particle"); +#endif + + const float x_i = x[pid]; + const float y_i = y[pid]; + const float z_i = z[pid]; + + /* Some powers of the softening length */ + const float h_i = epsilon[pid]; + const float h_inv_i = 1.f / h_i; + + /* Distance to the Multipole */ + float dx = CoM_j[0] - x_i; + float dy = CoM_j[1] - y_i; + float dz = CoM_j[2] - z_i; + + /* Apply periodic BCs? */ + if (periodic) { + dx = nearestf(dx, dim[0]); + dy = nearestf(dy, dim[1]); + dz = nearestf(dz, dim[2]); + } + + const float r2 = dx * dx + dy * dy + dz * dz; + +#ifdef SWIFT_DEBUG_CHECKS + const float r_max_j = cj->grav.multipole->r_max; + const float r_max2 = r_max_j * r_max_j; + const float theta_crit2 = e->gravity_properties->theta_crit2; + + /* Note: 0.99 and 1.1 to avoid FP rounding false-positives */ + if (!gravity_M2P_accept(r_max2, theta_crit2 * 1.1, r2, 0.99 * h_i)) + error( + "use_mpole[i] set when M2P accept fails CoM=[%e %e %e] pos=[%e %e " + "%e], rmax=%e r=%e epsilon=%e", + CoM_j[0], CoM_j[1], CoM_j[2], x_i, y_i, z_i, r_max_j, sqrtf(r2), h_i); +#endif + + /* Interact! */ + float f_x, f_y, f_z, pot_ij; + runner_iact_grav_pm_full(dx, dy, dz, r2, h_i, h_inv_i, multi_j, &f_x, &f_y, + &f_z, &pot_ij); + + /* Store it back */ + a_x[pid] += f_x; + a_y[pid] += f_y; + a_z[pid] += f_z; + pot[pid] += pot_ij; + +#ifdef SWIFT_DEBUG_CHECKS + /* Update the interaction counter */ + if (pid < gcount_i) + gparts_i[pid].num_interacted += cj->grav.multipole->m_pole.num_gpart; +#endif + } +} + +/** + * @brief Compute the gravity interactions between all particles + * of a cell and the multipole of the other cell. + * + * The calculation is performedusing the pre-filled + * #gravity_cache structure. The loop over the i cache should auto-vectorize. + * + * This function only makes sense in periodic BCs. + * + * @param ci_cache #gravity_cache contaning the particles to be updated. + * @param gcount_padded_i The number of particles in the cell i padded to the + * vector length. + * @param CoM_j Position of the #multipole in #cell j. + * @param multi_j The #multipole in #cell j. + * @param dim The size of the simulation volume. + * @param r_s_inv The inverse of the gravity-mesh smoothing-scale. + * + * @param e The #engine (for debugging checks only). + * @param gparts_i The #gpart in cell i (for debugging checks only). + * @param gcount_i The number of particles in the cell i (for debugging checks + * only). + * @param cj The #cell j (for debugging checks only). + */ +static INLINE void runner_dopair_grav_pm_truncated( + struct gravity_cache *ci_cache, const int gcount_padded_i, + const float CoM_j[3], const struct multipole *restrict multi_j, + const float dim[3], const float r_s_inv, const struct engine *restrict e, + struct gpart *restrict gparts_i, const int gcount_i, + const struct cell *restrict cj) { + +#ifdef SWIFT_DEBUG_CHECKS + if (!e->s->periodic) + error("Calling truncated PP function in non-periodic setup."); +#endif + + /* Make the compiler understand we are in happy vectorization land */ + swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, y, ci_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, z, ci_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, epsilon, ci_cache->epsilon, + SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, a_x, ci_cache->a_x, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, a_y, ci_cache->a_y, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, a_z, ci_cache->a_z, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, pot, ci_cache->pot, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(int, active, ci_cache->active, + SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(int, use_mpole, ci_cache->use_mpole, + SWIFT_CACHE_ALIGNMENT); + swift_assume_size(gcount_padded_i, VEC_SIZE); + + /* Loop over all particles in ci... */ + for (int pid = 0; pid < gcount_padded_i; pid++) { + + /* Skip inactive particles */ + if (!active[pid]) continue; + + /* Skip particle that cannot use the multipole */ + if (!use_mpole[pid]) continue; + +#ifdef SWIFT_DEBUG_CHECKS + if (pid < gcount_i && !gpart_is_active(&gparts_i[pid], e)) + error("Active particle went through the cache"); + + /* Check that particles have been drifted to the current time */ + if (gparts_i[pid].ti_drift != e->ti_current) + error("gpi not drifted to current time"); + + /* Check that we are not updated an inhibited particle */ + if (gpart_is_inhibited(&gparts_i[pid], e)) + error("Updating an inhibited particle!"); + + /* Check that the particle was initialised */ + if (gparts_i[pid].initialised == 0) + error("Adding forces to an un-initialised gpart."); + + if (pid >= gcount_i) error("Adding forces to padded particle"); +#endif + + const float x_i = x[pid]; + const float y_i = y[pid]; + const float z_i = z[pid]; + + /* Some powers of the softening length */ + const float h_i = epsilon[pid]; + const float h_inv_i = 1.f / h_i; + + /* Distance to the Multipole */ + float dx = CoM_j[0] - x_i; + float dy = CoM_j[1] - y_i; + float dz = CoM_j[2] - z_i; + + /* Apply periodic BCs */ + dx = nearestf(dx, dim[0]); + dy = nearestf(dy, dim[1]); + dz = nearestf(dz, dim[2]); + + const float r2 = dx * dx + dy * dy + dz * dz; + +#ifdef SWIFT_DEBUG_CHECKS + const float r_max_j = cj->grav.multipole->r_max; + const float r_max2 = r_max_j * r_max_j; + const float theta_crit2 = e->gravity_properties->theta_crit2; + + /* 0.99 and 1.1 to avoid FP rounding false-positives */ + if (!gravity_M2P_accept(r_max2, theta_crit2 * 1.1, r2, 0.99 * h_i)) + error( + "use_mpole[i] set when M2P accept fails CoM=[%e %e %e] pos=[%e %e " + "%e], rmax=%e", + CoM_j[0], CoM_j[1], CoM_j[2], x_i, y_i, z_i, r_max_j); +#endif + + /* Interact! */ + float f_x, f_y, f_z, pot_ij; + runner_iact_grav_pm_truncated(dx, dy, dz, r2, h_i, h_inv_i, r_s_inv, + multi_j, &f_x, &f_y, &f_z, &pot_ij); + + /* Store it back */ + a_x[pid] += f_x; + a_y[pid] += f_y; + a_z[pid] += f_z; + pot[pid] += pot_ij; + +#ifdef SWIFT_DEBUG_CHECKS + /* Update the interaction counter */ + if (pid < gcount_i) + gparts_i[pid].num_interacted += cj->grav.multipole->m_pole.num_gpart; +#endif + } +} + +/** + * @brief Computes the interaction of all the particles in a cell with all the + * particles of another cell. + * + * This function switches between the full potential and the truncated one + * depending on needs. It will also use the M2P (multipole) interaction + * for the subset of particles in either cell for which the distance criterion + * is valid. + * + * This function starts by constructing the require #gravity_cache for both + * cells and then call the specialised functions doing the actual work on + * the caches. It then write the data back to the particles. + * + * @param r The #runner. + * @param ci The first #cell. + * @param cj The other #cell. + * @param symmetric Are we updating both cells (1) or just ci (0) ? + * @param allow_mpole Are we allowing the use of P2M interactions ? + */ +void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj, + const int symmetric, const int allow_mpole) { + + /* Recover some useful constants */ + const struct engine *e = r->e; + const int periodic = e->mesh->periodic; + const float dim[3] = {(float)e->mesh->dim[0], (float)e->mesh->dim[1], + (float)e->mesh->dim[2]}; + const float r_s_inv = e->mesh->r_s_inv; + const double min_trunc = e->mesh->r_cut_min; + + TIMER_TIC; + + /* Record activity status */ + const int ci_active = + cell_is_active_gravity(ci, e) && (ci->nodeID == e->nodeID); + const int cj_active = + cell_is_active_gravity(cj, e) && (cj->nodeID == e->nodeID); + + /* Anything to do here? */ + if (!ci_active && !cj_active) return; + if (!ci_active && !symmetric) return; + + /* Check that we are not doing something stupid */ + if (ci->split || cj->split) error("Running P-P on splitable cells"); + + /* Let's start by checking things are drifted */ + if (!cell_are_gpart_drifted(ci, e)) error("Un-drifted gparts"); + if (!cell_are_gpart_drifted(cj, e)) error("Un-drifted gparts"); + if (cj_active && ci->grav.ti_old_multipole != e->ti_current) + error("Un-drifted multipole"); + if (ci_active && cj->grav.ti_old_multipole != e->ti_current) + error("Un-drifted multipole"); + + /* Caches to play with */ + struct gravity_cache *const ci_cache = &r->ci_gravity_cache; + struct gravity_cache *const cj_cache = &r->cj_gravity_cache; + + /* Shift to apply to the particles in each cell */ + const double shift_i[3] = {0., 0., 0.}; + const double shift_j[3] = {0., 0., 0.}; + + /* Recover the multipole info and shift the CoM locations */ + const float rmax_i = ci->grav.multipole->r_max; + const float rmax_j = cj->grav.multipole->r_max; + const float rmax2_i = rmax_i * rmax_i; + const float rmax2_j = rmax_j * rmax_j; + const struct multipole *multi_i = &ci->grav.multipole->m_pole; + const struct multipole *multi_j = &cj->grav.multipole->m_pole; + const float CoM_i[3] = {(float)(ci->grav.multipole->CoM[0] - shift_i[0]), + (float)(ci->grav.multipole->CoM[1] - shift_i[1]), + (float)(ci->grav.multipole->CoM[2] - shift_i[2])}; + const float CoM_j[3] = {(float)(cj->grav.multipole->CoM[0] - shift_j[0]), + (float)(cj->grav.multipole->CoM[1] - shift_j[1]), + (float)(cj->grav.multipole->CoM[2] - shift_j[2])}; + + /* Start by constructing particle caches */ + + /* Computed the padded counts */ + const int gcount_i = ci->grav.count; + const int gcount_j = cj->grav.count; + const int gcount_padded_i = gcount_i - (gcount_i % VEC_SIZE) + VEC_SIZE; + const int gcount_padded_j = gcount_j - (gcount_j % VEC_SIZE) + VEC_SIZE; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that we fit in cache */ + if (gcount_i > ci_cache->count || gcount_j > cj_cache->count) + error("Not enough space in the caches! gcount_i=%d gcount_j=%d", gcount_i, + gcount_j); +#endif + + /* Fill the caches */ + gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim, + ci_cache, ci->grav.parts, gcount_i, gcount_padded_i, + shift_i, CoM_j, rmax2_j, ci, e->gravity_properties); + gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim, + cj_cache, cj->grav.parts, gcount_j, gcount_padded_j, + shift_j, CoM_i, rmax2_i, cj, e->gravity_properties); + + /* Can we use the Newtonian version or do we need the truncated one ? */ + if (!periodic) { + + /* Not periodic -> Can always use Newtonian potential */ + + /* Let's updated the active cell(s) only */ + if (ci_active) { + + /* First the P2P */ + runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j, + gcount_padded_j, periodic, dim, e, + ci->grav.parts, cj->grav.parts); + + /* Then the M2P */ + if (allow_mpole) + runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j, + periodic, dim, e, ci->grav.parts, gcount_i, + cj); + } + if (cj_active && symmetric) { + + /* First the P2P */ + runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i, + gcount_padded_i, periodic, dim, e, + cj->grav.parts, ci->grav.parts); + + /* Then the M2P */ + if (allow_mpole) + runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i, + periodic, dim, e, cj->grav.parts, gcount_j, + ci); + } + + } else { /* Periodic BC */ + + /* Get the relative distance between the CoMs */ + const double dx[3] = {CoM_j[0] - CoM_i[0], CoM_j[1] - CoM_i[1], + CoM_j[2] - CoM_i[2]}; + const double r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + + /* Get the maximal distance between any two particles */ + const double max_r = sqrt(r2) + rmax_i + rmax_j; + + /* Do we need to use the truncated interactions ? */ + if (max_r > min_trunc) { + + /* Periodic but far-away cells must use the truncated potential */ + + /* Let's updated the active cell(s) only */ + if (ci_active) { + + /* First the (truncated) P2P */ + runner_dopair_grav_pp_truncated(ci_cache, cj_cache, gcount_i, gcount_j, + gcount_padded_j, dim, r_s_inv, e, + ci->grav.parts, cj->grav.parts); + + /* Then the M2P */ + if (allow_mpole) + runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j, + multi_j, dim, r_s_inv, e, + ci->grav.parts, gcount_i, cj); + } + if (cj_active && symmetric) { + + /* First the (truncated) P2P */ + runner_dopair_grav_pp_truncated(cj_cache, ci_cache, gcount_j, gcount_i, + gcount_padded_i, dim, r_s_inv, e, + cj->grav.parts, ci->grav.parts); + + /* Then the M2P */ + if (allow_mpole) + runner_dopair_grav_pm_truncated(cj_cache, gcount_padded_j, CoM_i, + multi_i, dim, r_s_inv, e, + cj->grav.parts, gcount_j, ci); + } + + } else { + + /* Periodic but close-by cells can use the full Newtonian potential */ + + /* Let's updated the active cell(s) only */ + if (ci_active) { + + /* First the (Newtonian) P2P */ + runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j, + gcount_padded_j, periodic, dim, e, + ci->grav.parts, cj->grav.parts); + + /* Then the M2P */ + if (allow_mpole) + runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j, + periodic, dim, e, ci->grav.parts, gcount_i, + cj); + } + if (cj_active && symmetric) { + + /* First the (Newtonian) P2P */ + runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i, + gcount_padded_i, periodic, dim, e, + cj->grav.parts, ci->grav.parts); + + /* Then the M2P */ + if (allow_mpole) + runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i, + periodic, dim, e, cj->grav.parts, gcount_j, + ci); + } + } + } + + /* Write back to the particles */ + if (ci_active) gravity_cache_write_back(ci_cache, ci->grav.parts, gcount_i); + if (cj_active && symmetric) + gravity_cache_write_back(cj_cache, cj->grav.parts, gcount_j); + + TIMER_TOC(timer_dopair_grav_pp); +} + +/** + * @brief Compute the non-truncated gravity interactions between all particles + * of a cell and the particles of the other cell. + * + * The calculation is performed non-symmetrically using the pre-filled + * #gravity_cache structures. The loop over the j cache should auto-vectorize. + * + * @param ci_cache #gravity_cache contaning the particles to be updated. + * @param gcount The number of particles in the cell. + * @param gcount_padded The number of particles in the cell padded to the + * vector length. + * + * @param e The #engine (for debugging checks only). + * @param gparts The #gpart in the cell (for debugging checks only). + */ +static INLINE void runner_doself_grav_pp_full( + struct gravity_cache *restrict ci_cache, const int gcount, + const int gcount_padded, const struct engine *e, struct gpart *gparts) { + + /* Loop over all particles in ci... */ + for (int pid = 0; pid < gcount; pid++) { + + /* Skip inactive particles */ + if (!ci_cache->active[pid]) continue; + + const float x_i = ci_cache->x[pid]; + const float y_i = ci_cache->y[pid]; + const float z_i = ci_cache->z[pid]; + const float h_i = ci_cache->epsilon[pid]; + + /* Local accumulators for the acceleration */ + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; + + /* Make the compiler understand we are in happy vectorization land */ + swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->epsilon, SWIFT_CACHE_ALIGNMENT); + swift_assume_size(gcount_padded, VEC_SIZE); + + /* Loop over every other particle in the cell. */ + for (int pjd = 0; pjd < gcount_padded; pjd++) { + + /* No self interaction */ + if (pid == pjd) continue; + + /* Get info about j */ + const float x_j = ci_cache->x[pjd]; + const float y_j = ci_cache->y[pjd]; + const float z_j = ci_cache->z[pjd]; + const float mass_j = ci_cache->m[pjd]; + const float h_j = ci_cache->epsilon[pjd]; + + /* Compute the pairwise (square) distance. */ + /* Note: no need for periodic wrapping inside a cell */ + const float dx = x_j - x_i; + const float dy = y_j - y_i; + const float dz = z_j - z_i; + const float r2 = dx * dx + dy * dy + dz * dz; + + /* Pick the maximal softening length of i and j */ + const float h = max(h_i, h_j); + const float h2 = h * h; + const float h_inv = 1.f / h; + const float h_inv_3 = h_inv * h_inv * h_inv; + +#ifdef SWIFT_DEBUG_CHECKS + if (r2 == 0.f && h2 == 0.) + error("Interacting particles with 0 distance and 0 softening."); + + /* Check that particles have been drifted to the current time */ + if (gparts[pid].ti_drift != e->ti_current) + error("gpi not drifted to current time"); + if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current && + !gpart_is_inhibited(&gparts[pjd], e)) + error("gpj not drifted to current time"); + + /* Check that we are not updated an inhibited particle */ + if (gpart_is_inhibited(&gparts[pid], e)) + error("Updating an inhibited particle!"); + + /* Check that the particle we interact with was not inhibited */ + if (pjd < gcount && gpart_is_inhibited(&gparts[pjd], e) && mass_j != 0.f) + error("Inhibited particle used as gravity source."); + + /* Check that the particle was initialised */ + if (gparts[pid].initialised == 0) + error("Adding forces to an un-initialised gpart."); +#endif + + /* Interact! */ + float f_ij, pot_ij; + runner_iact_grav_pp_full(r2, h2, h_inv, h_inv_3, mass_j, &f_ij, &pot_ij); + + /* Store it back */ + a_x += f_ij * dx; + a_y += f_ij * dy; + a_z += f_ij * dz; + pot += pot_ij; + +#ifdef SWIFT_DEBUG_CHECKS + /* Update the interaction counter if it's not a padded gpart */ + if (pjd < gcount && !gpart_is_inhibited(&gparts[pjd], e)) + gparts[pid].num_interacted++; +#endif + } + + /* Store everything back in cache */ + ci_cache->a_x[pid] += a_x; + ci_cache->a_y[pid] += a_y; + ci_cache->a_z[pid] += a_z; + ci_cache->pot[pid] += pot; + } +} + +/** + * @brief Compute the truncated gravity interactions between all particles + * of a cell and the particles of the other cell. + * + * The calculation is performed non-symmetrically using the pre-filled + * #gravity_cache structures. The loop over the j cache should auto-vectorize. + * + * This function only makes sense in periodic BCs. + * + * @param ci_cache #gravity_cache contaning the particles to be updated. + * @param gcount The number of particles in the cell. + * @param gcount_padded The number of particles in the cell padded to the + * vector length. + * @param r_s_inv The inverse of the gravity-mesh smoothing-scale. + * + * @param e The #engine (for debugging checks only). + * @param gparts The #gpart in the cell (for debugging checks only). + */ +static INLINE void runner_doself_grav_pp_truncated( + struct gravity_cache *restrict ci_cache, const int gcount, + const int gcount_padded, const float r_s_inv, const struct engine *e, + struct gpart *gparts) { + +#ifdef SWIFT_DEBUG_CHECKS + if (!e->s->periodic) + error("Calling truncated PP function in non-periodic setup."); +#endif + + /* Loop over all particles in ci... */ + for (int pid = 0; pid < gcount; pid++) { + + /* Skip inactive particles */ + if (!ci_cache->active[pid]) continue; + + const float x_i = ci_cache->x[pid]; + const float y_i = ci_cache->y[pid]; + const float z_i = ci_cache->z[pid]; + const float h_i = ci_cache->epsilon[pid]; + + /* Local accumulators for the acceleration and potential */ + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; + + /* Make the compiler understand we are in happy vectorization land */ + swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->epsilon, SWIFT_CACHE_ALIGNMENT); + swift_assume_size(gcount_padded, VEC_SIZE); + + /* Loop over every other particle in the cell. */ + for (int pjd = 0; pjd < gcount_padded; pjd++) { + + /* No self interaction */ + if (pid == pjd) continue; + + /* Get info about j */ + const float x_j = ci_cache->x[pjd]; + const float y_j = ci_cache->y[pjd]; + const float z_j = ci_cache->z[pjd]; + const float mass_j = ci_cache->m[pjd]; + const float h_j = ci_cache->epsilon[pjd]; + + /* Compute the pairwise (square) distance. */ + /* Note: no need for periodic wrapping inside a cell */ + const float dx = x_j - x_i; + const float dy = y_j - y_i; + const float dz = z_j - z_i; + + const float r2 = dx * dx + dy * dy + dz * dz; + + /* Pick the maximal softening length of i and j */ + const float h = max(h_i, h_j); + const float h2 = h * h; + const float h_inv = 1.f / h; + const float h_inv_3 = h_inv * h_inv * h_inv; + +#ifdef SWIFT_DEBUG_CHECKS + if (r2 == 0.f && h2 == 0.) + error("Interacting particles with 0 distance and 0 softening."); + + /* Check that particles have been drifted to the current time */ + if (gparts[pid].ti_drift != e->ti_current) + error("gpi not drifted to current time"); + if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current && + !gpart_is_inhibited(&gparts[pjd], e)) + error("gpj not drifted to current time"); + + /* Check that we are not updated an inhibited particle */ + if (gpart_is_inhibited(&gparts[pid], e)) + error("Updating an inhibited particle!"); + + /* Check that the particle we interact with was not inhibited */ + if (pjd < gcount && gpart_is_inhibited(&gparts[pjd], e) && mass_j != 0.f) + error("Inhibited particle used as gravity source."); + + /* Check that the particle was initialised */ + if (gparts[pid].initialised == 0) + error("Adding forces to an un-initialised gpart."); +#endif + + /* Interact! */ + float f_ij, pot_ij; + runner_iact_grav_pp_truncated(r2, h2, h_inv, h_inv_3, mass_j, r_s_inv, + &f_ij, &pot_ij); + + /* Store it back */ + a_x += f_ij * dx; + a_y += f_ij * dy; + a_z += f_ij * dz; + pot += pot_ij; + +#ifdef SWIFT_DEBUG_CHECKS + /* Update the interaction counter if it's not a padded gpart */ + if (pjd < gcount && !gpart_is_inhibited(&gparts[pjd], e)) + gparts[pid].num_interacted++; +#endif + } + + /* Store everything back in cache */ + ci_cache->a_x[pid] += a_x; + ci_cache->a_y[pid] += a_y; + ci_cache->a_z[pid] += a_z; + ci_cache->pot[pid] += pot; + } +} + +/** + * @brief Computes the interaction of all the particles in a cell with all the + * other ones. + * + * This function switches between the full potential and the truncated one + * depending on needs. + * + * This function starts by constructing the require #gravity_cache for the + * cell and then call the specialised functions doing the actual work on + * the cache. It then write the data back to the particles. + * + * @param r The #runner. + * @param c The #cell. + */ +void runner_doself_grav_pp(struct runner *r, struct cell *c) { + + /* Recover some useful constants */ + const struct engine *e = r->e; + const int periodic = e->mesh->periodic; + const float r_s_inv = e->mesh->r_s_inv; + const double min_trunc = e->mesh->r_cut_min; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->grav.count == 0) error("Doing self gravity on an empty cell !"); +#endif + + /* Anything to do here? */ + if (!cell_is_active_gravity(c, e)) return; + + /* Check that we are not doing something stupid */ + if (c->split) error("Running P-P on a splitable cell"); + + /* Do we need to start by drifting things ? */ + if (!cell_are_gpart_drifted(c, e)) error("Un-drifted gparts"); + + /* Start by constructing a cache for the particles */ + struct gravity_cache *const ci_cache = &r->ci_gravity_cache; + + /* Shift to apply to the particles in the cell */ + const double loc[3] = {c->loc[0] + 0.5 * c->width[0], + c->loc[1] + 0.5 * c->width[1], + c->loc[2] + 0.5 * c->width[2]}; + + /* Computed the padded counts */ + const int gcount = c->grav.count; + const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that we fit in cache */ + if (gcount > ci_cache->count) + error("Not enough space in the cache! gcount=%d", gcount); +#endif + + /* Fill the cache */ + gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, c->grav.parts, + gcount, gcount_padded, loc, c, + e->gravity_properties); + + /* Can we use the Newtonian version or do we need the truncated one ? */ + if (!periodic) { + + /* Not periodic -> Can always use Newtonian potential */ + runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, + c->grav.parts); + + } else { + + /* Get the maximal distance between any two particles */ + const double max_r = 2. * c->grav.multipole->r_max; + + /* Do we need to use the truncated interactions ? */ + if (max_r > min_trunc) { + + /* Periodic but far-away cells must use the truncated potential */ + runner_doself_grav_pp_truncated(ci_cache, gcount, gcount_padded, r_s_inv, + e, c->grav.parts); + + } else { + + /* Periodic but close-by cells can use the full Newtonian potential */ + runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, + c->grav.parts); + } + } + + /* Write back to the particles */ + gravity_cache_write_back(ci_cache, c->grav.parts, gcount); + + TIMER_TOC(timer_doself_grav_pp); +} + +/** + * @brief Computes the interaction of the field tensor and multipole + * of two cells symmetrically. + * + * @param r The #runner. + * @param ci The first #cell. + * @param cj The second #cell. + */ +static INLINE void runner_dopair_grav_mm_symmetric(struct runner *r, + struct cell *restrict ci, + struct cell *restrict cj) { + + /* Some constants */ + const struct engine *e = r->e; + const struct gravity_props *props = e->gravity_properties; + const int periodic = e->mesh->periodic; + const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; + const float r_s_inv = e->mesh->r_s_inv; + + TIMER_TIC; + + /* Anything to do here? */ + if ((!cell_is_active_gravity_mm(ci, e) || ci->nodeID != engine_rank) || + (!cell_is_active_gravity_mm(cj, e) || cj->nodeID != engine_rank)) + error("Invalid state in symmetric M-M calculation!"); + + /* Short-cut to the multipole */ + const struct multipole *multi_i = &ci->grav.multipole->m_pole; + const struct multipole *multi_j = &cj->grav.multipole->m_pole; + +#ifdef SWIFT_DEBUG_CHECKS + if (ci == cj) error("Interacting a cell with itself using M2L"); + + if (multi_i->num_gpart == 0) + error("Multipole i does not seem to have been set."); + + if (multi_j->num_gpart == 0) + error("Multipole j does not seem to have been set."); + + if (ci->grav.multipole->pot.ti_init != e->ti_current) + error("ci->grav tensor not initialised."); + + if (ci->grav.multipole->pot.ti_init != e->ti_current) + error("cj->grav tensor not initialised."); + + if (ci->grav.ti_old_multipole != e->ti_current) + error( + "Undrifted multipole ci->grav.ti_old_multipole=%lld ci->nodeID=%d " + "cj->nodeID=%d e->ti_current=%lld", + ci->grav.ti_old_multipole, ci->nodeID, cj->nodeID, e->ti_current); + + if (cj->grav.ti_old_multipole != e->ti_current) + error( + "Undrifted multipole cj->grav.ti_old_multipole=%lld cj->nodeID=%d " + "ci->nodeID=%d e->ti_current=%lld", + cj->grav.ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current); +#endif + + /* Let's interact at this level */ + gravity_M2L_symmetric(&ci->grav.multipole->pot, &cj->grav.multipole->pot, + multi_i, multi_j, ci->grav.multipole->CoM, + cj->grav.multipole->CoM, props, periodic, dim, r_s_inv); + + TIMER_TOC(timer_dopair_grav_mm); +} + +/** + * @brief Computes the interaction of the field tensor in a cell with the + * multipole of another cell. + * + * @param r The #runner. + * @param ci The #cell with field tensor to interact. + * @param cj The #cell with the multipole. + */ +static INLINE void runner_dopair_grav_mm_nonsym( + struct runner *r, struct cell *restrict ci, + const struct cell *restrict cj) { + + /* Some constants */ + const struct engine *e = r->e; + const struct gravity_props *props = e->gravity_properties; + const int periodic = e->mesh->periodic; + const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; + const float r_s_inv = e->mesh->r_s_inv; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_gravity_mm(ci, e) || ci->nodeID != engine_rank) return; + + /* Short-cut to the multipole */ + const struct multipole *multi_j = &cj->grav.multipole->m_pole; + +#ifdef SWIFT_DEBUG_CHECKS + if (ci == cj) error("Interacting a cell with itself using M2L"); + + if (multi_j->num_gpart == 0) + error("Multipole does not seem to have been set."); + + if (ci->grav.multipole->pot.ti_init != e->ti_current) + error("ci->grav tensor not initialised."); + + if (cj->grav.ti_old_multipole != e->ti_current) + error( + "Undrifted multipole cj->grav.ti_old_multipole=%lld cj->nodeID=%d " + "ci->nodeID=%d e->ti_current=%lld", + cj->grav.ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current); +#endif + + /* Let's interact at this level */ + gravity_M2L_nonsym(&ci->grav.multipole->pot, multi_j, ci->grav.multipole->CoM, + cj->grav.multipole->CoM, props, periodic, dim, r_s_inv); + + TIMER_TOC(timer_dopair_grav_mm); +} + +/** + * @brief Call the M-M calculation on two cells if active. + * + * @param r The #runner object. + * @param ci The first #cell. + * @param cj The second #cell. + */ +static INLINE void runner_dopair_grav_mm(struct runner *r, + struct cell *restrict ci, + struct cell *restrict cj) { + + const struct engine *e = r->e; + + /* What do we need to do? */ + const int do_i = + cell_is_active_gravity_mm(ci, e) && (ci->nodeID == e->nodeID); + const int do_j = + cell_is_active_gravity_mm(cj, e) && (cj->nodeID == e->nodeID); + + /* Do we need drifting first? */ + if (ci->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e); + if (cj->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(cj, e); + + /* Interact! */ + if (do_i && do_j) + runner_dopair_grav_mm_symmetric(r, ci, cj); + else if (do_i) + runner_dopair_grav_mm_nonsym(r, ci, cj); + else if (do_j) + runner_dopair_grav_mm_nonsym(r, cj, ci); +} + +/** + * @brief Computes all the M-M interactions between all the well-separated (at + * rebuild) pairs of progenies of the two cells. + * + * @param r The #runner thread. + * @param flags The task flag containing the list of well-separated pairs as a + * bit-field. + * @param ci The first #cell. + * @param cj The second #cell. + */ +void runner_dopair_grav_mm_progenies(struct runner *r, const long long flags, + struct cell *restrict ci, + struct cell *restrict cj) { + + /* Loop over all pairs of progenies */ + for (int i = 0; i < 8; i++) { + if (ci->progeny[i] != NULL) { + for (int j = 0; j < 8; j++) { + if (cj->progeny[j] != NULL) { + + struct cell *cpi = ci->progeny[i]; + struct cell *cpj = cj->progeny[j]; + + const int flag = i * 8 + j; + + /* Did we agree to use an M-M interaction here at the last rebuild? */ + if (flags & (1ULL << flag)) runner_dopair_grav_mm(r, cpi, cpj); + } + } + } + } +} + +static INLINE void runner_dopair_recursive_grav_pm(struct runner *r, + struct cell *ci, + const struct cell *cj) { + /* Some constants */ + const struct engine *e = r->e; + const int periodic = e->mesh->periodic; + const float dim[3] = {(float)e->mesh->dim[0], (float)e->mesh->dim[1], + (float)e->mesh->dim[2]}; + const float r_s_inv = e->mesh->r_s_inv; + + /* Anything to do here? */ + if (!(cell_is_active_gravity(ci, e) && ci->nodeID == e->nodeID)) return; + +#ifdef SWIFT_DEBUG_CHECKS + /* Early abort? */ + if (ci->grav.count == 0 || cj->grav.count == 0) + error("Doing pair gravity on an empty cell !"); + + /* Sanity check */ + if (ci == cj) error("Pair interaction between a cell and itself."); + + if (cj->grav.ti_old_multipole != e->ti_current) + error("cj->grav.multipole not drifted."); +#endif + + /* Can we recurse further? */ + if (ci->split) { + + /* Loop over ci's children */ + for (int k = 0; k < 8; k++) { + if (ci->progeny[k] != NULL) + runner_dopair_recursive_grav_pm(r, ci->progeny[k], cj); + } + + /* Ok, let's do the interaction here */ + } else { + + /* Start by constructing particle caches */ + + /* Cache to play with */ + struct gravity_cache *const ci_cache = &r->ci_gravity_cache; + + /* Computed the padded counts */ + const int gcount_i = ci->grav.count; + const int gcount_padded_i = gcount_i - (gcount_i % VEC_SIZE) + VEC_SIZE; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that we fit in cache */ + if (gcount_i > ci_cache->count) + error("Not enough space in the cache! gcount_i=%d", gcount_i); +#endif + + /* Recover the multipole info and the CoM locations */ + const struct multipole *multi_j = &cj->grav.multipole->m_pole; + const float r_max = cj->grav.multipole->r_max; + const float CoM_j[3] = {(float)(cj->grav.multipole->CoM[0]), + (float)(cj->grav.multipole->CoM[1]), + (float)(cj->grav.multipole->CoM[2])}; + + /* Fill the cache */ + gravity_cache_populate_all_mpole( + e->max_active_bin, periodic, dim, ci_cache, ci->grav.parts, gcount_i, + gcount_padded_i, ci, CoM_j, r_max * r_max, e->gravity_properties); + + /* Can we use the Newtonian version or do we need the truncated one ? */ + if (!periodic) { + + runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j, + periodic, dim, e, ci->grav.parts, gcount_i, + cj); + + } else { + + runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j, multi_j, + dim, r_s_inv, e, ci->grav.parts, gcount_i, + cj); + } + + /* Write back to the particles */ + gravity_cache_write_back(ci_cache, ci->grav.parts, gcount_i); + } +} + +/** + * @brief Computes the interaction of all the particles in a cell with all the + * particles of another cell. + * + * This function will try to recurse as far down the tree as possible and only + * default to direct summation if there is no better option. + * + * If using periodic BCs, we will abort the recursion if th distance between the + * cells is larger than the set threshold. + * + * @param r The #runner. + * @param ci The first #cell. + * @param cj The other #cell. + * @param gettimer Are we timing this ? + */ +void runner_dopair_recursive_grav(struct runner *r, struct cell *ci, + struct cell *cj, int gettimer) { + + /* Some constants */ + const struct engine *e = r->e; + const int nodeID = e->nodeID; + const int periodic = e->mesh->periodic; + const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; + const double theta_crit2 = e->gravity_properties->theta_crit2; + const double max_distance = e->mesh->r_cut_max; + + /* Anything to do here? */ + if (!((cell_is_active_gravity(ci, e) && ci->nodeID == nodeID) || + (cell_is_active_gravity(cj, e) && cj->nodeID == nodeID))) + return; + +#ifdef SWIFT_DEBUG_CHECKS + + const int gcount_i = ci->grav.count; + const int gcount_j = cj->grav.count; + + /* Early abort? */ + if (gcount_i == 0 || gcount_j == 0) + error("Doing pair gravity on an empty cell !"); + + /* Sanity check */ + if (ci == cj) error("Pair interaction between a cell and itself."); + + if (cell_is_active_gravity(ci, e) && + ci->grav.ti_old_multipole != e->ti_current) + error("ci->grav.multipole not drifted."); + if (cell_is_active_gravity(cj, e) && + cj->grav.ti_old_multipole != e->ti_current) + error("cj->grav.multipole not drifted."); +#endif + + TIMER_TIC; + + /* Recover the multipole information */ + struct gravity_tensors *const multi_i = ci->grav.multipole; + struct gravity_tensors *const multi_j = cj->grav.multipole; + + /* Get the distance between the CoMs */ + double dx = multi_i->CoM[0] - multi_j->CoM[0]; + double dy = multi_i->CoM[1] - multi_j->CoM[1]; + double dz = multi_i->CoM[2] - multi_j->CoM[2]; + + /* Apply BC */ + if (periodic) { + dx = nearest(dx, dim[0]); + dy = nearest(dy, dim[1]); + dz = nearest(dz, dim[2]); + } + const double r2 = dx * dx + dy * dy + dz * dz; + + /* Minimal distance between any 2 particles in the two cells */ + const double r_lr_check = sqrt(r2) - (multi_i->r_max + multi_j->r_max); + + /* Are we beyond the distance where the truncated forces are 0? */ + if (periodic && r_lr_check > max_distance) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Need to account for the interactions we missed */ + if (cell_is_active_gravity(ci, e)) + multi_i->pot.num_interacted += multi_j->m_pole.num_gpart; + if (cell_is_active_gravity(cj, e)) + multi_j->pot.num_interacted += multi_i->m_pole.num_gpart; +#endif + return; + } + + /* OK, we actually need to compute this pair. Let's find the cheapest + * option... */ + + /* Can we use M-M interactions ? */ + if (gravity_M2L_accept(multi_i->r_max, multi_j->r_max, theta_crit2, r2, + multi_i->m_pole.max_softening, + multi_j->m_pole.max_softening)) { + + /* Go M-M */ + runner_dopair_grav_mm(r, ci, cj); + + } else if (!ci->split && !cj->split) { + + /* We have two leaves. Go P-P. */ + runner_dopair_grav_pp(r, ci, cj, /*symmetric*/ 1, /*allow_mpoles*/ 1); + + } else { + + /* Alright, we'll have to split and recurse. */ + /* We know at least one of ci and cj is splittable */ + + const double ri_max = multi_i->r_max; + const double rj_max = multi_j->r_max; + + /* Split the larger of the two cells and start over again */ + if (ri_max > rj_max) { + + /* Can we actually split that interaction ? */ + if (ci->split) { + + /* Loop over ci's children */ + for (int k = 0; k < 8; k++) { + if (ci->progeny[k] != NULL) + runner_dopair_recursive_grav(r, ci->progeny[k], cj, 0); + } + + } else { + /* cj is split */ + + /* MATTHIEU: This could maybe be replaced by P-M interactions ? */ + + /* Loop over cj's children */ + for (int k = 0; k < 8; k++) { + if (cj->progeny[k] != NULL) + runner_dopair_recursive_grav(r, ci, cj->progeny[k], 0); + } + } + } else { + + /* Can we actually split that interaction ? */ + if (cj->split) { + + /* Loop over cj's children */ + for (int k = 0; k < 8; k++) { + if (cj->progeny[k] != NULL) + runner_dopair_recursive_grav(r, ci, cj->progeny[k], 0); + } + + } else { + /* ci is split */ + + /* MATTHIEU: This could maybe be replaced by P-M interactions ? */ + + /* Loop over ci's children */ + for (int k = 0; k < 8; k++) { + if (ci->progeny[k] != NULL) + runner_dopair_recursive_grav(r, ci->progeny[k], cj, 0); + } + } + } + } + + if (gettimer) TIMER_TOC(timer_dosub_pair_grav); +} + +/** + * @brief Computes the interaction of all the particles in a cell. + * + * This function will try to recurse as far down the tree as possible and only + * default to direct summation if there is no better option. + * + * @param r The #runner. + * @param c The first #cell. + * @param gettimer Are we timing this ? + */ +void runner_doself_recursive_grav(struct runner *r, struct cell *c, + int gettimer) { + + /* Some constants */ + const struct engine *e = r->e; + +#ifdef SWIFT_DEBUG_CHECKS + /* Early abort? */ + if (c->grav.count == 0) error("Doing self gravity on an empty cell !"); +#endif + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_gravity(c, e)) return; + + /* If the cell is split, interact each progeny with itself, and with + each of its siblings. */ + if (c->split) { + + for (int j = 0; j < 8; j++) { + if (c->progeny[j] != NULL) { + + runner_doself_recursive_grav(r, c->progeny[j], 0); + + for (int k = j + 1; k < 8; k++) { + if (c->progeny[k] != NULL) { + + runner_dopair_recursive_grav(r, c->progeny[j], c->progeny[k], 0); + } + } + } + } + } + + /* If the cell is not split, then just go for it... */ + else { + + runner_doself_grav_pp(r, c); + } + + if (gettimer) TIMER_TOC(timer_dosub_self_grav); +} + +/** + * @brief Performs all M-M interactions between a given top-level cell and all + * the other top-levels that are far enough. + * + * @param r The thread #runner. + * @param ci The #cell of interest. + * @param timer Are we timing this ? + */ +void runner_do_grav_long_range(struct runner *r, struct cell *ci, int timer) { + + /* Some constants */ + const struct engine *e = r->e; + const int periodic = e->mesh->periodic; + const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; + const double theta_crit2 = e->gravity_properties->theta_crit2; + const double max_distance2 = e->mesh->r_cut_max * e->mesh->r_cut_max; + + TIMER_TIC; + + /* Recover the list of top-level cells */ + struct cell *cells = e->s->cells_top; + int *cells_with_particles = e->s->cells_with_particles_top; + const int nr_cells_with_particles = e->s->nr_cells_with_particles; + + /* Anything to do here? */ + if (!cell_is_active_gravity(ci, e)) return; + + if (ci->nodeID != engine_rank) + error("Non-local cell in long-range gravity task!"); + + /* Check multipole has been drifted */ + if (ci->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e); + + /* Get this cell's multipole information */ + struct gravity_tensors *const multi_i = ci->grav.multipole; + + /* Find this cell's top-level (great-)parent */ + struct cell *top = ci; + while (top->parent != NULL) top = top->parent; + + /* Recover the top-level multipole (for distance checks) */ + struct gravity_tensors *const multi_top = top->grav.multipole; + const double CoM_rebuild_top[3] = {multi_top->CoM_rebuild[0], + multi_top->CoM_rebuild[1], + multi_top->CoM_rebuild[2]}; + + /* Loop over all the top-level cells and go for a M-M interaction if + * well-separated */ + for (int n = 0; n < nr_cells_with_particles; ++n) { + + /* Handle on the top-level cell and it's gravity business*/ + const struct cell *cj = &cells[cells_with_particles[n]]; + const struct gravity_tensors *const multi_j = cj->grav.multipole; + + /* Avoid self contributions */ + if (top == cj) continue; + + /* Skip empty cells */ + if (multi_j->m_pole.M_000 == 0.f) continue; + + /* Can we escape early in the periodic BC case? */ + if (periodic) { + + /* Minimal distance between any pair of particles */ + const double min_radius2 = + cell_min_dist2_same_size(top, cj, periodic, dim); + + /* Are we beyond the distance where the truncated forces are 0 ?*/ + if (min_radius2 > max_distance2) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Need to account for the interactions we missed */ + multi_i->pot.num_interacted += multi_j->m_pole.num_gpart; +#endif + + /* Record that this multipole received a contribution */ + multi_i->pot.interacted = 1; + + /* We are done here. */ + continue; + } + } + + /* Get the distance between the CoMs at the last rebuild*/ + double dx_r = CoM_rebuild_top[0] - multi_j->CoM_rebuild[0]; + double dy_r = CoM_rebuild_top[1] - multi_j->CoM_rebuild[1]; + double dz_r = CoM_rebuild_top[2] - multi_j->CoM_rebuild[2]; + + /* Apply BC */ + if (periodic) { + dx_r = nearest(dx_r, dim[0]); + dy_r = nearest(dy_r, dim[1]); + dz_r = nearest(dz_r, dim[2]); + } + const double r2_rebuild = dx_r * dx_r + dy_r * dy_r + dz_r * dz_r; + + /* Are we in charge of this cell pair? */ + if (gravity_M2L_accept(multi_top->r_max_rebuild, multi_j->r_max_rebuild, + theta_crit2, r2_rebuild, + multi_top->m_pole.max_softening, + multi_j->m_pole.max_softening)) { + + /* Call the PM interaction fucntion on the active sub-cells of ci */ + runner_dopair_grav_mm_nonsym(r, ci, cj); + // runner_dopair_recursive_grav_pm(r, ci, cj); + + /* Record that this multipole received a contribution */ + multi_i->pot.interacted = 1; + + } /* We are in charge of this pair */ + } /* Loop over top-level cells */ + + if (timer) TIMER_TOC(timer_dograv_long_range); +} diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h index c6885746a29fd7b6bd828496316f8dad01c1b7da..34f3e9ec147574357620cc8f485889b87880f06e 100644 --- a/src/runner_doiact_grav.h +++ b/src/runner_doiact_grav.h @@ -20,1796 +20,30 @@ #ifndef SWIFT_RUNNER_DOIACT_GRAV_H #define SWIFT_RUNNER_DOIACT_GRAV_H -/* Includes. */ -#include "active.h" -#include "cell.h" -#include "gravity.h" -#include "gravity_cache.h" -#include "gravity_iact.h" -#include "inline.h" -#include "part.h" -#include "space_getsid.h" -#include "timers.h" +#include "../config.h" -/** - * @brief Recursively propagate the multipoles down the tree by applying the - * L2L and L2P kernels. - * - * @param r The #runner. - * @param c The #cell we are working on. - * @param timer Are we timing this ? - */ -static INLINE void runner_do_grav_down(struct runner *r, struct cell *c, - int timer) { - - /* Some constants */ - const struct engine *e = r->e; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->grav.ti_old_multipole != e->ti_current) - error("c->multipole not drifted."); - if (c->grav.multipole->pot.ti_init != e->ti_current) - error("c->field tensor not initialised"); -#endif - - if (c->split) { - - /* Node case */ - - /* Add the field-tensor to all the 8 progenitors */ - for (int k = 0; k < 8; ++k) { - struct cell *cp = c->progeny[k]; - - /* Do we have a progenitor with any active g-particles ? */ - if (cp != NULL && cell_is_active_gravity(cp, e)) { - -#ifdef SWIFT_DEBUG_CHECKS - if (cp->grav.ti_old_multipole != e->ti_current) - error("cp->multipole not drifted."); - if (cp->grav.multipole->pot.ti_init != e->ti_current) - error("cp->field tensor not initialised"); -#endif - /* If the tensor received any contribution, push it down */ - if (c->grav.multipole->pot.interacted) { - - struct grav_tensor shifted_tensor; - - /* Shift the field tensor */ - gravity_L2L(&shifted_tensor, &c->grav.multipole->pot, - cp->grav.multipole->CoM, c->grav.multipole->CoM); - - /* Add it to this level's tensor */ - gravity_field_tensors_add(&cp->grav.multipole->pot, &shifted_tensor); - } - - /* Recurse */ - runner_do_grav_down(r, cp, 0); - } - } - - } else { - - /* Leaf case */ - - /* We can abort early if no interactions via multipole happened */ - if (!c->grav.multipole->pot.interacted) return; - - if (!cell_are_gpart_drifted(c, e)) error("Un-drifted gparts"); - - /* Cell properties */ - struct gpart *gparts = c->grav.parts; - const int gcount = c->grav.count; - const struct grav_tensor *pot = &c->grav.multipole->pot; - const double CoM[3] = {c->grav.multipole->CoM[0], c->grav.multipole->CoM[1], - c->grav.multipole->CoM[2]}; - - /* Apply accelerations to the particles */ - for (int i = 0; i < gcount; ++i) { - - /* Get a handle on the gpart */ - struct gpart *gp = &gparts[i]; - - /* Update if active */ - if (gpart_is_active(gp, e)) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that particles have been drifted to the current time */ - if (gp->ti_drift != e->ti_current) - error("gpart not drifted to current time"); - if (c->grav.multipole->pot.ti_init != e->ti_current) - error("c->field tensor not initialised"); - - /* Check that we are not updated an inhibited particle */ - if (gpart_is_inhibited(gp, e)) error("Updating an inhibited particle!"); - - /* Check that the particle was initialised */ - if (gp->initialised == 0) - error("Adding forces to an un-initialised gpart."); -#endif - /* Apply the kernel */ - gravity_L2P(pot, CoM, gp); - } - } - } - - if (timer) TIMER_TOC(timer_dograv_down); -} - -/** - * @brief Compute the non-truncated gravity interactions between all particles - * of a cell and the particles of the other cell. - * - * The calculation is performed non-symmetrically using the pre-filled - * #gravity_cache structures. The loop over the j cache should auto-vectorize. - * - * @param ci_cache #gravity_cache contaning the particles to be updated. - * @param cj_cache #gravity_cache contaning the source particles. - * @param gcount_i The number of particles in the cell i. - * @param gcount_padded_j The number of particles in the cell j padded to the - * vector length. - * @param periodic Is the calculation using periodic BCs ? - * @param dim The size of the simulation volume. - * - * @param e The #engine (for debugging checks only). - * @param gparts_i The #gpart in cell i (for debugging checks only). - * @param gparts_j The #gpart in cell j (for debugging checks only). - * @param gcount_j The number of particles in the cell j (for debugging checks - * only). - */ -static INLINE void runner_dopair_grav_pp_full( - struct gravity_cache *restrict ci_cache, - struct gravity_cache *restrict cj_cache, const int gcount_i, - const int gcount_j, const int gcount_padded_j, const int periodic, - const float dim[3], const struct engine *restrict e, - struct gpart *restrict gparts_i, const struct gpart *restrict gparts_j) { - - /* Loop over all particles in ci... */ - for (int pid = 0; pid < gcount_i; pid++) { - - /* Skip inactive particles */ - if (!ci_cache->active[pid]) continue; - - /* Skip particle that can use the multipole */ - if (ci_cache->use_mpole[pid]) continue; - -#ifdef SWIFT_DEBUG_CHECKS - if (!gpart_is_active(&gparts_i[pid], e)) - error("Inactive particle went through the cache"); -#endif - - const float x_i = ci_cache->x[pid]; - const float y_i = ci_cache->y[pid]; - const float z_i = ci_cache->z[pid]; - - /* Some powers of the softening length */ - const float h_i = ci_cache->epsilon[pid]; - const float h2_i = h_i * h_i; - const float h_inv_i = 1.f / h_i; - const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; - - /* Local accumulators for the acceleration and potential */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; - - /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, cj_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, cj_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, cj_cache->m, SWIFT_CACHE_ALIGNMENT); - swift_assume_size(gcount_padded_j, VEC_SIZE); - - /* Loop over every particle in the other cell. */ - for (int pjd = 0; pjd < gcount_padded_j; pjd++) { - - /* Get info about j */ - const float x_j = cj_cache->x[pjd]; - const float y_j = cj_cache->y[pjd]; - const float z_j = cj_cache->z[pjd]; - const float mass_j = cj_cache->m[pjd]; - - /* Compute the pairwise distance. */ - float dx = x_j - x_i; - float dy = y_j - y_i; - float dz = z_j - z_i; - - /* Correct for periodic BCs */ - if (periodic) { - dx = nearestf(dx, dim[0]); - dy = nearestf(dy, dim[1]); - dz = nearestf(dz, dim[2]); - } - - const float r2 = dx * dx + dy * dy + dz * dz; - -#ifdef SWIFT_DEBUG_CHECKS - if (r2 == 0.f && h2_i == 0.) - error("Interacting particles with 0 distance and 0 softening."); - - /* Check that particles have been drifted to the current time */ - if (gparts_i[pid].ti_drift != e->ti_current) - error("gpi not drifted to current time"); - if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current && - !gpart_is_inhibited(&gparts_j[pjd], e)) - error("gpj not drifted to current time"); - - /* Check that we are not updated an inhibited particle */ - if (gpart_is_inhibited(&gparts_i[pid], e)) - error("Updating an inhibited particle!"); - - /* Check that the particle we interact with was not inhibited */ - if (pjd < gcount_j && gpart_is_inhibited(&gparts_j[pjd], e) && - mass_j != 0.f) - error("Inhibited particle used as gravity source."); - - /* Check that the particle was initialised */ - if (gparts_i[pid].initialised == 0) - error("Adding forces to an un-initialised gpart."); -#endif - - /* Interact! */ - float f_ij, pot_ij; - runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij, - &pot_ij); - - /* Store it back */ - a_x += f_ij * dx; - a_y += f_ij * dy; - a_z += f_ij * dz; - pot += pot_ij; - -#ifdef SWIFT_DEBUG_CHECKS - /* Update the interaction counter if it's not a padded gpart */ - if (pjd < gcount_j && !gpart_is_inhibited(&gparts_j[pjd], e)) - gparts_i[pid].num_interacted++; -#endif - } - - /* Store everything back in cache */ - ci_cache->a_x[pid] += a_x; - ci_cache->a_y[pid] += a_y; - ci_cache->a_z[pid] += a_z; - ci_cache->pot[pid] += pot; - } -} - -/** - * @brief Compute the truncated gravity interactions between all particles - * of a cell and the particles of the other cell. - * - * The calculation is performed non-symmetrically using the pre-filled - * #gravity_cache structures. The loop over the j cache should auto-vectorize. - * - * This function only makes sense in periodic BCs. - * - * @param ci_cache #gravity_cache contaning the particles to be updated. - * @param cj_cache #gravity_cache contaning the source particles. - * @param gcount_i The number of particles in the cell i. - * @param gcount_padded_j The number of particles in the cell j padded to the - * vector length. - * @param dim The size of the simulation volume. - * @param r_s_inv The inverse of the gravity-mesh smoothing-scale. - * - * @param e The #engine (for debugging checks only). - * @param gparts_i The #gpart in cell i (for debugging checks only). - * @param gparts_j The #gpart in cell j (for debugging checks only). - * @param gcount_j The number of particles in the cell j (for debugging checks - * only). - */ -static INLINE void runner_dopair_grav_pp_truncated( - struct gravity_cache *restrict ci_cache, - struct gravity_cache *restrict cj_cache, const int gcount_i, - const int gcount_j, const int gcount_padded_j, const float dim[3], - const float r_s_inv, const struct engine *restrict e, - struct gpart *restrict gparts_i, const struct gpart *restrict gparts_j) { - -#ifdef SWIFT_DEBUG_CHECKS - if (!e->s->periodic) - error("Calling truncated PP function in non-periodic setup."); -#endif - - /* Loop over all particles in ci... */ - for (int pid = 0; pid < gcount_i; pid++) { - - /* Skip inactive particles */ - if (!ci_cache->active[pid]) continue; - - /* Skip particle that can use the multipole */ - if (ci_cache->use_mpole[pid]) continue; - -#ifdef SWIFT_DEBUG_CHECKS - if (!gpart_is_active(&gparts_i[pid], e)) - error("Inactive particle went through the cache"); -#endif - - const float x_i = ci_cache->x[pid]; - const float y_i = ci_cache->y[pid]; - const float z_i = ci_cache->z[pid]; - - /* Some powers of the softening length */ - const float h_i = ci_cache->epsilon[pid]; - const float h2_i = h_i * h_i; - const float h_inv_i = 1.f / h_i; - const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; - - /* Local accumulators for the acceleration and potential */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; - - /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, cj_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, cj_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, cj_cache->m, SWIFT_CACHE_ALIGNMENT); - swift_assume_size(gcount_padded_j, VEC_SIZE); - - /* Loop over every particle in the other cell. */ - for (int pjd = 0; pjd < gcount_padded_j; pjd++) { - - /* Get info about j */ - const float x_j = cj_cache->x[pjd]; - const float y_j = cj_cache->y[pjd]; - const float z_j = cj_cache->z[pjd]; - const float mass_j = cj_cache->m[pjd]; - - /* Compute the pairwise distance. */ - float dx = x_j - x_i; - float dy = y_j - y_i; - float dz = z_j - z_i; - - /* Correct for periodic BCs */ - dx = nearestf(dx, dim[0]); - dy = nearestf(dy, dim[1]); - dz = nearestf(dz, dim[2]); - - const float r2 = dx * dx + dy * dy + dz * dz; - -#ifdef SWIFT_DEBUG_CHECKS - if (r2 == 0.f && h2_i == 0.) - error("Interacting particles with 0 distance and 0 softening."); - - /* Check that particles have been drifted to the current time */ - if (gparts_i[pid].ti_drift != e->ti_current) - error("gpi not drifted to current time"); - if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current && - !gpart_is_inhibited(&gparts_j[pjd], e)) - error("gpj not drifted to current time"); - - /* Check that we are not updated an inhibited particle */ - if (gpart_is_inhibited(&gparts_i[pid], e)) - error("Updating an inhibited particle!"); - - /* Check that the particle we interact with was not inhibited */ - if (pjd < gcount_j && gpart_is_inhibited(&gparts_j[pjd], e) && - mass_j != 0.f) - error("Inhibited particle used as gravity source."); - - /* Check that the particle was initialised */ - if (gparts_i[pid].initialised == 0) - error("Adding forces to an un-initialised gpart."); -#endif - - /* Interact! */ - float f_ij, pot_ij; - runner_iact_grav_pp_truncated(r2, h2_i, h_inv_i, h_inv3_i, mass_j, - r_s_inv, &f_ij, &pot_ij); - - /* Store it back */ - a_x += f_ij * dx; - a_y += f_ij * dy; - a_z += f_ij * dz; - pot += pot_ij; - -#ifdef SWIFT_DEBUG_CHECKS - /* Update the interaction counter if it's not a padded gpart */ - if (pjd < gcount_j && !gpart_is_inhibited(&gparts_j[pjd], e)) - gparts_i[pid].num_interacted++; -#endif - } - - /* Store everything back in cache */ - ci_cache->a_x[pid] += a_x; - ci_cache->a_y[pid] += a_y; - ci_cache->a_z[pid] += a_z; - ci_cache->pot[pid] += pot; - } -} - -/** - * @brief Compute the gravity interactions between all particles - * of a cell and the multipole of the other cell. - * - * The calculation is performedusing the pre-filled - * #gravity_cache structure. The loop over the i cache should auto-vectorize. - * - * @param ci_cache #gravity_cache contaning the particles to be updated. - * @param gcount_padded_i The number of particles in the cell i padded to the - * vector length. - * @param CoM_j Position of the #multipole in #cell j. - * @param multi_j The #multipole in #cell j. - * @param periodic Is the calculation using periodic BCs ? - * @param dim The size of the simulation volume. - * - * @param e The #engine (for debugging checks only). - * @param gparts_i The #gpart in cell i (for debugging checks only). - * @param gcount_i The number of particles in the cell i (for debugging checks - * only). - * @param cj The #cell j (for debugging checks only). - */ -static INLINE void runner_dopair_grav_pm_full( - struct gravity_cache *ci_cache, const int gcount_padded_i, - const float CoM_j[3], const struct multipole *restrict multi_j, - const int periodic, const float dim[3], const struct engine *restrict e, - struct gpart *restrict gparts_i, const int gcount_i, - const struct cell *restrict cj) { - - /* Make the compiler understand we are in happy vectorization land */ - swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, y, ci_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, z, ci_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, epsilon, ci_cache->epsilon, - SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, a_x, ci_cache->a_x, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, a_y, ci_cache->a_y, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, a_z, ci_cache->a_z, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, pot, ci_cache->pot, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(int, active, ci_cache->active, - SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(int, use_mpole, ci_cache->use_mpole, - SWIFT_CACHE_ALIGNMENT); - swift_assume_size(gcount_padded_i, VEC_SIZE); - - /* Loop over all particles in ci... */ - for (int pid = 0; pid < gcount_padded_i; pid++) { - - /* Skip inactive particles */ - if (!active[pid]) continue; - - /* Skip particle that cannot use the multipole */ - if (!use_mpole[pid]) continue; - -#ifdef SWIFT_DEBUG_CHECKS - if (pid < gcount_i && !gpart_is_active(&gparts_i[pid], e)) - error("Active particle went through the cache"); - - /* Check that particles have been drifted to the current time */ - if (gparts_i[pid].ti_drift != e->ti_current) - error("gpi not drifted to current time"); - - /* Check that we are not updated an inhibited particle */ - if (gpart_is_inhibited(&gparts_i[pid], e)) - error("Updating an inhibited particle!"); - - /* Check that the particle was initialised */ - if (gparts_i[pid].initialised == 0) - error("Adding forces to an un-initialised gpart."); - - if (pid >= gcount_i) error("Adding forces to padded particle"); -#endif - - const float x_i = x[pid]; - const float y_i = y[pid]; - const float z_i = z[pid]; - - /* Some powers of the softening length */ - const float h_i = epsilon[pid]; - const float h_inv_i = 1.f / h_i; - - /* Distance to the Multipole */ - float dx = CoM_j[0] - x_i; - float dy = CoM_j[1] - y_i; - float dz = CoM_j[2] - z_i; - - /* Apply periodic BCs? */ - if (periodic) { - dx = nearestf(dx, dim[0]); - dy = nearestf(dy, dim[1]); - dz = nearestf(dz, dim[2]); - } - - const float r2 = dx * dx + dy * dy + dz * dz; - -#ifdef SWIFT_DEBUG_CHECKS - const float r_max_j = cj->grav.multipole->r_max; - const float r_max2 = r_max_j * r_max_j; - const float theta_crit2 = e->gravity_properties->theta_crit2; - - /* Note: 1.1 to avoid FP rounding false-positives */ - if (!gravity_M2P_accept(r_max2, theta_crit2 * 1.1, r2)) - error( - "use_mpole[i] set when M2P accept fails CoM=[%e %e %e] pos=[%e %e " - "%e], rmax=%e", - CoM_j[0], CoM_j[1], CoM_j[2], x_i, y_i, z_i, r_max_j); -#endif - - /* Interact! */ - float f_x, f_y, f_z, pot_ij; - runner_iact_grav_pm_full(dx, dy, dz, r2, h_i, h_inv_i, multi_j, &f_x, &f_y, - &f_z, &pot_ij); - - /* Store it back */ - a_x[pid] += f_x; - a_y[pid] += f_y; - a_z[pid] += f_z; - pot[pid] += pot_ij; - -#ifdef SWIFT_DEBUG_CHECKS - /* Update the interaction counter */ - if (pid < gcount_i) - gparts_i[pid].num_interacted += cj->grav.multipole->m_pole.num_gpart; -#endif - } -} - -/** - * @brief Compute the gravity interactions between all particles - * of a cell and the multipole of the other cell. - * - * The calculation is performedusing the pre-filled - * #gravity_cache structure. The loop over the i cache should auto-vectorize. - * - * This function only makes sense in periodic BCs. - * - * @param ci_cache #gravity_cache contaning the particles to be updated. - * @param gcount_padded_i The number of particles in the cell i padded to the - * vector length. - * @param CoM_j Position of the #multipole in #cell j. - * @param multi_j The #multipole in #cell j. - * @param dim The size of the simulation volume. - * @param r_s_inv The inverse of the gravity-mesh smoothing-scale. - * - * @param e The #engine (for debugging checks only). - * @param gparts_i The #gpart in cell i (for debugging checks only). - * @param gcount_i The number of particles in the cell i (for debugging checks - * only). - * @param cj The #cell j (for debugging checks only). - */ -static INLINE void runner_dopair_grav_pm_truncated( - struct gravity_cache *ci_cache, const int gcount_padded_i, - const float CoM_j[3], const struct multipole *restrict multi_j, - const float dim[3], const float r_s_inv, const struct engine *restrict e, - struct gpart *restrict gparts_i, const int gcount_i, - const struct cell *restrict cj) { - -#ifdef SWIFT_DEBUG_CHECKS - if (!e->s->periodic) - error("Calling truncated PP function in non-periodic setup."); -#endif - - /* Make the compiler understand we are in happy vectorization land */ - swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, y, ci_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, z, ci_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, epsilon, ci_cache->epsilon, - SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, a_x, ci_cache->a_x, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, a_y, ci_cache->a_y, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, a_z, ci_cache->a_z, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, pot, ci_cache->pot, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(int, active, ci_cache->active, - SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(int, use_mpole, ci_cache->use_mpole, - SWIFT_CACHE_ALIGNMENT); - swift_assume_size(gcount_padded_i, VEC_SIZE); - - /* Loop over all particles in ci... */ - for (int pid = 0; pid < gcount_padded_i; pid++) { - - /* Skip inactive particles */ - if (!active[pid]) continue; - - /* Skip particle that cannot use the multipole */ - if (!use_mpole[pid]) continue; - -#ifdef SWIFT_DEBUG_CHECKS - if (pid < gcount_i && !gpart_is_active(&gparts_i[pid], e)) - error("Active particle went through the cache"); - - /* Check that particles have been drifted to the current time */ - if (gparts_i[pid].ti_drift != e->ti_current) - error("gpi not drifted to current time"); - - /* Check that we are not updated an inhibited particle */ - if (gpart_is_inhibited(&gparts_i[pid], e)) - error("Updating an inhibited particle!"); - - /* Check that the particle was initialised */ - if (gparts_i[pid].initialised == 0) - error("Adding forces to an un-initialised gpart."); - - if (pid >= gcount_i) error("Adding forces to padded particle"); -#endif - - const float x_i = x[pid]; - const float y_i = y[pid]; - const float z_i = z[pid]; - - /* Some powers of the softening length */ - const float h_i = epsilon[pid]; - const float h_inv_i = 1.f / h_i; - - /* Distance to the Multipole */ - float dx = CoM_j[0] - x_i; - float dy = CoM_j[1] - y_i; - float dz = CoM_j[2] - z_i; - - /* Apply periodic BCs */ - dx = nearestf(dx, dim[0]); - dy = nearestf(dy, dim[1]); - dz = nearestf(dz, dim[2]); - - const float r2 = dx * dx + dy * dy + dz * dz; - -#ifdef SWIFT_DEBUG_CHECKS - const float r_max_j = cj->grav.multipole->r_max; - const float r_max2 = r_max_j * r_max_j; - const float theta_crit2 = e->gravity_properties->theta_crit2; - - /* 1.1 to avoid FP rounding false-positives */ - if (!gravity_M2P_accept(r_max2, theta_crit2 * 1.1, r2)) - error( - "use_mpole[i] set when M2P accept fails CoM=[%e %e %e] pos=[%e %e " - "%e], rmax=%e", - CoM_j[0], CoM_j[1], CoM_j[2], x_i, y_i, z_i, r_max_j); -#endif - - /* Interact! */ - float f_x, f_y, f_z, pot_ij; - runner_iact_grav_pm_truncated(dx, dy, dz, r2, h_i, h_inv_i, r_s_inv, - multi_j, &f_x, &f_y, &f_z, &pot_ij); - - /* Store it back */ - a_x[pid] += f_x; - a_y[pid] += f_y; - a_z[pid] += f_z; - pot[pid] += pot_ij; - -#ifdef SWIFT_DEBUG_CHECKS - /* Update the interaction counter */ - if (pid < gcount_i) - gparts_i[pid].num_interacted += cj->grav.multipole->m_pole.num_gpart; -#endif - } -} - -/** - * @brief Computes the interaction of all the particles in a cell with all the - * particles of another cell. - * - * This function switches between the full potential and the truncated one - * depending on needs. It will also use the M2P (multipole) interaction - * for the subset of particles in either cell for which the distance criterion - * is valid. - * - * This function starts by constructing the require #gravity_cache for both - * cells and then call the specialised functions doing the actual work on - * the caches. It then write the data back to the particles. - * - * @param r The #runner. - * @param ci The first #cell. - * @param cj The other #cell. - * @param symmetric Are we updating both cells (1) or just ci (0) ? - * @param allow_mpole Are we allowing the use of P2M interactions ? - */ -static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci, - struct cell *cj, const int symmetric, - const int allow_mpole) { - - /* Recover some useful constants */ - const struct engine *e = r->e; - const int periodic = e->mesh->periodic; - const float dim[3] = {(float)e->mesh->dim[0], (float)e->mesh->dim[1], - (float)e->mesh->dim[2]}; - const float r_s_inv = e->mesh->r_s_inv; - const double min_trunc = e->mesh->r_cut_min; - - TIMER_TIC; - - /* Record activity status */ - const int ci_active = - cell_is_active_gravity(ci, e) && (ci->nodeID == e->nodeID); - const int cj_active = - cell_is_active_gravity(cj, e) && (cj->nodeID == e->nodeID); - - /* Anything to do here? */ - if (!ci_active && !cj_active) return; - if (!ci_active && !symmetric) return; - - /* Check that we are not doing something stupid */ - if (ci->split || cj->split) error("Running P-P on splitable cells"); - - /* Let's start by checking things are drifted */ - if (!cell_are_gpart_drifted(ci, e)) error("Un-drifted gparts"); - if (!cell_are_gpart_drifted(cj, e)) error("Un-drifted gparts"); - if (cj_active && ci->grav.ti_old_multipole != e->ti_current) - error("Un-drifted multipole"); - if (ci_active && cj->grav.ti_old_multipole != e->ti_current) - error("Un-drifted multipole"); - - /* Caches to play with */ - struct gravity_cache *const ci_cache = &r->ci_gravity_cache; - struct gravity_cache *const cj_cache = &r->cj_gravity_cache; - - /* Shift to apply to the particles in each cell */ - const double shift_i[3] = {0., 0., 0.}; - const double shift_j[3] = {0., 0., 0.}; - - /* Recover the multipole info and shift the CoM locations */ - const float rmax_i = ci->grav.multipole->r_max; - const float rmax_j = cj->grav.multipole->r_max; - const float rmax2_i = rmax_i * rmax_i; - const float rmax2_j = rmax_j * rmax_j; - const struct multipole *multi_i = &ci->grav.multipole->m_pole; - const struct multipole *multi_j = &cj->grav.multipole->m_pole; - const float CoM_i[3] = {(float)(ci->grav.multipole->CoM[0] - shift_i[0]), - (float)(ci->grav.multipole->CoM[1] - shift_i[1]), - (float)(ci->grav.multipole->CoM[2] - shift_i[2])}; - const float CoM_j[3] = {(float)(cj->grav.multipole->CoM[0] - shift_j[0]), - (float)(cj->grav.multipole->CoM[1] - shift_j[1]), - (float)(cj->grav.multipole->CoM[2] - shift_j[2])}; - - /* Start by constructing particle caches */ - - /* Computed the padded counts */ - const int gcount_i = ci->grav.count; - const int gcount_j = cj->grav.count; - const int gcount_padded_i = gcount_i - (gcount_i % VEC_SIZE) + VEC_SIZE; - const int gcount_padded_j = gcount_j - (gcount_j % VEC_SIZE) + VEC_SIZE; - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that we fit in cache */ - if (gcount_i > ci_cache->count || gcount_j > cj_cache->count) - error("Not enough space in the caches! gcount_i=%d gcount_j=%d", gcount_i, - gcount_j); -#endif - - /* Fill the caches */ - gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim, - ci_cache, ci->grav.parts, gcount_i, gcount_padded_i, - shift_i, CoM_j, rmax2_j, ci, e->gravity_properties); - gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim, - cj_cache, cj->grav.parts, gcount_j, gcount_padded_j, - shift_j, CoM_i, rmax2_i, cj, e->gravity_properties); - - /* Can we use the Newtonian version or do we need the truncated one ? */ - if (!periodic) { - - /* Not periodic -> Can always use Newtonian potential */ - - /* Let's updated the active cell(s) only */ - if (ci_active) { - - /* First the P2P */ - runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j, - gcount_padded_j, periodic, dim, e, - ci->grav.parts, cj->grav.parts); - - /* Then the M2P */ - if (allow_mpole) - runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j, - periodic, dim, e, ci->grav.parts, gcount_i, - cj); - } - if (cj_active && symmetric) { - - /* First the P2P */ - runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i, - gcount_padded_i, periodic, dim, e, - cj->grav.parts, ci->grav.parts); - - /* Then the M2P */ - if (allow_mpole) - runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i, - periodic, dim, e, cj->grav.parts, gcount_j, - ci); - } - - } else { /* Periodic BC */ - - /* Get the relative distance between the CoMs */ - const double dx[3] = {CoM_j[0] - CoM_i[0], CoM_j[1] - CoM_i[1], - CoM_j[2] - CoM_i[2]}; - const double r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; - - /* Get the maximal distance between any two particles */ - const double max_r = sqrt(r2) + rmax_i + rmax_j; - - /* Do we need to use the truncated interactions ? */ - if (max_r > min_trunc) { - - /* Periodic but far-away cells must use the truncated potential */ - - /* Let's updated the active cell(s) only */ - if (ci_active) { - - /* First the (truncated) P2P */ - runner_dopair_grav_pp_truncated(ci_cache, cj_cache, gcount_i, gcount_j, - gcount_padded_j, dim, r_s_inv, e, - ci->grav.parts, cj->grav.parts); - - /* Then the M2P */ - if (allow_mpole) - runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j, - multi_j, dim, r_s_inv, e, - ci->grav.parts, gcount_i, cj); - } - if (cj_active && symmetric) { - - /* First the (truncated) P2P */ - runner_dopair_grav_pp_truncated(cj_cache, ci_cache, gcount_j, gcount_i, - gcount_padded_i, dim, r_s_inv, e, - cj->grav.parts, ci->grav.parts); - - /* Then the M2P */ - if (allow_mpole) - runner_dopair_grav_pm_truncated(cj_cache, gcount_padded_j, CoM_i, - multi_i, dim, r_s_inv, e, - cj->grav.parts, gcount_j, ci); - } - - } else { - - /* Periodic but close-by cells can use the full Newtonian potential */ - - /* Let's updated the active cell(s) only */ - if (ci_active) { - - /* First the (Newtonian) P2P */ - runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j, - gcount_padded_j, periodic, dim, e, - ci->grav.parts, cj->grav.parts); - - /* Then the M2P */ - if (allow_mpole) - runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j, - periodic, dim, e, ci->grav.parts, gcount_i, - cj); - } - if (cj_active && symmetric) { - - /* First the (Newtonian) P2P */ - runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i, - gcount_padded_i, periodic, dim, e, - cj->grav.parts, ci->grav.parts); - - /* Then the M2P */ - if (allow_mpole) - runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i, - periodic, dim, e, cj->grav.parts, gcount_j, - ci); - } - } - } - - /* Write back to the particles */ - if (ci_active) gravity_cache_write_back(ci_cache, ci->grav.parts, gcount_i); - if (cj_active && symmetric) - gravity_cache_write_back(cj_cache, cj->grav.parts, gcount_j); - - TIMER_TOC(timer_dopair_grav_pp); -} - -/** - * @brief Compute the non-truncated gravity interactions between all particles - * of a cell and the particles of the other cell. - * - * The calculation is performed non-symmetrically using the pre-filled - * #gravity_cache structures. The loop over the j cache should auto-vectorize. - * - * @param ci_cache #gravity_cache contaning the particles to be updated. - * @param gcount The number of particles in the cell. - * @param gcount_padded The number of particles in the cell padded to the - * vector length. - * - * @param e The #engine (for debugging checks only). - * @param gparts The #gpart in the cell (for debugging checks only). - */ -static INLINE void runner_doself_grav_pp_full( - struct gravity_cache *restrict ci_cache, const int gcount, - const int gcount_padded, const struct engine *e, struct gpart *gparts) { - - /* Loop over all particles in ci... */ - for (int pid = 0; pid < gcount; pid++) { - - /* Skip inactive particles */ - if (!ci_cache->active[pid]) continue; - - const float x_i = ci_cache->x[pid]; - const float y_i = ci_cache->y[pid]; - const float z_i = ci_cache->z[pid]; - - /* Some powers of the softening length */ - const float h_i = ci_cache->epsilon[pid]; - const float h2_i = h_i * h_i; - const float h_inv_i = 1.f / h_i; - const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; - - /* Local accumulators for the acceleration */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; - - /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, ci_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, ci_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, ci_cache->m, SWIFT_CACHE_ALIGNMENT); - swift_assume_size(gcount_padded, VEC_SIZE); - - /* Loop over every other particle in the cell. */ - for (int pjd = 0; pjd < gcount_padded; pjd++) { - - /* No self interaction */ - if (pid == pjd) continue; - - /* Get info about j */ - const float x_j = ci_cache->x[pjd]; - const float y_j = ci_cache->y[pjd]; - const float z_j = ci_cache->z[pjd]; - const float mass_j = ci_cache->m[pjd]; - - /* Compute the pairwise (square) distance. */ - /* Note: no need for periodic wrapping inside a cell */ - const float dx = x_j - x_i; - const float dy = y_j - y_i; - const float dz = z_j - z_i; - const float r2 = dx * dx + dy * dy + dz * dz; - -#ifdef SWIFT_DEBUG_CHECKS - if (r2 == 0.f && h2_i == 0.) - error("Interacting particles with 0 distance and 0 softening."); - - /* Check that particles have been drifted to the current time */ - if (gparts[pid].ti_drift != e->ti_current) - error("gpi not drifted to current time"); - if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current && - !gpart_is_inhibited(&gparts[pjd], e)) - error("gpj not drifted to current time"); - - /* Check that we are not updated an inhibited particle */ - if (gpart_is_inhibited(&gparts[pid], e)) - error("Updating an inhibited particle!"); - - /* Check that the particle we interact with was not inhibited */ - if (pjd < gcount && gpart_is_inhibited(&gparts[pjd], e) && mass_j != 0.f) - error("Inhibited particle used as gravity source."); - - /* Check that the particle was initialised */ - if (gparts[pid].initialised == 0) - error("Adding forces to an un-initialised gpart."); -#endif - - /* Interact! */ - float f_ij, pot_ij; - runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij, - &pot_ij); - - /* Store it back */ - a_x += f_ij * dx; - a_y += f_ij * dy; - a_z += f_ij * dz; - pot += pot_ij; - -#ifdef SWIFT_DEBUG_CHECKS - /* Update the interaction counter if it's not a padded gpart */ - if (pjd < gcount && !gpart_is_inhibited(&gparts[pjd], e)) - gparts[pid].num_interacted++; -#endif - } - - /* Store everything back in cache */ - ci_cache->a_x[pid] += a_x; - ci_cache->a_y[pid] += a_y; - ci_cache->a_z[pid] += a_z; - ci_cache->pot[pid] += pot; - } -} - -/** - * @brief Compute the truncated gravity interactions between all particles - * of a cell and the particles of the other cell. - * - * The calculation is performed non-symmetrically using the pre-filled - * #gravity_cache structures. The loop over the j cache should auto-vectorize. - * - * This function only makes sense in periodic BCs. - * - * @param ci_cache #gravity_cache contaning the particles to be updated. - * @param gcount The number of particles in the cell. - * @param gcount_padded The number of particles in the cell padded to the - * vector length. - * @param r_s_inv The inverse of the gravity-mesh smoothing-scale. - * - * @param e The #engine (for debugging checks only). - * @param gparts The #gpart in the cell (for debugging checks only). - */ -static INLINE void runner_doself_grav_pp_truncated( - struct gravity_cache *restrict ci_cache, const int gcount, - const int gcount_padded, const float r_s_inv, const struct engine *e, - struct gpart *gparts) { - -#ifdef SWIFT_DEBUG_CHECKS - if (!e->s->periodic) - error("Calling truncated PP function in non-periodic setup."); -#endif - - /* Loop over all particles in ci... */ - for (int pid = 0; pid < gcount; pid++) { - - /* Skip inactive particles */ - if (!ci_cache->active[pid]) continue; - - const float x_i = ci_cache->x[pid]; - const float y_i = ci_cache->y[pid]; - const float z_i = ci_cache->z[pid]; - - /* Some powers of the softening length */ - const float h_i = ci_cache->epsilon[pid]; - const float h2_i = h_i * h_i; - const float h_inv_i = 1.f / h_i; - const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; - - /* Local accumulators for the acceleration and potential */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; - - /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, ci_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, ci_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(float, ci_cache->m, SWIFT_CACHE_ALIGNMENT); - swift_assume_size(gcount_padded, VEC_SIZE); - - /* Loop over every other particle in the cell. */ - for (int pjd = 0; pjd < gcount_padded; pjd++) { - - /* No self interaction */ - if (pid == pjd) continue; - - /* Get info about j */ - const float x_j = ci_cache->x[pjd]; - const float y_j = ci_cache->y[pjd]; - const float z_j = ci_cache->z[pjd]; - const float mass_j = ci_cache->m[pjd]; - - /* Compute the pairwise (square) distance. */ - /* Note: no need for periodic wrapping inside a cell */ - const float dx = x_j - x_i; - const float dy = y_j - y_i; - const float dz = z_j - z_i; - - const float r2 = dx * dx + dy * dy + dz * dz; - -#ifdef SWIFT_DEBUG_CHECKS - if (r2 == 0.f && h2_i == 0.) - error("Interacting particles with 0 distance and 0 softening."); - - /* Check that particles have been drifted to the current time */ - if (gparts[pid].ti_drift != e->ti_current) - error("gpi not drifted to current time"); - if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current && - !gpart_is_inhibited(&gparts[pjd], e)) - error("gpj not drifted to current time"); - - /* Check that we are not updated an inhibited particle */ - if (gpart_is_inhibited(&gparts[pid], e)) - error("Updating an inhibited particle!"); - - /* Check that the particle we interact with was not inhibited */ - if (pjd < gcount && gpart_is_inhibited(&gparts[pjd], e) && mass_j != 0.f) - error("Inhibited particle used as gravity source."); - - /* Check that the particle was initialised */ - if (gparts[pid].initialised == 0) - error("Adding forces to an un-initialised gpart."); -#endif - - /* Interact! */ - float f_ij, pot_ij; - runner_iact_grav_pp_truncated(r2, h2_i, h_inv_i, h_inv3_i, mass_j, - r_s_inv, &f_ij, &pot_ij); - - /* Store it back */ - a_x += f_ij * dx; - a_y += f_ij * dy; - a_z += f_ij * dz; - pot += pot_ij; - -#ifdef SWIFT_DEBUG_CHECKS - /* Update the interaction counter if it's not a padded gpart */ - if (pjd < gcount && !gpart_is_inhibited(&gparts[pjd], e)) - gparts[pid].num_interacted++; -#endif - } - - /* Store everything back in cache */ - ci_cache->a_x[pid] += a_x; - ci_cache->a_y[pid] += a_y; - ci_cache->a_z[pid] += a_z; - ci_cache->pot[pid] += pot; - } -} - -/** - * @brief Computes the interaction of all the particles in a cell with all the - * other ones. - * - * This function switches between the full potential and the truncated one - * depending on needs. - * - * This function starts by constructing the require #gravity_cache for the - * cell and then call the specialised functions doing the actual work on - * the cache. It then write the data back to the particles. - * - * @param r The #runner. - * @param c The #cell. - */ -static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) { - - /* Recover some useful constants */ - const struct engine *e = r->e; - const int periodic = e->mesh->periodic; - const float r_s_inv = e->mesh->r_s_inv; - const double min_trunc = e->mesh->r_cut_min; - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->grav.count == 0) error("Doing self gravity on an empty cell !"); -#endif - - /* Anything to do here? */ - if (!cell_is_active_gravity(c, e)) return; - - /* Check that we are not doing something stupid */ - if (c->split) error("Running P-P on a splitable cell"); - - /* Do we need to start by drifting things ? */ - if (!cell_are_gpart_drifted(c, e)) error("Un-drifted gparts"); - - /* Start by constructing a cache for the particles */ - struct gravity_cache *const ci_cache = &r->ci_gravity_cache; - - /* Shift to apply to the particles in the cell */ - const double loc[3] = {c->loc[0] + 0.5 * c->width[0], - c->loc[1] + 0.5 * c->width[1], - c->loc[2] + 0.5 * c->width[2]}; - - /* Computed the padded counts */ - const int gcount = c->grav.count; - const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE; - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that we fit in cache */ - if (gcount > ci_cache->count) - error("Not enough space in the cache! gcount=%d", gcount); -#endif - - /* Fill the cache */ - gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, c->grav.parts, - gcount, gcount_padded, loc, c, - e->gravity_properties); - - /* Can we use the Newtonian version or do we need the truncated one ? */ - if (!periodic) { - - /* Not periodic -> Can always use Newtonian potential */ - runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, - c->grav.parts); - - } else { - - /* Get the maximal distance between any two particles */ - const double max_r = 2. * c->grav.multipole->r_max; - - /* Do we need to use the truncated interactions ? */ - if (max_r > min_trunc) { - - /* Periodic but far-away cells must use the truncated potential */ - runner_doself_grav_pp_truncated(ci_cache, gcount, gcount_padded, r_s_inv, - e, c->grav.parts); - - } else { - - /* Periodic but close-by cells can use the full Newtonian potential */ - runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, - c->grav.parts); - } - } - - /* Write back to the particles */ - gravity_cache_write_back(ci_cache, c->grav.parts, gcount); - - TIMER_TOC(timer_doself_grav_pp); -} - -/** - * @brief Computes the interaction of the field tensor and multipole - * of two cells symmetrically. - * - * @param r The #runner. - * @param ci The first #cell. - * @param cj The second #cell. - */ -static INLINE void runner_dopair_grav_mm_symmetric(struct runner *r, - struct cell *restrict ci, - struct cell *restrict cj) { - - /* Some constants */ - const struct engine *e = r->e; - const struct gravity_props *props = e->gravity_properties; - const int periodic = e->mesh->periodic; - const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; - const float r_s_inv = e->mesh->r_s_inv; - - TIMER_TIC; - - /* Anything to do here? */ - if ((!cell_is_active_gravity_mm(ci, e) || ci->nodeID != engine_rank) || - (!cell_is_active_gravity_mm(cj, e) || cj->nodeID != engine_rank)) - error("Invalid state in symmetric M-M calculation!"); - - /* Short-cut to the multipole */ - const struct multipole *multi_i = &ci->grav.multipole->m_pole; - const struct multipole *multi_j = &cj->grav.multipole->m_pole; - -#ifdef SWIFT_DEBUG_CHECKS - if (ci == cj) error("Interacting a cell with itself using M2L"); - - if (multi_i->num_gpart == 0) - error("Multipole i does not seem to have been set."); - - if (multi_j->num_gpart == 0) - error("Multipole j does not seem to have been set."); - - if (ci->grav.multipole->pot.ti_init != e->ti_current) - error("ci->grav tensor not initialised."); - - if (ci->grav.multipole->pot.ti_init != e->ti_current) - error("cj->grav tensor not initialised."); - - if (ci->grav.ti_old_multipole != e->ti_current) - error( - "Undrifted multipole ci->grav.ti_old_multipole=%lld ci->nodeID=%d " - "cj->nodeID=%d e->ti_current=%lld", - ci->grav.ti_old_multipole, ci->nodeID, cj->nodeID, e->ti_current); - - if (cj->grav.ti_old_multipole != e->ti_current) - error( - "Undrifted multipole cj->grav.ti_old_multipole=%lld cj->nodeID=%d " - "ci->nodeID=%d e->ti_current=%lld", - cj->grav.ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current); -#endif - - /* Let's interact at this level */ - gravity_M2L_symmetric(&ci->grav.multipole->pot, &cj->grav.multipole->pot, - multi_i, multi_j, ci->grav.multipole->CoM, - cj->grav.multipole->CoM, props, periodic, dim, r_s_inv); - - TIMER_TOC(timer_dopair_grav_mm); -} - -/** - * @brief Computes the interaction of the field tensor in a cell with the - * multipole of another cell. - * - * @param r The #runner. - * @param ci The #cell with field tensor to interact. - * @param cj The #cell with the multipole. - */ -static INLINE void runner_dopair_grav_mm_nonsym( - struct runner *r, struct cell *restrict ci, - const struct cell *restrict cj) { - - /* Some constants */ - const struct engine *e = r->e; - const struct gravity_props *props = e->gravity_properties; - const int periodic = e->mesh->periodic; - const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; - const float r_s_inv = e->mesh->r_s_inv; - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_gravity_mm(ci, e) || ci->nodeID != engine_rank) return; - - /* Short-cut to the multipole */ - const struct multipole *multi_j = &cj->grav.multipole->m_pole; - -#ifdef SWIFT_DEBUG_CHECKS - if (ci == cj) error("Interacting a cell with itself using M2L"); - - if (multi_j->num_gpart == 0) - error("Multipole does not seem to have been set."); - - if (ci->grav.multipole->pot.ti_init != e->ti_current) - error("ci->grav tensor not initialised."); - - if (cj->grav.ti_old_multipole != e->ti_current) - error( - "Undrifted multipole cj->grav.ti_old_multipole=%lld cj->nodeID=%d " - "ci->nodeID=%d e->ti_current=%lld", - cj->grav.ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current); -#endif - - /* Let's interact at this level */ - gravity_M2L_nonsym(&ci->grav.multipole->pot, multi_j, ci->grav.multipole->CoM, - cj->grav.multipole->CoM, props, periodic, dim, r_s_inv); - - TIMER_TOC(timer_dopair_grav_mm); -} - -/** - * @brief Call the M-M calculation on two cells if active. - * - * @param r The #runner object. - * @param ci The first #cell. - * @param cj The second #cell. - */ -static INLINE void runner_dopair_grav_mm(struct runner *r, - struct cell *restrict ci, - struct cell *restrict cj) { - - const struct engine *e = r->e; - - /* What do we need to do? */ - const int do_i = - cell_is_active_gravity_mm(ci, e) && (ci->nodeID == e->nodeID); - const int do_j = - cell_is_active_gravity_mm(cj, e) && (cj->nodeID == e->nodeID); - - /* Do we need drifting first? */ - if (ci->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e); - if (cj->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(cj, e); - - /* Interact! */ - if (do_i && do_j) - runner_dopair_grav_mm_symmetric(r, ci, cj); - else if (do_i) - runner_dopair_grav_mm_nonsym(r, ci, cj); - else if (do_j) - runner_dopair_grav_mm_nonsym(r, cj, ci); -} - -/** - * @brief Computes all the M-M interactions between all the well-separated (at - * rebuild) pairs of progenies of the two cells. - * - * @param r The #runner thread. - * @param flags The task flag containing the list of well-separated pairs as a - * bit-field. - * @param ci The first #cell. - * @param cj The second #cell. - */ -static INLINE void runner_dopair_grav_mm_progenies(struct runner *r, - const long long flags, - struct cell *restrict ci, - struct cell *restrict cj) { - - /* Loop over all pairs of progenies */ - for (int i = 0; i < 8; i++) { - if (ci->progeny[i] != NULL) { - for (int j = 0; j < 8; j++) { - if (cj->progeny[j] != NULL) { - - struct cell *cpi = ci->progeny[i]; - struct cell *cpj = cj->progeny[j]; - - const int flag = i * 8 + j; - - /* Did we agree to use an M-M interaction here at the last rebuild? */ - if (flags & (1ULL << flag)) runner_dopair_grav_mm(r, cpi, cpj); - } - } - } - } -} - -static INLINE void runner_dopair_recursive_grav_pm(struct runner *r, - struct cell *ci, - const struct cell *cj) { - /* Some constants */ - const struct engine *e = r->e; - const int periodic = e->mesh->periodic; - const float dim[3] = {(float)e->mesh->dim[0], (float)e->mesh->dim[1], - (float)e->mesh->dim[2]}; - const float r_s_inv = e->mesh->r_s_inv; - - /* Anything to do here? */ - if (!(cell_is_active_gravity(ci, e) && ci->nodeID == e->nodeID)) return; - -#ifdef SWIFT_DEBUG_CHECKS - /* Early abort? */ - if (ci->grav.count == 0 || cj->grav.count == 0) - error("Doing pair gravity on an empty cell !"); - - /* Sanity check */ - if (ci == cj) error("Pair interaction between a cell and itself."); - - if (cj->grav.ti_old_multipole != e->ti_current) - error("cj->grav.multipole not drifted."); -#endif - - /* Can we recurse further? */ - if (ci->split) { - - /* Loop over ci's children */ - for (int k = 0; k < 8; k++) { - if (ci->progeny[k] != NULL) - runner_dopair_recursive_grav_pm(r, ci->progeny[k], cj); - } - - /* Ok, let's do the interaction here */ - } else { - - /* Start by constructing particle caches */ - - /* Cache to play with */ - struct gravity_cache *const ci_cache = &r->ci_gravity_cache; - - /* Computed the padded counts */ - const int gcount_i = ci->grav.count; - const int gcount_padded_i = gcount_i - (gcount_i % VEC_SIZE) + VEC_SIZE; - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that we fit in cache */ - if (gcount_i > ci_cache->count) - error("Not enough space in the cache! gcount_i=%d", gcount_i); -#endif - - /* Recover the multipole info and the CoM locations */ - const struct multipole *multi_j = &cj->grav.multipole->m_pole; - const float r_max = cj->grav.multipole->r_max; - const float CoM_j[3] = {(float)(cj->grav.multipole->CoM[0]), - (float)(cj->grav.multipole->CoM[1]), - (float)(cj->grav.multipole->CoM[2])}; - - /* Fill the cache */ - gravity_cache_populate_all_mpole( - e->max_active_bin, periodic, dim, ci_cache, ci->grav.parts, gcount_i, - gcount_padded_i, ci, CoM_j, r_max * r_max, e->gravity_properties); - - /* Can we use the Newtonian version or do we need the truncated one ? */ - if (!periodic) { - - runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j, - periodic, dim, e, ci->grav.parts, gcount_i, - cj); - - } else { - - runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j, multi_j, - dim, r_s_inv, e, ci->grav.parts, gcount_i, - cj); - } - - /* Write back to the particles */ - gravity_cache_write_back(ci_cache, ci->grav.parts, gcount_i); - } -} - -/** - * @brief Computes the interaction of all the particles in a cell with all the - * particles of another cell. - * - * This function will try to recurse as far down the tree as possible and only - * default to direct summation if there is no better option. - * - * If using periodic BCs, we will abort the recursion if th distance between the - * cells is larger than the set threshold. - * - * @param r The #runner. - * @param ci The first #cell. - * @param cj The other #cell. - * @param gettimer Are we timing this ? - */ -static INLINE void runner_dopair_recursive_grav(struct runner *r, - struct cell *ci, - struct cell *cj, int gettimer) { - - /* Some constants */ - const struct engine *e = r->e; - const int nodeID = e->nodeID; - const int periodic = e->mesh->periodic; - const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; - const double theta_crit2 = e->gravity_properties->theta_crit2; - const double max_distance = e->mesh->r_cut_max; - - /* Anything to do here? */ - if (!((cell_is_active_gravity(ci, e) && ci->nodeID == nodeID) || - (cell_is_active_gravity(cj, e) && cj->nodeID == nodeID))) - return; - -#ifdef SWIFT_DEBUG_CHECKS - - const int gcount_i = ci->grav.count; - const int gcount_j = cj->grav.count; - - /* Early abort? */ - if (gcount_i == 0 || gcount_j == 0) - error("Doing pair gravity on an empty cell !"); - - /* Sanity check */ - if (ci == cj) error("Pair interaction between a cell and itself."); - - if (cell_is_active_gravity(ci, e) && - ci->grav.ti_old_multipole != e->ti_current) - error("ci->grav.multipole not drifted."); - if (cell_is_active_gravity(cj, e) && - cj->grav.ti_old_multipole != e->ti_current) - error("cj->grav.multipole not drifted."); -#endif - - TIMER_TIC; - - /* Recover the multipole information */ - struct gravity_tensors *const multi_i = ci->grav.multipole; - struct gravity_tensors *const multi_j = cj->grav.multipole; - - /* Get the distance between the CoMs */ - double dx = multi_i->CoM[0] - multi_j->CoM[0]; - double dy = multi_i->CoM[1] - multi_j->CoM[1]; - double dz = multi_i->CoM[2] - multi_j->CoM[2]; - - /* Apply BC */ - if (periodic) { - dx = nearest(dx, dim[0]); - dy = nearest(dy, dim[1]); - dz = nearest(dz, dim[2]); - } - const double r2 = dx * dx + dy * dy + dz * dz; - - /* Minimal distance between any 2 particles in the two cells */ - const double r_lr_check = sqrt(r2) - (multi_i->r_max + multi_j->r_max); - - /* Are we beyond the distance where the truncated forces are 0? */ - if (periodic && r_lr_check > max_distance) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Need to account for the interactions we missed */ - if (cell_is_active_gravity(ci, e)) - multi_i->pot.num_interacted += multi_j->m_pole.num_gpart; - if (cell_is_active_gravity(cj, e)) - multi_j->pot.num_interacted += multi_i->m_pole.num_gpart; -#endif - return; - } - - /* OK, we actually need to compute this pair. Let's find the cheapest - * option... */ - - /* Can we use M-M interactions ? */ - if (gravity_M2L_accept(multi_i->r_max, multi_j->r_max, theta_crit2, r2)) { - - /* Go M-M */ - runner_dopair_grav_mm(r, ci, cj); - - } else if (!ci->split && !cj->split) { - - /* We have two leaves. Go P-P. */ - runner_dopair_grav_pp(r, ci, cj, /*symmetric*/ 1, /*allow_mpoles*/ 1); - - } else { - - /* Alright, we'll have to split and recurse. */ - /* We know at least one of ci and cj is splittable */ - - const double ri_max = multi_i->r_max; - const double rj_max = multi_j->r_max; - - /* Split the larger of the two cells and start over again */ - if (ri_max > rj_max) { - - /* Can we actually split that interaction ? */ - if (ci->split) { - - /* Loop over ci's children */ - for (int k = 0; k < 8; k++) { - if (ci->progeny[k] != NULL) - runner_dopair_recursive_grav(r, ci->progeny[k], cj, 0); - } - - } else { - /* cj is split */ - - /* MATTHIEU: This could maybe be replaced by P-M interactions ? */ - - /* Loop over cj's children */ - for (int k = 0; k < 8; k++) { - if (cj->progeny[k] != NULL) - runner_dopair_recursive_grav(r, ci, cj->progeny[k], 0); - } - } - } else { - - /* Can we actually split that interaction ? */ - if (cj->split) { - - /* Loop over cj's children */ - for (int k = 0; k < 8; k++) { - if (cj->progeny[k] != NULL) - runner_dopair_recursive_grav(r, ci, cj->progeny[k], 0); - } - - } else { - /* ci is split */ - - /* MATTHIEU: This could maybe be replaced by P-M interactions ? */ - - /* Loop over ci's children */ - for (int k = 0; k < 8; k++) { - if (ci->progeny[k] != NULL) - runner_dopair_recursive_grav(r, ci->progeny[k], cj, 0); - } - } - } - } - - if (gettimer) TIMER_TOC(timer_dosub_pair_grav); -} - -/** - * @brief Computes the interaction of all the particles in a cell. - * - * This function will try to recurse as far down the tree as possible and only - * default to direct summation if there is no better option. - * - * @param r The #runner. - * @param c The first #cell. - * @param gettimer Are we timing this ? - */ -static INLINE void runner_doself_recursive_grav(struct runner *r, - struct cell *c, int gettimer) { - - /* Some constants */ - const struct engine *e = r->e; - -#ifdef SWIFT_DEBUG_CHECKS - /* Early abort? */ - if (c->grav.count == 0) error("Doing self gravity on an empty cell !"); -#endif - - TIMER_TIC; - - /* Anything to do here? */ - if (!cell_is_active_gravity(c, e)) return; - - /* If the cell is split, interact each progeny with itself, and with - each of its siblings. */ - if (c->split) { - - for (int j = 0; j < 8; j++) { - if (c->progeny[j] != NULL) { - - runner_doself_recursive_grav(r, c->progeny[j], 0); - - for (int k = j + 1; k < 8; k++) { - if (c->progeny[k] != NULL) { - - runner_dopair_recursive_grav(r, c->progeny[j], c->progeny[k], 0); - } - } - } - } - } - - /* If the cell is not split, then just go for it... */ - else { - - runner_doself_grav_pp(r, c); - } - - if (gettimer) TIMER_TOC(timer_dosub_self_grav); -} - -/** - * @brief Performs all M-M interactions between a given top-level cell and all - * the other top-levels that are far enough. - * - * @param r The thread #runner. - * @param ci The #cell of interest. - * @param timer Are we timing this ? - */ -static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci, - int timer) { - - /* Some constants */ - const struct engine *e = r->e; - const int periodic = e->mesh->periodic; - const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; - const double theta_crit2 = e->gravity_properties->theta_crit2; - const double max_distance2 = e->mesh->r_cut_max * e->mesh->r_cut_max; - - TIMER_TIC; - - /* Recover the list of top-level cells */ - struct cell *cells = e->s->cells_top; - int *cells_with_particles = e->s->cells_with_particles_top; - const int nr_cells_with_particles = e->s->nr_cells_with_particles; - - /* Anything to do here? */ - if (!cell_is_active_gravity(ci, e)) return; - - if (ci->nodeID != engine_rank) - error("Non-local cell in long-range gravity task!"); - - /* Check multipole has been drifted */ - if (ci->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e); - - /* Get this cell's multipole information */ - struct gravity_tensors *const multi_i = ci->grav.multipole; - - /* Find this cell's top-level (great-)parent */ - struct cell *top = ci; - while (top->parent != NULL) top = top->parent; - - /* Recover the top-level multipole (for distance checks) */ - struct gravity_tensors *const multi_top = top->grav.multipole; - const double CoM_rebuild_top[3] = {multi_top->CoM_rebuild[0], - multi_top->CoM_rebuild[1], - multi_top->CoM_rebuild[2]}; - - /* Loop over all the top-level cells and go for a M-M interaction if - * well-separated */ - for (int n = 0; n < nr_cells_with_particles; ++n) { - - /* Handle on the top-level cell and it's gravity business*/ - const struct cell *cj = &cells[cells_with_particles[n]]; - const struct gravity_tensors *const multi_j = cj->grav.multipole; - - /* Avoid self contributions */ - if (top == cj) continue; - - /* Skip empty cells */ - if (multi_j->m_pole.M_000 == 0.f) continue; - - /* Can we escape early in the periodic BC case? */ - if (periodic) { - - /* Minimal distance between any pair of particles */ - const double min_radius2 = - cell_min_dist2_same_size(top, cj, periodic, dim); - - /* Are we beyond the distance where the truncated forces are 0 ?*/ - if (min_radius2 > max_distance2) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Need to account for the interactions we missed */ - multi_i->pot.num_interacted += multi_j->m_pole.num_gpart; -#endif - - /* Record that this multipole received a contribution */ - multi_i->pot.interacted = 1; +struct runner; +struct cell; - /* We are done here. */ - continue; - } - } +void runner_do_grav_down(struct runner *r, struct cell *c, int timer); - /* Get the distance between the CoMs at the last rebuild*/ - double dx_r = CoM_rebuild_top[0] - multi_j->CoM_rebuild[0]; - double dy_r = CoM_rebuild_top[1] - multi_j->CoM_rebuild[1]; - double dz_r = CoM_rebuild_top[2] - multi_j->CoM_rebuild[2]; +void runner_doself_recursive_grav(struct runner *r, struct cell *c, + int gettimer); - /* Apply BC */ - if (periodic) { - dx_r = nearest(dx_r, dim[0]); - dy_r = nearest(dy_r, dim[1]); - dz_r = nearest(dz_r, dim[2]); - } - const double r2_rebuild = dx_r * dx_r + dy_r * dy_r + dz_r * dz_r; +void runner_dopair_recursive_grav(struct runner *r, struct cell *ci, + struct cell *cj, int gettimer); - /* Are we in charge of this cell pair? */ - if (gravity_M2L_accept(multi_top->r_max_rebuild, multi_j->r_max_rebuild, - theta_crit2, r2_rebuild)) { +void runner_dopair_grav_mm_progenies(struct runner *r, const long long flags, + struct cell *restrict ci, + struct cell *restrict cj); - /* Call the PM interaction fucntion on the active sub-cells of ci */ - runner_dopair_grav_mm_nonsym(r, ci, cj); - // runner_dopair_recursive_grav_pm(r, ci, cj); +void runner_do_grav_long_range(struct runner *r, struct cell *ci, int timer); - /* Record that this multipole received a contribution */ - multi_i->pot.interacted = 1; +/* Internal functions (for unit tests and debugging) */ - } /* We are in charge of this pair */ - } /* Loop over top-level cells */ +void runner_doself_grav_pp(struct runner *r, struct cell *c); - if (timer) TIMER_TOC(timer_dograv_long_range); -} +void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj, + const int symmetric, const int allow_mpole); #endif /* SWIFT_RUNNER_DOIACT_GRAV_H */ diff --git a/src/runner_doiact_hydro.c b/src/runner_doiact_hydro.c new file mode 100644 index 0000000000000000000000000000000000000000..480ea59f0a536aa340b7e4d8f838bef3a0cca072 --- /dev/null +++ b/src/runner_doiact_hydro.c @@ -0,0 +1,63 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Local headers. */ +#include "active.h" +#include "cell.h" +#include "engine.h" +#include "pressure_floor_iact.h" +#include "runner.h" +#include "runner_doiact_hydro_vec.h" +#include "space_getsid.h" +#include "timers.h" + +/* Import the density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_functions_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + +/* Import the gradient loop functions (if required). */ +#ifdef EXTRA_HYDRO_LOOP +#define FUNCTION gradient +#define FUNCTION_TASK_LOOP TASK_LOOP_GRADIENT +#include "runner_doiact_functions_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP +#endif + +/* Import the force loop functions. */ +#define FUNCTION force +#define FUNCTION_TASK_LOOP TASK_LOOP_FORCE +#include "runner_doiact_functions_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + +/* Import the limiter loop functions. */ +#define FUNCTION limiter +#define FUNCTION_TASK_LOOP TASK_LOOP_LIMITER +#include "runner_doiact_functions_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP diff --git a/src/runner_doiact_hydro.h b/src/runner_doiact_hydro.h new file mode 100644 index 0000000000000000000000000000000000000000..1fd54c1037e2d0b9c7a671311cfee4720ebe8d84 --- /dev/null +++ b/src/runner_doiact_hydro.h @@ -0,0 +1,151 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ + +/* Before including this file, define FUNCTION, which is the + name of the interaction function. This creates the interaction functions + runner_dopair_FUNCTION, runner_dopair_FUNCTION_naive, runner_doself_FUNCTION, + and runner_dosub_FUNCTION calling the pairwise interaction function + runner_iact_FUNCTION. */ + +#define PASTE(x, y) x##_##y + +#define _DOPAIR1_BRANCH(f) PASTE(runner_dopair1_branch, f) +#define DOPAIR1_BRANCH _DOPAIR1_BRANCH(FUNCTION) + +#define _DOPAIR1(f) PASTE(runner_dopair1, f) +#define DOPAIR1 _DOPAIR1(FUNCTION) + +#define _DOPAIR2_BRANCH(f) PASTE(runner_dopair2_branch, f) +#define DOPAIR2_BRANCH _DOPAIR2_BRANCH(FUNCTION) + +#define _DOPAIR2(f) PASTE(runner_dopair2, f) +#define DOPAIR2 _DOPAIR2(FUNCTION) + +#define _DOPAIR_SUBSET(f) PASTE(runner_dopair_subset, f) +#define DOPAIR_SUBSET _DOPAIR_SUBSET(FUNCTION) + +#define _DOPAIR_SUBSET_BRANCH(f) PASTE(runner_dopair_subset_branch, f) +#define DOPAIR_SUBSET_BRANCH _DOPAIR_SUBSET_BRANCH(FUNCTION) + +#define _DOPAIR_SUBSET_NOSORT(f) PASTE(runner_dopair_subset_nosort, f) +#define DOPAIR_SUBSET_NOSORT _DOPAIR_SUBSET_NOSORT(FUNCTION) + +#define _DOPAIR_SUBSET_NAIVE(f) PASTE(runner_dopair_subset_naive, f) +#define DOPAIR_SUBSET_NAIVE _DOPAIR_SUBSET_NAIVE(FUNCTION) + +#define _DOPAIR1_NAIVE(f) PASTE(runner_dopair1_naive, f) +#define DOPAIR1_NAIVE _DOPAIR1_NAIVE(FUNCTION) + +#define _DOPAIR2_NAIVE(f) PASTE(runner_dopair2_naive, f) +#define DOPAIR2_NAIVE _DOPAIR2_NAIVE(FUNCTION) + +#define _DOSELF1_NAIVE(f) PASTE(runner_doself1_naive, f) +#define DOSELF1_NAIVE _DOSELF1_NAIVE(FUNCTION) + +#define _DOSELF2_NAIVE(f) PASTE(runner_doself2_naive, f) +#define DOSELF2_NAIVE _DOSELF2_NAIVE(FUNCTION) + +#define _DOSELF1_BRANCH(f) PASTE(runner_doself1_branch, f) +#define DOSELF1_BRANCH _DOSELF1_BRANCH(FUNCTION) + +#define _DOSELF1(f) PASTE(runner_doself1, f) +#define DOSELF1 _DOSELF1(FUNCTION) + +#define _DOSELF2_BRANCH(f) PASTE(runner_doself2_branch, f) +#define DOSELF2_BRANCH _DOSELF2_BRANCH(FUNCTION) + +#define _DOSELF2(f) PASTE(runner_doself2, f) +#define DOSELF2 _DOSELF2(FUNCTION) + +#define _DOSELF_SUBSET(f) PASTE(runner_doself_subset, f) +#define DOSELF_SUBSET _DOSELF_SUBSET(FUNCTION) + +#define _DOSELF_SUBSET_BRANCH(f) PASTE(runner_doself_subset_branch, f) +#define DOSELF_SUBSET_BRANCH _DOSELF_SUBSET_BRANCH(FUNCTION) + +#define _DOSUB_SELF1(f) PASTE(runner_dosub_self1, f) +#define DOSUB_SELF1 _DOSUB_SELF1(FUNCTION) + +#define _DOSUB_PAIR1(f) PASTE(runner_dosub_pair1, f) +#define DOSUB_PAIR1 _DOSUB_PAIR1(FUNCTION) + +#define _DOSUB_SELF2(f) PASTE(runner_dosub_self2, f) +#define DOSUB_SELF2 _DOSUB_SELF2(FUNCTION) + +#define _DOSUB_PAIR2(f) PASTE(runner_dosub_pair2, f) +#define DOSUB_PAIR2 _DOSUB_PAIR2(FUNCTION) + +#define _DOSUB_SUBSET(f) PASTE(runner_dosub_subset, f) +#define DOSUB_SUBSET _DOSUB_SUBSET(FUNCTION) + +#define _IACT_NONSYM(f) PASTE(runner_iact_nonsym, f) +#define IACT_NONSYM _IACT_NONSYM(FUNCTION) + +#define _IACT(f) PASTE(runner_iact, f) +#define IACT _IACT(FUNCTION) + +#define _IACT_NONSYM_VEC(f) PASTE(runner_iact_nonsym_vec, f) +#define IACT_NONSYM_VEC _IACT_NONSYM_VEC(FUNCTION) + +#define _IACT_VEC(f) PASTE(runner_iact_vec, f) +#define IACT_VEC _IACT_VEC(FUNCTION) + +#define _TIMER_DOSELF(f) PASTE(timer_doself, f) +#define TIMER_DOSELF _TIMER_DOSELF(FUNCTION) + +#define _TIMER_DOPAIR(f) PASTE(timer_dopair, f) +#define TIMER_DOPAIR _TIMER_DOPAIR(FUNCTION) + +#define _TIMER_DOSUB_SELF(f) PASTE(timer_dosub_self, f) +#define TIMER_DOSUB_SELF _TIMER_DOSUB_SELF(FUNCTION) + +#define _TIMER_DOSUB_PAIR(f) PASTE(timer_dosub_pair, f) +#define TIMER_DOSUB_PAIR _TIMER_DOSUB_PAIR(FUNCTION) + +#define _TIMER_DOSELF_SUBSET(f) PASTE(timer_doself_subset, f) +#define TIMER_DOSELF_SUBSET _TIMER_DOSELF_SUBSET(FUNCTION) + +#define _TIMER_DOPAIR_SUBSET(f) PASTE(timer_dopair_subset, f) +#define TIMER_DOPAIR_SUBSET _TIMER_DOPAIR_SUBSET(FUNCTION) + +void DOSELF1_BRANCH(struct runner *r, struct cell *c); +void DOSELF2_BRANCH(struct runner *r, struct cell *c); + +void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj); +void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj); + +void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer); +void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer); + +void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, + int gettimer); +void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, + int gettimer); + +void DOSELF_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci, + struct part *restrict parts, int *restrict ind, + int count); + +void DOPAIR_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci, + struct part *restrict parts_i, int *restrict ind, + int count, struct cell *restrict cj); + +void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts, + int *ind, int count, struct cell *cj, int gettimer); diff --git a/src/runner_doiact_vec.c b/src/runner_doiact_hydro_vec.c similarity index 99% rename from src/runner_doiact_vec.c rename to src/runner_doiact_hydro_vec.c index 68f34b0d3b8fc9c79097522f8a1618f86957612e..59401e4050dcb4481d1c56aa8857106558a06880 100644 --- a/src/runner_doiact_vec.c +++ b/src/runner_doiact_hydro_vec.c @@ -21,7 +21,7 @@ #include "../config.h" /* This object's header. */ -#include "runner_doiact_vec.h" +#include "runner_doiact_hydro_vec.h" #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) diff --git a/src/runner_doiact_vec.h b/src/runner_doiact_hydro_vec.h similarity index 100% rename from src/runner_doiact_vec.h rename to src/runner_doiact_hydro_vec.h diff --git a/src/runner_doiact_stars.c b/src/runner_doiact_stars.c new file mode 100644 index 0000000000000000000000000000000000000000..1e1267df5195f727a19252b6ee654629e23149b6 --- /dev/null +++ b/src/runner_doiact_stars.c @@ -0,0 +1,47 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Local headers. */ +#include "active.h" +#include "cell.h" +#include "engine.h" +#include "feedback.h" +#include "runner.h" +#include "space_getsid.h" +#include "stars.h" +#include "timers.h" + +/* Import the stars density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_functions_stars.h" +#undef FUNCTION_TASK_LOOP +#undef FUNCTION + +/* Import the stars feedback loop functions. */ +#define FUNCTION feedback +#define FUNCTION_TASK_LOOP TASK_LOOP_FEEDBACK +#include "runner_doiact_functions_stars.h" +#undef FUNCTION_TASK_LOOP +#undef FUNCTION diff --git a/src/runner_doiact_stars.h b/src/runner_doiact_stars.h index 7e9780def83bbdbab83a431a757a52f3ba51d2e4..2d41d5a0bd1b1003039e1795eec205889b46baf6 100644 --- a/src/runner_doiact_stars.h +++ b/src/runner_doiact_stars.h @@ -86,1307 +86,21 @@ #define _IACT_STARS(f) PASTE(runner_iact_nonsym_stars, f) #define IACT_STARS _IACT_STARS(FUNCTION) -/** - * @brief Calculate the number density of #part around the #spart - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void DOSELF1_STARS(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 int with_cosmology = e->policy & engine_policy_cosmology; - const integertime_t ti_current = e->ti_current; - const struct cosmology *cosmo = e->cosmology; - - /* Anything to do here? */ - if (c->hydro.count == 0 || c->stars.count == 0) return; - if (!cell_is_active_stars(c, e)) return; - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - const int scount = c->stars.count; - const int count = c->hydro.count; - struct spart *restrict sparts = c->stars.parts; - struct part *restrict parts = c->hydro.parts; - struct xpart *restrict xparts = c->hydro.xparts; - - /* Loop over the sparts in ci. */ - for (int sid = 0; sid < scount; sid++) { - - /* Get a hold of the ith spart in ci. */ - struct spart *restrict si = &sparts[sid]; - - /* Skip inactive particles */ - if (!spart_is_active(si, e)) continue; - - /* Skip inactive particles */ - if (!feedback_is_active(si, e->time, cosmo, with_cosmology)) 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_STARS(r2, dx, hi, hj, si, pj, a, H); -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hi, hj, si, pj, xpj, cosmo, - ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, - ti_current); -#endif - } - } /* loop over the parts in ci. */ - } /* loop over the sparts in ci. */ - - TIMER_TOC(TIMER_DOSELF_STARS); -} - -/** - * @brief Calculate the number density of cj #part around the ci #spart - * - * @param r runner task - * @param ci The first #cell - * @param cj The second #cell - */ -void DO_NONSYM_PAIR1_STARS_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 int with_cosmology = e->policy & engine_policy_cosmology; - const integertime_t ti_current = e->ti_current; - const struct cosmology *cosmo = e->cosmology; - - /* Anything to do here? */ - if (cj->hydro.count == 0 || ci->stars.count == 0) return; - if (!cell_is_active_stars(ci, e)) return; - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - const int scount_i = ci->stars.count; - const int count_j = cj->hydro.count; - struct spart *restrict sparts_i = ci->stars.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 sparts in ci. */ - for (int sid = 0; sid < scount_i; sid++) { - - /* Get a hold of the ith spart in ci. */ - struct spart *restrict si = &sparts_i[sid]; - - /* Skip inactive particles */ - if (!spart_is_active(si, e)) continue; - - /* Skip inactive particles */ - if (!feedback_is_active(si, e->time, cosmo, with_cosmology)) 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_STARS(r2, dx, hi, hj, si, pj, a, H); - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hi, hj, si, pj, xpj, cosmo, - ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, - ti_current); -#endif - } - } /* loop over the parts in cj. */ - } /* loop over the parts in ci. */ -} - -/** - * @brief Compute the interactions between a cell pair. - * - * @param r The #runner. - * @param ci The first #cell. - * @param cj The second #cell. - * @param sid The direction of the pair. - * @param shift The shift vector to apply to the particles in ci. - */ -void DO_SYM_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, - const int sid, const double *shift) { - - TIMER_TIC; - - const struct engine *e = r->e; - const int with_cosmology = e->policy & engine_policy_cosmology; - const integertime_t ti_current = e->ti_current; - const struct cosmology *cosmo = e->cosmology; - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - /* Get the cutoff shift. */ - double rshift = 0.0; - for (int k = 0; k < 3; k++) rshift += shift[k] * runner_shift[sid][k]; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - const int do_ci_stars = (ci->nodeID == e->nodeID) && (ci->stars.count != 0) && - (cj->hydro.count != 0) && cell_is_active_stars(ci, e); - const int do_cj_stars = (cj->nodeID == e->nodeID) && (cj->stars.count != 0) && - (ci->hydro.count != 0) && cell_is_active_stars(cj, e); -#else - /* here we are updating the hydro -> switch ci, cj for local */ - const int do_ci_stars = (cj->nodeID == e->nodeID) && (ci->stars.count != 0) && - (cj->hydro.count != 0) && cell_is_active_stars(ci, e); - const int do_cj_stars = (ci->nodeID == e->nodeID) && (cj->stars.count != 0) && - (ci->hydro.count != 0) && cell_is_active_stars(cj, e); -#endif - - if (do_ci_stars) { - - /* Pick-out the sorted lists. */ - const struct sort_entry *restrict sort_j = cj->hydro.sort[sid]; - const struct sort_entry *restrict sort_i = ci->stars.sort[sid]; - -#ifdef SWIFT_DEBUG_CHECKS - /* Some constants used to checks that the parts are in the right frame */ - const float shift_threshold_x = - 2. * ci->width[0] + - 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); - const float shift_threshold_y = - 2. * ci->width[1] + - 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); - const float shift_threshold_z = - 2. * ci->width[2] + - 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); -#endif /* SWIFT_DEBUG_CHECKS */ - - /* Get some other useful values. */ - const double hi_max = ci->stars.h_max * kernel_gamma - rshift; - const int count_i = ci->stars.count; - const int count_j = cj->hydro.count; - struct spart *restrict sparts_i = ci->stars.parts; - struct part *restrict parts_j = cj->hydro.parts; - struct xpart *restrict xparts_j = cj->hydro.xparts; - const double dj_min = sort_j[0].d; - const float dx_max_rshift = - (ci->stars.dx_max_sort + cj->hydro.dx_max_sort) - rshift; - const float dx_max = (ci->stars.dx_max_sort + cj->hydro.dx_max_sort); - - /* Loop over the sparts in ci. */ - for (int pid = count_i - 1; - pid >= 0 && sort_i[pid].d + hi_max + dx_max > dj_min; pid--) { - - /* Get a hold of the ith part in ci. */ - struct spart *restrict spi = &sparts_i[sort_i[pid].i]; - const float hi = spi->h; - - /* Skip inactive particles */ - if (!spart_is_active(spi, e)) continue; - - /* Skip inactive particles */ - if (!feedback_is_active(spi, e->time, cosmo, with_cosmology)) continue; - - /* Compute distance from the other cell. */ - const double px[3] = {spi->x[0], spi->x[1], spi->x[2]}; - float dist = px[0] * runner_shift[sid][0] + px[1] * runner_shift[sid][1] + - px[2] * runner_shift[sid][2]; - - /* Is there anything we need to interact with ? */ - const double di = dist + hi * kernel_gamma + dx_max_rshift; - if (di < dj_min) continue; - - /* Get some additional information about pi */ - const float hig2 = hi * hi * kernel_gamma2; - const float pix = spi->x[0] - (cj->loc[0] + shift[0]); - const float piy = spi->x[1] - (cj->loc[1] + shift[1]); - const float piz = spi->x[2] - (cj->loc[2] + shift[2]); - - /* Loop over the parts in cj. */ - for (int pjd = 0; pjd < count_j && sort_j[pjd].d < di; pjd++) { - - /* Recover pj */ - struct part *pj = &parts_j[sort_j[pjd].i]; - struct xpart *xpj = &xparts_j[sort_j[pjd].i]; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pj, e)) continue; - - const float hj = pj->h; - const float pjx = pj->x[0] - cj->loc[0]; - const float pjy = pj->x[1] - cj->loc[1]; - const float pjz = pj->x[2] - cj->loc[2]; - - /* Compute the pairwise distance. */ - float dx[3] = {pix - pjx, piy - pjy, piz - pjz}; - const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that particles are in the correct frame after the shifts */ - if (pix > shift_threshold_x || pix < -shift_threshold_x) - error( - "Invalid particle position in X for pi (pix=%e ci->width[0]=%e)", - pix, ci->width[0]); - if (piy > shift_threshold_y || piy < -shift_threshold_y) - error( - "Invalid particle position in Y for pi (piy=%e ci->width[1]=%e)", - piy, ci->width[1]); - if (piz > shift_threshold_z || piz < -shift_threshold_z) - error( - "Invalid particle position in Z for pi (piz=%e ci->width[2]=%e)", - piz, ci->width[2]); - if (pjx > shift_threshold_x || pjx < -shift_threshold_x) - error( - "Invalid particle position in X for pj (pjx=%e ci->width[0]=%e)", - pjx, ci->width[0]); - if (pjy > shift_threshold_y || pjy < -shift_threshold_y) - error( - "Invalid particle position in Y for pj (pjy=%e ci->width[1]=%e)", - pjy, ci->width[1]); - if (pjz > shift_threshold_z || pjz < -shift_threshold_z) - error( - "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", - pjz, ci->width[2]); - - /* Check that particles have been drifted to the current time */ - if (spi->ti_drift != e->ti_current) - error("Particle spi not drifted to 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_STARS(r2, dx, hi, hj, spi, pj, a, H); - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, - cosmo, ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, - ti_current); -#endif - } - } /* loop over the parts in cj. */ - } /* loop over the parts in ci. */ - } /* do_ci_stars */ - - if (do_cj_stars) { - /* Pick-out the sorted lists. */ - const struct sort_entry *restrict sort_i = ci->hydro.sort[sid]; - const struct sort_entry *restrict sort_j = cj->stars.sort[sid]; - -#ifdef SWIFT_DEBUG_CHECKS - /* Some constants used to checks that the parts are in the right frame */ - const float shift_threshold_x = - 2. * ci->width[0] + - 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); - const float shift_threshold_y = - 2. * ci->width[1] + - 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); - const float shift_threshold_z = - 2. * ci->width[2] + - 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); -#endif /* SWIFT_DEBUG_CHECKS */ - - /* Get some other useful values. */ - const double hj_max = cj->hydro.h_max * kernel_gamma; - const int count_i = ci->hydro.count; - const int count_j = cj->stars.count; - struct part *restrict parts_i = ci->hydro.parts; - struct xpart *restrict xparts_i = ci->hydro.xparts; - struct spart *restrict sparts_j = cj->stars.parts; - const double di_max = sort_i[count_i - 1].d - rshift; - const float dx_max_rshift = - (ci->hydro.dx_max_sort + cj->stars.dx_max_sort) + rshift; - const float dx_max = (ci->hydro.dx_max_sort + cj->stars.dx_max_sort); - - /* Loop over the parts in cj. */ - for (int pjd = 0; pjd < count_j && sort_j[pjd].d - hj_max - dx_max < di_max; - pjd++) { - - /* Get a hold of the jth part in cj. */ - struct spart *spj = &sparts_j[sort_j[pjd].i]; - const float hj = spj->h; - - /* Skip inactive particles */ - if (!spart_is_active(spj, e)) continue; - - /* Skip inactive particles */ - if (!feedback_is_active(spj, e->time, cosmo, with_cosmology)) continue; - - /* Compute distance from the other cell. */ - const double px[3] = {spj->x[0], spj->x[1], spj->x[2]}; - float dist = px[0] * runner_shift[sid][0] + px[1] * runner_shift[sid][1] + - px[2] * runner_shift[sid][2]; - - /* Is there anything we need to interact with ? */ - const double dj = dist - hj * kernel_gamma - dx_max_rshift; - if (dj - rshift > di_max) continue; - - /* Get some additional information about pj */ - const float hjg2 = hj * hj * kernel_gamma2; - const float pjx = spj->x[0] - cj->loc[0]; - const float pjy = spj->x[1] - cj->loc[1]; - const float pjz = spj->x[2] - cj->loc[2]; - - /* Loop over the parts in ci. */ - for (int pid = count_i - 1; pid >= 0 && sort_i[pid].d > dj; pid--) { - - /* Recover pi */ - struct part *pi = &parts_i[sort_i[pid].i]; - struct xpart *xpi = &xparts_i[sort_i[pid].i]; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pi, e)) continue; - - const float hi = pi->h; - const float pix = pi->x[0] - (cj->loc[0] + shift[0]); - const float piy = pi->x[1] - (cj->loc[1] + shift[1]); - const float piz = pi->x[2] - (cj->loc[2] + shift[2]); - - /* Compute the pairwise distance. */ - float dx[3] = {pjx - pix, pjy - piy, pjz - piz}; - const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that particles are in the correct frame after the shifts */ - if (pix > shift_threshold_x || pix < -shift_threshold_x) - error( - "Invalid particle position in X for pi (pix=%e ci->width[0]=%e)", - pix, ci->width[0]); - if (piy > shift_threshold_y || piy < -shift_threshold_y) - error( - "Invalid particle position in Y for pi (piy=%e ci->width[1]=%e)", - piy, ci->width[1]); - if (piz > shift_threshold_z || piz < -shift_threshold_z) - error( - "Invalid particle position in Z for pi (piz=%e ci->width[2]=%e)", - piz, ci->width[2]); - if (pjx > shift_threshold_x || pjx < -shift_threshold_x) - error( - "Invalid particle position in X for pj (pjx=%e ci->width[0]=%e)", - pjx, ci->width[0]); - if (pjy > shift_threshold_y || pjy < -shift_threshold_y) - error( - "Invalid particle position in Y for pj (pjy=%e ci->width[1]=%e)", - pjy, ci->width[1]); - if (pjz > shift_threshold_z || pjz < -shift_threshold_z) - error( - "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", - pjz, ci->width[2]); - - /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current) - error("Particle pi not drifted to current time"); - if (spj->ti_drift != e->ti_current) - error("Particle spj not drifted to current time"); -#endif - - /* Hit or miss? */ - if (r2 < hjg2) { - - IACT_STARS(r2, dx, hj, hi, spj, pi, a, H); - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hj, hi, spj, pi, xpi, - cosmo, ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hj, hi, spj, pi, xpi, cosmo, - ti_current); -#endif - } - } /* loop over the parts in ci. */ - } /* loop over the parts in cj. */ - } /* Cell cj is active */ - - TIMER_TOC(TIMER_DOPAIR_STARS); -} - -void DOPAIR1_STARS_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_stars = ci->nodeID == r->e->nodeID; - const int do_cj_stars = cj->nodeID == r->e->nodeID; -#else - /* here we are updating the hydro -> switch ci, cj */ - const int do_ci_stars = cj->nodeID == r->e->nodeID; - const int do_cj_stars = ci->nodeID == r->e->nodeID; -#endif - if (do_ci_stars && ci->stars.count != 0 && cj->hydro.count != 0) - DO_NONSYM_PAIR1_STARS_NAIVE(r, ci, cj); - if (do_cj_stars && cj->stars.count != 0 && ci->hydro.count != 0) - DO_NONSYM_PAIR1_STARS_NAIVE(r, cj, ci); - - TIMER_TOC(TIMER_DOPAIR_STARS); -} - -/** - * @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 sparts_i The #part to interact with @c cj. - * @param ind The list of indices of particles in @c ci to interact with. - * @param scount The number of particles in @c ind. - * @param cj The second #cell. - * @param sid The direction of the pair. - * @param flipped Flag to check whether the cells have been flipped or not. - * @param shift The shift vector to apply to the particles in ci. - */ -void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, - struct spart *restrict sparts_i, int *restrict ind, - int scount, struct cell *restrict cj, const int sid, - const int flipped, const double *shift) { - - const struct engine *e = r->e; - const integertime_t ti_current = e->ti_current; - const struct cosmology *cosmo = e->cosmology; - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - 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; - - /* Pick-out the sorted lists. */ - const struct sort_entry *restrict sort_j = cj->hydro.sort[sid]; - const float dxj = cj->hydro.dx_max_sort; - - /* Sparts are on the left? */ - if (!flipped) { - - /* Loop over the sparts_i. */ - for (int pid = 0; pid < scount; pid++) { - - /* Get a hold of the ith spart in ci. */ - struct spart *restrict spi = &sparts_i[ind[pid]]; - const double pix = spi->x[0] - (shift[0]); - const double piy = spi->x[1] - (shift[1]); - const double piz = spi->x[2] - (shift[2]); - const float hi = spi->h; - const float hig2 = hi * hi * kernel_gamma2; - const double di = hi * kernel_gamma + dxj + pix * runner_shift[sid][0] + - piy * runner_shift[sid][1] + piz * runner_shift[sid][2]; - - /* Loop over the parts in cj. */ - for (int pjd = 0; pjd < count_j && sort_j[pjd].d < di; pjd++) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts_j[sort_j[pjd].i]; - struct xpart *restrict xpj = &xparts_j[sort_j[pjd].i]; - - /* 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 (spi->ti_drift != e->ti_current) - error("Particle pi not drifted to 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_STARS(r2, dx, hi, hj, spi, pj, a, H); - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, - cosmo, ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, - ti_current); -#endif - } - } /* loop over the parts in cj. */ - } /* loop over the sparts in ci. */ - } - - /* Sparts are on the right. */ - else { - - /* Loop over the sparts_i. */ - for (int pid = 0; pid < scount; pid++) { - - /* Get a hold of the ith spart in ci. */ - struct spart *restrict spi = &sparts_i[ind[pid]]; - const double pix = spi->x[0] - (shift[0]); - const double piy = spi->x[1] - (shift[1]); - const double piz = spi->x[2] - (shift[2]); - const float hi = spi->h; - const float hig2 = hi * hi * kernel_gamma2; - const double di = -hi * kernel_gamma - dxj + pix * runner_shift[sid][0] + - piy * runner_shift[sid][1] + piz * runner_shift[sid][2]; - - /* Loop over the parts in cj. */ - for (int pjd = count_j - 1; pjd >= 0 && di < sort_j[pjd].d; pjd--) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts_j[sort_j[pjd].i]; - struct xpart *restrict xpj = &xparts_j[sort_j[pjd].i]; - - /* 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 (spi->ti_drift != e->ti_current) - error("Particle pi not drifted to 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_STARS(r2, dx, hi, hj, spi, pj, a, H); - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, - cosmo, ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, - ti_current); -#endif - } - } /* loop over the parts in cj. */ - } /* loop over the sparts in ci. */ - } -} +void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c); +void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj); -/** - * @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 sparts_i The #part to interact with @c cj. - * @param ind The list of indices of particles in @c ci to interact with. - * @param scount 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_STARS_NAIVE(struct runner *r, struct cell *restrict ci, - struct spart *restrict sparts_i, - int *restrict ind, int scount, - 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; - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - 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 < scount; pid++) { - - /* Get a hold of the ith part in ci. */ - struct spart *restrict spi = &sparts_i[ind[pid]]; - - const double pix = spi->x[0] - (shift[0]); - const double piy = spi->x[1] - (shift[1]); - const double piz = spi->x[2] - (shift[2]); - const float hi = spi->h; - const float hig2 = hi * hi * kernel_gamma2; - -#ifdef SWIFT_DEBUG_CHECKS - if (!spart_is_active(spi, 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_STARS(r2, dx, hi, hj, spi, pj, a, H); - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, cosmo, - ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, - ti_current); -#endif - } - } /* 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 sparts The #spart to interact. - * @param ind The list of indices of particles in @c ci to interact with. - * @param scount The number of particles in @c ind. - */ -void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, - struct spart *restrict sparts, int *restrict ind, - int scount) { - -#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; - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - 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 spid = 0; spid < scount; spid++) { - - /* Get a hold of the ith part in ci. */ - struct spart *spi = &sparts[ind[spid]]; - const float spix[3] = {(float)(spi->x[0] - ci->loc[0]), - (float)(spi->x[1] - ci->loc[1]), - (float)(spi->x[2] - ci->loc[2])}; - const float hi = spi->h; - const float hig2 = hi * hi * kernel_gamma2; - -#ifdef SWIFT_DEBUG_CHECKS - if (!spart_is_active(spi, 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] = {spix[0] - pjx[0], spix[1] - pjx[1], spix[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_STARS(r2, dx, hi, pj->h, spi, pj, a, H); -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - runner_iact_nonsym_feedback_density(r2, dx, hi, pj->h, spi, pj, xpj, - cosmo, ti_current); -#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) - runner_iact_nonsym_feedback_apply(r2, dx, hi, pj->h, spi, pj, xpj, - cosmo, ti_current); -#endif - } - } /* loop over the parts in cj. */ - } /* loop over the parts in ci. */ -} +void DOSUB_SELF1_STARS(struct runner *r, struct cell *ci, int gettimer); +void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, + int gettimer); -/** - * @brief Determine which version of DOSELF1_SUBSET_STARS needs to be called - * depending on the optimisation level. - * - * @param r The #runner. - * @param ci The first #cell. - * @param sparts The #spart to interact. - * @param ind The list of indices of particles in @c ci to interact with. - * @param scount The number of particles in @c ind. - */ void DOSELF1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, struct spart *restrict sparts, - int *restrict ind, int scount) { + int *restrict ind, int scount); - DOSELF1_SUBSET_STARS(r, ci, sparts, ind, scount); -} - -/** - * @brief Determine which version of DOPAIR1_SUBSET_STARS needs to be called - * depending on the orientation of the cells or whether DOPAIR1_SUBSET_STARS - * needs to be called at all. - * - * @param r The #runner. - * @param ci The first #cell. - * @param sparts_i The #spart to interact with @c cj. - * @param ind The list of indices of particles in @c ci to interact with. - * @param scount The number of particles in @c ind. - * @param cj The second #cell. - */ void DOPAIR1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, struct spart *restrict sparts_i, int *restrict ind, int scount, - 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]; - } - -#ifdef SWIFT_USE_NAIVE_INTERACTIONS_STARS - DOPAIR1_SUBSET_STARS_NAIVE(r, ci, sparts_i, ind, scount, cj, shift); -#else - /* Get the sorting index. */ - int sid = 0; - for (int k = 0; k < 3; k++) - sid = 3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0) - ? 0 - : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2 : 1); - - /* Switch the cells around? */ - const int flipped = runner_flip[sid]; - sid = sortlistID[sid]; - - /* Has the cell cj been sorted? */ - if (!(cj->hydro.sorted & (1 << sid)) || - cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin) - error("Interacting unsorted cells."); - - DOPAIR1_SUBSET_STARS(r, ci, sparts_i, ind, scount, cj, sid, flipped, shift); -#endif -} + struct cell *restrict cj); void DOSUB_SUBSET_STARS(struct runner *r, struct cell *ci, struct spart *sparts, - int *ind, int scount, struct cell *cj, int gettimer) { - - const struct engine *e = r->e; - struct space *s = e->s; - - /* Should we even bother? */ - if (!cell_is_active_stars(ci, e) && - (cj == NULL || !cell_is_active_stars(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 (&sparts[ind[0]] >= &ci->progeny[k]->stars.parts[0] && - &sparts[ind[0]] < - &ci->progeny[k]->stars.parts[ci->progeny[k]->stars.count]) { - sub = ci->progeny[k]; - break; - } - } - } - } - - /* Is this a single cell? */ - if (cj == NULL) { - - /* Recurse? */ - if (cell_can_recurse_in_self_stars_task(ci)) { - - /* Loop over all progeny. */ - DOSUB_SUBSET_STARS(r, sub, sparts, ind, scount, NULL, 0); - for (int j = 0; j < 8; j++) - if (ci->progeny[j] != sub && ci->progeny[j] != NULL) - DOSUB_SUBSET_STARS(r, sub, sparts, ind, scount, ci->progeny[j], 0); - - } - - /* Otherwise, compute self-interaction. */ - else - DOSELF1_SUBSET_BRANCH_STARS(r, ci, sparts, ind, scount); - } /* self-interaction. */ - - /* Otherwise, it's a pair interaction. */ - else { - - /* Recurse? */ - if (cell_can_recurse_in_pair_stars_task(ci, cj) && - cell_can_recurse_in_pair_stars_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_STARS(r, ci->progeny[pid], sparts, ind, scount, - cj->progeny[pjd], 0); - if (ci->progeny[pid] != NULL && cj->progeny[pjd] == sub) - DOSUB_SUBSET_STARS(r, cj->progeny[pjd], sparts, ind, scount, - ci->progeny[pid], 0); - } - } - - /* Otherwise, compute the pair directly. */ - else if (cell_is_active_stars(ci, e) && cj->hydro.count > 0) { - - /* Do any of the cells need to be drifted first? */ - if (cell_is_active_stars(ci, e)) { - if (!cell_are_spart_drifted(ci, e)) error("Cell should be drifted!"); - if (!cell_are_part_drifted(cj, e)) error("Cell should be drifted!"); - } - - DOPAIR1_SUBSET_BRANCH_STARS(r, ci, sparts, ind, scount, cj); - } - - } /* otherwise, pair interaction. */ -} - -/** - * @brief Determine which version of DOSELF1_STARS needs to be called depending - * on the optimisation level. - * - * @param r #runner - * @param c #cell c - * - */ -void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c) { - - const struct engine *restrict e = r->e; - - /* Anything to do here? */ - if (c->stars.count == 0) return; - - /* Anything to do here? */ - if (!cell_is_active_stars(c, e)) return; - - /* Did we mess up the recursion? */ - if (c->stars.h_max_old * kernel_gamma > c->dmin) - error("Cell smaller than smoothing length"); - - DOSELF1_STARS(r, c, 1); -} - -#define RUNNER_CHECK_SORT(TYPE, PART, cj, ci, sid) \ - ({ \ - const struct sort_entry *restrict sort_j = cj->TYPE.sort[sid]; \ - \ - for (int pjd = 0; pjd < cj->TYPE.count; pjd++) { \ - const struct PART *p = &cj->TYPE.parts[sort_j[pjd].i]; \ - if (PART##_is_inhibited(p, e)) continue; \ - \ - const float d = p->x[0] * runner_shift[sid][0] + \ - p->x[1] * runner_shift[sid][1] + \ - p->x[2] * runner_shift[sid][2]; \ - if ((fabsf(d - sort_j[pjd].d) - cj->TYPE.dx_max_sort) > \ - 1.0e-4 * max(fabsf(d), cj->TYPE.dx_max_sort_old) && \ - (fabsf(d - sort_j[pjd].d) - cj->TYPE.dx_max_sort) > \ - cj->width[0] * 1.0e-10) \ - error( \ - "particle shift diff exceeds dx_max_sort in cell cj. " \ - "cj->nodeID=%d " \ - "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->" #TYPE \ - ".dx_max_sort=%e " \ - "cj->" #TYPE \ - ".dx_max_sort_old=%e, cellID=%i super->cellID=%i" \ - "cj->depth=%d cj->maxdepth=%d", \ - cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->TYPE.dx_max_sort, \ - cj->TYPE.dx_max_sort_old, cj->cellID, cj->hydro.super->cellID, \ - cj->depth, cj->maxdepth); \ - } \ - }) - -/** - * @brief Determine which version of DOPAIR1_STARS needs to be called depending - * on the orientation of the cells or whether DOPAIR1_STARS needs to be called - * at all. - * - * @param r #runner - * @param ci #cell ci - * @param cj #cell cj - * - */ -void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj) { - - const struct engine *restrict e = r->e; - - /* Get the sort ID. */ - double shift[3] = {0.0, 0.0, 0.0}; - const int sid = space_getsid(e->s, &ci, &cj, shift); - - const int ci_active = cell_is_active_stars(ci, e); - const int cj_active = cell_is_active_stars(cj, e); -#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) - const int do_ci_stars = ci->nodeID == e->nodeID; - const int do_cj_stars = cj->nodeID == e->nodeID; -#else - /* here we are updating the hydro -> switch ci, cj */ - const int do_ci_stars = cj->nodeID == e->nodeID; - const int do_cj_stars = ci->nodeID == e->nodeID; -#endif - const int do_ci = (ci->stars.count != 0 && cj->hydro.count != 0 && - ci_active && do_ci_stars); - const int do_cj = (cj->stars.count != 0 && ci->hydro.count != 0 && - cj_active && do_cj_stars); - - /* Anything to do here? */ - if (!do_ci && !do_cj) return; - - /* Check that cells are drifted. */ - if (do_ci && - (!cell_are_spart_drifted(ci, e) || !cell_are_part_drifted(cj, e))) - error("Interacting undrifted cells."); - - /* Have the cells been sorted? */ - if (do_ci && (!(ci->stars.sorted & (1 << sid)) || - ci->stars.dx_max_sort_old > space_maxreldx * ci->dmin)) - error("Interacting unsorted cells."); - - if (do_ci && (!(cj->hydro.sorted & (1 << sid)) || - cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)) - error("Interacting unsorted cells."); - - if (do_cj && - (!cell_are_part_drifted(ci, e) || !cell_are_spart_drifted(cj, e))) - error("Interacting undrifted cells."); - - /* Have the cells been sorted? */ - if (do_cj && (!(ci->hydro.sorted & (1 << sid)) || - ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin)) - error("Interacting unsorted cells."); - - if (do_cj && (!(cj->stars.sorted & (1 << sid)) || - cj->stars.dx_max_sort_old > space_maxreldx * cj->dmin)) - error("Interacting unsorted cells."); - -#ifdef SWIFT_DEBUG_CHECKS - if (do_ci) { - // MATTHIEU: This test is faulty. To be fixed... - // RUNNER_CHECK_SORT(hydro, part, cj, ci, sid); - RUNNER_CHECK_SORT(stars, spart, ci, cj, sid); - } - - if (do_cj) { - // MATTHIEU: This test is faulty. To be fixed... - // RUNNER_CHECK_SORT(hydro, part, ci, cj, sid); - RUNNER_CHECK_SORT(stars, spart, cj, ci, sid); - } -#endif /* SWIFT_DEBUG_CHECKS */ - -#ifdef SWIFT_USE_NAIVE_INTERACTIONS_STARS - DOPAIR1_STARS_NAIVE(r, ci, cj, 1); -#else - DO_SYM_PAIR1_STARS(r, ci, cj, sid, shift); -#endif -} - -/** - * @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_STARS(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->stars.count != 0 && cj->hydro.count != 0 && - cell_is_active_stars(ci, e); - const int should_do_cj = cj->stars.count != 0 && ci->hydro.count != 0 && - cell_is_active_stars(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_stars_task(ci, cj) && - cell_can_recurse_in_pair_stars_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_STARS(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_stars = ci->nodeID == e->nodeID; - const int do_cj_stars = cj->nodeID == e->nodeID; -#else - /* here we are updating the hydro -> switch ci, cj */ - const int do_ci_stars = cj->nodeID == e->nodeID; - const int do_cj_stars = ci->nodeID == e->nodeID; -#endif - const int do_ci = ci->stars.count != 0 && cj->hydro.count != 0 && - cell_is_active_stars(ci, e) && do_ci_stars; - const int do_cj = cj->stars.count != 0 && ci->hydro.count != 0 && - cell_is_active_stars(cj, e) && do_cj_stars; - - if (do_ci) { - - /* Make sure both cells are drifted to the current timestep. */ - if (!cell_are_spart_drifted(ci, e)) - error("Interacting undrifted cells (sparts)."); - - if (!cell_are_part_drifted(cj, e)) - error("Interacting undrifted cells (parts)."); - - /* Do any of the cells need to be sorted first? */ - if (!(ci->stars.sorted & (1 << sid)) || - ci->stars.dx_max_sort_old > ci->dmin * space_maxreldx) { - error("Interacting unsorted cell (sparts)."); - } - - if (!(cj->hydro.sorted & (1 << sid)) || - cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx) - error("Interacting unsorted cell (parts). %i", cj->nodeID); - } - - 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_spart_drifted(cj, e)) - error("Interacting undrifted cells (sparts)."); - - /* Do any of the cells need to be sorted first? */ - if (!(ci->hydro.sorted & (1 << sid)) || - ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx) { - error("Interacting unsorted cell (parts)."); - } - - if (!(cj->stars.sorted & (1 << sid)) || - cj->stars.dx_max_sort_old > cj->dmin * space_maxreldx) { - error("Interacting unsorted cell (sparts)."); - } - } - - if (do_ci || do_cj) DOPAIR1_BRANCH_STARS(r, ci, cj); - } - - TIMER_TOC(TIMER_DOSUB_PAIR_STARS); -} - -/** - * @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_STARS(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->stars.count == 0 || - !cell_is_active_stars(ci, r->e)) - return; - - /* Recurse? */ - if (cell_can_recurse_in_self_stars_task(ci)) { - - /* Loop over all progeny. */ - for (int k = 0; k < 8; k++) - if (ci->progeny[k] != NULL) { - DOSUB_SELF1_STARS(r, ci->progeny[k], 0); - for (int j = k + 1; j < 8; j++) - if (ci->progeny[j] != NULL) - DOSUB_PAIR1_STARS(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_spart_drifted(ci, r->e)) error("Interacting undrifted cell."); - - DOSELF1_BRANCH_STARS(r, ci); - } - - TIMER_TOC(TIMER_DOSUB_SELF_STARS); -} + int *ind, int scount, struct cell *cj, int gettimer); diff --git a/src/runner_drift.c b/src/runner_drift.c new file mode 100644 index 0000000000000000000000000000000000000000..8c4376743cd50ffea4709cb471959864cedcc4b7 --- /dev/null +++ b/src/runner_drift.c @@ -0,0 +1,96 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "active.h" +#include "cell.h" +#include "engine.h" +#include "timers.h" + +/** + * @brief Drift all part in a cell. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_drift_part(struct runner *r, struct cell *c, int timer) { + + TIMER_TIC; + + cell_drift_part(c, r->e, 0); + + if (timer) TIMER_TOC(timer_drift_part); +} + +/** + * @brief Drift all gpart in a cell. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer) { + + TIMER_TIC; + + cell_drift_gpart(c, r->e, 0); + + if (timer) TIMER_TOC(timer_drift_gpart); +} + +/** + * @brief Drift all spart in a cell. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_drift_spart(struct runner *r, struct cell *c, int timer) { + + TIMER_TIC; + + cell_drift_spart(c, r->e, 0); + + if (timer) TIMER_TOC(timer_drift_spart); +} + +/** + * @brief Drift all bpart in a cell. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_drift_bpart(struct runner *r, struct cell *c, int timer) { + + TIMER_TIC; + + cell_drift_bpart(c, r->e, 0); + + if (timer) TIMER_TOC(timer_drift_bpart); +} diff --git a/src/runner_ghost.c b/src/runner_ghost.c new file mode 100644 index 0000000000000000000000000000000000000000..2c1e8cd7190858014f7914e293b5ffdadbdc2707 --- /dev/null +++ b/src/runner_ghost.c @@ -0,0 +1,1355 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "active.h" +#include "black_holes.h" +#include "cell.h" +#include "engine.h" +#include "feedback.h" +#include "pressure_floor.h" +#include "pressure_floor_iact.h" +#include "space_getsid.h" +#include "stars.h" +#include "timers.h" +#include "tracers.h" + +/* Import the density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + +/* Import the stars density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_stars.h" +#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 + +/** + * @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_stars_ghost(struct runner *r, struct cell *c, int timer) { + + struct spart *restrict sparts = c->stars.parts; + const struct engine *e = r->e; + const struct unit_system *us = e->internal_units; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology *cosmo = e->cosmology; + const struct feedback_props *feedback_props = e->feedback_props; + const float stars_h_max = e->hydro_properties->h_max; + const float stars_h_min = e->hydro_properties->h_min; + const float eps = e->stars_properties->h_tolerance; + const float stars_eta_dim = + pow_dimension(e->stars_properties->eta_neighbours); + const int max_smoothing_iter = e->stars_properties->max_smoothing_iterations; + int redo = 0, scount = 0; + + /* Running value of the maximal smoothing length */ + double h_max = c->stars.h_max; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != e->nodeID) + error("Running the star ghost on a foreign node!"); +#endif + + /* Anything to do here? */ + if (c->stars.count == 0) return; + if (!cell_is_active_stars(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + runner_do_stars_ghost(r, c->progeny[k], 0); + + /* Update h_max */ + h_max = max(h_max, c->progeny[k]->stars.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->stars.count)) == NULL) + error("Can't allocate memory for sid."); + if ((h_0 = (float *)malloc(sizeof(float) * c->stars.count)) == NULL) + error("Can't allocate memory for h_0."); + if ((left = (float *)malloc(sizeof(float) * c->stars.count)) == NULL) + error("Can't allocate memory for left."); + if ((right = (float *)malloc(sizeof(float) * c->stars.count)) == NULL) + error("Can't allocate memory for right."); + for (int k = 0; k < c->stars.count; k++) + if (spart_is_active(&sparts[k], e) && + feedback_is_active(&sparts[k], e->time, cosmo, with_cosmology)) { + sid[scount] = k; + h_0[scount] = sparts[k].h; + left[scount] = 0.f; + right[scount] = stars_h_max; + ++scount; + } + + /* While there are particles that need to be updated... */ + for (int num_reruns = 0; scount > 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 < scount; i++) { + + /* Get a direct pointer on the part. */ + struct spart *sp = &sparts[sid[i]]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Is this part within the timestep? */ + if (!spart_is_active(sp, e)) + error("Ghost applied to inactive particle"); +#endif + + /* Get some useful values */ + const float h_init = h_0[i]; + const float h_old = sp->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 (sp->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 */ + stars_end_density(sp, cosmo); + + /* Compute one step of the Newton-Raphson scheme */ + const float n_sum = sp->density.wcount * h_old_dim; + const float n_target = stars_eta_dim; + const float f = n_sum - n_target; + const float f_prime = + sp->density.wcount_dh * h_old_dim + + hydro_dimension * sp->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 (((sp->h >= stars_h_max) && (f < 0.f)) || + ((sp->h <= stars_h_min) && (f > 0.f))) { + + stars_reset_feedback(sp); + + /* Only do feedback if stars have a reasonable birth time */ + if (feedback_do_feedback(sp)) { + + const integertime_t ti_step = get_integer_timestep(sp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current - 1, sp->time_bin); + + /* Get particle time-step */ + double dt; + if (with_cosmology) { + dt = cosmology_get_delta_time(e->cosmology, ti_begin, + ti_begin + ti_step); + } else { + dt = get_timestep(sp->time_bin, e->time_base); + } + + /* Calculate age of the star at current time */ + double star_age_end_of_step; + if (with_cosmology) { + star_age_end_of_step = + cosmology_get_delta_time_from_scale_factors( + cosmo, (double)sp->birth_scale_factor, cosmo->a); + } else { + star_age_end_of_step = (float)e->time - sp->birth_time; + } + + /* Has this star been around for a while ? */ + if (star_age_end_of_step > 0.) { + + /* Age of the star at the start of the step */ + const double star_age_beg_of_step = + max(star_age_end_of_step - dt, 0.); + + /* Compute the stellar evolution */ + feedback_evolve_spart(sp, feedback_props, cosmo, us, + star_age_beg_of_step, dt); + } else { + + /* Reset the feedback fields of the star particle */ + feedback_reset_feedback(sp, feedback_props); + } + } else { + + feedback_reset_feedback(sp, feedback_props); + } + + /* 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, sp->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 */ + sp->h = pow_inv_dimension( + 0.5f * (pow_dimension(left[i]) + pow_dimension(right[i]))); + + } else { + + /* Normal case */ + sp->h = h_new; + } + + /* If below the absolute maximum, try again */ + if (sp->h < stars_h_max && sp->h > stars_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 */ + stars_init_spart(sp); + feedback_init_spart(sp); + + /* Off we go ! */ + continue; + + } else if (sp->h <= stars_h_min) { + + /* Ok, this particle is a lost cause... */ + sp->h = stars_h_min; + + } else if (sp->h >= stars_h_max) { + + /* Ok, this particle is a lost cause... */ + sp->h = stars_h_max; + + /* Do some damage control if no neighbours at all were found */ + if (has_no_neighbours) { + stars_spart_has_no_neighbours(sp, 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, sp->h); + + stars_reset_feedback(sp); + + /* Only do feedback if stars have a reasonable birth time */ + if (feedback_do_feedback(sp)) { + + const integertime_t ti_step = get_integer_timestep(sp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current - 1, sp->time_bin); + + /* Get particle time-step */ + double dt; + if (with_cosmology) { + dt = cosmology_get_delta_time(e->cosmology, ti_begin, + ti_begin + ti_step); + } else { + dt = get_timestep(sp->time_bin, e->time_base); + } + + /* Calculate age of the star at current time */ + double star_age_end_of_step; + if (with_cosmology) { + star_age_end_of_step = cosmology_get_delta_time_from_scale_factors( + cosmo, sp->birth_scale_factor, (float)cosmo->a); + } else { + star_age_end_of_step = (float)e->time - sp->birth_time; + } + + /* Has this star been around for a while ? */ + if (star_age_end_of_step > 0.) { + + /* Age of the star at the start of the step */ + const double star_age_beg_of_step = + max(star_age_end_of_step - dt, 0.); + + /* Compute the stellar evolution */ + feedback_evolve_spart(sp, feedback_props, cosmo, us, + star_age_beg_of_step, dt); + } else { + + /* Reset the feedback fields of the star particle */ + feedback_reset_feedback(sp, feedback_props); + } + } else { + + /* Reset the feedback fields of the star particle */ + feedback_reset_feedback(sp, feedback_props); + } + } + + /* We now need to treat the particles whose smoothing length had not + * converged again */ + + /* Re-set the counter for the next loop (potentially). */ + scount = redo; + if (scount > 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->stars.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_stars_density(r, finger, sparts, sid, + scount); + + /* Otherwise, pair interaction? */ + else if (l->t->type == task_type_pair) { + + /* Left or right? */ + if (l->t->ci == finger) + runner_dopair_subset_branch_stars_density( + r, finger, sparts, sid, scount, l->t->cj); + else + runner_dopair_subset_branch_stars_density( + r, finger, sparts, sid, scount, l->t->ci); + } + + /* Otherwise, sub-self interaction? */ + else if (l->t->type == task_type_sub_self) + runner_dosub_subset_stars_density(r, finger, sparts, sid, scount, + 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_stars_density(r, finger, sparts, sid, + scount, l->t->cj, 1); + else + runner_dosub_subset_stars_density(r, finger, sparts, sid, + scount, l->t->ci, 1); + } + } + } + } + } + + if (scount) { + error("Smoothing length failed to converge on %i particles.", scount); + } + + /* Be clean */ + free(left); + free(right); + free(sid); + free(h_0); + } + + /* Update h_max */ + c->stars.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->stars.ghost) { + for (struct cell *tmp = c->parent; tmp != NULL; tmp = tmp->parent) { + atomic_max_d(&tmp->stars.h_max, h_max); + } + } + + 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_density_ghost(struct runner *r, struct cell *c, + int timer) { + + struct bpart *restrict bparts = c->black_holes.parts; + const struct engine *e = r->e; + 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_density_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))) { + + 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 */ + + black_holes_reset_feedback(bp); + + /* Check if h_max has increased */ + h_max = max(h_max, bp->h); + } + + /* 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.density_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 Intermediate task after the BHs have done their swallowing step. + * This is used to update the BH quantities if necessary. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_black_holes_swallow_ghost(struct runner *r, struct cell *c, + int timer) { + + struct bpart *restrict bparts = c->black_holes.parts; + const int count = c->black_holes.count; + const struct engine *e = r->e; + const int with_cosmology = e->policy & engine_policy_cosmology; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_hydro(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) + runner_do_black_holes_swallow_ghost(r, c->progeny[k], 0); + } else { + + /* Loop over the parts in this cell. */ + for (int i = 0; i < count; i++) { + + /* Get a direct pointer on the part. */ + struct bpart *bp = &bparts[i]; + + if (bpart_is_active(bp, e)) { + + /* Compute the final operations for repositioning of this BH */ + black_holes_end_reposition(bp, e->black_holes_properties, + e->physical_constants, e->cosmology); + + /* 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); + } + } + } + + if (timer) TIMER_TOC(timer_do_black_holes_ghost); +} + +/** + * @brief Intermediate task after the gradient loop that does final operations + * on the gradient quantities and optionally slope limits the gradients + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) { + +#ifdef EXTRA_HYDRO_LOOP + + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + const int count = c->hydro.count; + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const double time_base = e->time_base; + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_hydro(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k], 0); + } else { + + /* Loop over the parts in this cell. */ + for (int i = 0; i < count; i++) { + + /* Get a direct pointer on the part. */ + struct part *restrict p = &parts[i]; + struct xpart *restrict xp = &xparts[i]; + + if (part_is_active(p, e)) { + + /* Finish the gradient calculation */ + hydro_end_gradient(p); + + /* As of here, particle force variables will be set. */ + + /* Calculate the time-step for passing to hydro_prepare_force. + * This is the physical time between the start and end of the time-step + * without any scale-factor powers. */ + double dt_alpha; + + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_alpha = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + } else { + dt_alpha = get_timestep(p->time_bin, time_base); + } + + /* Compute variables required for the force loop */ + hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); + + /* The particle force values are now set. Do _NOT_ + try to read any particle density variables! */ + + /* Prepare the particle for the force loop over neighbours */ + hydro_reset_acceleration(p); + } + } + } + + if (timer) TIMER_TOC(timer_do_extra_ghost); + +#else + error("SWIFT was not compiled with the extra hydro loop activated."); +#endif +} + +/** + * @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_ghost(struct runner *r, struct cell *c, int timer) { + + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + const struct engine *e = r->e; + const struct space *s = e->s; + const struct hydro_space *hs = &s->hs; + const struct cosmology *cosmo = e->cosmology; + const struct chemistry_global_data *chemistry = e->chemistry; + + const int with_cosmology = (e->policy & engine_policy_cosmology); + + const float hydro_h_max = e->hydro_properties->h_max; + const float hydro_h_min = e->hydro_properties->h_min; + const float eps = e->hydro_properties->h_tolerance; + const float hydro_eta_dim = + pow_dimension(e->hydro_properties->eta_neighbours); + const int max_smoothing_iter = e->hydro_properties->max_smoothing_iterations; + int redo = 0, count = 0; + + /* Running value of the maximal smoothing length */ + double h_max = c->hydro.h_max; + + TIMER_TIC; + + /* Anything to do here? */ + if (c->hydro.count == 0) return; + if (!cell_is_active_hydro(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + runner_do_ghost(r, c->progeny[k], 0); + + /* Update h_max */ + h_max = max(h_max, c->progeny[k]->hydro.h_max); + } + } + } else { + + /* Init the list of active particles that have to be updated and their + * current smoothing lengths. */ + int *pid = NULL; + float *h_0 = NULL; + float *left = NULL; + float *right = NULL; + if ((pid = (int *)malloc(sizeof(int) * c->hydro.count)) == NULL) + error("Can't allocate memory for pid."); + if ((h_0 = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) + error("Can't allocate memory for h_0."); + if ((left = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) + error("Can't allocate memory for left."); + if ((right = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) + error("Can't allocate memory for right."); + for (int k = 0; k < c->hydro.count; k++) + if (part_is_active(&parts[k], e)) { + pid[count] = k; + h_0[count] = parts[k].h; + left[count] = 0.f; + right[count] = hydro_h_max; + ++count; + } + + /* While there are particles that need to be updated... */ + for (int num_reruns = 0; count > 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 < count; i++) { + + /* Get a direct pointer on the part. */ + struct part *p = &parts[pid[i]]; + struct xpart *xp = &xparts[pid[i]]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Is this part within the timestep? */ + if (!part_is_active(p, e)) error("Ghost applied to inactive particle"); +#endif + + /* Get some useful values */ + const float h_init = h_0[i]; + const float h_old = p->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 (p->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 */ + hydro_end_density(p, cosmo); + chemistry_end_density(p, chemistry, cosmo); + pressure_floor_end_density(p, cosmo); + + /* Compute one step of the Newton-Raphson scheme */ + const float n_sum = p->density.wcount * h_old_dim; + const float n_target = hydro_eta_dim; + const float f = n_sum - n_target; + const float f_prime = + p->density.wcount_dh * h_old_dim + + hydro_dimension * p->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 (((p->h >= hydro_h_max) && (f < 0.f)) || + ((p->h <= hydro_h_min) && (f > 0.f))) { + + /* We have a particle whose smoothing length is already set (wants + * to be larger but has already hit the maximum OR wants to be + * smaller but has already reached the minimum). So, just tidy up + * as if the smoothing length had converged correctly */ + +#ifdef EXTRA_HYDRO_LOOP + + /* As of here, particle gradient variables will be set. */ + /* The force variables are set in the extra ghost. */ + + /* Compute variables required for the gradient loop */ + hydro_prepare_gradient(p, xp, cosmo); + + /* The particle gradient values are now set. Do _NOT_ + try to read any particle density variables! */ + + /* Prepare the particle for the gradient loop over neighbours + */ + hydro_reset_gradient(p); + +#else + const struct hydro_props *hydro_props = e->hydro_properties; + + /* Calculate the time-step for passing to hydro_prepare_force, used + * for the evolution of alpha factors (i.e. those involved in the + * artificial viscosity and thermal conduction terms) */ + const double time_base = e->time_base; + const integertime_t ti_current = e->ti_current; + double dt_alpha; + + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_alpha = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + } else { + dt_alpha = get_timestep(p->time_bin, time_base); + } + + /* As of here, particle force variables will be set. */ + + /* Compute variables required for the force loop */ + hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); + + /* The particle force values are now set. Do _NOT_ + try to read any particle density variables! */ + + /* Prepare the particle for the force loop over neighbours */ + hydro_reset_acceleration(p); + +#endif /* EXTRA_HYDRO_LOOP */ + + /* 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, p->id, h_init, h_old, h_new, f, f_prime, n_sum, + n_target, left[i], right[i]); + } + +#ifdef SWIFT_DEBUG_CHECKS + if ((f > 0.f && h_new > h_old) || (f < 0.f && h_new < h_old)) + error( + "Smoothing length correction not going in the right direction"); +#endif + + /* 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 */ + p->h = pow_inv_dimension( + 0.5f * (pow_dimension(left[i]) + pow_dimension(right[i]))); + + } else { + + /* Normal case */ + p->h = h_new; + } + + /* If within the allowed range, try again */ + if (p->h < hydro_h_max && p->h > hydro_h_min) { + + /* Flag for another round of fun */ + pid[redo] = pid[i]; + h_0[redo] = h_0[i]; + left[redo] = left[i]; + right[redo] = right[i]; + redo += 1; + + /* Re-initialise everything */ + hydro_init_part(p, hs); + chemistry_init_part(p, chemistry); + pressure_floor_init_part(p, xp); + tracers_after_init(p, xp, e->internal_units, e->physical_constants, + with_cosmology, e->cosmology, + e->hydro_properties, e->cooling_func, e->time); + + /* Off we go ! */ + continue; + + } else if (p->h <= hydro_h_min) { + + /* Ok, this particle is a lost cause... */ + p->h = hydro_h_min; + + } else if (p->h >= hydro_h_max) { + + /* Ok, this particle is a lost cause... */ + p->h = hydro_h_max; + + /* Do some damage control if no neighbours at all were found */ + if (has_no_neighbours) { + hydro_part_has_no_neighbours(p, xp, cosmo); + chemistry_part_has_no_neighbours(p, xp, chemistry, cosmo); + pressure_floor_part_has_no_neighbours(p, xp, 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 is increased */ + h_max = max(h_max, p->h); + +#ifdef EXTRA_HYDRO_LOOP + + /* As of here, particle gradient variables will be set. */ + /* The force variables are set in the extra ghost. */ + + /* Compute variables required for the gradient loop */ + hydro_prepare_gradient(p, xp, cosmo); + + /* The particle gradient values are now set. Do _NOT_ + try to read any particle density variables! */ + + /* Prepare the particle for the gradient loop over neighbours */ + hydro_reset_gradient(p); + +#else + const struct hydro_props *hydro_props = e->hydro_properties; + + /* Calculate the time-step for passing to hydro_prepare_force, used + * for the evolution of alpha factors (i.e. those involved in the + * artificial viscosity and thermal conduction terms) */ + const double time_base = e->time_base; + const integertime_t ti_current = e->ti_current; + double dt_alpha; + + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_alpha = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + } else { + dt_alpha = get_timestep(p->time_bin, time_base); + } + + /* As of here, particle force variables will be set. */ + + /* Compute variables required for the force loop */ + hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); + + /* The particle force values are now set. Do _NOT_ + try to read any particle density variables! */ + + /* Prepare the particle for the force loop over neighbours */ + hydro_reset_acceleration(p); + +#endif /* EXTRA_HYDRO_LOOP */ + } + + /* We now need to treat the particles whose smoothing length had not + * converged again */ + + /* Re-set the counter for the next loop (potentially). */ + count = redo; + if (count > 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->hydro.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_density(r, finger, parts, pid, count); + + /* Otherwise, pair interaction? */ + else if (l->t->type == task_type_pair) { + + /* Left or right? */ + if (l->t->ci == finger) + runner_dopair_subset_branch_density(r, finger, parts, pid, + count, l->t->cj); + else + runner_dopair_subset_branch_density(r, finger, parts, pid, + count, l->t->ci); + } + + /* Otherwise, sub-self interaction? */ + else if (l->t->type == task_type_sub_self) + runner_dosub_subset_density(r, finger, parts, pid, count, 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_density(r, finger, parts, pid, count, + l->t->cj, 1); + else + runner_dosub_subset_density(r, finger, parts, pid, count, + l->t->ci, 1); + } + } + } + } + } + + if (count) { + error("Smoothing length failed to converge on %i particles.", count); + } + + /* Be clean */ + free(left); + free(right); + free(pid); + free(h_0); + } + + /* Update h_max */ + c->hydro.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->hydro.ghost) { + for (struct cell *tmp = c->parent; tmp != NULL; tmp = tmp->parent) { + atomic_max_d(&tmp->hydro.h_max, h_max); + } + } + + if (timer) TIMER_TOC(timer_do_ghost); +} diff --git a/src/runner_main.c b/src/runner_main.c new file mode 100644 index 0000000000000000000000000000000000000000..a674b64ae671bf33df0b5ba9eaa951097d738ba9 --- /dev/null +++ b/src/runner_main.c @@ -0,0 +1,495 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* MPI headers. */ +#ifdef WITH_MPI +#include <mpi.h> +#endif + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "engine.h" +#include "scheduler.h" +#include "space_getsid.h" +#include "timers.h" + +/* Import the gravity loop functions. */ +#include "runner_doiact_grav.h" + +/* Import the density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + +/* Import the gradient loop functions (if required). */ +#ifdef EXTRA_HYDRO_LOOP +#define FUNCTION gradient +#define FUNCTION_TASK_LOOP TASK_LOOP_GRADIENT +#include "runner_doiact_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP +#endif + +/* Import the force loop functions. */ +#define FUNCTION force +#define FUNCTION_TASK_LOOP TASK_LOOP_FORCE +#include "runner_doiact_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + +/* Import the limiter loop functions. */ +#define FUNCTION limiter +#define FUNCTION_TASK_LOOP TASK_LOOP_LIMITER +#include "runner_doiact_hydro.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + +/* Import the stars density loop functions. */ +#define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY +#include "runner_doiact_stars.h" +#undef FUNCTION_TASK_LOOP +#undef FUNCTION + +/* Import the stars feedback loop functions. */ +#define FUNCTION feedback +#define FUNCTION_TASK_LOOP TASK_LOOP_FEEDBACK +#include "runner_doiact_stars.h" +#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 swallow +#define FUNCTION_TASK_LOOP TASK_LOOP_SWALLOW +#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 The #runner main thread routine. + * + * @param data A pointer to this thread's data. + */ +void *runner_main(void *data) { + + struct runner *r = (struct runner *)data; + struct engine *e = r->e; + struct scheduler *sched = &e->sched; + unsigned int seed = r->id; + pthread_setspecific(sched->local_seed_pointer, &seed); + /* Main loop. */ + while (1) { + + /* Wait at the barrier. */ + engine_barrier(e); + + /* Can we go home yet? */ + if (e->step_props & engine_step_prop_done) break; + + /* Re-set the pointer to the previous task, as there is none. */ + struct task *t = NULL; + struct task *prev = NULL; + + /* Loop while there are tasks... */ + while (1) { + + /* If there's no old task, try to get a new one. */ + if (t == NULL) { + + /* Get the task. */ + TIMER_TIC + t = scheduler_gettask(sched, r->qid, prev); + TIMER_TOC(timer_gettask); + + /* Did I get anything? */ + if (t == NULL) break; + } + + /* Get the cells. */ + struct cell *ci = t->ci; + struct cell *cj = t->cj; + +#ifdef SWIFT_DEBUG_TASKS + /* Mark the thread we run on */ + t->rid = r->cpuid; + + /* And recover the pair direction */ + if (t->type == task_type_pair || t->type == task_type_sub_pair) { + struct cell *ci_temp = ci; + struct cell *cj_temp = cj; + double shift[3]; + t->sid = space_getsid(e->s, &ci_temp, &cj_temp, shift); + } else { + t->sid = -1; + } +#endif + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that we haven't scheduled an inactive task */ + t->ti_run = e->ti_current; + /* Store the task that will be running (for debugging only) */ + r->t = t; +#endif + + /* Different types of tasks... */ + switch (t->type) { + case task_type_self: + if (t->subtype == task_subtype_density) + runner_doself1_branch_density(r, ci); +#ifdef EXTRA_HYDRO_LOOP + else if (t->subtype == task_subtype_gradient) + runner_doself1_branch_gradient(r, ci); +#endif + else if (t->subtype == task_subtype_force) + runner_doself2_branch_force(r, ci); + else if (t->subtype == task_subtype_limiter) + runner_doself2_branch_limiter(r, ci); + else if (t->subtype == task_subtype_grav) + runner_doself_recursive_grav(r, ci, 1); + else if (t->subtype == task_subtype_external_grav) + runner_do_grav_external(r, ci, 1); + else if (t->subtype == task_subtype_stars_density) + 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_swallow) + runner_doself_branch_bh_swallow(r, ci); + else if (t->subtype == task_subtype_do_gas_swallow) + runner_do_gas_swallow_self(r, ci, 1); + else if (t->subtype == task_subtype_do_bh_swallow) + runner_do_bh_swallow_self(r, ci, 1); + else if (t->subtype == task_subtype_bh_feedback) + runner_doself_branch_bh_feedback(r, ci); + else + error("Unknown/invalid task subtype (%s).", + subtaskID_names[t->subtype]); + break; + + case task_type_pair: + if (t->subtype == task_subtype_density) + runner_dopair1_branch_density(r, ci, cj); +#ifdef EXTRA_HYDRO_LOOP + else if (t->subtype == task_subtype_gradient) + runner_dopair1_branch_gradient(r, ci, cj); +#endif + else if (t->subtype == task_subtype_force) + runner_dopair2_branch_force(r, ci, cj); + else if (t->subtype == task_subtype_limiter) + runner_dopair2_branch_limiter(r, ci, cj); + else if (t->subtype == task_subtype_grav) + runner_dopair_recursive_grav(r, ci, cj, 1); + else if (t->subtype == task_subtype_stars_density) + 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_swallow) + runner_dopair_branch_bh_swallow(r, ci, cj); + else if (t->subtype == task_subtype_do_gas_swallow) + runner_do_gas_swallow_pair(r, ci, cj, 1); + else if (t->subtype == task_subtype_do_bh_swallow) + runner_do_bh_swallow_pair(r, ci, cj, 1); + else if (t->subtype == task_subtype_bh_feedback) + runner_dopair_branch_bh_feedback(r, ci, cj); + else + error("Unknown/invalid task subtype (%s/%s).", + taskID_names[t->type], subtaskID_names[t->subtype]); + break; + + case task_type_sub_self: + if (t->subtype == task_subtype_density) + runner_dosub_self1_density(r, ci, 1); +#ifdef EXTRA_HYDRO_LOOP + else if (t->subtype == task_subtype_gradient) + runner_dosub_self1_gradient(r, ci, 1); +#endif + else if (t->subtype == task_subtype_force) + runner_dosub_self2_force(r, ci, 1); + else if (t->subtype == task_subtype_limiter) + runner_dosub_self2_limiter(r, ci, 1); + else if (t->subtype == task_subtype_stars_density) + 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_swallow) + runner_dosub_self_bh_swallow(r, ci, 1); + else if (t->subtype == task_subtype_do_gas_swallow) + runner_do_gas_swallow_self(r, ci, 1); + else if (t->subtype == task_subtype_do_bh_swallow) + runner_do_bh_swallow_self(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 (%s/%s).", + taskID_names[t->type], subtaskID_names[t->subtype]); + break; + + case task_type_sub_pair: + if (t->subtype == task_subtype_density) + runner_dosub_pair1_density(r, ci, cj, 1); +#ifdef EXTRA_HYDRO_LOOP + else if (t->subtype == task_subtype_gradient) + runner_dosub_pair1_gradient(r, ci, cj, 1); +#endif + else if (t->subtype == task_subtype_force) + runner_dosub_pair2_force(r, ci, cj, 1); + else if (t->subtype == task_subtype_limiter) + runner_dosub_pair2_limiter(r, ci, cj, 1); + else if (t->subtype == task_subtype_stars_density) + 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_swallow) + runner_dosub_pair_bh_swallow(r, ci, cj, 1); + else if (t->subtype == task_subtype_do_gas_swallow) + runner_do_gas_swallow_pair(r, ci, cj, 1); + else if (t->subtype == task_subtype_do_bh_swallow) + runner_do_bh_swallow_pair(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 (%s/%s).", + taskID_names[t->type], subtaskID_names[t->subtype]); + break; + + case task_type_sort: + /* Cleanup only if any of the indices went stale. */ + runner_do_hydro_sort( + r, ci, t->flags, + ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, 1); + /* Reset the sort flags as our work here is done. */ + t->flags = 0; + break; + case task_type_stars_sort: + /* Cleanup only if any of the indices went stale. */ + runner_do_stars_sort( + r, ci, t->flags, + ci->stars.dx_max_sort_old > space_maxreldx * ci->dmin, 1); + /* Reset the sort flags as our work here is done. */ + t->flags = 0; + break; + case task_type_init_grav: + runner_do_init_grav(r, ci, 1); + break; + case task_type_ghost: + runner_do_ghost(r, ci, 1); + break; +#ifdef EXTRA_HYDRO_LOOP + case task_type_extra_ghost: + runner_do_extra_ghost(r, ci, 1); + break; +#endif + case task_type_stars_ghost: + runner_do_stars_ghost(r, ci, 1); + break; + case task_type_bh_density_ghost: + runner_do_black_holes_density_ghost(r, ci, 1); + break; + case task_type_bh_swallow_ghost3: + runner_do_black_holes_swallow_ghost(r, ci, 1); + break; + case task_type_drift_part: + runner_do_drift_part(r, ci, 1); + break; + case task_type_drift_spart: + runner_do_drift_spart(r, ci, 1); + break; + case task_type_drift_bpart: + runner_do_drift_bpart(r, ci, 1); + break; + case task_type_drift_gpart: + runner_do_drift_gpart(r, ci, 1); + break; + case task_type_kick1: + runner_do_kick1(r, ci, 1); + break; + case task_type_kick2: + runner_do_kick2(r, ci, 1); + break; + case task_type_end_hydro_force: + runner_do_end_hydro_force(r, ci, 1); + break; + case task_type_end_grav_force: + runner_do_end_grav_force(r, ci, 1); + break; + case task_type_logger: + runner_do_logger(r, ci, 1); + break; + case task_type_timestep: + runner_do_timestep(r, ci, 1); + break; + case task_type_timestep_limiter: + runner_do_limiter(r, ci, 0, 1); + break; +#ifdef WITH_MPI + case task_type_send: + if (t->subtype == task_subtype_tend_part) { + free(t->buff); + } else if (t->subtype == task_subtype_tend_gpart) { + free(t->buff); + } else if (t->subtype == task_subtype_tend_spart) { + free(t->buff); + } else if (t->subtype == task_subtype_tend_bpart) { + free(t->buff); + } else if (t->subtype == task_subtype_sf_counts) { + free(t->buff); + } else if (t->subtype == task_subtype_part_swallow) { + free(t->buff); + } else if (t->subtype == task_subtype_bpart_merger) { + free(t->buff); + } + break; + case task_type_recv: + if (t->subtype == task_subtype_tend_part) { + cell_unpack_end_step_hydro(ci, (struct pcell_step_hydro *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_tend_gpart) { + cell_unpack_end_step_grav(ci, (struct pcell_step_grav *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_tend_spart) { + cell_unpack_end_step_stars(ci, (struct pcell_step_stars *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_tend_bpart) { + cell_unpack_end_step_black_holes( + ci, (struct pcell_step_black_holes *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_sf_counts) { + cell_unpack_sf_counts(ci, (struct pcell_sf *)t->buff); + cell_clear_stars_sort_flags(ci, /*clear_unused_flags=*/0); + free(t->buff); + } else if (t->subtype == task_subtype_xv) { + runner_do_recv_part(r, ci, 1, 1); + } else if (t->subtype == task_subtype_rho) { + runner_do_recv_part(r, ci, 0, 1); + } else if (t->subtype == task_subtype_gradient) { + runner_do_recv_part(r, ci, 0, 1); + } else if (t->subtype == task_subtype_part_swallow) { + cell_unpack_part_swallow(ci, + (struct black_holes_part_data *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_bpart_merger) { + cell_unpack_bpart_swallow(ci, + (struct black_holes_bpart_data *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_limiter) { + runner_do_recv_part(r, ci, 0, 1); + } else if (t->subtype == task_subtype_gpart) { + 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_rho) { + runner_do_recv_bpart(r, ci, 1, 1); + } else if (t->subtype == task_subtype_bpart_swallow) { + runner_do_recv_bpart(r, ci, 0, 1); + } else if (t->subtype == task_subtype_bpart_feedback) { + runner_do_recv_bpart(r, ci, 0, 1); + } else if (t->subtype == task_subtype_multipole) { + cell_unpack_multipoles(ci, (struct gravity_tensors *)t->buff); + free(t->buff); + } else { + error("Unknown/invalid task subtype (%d).", t->subtype); + } + break; +#endif + case task_type_grav_down: + runner_do_grav_down(r, t->ci, 1); + break; + case task_type_grav_mesh: + runner_do_grav_mesh(r, t->ci, 1); + break; + case task_type_grav_long_range: + runner_do_grav_long_range(r, t->ci, 1); + break; + case task_type_grav_mm: + runner_dopair_grav_mm_progenies(r, t->flags, t->ci, t->cj); + break; + case task_type_cooling: + runner_do_cooling(r, t->ci, 1); + break; + case task_type_star_formation: + runner_do_star_formation(r, t->ci, 1); + break; + case task_type_stars_resort: + runner_do_stars_resort(r, t->ci, 1); + break; + case task_type_fof_self: + runner_do_fof_self(r, t->ci, 1); + break; + case task_type_fof_pair: + runner_do_fof_pair(r, t->ci, t->cj, 1); + break; + default: + error("Unknown/invalid task type (%d).", t->type); + } + +/* Mark that we have run this task on these cells */ +#ifdef SWIFT_DEBUG_CHECKS + if (ci != NULL) { + ci->tasks_executed[t->type]++; + ci->subtasks_executed[t->subtype]++; + } + if (cj != NULL) { + cj->tasks_executed[t->type]++; + cj->subtasks_executed[t->subtype]++; + } + + /* This runner is not doing a task anymore */ + r->t = NULL; +#endif + + /* We're done with this task, see if we get a next one. */ + prev = t; + t = scheduler_done(sched, t); + + } /* main loop. */ + } + + /* Be kind, rewind. */ + return NULL; +} diff --git a/src/runner_others.c b/src/runner_others.c new file mode 100644 index 0000000000000000000000000000000000000000..5ffaf7aa321f658b6e0e7e10a9cb8ad2f4a5a541 --- /dev/null +++ b/src/runner_others.c @@ -0,0 +1,660 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) + * 2016 John A. Regan (john.a.regan@durham.ac.uk) + * Tom Theuns (tom.theuns@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <float.h> +#include <limits.h> +#include <stdlib.h> + +/* MPI headers. */ +#ifdef WITH_MPI +#include <mpi.h> +#endif + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "active.h" +#include "cell.h" +#include "chemistry.h" +#include "cooling.h" +#include "engine.h" +#include "error.h" +#include "gravity.h" +#include "hydro.h" +#include "logger.h" +#include "pressure_floor.h" +#include "space.h" +#include "star_formation.h" +#include "star_formation_logger.h" +#include "stars.h" +#include "timers.h" +#include "tracers.h" + +/** + * @brief Calculate gravity acceleration from external potential + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_do_grav_external(struct runner *r, struct cell *c, int timer) { + + struct gpart *restrict gparts = c->grav.parts; + const int gcount = c->grav.count; + const struct engine *e = r->e; + const struct external_potential *potential = e->external_potential; + const struct phys_const *constants = e->physical_constants; + const double time = r->e->time; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_gravity(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_grav_external(r, c->progeny[k], 0); + } else { + + /* Loop over the gparts in this cell. */ + for (int i = 0; i < gcount; i++) { + + /* Get a direct pointer on the part. */ + struct gpart *restrict gp = &gparts[i]; + + /* Is this part within the time step? */ + if (gpart_is_active(gp, e)) { + external_gravity_acceleration(time, potential, constants, gp); + } + } + } + + if (timer) TIMER_TOC(timer_dograv_external); +} + +/** + * @brief Calculate gravity accelerations from the periodic mesh + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_do_grav_mesh(struct runner *r, struct cell *c, int timer) { + + struct gpart *restrict gparts = c->grav.parts; + const int gcount = c->grav.count; + const struct engine *e = r->e; + +#ifdef SWIFT_DEBUG_CHECKS + if (!e->s->periodic) error("Calling mesh forces in non-periodic mode."); +#endif + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_gravity(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_grav_mesh(r, c->progeny[k], 0); + } else { + + /* Get the forces from the gravity mesh */ + pm_mesh_interpolate_forces(e->mesh, e, gparts, gcount); + } + + if (timer) TIMER_TOC(timer_dograv_mesh); +} + +/** + * @brief Calculate change in thermal state of particles induced + * by radiative cooling and heating. + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_do_cooling(struct runner *r, struct cell *c, int timer) { + + const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cooling_function_data *cooling_func = e->cooling_func; + const struct phys_const *constants = e->physical_constants; + const struct unit_system *us = e->internal_units; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *entropy_floor_props = e->entropy_floor; + const double time_base = e->time_base; + const integertime_t ti_current = e->ti_current; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + const int count = c->hydro.count; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_hydro(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_cooling(r, c->progeny[k], 0); + } else { + + /* Loop over the parts in this cell. */ + for (int i = 0; i < count; i++) { + + /* Get a direct pointer on the part. */ + struct part *restrict p = &parts[i]; + struct xpart *restrict xp = &xparts[i]; + + if (part_is_active(p, e)) { + + double dt_cool, dt_therm; + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_cool = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + dt_therm = cosmology_get_therm_kick_factor(e->cosmology, ti_begin, + ti_begin + ti_step); + + } else { + dt_cool = get_timestep(p->time_bin, time_base); + dt_therm = get_timestep(p->time_bin, time_base); + } + + /* Let's cool ! */ + cooling_cool_part(constants, us, cosmo, hydro_props, + entropy_floor_props, cooling_func, p, xp, dt_cool, + dt_therm); + } + } + } + + if (timer) TIMER_TOC(timer_do_cooling); +} + +/** + * + */ +void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { + + struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + const struct star_formation *sf_props = e->star_formation; + const struct phys_const *phys_const = e->physical_constants; + const int count = c->hydro.count; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const int with_feedback = (e->policy & engine_policy_feedback); + const struct hydro_props *restrict hydro_props = e->hydro_properties; + const struct unit_system *restrict us = e->internal_units; + struct cooling_function_data *restrict cooling = e->cooling_func; + const struct entropy_floor_properties *entropy_floor = e->entropy_floor; + const double time_base = e->time_base; + const integertime_t ti_current = e->ti_current; + const int current_stars_count = c->stars.count; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != e->nodeID) + error("Running star formation task on a foreign node!"); +#endif + + /* Anything to do here? */ + if (c->hydro.count == 0 || !cell_is_active_hydro(c, e)) { + star_formation_logger_log_inactive_cell(&c->stars.sfh); + return; + } + + /* Reset the SFR */ + star_formation_logger_init(&c->stars.sfh); + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + /* Load the child cell */ + struct cell *restrict cp = c->progeny[k]; + + /* Do the recursion */ + runner_do_star_formation(r, cp, 0); + + /* Update current cell using child cells */ + star_formation_logger_add(&c->stars.sfh, &cp->stars.sfh); + } + } else { + + /* Loop over the gas particles in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + struct xpart *restrict xp = &xparts[k]; + + /* Only work on active particles */ + if (part_is_active(p, e)) { + + /* Is this particle star forming? */ + if (star_formation_is_star_forming(p, xp, sf_props, phys_const, cosmo, + hydro_props, us, cooling, + entropy_floor)) { + + /* Time-step size for this particle */ + double dt_star; + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_star = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + + } else { + dt_star = get_timestep(p->time_bin, time_base); + } + + /* Compute the SF rate of the particle */ + star_formation_compute_SFR(p, xp, sf_props, phys_const, cosmo, + dt_star); + + /* Add the SFR and SFR*dt to the SFH struct of this cell */ + star_formation_logger_log_active_part(p, xp, &c->stars.sfh, dt_star); + + /* Are we forming a star particle from this SF rate? */ + if (star_formation_should_convert_to_star(p, xp, sf_props, e, + dt_star)) { + + /* Convert the gas particle to a star particle */ + struct spart *sp = cell_convert_part_to_spart(e, c, p, xp); + + /* Did we get a star? (Or did we run out of spare ones?) */ + if (sp != NULL) { + + /* message("We formed a star id=%lld cellID=%d", sp->id, + * c->cellID); */ + + /* Copy the properties of the gas particle to the star particle */ + star_formation_copy_properties(p, xp, sp, e, sf_props, cosmo, + with_cosmology, phys_const, + hydro_props, us, cooling); + + /* Update the Star formation history */ + star_formation_logger_log_new_spart(sp, &c->stars.sfh); + } + } + + } else { /* Are we not star-forming? */ + + /* Update the particle to flag it as not star-forming */ + star_formation_update_part_not_SFR(p, xp, e, sf_props, + with_cosmology); + + } /* Not Star-forming? */ + + } else { /* is active? */ + + /* Check if the particle is not inhibited */ + if (!part_is_inhibited(p, e)) { + star_formation_logger_log_inactive_part(p, xp, &c->stars.sfh); + } + } + } /* Loop over particles */ + } + + /* If we formed any stars, the star sorts are now invalid. We need to + * re-compute them. */ + if (with_feedback && (c == c->top) && + (current_stars_count != c->stars.count)) { + cell_set_star_resort_flag(c); + } + + if (timer) TIMER_TOC(timer_do_star_formation); +} + +/** + * @brief End the hydro force calculation of all active particles in a cell + * by multiplying the acccelerations by the relevant constants + * + * @param r The #runner thread. + * @param c The #cell. + * @param timer Are we timing this ? + */ +void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer) { + + const struct engine *e = r->e; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_hydro(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_end_hydro_force(r, c->progeny[k], 0); + } else { + + const struct cosmology *cosmo = e->cosmology; + const int count = c->hydro.count; + struct part *restrict parts = c->hydro.parts; + + /* Loop over the gas particles in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + + if (part_is_active(p, e)) { + + /* Finish the force loop */ + hydro_end_force(p, cosmo); + chemistry_end_force(p, cosmo); + +#ifdef SWIFT_BOUNDARY_PARTICLES + + /* Get the ID of the part */ + const long long id = p->id; + + /* Cancel hdyro forces of these particles */ + if (id < SWIFT_BOUNDARY_PARTICLES) { + + /* Don't move ! */ + hydro_reset_acceleration(p); + +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + + /* Some values need to be reset in the Gizmo case. */ + hydro_prepare_force(p, &c->hydro.xparts[k], cosmo, + e->hydro_properties, 0); +#endif + } +#endif + } + } + } + + if (timer) TIMER_TOC(timer_end_hydro_force); +} + +/** + * @brief End the gravity force calculation of all active particles in a cell + * by multiplying the acccelerations by the relevant constants + * + * @param r The #runner thread. + * @param c The #cell. + * @param timer Are we timing this ? + */ +void runner_do_end_grav_force(struct runner *r, struct cell *c, int timer) { + + const struct engine *e = r->e; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_gravity(c, e)) return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_end_grav_force(r, c->progeny[k], 0); + } else { + + const struct space *s = e->s; + const int periodic = s->periodic; + const float G_newton = e->physical_constants->const_newton_G; + + /* Potential normalisation in the case of periodic gravity */ + float potential_normalisation = 0.; + if (periodic && (e->policy & engine_policy_self_gravity)) { + const double volume = s->dim[0] * s->dim[1] * s->dim[2]; + const double r_s = e->mesh->r_s; + potential_normalisation = 4. * M_PI * e->total_mass * r_s * r_s / volume; + } + + const int gcount = c->grav.count; + struct gpart *restrict gparts = c->grav.parts; + + /* Loop over the g-particles in this cell. */ + for (int k = 0; k < gcount; k++) { + + /* Get a handle on the gpart. */ + struct gpart *restrict gp = &gparts[k]; + + if (gpart_is_active(gp, e)) { + + /* Finish the force calculation */ + gravity_end_force(gp, G_newton, potential_normalisation, periodic); + +#ifdef SWIFT_MAKE_GRAVITY_GLASS + + /* Negate the gravity forces */ + gp->a_grav[0] *= -1.f; + gp->a_grav[1] *= -1.f; + gp->a_grav[2] *= -1.f; +#endif + +#ifdef SWIFT_NO_GRAVITY_BELOW_ID + + /* Get the ID of the gpart */ + long long id = 0; + if (gp->type == swift_type_gas) + id = e->s->parts[-gp->id_or_neg_offset].id; + else if (gp->type == swift_type_stars) + id = e->s->sparts[-gp->id_or_neg_offset].id; + else if (gp->type == swift_type_black_hole) + error("Unexisting type"); + else + id = gp->id_or_neg_offset; + + /* Cancel gravity forces of these particles */ + if (id < SWIFT_NO_GRAVITY_BELOW_ID) { + + /* Don't move ! */ + gp->a_grav[0] = 0.f; + gp->a_grav[1] = 0.f; + gp->a_grav[2] = 0.f; + } +#endif + +#ifdef SWIFT_DEBUG_CHECKS + if ((e->policy & engine_policy_self_gravity) && + !(e->policy & engine_policy_black_holes)) { + + /* Let's add a self interaction to simplify the count */ + gp->num_interacted++; + + /* Check that this gpart has interacted with all the other + * particles (via direct or multipoles) in the box */ + if (gp->num_interacted != + e->total_nr_gparts - e->count_inhibited_gparts) { + + /* Get the ID of the gpart */ + long long my_id = 0; + if (gp->type == swift_type_gas) + my_id = e->s->parts[-gp->id_or_neg_offset].id; + else if (gp->type == swift_type_stars) + my_id = e->s->sparts[-gp->id_or_neg_offset].id; + else if (gp->type == swift_type_black_hole) + error("Unexisting type"); + else + my_id = gp->id_or_neg_offset; + + error( + "g-particle (id=%lld, type=%s) did not interact " + "gravitationally with all other gparts " + "gp->num_interacted=%lld, total_gparts=%lld (local " + "num_gparts=%zd inhibited_gparts=%lld)", + my_id, part_type_names[gp->type], gp->num_interacted, + e->total_nr_gparts, e->s->nr_gparts, e->count_inhibited_gparts); + } + } +#endif + } + } + } + if (timer) TIMER_TOC(timer_end_grav_force); +} + +/** + * @brief Write the required particles through the logger. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_logger(struct runner *r, struct cell *c, int timer) { + +#ifdef WITH_LOGGER + TIMER_TIC; + + const struct engine *e = r->e; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + const int count = c->hydro.count; + + /* Anything to do here? */ + if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e)) return; + + /* Recurse? Avoid spending too much time in useless cells. */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_logger(r, c->progeny[k], 0); + } else { + + /* Loop over the parts in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + struct xpart *restrict xp = &xparts[k]; + + /* If particle needs to be log */ + /* This is the same function than part_is_active, except for + * debugging checks */ + if (part_is_active(p, e)) { + + if (logger_should_write(&xp->logger_data, e->logger)) { + /* Write particle */ + /* Currently writing everything, should adapt it through time */ + logger_log_part(e->logger, p, + logger_mask_data[logger_x].mask | + logger_mask_data[logger_v].mask | + logger_mask_data[logger_a].mask | + logger_mask_data[logger_u].mask | + logger_mask_data[logger_h].mask | + logger_mask_data[logger_rho].mask | + logger_mask_data[logger_consts].mask, + &xp->logger_data.last_offset); + + /* Set counter back to zero */ + xp->logger_data.steps_since_last_output = 0; + } else + /* Update counter */ + xp->logger_data.steps_since_last_output += 1; + } + } + } + + if (c->grav.count > 0) error("gparts not implemented"); + + if (c->stars.count > 0) error("sparts not implemented"); + + if (timer) TIMER_TOC(timer_logger); + +#else + error("Logger disabled, please enable it during configuration"); +#endif +} + +/** + * @brief Recursively search for FOF groups in a single cell. + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_do_fof_self(struct runner *r, struct cell *c, int timer) { + +#ifdef WITH_FOF + + TIMER_TIC; + + const struct engine *e = r->e; + struct space *s = e->s; + const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; + const int periodic = s->periodic; + const struct gpart *const gparts = s->gparts; + const double search_r2 = e->fof_properties->l_x2; + + rec_fof_search_self(e->fof_properties, dim, search_r2, periodic, gparts, c); + + if (timer) TIMER_TOC(timer_fof_self); + +#else + error("SWIFT was not compiled with FOF enabled!"); +#endif +} + +/** + * @brief Recursively search for FOF groups between a pair of cells. + * + * @param r runner task + * @param ci cell i + * @param cj cell j + * @param timer 1 if the time is to be recorded. + */ +void runner_do_fof_pair(struct runner *r, struct cell *ci, struct cell *cj, + int timer) { + +#ifdef WITH_FOF + + TIMER_TIC; + + const struct engine *e = r->e; + struct space *s = e->s; + const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; + const int periodic = s->periodic; + const struct gpart *const gparts = s->gparts; + const double search_r2 = e->fof_properties->l_x2; + + rec_fof_search_pair(e->fof_properties, dim, search_r2, periodic, gparts, ci, + cj); + + if (timer) TIMER_TOC(timer_fof_pair); +#else + error("SWIFT was not compiled with FOF enabled!"); +#endif +} diff --git a/src/runner_recv.c b/src/runner_recv.c new file mode 100644 index 0000000000000000000000000000000000000000..803e68c2106933684109e798e24952a0dbdfea6e --- /dev/null +++ b/src/runner_recv.c @@ -0,0 +1,368 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* MPI headers. */ +#ifdef WITH_MPI +#include <mpi.h> +#endif + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "engine.h" +#include "timers.h" + +/** + * @brief Construct the cell properties from the received #part. + * + * @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_part(struct runner *r, struct cell *c, int clear_sorts, + int timer) { +#ifdef WITH_MPI + + const struct part *restrict parts = c->hydro.parts; + const size_t nr_parts = c->hydro.count; + const integertime_t ti_current = r->e->ti_current; + + TIMER_TIC; + + integertime_t ti_hydro_end_min = max_nr_timesteps; + integertime_t ti_hydro_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 + + /* Clear this cell's sorted mask. */ + if (clear_sorts) c->hydro.sorted = 0; + + /* If this cell is a leaf, collect the particle data. */ + if (!c->split) { + + /* Collect everything... */ + for (size_t k = 0; k < nr_parts; k++) { + if (parts[k].time_bin == time_bin_inhibited) continue; + time_bin_min = min(time_bin_min, parts[k].time_bin); + time_bin_max = max(time_bin_max, parts[k].time_bin); + h_max = max(h_max, parts[k].h); + } + + /* Convert into a time */ + ti_hydro_end_min = get_integer_time_end(ti_current, time_bin_min); + ti_hydro_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]->hydro.count > 0) { + runner_do_recv_part(r, c->progeny[k], clear_sorts, 0); + ti_hydro_end_min = + min(ti_hydro_end_min, c->progeny[k]->hydro.ti_end_min); + ti_hydro_end_max = + max(ti_hydro_end_max, c->progeny[k]->hydro.ti_end_max); + h_max = max(h_max, c->progeny[k]->hydro.h_max); + } + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_hydro_end_min < ti_current) + error( + "Received a cell at an incorrect time c->ti_end_min=%lld, " + "e->ti_current=%lld.", + ti_hydro_end_min, ti_current); +#endif + + /* ... and store. */ + // c->hydro.ti_end_min = ti_hydro_end_min; + // c->hydro.ti_end_max = ti_hydro_end_max; + c->hydro.ti_old_part = ti_current; + c->hydro.h_max = h_max; + + if (timer) TIMER_TOC(timer_dorecv_part); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + +/** + * @brief Construct the cell properties from the received #gpart. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_recv_gpart(struct runner *r, struct cell *c, int timer) { + +#ifdef WITH_MPI + + const struct gpart *restrict gparts = c->grav.parts; + const size_t nr_gparts = c->grav.count; + const integertime_t ti_current = r->e->ti_current; + + TIMER_TIC; + + integertime_t ti_gravity_end_min = max_nr_timesteps; + integertime_t ti_gravity_end_max = 0; + timebin_t time_bin_min = num_time_bins; + timebin_t time_bin_max = 0; + +#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_gparts; k++) { + if (gparts[k].time_bin == time_bin_inhibited) continue; + time_bin_min = min(time_bin_min, gparts[k].time_bin); + time_bin_max = max(time_bin_max, gparts[k].time_bin); + } + + /* Convert into a time */ + ti_gravity_end_min = get_integer_time_end(ti_current, time_bin_min); + ti_gravity_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]->grav.count > 0) { + runner_do_recv_gpart(r, c->progeny[k], 0); + ti_gravity_end_min = + min(ti_gravity_end_min, c->progeny[k]->grav.ti_end_min); + ti_gravity_end_max = + max(ti_gravity_end_max, c->progeny[k]->grav.ti_end_max); + } + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_gravity_end_min < ti_current) + error( + "Received a cell at an incorrect time c->ti_end_min=%lld, " + "e->ti_current=%lld.", + ti_gravity_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->grav.ti_old_part = ti_current; + + if (timer) TIMER_TOC(timer_dorecv_gpart); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + +/** + * @brief Construct the cell properties from the received #spart. + * + * @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_spart(struct runner *r, struct cell *c, int clear_sorts, + int timer) { + +#ifdef WITH_MPI + + struct spart *restrict sparts = c->stars.parts; + const size_t nr_sparts = c->stars.count; + const integertime_t ti_current = r->e->ti_current; + + TIMER_TIC; + + integertime_t ti_stars_end_min = max_nr_timesteps; + integertime_t ti_stars_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 + + /* Clear this cell's sorted mask. */ + if (clear_sorts) c->stars.sorted = 0; + + /* If this cell is a leaf, collect the particle data. */ + if (!c->split) { + + /* Collect everything... */ + for (size_t k = 0; k < nr_sparts; k++) { +#ifdef DEBUG_INTERACTIONS_STARS + sparts[k].num_ngb_force = 0; +#endif + if (sparts[k].time_bin == time_bin_inhibited) continue; + time_bin_min = min(time_bin_min, sparts[k].time_bin); + time_bin_max = max(time_bin_max, sparts[k].time_bin); + h_max = max(h_max, sparts[k].h); + } + + /* Convert into a time */ + ti_stars_end_min = get_integer_time_end(ti_current, time_bin_min); + ti_stars_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]->stars.count > 0) { + runner_do_recv_spart(r, c->progeny[k], clear_sorts, 0); + ti_stars_end_min = + min(ti_stars_end_min, c->progeny[k]->stars.ti_end_min); + ti_stars_end_max = + max(ti_stars_end_max, c->progeny[k]->stars.ti_end_max); + h_max = max(h_max, c->progeny[k]->stars.h_max); + } + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_stars_end_min < ti_current && + !(r->e->policy & engine_policy_star_formation)) + error( + "Received a cell at an incorrect time c->ti_end_min=%lld, " + "e->ti_current=%lld.", + ti_stars_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->stars.ti_old_part = ti_current; + c->stars.h_max = h_max; + + if (timer) TIMER_TOC(timer_dorecv_spart); + +#else + error("SWIFT was not compiled with MPI support."); +#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 + + /* message("Receiving bparts id=%lld time_bin=%d", */ + /* bparts[k].id, bparts[k].time_bin); */ + + 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]->black_holes.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 +} diff --git a/src/runner_sort.c b/src/runner_sort.c new file mode 100644 index 0000000000000000000000000000000000000000..914b64f93b970000885b1b578d762d3f15455332 --- /dev/null +++ b/src/runner_sort.c @@ -0,0 +1,708 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "active.h" +#include "cell.h" +#include "engine.h" +#include "timers.h" + +/** + * @brief Sorts again all the stars in a given cell hierarchy. + * + * This is intended to be used after the star formation task has been run + * to get the cells back into a state where self/pair star tasks can be run. + * + * @param r The thread #runner. + * @param c The top-level cell to run on. + * @param timer Are we timing this? + */ +void runner_do_stars_resort(struct runner *r, struct cell *c, const int timer) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != r->e->nodeID) error("Task must be run locally!"); +#endif + + TIMER_TIC; + + /* Did we demand a recalculation of the stars'sorts? */ + if (cell_get_flag(c, cell_flag_do_stars_resort)) { + runner_do_all_stars_sort(r, c); + cell_clear_flag(c, cell_flag_do_stars_resort); + } + + if (timer) TIMER_TOC(timer_do_stars_resort); +} + +/** + * @brief Sort the entries in ascending order using QuickSort. + * + * @param sort The entries + * @param N The number of entries. + */ +void runner_do_sort_ascending(struct sort_entry *sort, int N) { + + struct { + short int lo, hi; + } qstack[10]; + int qpos, i, j, lo, hi, imin; + struct sort_entry temp; + float pivot; + + /* Sort parts in cell_i in decreasing order with quicksort */ + qstack[0].lo = 0; + qstack[0].hi = N - 1; + qpos = 0; + while (qpos >= 0) { + lo = qstack[qpos].lo; + hi = qstack[qpos].hi; + qpos -= 1; + if (hi - lo < 15) { + for (i = lo; i < hi; i++) { + imin = i; + for (j = i + 1; j <= hi; j++) + if (sort[j].d < sort[imin].d) imin = j; + if (imin != i) { + temp = sort[imin]; + sort[imin] = sort[i]; + sort[i] = temp; + } + } + } else { + pivot = sort[(lo + hi) / 2].d; + i = lo; + j = hi; + while (i <= j) { + while (sort[i].d < pivot) i++; + while (sort[j].d > pivot) j--; + if (i <= j) { + if (i < j) { + temp = sort[i]; + sort[i] = sort[j]; + sort[j] = temp; + } + i += 1; + j -= 1; + } + } + if (j > (lo + hi) / 2) { + if (lo < j) { + qpos += 1; + qstack[qpos].lo = lo; + qstack[qpos].hi = j; + } + if (i < hi) { + qpos += 1; + qstack[qpos].lo = i; + qstack[qpos].hi = hi; + } + } else { + if (i < hi) { + qpos += 1; + qstack[qpos].lo = i; + qstack[qpos].hi = hi; + } + if (lo < j) { + qpos += 1; + qstack[qpos].lo = lo; + qstack[qpos].hi = j; + } + } + } + } +} + +#ifdef SWIFT_DEBUG_CHECKS +/** + * @brief Recursively checks that the flags are consistent in a cell hierarchy. + * + * Debugging function. Exists in two flavours: hydro & stars. + */ +#define RUNNER_CHECK_SORTS(TYPE) \ + void runner_check_sorts_##TYPE(struct cell *c, int flags) { \ + \ + if (flags & ~c->TYPE.sorted) error("Inconsistent sort flags (downward)!"); \ + if (c->split) \ + for (int k = 0; k < 8; k++) \ + if (c->progeny[k] != NULL && c->progeny[k]->TYPE.count > 0) \ + runner_check_sorts_##TYPE(c->progeny[k], c->TYPE.sorted); \ + } +#else +#define RUNNER_CHECK_SORTS(TYPE) \ + void runner_check_sorts_##TYPE(struct cell *c, int flags) { \ + error("Calling debugging code without debugging flag activated."); \ + } +#endif + +RUNNER_CHECK_SORTS(hydro) +RUNNER_CHECK_SORTS(stars) + +/** + * @brief Sort the particles in the given cell along all cardinal directions. + * + * @param r The #runner. + * @param c The #cell. + * @param flags Cell flag. + * @param cleanup If true, re-build the sorts for the selected flags instead + * of just adding them. + * @param clock Flag indicating whether to record the timing or not, needed + * for recursive calls. + */ +void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, + int cleanup, int clock) { + + struct sort_entry *fingers[8]; + const int count = c->hydro.count; + const struct part *parts = c->hydro.parts; + struct xpart *xparts = c->hydro.xparts; + float buff[8]; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super == NULL) error("Task called above the super level!!!"); +#endif + + /* We need to do the local sorts plus whatever was requested further up. */ + flags |= c->hydro.do_sort; + if (cleanup) { + c->hydro.sorted = 0; + } else { + flags &= ~c->hydro.sorted; + } + if (flags == 0 && !cell_get_flag(c, cell_flag_do_hydro_sub_sort)) return; + + /* Check that the particles have been moved to the current time */ + if (flags && !cell_are_part_drifted(c, r->e)) + error("Sorting un-drifted cell c->nodeID=%d", c->nodeID); + +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure the sort flags are consistent (downward). */ + runner_check_sorts_hydro(c, c->hydro.sorted); + + /* Make sure the sort flags are consistent (upard). */ + for (struct cell *finger = c->parent; finger != NULL; + finger = finger->parent) { + if (finger->hydro.sorted & ~c->hydro.sorted) + error("Inconsistent sort flags (upward)."); + } + + /* Update the sort timer which represents the last time the sorts + were re-set. */ + if (c->hydro.sorted == 0) c->hydro.ti_sort = r->e->ti_current; +#endif + + /* Allocate memory for sorting. */ + cell_malloc_hydro_sorts(c, flags); + + /* Does this cell have any progeny? */ + if (c->split) { + + /* Fill in the gaps within the progeny. */ + float dx_max_sort = 0.0f; + float dx_max_sort_old = 0.0f; + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + + if (c->progeny[k]->hydro.count > 0) { + + /* Only propagate cleanup if the progeny is stale. */ + runner_do_hydro_sort( + r, c->progeny[k], flags, + cleanup && (c->progeny[k]->hydro.dx_max_sort_old > + space_maxreldx * c->progeny[k]->dmin), + 0); + dx_max_sort = max(dx_max_sort, c->progeny[k]->hydro.dx_max_sort); + dx_max_sort_old = + max(dx_max_sort_old, c->progeny[k]->hydro.dx_max_sort_old); + } else { + + /* We need to clean up the unused flags that were in case the + number of particles in the cell would change */ + cell_clear_hydro_sort_flags(c->progeny[k], /*clear_unused_flags=*/1); + } + } + } + c->hydro.dx_max_sort = dx_max_sort; + c->hydro.dx_max_sort_old = dx_max_sort_old; + + /* Loop over the 13 different sort arrays. */ + for (int j = 0; j < 13; j++) { + + /* Has this sort array been flagged? */ + if (!(flags & (1 << j))) continue; + + /* Init the particle index offsets. */ + int off[8]; + off[0] = 0; + for (int k = 1; k < 8; k++) + if (c->progeny[k - 1] != NULL) + off[k] = off[k - 1] + c->progeny[k - 1]->hydro.count; + else + off[k] = off[k - 1]; + + /* Init the entries and indices. */ + int inds[8]; + for (int k = 0; k < 8; k++) { + inds[k] = k; + if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0) { + fingers[k] = c->progeny[k]->hydro.sort[j]; + buff[k] = fingers[k]->d; + off[k] = off[k]; + } else + buff[k] = FLT_MAX; + } + + /* Sort the buffer. */ + for (int i = 0; i < 7; i++) + for (int k = i + 1; k < 8; k++) + if (buff[inds[k]] < buff[inds[i]]) { + int temp_i = inds[i]; + inds[i] = inds[k]; + inds[k] = temp_i; + } + + /* For each entry in the new sort list. */ + struct sort_entry *finger = c->hydro.sort[j]; + for (int ind = 0; ind < count; ind++) { + + /* Copy the minimum into the new sort array. */ + finger[ind].d = buff[inds[0]]; + finger[ind].i = fingers[inds[0]]->i + off[inds[0]]; + + /* Update the buffer. */ + fingers[inds[0]] += 1; + buff[inds[0]] = fingers[inds[0]]->d; + + /* Find the smallest entry. */ + for (int k = 1; k < 8 && buff[inds[k]] < buff[inds[k - 1]]; k++) { + int temp_i = inds[k - 1]; + inds[k - 1] = inds[k]; + inds[k] = temp_i; + } + + } /* Merge. */ + + /* Add a sentinel. */ + c->hydro.sort[j][count].d = FLT_MAX; + c->hydro.sort[j][count].i = 0; + + /* Mark as sorted. */ + atomic_or(&c->hydro.sorted, 1 << j); + + } /* loop over sort arrays. */ + + } /* progeny? */ + + /* Otherwise, just sort. */ + else { + + /* Reset the sort distance */ + if (c->hydro.sorted == 0) { +#ifdef SWIFT_DEBUG_CHECKS + if (xparts != NULL && c->nodeID != engine_rank) + error("Have non-NULL xparts in foreign cell"); +#endif + + /* And the individual sort distances if we are a local cell */ + if (xparts != NULL) { + for (int k = 0; k < count; k++) { + xparts[k].x_diff_sort[0] = 0.0f; + xparts[k].x_diff_sort[1] = 0.0f; + xparts[k].x_diff_sort[2] = 0.0f; + } + } + c->hydro.dx_max_sort_old = 0.f; + c->hydro.dx_max_sort = 0.f; + } + + /* Fill the sort array. */ + for (int k = 0; k < count; k++) { + const double px[3] = {parts[k].x[0], parts[k].x[1], parts[k].x[2]}; + for (int j = 0; j < 13; j++) + if (flags & (1 << j)) { + c->hydro.sort[j][k].i = k; + c->hydro.sort[j][k].d = px[0] * runner_shift[j][0] + + px[1] * runner_shift[j][1] + + px[2] * runner_shift[j][2]; + } + } + + /* Add the sentinel and sort. */ + for (int j = 0; j < 13; j++) + if (flags & (1 << j)) { + c->hydro.sort[j][count].d = FLT_MAX; + c->hydro.sort[j][count].i = 0; + runner_do_sort_ascending(c->hydro.sort[j], count); + atomic_or(&c->hydro.sorted, 1 << j); + } + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify the sorting. */ + for (int j = 0; j < 13; j++) { + if (!(flags & (1 << j))) continue; + struct sort_entry *finger = c->hydro.sort[j]; + for (int k = 1; k < count; k++) { + if (finger[k].d < finger[k - 1].d) + error("Sorting failed, ascending array."); + if (finger[k].i >= count) error("Sorting failed, indices borked."); + } + } + + /* Make sure the sort flags are consistent (downward). */ + runner_check_sorts_hydro(c, flags); + + /* Make sure the sort flags are consistent (upward). */ + for (struct cell *finger = c->parent; finger != NULL; + finger = finger->parent) { + if (finger->hydro.sorted & ~c->hydro.sorted) + error("Inconsistent sort flags."); + } +#endif + + /* Clear the cell's sort flags. */ + c->hydro.do_sort = 0; + cell_clear_flag(c, cell_flag_do_hydro_sub_sort); + c->hydro.requires_sorts = 0; + + if (clock) TIMER_TOC(timer_dosort); +} + +/** + * @brief Sort the stars particles in the given cell along all cardinal + * directions. + * + * @param r The #runner. + * @param c The #cell. + * @param flags Cell flag. + * @param cleanup If true, re-build the sorts for the selected flags instead + * of just adding them. + * @param clock Flag indicating whether to record the timing or not, needed + * for recursive calls. + */ +void runner_do_stars_sort(struct runner *r, struct cell *c, int flags, + int cleanup, int clock) { + + struct sort_entry *fingers[8]; + const int count = c->stars.count; + struct spart *sparts = c->stars.parts; + float buff[8]; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super == NULL) error("Task called above the super level!!!"); +#endif + + /* We need to do the local sorts plus whatever was requested further up. */ + flags |= c->stars.do_sort; + if (cleanup) { + c->stars.sorted = 0; + } else { + flags &= ~c->stars.sorted; + } + if (flags == 0 && !cell_get_flag(c, cell_flag_do_stars_sub_sort)) return; + + /* Check that the particles have been moved to the current time */ + if (flags && !cell_are_spart_drifted(c, r->e)) { + error("Sorting un-drifted cell c->nodeID=%d", c->nodeID); + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure the sort flags are consistent (downward). */ + runner_check_sorts_stars(c, c->stars.sorted); + + /* Make sure the sort flags are consistent (upward). */ + for (struct cell *finger = c->parent; finger != NULL; + finger = finger->parent) { + if (finger->stars.sorted & ~c->stars.sorted) + error("Inconsistent sort flags (upward)."); + } + + /* Update the sort timer which represents the last time the sorts + were re-set. */ + if (c->stars.sorted == 0) c->stars.ti_sort = r->e->ti_current; +#endif + + /* start by allocating the entry arrays in the requested dimensions. */ + cell_malloc_stars_sorts(c, flags); + + /* Does this cell have any progeny? */ + if (c->split) { + + /* Fill in the gaps within the progeny. */ + float dx_max_sort = 0.0f; + float dx_max_sort_old = 0.0f; + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + + if (c->progeny[k]->stars.count > 0) { + + /* Only propagate cleanup if the progeny is stale. */ + const int cleanup_prog = + cleanup && (c->progeny[k]->stars.dx_max_sort_old > + space_maxreldx * c->progeny[k]->dmin); + runner_do_stars_sort(r, c->progeny[k], flags, cleanup_prog, 0); + dx_max_sort = max(dx_max_sort, c->progeny[k]->stars.dx_max_sort); + dx_max_sort_old = + max(dx_max_sort_old, c->progeny[k]->stars.dx_max_sort_old); + } else { + + /* We need to clean up the unused flags that were in case the + number of particles in the cell would change */ + cell_clear_stars_sort_flags(c->progeny[k], /*clear_unused_flags=*/1); + } + } + } + c->stars.dx_max_sort = dx_max_sort; + c->stars.dx_max_sort_old = dx_max_sort_old; + + /* Loop over the 13 different sort arrays. */ + for (int j = 0; j < 13; j++) { + + /* Has this sort array been flagged? */ + if (!(flags & (1 << j))) continue; + + /* Init the particle index offsets. */ + int off[8]; + off[0] = 0; + for (int k = 1; k < 8; k++) + if (c->progeny[k - 1] != NULL) + off[k] = off[k - 1] + c->progeny[k - 1]->stars.count; + else + off[k] = off[k - 1]; + + /* Init the entries and indices. */ + int inds[8]; + for (int k = 0; k < 8; k++) { + inds[k] = k; + if (c->progeny[k] != NULL && c->progeny[k]->stars.count > 0) { + fingers[k] = c->progeny[k]->stars.sort[j]; + buff[k] = fingers[k]->d; + off[k] = off[k]; + } else + buff[k] = FLT_MAX; + } + + /* Sort the buffer. */ + for (int i = 0; i < 7; i++) + for (int k = i + 1; k < 8; k++) + if (buff[inds[k]] < buff[inds[i]]) { + int temp_i = inds[i]; + inds[i] = inds[k]; + inds[k] = temp_i; + } + + /* For each entry in the new sort list. */ + struct sort_entry *finger = c->stars.sort[j]; + for (int ind = 0; ind < count; ind++) { + + /* Copy the minimum into the new sort array. */ + finger[ind].d = buff[inds[0]]; + finger[ind].i = fingers[inds[0]]->i + off[inds[0]]; + + /* Update the buffer. */ + fingers[inds[0]] += 1; + buff[inds[0]] = fingers[inds[0]]->d; + + /* Find the smallest entry. */ + for (int k = 1; k < 8 && buff[inds[k]] < buff[inds[k - 1]]; k++) { + int temp_i = inds[k - 1]; + inds[k - 1] = inds[k]; + inds[k] = temp_i; + } + + } /* Merge. */ + + /* Add a sentinel. */ + c->stars.sort[j][count].d = FLT_MAX; + c->stars.sort[j][count].i = 0; + + /* Mark as sorted. */ + atomic_or(&c->stars.sorted, 1 << j); + + } /* loop over sort arrays. */ + + } /* progeny? */ + + /* Otherwise, just sort. */ + else { + + /* Reset the sort distance */ + if (c->stars.sorted == 0) { + + /* And the individual sort distances if we are a local cell */ + for (int k = 0; k < count; k++) { + sparts[k].x_diff_sort[0] = 0.0f; + sparts[k].x_diff_sort[1] = 0.0f; + sparts[k].x_diff_sort[2] = 0.0f; + } + c->stars.dx_max_sort_old = 0.f; + c->stars.dx_max_sort = 0.f; + } + + /* Fill the sort array. */ + for (int k = 0; k < count; k++) { + const double px[3] = {sparts[k].x[0], sparts[k].x[1], sparts[k].x[2]}; + for (int j = 0; j < 13; j++) + if (flags & (1 << j)) { + c->stars.sort[j][k].i = k; + c->stars.sort[j][k].d = px[0] * runner_shift[j][0] + + px[1] * runner_shift[j][1] + + px[2] * runner_shift[j][2]; + } + } + + /* Add the sentinel and sort. */ + for (int j = 0; j < 13; j++) + if (flags & (1 << j)) { + c->stars.sort[j][count].d = FLT_MAX; + c->stars.sort[j][count].i = 0; + runner_do_sort_ascending(c->stars.sort[j], count); + atomic_or(&c->stars.sorted, 1 << j); + } + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify the sorting. */ + for (int j = 0; j < 13; j++) { + if (!(flags & (1 << j))) continue; + struct sort_entry *finger = c->stars.sort[j]; + for (int k = 1; k < count; k++) { + if (finger[k].d < finger[k - 1].d) + error("Sorting failed, ascending array."); + if (finger[k].i >= count) error("Sorting failed, indices borked."); + } + } + + /* Make sure the sort flags are consistent (downward). */ + runner_check_sorts_stars(c, flags); + + /* Make sure the sort flags are consistent (upward). */ + for (struct cell *finger = c->parent; finger != NULL; + finger = finger->parent) { + if (finger->stars.sorted & ~c->stars.sorted) + error("Inconsistent sort flags."); + } +#endif + + /* Clear the cell's sort flags. */ + c->stars.do_sort = 0; + cell_clear_flag(c, cell_flag_do_stars_sub_sort); + c->stars.requires_sorts = 0; + + if (clock) TIMER_TOC(timer_do_stars_sort); +} + +/** + * @brief Recurse into a cell until reaching the super level and call + * the hydro sorting function there. + * + * This function must be called at or above the super level! + * + * This function will sort the particles in all 13 directions. + * + * @param r the #runner. + * @param c the #cell. + */ +void runner_do_all_hydro_sort(struct runner *r, struct cell *c) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != engine_rank) error("Function called on a foreign cell!"); +#endif + + if (!cell_is_active_hydro(c, r->e)) return; + + /* Shall we sort at this level? */ + if (c->hydro.super == c) { + + /* Sort everything */ + runner_do_hydro_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); + + } else { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super != NULL) error("Function called below the super level!"); +#endif + + /* Ok, then, let's try lower */ + if (c->split) { + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) runner_do_all_hydro_sort(r, c->progeny[k]); + } + } else { +#ifdef SWIFT_DEBUG_CHECKS + error("Reached a leaf without encountering a hydro super cell!"); +#endif + } + } +} + +/** + * @brief Recurse into a cell until reaching the super level and call + * the star sorting function there. + * + * This function must be called at or above the super level! + * + * This function will sort the particles in all 13 directions. + * + * @param r the #runner. + * @param c the #cell. + */ +void runner_do_all_stars_sort(struct runner *r, struct cell *c) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != engine_rank) error("Function called on a foreign cell!"); +#endif + + if (!cell_is_active_stars(c, r->e) && !cell_is_active_hydro(c, r->e)) return; + + /* Shall we sort at this level? */ + if (c->hydro.super == c) { + + /* Sort everything */ + runner_do_stars_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); + + } else { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super != NULL) error("Function called below the super level!"); +#endif + + /* Ok, then, let's try lower */ + if (c->split) { + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) runner_do_all_stars_sort(r, c->progeny[k]); + } + } else { +#ifdef SWIFT_DEBUG_CHECKS + error("Reached a leaf without encountering a hydro super cell!"); +#endif + } + } +} diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c new file mode 100644 index 0000000000000000000000000000000000000000..7fd22424a8c5cb5a5d87d60c5a234feb892906a3 --- /dev/null +++ b/src/runner_time_integration.c @@ -0,0 +1,1071 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "active.h" +#include "black_holes.h" +#include "cell.h" +#include "engine.h" +#include "kick.h" +#include "timers.h" +#include "timestep.h" +#include "timestep_limiter.h" +#include "tracers.h" + +/** + * @brief Initialize the multipoles before the gravity calculation. + * + * @param r The runner thread. + * @param c The cell. + * @param timer 1 if the time is to be recorded. + */ +void runner_do_init_grav(struct runner *r, struct cell *c, int timer) { + + const struct engine *e = r->e; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + if (!(e->policy & engine_policy_self_gravity)) + error("Grav-init task called outside of self-gravity calculation"); +#endif + + /* Anything to do here? */ + if (!cell_is_active_gravity(c, e)) return; + + /* Reset the gravity acceleration tensors */ + gravity_field_tensors_init(&c->grav.multipole->pot, e->ti_current); + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) runner_do_init_grav(r, c->progeny[k], 0); + } + } + + if (timer) TIMER_TOC(timer_init_grav); +} + +/** + * @brief Perform the first half-kick on all the active particles in a cell. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_kick1(struct runner *r, struct cell *c, int timer) { + + const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *entropy_floor = e->entropy_floor; + const int with_cosmology = (e->policy & engine_policy_cosmology); + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + struct gpart *restrict gparts = c->grav.parts; + struct spart *restrict sparts = c->stars.parts; + struct bpart *restrict bparts = c->black_holes.parts; + const int count = c->hydro.count; + const int gcount = c->grav.count; + const int scount = c->stars.count; + const int bcount = c->black_holes.count; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_starting_hydro(c, e) && !cell_is_starting_gravity(c, e) && + !cell_is_starting_stars(c, e) && !cell_is_starting_black_holes(c, e)) + return; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_kick1(r, c->progeny[k], 0); + } else { + + /* Loop over the parts in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + struct xpart *restrict xp = &xparts[k]; + + /* If particle needs to be kicked */ + if (part_is_starting(p, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + if (p->wakeup == time_bin_awake) + error("Woken-up particle that has not been processed in kick1"); +#endif + + /* Skip particles that have been woken up and treated by the limiter. */ + if (p->wakeup != time_bin_not_awake) continue; + + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current + 1, p->time_bin); + +#ifdef SWIFT_DEBUG_CHECKS + const integertime_t ti_end = ti_begin + ti_step; + + if (ti_begin != ti_current) + error( + "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " + "ti_step=%lld time_bin=%d wakeup=%d ti_current=%lld", + ti_end, ti_begin, ti_step, p->time_bin, p->wakeup, ti_current); +#endif + + /* Time interval for this half-kick */ + double dt_kick_grav, dt_kick_hydro, dt_kick_therm, dt_kick_corr; + if (with_cosmology) { + dt_kick_hydro = cosmology_get_hydro_kick_factor( + cosmo, ti_begin, ti_begin + ti_step / 2); + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + dt_kick_therm = cosmology_get_therm_kick_factor( + cosmo, ti_begin, ti_begin + ti_step / 2); + dt_kick_corr = cosmology_get_corr_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + } else { + dt_kick_hydro = (ti_step / 2) * time_base; + dt_kick_grav = (ti_step / 2) * time_base; + dt_kick_therm = (ti_step / 2) * time_base; + dt_kick_corr = (ti_step / 2) * time_base; + } + + /* do the kick */ + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, + dt_kick_corr, cosmo, hydro_props, entropy_floor, ti_begin, + ti_begin + ti_step / 2); + + /* Update the accelerations to be used in the drift for hydro */ + if (p->gpart != NULL) { + + xp->a_grav[0] = p->gpart->a_grav[0]; + xp->a_grav[1] = p->gpart->a_grav[1]; + xp->a_grav[2] = p->gpart->a_grav[2]; + } + } + } + + /* Loop over the gparts in this cell. */ + for (int k = 0; k < gcount; k++) { + + /* Get a handle on the part. */ + struct gpart *restrict gp = &gparts[k]; + + /* If the g-particle has no counterpart and needs to be kicked */ + if ((gp->type == swift_type_dark_matter || + gp->type == swift_type_dark_matter_background) && + gpart_is_starting(gp, e)) { + + const integertime_t ti_step = get_integer_timestep(gp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current + 1, gp->time_bin); + +#ifdef SWIFT_DEBUG_CHECKS + const integertime_t ti_end = + get_integer_time_end(ti_current + 1, gp->time_bin); + + if (ti_begin != ti_current) + error( + "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " + "ti_step=%lld time_bin=%d ti_current=%lld", + ti_end, ti_begin, ti_step, gp->time_bin, ti_current); +#endif + + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + + /* do the kick */ + kick_gpart(gp, dt_kick_grav, ti_begin, ti_begin + ti_step / 2); + } + } + + /* Loop over the stars particles in this cell. */ + for (int k = 0; k < scount; k++) { + + /* Get a handle on the s-part. */ + struct spart *restrict sp = &sparts[k]; + + /* If particle needs to be kicked */ + if (spart_is_starting(sp, e)) { + + const integertime_t ti_step = get_integer_timestep(sp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current + 1, sp->time_bin); + +#ifdef SWIFT_DEBUG_CHECKS + const integertime_t ti_end = + get_integer_time_end(ti_current + 1, sp->time_bin); + + if (ti_begin != ti_current) + error( + "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " + "ti_step=%lld time_bin=%d ti_current=%lld", + ti_end, ti_begin, ti_step, sp->time_bin, ti_current); +#endif + + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + + /* do the kick */ + kick_spart(sp, dt_kick_grav, ti_begin, ti_begin + ti_step / 2); + } + } + + /* Loop over the black hole particles in this cell. */ + for (int k = 0; k < bcount; k++) { + + /* Get a handle on the s-part. */ + struct bpart *restrict bp = &bparts[k]; + + /* If particle needs to be kicked */ + if (bpart_is_starting(bp, e)) { + + const integertime_t ti_step = get_integer_timestep(bp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current + 1, bp->time_bin); + +#ifdef SWIFT_DEBUG_CHECKS + const integertime_t ti_end = + get_integer_time_end(ti_current + 1, bp->time_bin); + + if (ti_begin != ti_current) + error( + "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " + "ti_step=%lld time_bin=%d ti_current=%lld", + ti_end, ti_begin, ti_step, bp->time_bin, ti_current); +#endif + + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + + /* do the kick */ + kick_bpart(bp, dt_kick_grav, ti_begin, ti_begin + ti_step / 2); + } + } + } + + if (timer) TIMER_TOC(timer_kick1); +} + +/** + * @brief Perform the second half-kick on all the active particles in a cell. + * + * Also prepares particles to be drifted. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_kick2(struct runner *r, struct cell *c, int timer) { + + const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *entropy_floor = e->entropy_floor; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const int count = c->hydro.count; + const int gcount = c->grav.count; + const int scount = c->stars.count; + const int bcount = c->black_holes.count; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + struct gpart *restrict gparts = c->grav.parts; + struct spart *restrict sparts = c->stars.parts; + struct bpart *restrict bparts = c->black_holes.parts; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && + !cell_is_active_stars(c, e) && !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_kick2(r, c->progeny[k], 0); + } else { + + /* Loop over the particles in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + struct xpart *restrict xp = &xparts[k]; + + /* If particle needs to be kicked */ + if (part_is_active(p, e)) { + + integertime_t ti_begin, ti_end, ti_step; + +#ifdef SWIFT_DEBUG_CHECKS + if (p->wakeup == time_bin_awake) + error("Woken-up particle that has not been processed in kick1"); +#endif + + if (p->wakeup == time_bin_not_awake) { + + /* Time-step from a regular kick */ + ti_step = get_integer_timestep(p->time_bin); + ti_begin = get_integer_time_begin(ti_current, p->time_bin); + ti_end = ti_begin + ti_step; + + } else { + + /* Time-step that follows a wake-up call */ + ti_begin = get_integer_time_begin(ti_current, p->wakeup); + ti_end = get_integer_time_end(ti_current, p->time_bin); + ti_step = ti_end - ti_begin; + + /* Reset the flag. Everything is back to normal from now on. */ + p->wakeup = time_bin_awake; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_begin + ti_step != ti_current) + error( + "Particle in wrong time-bin, ti_begin=%lld, ti_step=%lld " + "time_bin=%d wakeup=%d ti_current=%lld", + ti_begin, ti_step, p->time_bin, p->wakeup, ti_current); +#endif + /* Time interval for this half-kick */ + double dt_kick_grav, dt_kick_hydro, dt_kick_therm, dt_kick_corr; + if (with_cosmology) { + dt_kick_hydro = cosmology_get_hydro_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_end); + dt_kick_grav = cosmology_get_grav_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_end); + dt_kick_therm = cosmology_get_therm_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_end); + dt_kick_corr = cosmology_get_corr_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_end); + } else { + dt_kick_hydro = (ti_end - (ti_begin + ti_step / 2)) * time_base; + dt_kick_grav = (ti_end - (ti_begin + ti_step / 2)) * time_base; + dt_kick_therm = (ti_end - (ti_begin + ti_step / 2)) * time_base; + dt_kick_corr = (ti_end - (ti_begin + ti_step / 2)) * time_base; + } + + /* Finish the time-step with a second half-kick */ + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, + dt_kick_corr, cosmo, hydro_props, entropy_floor, + ti_begin + ti_step / 2, ti_end); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that kick and the drift are synchronized */ + if (p->ti_drift != p->ti_kick) error("Error integrating part in time."); +#endif + + /* Prepare the values to be drifted */ + hydro_reset_predicted_values(p, xp, cosmo); + } + } + + /* Loop over the g-particles in this cell. */ + for (int k = 0; k < gcount; k++) { + + /* Get a handle on the part. */ + struct gpart *restrict gp = &gparts[k]; + + /* If the g-particle has no counterpart and needs to be kicked */ + if ((gp->type == swift_type_dark_matter || + gp->type == swift_type_dark_matter_background) && + gpart_is_active(gp, e)) { + + const integertime_t ti_step = get_integer_timestep(gp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current, gp->time_bin); + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_begin + ti_step != ti_current) + error("Particle in wrong time-bin"); +#endif + + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + + /* Finish the time-step with a second half-kick */ + kick_gpart(gp, dt_kick_grav, ti_begin + ti_step / 2, + ti_begin + ti_step); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that kick and the drift are synchronized */ + if (gp->ti_drift != gp->ti_kick) + error("Error integrating g-part in time."); +#endif + + /* Prepare the values to be drifted */ + gravity_reset_predicted_values(gp); + } + } + + /* Loop over the particles in this cell. */ + for (int k = 0; k < scount; k++) { + + /* Get a handle on the part. */ + struct spart *restrict sp = &sparts[k]; + + /* If particle needs to be kicked */ + if (spart_is_active(sp, e)) { + + const integertime_t ti_step = get_integer_timestep(sp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current, sp->time_bin); + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_begin + ti_step != ti_current) + error("Particle in wrong time-bin"); +#endif + + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + + /* Finish the time-step with a second half-kick */ + kick_spart(sp, dt_kick_grav, ti_begin + ti_step / 2, + ti_begin + ti_step); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that kick and the drift are synchronized */ + if (sp->ti_drift != sp->ti_kick) + error("Error integrating s-part in time."); +#endif + + /* Prepare the values to be drifted */ + stars_reset_predicted_values(sp); + } + } + + /* Loop over the particles in this cell. */ + for (int k = 0; k < bcount; k++) { + + /* Get a handle on the part. */ + struct bpart *restrict bp = &bparts[k]; + + /* If particle needs to be kicked */ + if (bpart_is_active(bp, e)) { + + const integertime_t ti_step = get_integer_timestep(bp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current, bp->time_bin); + +#ifdef SWIFT_DEBUG_CHECKS + if (ti_begin + ti_step != ti_current) + error("Particle in wrong time-bin"); +#endif + + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + + /* Finish the time-step with a second half-kick */ + kick_bpart(bp, dt_kick_grav, ti_begin + ti_step / 2, + ti_begin + ti_step); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that kick and the drift are synchronized */ + if (bp->ti_drift != bp->ti_kick) + error("Error integrating b-part in time."); +#endif + + /* Prepare the values to be drifted */ + black_holes_reset_predicted_values(bp); + } + } + } + if (timer) TIMER_TOC(timer_kick2); +} + +/** + * @brief Computes the next time-step of all active particles in this cell + * and update the cell's statistics. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_timestep(struct runner *r, struct cell *c, int timer) { + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const int count = c->hydro.count; + const int gcount = c->grav.count; + const int scount = c->stars.count; + const int bcount = c->black_holes.count; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + struct gpart *restrict gparts = c->grav.parts; + struct spart *restrict sparts = c->stars.parts; + struct bpart *restrict bparts = c->black_holes.parts; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && + !cell_is_active_stars(c, e) && !cell_is_active_black_holes(c, e)) { + c->hydro.updated = 0; + c->grav.updated = 0; + c->stars.updated = 0; + c->black_holes.updated = 0; + return; + } + + int updated = 0, g_updated = 0, s_updated = 0, b_updated = 0; + integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, + ti_hydro_beg_max = 0; + integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, + ti_gravity_beg_max = 0; + integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, + ti_stars_beg_max = 0; + integertime_t ti_black_holes_end_min = max_nr_timesteps, + ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; + + /* No children? */ + if (!c->split) { + + /* Loop over the particles in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + struct xpart *restrict xp = &xparts[k]; + + /* If particle needs updating */ + if (part_is_active(p, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Current end of time-step */ + const integertime_t ti_end = + get_integer_time_end(ti_current, p->time_bin); + + if (ti_end != ti_current) + error("Computing time-step of rogue particle."); +#endif + + /* Get new time-step */ + const integertime_t ti_new_step = get_part_timestep(p, xp, e); + + /* Update particle */ + p->time_bin = get_time_bin(ti_new_step); + if (p->gpart != NULL) p->gpart->time_bin = p->time_bin; + + /* Update the tracers properties */ + tracers_after_timestep(p, xp, e->internal_units, e->physical_constants, + with_cosmology, e->cosmology, + e->hydro_properties, e->cooling_func, e->time); + + /* Number of updated particles */ + updated++; + if (p->gpart != NULL) g_updated++; + + /* What is the next sync-point ? */ + ti_hydro_end_min = min(ti_current + ti_new_step, ti_hydro_end_min); + ti_hydro_end_max = max(ti_current + ti_new_step, ti_hydro_end_max); + + /* What is the next starting point for this cell ? */ + ti_hydro_beg_max = max(ti_current, ti_hydro_beg_max); + + if (p->gpart != NULL) { + + /* What is the next sync-point ? */ + ti_gravity_end_min = + min(ti_current + ti_new_step, ti_gravity_end_min); + ti_gravity_end_max = + max(ti_current + ti_new_step, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); + } + } + + else { /* part is inactive */ + + if (!part_is_inhibited(p, e)) { + + const integertime_t ti_end = + get_integer_time_end(ti_current, p->time_bin); + + const integertime_t ti_beg = + get_integer_time_begin(ti_current + 1, p->time_bin); + + /* What is the next sync-point ? */ + ti_hydro_end_min = min(ti_end, ti_hydro_end_min); + ti_hydro_end_max = max(ti_end, ti_hydro_end_max); + + /* What is the next starting point for this cell ? */ + ti_hydro_beg_max = max(ti_beg, ti_hydro_beg_max); + + if (p->gpart != NULL) { + + /* What is the next sync-point ? */ + ti_gravity_end_min = min(ti_end, ti_gravity_end_min); + ti_gravity_end_max = max(ti_end, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); + } + } + } + } + + /* Loop over the g-particles in this cell. */ + for (int k = 0; k < gcount; k++) { + + /* Get a handle on the part. */ + struct gpart *restrict gp = &gparts[k]; + + /* If the g-particle has no counterpart */ + if (gp->type == swift_type_dark_matter || + gp->type == swift_type_dark_matter_background) { + + /* need to be updated ? */ + if (gpart_is_active(gp, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Current end of time-step */ + const integertime_t ti_end = + get_integer_time_end(ti_current, gp->time_bin); + + if (ti_end != ti_current) + error("Computing time-step of rogue particle."); +#endif + + /* Get new time-step */ + const integertime_t ti_new_step = get_gpart_timestep(gp, e); + + /* Update particle */ + gp->time_bin = get_time_bin(ti_new_step); + + /* Number of updated g-particles */ + g_updated++; + + /* What is the next sync-point ? */ + ti_gravity_end_min = + min(ti_current + ti_new_step, ti_gravity_end_min); + ti_gravity_end_max = + max(ti_current + ti_new_step, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); + + } else { /* gpart is inactive */ + + if (!gpart_is_inhibited(gp, e)) { + + const integertime_t ti_end = + get_integer_time_end(ti_current, gp->time_bin); + + /* What is the next sync-point ? */ + ti_gravity_end_min = min(ti_end, ti_gravity_end_min); + ti_gravity_end_max = max(ti_end, ti_gravity_end_max); + + const integertime_t ti_beg = + get_integer_time_begin(ti_current + 1, gp->time_bin); + + /* What is the next starting point for this cell ? */ + ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); + } + } + } + } + + /* Loop over the star particles in this cell. */ + for (int k = 0; k < scount; k++) { + + /* Get a handle on the part. */ + struct spart *restrict sp = &sparts[k]; + + /* need to be updated ? */ + if (spart_is_active(sp, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Current end of time-step */ + const integertime_t ti_end = + get_integer_time_end(ti_current, sp->time_bin); + + if (ti_end != ti_current) + error("Computing time-step of rogue particle."); +#endif + /* Get new time-step */ + const integertime_t ti_new_step = get_spart_timestep(sp, e); + + /* Update particle */ + sp->time_bin = get_time_bin(ti_new_step); + sp->gpart->time_bin = get_time_bin(ti_new_step); + + /* Number of updated s-particles */ + s_updated++; + g_updated++; + + ti_stars_end_min = min(ti_current + ti_new_step, ti_stars_end_min); + ti_stars_end_max = max(ti_current + ti_new_step, ti_stars_end_max); + ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); + ti_gravity_end_max = max(ti_current + ti_new_step, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_stars_beg_max = max(ti_current, ti_stars_beg_max); + ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); + + /* star particle is inactive but not inhibited */ + } else { + + if (!spart_is_inhibited(sp, e)) { + + const integertime_t ti_end = + get_integer_time_end(ti_current, sp->time_bin); + + const integertime_t ti_beg = + get_integer_time_begin(ti_current + 1, sp->time_bin); + + ti_stars_end_min = min(ti_end, ti_stars_end_min); + ti_stars_end_max = max(ti_end, ti_stars_end_max); + ti_gravity_end_min = min(ti_end, ti_gravity_end_min); + ti_gravity_end_max = max(ti_end, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_stars_beg_max = max(ti_beg, ti_stars_beg_max); + ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); + } + } + } + + /* Loop over the star particles in this cell. */ + for (int k = 0; k < bcount; k++) { + + /* Get a handle on the part. */ + struct bpart *restrict bp = &bparts[k]; + + /* need to be updated ? */ + if (bpart_is_active(bp, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Current end of time-step */ + const integertime_t ti_end = + get_integer_time_end(ti_current, bp->time_bin); + + if (ti_end != ti_current) + error("Computing time-step of rogue particle."); +#endif + /* Get new time-step */ + const integertime_t ti_new_step = get_bpart_timestep(bp, e); + + /* Update particle */ + bp->time_bin = get_time_bin(ti_new_step); + bp->gpart->time_bin = get_time_bin(ti_new_step); + + /* Number of updated s-particles */ + b_updated++; + g_updated++; + + ti_black_holes_end_min = + min(ti_current + ti_new_step, ti_black_holes_end_min); + ti_black_holes_end_max = + max(ti_current + ti_new_step, ti_black_holes_end_max); + ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); + ti_gravity_end_max = max(ti_current + ti_new_step, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_black_holes_beg_max = max(ti_current, ti_black_holes_beg_max); + ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); + + /* star particle is inactive but not inhibited */ + } else { + + if (!bpart_is_inhibited(bp, e)) { + + const integertime_t ti_end = + get_integer_time_end(ti_current, bp->time_bin); + + const integertime_t ti_beg = + get_integer_time_begin(ti_current + 1, bp->time_bin); + + ti_black_holes_end_min = min(ti_end, ti_black_holes_end_min); + ti_black_holes_end_max = max(ti_end, ti_black_holes_end_max); + ti_gravity_end_min = min(ti_end, ti_gravity_end_min); + ti_gravity_end_max = max(ti_end, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_black_holes_beg_max = max(ti_beg, ti_black_holes_beg_max); + ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); + } + } + } + + } else { + + /* Loop over the progeny. */ + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *restrict cp = c->progeny[k]; + + /* Recurse */ + runner_do_timestep(r, cp, 0); + + /* And aggregate */ + updated += cp->hydro.updated; + g_updated += cp->grav.updated; + s_updated += cp->stars.updated; + b_updated += cp->black_holes.updated; + + ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); + ti_hydro_end_max = max(cp->hydro.ti_end_max, ti_hydro_end_max); + ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); + + ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); + ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max); + ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); + + ti_stars_end_min = min(cp->stars.ti_end_min, ti_stars_end_min); + ti_stars_end_max = max(cp->grav.ti_end_max, ti_stars_end_max); + ti_stars_beg_max = max(cp->grav.ti_beg_max, ti_stars_beg_max); + + ti_black_holes_end_min = + min(cp->black_holes.ti_end_min, ti_black_holes_end_min); + ti_black_holes_end_max = + max(cp->grav.ti_end_max, ti_black_holes_end_max); + ti_black_holes_beg_max = + max(cp->grav.ti_beg_max, ti_black_holes_beg_max); + } + } + } + + /* Store the values. */ + c->hydro.updated = updated; + c->grav.updated = g_updated; + c->stars.updated = s_updated; + c->black_holes.updated = b_updated; + + c->hydro.ti_end_min = ti_hydro_end_min; + c->hydro.ti_end_max = ti_hydro_end_max; + c->hydro.ti_beg_max = ti_hydro_beg_max; + c->grav.ti_end_min = ti_gravity_end_min; + c->grav.ti_end_max = ti_gravity_end_max; + c->grav.ti_beg_max = ti_gravity_beg_max; + c->stars.ti_end_min = ti_stars_end_min; + c->stars.ti_end_max = ti_stars_end_max; + c->stars.ti_beg_max = ti_stars_beg_max; + c->black_holes.ti_end_min = ti_black_holes_end_min; + c->black_holes.ti_end_max = ti_black_holes_end_max; + c->black_holes.ti_beg_max = ti_black_holes_beg_max; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.ti_end_min == e->ti_current && + c->hydro.ti_end_min < max_nr_timesteps) + error("End of next hydro step is current time!"); + if (c->grav.ti_end_min == e->ti_current && + c->grav.ti_end_min < max_nr_timesteps) + error("End of next gravity step is current time!"); + if (c->stars.ti_end_min == e->ti_current && + c->stars.ti_end_min < max_nr_timesteps) + error("End of next stars step is current time!"); + if (c->black_holes.ti_end_min == e->ti_current && + c->black_holes.ti_end_min < max_nr_timesteps) + error("End of next black holes step is current time!"); +#endif + + if (timer) TIMER_TOC(timer_timestep); +} + +/** + * @brief Apply the time-step limiter to all awaken particles in a cell + * hierarchy. + * + * @param r The task #runner. + * @param c The #cell. + * @param force Limit the particles irrespective of the #cell flags. + * @param timer Are we timing this ? + */ +void runner_do_limiter(struct runner *r, struct cell *c, int force, int timer) { + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const int count = c->hydro.count; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that we only limit local cells. */ + if (c->nodeID != engine_rank) error("Limiting dt of a foreign cell is nope."); +#endif + + integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, + ti_hydro_beg_max = 0; + integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, + ti_gravity_beg_max = 0; + + /* Limit irrespective of cell flags? */ + force = (force || cell_get_flag(c, cell_flag_do_hydro_limiter)); + + /* Early abort? */ + if (c->hydro.count == 0) { + + /* Clear the limiter flags. */ + cell_clear_flag( + c, cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); + return; + } + + /* Loop over the progeny ? */ + if (c->split && (force || cell_get_flag(c, cell_flag_do_hydro_sub_limiter))) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *restrict cp = c->progeny[k]; + + /* Recurse */ + runner_do_limiter(r, cp, force, 0); + + /* And aggregate */ + ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); + ti_hydro_end_max = max(cp->hydro.ti_end_max, ti_hydro_end_max); + ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); + ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); + ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max); + ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); + } + } + + /* Store the updated values */ + c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); + c->hydro.ti_end_max = max(c->hydro.ti_end_max, ti_hydro_end_max); + c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); + c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); + c->grav.ti_end_max = max(c->grav.ti_end_max, ti_gravity_end_max); + c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); + + } else if (!c->split && force) { + + ti_hydro_end_min = c->hydro.ti_end_min; + ti_hydro_end_max = c->hydro.ti_end_max; + ti_hydro_beg_max = c->hydro.ti_beg_max; + ti_gravity_end_min = c->grav.ti_end_min; + ti_gravity_end_max = c->grav.ti_end_max; + ti_gravity_beg_max = c->grav.ti_beg_max; + + /* Loop over the gas particles in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + struct xpart *restrict xp = &xparts[k]; + + /* Avoid inhibited particles */ + if (part_is_inhibited(p, e)) continue; + + /* If the particle will be active no need to wake it up */ + if (part_is_active(p, e) && p->wakeup != time_bin_not_awake) + p->wakeup = time_bin_not_awake; + + /* Bip, bip, bip... wake-up time */ + if (p->wakeup <= time_bin_awake) { + + /* Apply the limiter and get the new time-step size */ + const integertime_t ti_new_step = timestep_limit_part(p, xp, e); + + /* What is the next sync-point ? */ + ti_hydro_end_min = min(ti_current + ti_new_step, ti_hydro_end_min); + ti_hydro_end_max = max(ti_current + ti_new_step, ti_hydro_end_max); + + /* What is the next starting point for this cell ? */ + ti_hydro_beg_max = max(ti_current, ti_hydro_beg_max); + + /* Also limit the gpart counter-part */ + if (p->gpart != NULL) { + + /* Register the time-bin */ + p->gpart->time_bin = p->time_bin; + + /* What is the next sync-point ? */ + ti_gravity_end_min = + min(ti_current + ti_new_step, ti_gravity_end_min); + ti_gravity_end_max = + max(ti_current + ti_new_step, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); + } + } + } + + /* Store the updated values */ + c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); + c->hydro.ti_end_max = max(c->hydro.ti_end_max, ti_hydro_end_max); + c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); + c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); + c->grav.ti_end_max = max(c->grav.ti_end_max, ti_gravity_end_max); + c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); + } + + /* Clear the limiter flags. */ + cell_clear_flag(c, + cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); + + if (timer) TIMER_TOC(timer_do_limiter); +} diff --git a/src/scheduler.c b/src/scheduler.c index 392c868e7e269bee630860ba94d5e87e4f1ac0b8..1fad63fd7141db2aad486aaaa7e4dc877a8aa3b8 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -568,6 +568,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { if (!is_self && !is_pair) { t->type = task_type_none; t->subtype = task_subtype_none; + t->ci = NULL; t->cj = NULL; t->skip = 1; break; @@ -600,7 +601,10 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { /* Add the self tasks. */ int first_child = 0; while (ci->progeny[first_child] == NULL) first_child++; + t->ci = ci->progeny[first_child]; + cell_set_flag(t->ci, cell_flag_has_tasks); + for (int k = first_child + 1; k < 8; k++) { /* Do we have a non-empty progenitor? */ if (ci->progeny[k] != NULL && @@ -710,8 +714,12 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { /* Loop over the sub-cell pairs for the current sid and add new tasks * for them. */ struct cell_split_pair *csp = &cell_split_pairs[sid]; + t->ci = ci->progeny[csp->pairs[0].pid]; t->cj = cj->progeny[csp->pairs[0].pjd]; + cell_set_flag(t->ci, cell_flag_has_tasks); + cell_set_flag(t->cj, cell_flag_has_tasks); + t->flags = csp->pairs[0].sid; for (int k = 1; k < csp->count; k++) { scheduler_splittask_hydro( @@ -767,6 +775,7 @@ static void scheduler_splittask_gravity(struct task *t, struct scheduler *s) { if ((t->ci == NULL) || (t->type == task_type_pair && t->cj == NULL)) { t->type = task_type_none; t->subtype = task_subtype_none; + t->ci = NULL; t->cj = NULL; t->skip = 1; break; @@ -794,7 +803,9 @@ static void scheduler_splittask_gravity(struct task *t, struct scheduler *s) { /* Add the self tasks. */ int first_child = 0; while (ci->progeny[first_child] == NULL) first_child++; + t->ci = ci->progeny[first_child]; + cell_set_flag(t->ci, cell_flag_has_tasks); for (int k = first_child + 1; k < 8; k++) if (ci->progeny[k] != NULL) @@ -912,6 +923,7 @@ static void scheduler_splittask_fof(struct task *t, struct scheduler *s) { t->ci->grav.count == 0 || (t->cj != NULL && t->cj->grav.count == 0)) { t->type = task_type_none; t->subtype = task_subtype_none; + t->ci = NULL; t->cj = NULL; t->skip = 1; break; @@ -1025,8 +1037,19 @@ void scheduler_splittasks_mapper(void *map_data, int num_elements, * @param s The #scheduler. * @param fof_tasks Are we splitting the FOF tasks (1)? Or the regular tasks * (0)? + * @param verbose Are we talkative? */ -void scheduler_splittasks(struct scheduler *s, const int fof_tasks) { +void scheduler_splittasks(struct scheduler *s, const int fof_tasks, + const int verbose) { + + if (verbose) { + message("space_subsize_self_hydro= %d", space_subsize_self_hydro); + message("space_subsize_pair_hydro= %d", space_subsize_pair_hydro); + message("space_subsize_self_stars= %d", space_subsize_self_stars); + message("space_subsize_pair_stars= %d", space_subsize_pair_stars); + message("space_subsize_self_grav= %d", space_subsize_self_grav); + message("space_subsize_pair_grav= %d", space_subsize_pair_grav); + } if (fof_tasks) { /* Call the mapper on each current task. */ @@ -1086,6 +1109,9 @@ struct task *scheduler_addtask(struct scheduler *s, enum task_types type, t->tic = 0; t->toc = 0; + if (ci != NULL) cell_set_flag(ci, cell_flag_has_tasks); + if (cj != NULL) cell_set_flag(cj, cell_flag_has_tasks); + /* Add an index for it. */ // lock_lock( &s->lock ); s->tasks_ind[atomic_inc(&s->nr_tasks)] = ind; @@ -1110,14 +1136,17 @@ void scheduler_set_unlocks(struct scheduler *s) { for (int k = 0; k < s->nr_unlocks; k++) { counts[s->unlock_ind[k]] += 1; -#ifdef SWIFT_DEBUG_CHECKS /* Check that we are not overflowing */ if (counts[s->unlock_ind[k]] < 0) - error("Task (type=%s/%s) unlocking more than %lld other tasks!", - taskID_names[s->tasks[s->unlock_ind[k]].type], - subtaskID_names[s->tasks[s->unlock_ind[k]].subtype], - (1LL << (8 * sizeof(short int) - 1)) - 1); -#endif + error( + "Task (type=%s/%s) unlocking more than %lld other tasks!\n" + "This likely a result of having tasks at vastly different levels" + "in the tree.\nYou may want to play with the 'Scheduler' " + "parameters to modify the task splitting strategy and reduce" + "the difference in task depths.", + taskID_names[s->tasks[s->unlock_ind[k]].type], + subtaskID_names[s->tasks[s->unlock_ind[k]].subtype], + (1LL << (8 * sizeof(short int) - 1)) - 1); } /* Compute the offset for each unlock block. */ @@ -1572,14 +1601,6 @@ void scheduler_enqueue_mapper(void *map_data, int num_elements, * @param s The #scheduler. */ void scheduler_start(struct scheduler *s) { - /* Reset all task timers. */ - for (int i = 0; i < s->nr_tasks; ++i) { - s->tasks[i].tic = 0; - s->tasks[i].toc = 0; -#ifdef SWIFT_DEBUG_TASKS - s->tasks[i].rid = -1; -#endif - } /* Re-wait the tasks. */ if (s->active_count > 1000) { @@ -1710,6 +1731,14 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { t->ci->hydro.count * sizeof(struct black_holes_part_data), MPI_BYTE, t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], &t->req); + } else if (t->subtype == task_subtype_bpart_merger) { + t->buff = (struct black_holes_bpart_data *)malloc( + sizeof(struct black_holes_bpart_data) * t->ci->black_holes.count); + err = MPI_Irecv( + t->buff, + t->ci->black_holes.count * sizeof(struct black_holes_bpart_data), + MPI_BYTE, t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); } else if (t->subtype == task_subtype_xv || t->subtype == task_subtype_rho || t->subtype == task_subtype_gradient) { @@ -1851,6 +1880,26 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], &t->req); } + } else if (t->subtype == task_subtype_bpart_merger) { + t->buff = (struct black_holes_bpart_data *)malloc( + sizeof(struct black_holes_bpart_data) * t->ci->black_holes.count); + cell_pack_bpart_swallow(t->ci, + (struct black_holes_bpart_data *)t->buff); + + if (t->ci->black_holes.count * sizeof(struct black_holes_bpart_data) > + s->mpi_message_limit) { + err = MPI_Isend(t->buff, + t->ci->black_holes.count * + sizeof(struct black_holes_bpart_data), + MPI_BYTE, t->cj->nodeID, t->flags, + subtaskMPI_comms[t->subtype], &t->req); + } else { + err = MPI_Issend(t->buff, + t->ci->black_holes.count * + sizeof(struct black_holes_bpart_data), + MPI_BYTE, t->cj->nodeID, t->flags, + subtaskMPI_comms[t->subtype], &t->req); + } } else if (t->subtype == task_subtype_xv || t->subtype == task_subtype_rho || diff --git a/src/scheduler.h b/src/scheduler.h index ac9bc754db1ea03f91c0ce522dc325c18d25ec09..694a88017b863d29ad9d15a4ae7d5acac5cf3223 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -190,7 +190,8 @@ void scheduler_reweight(struct scheduler *s, int verbose); struct task *scheduler_addtask(struct scheduler *s, enum task_types type, enum task_subtypes subtype, int flags, int implicit, struct cell *ci, struct cell *cj); -void scheduler_splittasks(struct scheduler *s, const int fof_tasks); +void scheduler_splittasks(struct scheduler *s, const int fof_tasks, + const int verbose); struct task *scheduler_done(struct scheduler *s, struct task *t); struct task *scheduler_unlock(struct scheduler *s, struct task *t); void scheduler_addunlock(struct scheduler *s, struct task *ta, struct task *tb); diff --git a/src/serial_io.c b/src/serial_io.c index c05c69e1e86b737e1e7f06b995f89f6bb548901a..0035ba89efb73e0e6d585bbc4b1e180aae507530 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -45,6 +45,7 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "fof_io.h" #include "gravity_io.h" #include "gravity_properties.h" #include "hydro_io.h" @@ -311,18 +312,18 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName, props.dimension, props.type); /* Write unit conversion factors for this data set */ - char buffer[FIELD_BUFFER_SIZE]; - units_cgs_conversion_string(buffer, snapshot_units, props.units); + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, snapshot_units, props.units, + props.scale_factor_exponent); float baseUnitsExp[5]; units_get_base_unit_exponents_array(baseUnitsExp, props.units); - const float a_factor_exp = units_a_factor(snapshot_units, props.units); io_write_attribute_f(h_data, "U_M exponent", baseUnitsExp[UNIT_MASS]); io_write_attribute_f(h_data, "U_L exponent", baseUnitsExp[UNIT_LENGTH]); io_write_attribute_f(h_data, "U_t exponent", baseUnitsExp[UNIT_TIME]); io_write_attribute_f(h_data, "U_I exponent", baseUnitsExp[UNIT_CURRENT]); io_write_attribute_f(h_data, "U_T exponent", baseUnitsExp[UNIT_TEMPERATURE]); - io_write_attribute_f(h_data, "h-scale exponent", 0); - io_write_attribute_f(h_data, "a-scale exponent", a_factor_exp); + io_write_attribute_f(h_data, "h-scale exponent", 0.f); + io_write_attribute_f(h_data, "a-scale exponent", props.scale_factor_exponent); io_write_attribute_s(h_data, "Expression for physical CGS units", buffer); /* Write the actual number this conversion factor corresponds to */ @@ -334,8 +335,16 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName, factor); io_write_attribute_d( h_data, - "Conversion factor to phyical CGS (including cosmological corrections)", - factor * pow(e->cosmology->a, a_factor_exp)); + "Conversion factor to physical CGS (including cosmological corrections)", + factor * pow(e->cosmology->a, props.scale_factor_exponent)); + +#ifdef SWIFT_DEBUG_CHECKS + if (strlen(props.description) == 0) + error("Invalid (empty) description of the field '%s'", props.name); +#endif + + /* Write the full description */ + io_write_attribute_s(h_data, "Description", props.description); /* Close everything */ H5Pclose(h_prop); @@ -451,6 +460,8 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param bparts (output) Array of #bpart particles. * @param Ngas (output) The number of #part read from the file on that node. * @param Ngparts (output) The number of #gpart read from the file on that node. + * @param Ngparts_background (output) The number of background #gpart (type 2) + * read from the file on that node. * @param Nstars (output) The number of #spart read from the file on that node. * @param Nblackholes (output) The number of #bpart read from the file on that * node. @@ -460,6 +471,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? * @param with_black_holes Are we reading black hole particles ? + * @param with_cosmology Are we running with cosmology ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -483,12 +495,12 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, void read_ic_serial(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, struct bpart** bparts, size_t* Ngas, - size_t* Ngparts, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_stars, int with_black_holes, int cleanup_h, - int cleanup_sqrt_a, double h, double a, int mpi_rank, - int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads, - int dry_run) { + size_t* Ngparts, size_t* Ngparts_background, size_t* Nstars, + size_t* Nblackholes, int* flag_entropy, int with_hydro, + int with_gravity, int with_stars, int with_black_holes, + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -501,11 +513,13 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, long long offset[swift_type_count] = {0}; int dimension = 3; /* Assume 3D if nothing is specified */ size_t Ndm = 0; + size_t Ndm_background = 0; struct unit_system* ic_units = (struct unit_system*)malloc(sizeof(struct unit_system)); /* Initialise counters */ - *Ngas = 0, *Ngparts = 0, *Nstars = 0, *Nblackholes = 0; + *Ngas = 0, *Ngparts = 0, *Ngparts_background = 0, *Nstars = 0, + *Nblackholes = 0; /* First read some information about the content */ if (mpi_rank == 0) { @@ -556,6 +570,13 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, io_read_attribute(h_grp, "NumPart_Total_HighWord", LONGLONG, numParticles_highWord); + /* Check that the user is not doing something silly when they e.g. restart + * from a snapshot by asserting that the current scale-factor (from + * parameter file) and the redshift in the header are consistent */ + if (with_cosmology) { + io_assert_valid_header_cosmology(h_grp, a); + } + for (int ptype = 0; ptype < swift_type_count; ++ptype) N_total[ptype] = (numParticles[ptype]) + (numParticles_highWord[ptype] << 32); @@ -666,11 +687,14 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store all gravity particles */ if (with_gravity) { - Ndm = N[1]; + Ndm = N[swift_type_dark_matter]; + Ndm_background = N[swift_type_dark_matter_background]; *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + + N[swift_type_dark_matter_background] + (with_stars ? N[swift_type_stars] : 0) + (with_black_holes ? N[swift_type_black_hole] : 0); + *Ngparts_background = Ndm_background; if (swift_memalign("gparts", (void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); @@ -731,6 +755,13 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, } break; + case swift_type_dark_matter_background: + if (with_gravity) { + Nparticles = Ndm_background; + darkmatter_read_particles(*gparts + Ndm, list, &num_fields); + } + break; + case swift_type_stars: if (with_stars) { Nparticles = *Nstars; @@ -780,17 +811,23 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, /* Prepare the DM particles */ io_prepare_dm_gparts(&tp, *gparts, Ndm); + /* Prepare the DM background particles */ + io_prepare_dm_background_gparts(&tp, *gparts + Ndm, Ndm_background); + /* Duplicate the hydro particles into gparts */ - if (with_hydro) io_duplicate_hydro_gparts(&tp, *parts, *gparts, *Ngas, Ndm); + if (with_hydro) + io_duplicate_hydro_gparts(&tp, *parts, *gparts, *Ngas, + Ndm + Ndm_background); /* Duplicate the stars particles into gparts */ if (with_stars) - io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, Ndm + *Ngas); + io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, + Ndm + Ndm_background + *Ngas); /* Duplicate the black holes particles into gparts */ if (with_black_holes) io_duplicate_black_holes_gparts(&tp, *bparts, *gparts, *Nblackholes, - Ndm + *Ngas + *Nstars); + Ndm + Ndm_background + *Ngas + *Nstars); threadpool_clean(&tp); } @@ -837,6 +874,8 @@ void write_output_serial(struct engine* e, const char* baseName, const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; const int with_temperature = e->policy & engine_policy_temperature; + const int with_fof = e->policy & engine_policy_fof; + const int with_DM_background = e->s->with_DM_background; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -854,7 +893,13 @@ void write_output_serial(struct engine* e, const char* baseName, // const size_t Nbaryons = Ngas + Nstars; // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; - /* Number of particles that we will write */ + size_t Ndm_background = 0; + if (with_DM_background) { + Ndm_background = io_count_dm_background_gparts(gparts, Ntot); + } + + /* Number of particles that we will write + * Recall that background particles are never inhibited and have no extras */ const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts; const size_t Ngas_written = @@ -866,7 +911,7 @@ void write_output_serial(struct engine* e, const char* baseName, const size_t Nbaryons_written = Ngas_written + Nstars_written + Nblackholes_written; const size_t Ndm_written = - Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; + Ntot_written > 0 ? Ntot_written - Nbaryons_written - Ndm_background : 0; /* File name */ char fileName[FILENAME_BUFFER_SIZE]; @@ -878,7 +923,8 @@ void write_output_serial(struct engine* e, const char* baseName, e->snapshot_output_count); /* Compute offset in the file and total number of particles */ - size_t N[swift_type_count] = {Ngas_written, Ndm_written, 0, 0, + size_t N[swift_type_count] = {Ngas_written, Ndm_written, + Ndm_background, 0, Nstars_written, Nblackholes_written}; long long N_total[swift_type_count] = {0}; long long offset[swift_type_count] = {0}; @@ -1172,6 +1218,9 @@ void write_output_serial(struct engine* e, const char* baseName, num_fields += cooling_write_particles( parts, xparts, list + num_fields, e->cooling_func); } + if (with_fof) { + num_fields += fof_write_parts(parts, xparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_parts(parts, xparts, list + num_fields); @@ -1210,6 +1259,10 @@ void write_output_serial(struct engine* e, const char* baseName, cooling_write_particles(parts_written, xparts_written, list + num_fields, e->cooling_func); } + if (with_fof) { + num_fields += fof_write_parts(parts_written, xparts_written, + list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_parts( parts_written, xparts_written, list + num_fields); @@ -1225,9 +1278,14 @@ void write_output_serial(struct engine* e, const char* baseName, case swift_type_dark_matter: { if (Ntot == Ndm_written) { - /* This is a DM-only run without inhibited particles */ + /* This is a DM-only run without background or inhibited particles + */ Nparticles = Ntot; darkmatter_write_particles(gparts, list, &num_fields); + if (with_fof) { + num_fields += + fof_write_gparts(gparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_gparts(e->s->gpart_group_data, list + num_fields); @@ -1261,6 +1319,10 @@ void write_output_serial(struct engine* e, const char* baseName, /* Select the fields to write */ darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_fof) { + num_fields += + fof_write_gparts(gparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_gparts( gpart_group_data_written, list + num_fields); @@ -1268,16 +1330,58 @@ void write_output_serial(struct engine* e, const char* baseName, } } break; + case swift_type_dark_matter_background: { + + /* Ok, we need to fish out the particles we want */ + Nparticles = Ndm_background; + + /* Allocate temporary array */ + if (swift_memalign("gparts_written", (void**)&gparts_written, + gpart_align, + Ndm_background * sizeof(struct gpart)) != 0) + error("Error while allocating temporart memory for gparts"); + + if (with_stf) { + if (swift_memalign("gpart_group_written", + (void**)&gpart_group_data_written, gpart_align, + Ndm_background * + sizeof(struct velociraptor_gpart_data)) != + 0) + error( + "Error while allocating temporart memory for gparts STF " + "data"); + } + + /* Collect the non-inhibited DM particles from gpart */ + io_collect_gparts_background_to_write( + gparts, e->s->gpart_group_data, gparts_written, + gpart_group_data_written, Ntot, Ndm_background, with_stf); + + /* Select the fields to write */ + darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts_written, list + num_fields); + } + if (with_stf) { + num_fields += velociraptor_write_gparts(gpart_group_data_written, + list + num_fields); + } + + } break; + case swift_type_stars: { if (Nstars == Nstars_written) { /* No inhibted particles: easy case */ Nparticles = Nstars; - stars_write_particles(sparts, list, &num_fields); + stars_write_particles(sparts, list, &num_fields, with_cosmology); num_fields += chemistry_write_sparticles(sparts, list + num_fields); num_fields += tracers_write_sparticles(sparts, list + num_fields, with_cosmology); + if (with_fof) { + num_fields += fof_write_sparts(sparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_sparts(sparts, list + num_fields); @@ -1298,11 +1402,16 @@ void write_output_serial(struct engine* e, const char* baseName, Nstars_written); /* Select the fields to write */ - stars_write_particles(sparts_written, list, &num_fields); + stars_write_particles(sparts_written, list, &num_fields, + with_cosmology); num_fields += - chemistry_write_sparticles(sparts, list + num_fields); - num_fields += tracers_write_sparticles(sparts, list + num_fields, - with_cosmology); + chemistry_write_sparticles(sparts_written, list + num_fields); + num_fields += tracers_write_sparticles( + sparts_written, list + num_fields, with_cosmology); + if (with_fof) { + num_fields += + fof_write_sparts(sparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_sparts(sparts_written, list + num_fields); @@ -1315,10 +1424,13 @@ void write_output_serial(struct engine* e, const char* baseName, /* No inhibted particles: easy case */ Nparticles = Nblackholes; - black_holes_write_particles(bparts, list, &num_fields); + black_holes_write_particles(bparts, list, &num_fields, + with_cosmology); num_fields += chemistry_write_bparticles(bparts, list + num_fields); - + if (with_fof) { + num_fields += fof_write_bparts(bparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_bparts(bparts, list + num_fields); @@ -1339,10 +1451,14 @@ void write_output_serial(struct engine* e, const char* baseName, Nblackholes_written); /* Select the fields to write */ - black_holes_write_particles(bparts_written, list, &num_fields); + black_holes_write_particles(bparts_written, list, &num_fields, + with_cosmology); num_fields += chemistry_write_bparticles(bparts, list + num_fields); - + if (with_fof) { + num_fields += + fof_write_bparts(bparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_bparts(bparts_written, list + num_fields); @@ -1359,8 +1475,8 @@ void write_output_serial(struct engine* e, const char* baseName, /* Did the user cancel this field? */ char field[PARSER_MAX_LINE_SIZE]; - sprintf(field, "SelectOutput:%s_%s", list[i].name, - part_type_names[ptype]); + sprintf(field, "SelectOutput:%.*s_%s", FIELD_BUFFER_SIZE, + list[i].name, part_type_names[ptype]); int should_write = parser_get_opt_param_int(params, field, 1); if (should_write) diff --git a/src/serial_io.h b/src/serial_io.h index 7d994181b135e9758a9e3a52971366c2d871f627..5b51b9b41870846c40b8d3d358f31ebfe7202203 100644 --- a/src/serial_io.h +++ b/src/serial_io.h @@ -38,12 +38,12 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, struct bpart** bparts, size_t* Ngas, - size_t* Ngparts, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_stars, int with_black_holes, int cleanup_h, - int cleanup_sqrt_a, double h, double a, int mpi_rank, - int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads, - int dry_run); + size_t* Ngparts, size_t* Ngparts_background, size_t* Nstars, + size_t* Nblackholes, int* flag_entropy, int with_hydro, + int with_gravity, int with_stars, int with_black_holes, + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int n_threads, int dry_run); void write_output_serial(struct engine* e, const char* baseName, const struct unit_system* internal_units, @@ -56,6 +56,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, int mpi_rank, long long offset, const struct unit_system* internal_units, const struct unit_system* snapshot_units); -#endif + +#endif /* HAVE_HDF5 && WITH_MPI && !HAVE_PARALLEL_HDF5 */ #endif /* SWIFT_SERIAL_IO_H */ diff --git a/src/single_io.c b/src/single_io.c index 7a7856d29ce35435f750f2f71d66e0269d114261..ad2560db96f7bf2b4c60141bf9ff945b2bb745b0 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -44,6 +44,7 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "fof_io.h" #include "gravity_io.h" #include "gravity_properties.h" #include "hydro_io.h" @@ -323,18 +324,18 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, props.dimension, props.type); /* Write unit conversion factors for this data set */ - char buffer[FIELD_BUFFER_SIZE]; - units_cgs_conversion_string(buffer, snapshot_units, props.units); + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, snapshot_units, props.units, + props.scale_factor_exponent); float baseUnitsExp[5]; units_get_base_unit_exponents_array(baseUnitsExp, props.units); - const float a_factor_exp = units_a_factor(snapshot_units, props.units); io_write_attribute_f(h_data, "U_M exponent", baseUnitsExp[UNIT_MASS]); io_write_attribute_f(h_data, "U_L exponent", baseUnitsExp[UNIT_LENGTH]); io_write_attribute_f(h_data, "U_t exponent", baseUnitsExp[UNIT_TIME]); io_write_attribute_f(h_data, "U_I exponent", baseUnitsExp[UNIT_CURRENT]); io_write_attribute_f(h_data, "U_T exponent", baseUnitsExp[UNIT_TEMPERATURE]); - io_write_attribute_f(h_data, "h-scale exponent", 0); - io_write_attribute_f(h_data, "a-scale exponent", a_factor_exp); + io_write_attribute_f(h_data, "h-scale exponent", 0.f); + io_write_attribute_f(h_data, "a-scale exponent", props.scale_factor_exponent); io_write_attribute_s(h_data, "Expression for physical CGS units", buffer); /* Write the actual number this conversion factor corresponds to */ @@ -346,8 +347,16 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, factor); io_write_attribute_d( h_data, - "Conversion factor to phyical CGS (including cosmological corrections)", - factor * pow(e->cosmology->a, a_factor_exp)); + "Conversion factor to physical CGS (including cosmological corrections)", + factor * pow(e->cosmology->a, props.scale_factor_exponent)); + +#ifdef SWIFT_DEBUG_CHECKS + if (strlen(props.description) == 0) + error("Invalid (empty) description of the field '%s'", props.name); +#endif + + /* Write the full description */ + io_write_attribute_s(h_data, "Description", props.description); /* Free and close everything */ swift_free("writebuff", temp); @@ -376,6 +385,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? * @param with_black_hole Are we reading black hole particles ? + * @param with_cosmology Are we running with cosmology ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -395,11 +405,11 @@ void read_ic_single(const char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, struct bpart** bparts, size_t* Ngas, - size_t* Ngparts, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_stars, int with_black_holes, int cleanup_h, - int cleanup_sqrt_a, double h, double a, int n_threads, - int dry_run) { + size_t* Ngparts, size_t* Ngparts_background, size_t* Nstars, + size_t* Nblackholes, int* flag_entropy, int with_hydro, + int with_gravity, int with_stars, int with_black_holes, + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -410,9 +420,11 @@ void read_ic_single(const char* fileName, size_t N[swift_type_count] = {0}; int dimension = 3; /* Assume 3D if nothing is specified */ size_t Ndm = 0; + size_t Ndm_background = 0; /* Initialise counters */ - *Ngas = 0, *Ngparts = 0, *Nstars = 0, *Nblackholes = 0; + *Ngas = 0, *Ngparts = 0, *Ngparts_background = 0, *Nstars = 0, + *Nblackholes = 0; /* Open file */ /* message("Opening file '%s' as IC.", fileName); */ @@ -457,6 +469,13 @@ void read_ic_single(const char* fileName, io_read_attribute(h_grp, "NumPart_Total_HighWord", LONGLONG, numParticles_highWord); + /* Check that the user is not doing something silly when they e.g. restart + * from a snapshot by asserting that the current scale-factor (from + * parameter file) and the redshift in the header are consistent */ + if (with_cosmology) { + io_assert_valid_header_cosmology(h_grp, a); + } + for (int ptype = 0; ptype < swift_type_count; ++ptype) N[ptype] = (numParticles[ptype]) + (numParticles_highWord[ptype] << 32); @@ -554,10 +573,13 @@ void read_ic_single(const char* fileName, /* Allocate memory to store all gravity particles */ if (with_gravity) { Ndm = N[swift_type_dark_matter]; + Ndm_background = N[swift_type_dark_matter_background]; *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + + N[swift_type_dark_matter_background] + (with_stars ? N[swift_type_stars] : 0) + (with_black_holes ? N[swift_type_black_hole] : 0); + *Ngparts_background = Ndm_background; if (swift_memalign("gparts", (void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); @@ -606,6 +628,13 @@ void read_ic_single(const char* fileName, } break; + case swift_type_dark_matter_background: + if (with_gravity) { + Nparticles = Ndm_background; + darkmatter_read_particles(*gparts + Ndm, list, &num_fields); + } + break; + case swift_type_stars: if (with_stars) { Nparticles = *Nstars; @@ -644,17 +673,23 @@ void read_ic_single(const char* fileName, /* Prepare the DM particles */ io_prepare_dm_gparts(&tp, *gparts, Ndm); + /* Prepare the DM background particles */ + io_prepare_dm_background_gparts(&tp, *gparts + Ndm, Ndm_background); + /* Duplicate the hydro particles into gparts */ - if (with_hydro) io_duplicate_hydro_gparts(&tp, *parts, *gparts, *Ngas, Ndm); + if (with_hydro) + io_duplicate_hydro_gparts(&tp, *parts, *gparts, *Ngas, + Ndm + Ndm_background); /* Duplicate the star particles into gparts */ if (with_stars) - io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, Ndm + *Ngas); + io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, + Ndm + Ndm_background + *Ngas); /* Duplicate the black hole particles into gparts */ if (with_black_holes) io_duplicate_black_holes_gparts(&tp, *bparts, *gparts, *Nblackholes, - Ndm + *Ngas + *Nstars); + Ndm + Ndm_background + *Ngas + *Nstars); threadpool_clean(&tp); } @@ -699,6 +734,8 @@ void write_output_single(struct engine* e, const char* baseName, const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; const int with_temperature = e->policy & engine_policy_temperature; + const int with_fof = e->policy & engine_policy_fof; + const int with_DM_background = e->s->with_DM_background; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -714,7 +751,13 @@ void write_output_single(struct engine* e, const char* baseName, // const size_t Nbaryons = Ngas + Nstars; // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; - /* Number of particles that we will write */ + size_t Ndm_background = 0; + if (with_DM_background) { + Ndm_background = io_count_dm_background_gparts(gparts, Ntot); + } + + /* Number of particles that we will write + * Recall that background particles are never inhibited and have no extras */ const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts; const size_t Ngas_written = @@ -726,11 +769,12 @@ void write_output_single(struct engine* e, const char* baseName, const size_t Nbaryons_written = Ngas_written + Nstars_written + Nblackholes_written; const size_t Ndm_written = - Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; + Ntot_written > 0 ? Ntot_written - Nbaryons_written - Ndm_background : 0; /* Format things in a Gadget-friendly array */ long long N_total[swift_type_count] = { - (long long)Ngas_written, (long long)Ndm_written, 0, 0, + (long long)Ngas_written, (long long)Ndm_written, + (long long)Ndm_background, 0, (long long)Nstars_written, (long long)Nblackholes_written}; /* File name */ @@ -971,6 +1015,9 @@ void write_output_single(struct engine* e, const char* baseName, num_fields += cooling_write_particles( parts, xparts, list + num_fields, e->cooling_func); } + if (with_fof) { + num_fields += fof_write_parts(parts, xparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_parts(parts, xparts, list + num_fields); @@ -1009,6 +1056,10 @@ void write_output_single(struct engine* e, const char* baseName, cooling_write_particles(parts_written, xparts_written, list + num_fields, e->cooling_func); } + if (with_fof) { + num_fields += fof_write_parts(parts_written, xparts_written, + list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_parts( parts_written, xparts_written, list + num_fields); @@ -1023,9 +1074,12 @@ void write_output_single(struct engine* e, const char* baseName, case swift_type_dark_matter: { if (Ntot == Ndm_written) { - /* This is a DM-only run without inhibited particles */ + /* This is a DM-only run without background or inhibited particles */ N = Ntot; darkmatter_write_particles(gparts, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_gparts(e->s->gpart_group_data, list + num_fields); @@ -1058,6 +1112,9 @@ void write_output_single(struct engine* e, const char* baseName, /* Select the fields to write */ darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_gparts(gpart_group_data_written, list + num_fields); @@ -1065,15 +1122,55 @@ void write_output_single(struct engine* e, const char* baseName, } } break; + case swift_type_dark_matter_background: { + + /* Ok, we need to fish out the particles we want */ + N = Ndm_background; + + /* Allocate temporary array */ + if (swift_memalign("gparts_written", (void**)&gparts_written, + gpart_align, + Ndm_background * sizeof(struct gpart)) != 0) + error("Error while allocating temporart memory for gparts"); + + if (with_stf) { + if (swift_memalign( + "gpart_group_written", (void**)&gpart_group_data_written, + gpart_align, + Ndm_background * sizeof(struct velociraptor_gpart_data)) != 0) + error( + "Error while allocating temporart memory for gparts STF " + "data"); + } + + /* Collect the non-inhibited DM particles from gpart */ + io_collect_gparts_background_to_write( + gparts, e->s->gpart_group_data, gparts_written, + gpart_group_data_written, Ntot, Ndm_background, with_stf); + + /* Select the fields to write */ + darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_fof) { + num_fields += fof_write_gparts(gparts_written, list + num_fields); + } + if (with_stf) { + num_fields += velociraptor_write_gparts(gpart_group_data_written, + list + num_fields); + } + } break; + case swift_type_stars: { if (Nstars == Nstars_written) { /* No inhibted particles: easy case */ N = Nstars; - stars_write_particles(sparts, list, &num_fields); + stars_write_particles(sparts, list, &num_fields, with_cosmology); num_fields += chemistry_write_sparticles(sparts, list + num_fields); num_fields += tracers_write_sparticles(sparts, list + num_fields, with_cosmology); + if (with_fof) { + num_fields += fof_write_sparts(sparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_sparts(sparts, list + num_fields); } @@ -1093,11 +1190,15 @@ void write_output_single(struct engine* e, const char* baseName, Nstars_written); /* Select the fields to write */ - stars_write_particles(sparts_written, list, &num_fields); + stars_write_particles(sparts_written, list, &num_fields, + with_cosmology); num_fields += chemistry_write_sparticles(sparts_written, list + num_fields); num_fields += tracers_write_sparticles( sparts_written, list + num_fields, with_cosmology); + if (with_fof) { + num_fields += fof_write_sparts(sparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_sparts(sparts_written, list + num_fields); @@ -1110,9 +1211,12 @@ void write_output_single(struct engine* e, const char* baseName, /* No inhibted particles: easy case */ N = Nblackholes; - black_holes_write_particles(bparts, list, &num_fields); + black_holes_write_particles(bparts, list, &num_fields, + with_cosmology); num_fields += chemistry_write_bparticles(bparts, list + num_fields); - + if (with_fof) { + num_fields += fof_write_bparts(bparts, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_bparts(bparts, list + num_fields); } @@ -1132,9 +1236,13 @@ void write_output_single(struct engine* e, const char* baseName, Nblackholes_written); /* Select the fields to write */ - black_holes_write_particles(bparts_written, list, &num_fields); + black_holes_write_particles(bparts_written, list, &num_fields, + with_cosmology); num_fields += chemistry_write_bparticles(bparts_written, list + num_fields); + if (with_fof) { + num_fields += fof_write_bparts(bparts_written, list + num_fields); + } if (with_stf) { num_fields += velociraptor_write_bparts(bparts_written, list + num_fields); @@ -1151,7 +1259,7 @@ void write_output_single(struct engine* e, const char* baseName, /* Did the user cancel this field? */ char field[PARSER_MAX_LINE_SIZE]; - sprintf(field, "SelectOutput:%s_%s", list[i].name, + sprintf(field, "SelectOutput:%.*s_%s", FIELD_BUFFER_SIZE, list[i].name, part_type_names[ptype]); int should_write = parser_get_opt_param_int(params, field, 1); @@ -1187,4 +1295,4 @@ void write_output_single(struct engine* e, const char* baseName, e->snapshot_output_count++; } -#endif /* HAVE_HDF5 */ +#endif /* HAVE_HDF5 && !WITH_MPI */ diff --git a/src/single_io.h b/src/single_io.h index 9ff04c893378d7f8c01621e7dbf387dc939d0157..591f0ef8be6ed831d5cf2a6d465498cc259286d5 100644 --- a/src/single_io.h +++ b/src/single_io.h @@ -34,11 +34,11 @@ void read_ic_single(const char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, struct bpart** bparts, size_t* Ngas, - size_t* Ndm, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_stars, int with_black_holes, int cleanup_h, - int cleanup_sqrt_a, double h, double a, int nr_threads, - int dry_run); + size_t* Ndm, size_t* Ndm_background, size_t* Nstars, + size_t* Nblackholes, int* flag_entropy, int with_hydro, + int with_gravity, int with_stars, int with_black_holes, + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int nr_threads, int dry_run); void write_output_single(struct engine* e, const char* baseName, const struct unit_system* internal_units, diff --git a/src/space.c b/src/space.c index f7c3b7f77953a87985692f4c1a03cfaf97b93a5e..eb498035d7c912f331870cfb0bb8bf84ad1559c4 100644 --- a/src/space.c +++ b/src/space.c @@ -56,9 +56,9 @@ #include "memuse.h" #include "minmax.h" #include "multipole.h" +#include "pressure_floor.h" #include "restart.h" #include "sort_part.h" -#include "star_formation.h" #include "star_formation_logger.h" #include "stars.h" #include "threadpool.h" @@ -92,6 +92,9 @@ int space_extra_gparts = space_extra_gparts_default; int engine_max_parts_per_ghost = engine_max_parts_per_ghost_default; int engine_max_sparts_per_ghost = engine_max_sparts_per_ghost_default; +/*! Maximal depth at which the stars resort task can be pushed */ +int engine_star_resort_task_depth = engine_star_resort_task_depth_default; + /*! Expected maximal number of strays received at a rebuild */ int space_expected_max_nr_strays = space_expected_max_nr_strays_default; #if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) @@ -238,9 +241,11 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->black_holes.density_ghost = NULL; c->black_holes.swallow_ghost[0] = NULL; c->black_holes.swallow_ghost[1] = NULL; + c->black_holes.swallow_ghost[2] = NULL; c->black_holes.density = NULL; c->black_holes.swallow = NULL; - c->black_holes.do_swallow = NULL; + c->black_holes.do_gas_swallow = NULL; + c->black_holes.do_bh_swallow = NULL; c->black_holes.feedback = NULL; c->kick1 = NULL; c->kick2 = NULL; @@ -1926,7 +1931,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Check that the multipole construction went OK */ if (s->with_self_gravity) for (int k = 0; k < s->nr_cells; k++) - cell_check_multipole(&s->cells_top[k]); + cell_check_multipole(&s->cells_top[k], s->e->gravity_properties); #endif /* Clean up any stray sort indices in the cell buffer. */ @@ -3601,7 +3606,8 @@ void space_split_recursive(struct space *s, struct cell *c, if (s->with_self_gravity) { if (gcount > 0) { - gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count); + gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count, + e->gravity_properties); } else { @@ -3943,9 +3949,12 @@ void space_synchronize_particle_positions_mapper(void *map_data, int nr_gparts, if (gp->type == swift_type_dark_matter) continue; + else if (gp->type == swift_type_dark_matter_background) + continue; + else if (gp->type == swift_type_gas) { - /* Get it's gassy friend */ + /* Get its gassy friend */ struct part *p = &s->parts[-gp->id_or_neg_offset]; struct xpart *xp = &s->xparts[-gp->id_or_neg_offset]; @@ -3963,7 +3972,7 @@ void space_synchronize_particle_positions_mapper(void *map_data, int nr_gparts, else if (gp->type == swift_type_stars) { - /* Get it's stellar friend */ + /* Get its stellar friend */ struct spart *sp = &s->sparts[-gp->id_or_neg_offset]; /* Synchronize positions */ @@ -3976,7 +3985,7 @@ void space_synchronize_particle_positions_mapper(void *map_data, int nr_gparts, else if (gp->type == swift_type_black_hole) { - /* Get it's black hole friend */ + /* Get its black hole friend */ struct bpart *bp = &s->bparts[-gp->id_or_neg_offset]; /* Synchronize positions */ @@ -3986,6 +3995,10 @@ void space_synchronize_particle_positions_mapper(void *map_data, int nr_gparts, gp->mass = bp->mass; } + + else { + error("Invalid type!"); + } } } @@ -4028,7 +4041,6 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, const int with_gravity = e->policy & engine_policy_self_gravity; const struct chemistry_global_data *chemistry = e->chemistry; - const struct star_formation *star_formation = e->star_formation; const struct cooling_function_data *cool_func = e->cooling_func; /* Check that the smoothing lengths are non-zero */ @@ -4079,9 +4091,8 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, /* Also initialise the chemistry */ chemistry_first_init_part(phys_const, us, cosmo, chemistry, &p[k], &xp[k]); - /* Also initialise the star formation */ - star_formation_first_init_part(phys_const, us, cosmo, star_formation, &p[k], - &xp[k]); + /* Also initialise the pressure floor */ + pressure_floor_first_init_part(phys_const, us, cosmo, &p[k], &xp[k]); /* And the cooling */ cooling_first_init_part(phys_const, us, cosmo, cool_func, &p[k], &xp[k]); @@ -4091,7 +4102,7 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, cool_func); /* And the black hole markers */ - black_holes_mark_as_not_swallowed(&p[k].black_holes_data); + black_holes_mark_part_as_not_swallowed(&p[k].black_holes_data); #ifdef SWIFT_DEBUG_CHECKS /* Check part->gpart->part linkeage. */ @@ -4319,6 +4330,9 @@ void space_first_init_bparts_mapper(void *restrict map_data, int count, black_holes_first_init_bpart(&bp[k], props); + /* And the black hole merger markers */ + black_holes_mark_bpart_as_not_swallowed(&bp[k].merger_data); + #ifdef SWIFT_DEBUG_CHECKS if (bp[k].gpart && bp[k].gpart->id_or_neg_offset != -(k + delta)) error("Invalid gpart -> bpart link"); @@ -4360,7 +4374,7 @@ void space_init_parts_mapper(void *restrict map_data, int count, for (int k = 0; k < count; k++) { hydro_init_part(&parts[k], hs); chemistry_init_part(&parts[k], e->chemistry); - star_formation_init_part(&parts[k], e->star_formation); + pressure_floor_init_part(&parts[k], &xparts[k]); tracers_after_init(&parts[k], &xparts[k], e->internal_units, e->physical_constants, with_cosmology, e->cosmology, e->hydro_properties, e->cooling_func, e->time); @@ -4520,6 +4534,7 @@ void space_convert_quantities(struct space *s, int verbose) { * @param hydro flag whether we are doing hydro or not? * @param self_gravity flag whether we are doing gravity or not? * @param star_formation flag whether we are doing star formation or not? + * @param DM_background Are we running with some DM background particles? * @param verbose Print messages to stdout or not. * @param dry_run If 1, just initialise stuff, don't do anything with the parts. * @@ -4534,7 +4549,8 @@ void space_init(struct space *s, struct swift_params *params, struct bpart *bparts, size_t Npart, size_t Ngpart, size_t Nspart, size_t Nbpart, int periodic, int replicate, int generate_gas_in_ics, int hydro, int self_gravity, - int star_formation, int verbose, int dry_run) { + int star_formation, int DM_background, int verbose, + int dry_run) { /* Clean-up everything */ bzero(s, sizeof(struct space)); @@ -4547,6 +4563,7 @@ void space_init(struct space *s, struct swift_params *params, s->with_self_gravity = self_gravity; s->with_hydro = hydro; s->with_star_formation = star_formation; + s->with_DM_background = DM_background; s->nr_parts = Npart; s->nr_gparts = Ngpart; s->nr_sparts = Nspart; @@ -4586,8 +4603,9 @@ void space_init(struct space *s, struct swift_params *params, Ngpart = s->nr_gparts; #ifdef SWIFT_DEBUG_CHECKS - part_verify_links(parts, gparts, sparts, bparts, Npart, Ngpart, Nspart, - Nbpart, 1); + if (!dry_run) + part_verify_links(parts, gparts, sparts, bparts, Npart, Ngpart, Nspart, + Nbpart, 1); #endif } @@ -4596,6 +4614,9 @@ void space_init(struct space *s, struct swift_params *params, error("Value of 'InitialConditions:replicate' (%d) is too small", replicate); if (replicate > 1) { + if (DM_background) + error("Can't replicate the space if background DM particles are in use."); + space_replicate(s, replicate, verbose); parts = s->parts; gparts = s->gparts; @@ -4834,16 +4855,19 @@ void space_replicate(struct space *s, int replicate, int verbose) { const size_t nr_parts = s->nr_parts; const size_t nr_gparts = s->nr_gparts; const size_t nr_sparts = s->nr_sparts; - const size_t nr_dm = nr_gparts - nr_parts - nr_sparts; + const size_t nr_bparts = s->nr_bparts; + const size_t nr_dm = nr_gparts - nr_parts - nr_sparts - nr_bparts; s->size_parts = s->nr_parts = nr_parts * factor; s->size_gparts = s->nr_gparts = nr_gparts * factor; s->size_sparts = s->nr_sparts = nr_sparts * factor; + s->size_bparts = s->nr_bparts = nr_bparts * factor; /* Allocate space for new particles */ struct part *parts = NULL; struct gpart *gparts = NULL; struct spart *sparts = NULL; + struct bpart *bparts = NULL; if (swift_memalign("parts", (void **)&parts, part_align, s->nr_parts * sizeof(struct part)) != 0) @@ -4857,6 +4881,10 @@ void space_replicate(struct space *s, int replicate, int verbose) { s->nr_sparts * sizeof(struct spart)) != 0) error("Failed to allocate new spart array."); + if (swift_memalign("bparts", (void **)&bparts, bpart_align, + s->nr_bparts * sizeof(struct bpart)) != 0) + error("Failed to allocate new bpart array."); + /* Replicate everything */ for (int i = 0; i < replicate; ++i) { for (int j = 0; j < replicate; ++j) { @@ -4868,6 +4896,8 @@ void space_replicate(struct space *s, int replicate, int verbose) { nr_parts * sizeof(struct part)); memcpy(sparts + offset * nr_sparts, s->sparts, nr_sparts * sizeof(struct spart)); + memcpy(bparts + offset * nr_bparts, s->bparts, + nr_bparts * sizeof(struct bpart)); memcpy(gparts + offset * nr_gparts, s->gparts, nr_gparts * sizeof(struct gpart)); @@ -4889,6 +4919,11 @@ void space_replicate(struct space *s, int replicate, int verbose) { sparts[n].x[1] += shift[1]; sparts[n].x[2] += shift[2]; } + for (size_t n = offset * nr_bparts; n < (offset + 1) * nr_bparts; ++n) { + bparts[n].x[0] += shift[0]; + bparts[n].x[1] += shift[1]; + bparts[n].x[2] += shift[2]; + } /* Set the correct links (recall gpart are sorted by type at start-up): first DM (unassociated gpart), then gas, then stars */ @@ -4910,6 +4945,16 @@ void space_replicate(struct space *s, int replicate, int verbose) { gparts[offset_gpart + n].id_or_neg_offset = -(offset_spart + n); } } + if (nr_bparts > 0 && nr_gparts > 0) { + const size_t offset_bpart = offset * nr_bparts; + const size_t offset_gpart = + offset * nr_gparts + nr_dm + nr_parts + nr_sparts; + + for (size_t n = 0; n < nr_bparts; ++n) { + bparts[offset_bpart + n].gpart = &gparts[offset_gpart + n]; + gparts[offset_gpart + n].id_or_neg_offset = -(offset_bpart + n); + } + } } } } @@ -4918,9 +4963,11 @@ void space_replicate(struct space *s, int replicate, int verbose) { swift_free("parts", s->parts); swift_free("gparts", s->gparts); swift_free("sparts", s->sparts); + swift_free("bparts", s->bparts); s->parts = parts; s->gparts = gparts; s->sparts = sparts; + s->bparts = bparts; /* Finally, update the domain size */ s->dim[0] *= replicate; @@ -5231,8 +5278,8 @@ void space_check_limiter(struct space *s) { /** * @brief #threadpool mapper function for the swallow debugging check */ -void space_check_swallow_mapper(void *map_data, int nr_parts, - void *extra_data) { +void space_check_part_swallow_mapper(void *map_data, int nr_parts, + void *extra_data) { #ifdef SWIFT_DEBUG_CHECKS /* Unpack the data */ struct part *restrict parts = (struct part *)map_data; @@ -5243,7 +5290,7 @@ void space_check_swallow_mapper(void *map_data, int nr_parts, if (parts[k].time_bin == time_bin_inhibited) continue; const long long swallow_id = - black_holes_get_swallow_id(&parts[k].black_holes_data); + black_holes_get_part_swallow_id(&parts[k].black_holes_data); if (swallow_id != -1) error("Particle has not been swallowed! id=%lld", parts[k].id); @@ -5253,6 +5300,31 @@ void space_check_swallow_mapper(void *map_data, int nr_parts, #endif } +/** + * @brief #threadpool mapper function for the swallow debugging check + */ +void space_check_bpart_swallow_mapper(void *map_data, int nr_bparts, + void *extra_data) { +#ifdef SWIFT_DEBUG_CHECKS + /* Unpack the data */ + struct bpart *restrict bparts = (struct bpart *)map_data; + + /* Verify that all particles have been swallowed or are untouched */ + for (int k = 0; k < nr_bparts; k++) { + + if (bparts[k].time_bin == time_bin_inhibited) continue; + + const long long swallow_id = + black_holes_get_bpart_swallow_id(&bparts[k].merger_data); + + if (swallow_id != -1) + error("BH particle has not been swallowed! id=%lld", bparts[k].id); + } +#else + error("Calling debugging code without debugging flag activated."); +#endif +} + /** * @brief Checks that all particles have their swallow flag in a "no swallow" * state. @@ -5264,8 +5336,11 @@ void space_check_swallow_mapper(void *map_data, int nr_parts, void space_check_swallow(struct space *s) { #ifdef SWIFT_DEBUG_CHECKS - threadpool_map(&s->e->threadpool, space_check_swallow_mapper, s->parts, - s->nr_parts, sizeof(struct part), 1000, NULL); + threadpool_map(&s->e->threadpool, space_check_part_swallow_mapper, s->parts, + s->nr_parts, sizeof(struct part), 0, NULL); + + threadpool_map(&s->e->threadpool, space_check_bpart_swallow_mapper, s->bparts, + s->nr_bparts, sizeof(struct bpart), 0, NULL); #else error("Calling debugging code without debugging flag activated."); #endif @@ -5340,6 +5415,7 @@ void space_clean(struct space *s) { swift_free("xparts", s->xparts); swift_free("gparts", s->gparts); swift_free("sparts", s->sparts); + swift_free("bparts", s->bparts); } /** @@ -5381,6 +5457,18 @@ void space_struct_dump(struct space *s, FILE *stream) { "space_extra_sparts", "space_extra_sparts"); restart_write_blocks(&space_extra_bparts, sizeof(int), 1, stream, "space_extra_bparts", "space_extra_bparts"); + restart_write_blocks(&space_expected_max_nr_strays, sizeof(int), 1, stream, + "space_expected_max_nr_strays", + "space_expected_max_nr_strays"); + restart_write_blocks(&engine_max_parts_per_ghost, sizeof(int), 1, stream, + "engine_max_parts_per_ghost", + "engine_max_parts_per_ghost"); + restart_write_blocks(&engine_max_sparts_per_ghost, sizeof(int), 1, stream, + "engine_max_sparts_per_ghost", + "engine_max_sparts_per_ghost"); + restart_write_blocks(&engine_star_resort_task_depth, sizeof(int), 1, stream, + "engine_star_resort_task_depth", + "engine_star_resort_task_depth"); /* More things to write. */ if (s->nr_parts > 0) { @@ -5439,6 +5527,14 @@ void space_struct_restore(struct space *s, FILE *stream) { "space_extra_sparts"); restart_read_blocks(&space_extra_bparts, sizeof(int), 1, stream, NULL, "space_extra_bparts"); + restart_read_blocks(&space_expected_max_nr_strays, sizeof(int), 1, stream, + NULL, "space_expected_max_nr_strays"); + restart_read_blocks(&engine_max_parts_per_ghost, sizeof(int), 1, stream, NULL, + "engine_max_parts_per_ghost"); + restart_read_blocks(&engine_max_sparts_per_ghost, sizeof(int), 1, stream, + NULL, "engine_max_sparts_per_ghost"); + restart_read_blocks(&engine_star_resort_task_depth, sizeof(int), 1, stream, + NULL, "engine_star_resort_task_depth"); /* Things that should be reconstructed in a rebuild. */ s->cells_top = NULL; @@ -5574,14 +5670,15 @@ void space_write_cell(const struct space *s, FILE *f, const struct cell *c) { * @brief Write a csv file containing the cell hierarchy * * @param s The #space. + * @param j The file number. */ -void space_write_cell_hierarchy(const struct space *s) { +void space_write_cell_hierarchy(const struct space *s, int j) { #ifdef SWIFT_CELL_GRAPH /* Open file */ char filename[200]; - sprintf(filename, "cell_hierarchy_%04i.csv", engine_rank); + sprintf(filename, "cell_hierarchy_%04i_%04i.csv", j, engine_rank); FILE *f = fopen(filename, "w"); if (f == NULL) error("Error opening task level file."); diff --git a/src/space.h b/src/space.h index c294bfae3612c699345bd4a267c40b67cffc2bf1..ad20641e4dc11559d33f512794fddf1b7453317a 100644 --- a/src/space.h +++ b/src/space.h @@ -103,6 +103,9 @@ struct space { /*! Are we doing star formation? */ int with_star_formation; + /*! Are we running with some DM background particles? */ + int with_DM_background; + /*! Width of the top-level cells. */ double width[3]; @@ -112,9 +115,6 @@ struct space { /*! The minimum top-level cell width allowed. */ double cell_min; - /*! Current maximum displacement for particles. */ - float dx_max; - /*! Space dimensions in number of top-cells. */ int cdim[3]; @@ -307,7 +307,8 @@ void space_init(struct space *s, struct swift_params *params, struct bpart *bparts, size_t Npart, size_t Ngpart, size_t Nspart, size_t Nbpart, int periodic, int replicate, int generate_gas_in_ics, int hydro, int gravity, - int star_formation, int verbose, int dry_run); + int star_formation, int DM_background, int verbose, + int dry_run); void space_sanitize(struct space *s); void space_map_cells_pre(struct space *s, int full, void (*fun)(struct cell *c, void *data), void *data); @@ -373,6 +374,6 @@ void space_free_foreign_parts(struct space *s); void space_struct_dump(struct space *s, FILE *stream); void space_struct_restore(struct space *s, FILE *stream); -void space_write_cell_hierarchy(const struct space *s); +void space_write_cell_hierarchy(const struct space *s, int j); #endif /* SWIFT_SPACE_H */ diff --git a/src/star_formation.c b/src/star_formation.c index 698a64cc636dd79f00feac3f6cc88bf519fe09c1..60cff1e2e68feaf7e71705b5079294ec478fad42 100644 --- a/src/star_formation.c +++ b/src/star_formation.c @@ -24,6 +24,7 @@ #include "part.h" #include "restart.h" #include "star_formation.h" +#include "star_formation_io.h" #include "units.h" /** diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h index 1fc6531656c94c566e1ffac502b5023e09094872..851f493801dc5cb0beee9cd07ea5415a5ad1ccf1 100644 --- a/src/star_formation/EAGLE/star_formation.h +++ b/src/star_formation/EAGLE/star_formation.h @@ -232,8 +232,11 @@ INLINE static int star_formation_is_star_forming( * because we also need to check if the physical density exceeded * the appropriate limit */ - const double Z = p->chemistry_data.smoothed_metal_mass_fraction_total; - const double X_H = p->chemistry_data.smoothed_metal_mass_fraction[0]; + const double Z = + chemistry_get_total_metal_mass_fraction_for_star_formation(p); + const float* const metal_fraction = + chemistry_get_metal_mass_fraction_for_star_formation(p); + const double X_H = metal_fraction[chemistry_element_H]; const double n_H = physical_density * X_H; /* Get the density threshold */ @@ -279,7 +282,9 @@ INLINE static void star_formation_compute_SFR( /* Hydrogen number density of this particle */ const double physical_density = hydro_get_physical_density(p, cosmo); - const double X_H = p->chemistry_data.smoothed_metal_mass_fraction[0]; + const float* const metal_fraction = + chemistry_get_metal_mass_fraction_for_star_formation(p); + const double X_H = metal_fraction[chemistry_element_H]; const double n_H = physical_density * X_H / phys_const->const_proton_mass; /* Are we above the threshold for automatic star formation? */ @@ -627,69 +632,4 @@ INLINE static void starformation_print_backend( starform->max_gas_density_HpCM3); } -/** - * @brief Finishes the density calculation. - * - * Nothing to do here. We do not need to compute any quantity in the hydro - * density loop for the EAGLE star formation model. - * - * @param p The particle to act upon - * @param cd The global star_formation information. - * @param cosmo The current cosmological model. - */ -__attribute__((always_inline)) INLINE static void star_formation_end_density( - struct part* restrict p, const struct star_formation* cd, - const struct cosmology* cosmo) {} - -/** - * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. - * - * Nothing to do here. We do not need to compute any quantity in the hydro - * density loop for the EAGLE star formation model. - * - * @param p The particle to act upon - * @param xp The extended particle data to act upon - * @param cd #star_formation containing star_formation informations. - * @param cosmo The current cosmological model. - */ -__attribute__((always_inline)) INLINE static void -star_formation_part_has_no_neighbours(struct part* restrict p, - struct xpart* restrict xp, - const struct star_formation* cd, - const struct cosmology* cosmo) {} - -/** - * @brief Sets the star_formation properties of the (x-)particles to a valid - * start state. - * - * Nothing to do here. - * - * @param phys_const The physical constant in internal units. - * @param us The unit system. - * @param cosmo The current cosmological model. - * @param data The global star_formation information used for this run. - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - */ -__attribute__((always_inline)) INLINE static void -star_formation_first_init_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct star_formation* data, - const struct part* restrict p, - struct xpart* restrict xp) {} - -/** - * @brief Sets the star_formation properties of the (x-)particles to a valid - * start state. - * - * Nothing to do here. We do not need to compute any quantity in the hydro - * density loop for the EAGLE star formation model. - * - * @param p Pointer to the particle data. - * @param data The global star_formation information. - */ -__attribute__((always_inline)) INLINE static void star_formation_init_part( - struct part* restrict p, const struct star_formation* data) {} - #endif /* SWIFT_EAGLE_STAR_FORMATION_H */ diff --git a/src/star_formation/EAGLE/star_formation_io.h b/src/star_formation/EAGLE/star_formation_io.h index cee96326e458d0581af6e62e452ac433dcf407bd..f8bf57145955d41f7ae0ecd4141651a24f1c2727 100644 --- a/src/star_formation/EAGLE/star_formation_io.h +++ b/src/star_formation/EAGLE/star_formation_io.h @@ -38,8 +38,11 @@ __attribute__((always_inline)) INLINE static int star_formation_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list) { - list[0] = - io_make_output_field("SFR", FLOAT, 1, UNIT_CONV_SFR, xparts, sf_data.SFR); + list[0] = io_make_output_field( + "StarFormationRates", FLOAT, 1, UNIT_CONV_SFR, 0.f, xparts, sf_data.SFR, + "If positive, star formation rates of the particles. If negative, stores " + "the last time/scale-factor at which the gas particle was star-forming. " + "If zero, the particle was never star-forming."); return 1; } diff --git a/src/star_formation/EAGLE/star_formation_logger.h b/src/star_formation/EAGLE/star_formation_logger.h index d634c876e52d45588ed0b93c0afc09731317c037..fea15242e9a83b81cf5c7801c0546a1bbb5a8eae 100644 --- a/src/star_formation/EAGLE/star_formation_logger.h +++ b/src/star_formation/EAGLE/star_formation_logger.h @@ -87,6 +87,28 @@ INLINE static void star_formation_logger_add( sf_update->SFR_inactive += sf_add->SFR_inactive; } +/** + * @brief add a star formation history struct to the engine star formation + * history accumulator struct + * + * @param sf_add the star formation accumulator struct which we want to add to + * the star formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add_to_accumulator( + struct star_formation_history_accumulator *sf_update, + const struct star_formation_history *sf_add) { + + /* Update the SFH structure */ + sf_update->new_stellar_mass = sf_add->new_stellar_mass; + + sf_update->SFR_active = sf_add->SFR_active; + + sf_update->SFRdt_active = sf_add->SFRdt_active; + + sf_update->SFR_inactive = sf_add->SFR_inactive; +} + /** * @brief Initialize the star formation history structure in the #engine * @@ -105,6 +127,24 @@ INLINE static void star_formation_logger_init( sfh->SFR_inactive = 0.f; } +/** + * @brief Initialize the star formation history structure in the #engine + * + * @param sfh The pointer to the star formation history structure + */ +INLINE static void star_formation_logger_accumulator_init( + struct star_formation_history_accumulator *sfh) { + + /* Initialize the collecting SFH structure to zero */ + sfh->new_stellar_mass = 0.f; + + sfh->SFR_active = 0.f; + + sfh->SFRdt_active = 0.f; + + sfh->SFR_inactive = 0.f; +} + /** * @brief Write the final SFH to a file * @@ -117,7 +157,7 @@ INLINE static void star_formation_logger_init( */ INLINE static void star_formation_logger_write_to_log_file( FILE *fp, const double time, const double a, const double z, - const struct star_formation_history sf, const int step) { + const struct star_formation_history_accumulator sf, const int step) { /* Calculate the total SFR */ const float totalSFR = sf.SFR_active + sf.SFR_inactive; diff --git a/src/star_formation/EAGLE/star_formation_logger_struct.h b/src/star_formation/EAGLE/star_formation_logger_struct.h index 2a23659c4d931735d1b82a6143b3d9f871f7137a..c03a00c97ead46f552350a43574c5bbe7ac6df1b 100644 --- a/src/star_formation/EAGLE/star_formation_logger_struct.h +++ b/src/star_formation/EAGLE/star_formation_logger_struct.h @@ -34,4 +34,21 @@ struct star_formation_history { float SFRdt_active; }; +/* Starformation history struct for the engine. + Allows to integrate in time some values. + Nothing to do in EAGLE => copy of star_formation_history */ +struct star_formation_history_accumulator { + /*! Total new stellar mass */ + float new_stellar_mass; + + /*! SFR of all particles */ + float SFR_inactive; + + /*! SFR of active particles */ + float SFR_active; + + /*! SFR*dt of active particles */ + float SFRdt_active; +}; + #endif /* SWIFT_EAGLE_STAR_FORMATION_LOGGER_STRUCT_H */ diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index c479feb5c66328f9fab8bf62593ca66b6658b79e..5fc3380fe6869bd5bcb9435fb0c129ac6fc0aad2 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 2019 Fabien Jeanquartier (fabien.jeanquartier@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 @@ -20,20 +21,24 @@ #define SWIFT_GEAR_STAR_FORMATION_H /* Local includes */ +#include "cooling.h" #include "cosmology.h" +#include "engine.h" #include "entropy_floor.h" #include "error.h" #include "hydro_properties.h" #include "parser.h" #include "part.h" #include "physical_constants.h" +#include "random.h" +#include "star_formation_struct.h" #include "units.h" /** * @brief Calculate if the gas has the potential of becoming * a star. * - * No star formation should occur, so return 0. + * Use the star formation criterion given by eq. 3 in Revaz & Jablonka 2018. * * @param starform the star formation law properties to use. * @param p the gas particles. @@ -46,7 +51,7 @@ * */ INLINE static int star_formation_is_star_forming( - const struct part* restrict p, const struct xpart* restrict xp, + struct part* restrict p, struct xpart* restrict xp, const struct star_formation* starform, const struct phys_const* phys_const, const struct cosmology* cosmo, const struct hydro_props* restrict hydro_props, @@ -54,14 +59,43 @@ INLINE static int star_formation_is_star_forming( const struct cooling_function_data* restrict cooling, const struct entropy_floor_properties* restrict entropy_floor) { - return 0; + const float temperature = cooling_get_temperature(phys_const, hydro_props, us, + cosmo, cooling, p, xp); + + const float temperature_max = starform->maximal_temperature; + + /* Check the temperature criterion */ + if (temperature > temperature_max) { + return 0; + } + + /* Get the required variables */ + const float sigma2 = p->pressure_floor_data.sigma2 * cosmo->a * cosmo->a; + const float n_jeans_2_3 = starform->n_jeans_2_3; + + const float h = p->h; + const float density = hydro_get_physical_density(p, cosmo); + + // TODO use GRACKLE */ + const float mu = hydro_props->mu_neutral; + + /* Compute the density criterion */ + const float coef = + M_PI_4 / (phys_const->const_newton_G * n_jeans_2_3 * h * h); + const float density_criterion = + coef * (hydro_gamma * phys_const->const_boltzmann_k * temperature / + (mu * phys_const->const_proton_mass) + + sigma2); + + /* Check the density criterion */ + return density > density_criterion; } /** - * @brief Compute the star-formation rate of a given particle and store - * it into the #xpart. + * @brief Compute the star-formation rate of a given particle. * - * Nothing to do here. + * Nothing to do here. Everything is done in + * #star_formation_should_convert_to_star. * * @param p #part. * @param xp the #xpart. @@ -71,15 +105,15 @@ INLINE static int star_formation_is_star_forming( * @param dt_star The time-step of this particle. */ INLINE static void star_formation_compute_SFR( - const struct part* restrict p, struct xpart* restrict xp, + struct part* restrict p, struct xpart* restrict xp, const struct star_formation* starform, const struct phys_const* phys_const, const struct cosmology* cosmo, const double dt_star) {} /** * @brief Decides whether a particle should be converted into a * star or not. - * - * No SF should occur, so return 0. + + * Compute the star formation rate from eq. 4 in Revaz & Jablonka 2012. * * @param p The #part. * @param xp The #xpart. @@ -89,18 +123,38 @@ INLINE static void star_formation_compute_SFR( * @return 1 if a conversion should be done, 0 otherwise. */ INLINE static int star_formation_should_convert_to_star( - const struct part* p, const struct xpart* xp, - const struct star_formation* starform, const struct engine* e, - const double dt_star) { + struct part* p, struct xpart* xp, const struct star_formation* starform, + const struct engine* e, const double dt_star) { + + const struct phys_const* phys_const = e->physical_constants; + const struct cosmology* cosmo = e->cosmology; + + /* Check that we are running a full time step */ + if (dt_star == 0.) { + return 0; + } + + /* Get a few variables */ + const float G = phys_const->const_newton_G; + const float density = hydro_get_physical_density(p, cosmo); - return 0; + /* Compute the probability */ + const float inv_free_fall_time = + sqrtf(density * 32.f * G * 0.33333333f * M_1_PI); + const float prob = 1.f - exp(-starform->star_formation_efficiency * + inv_free_fall_time * dt_star); + + /* Roll the dice... */ + const float random_number = + random_unit_interval(p->id, e->ti_current, random_number_star_formation); + + /* Can we form a star? */ + return random_number < prob; } /** * @brief Update the SF properties of a particle that is not star forming. * - * Nothing to do here. - * * @param p The #part. * @param xp The #xpart. * @param e The #engine. @@ -115,8 +169,6 @@ INLINE static void star_formation_update_part_not_SFR( * @brief Copies the properties of the gas particle over to the * star particle. * - * Nothing to do here. - * * @param e The #engine * @param p the gas particles. * @param xp the additional properties of the gas particles. @@ -133,21 +185,33 @@ INLINE static void star_formation_copy_properties( const struct phys_const* phys_const, const struct hydro_props* restrict hydro_props, const struct unit_system* restrict us, - const struct cooling_function_data* restrict cooling) {} + const struct cooling_function_data* restrict cooling) { -/** - * @brief initialization of the star formation law - * - * @param parameter_file The parsed parameter file - * @param phys_const Physical constants in internal units - * @param us The current internal system of units - * @param starform the star formation law properties to initialize - * - */ -INLINE static void starformation_init_backend( - struct swift_params* parameter_file, const struct phys_const* phys_const, - const struct unit_system* us, const struct hydro_props* hydro_props, - const struct star_formation* starform) {} + /* Store the current mass */ + sp->mass = hydro_get_mass(p); + sp->birth.mass = sp->mass; + + /* Store either the birth_scale_factor or birth_time depending */ + if (with_cosmology) { + sp->birth_scale_factor = cosmo->a; + } else { + sp->birth_time = e->time; + } + + // TODO copy only metals + /* Store the chemistry struct in the star particle */ + sp->chemistry_data = p->chemistry_data; + + /* Store the tracers data */ + sp->tracers_data = xp->tracers_data; + + /* Store the birth density in the star particle */ + sp->birth.density = hydro_get_physical_density(p, cosmo); + + /* Store the birth temperature*/ + sp->birth.temperature = cooling_get_temperature(phys_const, hydro_props, us, + cosmo, cooling, p, xp); +} /** * @brief Prints the used parameters of the star formation law @@ -156,66 +220,7 @@ INLINE static void starformation_init_backend( */ INLINE static void starformation_print_backend( const struct star_formation* starform) { - message("Star formation law is 'GEAR'"); } -/** - * @brief Finishes the density calculation. - * - * @param p The particle to act upon - * @param cd The global star_formation information. - * @param cosmo The current cosmological model. - */ -__attribute__((always_inline)) INLINE static void star_formation_end_density( - struct part* restrict p, const struct star_formation* cd, - const struct cosmology* cosmo) {} - -/** - * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. - * - * @param p The particle to act upon - * @param xp The extended particle data to act upon - * @param cd #star_formation containing star_formation informations. - * @param cosmo The current cosmological model. - */ -__attribute__((always_inline)) INLINE static void -star_formation_part_has_no_neighbours(struct part* restrict p, - struct xpart* restrict xp, - const struct star_formation* cd, - const struct cosmology* cosmo) {} - -/** - * @brief Sets the star_formation properties of the (x-)particles to a valid - * start state. - * - * Nothing to do here. - * - * @param phys_const The physical constant in internal units. - * @param us The unit system. - * @param cosmo The current cosmological model. - * @param data The global star_formation information used for this run. - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - */ -__attribute__((always_inline)) INLINE static void -star_formation_first_init_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct star_formation* data, - const struct part* restrict p, - struct xpart* restrict xp) {} - -/** - * @brief Sets the star_formation properties of the (x-)particles to a valid - * start state. - * - * Nothing to do here. - * - * @param p Pointer to the particle data. - * @param data The global star_formation information. - */ -__attribute__((always_inline)) INLINE static void star_formation_init_part( - struct part* restrict p, const struct star_formation* data) {} - #endif /* SWIFT_GEAR_STAR_FORMATION_H */ diff --git a/src/star_formation/GEAR/star_formation_io.h b/src/star_formation/GEAR/star_formation_io.h index 6ef04c49c4abcd00175aaa164271628a9ff89360..7e6dcefce633fd55f421e3c8672eaf8e6f18579c 100644 --- a/src/star_formation/GEAR/star_formation_io.h +++ b/src/star_formation/GEAR/star_formation_io.h @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 2019 Fabien Jeanquartier (fabien.jeanquartier@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 @@ -37,8 +38,38 @@ __attribute__((always_inline)) INLINE static int star_formation_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list) { - + /* Nothing to write here */ return 0; } +/** + * @brief initialization of the star formation law + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param starform the star formation law properties to initialize + * + */ +INLINE static void starformation_init_backend( + struct swift_params* parameter_file, const struct phys_const* phys_const, + const struct unit_system* us, const struct hydro_props* hydro_props, + struct star_formation* starform) { + + /* Star formation efficiency */ + starform->star_formation_efficiency = parser_get_param_double( + parameter_file, "GEARStarFormation:star_formation_efficiency"); + + /* Maximum temperature for star formation */ + starform->maximal_temperature = parser_get_param_double( + parameter_file, "GEARStarFormation:maximal_temperature"); + + /* Get the jeans factor */ + starform->n_jeans_2_3 = pow(pressure_floor_props.n_jeans, 2. / 3.); + + /* Apply unit change */ + starform->maximal_temperature *= + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); +} + #endif /* SWIFT_STAR_FORMATION_GEAR_IO_H */ diff --git a/src/star_formation/GEAR/star_formation_logger.h b/src/star_formation/GEAR/star_formation_logger.h index 5b9e033d21d3d202f3c289d1dd6a843ba17fa524..84475909c0f524a4f930a48dbcc3b0943719f8b0 100644 --- a/src/star_formation/GEAR/star_formation_logger.h +++ b/src/star_formation/GEAR/star_formation_logger.h @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 2019 Fabien Jeanquartier (fabien.jeanquartier@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 @@ -27,16 +28,28 @@ #include "hydro.h" #include "part.h" #include "star_formation_logger_struct.h" +#include "units.h" /** - * @brief Update the stellar mass in the current cell after creating - * the new star particle spart sp + * @brief Update the stellar quantities in the current cell after creating + * the new star particle spart sp. * + * @param time_step, the current time step of the simulation * @param sp new created star particle * @param sf the star_formation_history struct of the current cell */ INLINE static void star_formation_logger_log_new_spart( - struct spart *sp, struct star_formation_history *sf) {} + const struct spart *sp, struct star_formation_history *sf) { + + /* Add mass of created sparticle to the total stellar mass in this cell */ + sf->new_stellar_mass += sp->mass; + + /* Increase the number of stars */ + sf->number_new_stars += 1; + + /* No need to deal with the integrated quantities, only the engine's one is + * updated */ +} /** * @brief Initialize the star formation history struct in the case the cell is @@ -45,41 +58,42 @@ INLINE static void star_formation_logger_log_new_spart( * @param sf the star_formation_history struct we want to initialize */ INLINE static void star_formation_logger_log_inactive_cell( - struct star_formation_history *sf) {} + struct star_formation_history *sf) { -/** - * @brief add a star formation history struct to an other star formation history - * struct - * - * @param sf_add the star formation struct which we want to add to the star - * formation history - * @param sf_update the star formation structure which we want to update - */ -INLINE static void star_formation_logger_add( - struct star_formation_history *sf_update, - const struct star_formation_history *sf_add) {} + /* Initialize the stellar mass to zero */ + sf->new_stellar_mass = 0.f; + /* initialize number of stars to zero*/ + sf->number_new_stars = 0; +} /** * @brief Initialize the star formation history structure in the #engine * * @param sfh The pointer to the star formation history structure */ INLINE static void star_formation_logger_init( - struct star_formation_history *sfh) {} + struct star_formation_history *sfh) { + /* Initialize the collecting SFH structure to zero */ + sfh->new_stellar_mass = 0.f; + sfh->number_new_stars = 0; +} /** - * @brief Write the final SFH to a file + * @brief add a star formation history struct to an other star formation history + * struct * - * @param fp The file to write to. - * @param time the simulation time (time since Big Bang) in internal units. - * @param a the scale factor. - * @param z the redshift. - * @param sf the #star_formation_history struct. - * @param step The time-step of the simulation. + * @param sf_add the star formation struct which we want to add to the star + * formation history + * @param sf_update the star formation structure which we want to update */ -INLINE static void star_formation_logger_write_to_log_file( - FILE *fp, const double time, const double a, const double z, - const struct star_formation_history sf, const int step) {} +INLINE static void star_formation_logger_add( + struct star_formation_history *sf_update, + const struct star_formation_history *sf_add) { + + /* Update the SFH structure */ + sf_update->number_new_stars += sf_add->number_new_stars; + sf_update->new_stellar_mass += sf_add->new_stellar_mass; +} /** * @brief Initialize the SFH logger file @@ -90,13 +104,47 @@ INLINE static void star_formation_logger_write_to_log_file( */ INLINE static void star_formation_logger_init_log_file( FILE *fp, const struct unit_system *restrict us, - const struct phys_const *phys_const) {} + const struct phys_const *phys_const) { + + /* Write some general text to the logger file */ + fprintf(fp, "# Star Formation History Logger file\n"); + fprintf(fp, "######################################################\n"); + fprintf(fp, "# The quantities are all given in internal physical units!\n"); + fprintf(fp, "#\n"); + fprintf(fp, "# (0) Simulation step\n"); + fprintf(fp, + "# (1) Time since Big Bang (cosmological run), Time since start of " + "the simulation (non-cosmological run).\n"); + fprintf(fp, "# Unit = %e seconds\n", us->UnitTime_in_cgs); + fprintf(fp, "# Unit = %e yr or %e Myr\n", 1.f / phys_const->const_year, + 1.f / phys_const->const_year / 1e6); + fprintf(fp, "# (2) Scale factor (no unit)\n"); + fprintf(fp, "# (3) Redshift (no unit)\n"); + fprintf(fp, + "# (4) Total number of stars formed in the simulation (no unit)\n"); + fprintf(fp, "# (5) Total stellar mass formed in the simulation.\n"); + fprintf(fp, "# Unit = %e gram\n", us->UnitMass_in_cgs); + fprintf(fp, "# Unit = %e solar mass\n", + 1.f / phys_const->const_solar_mass); + fprintf(fp, + "# (6) Number of stars formed in the current time step (no unit).\n"); + fprintf(fp, "# (7) Mass of stars formed in the current time step.\n"); + fprintf(fp, "# Unit = %e gram\n", us->UnitMass_in_cgs); + fprintf(fp, "# Unit = %e solar mass\n", + 1.f / phys_const->const_solar_mass); + fprintf(fp, + "# (0) (1) (2) (3) (4) " + " (5) (6) (7)\n"); +} /** * @brief Add the SFR tracer to the total active SFR of this cell * + * Nothing to do here + * * @param p the #part * @param xp the #xpart + * * @param sf the SFH logger struct * @param dt_star The length of the time-step in physical internal units. */ @@ -108,6 +156,8 @@ INLINE static void star_formation_logger_log_active_part( * @brief Add the SFR tracer to the total inactive SFR of this cell as long as * the SFR tracer is larger than 0 * + * Nothing to do here + * * @param p the #part * @param xp the #xpart * @param sf the SFH logger struct @@ -116,4 +166,57 @@ INLINE static void star_formation_logger_log_inactive_part( const struct part *p, const struct xpart *xp, struct star_formation_history *sf) {} +/** + * @brief add a star formation history struct to an other star formation history + * struct + * + * @param sf_add the star formation struct which we want to add to the star + * formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add_to_accumulator( + struct star_formation_history_accumulator *sf_update, + const struct star_formation_history *sf_add) { + + /* Update the SFH structure */ + sf_update->number_new_stars = sf_add->number_new_stars; + sf_update->new_stellar_mass = sf_add->new_stellar_mass; + sf_update->total_number_stars += sf_add->number_new_stars; + sf_update->total_stellar_mass += sf_add->new_stellar_mass; +} + +/** + * @brief Write the final SFH to a file + * + * @param fp The file to write to. + * @param time the simulation time (time since Big Bang) in internal units. + * @param a the scale factor. + * @param z the redshift. + * @param sf the #star_formation_history struct. + * @param step The time-step of the simulation. + */ +INLINE static void star_formation_logger_write_to_log_file( + FILE *fp, const double time, const double a, const double z, + struct star_formation_history_accumulator sf, const int step) { + + fprintf(fp, "%6d %16e %12.7f %14e %14ld %14e %14ld %14e\n", step, time, a, z, + sf.total_number_stars, sf.total_stellar_mass, sf.number_new_stars, + sf.new_stellar_mass); +} + +/** + * @brief Initialize the star formation history struct in the #engine when + * starting the simulation. + * + * @param sfh the star_formation_history struct we want to initialize + */ +INLINE static void star_formation_logger_accumulator_init( + struct star_formation_history_accumulator *sfh) { + /* Initialize all values to zero */ + sfh->new_stellar_mass = 0.f; + sfh->number_new_stars = 0; + sfh->total_number_stars = 0; + sfh->total_stellar_mass = 0.f; +} + #endif /* SWIFT_GEAR_STARFORMATION_LOGGER_H */ diff --git a/src/star_formation/GEAR/star_formation_logger_struct.h b/src/star_formation/GEAR/star_formation_logger_struct.h index 04b5dfdc038f7b684cfb1f2079d13eb312624b3f..63e3af06a7cd5af375662a1ba28dc5000a69dc3f 100644 --- a/src/star_formation/GEAR/star_formation_logger_struct.h +++ b/src/star_formation/GEAR/star_formation_logger_struct.h @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 2019 Fabien Jeanquartier (fabien.jeanquartier@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 @@ -19,7 +20,33 @@ #ifndef SWIFT_GEAR_STAR_FORMATION_LOGGER_STRUCT_H #define SWIFT_GEAR_STAR_FORMATION_LOGGER_STRUCT_H -/* Starformation history struct */ -struct star_formation_history {}; +/** + * Structure containing the star formation information from the cells. + */ +struct star_formation_history { + /*! Stellar mass created in the current timestep */ + float new_stellar_mass; -#endif /* SWIFT_NONE_STAR_FORMATION_STRUCT_H */ + /*! Number of stars created in this timestep */ + long int number_new_stars; +}; + +/** + * Structure containing the global star formation information (including time + * integrated variables). + */ +struct star_formation_history_accumulator { + /*! Total stellar mass from the begining of the simulation */ + float total_stellar_mass; + + /*! Total number of stars */ + long int total_number_stars; + + /*! Stellar mass created in the current timestep */ + float new_stellar_mass; + + /*! Number of stars created in this timestep */ + long int number_new_stars; +}; + +#endif /* SWIFT_GEAR_STAR_FORMATION_LOGGER_STRUCT_H */ diff --git a/src/star_formation/GEAR/star_formation_struct.h b/src/star_formation/GEAR/star_formation_struct.h index 9b4e216fd2955f29d89dade6ee46c0e1af715cdb..50a735ff45f27ccb197ae6089f13237f67a59e3f 100644 --- a/src/star_formation/GEAR/star_formation_struct.h +++ b/src/star_formation/GEAR/star_formation_struct.h @@ -25,7 +25,20 @@ */ struct star_formation_xpart_data {}; -/* Starformation struct */ -struct star_formation {}; +/** + * @brief Global star formation properties + */ +struct star_formation { + + /*! Number of particle required to resolved the + * Jeans criterion (at power 2/3) */ + float n_jeans_2_3; + + /*! Maximal temperature for forming a star */ + float maximal_temperature; + + /*! Star formation efficiency */ + float star_formation_efficiency; +}; #endif /* SWIFT_GEAR_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation/none/star_formation.h b/src/star_formation/none/star_formation.h index dfe645718d689841f89cf592194d435af299a642..0f53e951cb5842e5be3bb9bbe64eb6686f822b1e 100644 --- a/src/star_formation/none/star_formation.h +++ b/src/star_formation/none/star_formation.h @@ -163,62 +163,4 @@ INLINE static void starformation_print_backend( message("Star formation law is 'No Star Formation'"); } -/** - * @brief Finishes the density calculation. - * - * @param p The particle to act upon - * @param cd The global star_formation information. - * @param cosmo The current cosmological model. - */ -__attribute__((always_inline)) INLINE static void star_formation_end_density( - struct part* restrict p, const struct star_formation* cd, - const struct cosmology* cosmo) {} - -/** - * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. - * - * @param p The particle to act upon - * @param xp The extended particle data to act upon - * @param cd #star_formation containing star_formation informations. - * @param cosmo The current cosmological model. - */ -__attribute__((always_inline)) INLINE static void -star_formation_part_has_no_neighbours(struct part* restrict p, - struct xpart* restrict xp, - const struct star_formation* cd, - const struct cosmology* cosmo) {} - -/** - * @brief Sets the star_formation properties of the (x-)particles to a valid - * start state. - * - * Nothing to do here. - * - * @param phys_const The physical constant in internal units. - * @param us The unit system. - * @param cosmo The current cosmological model. - * @param data The global star_formation information used for this run. - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - */ -__attribute__((always_inline)) INLINE static void -star_formation_first_init_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct star_formation* data, - const struct part* restrict p, - struct xpart* restrict xp) {} - -/** - * @brief Sets the star_formation properties of the (x-)particles to a valid - * start state. - * - * Nothing to do here. - * - * @param p Pointer to the particle data. - * @param data The global star_formation information. - */ -__attribute__((always_inline)) INLINE static void star_formation_init_part( - struct part* restrict p, const struct star_formation* data) {} - #endif /* SWIFT_NONE_STAR_FORMATION_H */ diff --git a/src/star_formation/none/star_formation_iact.h b/src/star_formation/none/star_formation_iact.h deleted file mode 100644 index dd74115bec699029748806b512c9d6bd7fb829fe..0000000000000000000000000000000000000000 --- a/src/star_formation/none/star_formation_iact.h +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * - * 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_NONE_STAR_FORMATION_IACT_H -#define SWIFT_NONE_STAR_FORMATION_IACT_H - -/** - * @file none/star_formation_iact.h - * @brief Density computation - */ - -/** - * @brief do star_formation computation after the runner_iact_density (symmetric - * version) - * - * @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 pi First particle. - * @param pj Second particle. - * @param a Current scale factor. - * @param H Current Hubble parameter. - */ -__attribute__((always_inline)) INLINE static void runner_iact_star_formation( - float r2, const float *dx, float hi, float hj, struct part *restrict pi, - struct part *restrict pj, float a, float H) {} - -/** - * @brief do star_formation computation after the runner_iact_density (non - * symmetric version) - * - * @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 pi First particle. - * @param pj Second particle (not updated). - * @param a Current scale factor. - * @param H Current Hubble parameter. - */ -__attribute__((always_inline)) INLINE static void -runner_iact_nonsym_star_formation(float r2, const float *dx, float hi, float hj, - struct part *restrict pi, - const struct part *restrict pj, float a, - float H) {} - -#endif /* SWIFT_NONE_STAR_FORMATION_IACT_H */ diff --git a/src/star_formation/none/star_formation_logger.h b/src/star_formation/none/star_formation_logger.h index b4e6987c03d295348fc8c22d66cb20d10e54378c..552df0c6cae533d1eb2678cb23dd675e0a058715 100644 --- a/src/star_formation/none/star_formation_logger.h +++ b/src/star_formation/none/star_formation_logger.h @@ -59,6 +59,18 @@ INLINE static void star_formation_logger_add( struct star_formation_history *sf_update, const struct star_formation_history *sf_add) {} +/** + * @brief add a star formation history accumulator struct to an other star + * formation history struct + * + * @param sf_add the star formation accumulator struct which we want to add to + * the star formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add_to_accumulator( + struct star_formation_history_accumulator *sf_update, + const struct star_formation_history *sf_add) {} + /** * @brief Initialize the star formation history structure in the #engine * @@ -67,6 +79,14 @@ INLINE static void star_formation_logger_add( INLINE static void star_formation_logger_init( struct star_formation_history *sfh) {} +/** + * @brief Initialize the star formation history structure in the #engine + * + * @param sfh The pointer to the star formation history structure + */ +INLINE static void star_formation_logger_accumulator_init( + struct star_formation_history_accumulator *sfh) {} + /** * @brief Write the final SFH to a file * @@ -74,12 +94,12 @@ INLINE static void star_formation_logger_init( * @param time the simulation time (time since Big Bang) in internal units. * @param a the scale factor. * @param z the redshift. - * @param sf the #star_formation_history struct. + * @param sf the #star_formation_history_accumulator struct. * @param step The time-step of the simulation. */ INLINE static void star_formation_logger_write_to_log_file( FILE *fp, const double time, const double a, const double z, - const struct star_formation_history sf, const int step) {} + const struct star_formation_history_accumulator sf, const int step) {} /** * @brief Initialize the SFH logger file diff --git a/src/star_formation/none/star_formation_logger_struct.h b/src/star_formation/none/star_formation_logger_struct.h index 9efda271da96faf2088169fd75d0e3c01247a429..b60c64f2eb47894db828a1bde0ca20803892c7db 100644 --- a/src/star_formation/none/star_formation_logger_struct.h +++ b/src/star_formation/none/star_formation_logger_struct.h @@ -22,4 +22,10 @@ /* Starformation history struct */ struct star_formation_history {}; +/* Starformation history accumulator struct. + This structure is only defined in the engine and + allows the user to integrate some quantities over time. +*/ +struct star_formation_history_accumulator {}; + #endif /* SWIFT_NONE_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation_struct.h b/src/star_formation_struct.h index 2a62d284b435c353525311979b343754856364e8..92386d532fb7e0ad445477bf9e3ec35fe597fe2f 100644 --- a/src/star_formation_struct.h +++ b/src/star_formation_struct.h @@ -27,7 +27,7 @@ /* Config parameters. */ #include "../config.h" -/* Import the right cooling definition */ +/* Import the right star formation definition */ #if defined(STAR_FORMATION_NONE) #include "./star_formation/none/star_formation_struct.h" #elif defined(STAR_FORMATION_EAGLE) diff --git a/src/stars.h b/src/stars.h index dd8390e0206580fc2a07a08e51bb69c6ee5ab5ed..dea6e07a87cabd7d1778ec2a850be4f3b16b04b0 100644 --- a/src/stars.h +++ b/src/stars.h @@ -29,6 +29,9 @@ #elif defined(STARS_EAGLE) #include "./stars/EAGLE/stars.h" #include "./stars/EAGLE/stars_iact.h" +#elif defined(STARS_GEAR) +#include "./stars/GEAR/stars.h" +#include "./stars/GEAR/stars_iact.h" #else #error "Invalid choice of star model" #endif diff --git a/src/stars/Default/stars_io.h b/src/stars/Default/stars_io.h index a8ec1cfa55728f9ca8a348d8fd6ec07d06b72185..54e6b5d05a823cb8401ec01c4fe962bbc8f01ca8 100644 --- a/src/stars/Default/stars_io.h +++ b/src/stars/Default/stars_io.h @@ -22,20 +22,6 @@ #include "io_properties.h" #include "stars_part.h" -INLINE static void convert_spart_pos(const struct engine *e, - const struct spart *sp, double *ret) { - - if (e->s->periodic) { - ret[0] = box_wrap(sp->x[0], 0.0, e->s->dim[0]); - ret[1] = box_wrap(sp->x[1], 0.0, e->s->dim[1]); - ret[2] = box_wrap(sp->x[2], 0.0, e->s->dim[2]); - } else { - ret[0] = sp->x[0]; - ret[1] = sp->x[1]; - ret[2] = sp->x[2]; - } -} - /** * @brief Specifies which s-particle fields to read from a dataset * @@ -63,31 +49,87 @@ INLINE static void stars_read_particles(struct spart *sparts, UNIT_CONV_LENGTH, sparts, h); } +INLINE static void convert_spart_pos(const struct engine *e, + const struct spart *sp, double *ret) { + + if (e->s->periodic) { + ret[0] = box_wrap(sp->x[0], 0.0, e->s->dim[0]); + ret[1] = box_wrap(sp->x[1], 0.0, e->s->dim[1]); + ret[2] = box_wrap(sp->x[2], 0.0, e->s->dim[2]); + } else { + ret[0] = sp->x[0]; + ret[1] = sp->x[1]; + ret[2] = sp->x[2]; + } +} + +INLINE static void convert_spart_vel(const struct engine *e, + const struct spart *sp, float *ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology *cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, sp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, sp->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + const struct gpart *gp = sp->gpart; + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + /** * @brief Specifies which s-particle fields to write to a dataset * * @param sparts The s-particle array. * @param list The list of i/o properties to write. * @param num_fields The number of i/o fields to write. + * @param with_cosmology Are we running a cosmological simulation? */ INLINE static void stars_write_particles(const struct spart *sparts, - struct io_props *list, - int *num_fields) { + struct io_props *list, int *num_fields, + int with_cosmology) { /* Say how much we want to write */ *num_fields = 5; /* List what we want to write */ list[0] = io_make_output_field_convert_spart( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, sparts, convert_spart_pos); - list[1] = - io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, sparts, v); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, sparts, mass); + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, sparts, + convert_spart_pos, "Co-moving position of the particles"); + + list[1] = io_make_output_field_convert_spart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, sparts, convert_spart_vel, + "Peculiar velocities of the particles. This is a * dx/dt where x is the " + "co-moving position of the particles."); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + sparts, mass, "Masses of the particles"); + list[3] = io_make_output_field("ParticleIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, - sparts, id); - list[4] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - sparts, h); + 0.f, sparts, id, "Unique ID of the particles"); + + list[4] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, sparts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); #ifdef DEBUG_INTERACTIONS_STARS diff --git a/src/stars/EAGLE/stars.h b/src/stars/EAGLE/stars.h index 09926d6a8bdd5af4eff120a9a6c57d15eb7e49c4..82ad2e25f474e6c66e6cf5e09f39188faa09b084 100644 --- a/src/stars/EAGLE/stars.h +++ b/src/stars/EAGLE/stars.h @@ -65,7 +65,8 @@ __attribute__((always_inline)) INLINE static void stars_first_init_spart( sp->time_bin = 0; sp->birth_density = 0.f; sp->f_E = -1.f; - sp->birth_time = stars_properties->spart_first_init_birth_time; + if (stars_properties->overwrite_birth_time) + sp->birth_time = stars_properties->spart_first_init_birth_time; stars_init_spart(sp); } diff --git a/src/stars/EAGLE/stars_io.h b/src/stars/EAGLE/stars_io.h index cfacd52106398f435e56a9a2a67d1016726e2295..0baafd380addfa1d6f8d60491be3da4c30b2a3aa 100644 --- a/src/stars/EAGLE/stars_io.h +++ b/src/stars/EAGLE/stars_io.h @@ -23,20 +23,6 @@ #include "io_properties.h" #include "stars_part.h" -INLINE static void convert_spart_pos(const struct engine *e, - const struct spart *sp, double *ret) { - - if (e->s->periodic) { - ret[0] = box_wrap(sp->x[0], 0.0, e->s->dim[0]); - ret[1] = box_wrap(sp->x[1], 0.0, e->s->dim[1]); - ret[2] = box_wrap(sp->x[2], 0.0, e->s->dim[2]); - } else { - ret[0] = sp->x[0]; - ret[1] = sp->x[1]; - ret[2] = sp->x[2]; - } -} - /** * @brief Specifies which s-particle fields to read from a dataset * @@ -49,7 +35,7 @@ INLINE static void stars_read_particles(struct spart *sparts, int *num_fields) { /* Say how much we want to read */ - *num_fields = 6; + *num_fields = 7; /* List what we want to read */ list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, @@ -64,6 +50,55 @@ INLINE static void stars_read_particles(struct spart *sparts, UNIT_CONV_LENGTH, sparts, h); list[5] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, sparts, mass_init); + list[6] = io_make_input_field("StellarFormationTime", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, sparts, birth_time); +} + +INLINE static void convert_spart_pos(const struct engine *e, + const struct spart *sp, double *ret) { + + if (e->s->periodic) { + ret[0] = box_wrap(sp->x[0], 0.0, e->s->dim[0]); + ret[1] = box_wrap(sp->x[1], 0.0, e->s->dim[1]); + ret[2] = box_wrap(sp->x[2], 0.0, e->s->dim[2]); + } else { + ret[0] = sp->x[0]; + ret[1] = sp->x[1]; + ret[2] = sp->x[2]; + } +} + +INLINE static void convert_spart_vel(const struct engine *e, + const struct spart *sp, float *ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology *cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, sp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, sp->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + const struct gpart *gp = sp->gpart; + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; } /** @@ -72,36 +107,68 @@ INLINE static void stars_read_particles(struct spart *sparts, * @param sparts The s-particle array. * @param list The list of i/o properties to write. * @param num_fields The number of i/o fields to write. + * @param with_cosmology Are we running a cosmological simulation? */ INLINE static void stars_write_particles(const struct spart *sparts, - struct io_props *list, - int *num_fields) { + struct io_props *list, int *num_fields, + const int with_cosmology) { /* Say how much we want to write */ *num_fields = 10; /* List what we want to write */ list[0] = io_make_output_field_convert_spart( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, sparts, convert_spart_pos); - list[1] = - io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, sparts, v); - list[2] = - io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, sparts, mass); + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, sparts, + convert_spart_pos, "Co-moving position of the particles"); + + list[1] = io_make_output_field_convert_spart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, sparts, convert_spart_vel, + "Peculiar velocities of the particles. This is a * dx/dt where x is the " + "co-moving position of the particles."); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + sparts, mass, + "Masses of the particles at the current point " + "in time (i.e. after stellar losses"); + list[3] = io_make_output_field("ParticleIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, - sparts, id); - list[4] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, - sparts, h); - list[5] = io_make_output_field("BirthDensity", FLOAT, 1, UNIT_CONV_DENSITY, - sparts, birth_density); - list[6] = io_make_output_field("InitialMasses", FLOAT, 1, UNIT_CONV_MASS, - sparts, mass_init); - list[7] = io_make_output_field("BirthTime", FLOAT, 1, UNIT_CONV_TIME, sparts, - birth_time); - list[8] = io_make_output_field("FeedbackEnergyFraction", FLOAT, 1, - UNIT_CONV_NO_UNITS, sparts, f_E); + 0.f, sparts, id, "Unique ID of the particles"); + + list[4] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, sparts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[5] = io_make_output_field( + "BirthDensities", FLOAT, 1, UNIT_CONV_DENSITY, 0.f, sparts, birth_density, + "Physical densities at the time of birth of the gas particles that " + "turned into stars (note that " + "we store the physical density at the birth redshift, no conversion is " + "needed)"); + + list[6] = io_make_output_field("InitialMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + sparts, mass_init, + "Masses of the star particles at birth time"); + + if (with_cosmology) { + list[7] = io_make_output_field( + "BirthScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + birth_scale_factor, "Scale-factors at which the stars were born"); + } else { + list[7] = io_make_output_field("BirthTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, + sparts, birth_time, + "Times at which the stars were born"); + } + + list[8] = io_make_output_field( + "FeedbackEnergyFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, f_E, + "Fractions of the canonical feedback energy that was used for the stars' " + "SNII feedback events"); + list[9] = - io_make_output_field("BirthTemperature", FLOAT, 1, UNIT_CONV_TEMPERATURE, - sparts, birth_temperature); + io_make_output_field("BirthTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, + 0.f, sparts, birth_temperature, + "Temperatures at the time of birth of the gas " + "particles that turned into stars"); } /** @@ -150,10 +217,15 @@ INLINE static void stars_props_init(struct stars_props *sp, else sp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); - /* Read birth time to set all stars in ICs to (defaults to -1 to indicate star - * present in ICs) */ - sp->spart_first_init_birth_time = - parser_get_opt_param_float(params, "Stars:birth_time", -1); + /* Do we want to overwrite the stars' birth time? */ + sp->overwrite_birth_time = + parser_get_opt_param_int(params, "Stars:overwrite_birth_time", 0); + + /* Read birth time to set all stars in ICs */ + if (sp->overwrite_birth_time) { + sp->spart_first_init_birth_time = + parser_get_param_float(params, "Stars:birth_time"); + } } /** @@ -177,6 +249,10 @@ INLINE static void stars_props_print(const struct stars_props *sp) { message("Maximal iterations in ghost task set to %d", sp->max_smoothing_iterations); + + if (sp->overwrite_birth_time) + message("Stars' birth time read from the ICs will be overwritten to %f", + sp->spart_first_init_birth_time); } #if defined(HAVE_HDF5) diff --git a/src/stars/EAGLE/stars_part.h b/src/stars/EAGLE/stars_part.h index bed54bb175026ae930668fb102dcfef9c9af76d7..9114cb9107e1259698c365be82e5318d60d37ac7 100644 --- a/src/stars/EAGLE/stars_part.h +++ b/src/stars/EAGLE/stars_part.h @@ -144,7 +144,7 @@ struct stars_props { /*! Smoothing length tolerance */ float h_tolerance; - /*! Tolerance on neighbour number (for info only)*/ + /*! Tolerance on neighbour number (for info only) */ float delta_neighbours; /*! Maximal number of iterations to converge h */ @@ -153,6 +153,9 @@ struct stars_props { /*! Maximal change of h over one time-step */ float log_max_h_change; + /*! Are we overwriting the stars' birth time read from the ICs? */ + int overwrite_birth_time; + /*! Value to set birth time of stars read from ICs */ float spart_first_init_birth_time; }; diff --git a/src/stars/GEAR/stars.h b/src/stars/GEAR/stars.h new file mode 100644 index 0000000000000000000000000000000000000000..467aaa164ba9762d85f8dfae85db86ff76ae779e --- /dev/null +++ b/src/stars/GEAR/stars.h @@ -0,0 +1,172 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_STARS_H +#define SWIFT_GEAR_STARS_H + +#include <float.h> +#include "minmax.h" + +/** + * @brief Computes the gravity time-step of a given star particle. + * + * @param sp Pointer to the s-particle data. + */ +__attribute__((always_inline)) INLINE static float stars_compute_timestep( + const struct spart* const sp) { + + return FLT_MAX; +} + +/** + * @brief Initialises the s-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon + * @param stars_properties The properties of the stellar model. + */ +__attribute__((always_inline)) INLINE static void stars_first_init_spart( + struct spart* sp, const struct stars_props* stars_properties) { + + sp->time_bin = 0; +} + +/** + * @brief Prepares a s-particle for its interactions + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_init_spart( + struct spart* sp) { + +#ifdef DEBUG_INTERACTIONS_STARS + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + sp->ids_ngbs_density[i] = -1; + sp->num_ngb_density = 0; +#endif + + sp->density.wcount = 0.f; + sp->density.wcount_dh = 0.f; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * @param sp The particle + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void stars_predict_extra( + struct spart* restrict sp, float dt_drift) {} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param sp The particle. + */ +__attribute__((always_inline)) INLINE static void stars_reset_predicted_values( + struct spart* restrict sp) {} + +/** + * @brief Finishes the calculation of (non-gravity) forces acting on stars + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_end_feedback( + struct spart* sp) {} + +/** + * @brief Kick the additional variables + * + * @param sp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void stars_kick_extra( + struct spart* sp, float dt) {} + +/** + * @brief Finishes the calculation of density on stars + * + * @param sp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void stars_end_density( + struct spart* sp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = sp->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 */ + sp->density.wcount *= h_inv_dim; + sp->density.wcount_dh *= h_inv_dim_plus_one; +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param sp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( + struct spart* restrict sp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = sp->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 */ + sp->density.wcount = kernel_root * h_inv_dim; + sp->density.wcount_dh = 0.f; +} + +/** + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on star, therefore no need to use it. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_reset_feedback( + struct spart* restrict p) { + +#ifdef DEBUG_INTERACTIONS_STARS + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + p->ids_ngbs_force[i] = -1; + p->num_ngb_force = 0; +#endif +} + +/** + * @brief Initializes constants related to stellar evolution, initializes imf, + * reads and processes yield tables + * + * @param params swift_params parameters structure + * @param stars stars_props data structure + */ +inline static void stars_evolve_init(struct swift_params* params, + struct stars_props* restrict stars) {} + +#endif /* SWIFT_GEAR_STARS_H */ diff --git a/src/stars/GEAR/stars_debug.h b/src/stars/GEAR/stars_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..41953367f6c8771ffd7460cee493c8330ecd874b --- /dev/null +++ b/src/stars/GEAR/stars_debug.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_STARS_DEBUG_H +#define SWIFT_GEAR_STARS_DEBUG_H + +__attribute__((always_inline)) INLINE static void stars_debug_particle( + const struct spart* p) { + printf( + "x=[%.3e,%.3e,%.3e], " + "v_full=[%.3e,%.3e,%.3e] p->mass=%.3e \n t_begin=%d, t_end=%d\n", + p->x[0], p->x[1], p->x[2], p->v_full[0], p->v_full[1], p->v_full[2], + p->mass, p->ti_begin, p->ti_end); +} + +#endif /* SWIFT_GEAR_STARS_DEBUG_H */ diff --git a/src/stars/GEAR/stars_iact.h b/src/stars/GEAR/stars_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..c7bda43fc0c66fcc890e6b05bacf871edfd10b5a --- /dev/null +++ b/src/stars/GEAR/stars_iact.h @@ -0,0 +1,94 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_STARS_IACT_H +#define SWIFT_GEAR_STARS_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 si First sparticle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_density(const float r2, const float *dx, + const float hi, const float hj, + struct spart *restrict si, + const struct part *restrict pj, const float a, + const float H) { + + 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 */ + si->density.wcount += wi; + si->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + +#ifdef DEBUG_INTERACTIONS_STARS + /* Update ngb counters */ + if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_STARS) + si->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 si First sparticle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_feedback(const float r2, const float *dx, + const float hi, const float hj, + struct spart *restrict si, + struct part *restrict pj, const float a, + const float H) { +#ifdef DEBUG_INTERACTIONS_STARS + /* Update ngb counters */ + if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_STARS) + si->ids_ngbs_force[si->num_ngb_force] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_force; +#endif +} + +#endif diff --git a/src/stars/GEAR/stars_io.h b/src/stars/GEAR/stars_io.h new file mode 100644 index 0000000000000000000000000000000000000000..dc40e86e29b19a370d549576fd955464bf0e609d --- /dev/null +++ b/src/stars/GEAR/stars_io.h @@ -0,0 +1,248 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_STARS_IO_H +#define SWIFT_GEAR_STARS_IO_H + +#include "io_properties.h" +#include "stars_part.h" + +/** + * @brief Specifies which s-particle fields to read from a dataset + * + * @param sparts The s-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 stars_read_particles(struct spart *sparts, + struct io_props *list, + int *num_fields) { + + /* Say how much we want to read */ + *num_fields = 5; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, sparts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, sparts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + sparts, mass); + list[3] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, sparts, id); + list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, + UNIT_CONV_LENGTH, sparts, h); +} + +/** + * @brief Specifies which s-particle fields to write to a dataset + * + * @param sparts The s-particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + * @param with_cosmology Is it a cosmological run? + */ +INLINE static void stars_write_particles(const struct spart *sparts, + struct io_props *list, int *num_fields, + const int with_cosmology) { + + /* Say how much we want to write */ + *num_fields = 9; + + /* List what we want to write */ + list[0] = + io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1., + sparts, x, "Co-moving positions of the particles"); + + list[1] = io_make_output_field( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, sparts, v, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + sparts, mass, "Masses of the particles"); + + list[3] = + io_make_output_field("ParticleIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + sparts, id, "Unique IDs of the particles"); + + list[4] = io_make_output_field( + "SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, sparts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + if (with_cosmology) { + list[5] = io_make_output_field( + "BirthScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + birth_scale_factor, "Scale-factors at which the stars were born"); + } else { + list[5] = io_make_output_field("BirthTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, + sparts, birth_time, + "Times at which the stars were born"); + } + + list[6] = io_make_output_field( + "BirthDensities", FLOAT, 1, UNIT_CONV_DENSITY, 0.f, sparts, birth.density, + "Physical densities at the time of birth of the gas particles that " + "turned into stars (note that " + "we store the physical density at the birth redshift, no conversion is " + "needed)"); + + list[7] = + io_make_output_field("BirthTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, + 0.f, sparts, birth.temperature, + "Temperatures at the time of birth of the gas " + "particles that turned into stars"); + + list[8] = io_make_output_field("BirthMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + sparts, birth.mass, + "Masses of the star particles at birth time"); + +#ifdef DEBUG_INTERACTIONS_STARS + + list += *num_fields; + *num_fields += 4; + + list[0] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS, + sparts, num_ngb_density); + list[1] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS, + sparts, num_ngb_force); + list[2] = io_make_output_field("Ids_ngb_density", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_STARS, + UNIT_CONV_NO_UNITS, sparts, ids_ngbs_density); + list[3] = io_make_output_field("Ids_ngb_force", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_STARS, + UNIT_CONV_NO_UNITS, sparts, ids_ngbs_force); +#endif +} + +/** + * @brief Initialize the global properties of the stars scheme. + * + * By default, takes the values provided by the hydro. + * + * @param sp The #stars_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param p The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +INLINE static void stars_props_init(struct stars_props *sp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *p, + const struct cosmology *cosmo) { + + /* Kernel properties */ + sp->eta_neighbours = parser_get_opt_param_float( + params, "Stars:resolution_eta", p->eta_neighbours); + + /* Tolerance for the smoothing length Newton-Raphson scheme */ + sp->h_tolerance = + parser_get_opt_param_float(params, "Stars:h_tolerance", p->h_tolerance); + + /* Get derived properties */ + sp->target_neighbours = pow_dimension(sp->eta_neighbours) * kernel_norm; + const float delta_eta = sp->eta_neighbours * (1.f + sp->h_tolerance); + sp->delta_neighbours = + (pow_dimension(delta_eta) - pow_dimension(sp->eta_neighbours)) * + kernel_norm; + + /* Number of iterations to converge h */ + sp->max_smoothing_iterations = parser_get_opt_param_int( + params, "Stars:max_ghost_iterations", p->max_smoothing_iterations); + + /* Time integration properties */ + const float max_volume_change = + parser_get_opt_param_float(params, "Stars:max_volume_change", -1); + if (max_volume_change == -1) + sp->log_max_h_change = p->log_max_h_change; + else + sp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); +} + +/** + * @brief Print the global properties of the stars scheme. + * + * @param sp The #stars_props. + */ +INLINE static void stars_props_print(const struct stars_props *sp) { + + /* Now stars */ + message("Stars kernel: %s with eta=%f (%.2f neighbours).", kernel_name, + sp->eta_neighbours, sp->target_neighbours); + + message("Stars relative tolerance in h: %.5f (+/- %.4f neighbours).", + sp->h_tolerance, sp->delta_neighbours); + + message( + "Stars integration: Max change of volume: %.2f " + "(max|dlog(h)/dt|=%f).", + pow_dimension(expf(sp->log_max_h_change)), sp->log_max_h_change); + + message("Maximal iterations in ghost task set to %d", + sp->max_smoothing_iterations); +} + +#if defined(HAVE_HDF5) +INLINE static void stars_props_print_snapshot(hid_t h_grpstars, + const struct stars_props *sp) { + + io_write_attribute_s(h_grpstars, "Kernel function", kernel_name); + io_write_attribute_f(h_grpstars, "Kernel target N_ngb", + sp->target_neighbours); + io_write_attribute_f(h_grpstars, "Kernel delta N_ngb", sp->delta_neighbours); + io_write_attribute_f(h_grpstars, "Kernel eta", sp->eta_neighbours); + io_write_attribute_f(h_grpstars, "Smoothing length tolerance", + sp->h_tolerance); + io_write_attribute_f(h_grpstars, "Volume log(max(delta h))", + sp->log_max_h_change); + io_write_attribute_f(h_grpstars, "Volume max change time-step", + pow_dimension(expf(sp->log_max_h_change))); + io_write_attribute_i(h_grpstars, "Max ghost iterations", + sp->max_smoothing_iterations); +} +#endif + +/** + * @brief Write a #stars_props struct to the given FILE as a stream of bytes. + * + * @param p the struct + * @param stream the file stream + */ +INLINE static void stars_props_struct_dump(const struct stars_props *p, + FILE *stream) { + restart_write_blocks((void *)p, sizeof(struct stars_props), 1, stream, + "starsprops", "stars props"); +} + +/** + * @brief Restore a stars_props struct from the given FILE as a stream of + * bytes. + * + * @param p the struct + * @param stream the file stream + */ +INLINE static void stars_props_struct_restore(const struct stars_props *p, + FILE *stream) { + restart_read_blocks((void *)p, sizeof(struct stars_props), 1, stream, NULL, + "stars props"); +} + +#endif /* SWIFT_GEAR_STAR_IO_H */ diff --git a/src/stars/GEAR/stars_part.h b/src/stars/GEAR/stars_part.h new file mode 100644 index 0000000000000000000000000000000000000000..bf68a580ef009f5a814fa17123301b5585d6084c --- /dev/null +++ b/src/stars/GEAR/stars_part.h @@ -0,0 +1,154 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_STAR_PART_H +#define SWIFT_GEAR_STAR_PART_H + +/* Some standard headers. */ +#include <stdlib.h> + +/* Read additional subgrid models */ +#include "chemistry_struct.h" +#include "feedback_struct.h" +#include "tracers_struct.h" + +/** + * @brief Particle fields for the star particles. + * + * All quantities related to gravity are stored in the associate #gpart. + */ +struct spart { + + /*! 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]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff_sort[3]; + + /*! Particle velocity. */ + float v[3]; + + /*! Star mass */ + float mass; + + /* Particle cutoff radius. */ + float h; + + /*! Union for the birth time and birth scale factor */ + union { + + /*! Birth time */ + float birth_time; + + /*! Birth scale factor */ + float birth_scale_factor; + }; + + /*! Particle time bin */ + timebin_t time_bin; + + struct { + + /* Number of neighbours. */ + float wcount; + + /* Number of neighbours spatial derivative. */ + float wcount_dh; + + } density; + + struct { + /*! birth density*/ + float density; + + /*! birth temperature*/ + float temperature; + + /*! birth mass */ + float mass; + } birth; + + /*! Feedback structure */ + struct feedback_spart_data feedback_data; + + /*! Tracer structure */ + struct tracers_xpart_data tracers_data; + + /*! Chemistry structure */ + struct chemistry_part_data chemistry_data; + +#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_STARS + /*! 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_STARS]; + + /*! 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_STARS]; +#endif + +} SWIFT_STRUCT_ALIGN; + +/** + * @brief Contains all the constants and parameters of the stars scheme + */ +struct stars_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; +}; + +#endif /* SWIFT_GEAR_STAR_PART_H */ diff --git a/src/swift.h b/src/swift.h index b9f8818d8b833231971abb1afb36ee4507648488..fe9196a8fcf6d1845c9446c480c7961504a4756f 100644 --- a/src/swift.h +++ b/src/swift.h @@ -55,6 +55,7 @@ #include "map.h" #include "memuse.h" #include "mesh_gravity.h" +#include "minmax.h" #include "multipole.h" #include "outputlist.h" #include "parallel_io.h" @@ -64,6 +65,7 @@ #include "periodic.h" #include "physical_constants.h" #include "potential.h" +#include "pressure_floor.h" #include "profiler.h" #include "queue.h" #include "random.h" diff --git a/src/task.c b/src/task.c index 9a0aa64faa3983d409a2a6c619c58795eab8d5e5..4d6cfa2482491b1a08f6b28f7188fb94448afb2e 100644 --- a/src/task.c +++ b/src/task.c @@ -95,18 +95,20 @@ const char *taskID_names[task_type_count] = {"none", "bh_density_ghost", "bh_swallow_ghost1", "bh_swallow_ghost2", + "bh_swallow_ghost3", "fof_self", "fof_pair"}; /* 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", "part_swallow", "gpart", "multipole", - "spart", "stars_density", "stars_feedback", "sf_count", - "bpart_rho", "bpart_swallow", "bpart_feedback", "bh_density", - "bh_swallow", "do_swallow", "bh_feedback"}; + "none", "density", "gradient", "force", + "limiter", "grav", "external_grav", "tend_part", + "tend_gpart", "tend_spart", "tend_bpart", "xv", + "rho", "part_swallow", "bpart_merger", "gpart", + "multipole", "spart", "stars_density", "stars_feedback", + "sf_count", "bpart_rho", "bpart_swallow", "bpart_feedback", + "bh_density", "bh_swallow", "do_gas_swallow", "do_bh_swallow", + "bh_feedback"}; #ifdef WITH_MPI /* MPI communicators for the subtypes. */ @@ -179,7 +181,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_type_drift_bpart: case task_type_bh_density_ghost: - case task_type_bh_swallow_ghost2: + case task_type_bh_swallow_ghost3: return task_action_bpart; break; @@ -204,10 +206,14 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_subtype_bh_density: case task_subtype_bh_feedback: case task_subtype_bh_swallow: - case task_subtype_do_swallow: + case task_subtype_do_gas_swallow: return task_action_all; break; + case task_subtype_do_bh_swallow: + return task_action_bpart; + break; + case task_subtype_grav: case task_subtype_external_grav: return task_action_gpart; @@ -421,6 +427,7 @@ void task_unlock(struct task *t) { case task_type_drift_part: case task_type_sort: case task_type_ghost: + case task_type_extra_ghost: case task_type_end_hydro_force: case task_type_timestep_limiter: cell_unlocktree(ci); @@ -449,9 +456,11 @@ void task_unlock(struct task *t) { } else if ((subtype == task_subtype_bh_density) || (subtype == task_subtype_bh_feedback) || (subtype == task_subtype_bh_swallow) || - (subtype == task_subtype_do_swallow)) { + (subtype == task_subtype_do_gas_swallow)) { cell_bunlocktree(ci); cell_unlocktree(ci); + } else if (subtype == task_subtype_do_bh_swallow) { + cell_bunlocktree(ci); } else { cell_unlocktree(ci); } @@ -473,11 +482,14 @@ void task_unlock(struct task *t) { } else if ((subtype == task_subtype_bh_density) || (subtype == task_subtype_bh_feedback) || (subtype == task_subtype_bh_swallow) || - (subtype == task_subtype_do_swallow)) { + (subtype == task_subtype_do_gas_swallow)) { cell_bunlocktree(ci); cell_bunlocktree(cj); cell_unlocktree(ci); cell_unlocktree(cj); + } else if (subtype == task_subtype_do_bh_swallow) { + cell_bunlocktree(ci); + cell_bunlocktree(cj); } else { cell_unlocktree(ci); cell_unlocktree(cj); @@ -561,6 +573,7 @@ int task_lock(struct task *t) { case task_type_drift_part: case task_type_sort: case task_type_ghost: + case task_type_extra_ghost: case task_type_end_hydro_force: case task_type_timestep_limiter: if (ci->hydro.hold) return 0; @@ -603,7 +616,7 @@ int task_lock(struct task *t) { } else if ((subtype == task_subtype_bh_density) || (subtype == task_subtype_bh_feedback) || (subtype == task_subtype_bh_swallow) || - (subtype == task_subtype_do_swallow)) { + (subtype == task_subtype_do_gas_swallow)) { if (ci->black_holes.hold) return 0; if (ci->hydro.hold) return 0; if (cell_blocktree(ci) != 0) return 0; @@ -611,6 +624,9 @@ int task_lock(struct task *t) { cell_bunlocktree(ci); return 0; } + } else if (subtype == task_subtype_do_bh_swallow) { + if (ci->black_holes.hold) return 0; + if (cell_blocktree(ci) != 0) return 0; } else { /* subtype == hydro */ if (ci->hydro.hold) return 0; if (cell_locktree(ci) != 0) return 0; @@ -660,7 +676,7 @@ int task_lock(struct task *t) { } else if ((subtype == task_subtype_bh_density) || (subtype == task_subtype_bh_feedback) || (subtype == task_subtype_bh_swallow) || - (subtype == task_subtype_do_swallow)) { + (subtype == task_subtype_do_gas_swallow)) { /* Lock the BHs and the gas particles in both cells */ if (ci->black_holes.hold || cj->black_holes.hold) return 0; if (ci->hydro.hold || cj->hydro.hold) return 0; @@ -680,6 +696,13 @@ int task_lock(struct task *t) { cell_unlocktree(ci); return 0; } + } else if (subtype == task_subtype_do_bh_swallow) { + if (ci->black_holes.hold || cj->black_holes.hold) return 0; + if (cell_blocktree(ci) != 0) return 0; + if (cell_blocktree(cj) != 0) { + cell_bunlocktree(ci); + return 0; + } } else { /* subtype == hydro */ /* Lock the parts in both cells */ if (ci->hydro.hold || cj->hydro.hold) return 0; @@ -803,8 +826,11 @@ void task_get_group_name(int type, int subtype, char *cluster) { case task_subtype_bh_swallow: strcpy(cluster, "BHSwallow"); break; - case task_subtype_do_swallow: - strcpy(cluster, "DoSwallow"); + case task_subtype_do_gas_swallow: + strcpy(cluster, "DoGasSwallow"); + break; + case task_subtype_do_bh_swallow: + strcpy(cluster, "DoBHSwallow"); break; case task_subtype_bh_feedback: strcpy(cluster, "BHFeedback"); @@ -867,7 +893,7 @@ void task_dump_all(struct engine *e, int step) { #ifdef SWIFT_DEBUG_TASKS /* Need this to convert ticks to seconds. */ - unsigned long long cpufreq = clocks_get_cpufreq(); + const unsigned long long cpufreq = clocks_get_cpufreq(); #ifdef WITH_MPI /* Make sure output file is empty, only on one rank. */ @@ -900,7 +926,8 @@ void task_dump_all(struct engine *e, int step) { e->s_updates, cpufreq); int count = 0; for (int l = 0; l < e->sched.nr_tasks; l++) { - if (!e->sched.tasks[l].implicit && e->sched.tasks[l].toc != 0) { + if (!e->sched.tasks[l].implicit && + e->sched.tasks[l].tic > e->tic_step) { fprintf( file_thread, " %03i %i %i %i %i %lli %lli %i %i %i %i %lli %i\n", engine_rank, e->sched.tasks[l].rid, e->sched.tasks[l].type, @@ -940,7 +967,7 @@ void task_dump_all(struct engine *e, int step) { (unsigned long long)e->toc_step, e->updates, e->g_updates, e->s_updates, 0, cpufreq); for (int l = 0; l < e->sched.nr_tasks; l++) { - if (!e->sched.tasks[l].implicit && e->sched.tasks[l].toc != 0) { + if (!e->sched.tasks[l].implicit && e->sched.tasks[l].tic > e->tic_step) { fprintf( file_thread, " %i %i %i %i %lli %lli %i %i %i %i %i\n", e->sched.tasks[l].rid, e->sched.tasks[l].type, @@ -970,7 +997,8 @@ void task_dump_all(struct engine *e, int step) { * Note that when running under MPI all the tasks can be summed into this single * file. In the fuller, human readable file, the statistics included are the * number of task of each type/subtype followed by the minimum, maximum, mean - * and total time, in millisec and then the fixed costs value. + * and total time taken and the same numbers for the start of the task, + * in millisec and then the fixed costs value. * * If header is set, only the fixed costs value is written into the output * file in a format that is suitable for inclusion in SWIFT (as @@ -987,16 +1015,22 @@ void task_dump_stats(const char *dumpfile, struct engine *e, int header, /* Need arrays for sum, min and max across all types and subtypes. */ double sum[task_type_count][task_subtype_count]; + double tsum[task_type_count][task_subtype_count]; double min[task_type_count][task_subtype_count]; + double tmin[task_type_count][task_subtype_count]; double max[task_type_count][task_subtype_count]; + double tmax[task_type_count][task_subtype_count]; int count[task_type_count][task_subtype_count]; for (int j = 0; j < task_type_count; j++) { for (int k = 0; k < task_subtype_count; k++) { sum[j][k] = 0.0; + tsum[j][k] = 0.0; count[j][k] = 0; min[j][k] = DBL_MAX; + tmin[j][k] = DBL_MAX; max[j][k] = 0.0; + tmax[j][k] = 0.0; } } @@ -1004,21 +1038,28 @@ void task_dump_stats(const char *dumpfile, struct engine *e, int header, for (int l = 0; l < e->sched.nr_tasks; l++) { int type = e->sched.tasks[l].type; - /* Skip implicit tasks, tasks that didn't run and MPI send/recv as these - * are not interesting (or meaningfully measured). */ - if (!e->sched.tasks[l].implicit && e->sched.tasks[l].toc != 0 && - type != task_type_send && type != task_type_recv) { + /* Skip implicit tasks, tasks that didn't run this step. */ + if (!e->sched.tasks[l].implicit && e->sched.tasks[l].tic > e->tic_step) { int subtype = e->sched.tasks[l].subtype; double dt = e->sched.tasks[l].toc - e->sched.tasks[l].tic; sum[type][subtype] += dt; + + double tic = (double)e->sched.tasks[l].tic; + tsum[type][subtype] += tic; count[type][subtype] += 1; if (dt < min[type][subtype]) { min[type][subtype] = dt; } + if (tic < tmin[type][subtype]) { + tmin[type][subtype] = tic; + } if (dt > max[type][subtype]) { max[type][subtype] = dt; } + if (tic > tmax[type][subtype]) { + tmax[type][subtype] = tic; + } total[0] += dt; } } @@ -1032,6 +1073,10 @@ void task_dump_stats(const char *dumpfile, struct engine *e, int header, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task sums"); + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : tsum), tsum, size, + MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task tsums"); + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : count), count, size, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task counts"); @@ -1040,10 +1085,18 @@ void task_dump_stats(const char *dumpfile, struct engine *e, int header, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task minima"); + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : tmin), tmin, size, + MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task minima"); + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : max), max, size, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task maxima"); + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : tmax), tmax, size, + MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task maxima"); + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : total), total, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task total time"); @@ -1057,29 +1110,36 @@ void task_dump_stats(const char *dumpfile, struct engine *e, int header, fprintf(dfile, "/* use as src/partition_fixed_costs.h */\n"); fprintf(dfile, "#define HAVE_FIXED_COSTS 1\n"); } else { - fprintf(dfile, "# task ntasks min max sum mean percent fixed_cost\n"); + fprintf(dfile, + "# task ntasks min max sum mean percent mintic maxtic" + " meantic fixed_cost\n"); } for (int j = 0; j < task_type_count; j++) { const char *taskID = taskID_names[j]; for (int k = 0; k < task_subtype_count; k++) { if (sum[j][k] > 0.0) { - double mean = sum[j][k] / (double)count[j][k]; - double perc = 100.0 * sum[j][k] / total[0]; /* Fixed cost is in .1ns as we want to compare between runs in * some absolute units. */ + double mean = sum[j][k] / (double)count[j][k]; int fixed_cost = (int)(clocks_from_ticks(mean) * 10000.f); if (header) { fprintf(dfile, "repartition_costs[%d][%d] = %10d; /* %s/%s */\n", j, k, fixed_cost, taskID, subtaskID_names[k]); } else { + double perc = 100.0 * sum[j][k] / total[0]; + double mintic = tmin[j][k] - e->tic_step; + double maxtic = tmax[j][k] - e->tic_step; + double meantic = tsum[j][k] / (double)count[j][k] - e->tic_step; fprintf(dfile, - "%15s/%-10s %10d %14.4f %14.4f %14.4f %14.4f %14.4f %10d\n", + "%15s/%-10s %10d %14.4f %14.4f %14.4f %14.4f %14.4f" + " %14.4f %14.4f %14.4f %10d\n", taskID, subtaskID_names[k], count[j][k], clocks_from_ticks(min[j][k]), clocks_from_ticks(max[j][k]), clocks_from_ticks(sum[j][k]), clocks_from_ticks(mean), perc, - fixed_cost); + clocks_from_ticks(mintic), clocks_from_ticks(maxtic), + clocks_from_ticks(meantic), fixed_cost); } } } diff --git a/src/task.h b/src/task.h index 363e125753bace6c84d99f0be2c18479b852a9c8..0c1d1d29b55a6038d0638fbff167668421171266 100644 --- a/src/task.h +++ b/src/task.h @@ -88,8 +88,9 @@ enum task_types { task_type_bh_in, /* Implicit */ task_type_bh_out, /* Implicit */ task_type_bh_density_ghost, - task_type_bh_swallow_ghost1, + task_type_bh_swallow_ghost1, /* Implicit */ task_type_bh_swallow_ghost2, + task_type_bh_swallow_ghost3, /* Implicit */ task_type_fof_self, task_type_fof_pair, task_type_count @@ -113,6 +114,7 @@ enum task_subtypes { task_subtype_xv, task_subtype_rho, task_subtype_part_swallow, + task_subtype_bpart_merger, task_subtype_gpart, task_subtype_multipole, task_subtype_spart, @@ -124,7 +126,8 @@ enum task_subtypes { task_subtype_bpart_feedback, task_subtype_bh_density, task_subtype_bh_swallow, - task_subtype_do_swallow, + task_subtype_do_gas_swallow, + task_subtype_do_bh_swallow, task_subtype_bh_feedback, task_subtype_count } __attribute__((packed)); diff --git a/src/timers.c b/src/timers.c index 8c193f9f2e15746bd551f8a56edafe7c80753334..cc4ebc90d491832178f1e48e96960f489c73f4c1 100644 --- a/src/timers.c +++ b/src/timers.c @@ -56,6 +56,9 @@ const char* timers_names[timer_count] = { "doself_limiter", "doself_stars_density", "doself_stars_feedback", + "doself_bh_density", + "doself_bh_swallow", + "doself_bh_feedback", "doself_grav_pp", "dopair_density", "dopair_gradient", @@ -63,6 +66,9 @@ const char* timers_names[timer_count] = { "dopair_limiter", "dopair_stars_density", "dopair_stars_feedback", + "dopair_bh_density", + "dopair_bh_swallow", + "dopair_bh_feedback", "dopair_grav_mm", "dopair_grav_pp", "dograv_external", @@ -76,6 +82,9 @@ const char* timers_names[timer_count] = { "dosub_self_limiter", "dosub_self_stars_density", "dosub_self_stars_feedback", + "dosub_self_bh_density", + "dosub_self_bh_swallow", + "dosub_self_bh_feedback", "dosub_self_grav", "dosub_pair_density", "dosub_pair_gradient", @@ -83,6 +92,9 @@ const char* timers_names[timer_count] = { "dosub_pair_limiter", "dosub_pair_stars_density", "dosub_pair_stars_feedback", + "dosub_pair_bh_density", + "dosub_pair_bh_swallow", + "dosub_pair_bh_feedback", "dosub_pair_grav", "doself_subset", "dopair_subset", @@ -91,9 +103,11 @@ const char* timers_names[timer_count] = { "do_ghost", "do_extra_ghost", "do_stars_ghost", + "do_black_holes_ghost", "dorecv_part", "dorecv_gpart", "dorecv_spart", + "dorecv_bpart", "do_limiter", "do_cooling", "do_star_formation", @@ -106,6 +120,7 @@ const char* timers_names[timer_count] = { "step", "logger", "do_stars_sort", + "do_stars_resort", "fof_self", "fof_pair", }; diff --git a/src/timers.h b/src/timers.h index e36aea2c6269ee07f813fc69764ed14e4d2cc550..96742567f549c156944333fafd4342dba14a4bee 100644 --- a/src/timers.h +++ b/src/timers.h @@ -57,6 +57,9 @@ enum { timer_doself_limiter, timer_doself_stars_density, timer_doself_stars_feedback, + timer_doself_bh_density, + timer_doself_bh_swallow, + timer_doself_bh_feedback, timer_doself_grav_pp, timer_dopair_density, timer_dopair_gradient, @@ -64,6 +67,9 @@ enum { timer_dopair_limiter, timer_dopair_stars_density, timer_dopair_stars_feedback, + timer_dopair_bh_density, + timer_dopair_bh_swallow, + timer_dopair_bh_feedback, timer_dopair_grav_mm, timer_dopair_grav_pp, timer_dograv_external, @@ -77,6 +83,9 @@ enum { timer_dosub_self_limiter, timer_dosub_self_stars_density, timer_dosub_self_stars_feedback, + timer_dosub_self_bh_density, + timer_dosub_self_bh_swallow, + timer_dosub_self_bh_feedback, timer_dosub_self_grav, timer_dosub_pair_density, timer_dosub_pair_gradient, @@ -84,6 +93,9 @@ enum { timer_dosub_pair_limiter, timer_dosub_pair_stars_density, timer_dosub_pair_stars_feedback, + timer_dosub_pair_bh_density, + timer_dosub_pair_bh_swallow, + timer_dosub_pair_bh_feedback, timer_dosub_pair_grav, timer_doself_subset, timer_dopair_subset, @@ -92,9 +104,11 @@ enum { timer_do_ghost, timer_do_extra_ghost, timer_do_stars_ghost, + timer_do_black_holes_ghost, timer_dorecv_part, timer_dorecv_gpart, timer_dorecv_spart, + timer_dorecv_bpart, timer_do_limiter, timer_do_cooling, timer_do_star_formation, diff --git a/src/timestep.h b/src/timestep.h index c2b1a10fcb3b0426e7c34625d65c1fd5353d25e9..cd9faaea612c8a666ae9077ccc5e3d85fd4677f9 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -146,8 +146,14 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( new_dt_grav = min(new_dt_self_grav, new_dt_ext_grav); } - /* Final time-step is minimum of hydro and gravity */ - float new_dt = min3(new_dt_hydro, new_dt_cooling, new_dt_grav); + /* Compute the next timestep (chemistry condition, e.g. diffusion) */ + const float new_dt_chemistry = + chemistry_timestep(e->physical_constants, e->cosmology, e->internal_units, + e->hydro_properties, e->chemistry, p); + + /* Final time-step is minimum of hydro, gravity and subgrid */ + float new_dt = + min4(new_dt_hydro, new_dt_cooling, new_dt_grav, new_dt_chemistry); /* Limit change in smoothing length */ const float dt_h_change = diff --git a/src/timestep_limiter.h b/src/timestep_limiter.h index d8555a352c8e1a799ac13d268932c9d37f30fe33..01b72daea5599b662c38fdc4b3ada8b2ac5b3d11 100644 --- a/src/timestep_limiter.h +++ b/src/timestep_limiter.h @@ -22,6 +22,9 @@ /* Config parameters. */ #include "../config.h" +/* Local headers. */ +#include "kick.h" + /** * @brief Wakes up a particle by rewinding it's kick1 back in time and applying * a new one such that the particle becomes active again in the next time-step. diff --git a/src/tools.c b/src/tools.c index bd467e1841b78d45a74b037c63e8ed58dd92183f..0643fb7922c0e3d56b70c6a0d1a30e3ca13154c6 100644 --- a/src/tools.c +++ b/src/tools.c @@ -45,8 +45,8 @@ #include "hydro.h" #include "part.h" #include "periodic.h" +#include "pressure_floor_iact.h" #include "runner.h" -#include "star_formation_iact.h" #include "stars.h" /** @@ -224,7 +224,7 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { /* Interact */ runner_iact_nonsym_density(r2, dx, hi, pj->h, pi, pj, a, H); runner_iact_nonsym_chemistry(r2, dx, hi, pj->h, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dx, hi, pj->h, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hi, pj->h, pi, pj, a, H); } } } @@ -257,7 +257,7 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { /* Interact */ runner_iact_nonsym_density(r2, dx, hj, pi->h, pj, pi, a, H); runner_iact_nonsym_chemistry(r2, dx, hj, pi->h, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dx, hj, pi->h, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dx, hj, pi->h, pj, pi, a, H); } } } @@ -535,7 +535,7 @@ void self_all_density(struct runner *r, struct cell *ci) { /* Interact */ runner_iact_nonsym_density(r2, dxi, hi, hj, pi, pj, a, H); runner_iact_nonsym_chemistry(r2, dxi, hi, hj, pi, pj, a, H); - runner_iact_nonsym_star_formation(r2, dxi, hi, hj, pi, pj, a, H); + runner_iact_nonsym_pressure_floor(r2, dxi, hi, hj, pi, pj, a, H); } /* Hit or miss? */ @@ -548,7 +548,7 @@ void self_all_density(struct runner *r, struct cell *ci) { /* Interact */ runner_iact_nonsym_density(r2, dxi, hj, hi, pj, pi, a, H); runner_iact_nonsym_chemistry(r2, dxi, hj, hi, pj, pi, a, H); - runner_iact_nonsym_star_formation(r2, dxi, hj, hi, pj, pi, a, H); + runner_iact_nonsym_pressure_floor(r2, dxi, hj, hi, pj, pi, a, H); } } } diff --git a/src/tracers/EAGLE/tracers_io.h b/src/tracers/EAGLE/tracers_io.h index 038cc1c8d3f92c2105d5b5c3ead958f60486ce9f..e058a02749a86f12838268af1aa719c9bc0cdeeb 100644 --- a/src/tracers/EAGLE/tracers_io.h +++ b/src/tracers/EAGLE/tracers_io.h @@ -54,20 +54,23 @@ __attribute__((always_inline)) INLINE static int tracers_write_particles( const struct part* parts, const struct xpart* xparts, struct io_props* list, const int with_cosmology) { - list[0] = io_make_output_field("Maximal Temperature", FLOAT, 1, - UNIT_CONV_TEMPERATURE, xparts, - tracers_data.maximum_temperature); + list[0] = io_make_output_field( + "MaximalTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, xparts, + tracers_data.maximum_temperature, + "Maximal temperatures ever reached by the particles"); if (with_cosmology) { list[1] = io_make_output_field( - "Maximal Temperature scale-factor", FLOAT, 1, UNIT_CONV_NO_UNITS, - xparts, tracers_data.maximum_temperature_scale_factor); + "MaximalTemperatureScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + xparts, tracers_data.maximum_temperature_scale_factor, + "Scale-factors at which the maximal temperature was reached"); } else { - list[1] = io_make_output_field("Maximal Temperature time", FLOAT, 1, - UNIT_CONV_NO_UNITS, xparts, - tracers_data.maximum_temperature_time); + list[1] = io_make_output_field( + "MaximalTemperatureTimes", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, xparts, + tracers_data.maximum_temperature_time, + "Times at which the maximal temperature was reached"); } return 2; @@ -77,22 +80,27 @@ __attribute__((always_inline)) INLINE static int tracers_write_sparticles( const struct spart* sparts, struct io_props* list, const int with_cosmology) { - list[0] = io_make_output_field("Maximal Temperature", FLOAT, 1, - UNIT_CONV_TEMPERATURE, sparts, - tracers_data.maximum_temperature); + list[0] = io_make_output_field( + "MaximalTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, sparts, + tracers_data.maximum_temperature, + "Maximal temperatures ever reached by the particles before they got " + "converted to stars"); if (with_cosmology) { list[1] = io_make_output_field( - "Maximal Temperature scale-factor", FLOAT, 1, UNIT_CONV_NO_UNITS, - sparts, tracers_data.maximum_temperature_scale_factor); + "MaximalTemperatureScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + sparts, tracers_data.maximum_temperature_scale_factor, + "Scale-factors at which the maximal temperature was reached"); } else { - list[1] = io_make_output_field("Maximal Temperature time", FLOAT, 1, - UNIT_CONV_NO_UNITS, sparts, - tracers_data.maximum_temperature_time); + list[1] = io_make_output_field( + "MaximalTemperatureTimes", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + tracers_data.maximum_temperature_time, + "Times at which the maximal temperature was reached"); } return 2; } + #endif /* SWIFT_TRACERS_EAGLE_IO_H */ diff --git a/src/units.c b/src/units.c index 807640a1d2b5844e721fa5b2815acd84e968efba..c7fe549aff5d8cbcec77814f40f0682bd0fc3a8c 100644 --- a/src/units.c +++ b/src/units.c @@ -427,31 +427,19 @@ float units_h_factor(const struct unit_system* us, return units_general_h_factor(us, baseUnitsExp); } -/** - * @brief Returns the scaling factor exponentiation for a given unit - * @param us The system of units in use - * @param unit The unit to convert - */ -float units_a_factor(const struct unit_system* us, - enum unit_conversion_factor unit) { - float baseUnitsExp[5] = {0.f}; - - units_get_base_unit_exponents_array(baseUnitsExp, unit); - - return units_general_a_factor(us, baseUnitsExp); -} - /** * @brief Returns a string containing the exponents of the base units making up * the conversion factors */ void units_cgs_conversion_string(char* buffer, const struct unit_system* us, - enum unit_conversion_factor unit) { + enum unit_conversion_factor unit, + float scale_factor_exponent) { float baseUnitsExp[5] = {0.f}; units_get_base_unit_exponents_array(baseUnitsExp, unit); - units_general_cgs_conversion_string(buffer, us, baseUnitsExp); + units_general_cgs_conversion_string(buffer, us, baseUnitsExp, + scale_factor_exponent); } /** @@ -490,22 +478,6 @@ float units_general_h_factor(const struct unit_system* us, return factor_exp; } -/** - * @brief Returns the scaling factor exponentiation for a given unit (expressed - * in terms of the 5 fundamental units) - * @param us The unit system used - * @param baseUnitsExponents The exponent of each base units required to form - * the desired quantity. See conversionFactor() for a working example - */ -float units_general_a_factor(const struct unit_system* us, - const float baseUnitsExponents[5]) { - float factor_exp = 0.f; - - factor_exp += baseUnitsExponents[UNIT_LENGTH]; - - return factor_exp; -} - /** * @brief Returns a string containing the exponents of the base units making up * the conversion factors (expressed in terms of the 5 fundamental units) @@ -517,13 +489,16 @@ float units_general_a_factor(const struct unit_system* us, * 140 chars at most) * @param us The UnitsSystem in use. * @param baseUnitsExponents The exponent of each base units required to form - * the desired quantity. See conversionFactor() for a working example + * the desired quantity. See conversionFactor() for a working example. + * @param scale_factor_exponent The scale-factor exponent to use to convert this + * unit to physical units. */ void units_general_cgs_conversion_string(char* buffer, const struct unit_system* us, - const float baseUnitsExponents[5]) { - char temp[32]; - const double a_exp = units_general_a_factor(us, baseUnitsExponents); + const float baseUnitsExponents[5], + float scale_factor_exponent) { + char temp[32] = {0}; + const double a_exp = scale_factor_exponent; const double h_exp = 0.; /* There are no h-factors in SWIFT outputs. */ /* Check whether we are unitless or not */ diff --git a/src/units.h b/src/units.h index b1eeeb33d63e3aee072d67b09768b7d3aa508f06..4769fa80edbe0c10fd24e064528251e551282153 100644 --- a/src/units.h +++ b/src/units.h @@ -125,10 +125,6 @@ float units_general_h_factor(const struct unit_system* us, const float baseUnitsExponents[5]); float units_h_factor(const struct unit_system* us, enum unit_conversion_factor unit); -float units_general_a_factor(const struct unit_system* us, - const float baseUnitsExponents[5]); -float units_a_factor(const struct unit_system* us, - enum unit_conversion_factor unit); /* Conversion to CGS */ double units_general_cgs_conversion_factor(const struct unit_system* us, @@ -137,9 +133,11 @@ double units_cgs_conversion_factor(const struct unit_system* us, enum unit_conversion_factor unit); void units_general_cgs_conversion_string(char* buffer, const struct unit_system* us, - const float baseUnitsExponents[5]); + const float baseUnitsExponents[5], + float scale_factor_exponent); void units_cgs_conversion_string(char* buffer, const struct unit_system* us, - enum unit_conversion_factor unit); + enum unit_conversion_factor unit, + float scale_factor_exponent); /* Conversion between systems */ double units_general_conversion_factor(const struct unit_system* from, diff --git a/src/velociraptor_interface.c b/src/velociraptor_interface.c index 47c107d047e3abfcac32571f780cf52649a9c38d..8cb65f6ac13fb6d4f813d68656363640b082123f 100644 --- a/src/velociraptor_interface.c +++ b/src/velociraptor_interface.c @@ -283,6 +283,13 @@ void velociraptor_convert_particles_mapper(void *map_data, int nr_gparts, swift_parts[i].T = 0.f; break; + case swift_type_dark_matter_background: + + swift_parts[i].id = gparts[i].id_or_neg_offset; + swift_parts[i].u = 0.f; + swift_parts[i].T = 0.f; + break; + default: error("Particle type not handled by VELOCIraptor."); } @@ -334,13 +341,19 @@ void velociraptor_init(struct engine *e) { } else { sim_info.icosmologicalsim = 0; } - sim_info.izoomsim = 0; + + /* Are we running a zoom? */ + if (e->s->with_DM_background) { + sim_info.izoomsim = 1; + } else { + sim_info.izoomsim = 0; + } /* Tell VELOCIraptor what we have in the simulation */ sim_info.idarkmatter = (e->total_nr_gparts - e->total_nr_parts > 0); sim_info.igas = (e->policy & engine_policy_hydro); sim_info.istar = (e->policy & engine_policy_stars); - sim_info.ibh = 0; // sim_info.ibh = (e->policy&engine_policy_bh); + sim_info.ibh = (e->policy & engine_policy_black_holes); sim_info.iother = 0; /* Be nice, talk! */ @@ -443,14 +456,45 @@ void velociraptor_invoke(struct engine *e, const int linked_with_snap) { /* Are we running with cosmology? */ if (e->policy & engine_policy_cosmology) { sim_info.icosmologicalsim = 1; - sim_info.izoomsim = 0; - const size_t total_nr_baryons = e->total_nr_parts + e->total_nr_sparts; - const size_t total_nr_dmparts = e->total_nr_gparts - total_nr_baryons; - sim_info.interparticlespacing = sim_info.period / cbrt(total_nr_dmparts); + + /* Are we running a zoom? */ + if (e->s->with_DM_background) { + sim_info.izoomsim = 1; + } else { + sim_info.izoomsim = 0; + } + + /* Collect the mass of the non-background gpart */ + double high_res_DM_mass = 0.; + for (size_t i = 0; i < e->s->nr_gparts; ++i) { + const struct gpart *gp = &e->s->gparts[i]; + if (gp->type == swift_type_dark_matter && + gp->time_bin != time_bin_inhibited && + gp->time_bin != time_bin_not_created) { + high_res_DM_mass = gp->mass; + break; + } + } + +#ifdef WITH_MPI + /* We need to all-reduce this in case one of the nodes had 0 DM particles. + */ + MPI_Allreduce(MPI_IN_PLACE, &high_res_DM_mass, 1, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); +#endif + + /* Linking length based on the mean DM inter-particle separation + * in the zoom region and assuming the mean density of the Universe + * is used in the zoom region. */ + const double mean_matter_density = + e->cosmology->Omega_m * e->cosmology->critical_density_0; + sim_info.interparticlespacing = + cbrt(high_res_DM_mass / mean_matter_density); + } else { - sim_info.icosmologicalsim = 0; sim_info.izoomsim = 0; - sim_info.interparticlespacing = -1; + sim_info.icosmologicalsim = 0; + sim_info.interparticlespacing = -1.; } /* Set the spatial extent of the simulation volume */ diff --git a/src/velociraptor_io.h b/src/velociraptor_io.h index d535e54815139e243b9a3bc40ec8dd4de2af1ac1..446fc3131bfc638a0b37307110a2e802c3dd01ea 100644 --- a/src/velociraptor_io.h +++ b/src/velociraptor_io.h @@ -61,8 +61,9 @@ __attribute__((always_inline)) INLINE static int velociraptor_write_parts( struct io_props* list) { list[0] = io_make_output_field_convert_part( - "GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, parts, xparts, - velociraptor_convert_part_groupID); + "VELOCIraptorGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + xparts, velociraptor_convert_part_groupID, + "Group IDs of the particles in the VELOCIraptor catalogue"); return 1; } @@ -70,8 +71,9 @@ __attribute__((always_inline)) INLINE static int velociraptor_write_parts( __attribute__((always_inline)) INLINE static int velociraptor_write_gparts( const struct velociraptor_gpart_data* group_data, struct io_props* list) { - list[0] = io_make_output_field("GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, - group_data, groupID); + list[0] = io_make_output_field( + "VELOCIraptorGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, group_data, + groupID, "Group IDs of the particles in the VELOCIraptor catalogue"); return 1; } @@ -80,8 +82,9 @@ __attribute__((always_inline)) INLINE static int velociraptor_write_sparts( const struct spart* sparts, struct io_props* list) { list[0] = io_make_output_field_convert_spart( - "GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, sparts, - velociraptor_convert_spart_groupID); + "VELOCIraptorGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + velociraptor_convert_spart_groupID, + "Group IDs of the particles in the VELOCIraptor catalogue"); return 1; } @@ -90,8 +93,9 @@ __attribute__((always_inline)) INLINE static int velociraptor_write_bparts( const struct bpart* bparts, struct io_props* list) { list[0] = io_make_output_field_convert_bpart( - "GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, bparts, - velociraptor_convert_bpart_groupID); + "VELOCIraptorGroupIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + velociraptor_convert_bpart_groupID, + "Group IDs of the particles in the VELOCIraptor catalogue"); return 1; } diff --git a/tests/Makefile.am b/tests/Makefile.am index b75e4ae34a23a6e8c208529345d925a4dcc44623..425190bc1531f8fa4822a705d6ee5d8656e04586 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,25 +23,25 @@ AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS TESTS = testGreetings testMaths testReading.sh testKernel \ testActivePair.sh test27cells.sh test27cellsPerturbed.sh \ testParser.sh test125cells.sh test125cellsPerturbed.sh testFFT \ - testAdiabaticIndex testRandom \ + testAdiabaticIndex testRandom testRandomSpacing \ testMatrixInversion testThreadpool testDump testLogger testInteractions.sh \ testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \ testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \ testPotentialPair testEOS testUtilities testSelectOutput.sh \ testCbrt testCosmology testOutputList testFormat.sh \ - test27cellsStars.sh test27cellsStarsPerturbed.sh + test27cellsStars.sh test27cellsStarsPerturbed.sh testHydroMPIrules # List of test programs to compile check_PROGRAMS = testGreetings testReading testTimeIntegration \ testActivePair test27cells test27cells_subset test125cells testParser \ testKernel testFFT testInteractions testMaths testRandom \ - testSymmetry testThreadpool \ + testSymmetry testThreadpool testRandomSpacing \ testAdiabaticIndex testRiemannExact testRiemannTRRS \ testRiemannHLLC testMatrixInversion testDump testLogger \ testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \ testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \ testSelectOutput testCbrt testCosmology testOutputList test27cellsStars \ - test27cellsStars_subset testCooling testFeedback testHashmap + test27cellsStars_subset testCooling testFeedback testHashmap testHydroMPIrules # Rebuild tests when SWIFT is updated. $(check_PROGRAMS): ../src/.libs/libswiftsim.a @@ -53,6 +53,8 @@ testMaths_SOURCES = testMaths.c testRandom_SOURCES = testRandom.c +testRandomSpacing_SOURCES = testRandomSpacing.c + testReading_SOURCES = testReading.c testSelectOutput_SOURCES = testSelectOutput.c @@ -132,6 +134,8 @@ testFeedback_SOURCES = testFeedback.c testHashmap_SOURCES = testHashmap.c +testHydroMPIrules = testHydroMPIrules.c + # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \ diff --git a/tests/testGravityDerivatives.c b/tests/testGravityDerivatives.c index f31967de7075bccfb2c7fb19c1ba262aa12da54f..9dba2dc929715b8da8763cc3dcd87a53e72d6d87 100644 --- a/tests/testGravityDerivatives.c +++ b/tests/testGravityDerivatives.c @@ -935,13 +935,14 @@ int main(int argc, char* argv[]) { message("Seed = %d", seed); srand(seed); + /* Start by testing M2L */ for (int i = 0; i < 100; ++i) { const double dx = 100. * ((double)rand() / (RAND_MAX)); const double dy = 100. * ((double)rand() / (RAND_MAX)); const double dz = 100. * ((double)rand() / (RAND_MAX)); - message("Testing gravity for r=(%e %e %e)", dx, dy, dz); + message("Testing M2L gravity for r=(%e %e %e)", dx, dy, dz); const double r_s = 100. * ((double)rand() / (RAND_MAX)); const double r_s_inv = 1. / r_s; @@ -955,12 +956,12 @@ int main(int argc, char* argv[]) { const double r_inv = 1. / sqrt(r2); const double r = r2 * r_inv; const double eps = r / 10.; - const double eps_inv = 1. / eps; /* Compute all derivatives */ struct potential_derivatives_M2L pot; - potential_derivatives_compute_M2L(dx, dy, dz, r2, r_inv, eps, eps_inv, - periodic, r_s_inv, &pot); + bzero(&pot, sizeof(struct potential_derivatives_M2L)); + potential_derivatives_compute_M2L(dx, dy, dz, r2, r_inv, eps, periodic, + r_s_inv, &pot); /* Minimal value we care about */ const double min = 1e-9; @@ -968,89 +969,211 @@ int main(int argc, char* argv[]) { /* Now check everything... */ /* 0th order terms */ - test(pot.D_000, D_000(dx, dy, dz, r_inv), tol, min, "D_000"); + test(pot.D_000, D_000(dx, dy, dz, r_inv), tol, min, "M2L D_000"); #if SELF_GRAVITY_MULTIPOLE_ORDER > 0 /* 1st order terms */ - test(pot.D_100, D_100(dx, dy, dz, r_inv), tol, min, "D_100"); - test(pot.D_010, D_010(dx, dy, dz, r_inv), tol, min, "D_010"); - test(pot.D_001, D_001(dx, dy, dz, r_inv), tol, min, "D_001"); + test(pot.D_100, D_100(dx, dy, dz, r_inv), tol, min, "M2L D_100"); + test(pot.D_010, D_010(dx, dy, dz, r_inv), tol, min, "M2L D_010"); + test(pot.D_001, D_001(dx, dy, dz, r_inv), tol, min, "M2L D_001"); #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 1 /* 2nd order terms */ - test(pot.D_200, D_200(dx, dy, dz, r_inv), tol, min, "D_200"); - test(pot.D_020, D_020(dx, dy, dz, r_inv), tol, min, "D_020"); - test(pot.D_002, D_002(dx, dy, dz, r_inv), tol, min, "D_002"); - test(pot.D_110, D_110(dx, dy, dz, r_inv), tol, min, "D_110"); - test(pot.D_101, D_101(dx, dy, dz, r_inv), tol, min, "D_101"); - test(pot.D_011, D_011(dx, dy, dz, r_inv), tol, min, "D_011"); + test(pot.D_200, D_200(dx, dy, dz, r_inv), tol, min, "M2L D_200"); + test(pot.D_020, D_020(dx, dy, dz, r_inv), tol, min, "M2L D_020"); + test(pot.D_002, D_002(dx, dy, dz, r_inv), tol, min, "M2L D_002"); + test(pot.D_110, D_110(dx, dy, dz, r_inv), tol, min, "M2L D_110"); + test(pot.D_101, D_101(dx, dy, dz, r_inv), tol, min, "M2L D_101"); + test(pot.D_011, D_011(dx, dy, dz, r_inv), tol, min, "M2L D_011"); #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 2 tol *= 2.5; /* 3rd order terms */ - test(pot.D_300, D_300(dx, dy, dz, r_inv), tol, min, "D_300"); - test(pot.D_030, D_030(dx, dy, dz, r_inv), tol, min, "D_030"); - test(pot.D_003, D_003(dx, dy, dz, r_inv), tol, min, "D_003"); - test(pot.D_210, D_210(dx, dy, dz, r_inv), tol, min, "D_210"); - test(pot.D_201, D_201(dx, dy, dz, r_inv), tol, min, "D_201"); - test(pot.D_120, D_120(dx, dy, dz, r_inv), tol, min, "D_120"); - test(pot.D_021, D_021(dx, dy, dz, r_inv), tol, min, "D_021"); - test(pot.D_102, D_102(dx, dy, dz, r_inv), tol, min, "D_102"); - test(pot.D_012, D_012(dx, dy, dz, r_inv), tol, min, "D_012"); - test(pot.D_111, D_111(dx, dy, dz, r_inv), tol, min, "D_111"); + test(pot.D_300, D_300(dx, dy, dz, r_inv), tol, min, "M2L D_300"); + test(pot.D_030, D_030(dx, dy, dz, r_inv), tol, min, "M2L D_030"); + test(pot.D_003, D_003(dx, dy, dz, r_inv), tol, min, "M2L D_003"); + test(pot.D_210, D_210(dx, dy, dz, r_inv), tol, min, "M2L D_210"); + test(pot.D_201, D_201(dx, dy, dz, r_inv), tol, min, "M2L D_201"); + test(pot.D_120, D_120(dx, dy, dz, r_inv), tol, min, "M2L D_120"); + test(pot.D_021, D_021(dx, dy, dz, r_inv), tol, min, "M2L D_021"); + test(pot.D_102, D_102(dx, dy, dz, r_inv), tol, min, "M2L D_102"); + test(pot.D_012, D_012(dx, dy, dz, r_inv), tol, min, "M2L D_012"); + test(pot.D_111, D_111(dx, dy, dz, r_inv), tol, min, "M2L D_111"); #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 3 /* 4th order terms */ - test(pot.D_400, D_400(dx, dy, dz, r_inv), tol, min, "D_400"); - test(pot.D_040, D_040(dx, dy, dz, r_inv), tol, min, "D_040"); - test(pot.D_004, D_004(dx, dy, dz, r_inv), tol, min, "D_004"); - test(pot.D_310, D_310(dx, dy, dz, r_inv), tol, min, "D_310"); - test(pot.D_301, D_301(dx, dy, dz, r_inv), tol, min, "D_301"); - test(pot.D_130, D_130(dx, dy, dz, r_inv), tol, min, "D_130"); - test(pot.D_031, D_031(dx, dy, dz, r_inv), tol, min, "D_031"); - test(pot.D_103, D_103(dx, dy, dz, r_inv), tol, min, "D_103"); - test(pot.D_013, D_013(dx, dy, dz, r_inv), tol, min, "D_013"); - test(pot.D_220, D_220(dx, dy, dz, r_inv), tol, min, "D_220"); - test(pot.D_202, D_202(dx, dy, dz, r_inv), tol, min, "D_202"); - test(pot.D_022, D_022(dx, dy, dz, r_inv), tol, min, "D_022"); - test(pot.D_211, D_211(dx, dy, dz, r_inv), tol, min, "D_211"); - test(pot.D_121, D_121(dx, dy, dz, r_inv), tol, min, "D_121"); - test(pot.D_112, D_112(dx, dy, dz, r_inv), tol, min, "D_112"); + test(pot.D_400, D_400(dx, dy, dz, r_inv), tol, min, "M2L D_400"); + test(pot.D_040, D_040(dx, dy, dz, r_inv), tol, min, "M2L D_040"); + test(pot.D_004, D_004(dx, dy, dz, r_inv), tol, min, "M2L D_004"); + test(pot.D_310, D_310(dx, dy, dz, r_inv), tol, min, "M2L D_310"); + test(pot.D_301, D_301(dx, dy, dz, r_inv), tol, min, "M2L D_301"); + test(pot.D_130, D_130(dx, dy, dz, r_inv), tol, min, "M2L D_130"); + test(pot.D_031, D_031(dx, dy, dz, r_inv), tol, min, "M2L D_031"); + test(pot.D_103, D_103(dx, dy, dz, r_inv), tol, min, "M2L D_103"); + test(pot.D_013, D_013(dx, dy, dz, r_inv), tol, min, "M2L D_013"); + test(pot.D_220, D_220(dx, dy, dz, r_inv), tol, min, "M2L D_220"); + test(pot.D_202, D_202(dx, dy, dz, r_inv), tol, min, "M2L D_202"); + test(pot.D_022, D_022(dx, dy, dz, r_inv), tol, min, "M2L D_022"); + test(pot.D_211, D_211(dx, dy, dz, r_inv), tol, min, "M2L D_211"); + test(pot.D_121, D_121(dx, dy, dz, r_inv), tol, min, "M2L D_121"); + test(pot.D_112, D_112(dx, dy, dz, r_inv), tol, min, "M2L D_112"); #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 4 tol *= 2.5; /* 5th order terms */ - test(pot.D_500, D_500(dx, dy, dz, r_inv), tol, min, "D_500"); - test(pot.D_050, D_050(dx, dy, dz, r_inv), tol, min, "D_050"); - test(pot.D_005, D_005(dx, dy, dz, r_inv), tol, min, "D_005"); - test(pot.D_410, D_410(dx, dy, dz, r_inv), tol, min, "D_410"); - test(pot.D_401, D_401(dx, dy, dz, r_inv), tol, min, "D_401"); - test(pot.D_140, D_140(dx, dy, dz, r_inv), tol, min, "D_140"); - test(pot.D_041, D_041(dx, dy, dz, r_inv), tol, min, "D_041"); - test(pot.D_104, D_104(dx, dy, dz, r_inv), tol, min, "D_104"); - test(pot.D_014, D_014(dx, dy, dz, r_inv), tol, min, "D_014"); - test(pot.D_320, D_320(dx, dy, dz, r_inv), tol, min, "D_320"); - test(pot.D_302, D_302(dx, dy, dz, r_inv), tol, min, "D_302"); - test(pot.D_230, D_230(dx, dy, dz, r_inv), tol, min, "D_230"); - test(pot.D_032, D_032(dx, dy, dz, r_inv), tol, min, "D_032"); - test(pot.D_203, D_203(dx, dy, dz, r_inv), tol, min, "D_203"); - test(pot.D_023, D_023(dx, dy, dz, r_inv), tol, min, "D_023"); - test(pot.D_311, D_311(dx, dy, dz, r_inv), tol, min, "D_311"); - test(pot.D_131, D_131(dx, dy, dz, r_inv), tol, min, "D_131"); - test(pot.D_113, D_113(dx, dy, dz, r_inv), tol, min, "D_113"); - test(pot.D_122, D_122(dx, dy, dz, r_inv), tol, min, "D_122"); - test(pot.D_212, D_212(dx, dy, dz, r_inv), tol, min, "D_212"); - test(pot.D_221, D_221(dx, dy, dz, r_inv), tol, min, "D_221"); + test(pot.D_500, D_500(dx, dy, dz, r_inv), tol, min, "M2L D_500"); + test(pot.D_050, D_050(dx, dy, dz, r_inv), tol, min, "M2L D_050"); + test(pot.D_005, D_005(dx, dy, dz, r_inv), tol, min, "M2L D_005"); + test(pot.D_410, D_410(dx, dy, dz, r_inv), tol, min, "M2L D_410"); + test(pot.D_401, D_401(dx, dy, dz, r_inv), tol, min, "M2L D_401"); + test(pot.D_140, D_140(dx, dy, dz, r_inv), tol, min, "M2L D_140"); + test(pot.D_041, D_041(dx, dy, dz, r_inv), tol, min, "M2L D_041"); + test(pot.D_104, D_104(dx, dy, dz, r_inv), tol, min, "M2L D_104"); + test(pot.D_014, D_014(dx, dy, dz, r_inv), tol, min, "M2L D_014"); + test(pot.D_320, D_320(dx, dy, dz, r_inv), tol, min, "M2L D_320"); + test(pot.D_302, D_302(dx, dy, dz, r_inv), tol, min, "M2L D_302"); + test(pot.D_230, D_230(dx, dy, dz, r_inv), tol, min, "M2L D_230"); + test(pot.D_032, D_032(dx, dy, dz, r_inv), tol, min, "M2L D_032"); + test(pot.D_203, D_203(dx, dy, dz, r_inv), tol, min, "M2L D_203"); + test(pot.D_023, D_023(dx, dy, dz, r_inv), tol, min, "M2L D_023"); + test(pot.D_311, D_311(dx, dy, dz, r_inv), tol, min, "M2L D_311"); + test(pot.D_131, D_131(dx, dy, dz, r_inv), tol, min, "M2L D_131"); + test(pot.D_113, D_113(dx, dy, dz, r_inv), tol, min, "M2L D_113"); + test(pot.D_122, D_122(dx, dy, dz, r_inv), tol, min, "M2L D_122"); + test(pot.D_212, D_212(dx, dy, dz, r_inv), tol, min, "M2L D_212"); + test(pot.D_221, D_221(dx, dy, dz, r_inv), tol, min, "M2L D_221"); #endif message("All good!"); } + + /* And now the M2P terms */ + for (int i = 0; i < 100; ++i) { + + const double dx = 100. * ((double)rand() / (RAND_MAX)); + const double dy = 100. * ((double)rand() / (RAND_MAX)); + const double dz = 100. * ((double)rand() / (RAND_MAX)); + + message("Testing M2P gravity for r=(%e %e %e)", dx, dy, dz); + + const double r_s = 100. * ((double)rand() / (RAND_MAX)); + const double r_s_inv = 1. / r_s; + + const int periodic = 0; + + message("Mesh scale r_s=%e periodic=%d", r_s, periodic); + + /* Compute distance */ + const double r2 = dx * dx + dy * dy + dz * dz; + const double r_inv = 1. / sqrt(r2); + const double r = r2 * r_inv; + const double eps = r / 10.; + + /* Compute all derivatives */ + struct potential_derivatives_M2P pot; + bzero(&pot, sizeof(struct potential_derivatives_M2P)); + potential_derivatives_compute_M2P(dx, dy, dz, r2, r_inv, eps, periodic, + r_s_inv, &pot); + + /* Minimal value we care about */ + const double min = 1e-9; + + /* Now check everything... + * + * Note that the M2P derivatives are computed to order + * SELF_GRAVITY_MULTIPOLE_ORDER + 1 by the function above. */ + + /* 0th order terms */ + test(pot.D_000, D_000(dx, dy, dz, r_inv), tol, min, "M2P D_000"); + + /* 1st order terms */ + test(pot.D_100, D_100(dx, dy, dz, r_inv), tol, min, "M2P D_100"); + test(pot.D_010, D_010(dx, dy, dz, r_inv), tol, min, "M2P D_010"); + test(pot.D_001, D_001(dx, dy, dz, r_inv), tol, min, "M2P D_001"); + +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 + + /* 2nd order terms */ + test(pot.D_200, D_200(dx, dy, dz, r_inv), tol, min, "M2P D_200"); + test(pot.D_020, D_020(dx, dy, dz, r_inv), tol, min, "M2P D_020"); + test(pot.D_002, D_002(dx, dy, dz, r_inv), tol, min, "M2P D_002"); + test(pot.D_110, D_110(dx, dy, dz, r_inv), tol, min, "M2P D_110"); + test(pot.D_101, D_101(dx, dy, dz, r_inv), tol, min, "M2P D_101"); + test(pot.D_011, D_011(dx, dy, dz, r_inv), tol, min, "M2P D_011"); +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 1 + + tol *= 2.5; + + /* 3rd order terms */ + test(pot.D_300, D_300(dx, dy, dz, r_inv), tol, min, "M2P D_300"); + test(pot.D_030, D_030(dx, dy, dz, r_inv), tol, min, "M2P D_030"); + test(pot.D_003, D_003(dx, dy, dz, r_inv), tol, min, "M2P D_003"); + test(pot.D_210, D_210(dx, dy, dz, r_inv), tol, min, "M2P D_210"); + test(pot.D_201, D_201(dx, dy, dz, r_inv), tol, min, "M2P D_201"); + test(pot.D_120, D_120(dx, dy, dz, r_inv), tol, min, "M2P D_120"); + test(pot.D_021, D_021(dx, dy, dz, r_inv), tol, min, "M2P D_021"); + test(pot.D_102, D_102(dx, dy, dz, r_inv), tol, min, "M2P D_102"); + test(pot.D_012, D_012(dx, dy, dz, r_inv), tol, min, "M2P D_012"); + test(pot.D_111, D_111(dx, dy, dz, r_inv), tol, min, "M2P D_111"); +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 2 + + /* 4th order terms */ + test(pot.D_400, D_400(dx, dy, dz, r_inv), tol, min, "M2P D_400"); + test(pot.D_040, D_040(dx, dy, dz, r_inv), tol, min, "M2P D_040"); + test(pot.D_004, D_004(dx, dy, dz, r_inv), tol, min, "M2P D_004"); + test(pot.D_310, D_310(dx, dy, dz, r_inv), tol, min, "M2P D_310"); + test(pot.D_301, D_301(dx, dy, dz, r_inv), tol, min, "M2P D_301"); + test(pot.D_130, D_130(dx, dy, dz, r_inv), tol, min, "M2P D_130"); + test(pot.D_031, D_031(dx, dy, dz, r_inv), tol, min, "M2P D_031"); + test(pot.D_103, D_103(dx, dy, dz, r_inv), tol, min, "M2P D_103"); + test(pot.D_013, D_013(dx, dy, dz, r_inv), tol, min, "M2P D_013"); + test(pot.D_220, D_220(dx, dy, dz, r_inv), tol, min, "M2P D_220"); + test(pot.D_202, D_202(dx, dy, dz, r_inv), tol, min, "M2P D_202"); + test(pot.D_022, D_022(dx, dy, dz, r_inv), tol, min, "M2P D_022"); + test(pot.D_211, D_211(dx, dy, dz, r_inv), tol, min, "M2P D_211"); + test(pot.D_121, D_121(dx, dy, dz, r_inv), tol, min, "M2P D_121"); + test(pot.D_112, D_112(dx, dy, dz, r_inv), tol, min, "M2P D_112"); +#endif +#if SELF_GRAVITY_MULTIPOLE_ORDER > 3 + + tol *= 2.5; + + /* 5th order terms */ + test(pot.D_500, D_500(dx, dy, dz, r_inv), tol, min, "M2P D_500"); + test(pot.D_050, D_050(dx, dy, dz, r_inv), tol, min, "M2P D_050"); + test(pot.D_005, D_005(dx, dy, dz, r_inv), tol, min, "M2P D_005"); + test(pot.D_410, D_410(dx, dy, dz, r_inv), tol, min, "M2P D_410"); + test(pot.D_401, D_401(dx, dy, dz, r_inv), tol, min, "M2P D_401"); + test(pot.D_140, D_140(dx, dy, dz, r_inv), tol, min, "M2P D_140"); + test(pot.D_041, D_041(dx, dy, dz, r_inv), tol, min, "M2P D_041"); + test(pot.D_104, D_104(dx, dy, dz, r_inv), tol, min, "M2P D_104"); + test(pot.D_014, D_014(dx, dy, dz, r_inv), tol, min, "M2P D_014"); + test(pot.D_320, D_320(dx, dy, dz, r_inv), tol, min, "M2P D_320"); + test(pot.D_302, D_302(dx, dy, dz, r_inv), tol, min, "M2P D_302"); + test(pot.D_230, D_230(dx, dy, dz, r_inv), tol, min, "M2P D_230"); + test(pot.D_032, D_032(dx, dy, dz, r_inv), tol, min, "M2P D_032"); + test(pot.D_203, D_203(dx, dy, dz, r_inv), tol, min, "M2P D_203"); + test(pot.D_023, D_023(dx, dy, dz, r_inv), tol, min, "M2P D_023"); + test(pot.D_311, D_311(dx, dy, dz, r_inv), tol, min, "M2P D_311"); + test(pot.D_131, D_131(dx, dy, dz, r_inv), tol, min, "M2P D_131"); + test(pot.D_113, D_113(dx, dy, dz, r_inv), tol, min, "M2P D_113"); + test(pot.D_122, D_122(dx, dy, dz, r_inv), tol, min, "M2P D_122"); + test(pot.D_212, D_212(dx, dy, dz, r_inv), tol, min, "M2P D_212"); + test(pot.D_221, D_221(dx, dy, dz, r_inv), tol, min, "M2P D_221"); + +#endif + message("All good!"); + } + + /* All happy */ return 0; } diff --git a/tests/testHydroMPIrules.c b/tests/testHydroMPIrules.c new file mode 100644 index 0000000000000000000000000000000000000000..0d56b584bbfc24042350e39921d01f53028bbf4e --- /dev/null +++ b/tests/testHydroMPIrules.c @@ -0,0 +1,153 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2019 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/>. + * + ******************************************************************************/ +#include "../config.h" + +#include <fenv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "swift.h" + +void print_bytes(void *p, size_t len) { + printf("("); + for (size_t i = 0; i < len; ++i) { + printf("%02x", ((unsigned char *)p)[i]); + if (i % 4 == 3) printf("|"); + } + printf(")\n"); +} + +void test(void) { + + /* Start with some values for the cosmological parameters */ + const float a = (float)random_uniform(0.8, 1.); + const float H = 1.f; + + /* Create two random particles (don't do this at home !) */ + struct part pi, pj; + for (size_t i = 0; i < sizeof(struct part) / sizeof(float); ++i) { + *(((float *)&pi) + i) = (float)random_uniform(0., 2.); + *(((float *)&pj) + i) = (float)random_uniform(0., 2.); + } + + /* Make the particle smoothing length and position reasonable */ + for (size_t i = 0; i < 3; ++i) pi.x[i] = random_uniform(-1., 1.); + for (size_t i = 0; i < 3; ++i) pj.x[i] = random_uniform(-1., 1.); + pi.h = 2.f; + pj.h = 2.f; + pi.id = 1ll; + pj.id = 2ll; + pi.time_bin = 1; + pj.time_bin = 1; + + /* Make an xpart companion */ + struct xpart xpi, xpj; + bzero(&xpi, sizeof(struct xpart)); + bzero(&xpj, sizeof(struct xpart)); + + /* Make some copies */ + struct part pi2, pj2; + memcpy(&pi2, &pi, sizeof(struct part)); + memcpy(&pj2, &pj, sizeof(struct part)); + + int i_not_ok = memcmp(&pi, &pi2, sizeof(struct part)); + int j_not_ok = memcmp(&pj, &pj2, sizeof(struct part)); + + if (i_not_ok) error("Particles 'pi' do not match after copy"); + if (j_not_ok) error("Particles 'pj' do not match after copy"); + + /* Compute distance vector */ + float dx[3]; + dx[0] = pi.x[0] - pj.x[0]; + dx[1] = pi.x[1] - pj.x[1]; + dx[2] = pi.x[2] - pj.x[2]; + float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + + /* --- Test the density loop --- */ + runner_iact_nonsym_density(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_nonsym_chemistry(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + + /* Check whether pj has been modified */ + j_not_ok = memcmp(&pj, &pj2, sizeof(struct part)); + + if (j_not_ok) { + printParticle_single(&pj, &xpj); + printParticle_single(&pj2, &xpj); + print_bytes(&pj, sizeof(struct part)); + print_bytes(&pj2, sizeof(struct part)); + error("Particles 'pj' do not match after density (byte = %d)", j_not_ok); + } + + /* --- Test the gradient loop --- */ +#ifdef EXTRA_HYDRO_LOOP + + runner_iact_nonsym_gradient(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + + /* Check whether pj has been modified */ + j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); + + if (j_not_ok) { + printParticle_single(&pj, &xpj); + printParticle_single(&pj2, &xpj); + print_bytes(&pj, sizeof(struct part)); + print_bytes(&pj2, sizeof(struct part)); + error("Particles 'pj' do not match after gradient (byte = %d)", j_not_ok); + } +#endif + + /* --- Test the force loop --- */ + runner_iact_nonsym_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + + /* Check that the particles are the same */ + j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); + + if (j_not_ok) { + printParticle_single(&pj, &xpj); + printParticle_single(&pj2, &xpj); + print_bytes(&pj, sizeof(struct part)); + print_bytes(&pj2, sizeof(struct part)); + error("Particles 'pj' do not match after force (byte = %d)", j_not_ok); + } +} + +int main(int argc, char *argv[]) { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + +/* Choke on FPEs */ +#ifdef HAVE_FE_ENABLE_EXCEPT + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + + /* Get some randomness going */ + const int seed = time(NULL); + message("Seed = %d", seed); + srand(seed); + + for (int i = 0; i < 100; ++i) { + message("Random test %d/100", i); + test(); + } + message("All good"); + + return 0; +} diff --git a/tests/testLogger.c b/tests/testLogger.c index c5be0d7cc18742bdc2fa6167462579c45fd43e92..d2c64e7fa3330ebd20cf8abc01a76e1dff08c8fc 100644 --- a/tests/testLogger.c +++ b/tests/testLogger.c @@ -32,8 +32,8 @@ /* Local headers. */ #include "swift.h" -void test_log_parts(struct logger *log) { - struct dump *d = log->dump; +void test_log_parts(struct logger_writer *log) { + struct dump *d = &log->dump; /* Write several copies of a part to the dump. */ struct part p; @@ -45,22 +45,27 @@ void test_log_parts(struct logger *log) { size_t offset = d->count; /* Write the full part. */ - logger_log_part(log, &p, - logger_mask_x | logger_mask_v | logger_mask_a | - logger_mask_u | logger_mask_h | logger_mask_rho | - logger_mask_consts, - &offset); + logger_log_part( + log, &p, + logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask | + logger_mask_data[logger_a].mask | logger_mask_data[logger_u].mask | + logger_mask_data[logger_h].mask | logger_mask_data[logger_rho].mask | + logger_mask_data[logger_consts].mask, + &offset); printf("Wrote part at offset %#016zx.\n", offset); /* Write only the position. */ p.x[0] = 2.0; - logger_log_part(log, &p, logger_mask_x, &offset); + logger_log_part(log, &p, logger_mask_data[logger_x].mask, &offset); printf("Wrote part at offset %#016zx.\n", offset); /* Write the position and velocity. */ p.x[0] = 3.0; p.v[0] = 0.3; - logger_log_part(log, &p, logger_mask_x | logger_mask_v, &offset); + logger_log_part( + log, &p, + logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask, + &offset); printf("Wrote part at offset %#016zx.\n", offset); /* Recover the last part from the dump. */ @@ -103,8 +108,8 @@ void test_log_parts(struct logger *log) { } } -void test_log_gparts(struct logger *log) { - struct dump *d = log->dump; +void test_log_gparts(struct logger_writer *log) { + struct dump *d = &log->dump; /* Write several copies of a part to the dump. */ struct gpart p; @@ -116,21 +121,26 @@ void test_log_gparts(struct logger *log) { size_t offset = d->count; /* Write the full part. */ - logger_log_gpart(log, &p, - logger_mask_x | logger_mask_v | logger_mask_a | - logger_mask_h | logger_mask_consts, - &offset); + logger_log_gpart( + log, &p, + logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask | + logger_mask_data[logger_a].mask | logger_mask_data[logger_h].mask | + logger_mask_data[logger_consts].mask, + &offset); printf("Wrote gpart at offset %#016zx.\n", offset); /* Write only the position. */ p.x[0] = 2.0; - logger_log_gpart(log, &p, logger_mask_x, &offset); + logger_log_gpart(log, &p, logger_mask_data[logger_x].mask, &offset); printf("Wrote gpart at offset %#016zx.\n", offset); /* Write the position and velocity. */ p.x[0] = 3.0; p.v_full[0] = 0.3; - logger_log_gpart(log, &p, logger_mask_x | logger_mask_v, &offset); + logger_log_gpart( + log, &p, + logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask, + &offset); printf("Wrote gpart at offset %#016zx.\n", offset); /* Recover the last part from the dump. */ @@ -173,8 +183,8 @@ void test_log_gparts(struct logger *log) { } } -void test_log_timestamps(struct logger *log) { - struct dump *d = log->dump; +void test_log_timestamps(struct logger_writer *log) { + struct dump *d = &log->dump; /* The timestamp to log. */ unsigned long long int t = 10; @@ -245,7 +255,7 @@ void test_log_timestamps(struct logger *log) { int main(int argc, char *argv[]) { /* Prepare a logger. */ - struct logger log; + struct logger_writer log; struct swift_params params; parser_read_file("logger.yml", ¶ms); logger_init(&log, ¶ms); @@ -265,7 +275,7 @@ int main(int argc, char *argv[]) { remove(filename); /* Clean the logger. */ - logger_clean(&log); + logger_free(&log); /* Return a happy number. */ return 0; diff --git a/tests/testPotentialPair.c b/tests/testPotentialPair.c index 064c86d42f8df907d1ffaaab164b6a2f8b534b19..d5fbda36a9ef79352f79627b5cef908030401da2 100644 --- a/tests/testPotentialPair.c +++ b/tests/testPotentialPair.c @@ -125,7 +125,8 @@ int main(int argc, char *argv[]) { struct gravity_props props; props.theta_crit2 = 0.; - props.epsilon_cur = eps; + props.epsilon_DM_cur = eps; + props.epsilon_baryon_cur = eps; e.gravity_properties = &props; struct runner r; @@ -386,7 +387,7 @@ int main(int argc, char *argv[]) { /* Now let's make a multipole out of it. */ gravity_reset(ci.grav.multipole); - gravity_P2M(ci.grav.multipole, ci.grav.parts, ci.grav.count); + gravity_P2M(ci.grav.multipole, ci.grav.parts, ci.grav.count, &props); gravity_multipole_print(&ci.grav.multipole->m_pole); diff --git a/tests/testPotentialSelf.c b/tests/testPotentialSelf.c index 10eb499570a591daaf0de2e011f2346077905e8e..ff55558aff77441458e822cfa11922bd2dabfa06 100644 --- a/tests/testPotentialSelf.c +++ b/tests/testPotentialSelf.c @@ -115,7 +115,8 @@ int main(int argc, char *argv[]) { struct gravity_props props; props.a_smooth = 1.25; - props.epsilon_cur = eps; + props.epsilon_DM_cur = eps; + props.epsilon_baryon_cur = eps; e.gravity_properties = &props; struct runner r; diff --git a/tests/testRandom.c b/tests/testRandom.c index 8d8619d63e54f3030a2e7288402c4a78857361cb..036f58b29115a2e646cea35e873ccdc9a4164e4e 100644 --- a/tests/testRandom.c +++ b/tests/testRandom.c @@ -101,10 +101,10 @@ int main(int argc, char* argv[]) { srand(seed); /* Time-step size */ - const int time_bin = 29; + const int time_bin = 30; /* Try a few different values for the ID */ - for (int i = 0; i < 20; ++i) { + for (int i = 0; i < 10; ++i) { const long long id = rand() * (1LL << 31) + rand(); const integertime_t increment = (1LL << time_bin); @@ -150,6 +150,9 @@ int main(int argc, char* argv[]) { const double r = random_unit_interval(id, ti_current, random_number_star_formation); + if (r < 0.0 || r >= 1.0) { + error("Generated random vlaue %f is not in [0, 1).", r); + } total += r; total2 += r * r; @@ -163,6 +166,9 @@ int main(int argc, char* argv[]) { /* Calculate if there is a correlation between different ids */ const double r_2ndid = random_unit_interval(idoffset, ti_current, random_number_star_formation); + if (r_2ndid < 0.0 || r_2ndid >= 1.0) { + error("Generated random vlaue %f is not in [0, 1).", r_2ndid); + } /* Pearson correlation for small different IDs */ pearsonIDs += r * r_2ndid; @@ -174,12 +180,21 @@ int main(int argc, char* argv[]) { const double r_sf = random_unit_interval(id, ti_current, random_number_stellar_feedback); + if (r_sf < 0.0 || r_sf >= 1.0) { + error("Generated random vlaue %f is not in [0, 1).", r_sf); + } const double r_se = random_unit_interval( id, ti_current, random_number_stellar_enrichment); + if (r_se < 0.0 || r_se >= 1.0) { + error("Generated random vlaue %f is not in [0, 1).", r_se); + } const double r_bh = random_unit_interval(id, ti_current, random_number_BH_feedback); + if (r_bh < 0.0 || r_bh >= 1.0) { + error("Generated random vlaue %f is not in [0, 1).", r_bh); + } /* Calculate the correlation between the different processes */ total_sf += r_sf; @@ -240,65 +255,110 @@ int main(int argc, char* argv[]) { /* Verify that the mean and variance match the expected values for a uniform * distribution */ - const double tolmean = 2e-4; - const double tolvar = 1e-3; - const double tolcorr = 4e-4; - - if ((fabs(mean - 0.5) / 0.5 > tolmean) || - (fabs(var - 1. / 12.) / (1. / 12.) > tolvar) || - (correlation > tolcorr) || (correlationID > tolcorr) || - (fabs(meanID - 0.5) / 0.5 > tolmean) || - (fabs(varID - 1. / 12.) / (1. / 12.) > tolvar) || - (corr_star_sf > tolcorr) || (corr_star_se > tolcorr) || - (corr_star_bh > tolcorr) || (corr_sf_se > tolcorr) || - (corr_sf_bh > tolcorr) || (corr_se_bh > tolcorr) || - (fabs(mean_sf - 0.5) / 0.5 > tolmean) || - (fabs(mean_se - 0.5) / 0.5 > tolmean) || - (fabs(mean_bh - 0.5) / 0.5 > tolmean) || - (fabs(var_sf - 1. / 12.) / (1. / 12.) > tolvar) || - (fabs(var_se - 1. / 12.) / (1. / 12.) > tolvar) || - (fabs(var_bh - 1. / 12.) / (1. / 12.) > tolvar)) { + + /* Set the allowed standard deviation */ + const double std_check = 5.; + + /* The mean is expected to deviate a maximum of std_check * std / sqrt(N) */ + const double tolmean = std_check / sqrtf(12.f * count); + + /* the variance is expected to deviate a maximum of std_check * variance + * * sqrt(2/(n-1)) */ + const double tolvar = + std_check * sqrtf(2.f / (12.f * ((double)count - 1.f))); + + /* The correlation coefficient is expected to deviate sqrt(1-R^2) + * / sqrt(n-2), in our case <R> = 0, so we get 1/sqrt(n-2) */ + const double tolcorr = std_check / sqrtf((double)count - 2.); + + if ((fabs(mean - 0.5) > tolmean) || (fabs(var - 1. / 12.) > tolvar) || + (fabs(correlation) > tolcorr) || (fabs(correlationID) > tolcorr) || + (fabs(meanID - 0.5) > tolmean) || (fabs(varID - 1. / 12.) > tolvar) || + (fabs(corr_star_sf) > tolcorr) || (fabs(corr_star_se) > tolcorr) || + (fabs(corr_star_bh) > tolcorr) || (fabs(corr_sf_se) > tolcorr) || + (fabs(corr_sf_bh) > tolcorr) || (fabs(corr_se_bh) > tolcorr) || + (fabs(mean_sf - 0.5) > tolmean) || (fabs(mean_se - 0.5) > tolmean) || + (fabs(mean_bh - 0.5) > tolmean) || (fabs(var_sf - 1. / 12.) > tolvar) || + (fabs(var_se - 1. / 12.) > tolvar) || + (fabs(var_bh - 1. / 12.) > tolvar)) { message("Test failed!"); message("Global result:"); message("Result: count=%d mean=%f var=%f, correlation=%f", count, mean, var, correlation); message("Expected: count=%d mean=%f var=%f, correlation=%f", count, 0.5f, 1. / 12., 0.); + message("Max difference: mean=%f var=%f, correlation=%f", + tolmean, tolvar, tolcorr); + message("Difference: mean=%f var=%f, correlation=%f", + fabs(mean - 0.5f), fabs(var - 1. / 12.), fabs(correlation)); message("ID part"); message( - "Result: count%d mean=%f var=%f" + "Result: count=%d mean=%f var=%f" " correlation=%f", count, meanID, varID, correlationID); message( - "Expected: count%d mean=%f var=%f" + "Expected: count=%d mean=%f var=%f" " correlation=%f", count, .5f, 1. / 12., 0.); + message("Max difference: mean=%f var=%f, correlation=%f", + tolmean, tolvar, tolcorr); + message("Difference: mean=%f var=%f, correlation=%f", + fabs(meanID - 0.5f), fabs(varID - 1. / 12.), fabs(correlation)); message("Different physical processes:"); message( "Means: stars=%f stellar feedback=%f stellar " - " enrichement=%f black holes=%f", + " enrichment=%f black holes=%f", mean, mean_sf, mean_se, mean_bh); message( "Expected: stars=%f stellar feedback=%f stellar " - " enrichement=%f black holes=%f", + " enrichment=%f black holes=%f", .5f, .5f, .5f, .5f); + message( + "Max diff: stars=%f stellar feedback=%f stellar " + " enrichment=%f black holes=%f", + tolmean, tolmean, tolmean, tolmean); + message( + "Diff: stars=%f stellar feedback=%f stellar " + " enrichment=%f black holes=%f", + fabs(mean - .5f), fabs(mean_sf - .5f), fabs(mean_se - .5f), + fabs(mean_bh - .5f)); + message(" "); message( "Var: stars=%f stellar feedback=%f stellar " - " enrichement=%f black holes=%f", + " enrichment=%f black holes=%f", var, var_sf, var_se, var_bh); message( "Expected: stars=%f stellar feedback=%f stellar " - " enrichement=%f black holes=%f", + " enrichment=%f black holes=%f", 1. / 12., 1. / 12., 1 / 12., 1. / 12.); message( - "Correlation: stars-sf=%f stars-se=%f stars-bh=%f" + "Max diff: stars=%f stellar feedback=%f stellar " + " enrichment=%f black holes=%f", + tolvar, tolvar, tolvar, tolvar); + message( + "Diff: stars=%f stellar feedback=%f stellar " + " enrichment=%f black holes=%f", + fabs(var - 1. / 12.), fabs(var_sf - 1. / 12.), + fabs(var_se - 1. / 12.), fabs(var_bh - 1. / 12.)); + message(" "); + message( + "Correlation: stars-sf=%f stars-se=%f stars-bh=%f " "sf-se=%f sf-bh=%f se-bh=%f", corr_star_sf, corr_star_se, corr_star_bh, corr_sf_se, corr_sf_bh, corr_se_bh); message( - "Expected: stars-sf=%f stars-se=%f stars-bh=%f" + "Expected: stars-sf=%f stars-se=%f stars-bh=%f " "sf-se=%f sf-bh=%f se-bh=%f", 0., 0., 0., 0., 0., 0.); + message( + "Max diff: stars-sf=%f stars-se=%f stars-bh=%f " + "sf-se=%f sf-bh=%f se-bh=%f", + tolcorr, tolcorr, tolcorr, tolcorr, tolcorr, tolcorr); + message( + "Diff: stars-sf=%f stars-se=%f stars-bh=%f " + "sf-se=%f sf-bh=%f se-bh=%f", + fabs(corr_star_sf), fabs(corr_star_se), fabs(corr_star_bh), + fabs(corr_sf_se), fabs(corr_sf_bh), fabs(corr_se_bh)); return 1; } } diff --git a/tests/testRandomSpacing.c b/tests/testRandomSpacing.c new file mode 100644 index 0000000000000000000000000000000000000000..0d2777ee702458ccaa6170483c48b83ce1a4fc7e --- /dev/null +++ b/tests/testRandomSpacing.c @@ -0,0 +1,192 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2019 Folkert Nobels (nobels@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +#include <fenv.h> + +/* Local headers. */ +#include "swift.h" + +/** + * @brief Test to check that the pseodo-random numbers in SWIFT are random + * enough for our purpose. + * + * @param argc Unused + * @param argv Unused + * @return 0 if everything is fine, 1 if random numbers are not random enough. + */ +int main(int argc, char* argv[]) { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + +/* Choke on FPEs */ +#ifdef HAVE_FE_ENABLE_EXCEPT + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + + /* Get some randomness going */ + const int seed = time(NULL); + message("Seed = %d", seed); + srand(seed); + + /* Time-step size */ + const int time_bin = 30; + + const double boundary[6] = {1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10}; + int count[6] = {0}; + unsigned long long int total = 0; + + /* Try a few different values for the ID */ + for (int i = 0; i < 10; ++i) { + + const long long id = rand() * (1LL << 31) + rand(); + const integertime_t increment = (1LL << time_bin); + + message("Testing id=%lld time_bin=%d", id, time_bin); + + /* Check that the numbers are uniform over the full-range of useful + * time-steps */ + for (integertime_t ti_current = 0LL; ti_current < max_nr_timesteps; + ti_current += increment) { + + total += 1; + ti_current += increment; + + const double r = + random_unit_interval(id, ti_current, random_number_star_formation); + + /* Count the number of random numbers below the boundaries */ + if (r < boundary[0]) count[0] += 1; + if (r < boundary[1]) count[1] += 1; + if (r < boundary[2]) count[2] += 1; + if (r < boundary[3]) count[3] += 1; + if (r < boundary[4]) count[4] += 1; + if (r < boundary[5]) count[5] += 1; + } + + /* Print counted number of random numbers below the boundaries */ + message("Categories | %6.0e %6.0e %6.0e %6.0e %6.0e %6.0e", + boundary[0], boundary[1], boundary[2], boundary[3], boundary[4], + boundary[5]); + message("total = %12lld | %6d %6d %6d %6d %6d %6d", total, count[0], + count[1], count[2], count[3], count[4], count[5]); + /* Calculate the expected amount of random numbers in this range */ + double expected_result[6]; + int expected_result_int[6]; + expected_result[0] = total * boundary[0]; + expected_result[1] = total * boundary[1]; + expected_result[2] = total * boundary[2]; + expected_result[3] = total * boundary[3]; + expected_result[4] = total * boundary[4]; + expected_result[5] = total * boundary[5]; + + expected_result_int[0] = (int)expected_result[0]; + expected_result_int[1] = (int)expected_result[1]; + expected_result_int[2] = (int)expected_result[2]; + expected_result_int[3] = (int)expected_result[3]; + expected_result_int[4] = (int)expected_result[4]; + expected_result_int[5] = (int)expected_result[5]; + + /* Print the expected numbers */ + message("expected | %6d %6d %6d %6d %6d %6d", + expected_result_int[0], expected_result_int[1], + expected_result_int[2], expected_result_int[3], + expected_result_int[4], expected_result_int[5]); + + int std_expected_result[6]; + + /* Calculate the allowed standard error deviation the maximum of: + * 1. the standard error of the expected number doing sqrt(N_expected) + * 2. The standard error of the counted number doing sqrt(N_count) + * 3. 1 to prevent low number statistics to crash for 1 while expected + * close to zero. + * + * 1 and 2 are for large numbers essentially the same but for small numbers + * it becomes imporatant (e.g. count=6 expected=.9, allowed 5+.9 so 6 + * fails, but sqrt(6) ~ 2.5 so it should be fine) */ + std_expected_result[0] = + (int)max3(sqrt(expected_result[0]), 1, sqrt(count[0])); + std_expected_result[1] = + (int)max3(sqrt(expected_result[1]), 1, sqrt(count[1])); + std_expected_result[2] = + (int)max3(sqrt(expected_result[2]), 1, sqrt(count[2])); + std_expected_result[3] = + (int)max3(sqrt(expected_result[3]), 1, sqrt(count[3])); + std_expected_result[4] = + (int)max3(sqrt(expected_result[4]), 1, sqrt(count[4])); + std_expected_result[5] = + (int)max3(sqrt(expected_result[5]), 1, sqrt(count[5])); + + /* We want 5 sigma (can be changed if necessary) */ + const int numb_sigma = 5; + + /* Print the differences and the 5 sigma differences */ + message("Difference | %6d %6d %6d %6d %6d %6d", + abs(expected_result_int[0] - count[0]), + abs(expected_result_int[1] - count[1]), + abs(expected_result_int[2] - count[2]), + abs(expected_result_int[3] - count[3]), + abs(expected_result_int[4] - count[4]), + abs(expected_result_int[5] - count[5])); + + message("5 sigma difference | %6d %6d %6d %6d %6d %6d", + numb_sigma * std_expected_result[0], + numb_sigma * std_expected_result[1], + numb_sigma * std_expected_result[2], + numb_sigma * std_expected_result[3], + numb_sigma * std_expected_result[4], + numb_sigma * std_expected_result[5]); + + /* Fail if it is not within numb_sigma (5) of the expected difference. */ + if (count[0] > + expected_result_int[0] + numb_sigma * std_expected_result[0] || + count[0] < + expected_result_int[0] - numb_sigma * std_expected_result[0] || + count[1] > + expected_result_int[1] + numb_sigma * std_expected_result[1] || + count[1] < + expected_result_int[1] - numb_sigma * std_expected_result[1] || + count[2] > + expected_result_int[2] + numb_sigma * std_expected_result[2] || + count[2] < + expected_result_int[2] - numb_sigma * std_expected_result[2] || + count[3] > + expected_result_int[3] + numb_sigma * std_expected_result[3] || + count[3] < + expected_result_int[3] - numb_sigma * std_expected_result[3] || + count[4] > + expected_result_int[4] + numb_sigma * std_expected_result[4] || + count[4] < + expected_result_int[4] - numb_sigma * std_expected_result[4] || + count[5] > + expected_result_int[5] + numb_sigma * std_expected_result[5] || + count[5] < + expected_result_int[5] - numb_sigma * std_expected_result[5]) { + message("Not all criteria satisfied!"); + return 1; + } + } + + message("All good!"); + return 0; +} diff --git a/tests/testReading.c b/tests/testReading.c index b2cf743a066920c3d28ea8768334a6a8b1c9b5f0..f1013d50dfe951eb93591a8767e0f13e6d2c04ea 100644 --- a/tests/testReading.c +++ b/tests/testReading.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { - size_t Ngas = 0, Ngpart = 0, Nspart = 0, Nbpart = 0; + size_t Ngas = 0, Ngpart = 0, Ngpart_background = 0, Nspart = 0, Nbpart = 0; int flag_entropy_ICs = -1; int i, j, k; double dim[3]; @@ -51,8 +51,16 @@ int main(int argc, char *argv[]) { /* Read data */ read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &bparts, - &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, 1, 1, 0, - 0, 0, 0, 1., 1., 1, 0); + &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, + &flag_entropy_ICs, + /*with_hydro=*/1, + /*with_gravity=*/1, + /*with_stars=*/0, + /*with_black_holes=*/0, + /*with_cosmology=*/0, + /*cleanup_h=*/0, + /*cleanup_sqrt_a=*/0, + /*h=*/1., /*a=*/1., /*n_threads=*/1, /*dry_run=*/0); /* Check global properties read are correct */ assert(dim[0] == boxSize); diff --git a/tests/testSelectOutput.c b/tests/testSelectOutput.c index 53d65adea152269c54cb5befcdb2970780cf063d..4ede910ab9d182ebc141988c05eac672ab0fb635 100644 --- a/tests/testSelectOutput.c +++ b/tests/testSelectOutput.c @@ -84,7 +84,7 @@ int main(int argc, char *argv[]) { clocks_set_cpufreq(cpufreq); char *base_name = "testSelectOutput"; - size_t Ngas = 0, Ngpart = 0, Nspart = 0, Nbpart = 0; + size_t Ngas = 0, Ngpart = 0, Ngpart_background = 0, Nspart = 0, Nbpart = 0; int flag_entropy_ICs = -1; int periodic = 1; double dim[3]; @@ -112,8 +112,16 @@ int main(int argc, char *argv[]) { /* Read data */ message("Reading initial conditions."); read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &bparts, - &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, 1, 0, 0, - 0, 0, 0, 1., 1., 1, 0); + &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, + &flag_entropy_ICs, + /*with_hydro=*/1, + /*with_gravity=*/0, + /*with_stars=*/0, + /*with_black_holes=*/0, + /*with_cosmology=*/0, + /*cleanup_h=*/0, + /*cleanup_sqrt_a=*/0, + /*h=*/1., /*a=*/1., /*n_threads=*/1, /*dry_run=*/0); /* pseudo initialization of the space */ message("Initialization of the space."); @@ -138,6 +146,7 @@ int main(int argc, char *argv[]) { /* pseudo initialization of the engine */ message("Initialization of the engine."); struct engine e; + sprintf(e.run_name, "Select Output Test"); select_output_engine_init(&e, &s, &cosmo, ¶m_file, &cooling, &hydro_properties); diff --git a/tests/testSelectOutput.py b/tests/testSelectOutput.py index aec7f4671fb2768acde768fd9929168559ebb3cb..97e1c865d133b161c5661c7ac63f728065461bd6 100644 --- a/tests/testSelectOutput.py +++ b/tests/testSelectOutput.py @@ -39,8 +39,8 @@ if "Coordinates" not in part0: if "Masses" not in part0: raise Exception("`Masses` not present in HDF5 but should be written") -if "Density" not in part0: - raise Exception("`Density` not present in HDF5 but should be written") +if "Densities" not in part0: + raise Exception("`Densities` not present in HDF5 but should be written") # check error detection diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index 1f0849bb9093948fa68d88984c285c44b403ba79..eb20c84ec8d38cd52eff10a316383a8797a8c6c0 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -55,8 +55,8 @@ void test(void) { } /* Make the particle smoothing length and position reasonable */ - for (size_t i = 0; i < 3; ++i) pi.x[0] = random_uniform(-1., 1.); - for (size_t i = 0; i < 3; ++i) pj.x[0] = random_uniform(-1., 1.); + for (size_t i = 0; i < 3; ++i) pi.x[i] = random_uniform(-1., 1.); + for (size_t i = 0; i < 3; ++i) pj.x[i] = random_uniform(-1., 1.); pi.h = 2.f; pj.h = 2.f; pi.id = 1ll; diff --git a/tests/tolerance_125_perturbed.dat b/tests/tolerance_125_perturbed.dat index 95f5f78246a82b7c326c87f9b4edbac4f51c65e9..d6b21204ae9cec00f0d84a20e3c58bc34a4b4be1 100644 --- a/tests/tolerance_125_perturbed.dat +++ b/tests/tolerance_125_perturbed.dat @@ -1,4 +1,4 @@ # ID pos_x pos_y pos_z v_x v_y v_z h rho div_v S u P c a_x a_y a_z h_dt v_sig dS/dt du/dt 0 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 0 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 3.6e-3 2e-3 2e-3 1e-4 1e-4 1e-4 1e-4 - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 2e-4 2e-4 2e-4 1e-6 1e-6 1e-6 1e-6 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 5e-4 5e-4 5e-4 1e-6 1e-6 1e-6 1e-6 diff --git a/theory/SPH/Derivation/sph_derivation.tex b/theory/SPH/Derivation/sph_derivation.tex index c770035df6050afe836d943dbe6538d4eafc262b..af39fb6833ab7a878d4328f8ba7645b5f07bf7ae 100644 --- a/theory/SPH/Derivation/sph_derivation.tex +++ b/theory/SPH/Derivation/sph_derivation.tex @@ -186,7 +186,7 @@ The spatial differential is fairly straightforward and is given by The differential with respect to the smoothing length is also straightforward after remembering that $W_{ij}(h_j) = w(|r_{ij}|/h_j)/h_j^{n_d}$. Then, \begin{align} - \frac{\partial y_i}{\partial h_i} = -\sum_{j=1}^N \frac{x_j}{h_j} + \frac{\partial y_i}{\partial h_i} = -\sum_{j=1}^N \frac{x_j}{h_i} \left[ n_d W_{ij}(h_i) + \frac{|r_{ij}|}{h_i} \left. diff --git a/theory/SPH/Flavours/sph_flavours.tex b/theory/SPH/Flavours/sph_flavours.tex index d84bdcc0b42129e9d5008051dd6e0e212e4e9463..d0fc3196074858c3dc143558279b2bc0003bd5fb 100644 --- a/theory/SPH/Flavours/sph_flavours.tex +++ b/theory/SPH/Flavours/sph_flavours.tex @@ -462,8 +462,8 @@ motion: \frac{\mathrm{d} \mathbf{v}_i}{\mathrm{d} t} = -\sum_j (\gamma - 1)^2 m_j u_j u_i &\left[\frac{f_{ij}}{\bar{P}_i} \nabla_i W_{ij}(h_i) ~+ \right. \nonumber \\ - &\frac{f_{ji}}{\bar{P}_j} \nabla_i W_{ji}(h_j) ~+ \nonumber \\ - & \left.\nu_{ij}\bar{\nabla_i W_{ij}}\right]~. + &\left.\frac{f_{ji}}{\bar{P}_j} \nabla_i W_{ji}(h_j)\right] ~+ \nonumber \\ + & m_j \nu_{ij}\bar{\nabla_i W_{ij}}~. \label{eq:sph:pu:eom} \end{align} which includes the Monaghan artificial viscosity term and Balsara switch in diff --git a/tools/make_cell_hierarchy.sh b/tools/make_cell_hierarchy.sh old mode 100644 new mode 100755 index 87fbe4c97f4aadcbb9be5867a62e8acb56415820..9d1d3caf7c4e2f0514c3d6ad5b2db48efa8958d5 --- a/tools/make_cell_hierarchy.sh +++ b/tools/make_cell_hierarchy.sh @@ -9,7 +9,7 @@ then rm $csv_output fi -for filename in ./cell_hierarchy_*.csv; +for filename in $@; do cat $filename >> cell_hierarchy.csv done