diff --git a/.gitignore b/.gitignore index 8fb76e03bdf2b6ab795f9ba141459315242dd7b6..438ea6c0b497f4939c67e35f4d4046d7346e33e1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ config.sub ltmain.sh libtool build +swift +swift_mpi +fof +fof_mpi src/version_string.h swift*.tar.gz @@ -23,10 +27,6 @@ doc/latex/ doc/man/ doc/Doxyfile -examples/swift -examples/swift_mpi -examples/fof -examples/fof_mpi examples/*/*/*.xmf examples/*/*/*.dat examples/*/*/*.png @@ -68,6 +68,11 @@ examples/Cooling/CoolingRates/cooling_element_*.dat examples/Cooling/CoolingRates/cooling_output.dat examples/SubgridTests/StellarEvolution/StellarEvolutionSolution* examples/SubgridTests/CosmologicalStellarEvolution/StellarEvolutionSolution* +examples/SmallCosmoVolume/SmallCosmoVolume_DM/power_spectra +examples/SmallCosmoVolume/SmallCosmoVolume_cooling/snapshots/ +examples/SmallCosmoVolume/SmallCosmoVolume_hydro/snapshots/ +examples/SmallCosmoVolume/SmallCosmoVolume_cooling/CloudyData_UVB=HM2012.h5 +examples/SmallCosmoVolume/SmallCosmoVolume_cooling/CloudyData_UVB=HM2012_shielded.h5 tests/testActivePair tests/testActivePair.sh @@ -131,6 +136,8 @@ tests/testMaths tests/testAtomic tests/testRandom tests/testRandomSpacing +tests/testRandomPoisson +tests/testRandomCone tests/testThreadpool tests/testParser tests/testFeedback @@ -145,9 +152,11 @@ tests/test125cells.sh tests/test125cellsPerturbed.sh tests/testParser.sh tests/testReading.sh +tests/testNeutrinoCosmology.sh tests/testSelectOutput.sh tests/unused_parser_output.yml tests/used_parser_output.yml +tests/output_list_params.yml tests/testAdiabaticIndex tests/testRiemannExact tests/testRiemannTRRS @@ -177,6 +186,7 @@ tests/testHashmap tests/testNeutrinoCosmology tests/testNeutrinoFermiDirac tests/testLog +tests/testTimeline tests/*.png tests/*.txt @@ -264,6 +274,9 @@ src/equation_of_state/planetary/*.txt *.out *.toc +## Figures +*.svg + ## Intermediate documents: *.dvi *-converted-to.* @@ -374,3 +387,6 @@ sympy-plots-for-*.tex/ # black formatting black_formatting_env + +# vscode +*.json diff --git a/AUTHORS b/AUTHORS index f3d984b3b3f5d040f5194251fb00ab126a6aeb93..5520d77633752e7ea863766984e516a67cef5a87 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,3 +23,6 @@ Roi Kugel kugel@strw.leidenuniv.nl Evgenii Chaikin chaikin@strw.leidenuniv.nl Sylvia Ploeckinger ploeckinger@lorentz.leidenuniv.nl Willem Elbers willem.h.elbers@durham.ac.uk +TK Chan chantsangkeung@gmail.com +Marcel van Daalen daalen@strw.leidenuniv.nl +Filip Husko filip.husko@durham.ac.uk \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000000000000000000000000000000000000..74de823ecba2dddb5bd394359ab450cf9b486959 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,46 @@ +cff-version: 1.2.0 +title: >- + SWIFT: SPH With Inter-dependent Fine-grained + Tasking +message: 'If you use this software, please cite it as below' +type: software +authors: + - given-names: Matthieu + family-names: Schaller + email: schaller@strw.leidenuniv.nl + affiliation: Leiden University + orcid: 'https://orcid.org/0000-0002-2395-4902' + - given-names: Gonnet + family-names: Pedro + - given-names: Peter + family-names: Draper + - given-names: Aidan + family-names: Chalk + - given-names: Bert + family-names: Vandenbroucke + - given-names: James + family-names: Willis + - given-names: Richard + family-names: Bower + - given-names: Josh + family-names: Borrow + - given-names: Loic + family-names: Hausammann + - given-names: Yves + family-names: Revaz + - given-names: Jacob + family-names: Kegerreis + - given-names: Mladen + family-names: Ivkovic + - given-names: Stuart + family-names: McAlpine + - given-names: Folkert + family-names: Nobels + - given-names: John + family-names: Helly + - given-names: Yannick + family-names: Bahé + - given-names: Willem + family-names: Elbers + - given-names: Filip + family-names: Husko diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1e9e535da6003cdc6f736caa6769f9896817a71..6db6cfbdaacd8f68cbd505b07a3982e8409fb5f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ The SWIFT source code is using a variation of the 'Google' formatting style. -The script 'format.sh' in the root directory applies the clang-format-10 +The script 'format.sh' in the root directory applies the clang-format-13 tool with our style choices to all the SWIFT C source file. Please apply the formatting script to the files before submitting a merge request. diff --git a/COPYING.LESSER b/COPYING.LESSER index 0927556b54440825c63fbf61b9e6179894a5547a..65c5ca88a67c30becee01c5a8816d964b03862f9 100644 --- a/COPYING.LESSER +++ b/COPYING.LESSER @@ -1,156 +1,164 @@ -### GNU LESSER GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Version 3, 29 June 2007 + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Copyright (C) 2007 Free Software Foundation, Inc. -<https://fsf.org/> -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. -This version of the GNU Lesser General Public License incorporates the -terms and conditions of version 3 of the GNU General Public License, -supplemented by the additional permissions listed below. + 0. Additional Definitions. -#### 0. Additional Definitions. + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. -As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the -GNU General Public License. + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. -"The Library" refers to a covered work governed by this License, other -than an Application or a Combined Work as defined below. - -An "Application" is any work that makes use of an interface provided + An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. -A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". -The "Minimal Corresponding Source" for a Combined Work means the + The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. -The "Corresponding Application Code" for a Combined Work means the + The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. -#### 1. Exception to Section 3 of the GNU GPL. + 1. Exception to Section 3 of the GNU GPL. -You may convey a covered work under sections 3 and 4 of this License + You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. -#### 2. Conveying Modified Versions. + 2. Conveying Modified Versions. -If you modify a copy of the Library, and, in your modifications, a + If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: -- a) under this License, provided that you make a good faith effort - to ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or -- b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. -#### 3. Object Code Incorporating Material from Library Header Files. + 3. Object Code Incorporating Material from Library Header Files. -The object code form of an Application may incorporate material from a -header file that is part of the Library. You may convey such object + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: -- a) Give prominent notice with each copy of the object code that - the Library is used in it and that the Library and its use are - covered by this License. -- b) Accompany the object code with a copy of the GNU GPL and this - license document. - -#### 4. Combined Works. - -You may convey a Combined Work under terms of your choice that, taken -together, effectively do not restrict modification of the portions of -the Library contained in the Combined Work and reverse engineering for -debugging such modifications, if you also do each of the following: - -- a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. -- b) Accompany the Combined Work with a copy of the GNU GPL and this - license document. -- c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. -- d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of - this License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with - the Library. A suitable mechanism is one that (a) uses at run - time a copy of the Library already present on the user's - computer system, and (b) will operate properly with a modified - version of the Library that is interface-compatible with the - Linked Version. -- e) Provide Installation Information, but only if you would - otherwise be required to provide such information under section 6 - of the GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the Application - with a modified version of the Linked Version. (If you use option - 4d0, the Installation Information must accompany the Minimal - Corresponding Source and Corresponding Application Code. If you - use option 4d1, you must provide the Installation Information in - the manner specified by section 6 of the GNU GPL for conveying - Corresponding Source.) - -#### 5. Combined Libraries. - -You may place library facilities that are a work based on the Library -side by side in a single library together with other library + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: -- a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities, conveyed under the terms of this License. -- b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. -#### 6. Revised Versions of the GNU Lesser General Public License. + 6. Revised Versions of the GNU Lesser General Public License. -The Free Software Foundation may publish revised and/or new versions + The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the Library -as you received it specifies that a certain numbered version of the -GNU Lesser General Public License "or any later version" applies to -it, you have the option of following the terms and conditions either -of that published version or of any later version published by the -Free Software Foundation. If the Library as you received it does not -specify a version number of the GNU Lesser General Public License, you -may choose any version of the GNU Lesser General Public License ever -published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the diff --git a/INSTALL.swift b/INSTALL.swift index 81768cf04864de25e81df5fbadba33d9348e58fb..6aba15db9d64d5a3ef0dec744554afdba3ba933a 100644 --- a/INSTALL.swift +++ b/INSTALL.swift @@ -37,6 +37,8 @@ SWIFT has been successfully built and tested with the following compilers: - GCC 4.8.x - Intel ICC 15.0.x - clang 3.4.x + - aocc 3.x + - icx (oneAPI) 2022.x More recent versions and slightly older ones should also be able to build the software. @@ -180,7 +182,7 @@ before you can build it. documentation. - python: - Examples and solution script use python and rely on the numpy + Examples and solution script use python 3 and rely on the numpy library version 1.8.2 or higher. @@ -189,7 +191,7 @@ before you can build it. ================== The SWIFT source code uses a variation of 'Google' style. The script -'format.sh' in the root directory applies the clang-format-10 tool with our +'format.sh' in the root directory applies the clang-format-13 tool with our style choices to all the SWIFT C source file. Please apply the formatting script to the files before submitting a merge request. diff --git a/Makefile.am b/Makefile.am index fd3106402afd02080f93c11de7890d7d5f153489..3ca9fd5e746dcbfa55d85e3632b3466d9ebc21ab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ # This file is part of SWIFT. # Copyright (c) 2012 pedro.gonnet@durham.ac.uk -# 2015 matthieu.schaller@durham.ac.uk +# 2015 schaller@strw.leidenuniv.nl # # 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 @@ -27,8 +27,79 @@ endif SUBDIRS += src argparse examples doc tests tools if HAVEEAGLECOOLING SUBDIRS += examples/Cooling/CoolingRates -endif +DIST_SUBDIRS = $(SUBDIRS) +else DIST_SUBDIRS = $(SUBDIRS) examples/Cooling/CoolingRates +endif + +# Common flags +MYFLAGS = + +# 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)/argparse $(HDF5_CPPFLAGS) \ + $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS) $(OPENMP_CFLAGS) \ + $(CHEALPIX_CFLAGS) + +AM_LDFLAGS = $(HDF5_LDFLAGS) + +# Extra libraries. +EXTRA_LIBS = $(GSL_LIBS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) \ + $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) \ + $(CHEALPIX_LIBS) + +# MPI libraries. +MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS) $(FFTW_MPI_LIBS) +MPI_FLAGS = -DWITH_MPI $(PARMETIS_INCS) $(METIS_INCS) $(FFTW_MPI_INCS) + +# Programs. +bin_PROGRAMS = swift + +# Also build the FOF tool? +if HAVESTANDALONEFOF +bin_PROGRAMS += fof +endif + +# Do we have the CSDS? +if HAVECSDS +LD_CSDS = csds/src/.libs/libcsds_writer.a +else +LD_CSDS = +endif + +# Build MPI versions as well? +if HAVEMPI +bin_PROGRAMS += swift_mpi +if HAVESTANDALONEFOF +bin_PROGRAMS += fof_mpi +endif +endif + +# engine_policy_setaffinity is available? +if HAVESETAFFINITY +ENGINE_POLICY_SETAFFINITY=| engine_policy_setaffinity +else +ENGINE_POLICY_SETAFFINITY= +endif + +# Sources for swift +swift_SOURCES = swift.c +swift_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" +swift_LDADD = src/libswiftsim.la argparse/libargparse.la $(VELOCIRAPTOR_LIBS) $(EXTRA_LIBS) $(LD_CSDS) + +# Sources for swift_mpi, do we need an affinity policy for MPI? +swift_mpi_SOURCES = swift.c +swift_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" +swift_mpi_LDADD = src/libswiftsim_mpi.la argparse/libargparse.la $(MPI_LIBS) $(VELOCIRAPTOR_MPI_LIBS) $(EXTRA_LIBS) $(LD_CSDS) + +# Sources for fof +fof_SOURCES = swift_fof.c +fof_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" +fof_LDADD = src/.libs/libswiftsim.a argparse/.libs/libargparse.a $(VELOCIRAPTOR_LIBS) $(EXTRA_LIBS) $(LD_CSDS) + +# Sources for fof_mpi, do we need an affinity policy for MPI? +fof_mpi_SOURCES = swift_fof.c +fof_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" +fof_mpi_LDADD = src/.libs/libswiftsim_mpi.a argparse/.libs/libargparse.a $(MPI_LIBS) $(VELOCIRAPTOR_MPI_LIBS) $(EXTRA_LIBS) $(LD_CSDS) # 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 3acdc3fa9eab31090098b0f0eb2d562a7c156eef..b3c32f001f123064b20cb524cb546a729a5bfce5 100644 --- a/README +++ b/README @@ -40,14 +40,16 @@ Parameters: -k, --sinks Run with sink particles. -u, --fof Run Friends-of-Friends algorithm to perform black hole seeding. + --lightcone Generate lightcone outputs. -x, --velociraptor Run with structure finding. --line-of-sight Run with line-of-sight outputs. --limiter Run with time-step limiter. --sync Run with time-step synchronization of particles hit by feedback events. - --csds Run with the Continuous Simulation Data Stream (CSDS). - -R, --radiation Run with radiative transfer. Work in - progress, currently has no effect. + --csds Run with the Continuous Simulation Data + Stream (CSDS). + -R, --radiation Run with radiative transfer. + --power Run with power spectrum outputs. Simulation meta-options: @@ -68,7 +70,8 @@ Parameters: Control options: -a, --pin Pin runners using processor affinity. - --interleave Interleave memory allocations over NUMA regions. + --interleave Interleave memory allocations across + NUMA regions. -d, --dry-run Dry run. Read the parameter file, allocates memory but does not read the particles from ICs. Exits before the start of time @@ -88,12 +91,12 @@ Parameters: read from the parameter file. Can be used more than once {sec:par:value}. -r, --restart Continue using restart files. - -t, --threads=<int> The number of task threads to use on each - MPI rank. Defaults to 1 if not specified. - --pool-threads=<int> The number of threads to use on each MPI - rank for the threadpool operations. - Defaults to the numbers of task threads - if not specified. + -t, --threads=<int> The number of task threads to use on each + MPI rank. Defaults to 1 if not specified. + --pool-threads=<int> The number of threads to use on each MPI + rank for the threadpool operations. + Defaults to the numbers of task threads + if not specified. -T, --timers=<int> Print timers every time-step. -v, --verbose=<int> Run in verbose mode, in MPI mode 2 outputs from all ranks. diff --git a/README.md b/README.md index 5564da74037216fae70b0e23cc75ad6a01940fc7..bdc64676714a6fd516907d828c9b026be569cc58 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +<a name="logo"/> +<div align="center"> +<a href="https://www.swiftsim.com/" target="_blank"> +<img src="https://swift.strw.leidenuniv.nl/SWIFT_banner.jpg" alt="SWIFT banner" width="1016" height="242"></img> +</a> +</div> + SWIFT: SPH WIth Fine-grained inter-dependent Tasking ==================================================== @@ -127,14 +134,16 @@ Parameters: -k, --sinks Run with sink particles. -u, --fof Run Friends-of-Friends algorithm to perform black hole seeding. + --lightcone Generate lightcone outputs. -x, --velociraptor Run with structure finding. --line-of-sight Run with line-of-sight outputs. --limiter Run with time-step limiter. --sync Run with time-step synchronization of particles hit by feedback events. - --csds Run with the Continuous Simulation Data Stream (CSDS). - -R, --radiation Run with radiative transfer. Work in - progress, currently has no effect. + --csds Run with the Continuous Simulation Data + Stream (CSDS). + -R, --radiation Run with radiative transfer. + --power Run with power spectrum outputs. Simulation meta-options: @@ -155,7 +164,8 @@ Parameters: Control options: -a, --pin Pin runners using processor affinity. - --interleave Interleave memory allocations over NUMA regions. + --interleave Interleave memory allocations across + NUMA regions. -d, --dry-run Dry run. Read the parameter file, allocates memory but does not read the particles from ICs. Exits before the start of time @@ -175,13 +185,12 @@ Parameters: read from the parameter file. Can be used more than once {sec:par:value}. -r, --restart Continue using restart files. - -t, --threads=<int> The number of task threads to use on each - MPI rank. Defaults to 1 if not specified. - --pool-threads=<int> The number of threads to use on each MPI - rank for the threadpool operations. - Defaults to the numbers of task threads + -t, --threads=<int> The number of task threads to use on each + MPI rank. Defaults to 1 if not specified. + --pool-threads=<int> The number of threads to use on each MPI + rank for the threadpool operations. + Defaults to the numbers of task threads if not specified. - rank. Defaults to 1 if not specified. -T, --timers=<int> Print timers every time-step. -v, --verbose=<int> Run in verbose mode, in MPI mode 2 outputs from all ranks. diff --git a/argparse/argparse.c b/argparse/argparse.c index 5760e42fda065e865d4247c6e5a20b177d1741ad..483a8a84605c043b1f4082599e0378a072d34f20 100644 --- a/argparse/argparse.c +++ b/argparse/argparse.c @@ -7,8 +7,9 @@ */ #include "argparse.h" -#include "config.h" +#include <config.h> +/* System includes */ #include <assert.h> #include <errno.h> #include <stdio.h> diff --git a/configure.ac b/configure.ac index f584b31ded3a49eff24f78e13c7751d07929f290..40bf209aad79e6b2352589c5f74f99635022a660 100644 --- a/configure.ac +++ b/configure.ac @@ -31,13 +31,16 @@ AC_CONFIG_MACRO_DIR([m4]) : ${CFLAGS=""} # Generate header file. -AM_CONFIG_HEADER(config.h) +AC_CONFIG_HEADERS([config.h]) + # Find and test the compiler. AX_CHECK_ENABLE_DEBUG +# Enable POSIX and platform extension preprocessor macros. +AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC AM_PROG_CC_C_O -AC_OPENMP +AX_OPENMP # If debug is selected then we also define SWIFT_DEVELOP_MODE to control # any developer code options. @@ -52,7 +55,7 @@ AC_DEFINE([_GLIBCXX_INCLUDE_NEXT_C_HEADERS],1,[Hack for min() and max() using g+ # Enable POSIX and platform extension preprocessor macros. AC_USE_SYSTEM_EXTENSIONS -# Check for compiler version and vendor. +# Check for C compiler version and vendor. AX_COMPILER_VENDOR AX_COMPILER_VERSION @@ -143,9 +146,11 @@ AC_ARG_ENABLE([mpi], [enable_mpi="$enableval"], [enable_mpi="yes"] ) +# Use extra flags set by AC_PROG_CC as part of $CC. Currently undocumented as ac_cv_prog_cc_stdc, +# so could change. good_mpi="yes" if test "$enable_mpi" = "yes"; then - AX_MPI([CC="$MPICC" AC_DEFINE(HAVE_MPI, 1, [Define if you have the MPI library.]) ]) + AX_MPI([CC="$MPICC $ac_cv_prog_cc_stdc" AC_DEFINE(HAVE_MPI, 1, [Define if you have the MPI library.]) ], [enable_mpi="no"]) MPI_LIBRARY="Unknown MPI" # Various MPI implementations require additional libraries when also using @@ -207,13 +212,16 @@ AM_CONDITIONAL([HAVEMPI],[test $enable_mpi = "yes"]) # Indicate that MPIRUN can be modified by an environment variable AC_ARG_VAR(MPIRUN, Path to the mpirun command if non-standard) -# Add libtool support (now that CC is defined). -LT_INIT +# Add libtool support (now that CC is defined). Disable shared libraries by default. +LT_INIT([disable-shared]) + +# Need C99 and inline support. Only for autoconfs to version 2.69. +m4_version_prereq([2.71], [], [AC_PROG_CC_C99]) -# Need C99 and inline support. -AC_PROG_CC_C99 +# Need inline support. AC_C_INLINE + # If debugging try to show inlined functions. if test "x$enable_debug" = "xyes"; then # Show inlined functions. @@ -268,7 +276,7 @@ AC_ARG_ENABLE([threadpool-debugging], ) if test "$enable_threadpool_debugging" = "yes"; then AC_DEFINE([SWIFT_DEBUG_THREADPOOL],1,[Enable threadpool debugging]) - LDFLAGS="$LDFLAGS -rdynamic" + LDFLAGS="$LDFLAGS -rdynamic -ldl" fi # Check if the general timers are switched on. @@ -363,7 +371,7 @@ AC_ARG_ENABLE([gravity-force-checks], [gravity_force_checks="$enableval"], [gravity_force_checks="no"] ) -if test "$gravity_force_checks" == "yes"; then +if test "$gravity_force_checks" = "yes"; then AC_MSG_ERROR(Need to specify the fraction of particles to check when using --enable-gravity-force-checks!) elif test "$gravity_force_checks" != "no"; then AC_DEFINE_UNQUOTED([SWIFT_GRAVITY_FORCE_CHECKS], [$enableval] ,[Enable gravity brute-force checks]) @@ -377,7 +385,7 @@ AC_ARG_ENABLE([hydro-density-checks], [hydro_density_checks="$enableval"], [hydro_density_checks="no"] ) -if test "$hydro_density_checks" == "yes"; then +if test "$hydro_density_checks" = "yes"; then AC_MSG_ERROR(Need to specify the fraction of particles to check when using --enable-hydro-density-checks!) elif test "$hydro_density_checks" != "no"; then AC_DEFINE_UNQUOTED([SWIFT_HYDRO_DENSITY_CHECKS], [$enableval] ,[Enable hydro density brute-force checks]) @@ -391,12 +399,26 @@ AC_ARG_ENABLE([stars-density-checks], [stars_density_checks="$enableval"], [stars_density_checks="no"] ) -if test "$stars_density_checks" == "yes"; then +if test "$stars_density_checks" = "yes"; then AC_MSG_ERROR(Need to specify the fraction of particles to check when using --enable-stars-density-checks!) elif test "$stars_density_checks" != "no"; then AC_DEFINE_UNQUOTED([SWIFT_STARS_DENSITY_CHECKS], [$enableval] ,[Enable stars density brute-force checks]) fi +# Check if ghost statistics are enabled +AC_ARG_ENABLE([ghost-statistics], + [AS_HELP_STRING([--enable-ghost-statistics], + [Gather statistics about the ghost iterations for hydro, stars and black holes in N bins @<:@N@:>@] + )], + [ghost_stats="$enableval"], + [ghost_stats="no"] +) +if test "$ghost_stats" == "yes"; then + AC_MSG_ERROR(Need to specify the number of bins when using --enable-ghost-statistics!) +elif test "$ghost_stats" != "no"; then + AC_DEFINE_UNQUOTED([SWIFT_GHOST_STATS], [$enableval] ,[Enable ghost statistics for hydro, stars and black holes]) +fi + # Check whether we want to switch on glass making AC_ARG_ENABLE([glass-making], [AS_HELP_STRING([--enable-glass-making], @@ -405,7 +427,7 @@ AC_ARG_ENABLE([glass-making], [gravity_glass_making="$enableval"], [gravity_glass_making="no"] ) -if test "$gravity_glass_making" == "yes"; then +if test "$gravity_glass_making" = "yes"; then AC_DEFINE([SWIFT_MAKE_GRAVITY_GLASS], 1, [Make the code run in a way to produce a glass file for gravity/cosmology]) fi @@ -417,7 +439,7 @@ AC_ARG_ENABLE([no-gravity-below-id], [no_gravity_below_id="$enableval"], [no_gravity_below_id="no"] ) -if test "$no_gravity_below_id" == "yes"; then +if test "$no_gravity_below_id" = "yes"; then AC_MSG_ERROR(Need to specify the ID below which particles get zero forces when using --enable-no-gravity-below-id!) 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]) @@ -431,7 +453,7 @@ AC_ARG_ENABLE([boundary-particles], [boundary_particles="$enableval"], [boundary_particles="no"] ) -if test "$boundary_particles" == "yes"; then +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]) @@ -446,7 +468,7 @@ AC_ARG_ENABLE([fixed-boundary-particles], [fixed_boundary_particles="$enableval"], [fixed_boundary_particles="no"] ) -if test "$fixed_boundary_particles" == "yes"; then +if test "$fixed_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 "$fixed_boundary_particles" != "no"; then AC_DEFINE_UNQUOTED([SWIFT_NO_GRAVITY_BELOW_ID], [$enableval] ,[Particles with smaller ID than this will have zero gravity forces]) @@ -682,7 +704,8 @@ AC_ARG_ENABLE([mpi-mesh-gravity], # Autoconf stuff. AC_PROG_INSTALL AC_PROG_MAKE_SET -AC_HEADER_STDC +AC_PROG_EGREP + # Check for the libraries we will need. AC_CHECK_LIB(m,sqrt,,AC_MSG_ERROR(something is wrong with the math library!)) @@ -746,7 +769,7 @@ have_pthread_barrier="no" AC_CHECK_LIB(pthread, pthread_barrier_init, have_pthread_barrier="yes", AC_MSG_WARN(POSIX implementation does not have barriers. SWIFT will use home-made ones.)) -if test "x$have_pthread_barrier" == "xyes"; then +if test "x$have_pthread_barrier" = "xyes"; then AC_DEFINE([HAVE_PTHREAD_BARRIERS], [1], [The posix library implements barriers]) fi @@ -778,7 +801,7 @@ if test "x$with_metis" != "xno"; then fi AC_CHECK_LIB([metis],[METIS_PartGraphKway], [have_metis="yes"], [have_metis="no"], $METIS_LIBS) - if test "$have_metis" == "yes"; then + if test "$have_metis" = "yes"; then AC_DEFINE([HAVE_METIS],1,[The METIS library is present.]) else AC_MSG_ERROR("Failed to find a METIS library") @@ -811,7 +834,7 @@ if test "x$with_parmetis" != "xno"; then fi AC_CHECK_LIB([parmetis],[ParMETIS_V3_RefineKway], [have_parmetis="yes"], [have_parmetis="no"], $PARMETIS_LIBS) - if test "$have_parmetis" == "no"; then + if test "$have_parmetis" = "no"; then # A build may use an external METIS library, check for that. @@ -827,7 +850,7 @@ if test "x$with_parmetis" != "xno"; then [have_parmetis="no"], [$METIS_LIBS $PARMETIS_LIBS]) fi - if test "$have_parmetis" == "yes"; then + if test "$have_parmetis" = "yes"; then AC_DEFINE([HAVE_PARMETIS],1,[The ParMETIS library is present.]) else AC_MSG_ERROR("Failed to find a ParMETIS library") @@ -849,10 +872,11 @@ AH_VERBATIM([__STDC_FORMAT_MACROS], # Check for FFTW. We test for this in the standard directories by default, # and only disable if using --with-fftw=no or --without-fftw. When a value # is given FFTW must be found. -# If FFTW is found, we check whether this is the threaded version. +# If FFTW is found, we check whether this is the threaded or openmp version. have_fftw="no" have_mpi_fftw="no" have_threaded_fftw="no" +have_openmp_fftw="no" AC_ARG_WITH([fftw], [AS_HELP_STRING([--with-fftw=PATH], [root directory where fftw is installed @<:@yes/no@:>@] @@ -905,7 +929,7 @@ if test "x$with_fftw" != "xno"; then # Verify that the library is threaded AC_CHECK_LIB([fftw3],[fftw_init_threads],[have_threaded_fftw="yes"], - [have_threaded_fftw="no"], $FFTW_THREADED_LIBS) + [have_threaded_fftw="no"], $FFTW_THREADED_LIBS) # If found, update things if test "x$have_threaded_fftw" = "xyes"; then @@ -913,7 +937,31 @@ if test "x$with_fftw" != "xno"; then FFTW_LIBS=$FFTW_THREADED_LIBS FFTW_INCS=$FFTW_THREADED_INCS + else + + # Same checks for OpenMP if preferred threaded failed. + if test "x$with_fftw" != "xyes" -a "x$with_fftw" != "xtest" -a "x$with_fftw" != "x"; then + FFTW_OPENMP_LIBS="-L$with_fftw/lib -lfftw3_omp -lfftw3" + FFTW_OPENMP_INCS="-I$with_fftw/include" + else + FFTW_OPENMP_LIBS="-lfftw3_omp -lfftw3" + FFTW_OPENMP_INCS="" + fi + + # Verify that the library works. Note requires AC_OPENMP called above. + AC_CHECK_LIB([fftw3],[fftw_init_threads],[have_openmp_fftw="yes"], + [have_openmp_fftw="no"], $FFTW_OPENMP_LIBS) + + # If found, update things + if test "x$have_openmp_fftw" = "xyes"; then + # Note OpenMP and pthreads use mostly the same calls, so define both. + AC_DEFINE([HAVE_THREADED_FFTW],1,[The threaded OpenMP FFTW library appears to be present.]) + AC_DEFINE([HAVE_OPENMP_FFTW],1,[The OpenMP FFTW library appears to be present.]) + FFTW_LIBS=$FFTW_OPENMP_LIBS + FFTW_INCS=$FFTW_OPENMP_INCS + fi fi + fi # If MPI mesh gravity is not disabled, check whether we have the MPI version of FFTW @@ -1308,7 +1356,7 @@ AC_SUBST([VELOCIRAPTOR_LIBS]) AM_CONDITIONAL([HAVEVELOCIRAPTOR],[test -n "$VELOCIRAPTOR_LIBS"]) # Now that we found VELOCIraptor, let's check how it was compiled. -if test "$have_velociraptor" == "yes"; then +if test "$have_velociraptor" = "yes"; then AC_CHECK_LIB( [velociraptor], [VR_NOMASS], @@ -1350,7 +1398,7 @@ AC_SUBST([VELOCIRAPTOR_MPI_LIBS]) AM_CONDITIONAL([HAVEVELOCIRAPTOR],[test -n "$VELOCIRAPTOR_MPI_LIBS"]) # Let's check how this one was compiled. -if test "$have_mpi_velociraptor" == "yes"; then +if test "$have_mpi_velociraptor" = "yes"; then AC_CHECK_LIB( [velociraptor], [VR_NOMASS], @@ -1361,10 +1409,10 @@ if test "$have_mpi_velociraptor" == "yes"; then fi # If we have one library, but not the other then use that for both. -if test "$have_mpi_velociraptor" == "yes" -a "$have_velociraptor" != "yes"; then +if test "$have_mpi_velociraptor" = "yes" -a "$have_velociraptor" != "yes"; then VELOCIRAPTOR_LIBS="$VELOCIRAPTOR_MPI_LIBS" AC_SUBST([VELOCIRAPTOR_LIBS]) -elif test "$have_velociraptor" == "yes" -a "$have_mpi_velociraptor" != "yes"; then +elif test "$have_velociraptor" = "yes" -a "$have_mpi_velociraptor" != "yes"; then VELOCIRAPTOR_MPI_LIBS="$VELOCIRAPTOR_LIBS" AC_SUBST([VELOCIRAPTOR_MPI_LIBS]) fi @@ -1397,6 +1445,38 @@ if test "$enable_velociraptor_orphans" = "yes"; then AC_DEFINE([HAVE_VELOCIRAPTOR_ORPHANS], 1, [Orphan particles should be written out]) fi +# Check if lightcone output is on. +AC_ARG_ENABLE([lightcone], + [AS_HELP_STRING([--enable-lightcone], + [Activate lightcone outputs.], + )], + [enable_lightcone="$enableval"], + [enable_lightcone="no"] +) +if test "$enable_lightcone" = "yes"; then + # Check for healpix for lightcone maps. May require cfitsio + # This sets CHEALPIX_LIBS and CHEALPIX_CFLAGS and #defines HAVE_CHEALPIX. + # It also adds a --with-cfitsio flag in case cfitsio is installed in a + # different location from healpix. + GV_FIND_LIBRARY([cfitsio], [CFITSIO], [cfitsio], [cfitsio], [ffclos]) + TMP_LIBS=${LIBS} + LIBS="${CFITSIO_LIBS} ${LIBS}" + GV_FIND_LIBRARY([chealpix], [CHEALPIX], [chealpix], [chealpix], [ang2vec]) + LIBS=${TMP_LIBS} + have_chealpix=${USE_CHEALPIX} + CHEALPIX_LIBS="${CHEALPIX_LIBS} ${CFITSIO_LIBS}" + AC_DEFINE([WITH_LIGHTCONE], 1, [Enable lightcone outputs]) + if test "$have_chealpix" != "yes"; then + AC_MSG_ERROR([Lightcone output requires the HEALPix C API. Please configure with --with-chealpix.]) + fi + # Also need to make sure we have GSL if we're making lightcones + if test "$have_gsl" != "yes"; then + AC_MSG_ERROR([Lightcone output requires GSL. Please configure with --with-gsl.]) + fi +else + have_chealpix="no" +fi + # Check for floating-point execeptions AC_CHECK_FUNC(feenableexcept, AC_DEFINE([HAVE_FE_ENABLE_EXCEPT],[1], [Defined if the floating-point exception can be enabled using non-standard GNU functions.])) @@ -1457,6 +1537,64 @@ if test "$ac_cv_func_pthread_setaffinity_np" = "yes" -a "x$with_numa" != "xno"; fi AC_SUBST([NUMA_LIBS]) + + +# Check for Sundials (required for the SPHM1RT library). +# There is a problems with the headers of this library +# as they do not pass the strict prototypes check when +# installed outside of the system directories. So we +# need to do this check in two phases. +have_sundials="no" +SUNDIALS_LIBS="" +SUNDIALS_INCS="" +AC_ARG_WITH([sundials], + [AS_HELP_STRING([--with-sundials=PATH], + [root directory where sundials is installed @<:@yes/no@:>@] + )], + [with_sundials="$withval"], + [with_sundials="no"] +) +if test "x$with_sundials" != "xno"; then + AC_PROG_FC + AC_FC_LIBRARY_LDFLAGS + if test "x$with_sundials" != "xyes" -a "x$with_sundials" != "x"; then + SUNDIALS_LIBS="-L$with_sundials/lib -lsundials_cvode -lsundials_nvecserial -lsundials_sunlinsoldense -lsundials_sunmatrixdense" + SUNDIALS_INCS="-I$with_sundials/include" + else + SUNDIALS_LIBS="-lsundials_cvode -lsundials_nvecserial -lsundials_sunlinsoldense -lsundials_sunmatrixdense" + SUNDIALS_INCS="" + fi + + AC_CHECK_LIB([sundials_cvode], [CVode], [have_sundials="yes"], + [have_sundials="no"], $SUNDIALS_LIBS) + + if test "$have_sundials" == "yes"; then + AC_DEFINE([HAVE_SUNDIALS],1,[The SUNDIALS library is present.]) + else + if test "x$with_sundials" != "xyes" -a "x$with_sundials" != "x"; then + # It might be that the libraries are in + # /lib64 rather than /lib + SUNDIALS_LIBS="-L$with_sundials/lib64 -lsundials_cvode -lsundials_nvecserial -lsundials_sunlinsoldense -lsundials_sunmatrixdense" + + # unset cached result of previous AC_CHECK_LIB + unset ac_cv_lib_sundials_cvode_CVode + + AC_CHECK_LIB([sundials_cvode], [CVode], [have_sundials="yes"], [have_sundials="no"], $SUNDIALS_LIBS) + + if test "$have_sundials" == "yes"; then + AC_DEFINE([HAVE_SUNDIALS],1,[The SUNDIALS library is present.]) + else + AC_MSG_ERROR("Failed to find a SUNDIALS library") + fi + + else + AC_MSG_ERROR("Failed to find a SUNDIALS library") + fi + fi +fi +AC_SUBST([SUNDIALS_LIBS]) + + # Check for Intel and PowerPC intrinsics header optionally used by vector.h. AC_CHECK_HEADERS([immintrin.h], [], [], [#ifdef HAVE_IMMINTRIN_H @@ -1470,8 +1608,33 @@ AC_CHECK_HEADERS([altivec.h], [], [], ]) # Check for timing functions needed by cycle.h. -AC_HEADER_TIME -AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h]) +AC_CHECK_HEADERS_ONCE([sys/time.h]) +if test $ac_cv_header_sys_time_h = yes; then + AC_DEFINE([TIME_WITH_SYS_TIME],[1],[Define to 1 if you can safely include both <sys/time.h> + and <time.h>. This macro is obsolete.]) +fi + +AC_CHECK_HEADERS([sys/time.h], [], [], +[#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif +]) +AC_CHECK_HEADERS([c_asm.h], [], [], +[#ifdef HAVE_C_ASM_H +# include <c_asm.h> +#endif +]) +AC_CHECK_HEADERS([intrinsics.h], [], [], +[#ifdef HAVE_INTRINSICS_H +# include <intrinsics.h> +#endif +]) +AC_CHECK_HEADERS([mach/mach_time.h], [], [], +[#ifdef HAVE_MACH_MACH_TIME_H +# include <mach/mach_time.h> +#endif +]) + AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t is defined in <sys/time.h>])],, [#if HAVE_SYS_TIME_H @@ -1489,12 +1652,12 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM( AC_MSG_RESULT($rtc_ok) # Special timers for the ARM v7 platforms (taken from FFTW-3 to match their cycle.h) -AC_ARG_ENABLE(armv7a-cntvct, [AC_HELP_STRING([--enable-armv7a-cntvct],[enable the cycle counter on Armv7a via the CNTVCT register])], have_armv7acntvct=$enableval) +AC_ARG_ENABLE(armv7a-cntvct, [AS_HELP_STRING([--enable-armv7a-cntvct],[enable the cycle counter on Armv7a via the CNTVCT register])], have_armv7acntvct=$enableval) if test "$have_armv7acntvct"x = "yes"x; then AC_DEFINE(HAVE_ARMV7A_CNTVCT,1,[Define if you have enabled the CNTVCT cycle counter on ARMv7a]) fi -AC_ARG_ENABLE(armv7a-pmccntr, [AC_HELP_STRING([--enable-armv7a-pmccntr],[enable the cycle counter on Armv7a via the PMCCNTR register])], have_armv7apmccntr=$enableval) +AC_ARG_ENABLE(armv7a-pmccntr, [AS_HELP_STRING([--enable-armv7a-pmccntr],[enable the cycle counter on Armv7a via the PMCCNTR register])], have_armv7apmccntr=$enableval) if test "$have_armv7apmccntr"x = "yes"x; then AC_DEFINE(HAVE_ARMV7A_PMCCNTR,1,[Define if you have enabled the PMCCNTR cycle counter on ARMv7a]) fi @@ -1519,6 +1682,8 @@ if test "$ax_cv_c_compiler_vendor" = "clang"; then AC_CHECK_LIB([m],[__sincosf], [AC_DEFINE([HAVE___SINCOSF],1,[The __sincosf function is present.])]) fi +# Check for glibc extension backtrace(). +AC_CHECK_FUNCS([backtrace backtrace_symbols]) # Add warning flags by default, if these can be used. Option =error adds # -Werror to GCC, clang and Intel. Note do this last as compiler tests may @@ -1590,6 +1755,26 @@ if test "$have_numa" != "no"; then fi AC_SUBST([NUMA_INCS]) + +# Second part of the Sundials library checks. +# We now decide if we need to use -isystem to +# get around the strict-prototypes problem. Assumes +# isystem is available when strict-prototypes is. +if test "x$with_sundials" != "xno"; then + if test "x$with_sundials" != "xyes" -a "x$with_sundials" != "x"; then + case "$CFLAGS" in + *strict-prototypes*) + SUNDIALS_INCS="-isystem$with_sundials/include" + ;; + *) + SUNDIALS_INCS="-I$with_sundials/include" + ;; + esac + fi +fi +AC_SUBST([SUNDIALS_INCS]) + + # Various package configuration options. # Master subgrid options @@ -1598,7 +1783,7 @@ AC_SUBST([NUMA_INCS]) # As an example for this, see the call to AC_ARG_WITH for cooling. AC_ARG_WITH([subgrid], [AS_HELP_STRING([--with-subgrid=<subgrid>], - [Master switch for subgrid methods. Inexperienced user should start here. Options are: @<:@none, GEAR, QLA, QLA-EAGLE, EAGLE, EAGLE-XL default: none@:>@] + [Master switch for subgrid methods. Inexperienced user should start here. Options are: @<:@none, GEAR, QLA, QLA-EAGLE, EAGLE, EAGLE-XL, SPIN_JET_EAGLE default: none@:>@] )], [with_subgrid="$withval"], [with_subgrid=none] @@ -1614,6 +1799,7 @@ with_subgrid_stars=none with_subgrid_star_formation=none with_subgrid_feedback=none with_subgrid_sink=none +with_subgrid_extra_io=none case "$with_subgrid" in yes) @@ -1624,12 +1810,13 @@ case "$with_subgrid" in GEAR) with_subgrid_cooling=grackle_0 with_subgrid_chemistry=GEAR_10 - with_subgrid_pressure_floor=GEAR + with_subgrid_pressure_floor=none with_subgrid_stars=GEAR with_subgrid_star_formation=GEAR with_subgrid_feedback=GEAR with_subgrid_black_holes=none with_subgrid_sink=none + with_subgrid_extra_io=none enable_fof=no ;; QLA) @@ -1642,6 +1829,7 @@ case "$with_subgrid" in with_subgrid_feedback=none with_subgrid_black_holes=none with_subgrid_sink=none + with_subgrid_extra_io=none enable_fof=no ;; QLA-EAGLE) @@ -1666,6 +1854,7 @@ case "$with_subgrid" in with_subgrid_feedback=EAGLE with_subgrid_black_holes=EAGLE with_subgrid_sink=none + with_subgrid_extra_io=none enable_fof=yes ;; EAGLE-XL) @@ -1678,6 +1867,20 @@ case "$with_subgrid" in with_subgrid_feedback=EAGLE with_subgrid_black_holes=EAGLE with_subgrid_sink=none + with_subgrid_extra_io=none + enable_fof=yes + ;; + SPIN_JET_EAGLE) + with_subgrid_cooling=EAGLE + with_subgrid_chemistry=EAGLE + with_subgrid_tracers=EAGLE + with_subgrid_entropy_floor=EAGLE + with_subgrid_stars=EAGLE + with_subgrid_star_formation=EAGLE + with_subgrid_feedback=EAGLE + with_subgrid_black_holes=SPIN_JET + with_subgrid_sink=none + with_subgrid_extra_io=none enable_fof=yes ;; *) @@ -1806,12 +2009,39 @@ case "$with_hydro" in AC_DEFINE([ANARCHY_PU_SPH], [1], [ANARCHY (PU) SPH]) ;; - *) AC_MSG_ERROR([Unknown hydrodynamics scheme: $with_hydro]) ;; esac +# SPMHD scheme. +AC_ARG_WITH([spmhd], + [AS_HELP_STRING([--with-spmhd=<scheme>], + [Magneto Hydro Dynamics SPH scheme to use @<:@none, direct-induction, direct-induction-fede, vector-potential default:none@:>@] + )], + [with_spmhd="$withval"], + [with_spmhd="none"] +) + +case "$with_spmhd" in + none) + AC_DEFINE([NONE_MHD], [1], [No mhd]) + ;; + *) + AC_MSG_ERROR([Unknown magneto-hydrodynamics scheme: $with_spmhd]) + ;; +esac + +if test "$with_hydro" = "gizmo-mfm" -a "$with_spmhd" != "none"; then + AC_MSG_ERROR([Cannot use an SPMHD scheme alongside a gizmo hydro solver!"]) +fi +if test "$with_hydro" = "gizmo-mfv" -a "$with_spmhd" != "none"; then + AC_MSG_ERROR([Cannot use an SPMHD scheme alongside a gizmo hydro solver!"]) +fi +if test "$with_hydro" = "shadowfax" -a "$with_spmhd" != "none"; then + AC_MSG_ERROR([Cannot use an SPMHD scheme alongside a gizmo hydro solver!"]) +fi + # Check if debugging interactions stars is switched on. AC_ARG_ENABLE([debug-interactions-stars], [AS_HELP_STRING([--enable-debug-interactions-stars], @@ -1822,7 +2052,7 @@ AC_ARG_ENABLE([debug-interactions-stars], ) if test "$enable_debug_interactions_stars" != "no"; then AC_DEFINE([DEBUG_INTERACTIONS_STARS],1,[Enable interaction debugging for stars]) - if test "$enable_debug_interactions_stars" == "yes"; then + if test "$enable_debug_interactions_stars" = "yes"; then AC_DEFINE([MAX_NUM_OF_NEIGHBOURS_STARS],256,[The maximum number of particle neighbours to be logged for stars]) [enable_debug_interactions_stars="yes (Logging up to 256 neighbours)"] else @@ -1842,7 +2072,7 @@ AC_ARG_ENABLE([debug-interactions], if test "$enable_debug_interactions" != "no"; then if test "$with_hydro" = "gadget2"; then AC_DEFINE([DEBUG_INTERACTIONS_SPH],1,[Enable interaction debugging]) - if test "$enable_debug_interactions" == "yes"; then + if test "$enable_debug_interactions" = "yes"; then AC_DEFINE([MAX_NUM_OF_NEIGHBOURS],256,[The maximum number of particle neighbours to be logged]) [enable_debug_interactions="yes (Logging up to 256 neighbours)"] else @@ -1864,7 +2094,7 @@ AC_ARG_ENABLE([debug-interactions-sinks], ) if test "$enable_debug_interactions_sinks" != "no"; then AC_DEFINE([DEBUG_INTERACTIONS_SINKS],1,[Enable interaction debugging for sinks]) - if test "$enable_debug_interactions_sinks" == "yes"; then + if test "$enable_debug_interactions_sinks" = "yes"; then AC_DEFINE([MAX_NUM_OF_NEIGHBOURS_SINKS],256,[The maximum number of particle neighbours to be logged for sinks]) [enable_debug_interactions_sinks="yes (Logging up to 256 neighbours)"] else @@ -2003,7 +2233,7 @@ case "$with_riemann" in ;; esac -if test "x$need_riemann_solver" == "xyes" -a "$with_riemann" == "none"; then +if test "x$need_riemann_solver" = "xyes" -a "$with_riemann" = "none"; then AC_MSG_ERROR([Hydro scheme $with_hydro requires selection of a Riemann solver!]) fi @@ -2025,25 +2255,32 @@ if test "$with_subgrid" != "none"; then fi fi +with_chemistry_name="none" case "$with_chemistry" in none) AC_DEFINE([CHEMISTRY_NONE], [1], [No chemistry function]) ;; GEAR_DIFFUSION_*) AC_DEFINE([CHEMISTRY_GEAR_DIFFUSION], [1], [Chemistry taken from the GEAR model including the metal diffusion]) - number_element=${with_chemistry:15} + number_element=${with_chemistry##*_} AC_DEFINE_UNQUOTED([GEAR_CHEMISTRY_ELEMENT_COUNT], [$number_element], [Number of element to follow]) + with_chemistry_name="GEAR (with $number_element elements and diffusion)" + with_chemistry="GEAR_DIFFUSION" ;; GEAR_*) AC_DEFINE([CHEMISTRY_GEAR], [1], [Chemistry taken from the GEAR model]) - number_element=${with_chemistry:5} + number_element=${with_chemistry#*_} AC_DEFINE_UNQUOTED([GEAR_CHEMISTRY_ELEMENT_COUNT], [$number_element], [Number of element to follow]) + with_chemistry_name="GEAR (with $number_element elements)" + with_chemistry="GEAR" ;; QLA) AC_DEFINE([CHEMISTRY_QLA], [1], [Chemistry taken from the Quick-Lyman-alpha model]) + with_chemistry_name="QLA" ;; EAGLE) AC_DEFINE([CHEMISTRY_EAGLE], [1], [Chemistry taken from the EAGLE model]) + with_chemistry_name="EAGLE (9 elements + smoothing)" ;; *) AC_MSG_ERROR([Unknown chemistry function: $with_chemistry]) @@ -2051,9 +2288,9 @@ case "$with_chemistry" in esac if test "$with_chemistry" != "none"; then - if test "$enable_hand_vec" == "yes"; then - if test "$enable_vec" == "yes"; then - if test "$with_hydro" == "gadget2"; then + if test "$enable_hand_vec" = "yes"; then + if test "$enable_vec" = "yes"; then + if test "$with_hydro" = "gadget2"; then AC_MSG_ERROR([Cannot run with hand vectorisation and chemistry yet. Please use --disable-hand-vec]) fi fi @@ -2078,51 +2315,61 @@ if test "$with_subgrid" != "none"; then fi fi +with_cooling_name="none" case "$with_cooling" in none) AC_DEFINE([COOLING_NONE], [1], [No cooling function]) ;; const-du) AC_DEFINE([COOLING_CONST_DU], [1], [Const du/dt cooling function]) + with_cooling_name=$with_cooling ;; const-lambda) AC_DEFINE([COOLING_CONST_LAMBDA], [1], [Const Lambda cooling function]) + with_cooling_name=$with_cooling ;; grackle_*) + + if test "$have_grackle" != "yes"; then + AC_MSG_ERROR([Grackle cooling: You need the grackle library for Grackle cooling. (--with-grackle=PATH)]) + fi + AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) - primordial_chemistry=${with_cooling:8} + primordial_chemistry=${with_cooling#*_} AC_DEFINE_UNQUOTED([COOLING_GRACKLE_MODE], [$primordial_chemistry], [Grackle chemistry network]) + with_cooling_name="Grackle $primordial_chemistry" + with_cooling="grackle" ;; QLA) AC_DEFINE([COOLING_QLA], [1], [Cooling following the Quick-Lyman-alpha model]) - with_cooling="QLA (Ploeckinger+20 tables) with constant primordial Z" + with_cooling_name="QLA (Ploeckinger+20 tables) with constant primordial Z" ;; QLA-EAGLE) AC_DEFINE([COOLING_QLA_EAGLE], [1], [Cooling following the Quick-Lyman-alpha model]) - with_cooling="QLA (Wiersma+09 tables) with constant primordial Z" + with_cooling_name="QLA (Wiersma+09 tables) with constant primordial Z" ;; EAGLE) AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model (Wiersma+09 tables)]) - with_cooling="EAGLE (Wiersma+09 tables)" + with_cooling_name="EAGLE (Wiersma+09 tables)" ;; Wiersma) AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model (Wiersma+09 tables)]) - with_cooling="EAGLE (Wiersma+09 tables)" + with_cooling_name="EAGLE (Wiersma+09 tables)" ;; COLIBRE) AC_DEFINE([COOLING_COLIBRE], [1], [Cooling following the COLIBRE model (Ploeckinger+20 tables)]) - with_cooling="COLIBRE (Ploeckinger+20 tables)" + with_cooling_name="COLIBRE (Ploeckinger+20 tables)" ;; Ploeckinger) AC_DEFINE([COOLING_COLIBRE], [1], [Cooling following the COLIBRE model (Ploeckinger+20 tables)]) - with_cooling="COLIBRE (Ploeckinger+20 tables)" + with_cooling_name="COLIBRE (Ploeckinger+20 tables)" ;; *) AC_MSG_ERROR([Unknown cooling function: $with_cooling]) ;; esac -if test "${with_cooling:0:5}" = "EAGLE" || test "${with_cooling:0:7}" = "COLIBRE"; then +if test "$with_cooling" = "EAGLE" || test "$with_cooling" = "COLIBRE"; then if test "$with_chemistry" = "none"; then AC_MSG_ERROR([Cannot run with EAGLE or COLIBRE cooling without chemistry. Please pick a chemistry model]) fi @@ -2157,6 +2404,35 @@ case "$with_tracers" in ;; esac +# Extra fields added to snapshots at i/o time +AC_ARG_WITH([extra_io], + [AS_HELP_STRING([--with-extra-io=<function>], + [Extra i/o field: @<:@none, EAGLE default: none@:>@] + )], + [with_extra_io="$withval"], + [with_extra_io="none"] +) + +if test "$with_subgrid" != "none"; then + if test "$with_extra_io" != "none"; then + AC_MSG_ERROR([Cannot provide with-subgrid and with-extra_io together]) + else + with_extra_io="$with_subgrid_extra_io" + fi +fi + +case "$with_extra_io" in + none) + AC_DEFINE([EXTRA_IO_NONE], [1], [No extra_io function]) + ;; + EAGLE) + AC_DEFINE([EXTRA_IO_EAGLE], [1], [Extra i/o fields taken from the EAGLE model]) + ;; + *) + AC_MSG_ERROR([Unknown extra-io choice: $with_extra_io]) + ;; +esac + # Stellar model. AC_ARG_WITH([stars], [AS_HELP_STRING([--with-stars=<model>], @@ -2209,21 +2485,23 @@ if test "$with_subgrid" != "none"; then fi fi +with_feedback_name="none" case "$with_feedback" in EAGLE-kinetic) AC_DEFINE([FEEDBACK_EAGLE_KINETIC], [1], [EAGLE kinetic stellar feedback and evolution model]) - with_feedback="EAGLE kinetic stellar feedback and evolution model" + with_feedback_name="EAGLE kinetic stellar feedback and evolution model" ;; EAGLE-thermal) AC_DEFINE([FEEDBACK_EAGLE_THERMAL], [1], [EAGLE thermal stellar feedback and evolution model]) - with_feedback="EAGLE thermal stellar feedback and evolution model" + with_feedback_name="EAGLE thermal stellar feedback and evolution model" ;; EAGLE) AC_DEFINE([FEEDBACK_EAGLE_THERMAL], [1], [EAGLE thermal stellar feedback and evolution model]) - with_feedback="EAGLE thermal stellar feedback and evolution model" + with_feedback_name="EAGLE thermal stellar feedback and evolution model" ;; GEAR) AC_DEFINE([FEEDBACK_GEAR], [1], [GEAR stellar feedback and evolution model]) + with_feedback_name="GEAR" ;; none) AC_DEFINE([FEEDBACK_NONE], [1], [No feedback]) @@ -2257,7 +2535,7 @@ AC_DEFINE_UNQUOTED([FEEDBACK_NR_RAYS_AGN], [$with_number_of_AGN_rays], [Number o # Black hole model. AC_ARG_WITH([black-holes], [AS_HELP_STRING([--with-black-holes=<model>], - [Black holes model to use @<:@none, EAGLE default: none@:>@] + [Black holes model to use @<:@none, EAGLE, SPIN_JET default: none@:>@] )], [with_black_holes="$withval"], [with_black_holes="none"] @@ -2278,6 +2556,10 @@ case "$with_black_holes" in EAGLE) AC_DEFINE([BLACK_HOLES_EAGLE], [1], [EAGLE black hole model]) ;; + SPIN_JET) + AC_DEFINE([BLACK_HOLES_SPIN_JET], [1], [Spin and jet black hole model]) + with_black_holes="SPIN_JETS (Husko+22)" + ;; *) AC_MSG_ERROR([Unknown black-hole model: $with_black_holes]) ;; @@ -2315,7 +2597,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, nfw_mn, hernquist, disc-patch, sine-wave, constant, default: none@:>@] + [external potential @<:@none, point-mass, point-mass-softened, isothermal, nfw, nfw-mn, hernquist, hernquist-sdmh05, disc-patch, sine-wave, constant, default: none@:>@] )], [with_potential="$withval"], [with_potential="none"] @@ -2333,10 +2615,13 @@ case "$with_potential" in hernquist) AC_DEFINE([EXTERNAL_POTENTIAL_HERNQUIST], [1], [Hernquist external potential]) ;; + hernquist-sdmh05) + AC_DEFINE([EXTERNAL_POTENTIAL_HERNQUIST_SDMH05], [1], [Hernquist external potential following Springel, Di Matteo & Hernquist 2005]) + ;; nfw) AC_DEFINE([EXTERNAL_POTENTIAL_NFW], [1], [Navarro-Frenk-White external potential]) ;; - nfw_mn) + nfw-mn) AC_DEFINE([EXTERNAL_POTENTIAL_NFW_MN], [1], [Navarro-Frenk-White + Miyamoto-Nagai disk external potential]) ;; disc-patch) @@ -2345,9 +2630,6 @@ case "$with_potential" in sine-wave) AC_DEFINE([EXTERNAL_POTENTIAL_SINE_WAVE], [1], [Sine wave external potential in 1D]) ;; - point-mass-ring) - AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS_RING], [1], [Point mass potential for Keplerian Ring (Hopkins 2015).]) - ;; point-mass-softened) AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS_SOFT], [1], [Softened point-mass potential with form 1/(r^2 + softening^2).]) ;; @@ -2477,9 +2759,8 @@ AC_DEFINE_UNQUOTED([SELF_GRAVITY_MULTIPOLE_ORDER], [$with_multipole_order], [Mul # Radiative transfer scheme AC_ARG_WITH([rt], [AS_HELP_STRING([--with-rt=<scheme>], - [Radiative transfer scheme to use @<:@none, GEAR_*, debug default: none@:>@. - This is still work in progress, currently no scheme works. - For GEAR, you need to provide the number of photon groups (e.g. GEAR_4)] + [Radiative transfer scheme to use @<:@none, GEAR_*, SPHM1RT_*, debug default: none@:>@. + For GEAR and SPHM1RT, the number of photon groups (e.g. GEAR_4) needs to be provided.] )], [with_rt="$withval"], [with_rt="none"] @@ -2516,10 +2797,10 @@ case "$with_rt" in ;; GEAR_*) AC_DEFINE([RT_GEAR], [1], [GEAR M1 closure scheme]) - number_group=${with_rt:5} + number_group=${with_rt#*_} AC_DEFINE_UNQUOTED([RT_NGROUPS], [$number_group], [Number of photon groups to follow]) - if test "$number_group" == 0; then + if test "$number_group" = "0"; then AC_MSG_ERROR([GEAR-RT: Cannot work with zero photon groups]) fi if ! test $number_group -eq $number_group; then @@ -2549,8 +2830,27 @@ case "$with_rt" in AC_DEFINE([RT_DEBUG], [1], [debugging scheme]) AC_DEFINE([SWIFT_RT_DEBUG_CHECKS], [1], [additional debugging checks for RT]) ;; - SPHM1RT) + SPHM1RT_*) AC_DEFINE([RT_SPHM1RT], [1], [SPHM1RT radiative transfer scheme (Chan+21: 2102.08404)]) + number_group=${with_rt#*_} + AC_DEFINE_UNQUOTED([RT_NGROUPS], [$number_group], [Number of photon groups to follow]) + + if test "$number_group" -eq "0"; then + AC_MSG_ERROR([SPHM1RT: Cannot work with zero photon groups]) + fi + if test $number_group -lt 4; then + AC_MSG_ERROR([SPHM1RT: Cannot work with < four photon groups for now]) + fi + if ! test $number_group -eq $number_group; then + # abuse -eq to check whether $number_group is an integer. -eq + # only works with those. + AC_MSG_ERROR([SPHM1RT: Cannot work with non-integer photon groups]) + fi + AC_MSG_CHECKING([for Sundials libraries]) + AC_MSG_RESULT($have_sundials) + if test "$have_sundials" != "yes"; then + AC_MSG_ERROR([The Sundials library is not present. Sundials is required for the SPHM1RT module.]) + fi ;; *) AC_MSG_ERROR([Unknown radiative transfer scheme: $with_rt]) @@ -2558,26 +2858,6 @@ case "$with_rt" in esac -AC_ARG_ENABLE([rt-hydro-controlled-injection], - [AS_HELP_STRING([--enable-rt-hydro-controlled-injection], - [enable the radiative transfer injection scheme where \ - active hydro particles gather radiation from neighbouring \ - stars. Experimental feature, use with caution. \ - @<:@no/yes@:>@ Default: no] - )], - [with_hydro_controlled_injection="${enableval}"], - [with_hydro_controlled_injection="no"] -) - -rt_extra_msg="" # extra message for ./configure printout -if test "$with_hydro_controlled_injection" = "yes"; then - rt_extra_msg=", hydro controlled injection" - AC_DEFINE([RT_HYDRO_CONTROLLED_INJECTION], 1, - [Enable hydro controlled radiative transfer injection scheme]) -fi - - - # Check for git, needed for revision stamps. AC_PATH_PROG([GIT_CMD], [git]) AC_SUBST([GIT_CMD]) @@ -2587,22 +2867,25 @@ DX_DOXYGEN_FEATURE(OFF) DX_INIT_DOXYGEN(SWIFT, doc/Doxyfile, doc/) AM_CONDITIONAL([HAVE_DOXYGEN], [test "$ac_cv_path_ac_pt_DX_DOXYGEN" != ""]) +# Check if using EAGLE extra I/O +AM_CONDITIONAL([HAVEEAGLEEXTRAIO], [test "${with_extra_io}" = "EAGLE"]) + # Check if using QLA cooling -AM_CONDITIONAL([HAVEQLACOOLING], [test "${with_cooling:0:6}" = "QLA (P"]) -AM_CONDITIONAL([HAVEQLAEAGLECOOLING], [test "${with_cooling:0:6}" = "QLA (W"]) +AM_CONDITIONAL([HAVEQLACOOLING], [test "$with_cooling" = "QLA"]) +AM_CONDITIONAL([HAVEQLAEAGLECOOLING], [test "$with_cooling" = "QLA-EAGLE"]) # Check if using EAGLE cooling -AM_CONDITIONAL([HAVEEAGLECOOLING], [test "${with_cooling:0:5}" = "EAGLE"]) +AM_CONDITIONAL([HAVEEAGLECOOLING], [test "$with_cooling" = "EAGLE"]) # Check if using COLIBRE cooling -AM_CONDITIONAL([HAVECOLIBRECOOLING], [test "${with_cooling:0:7}" = "COLIBRE"]) +AM_CONDITIONAL([HAVECOLIBRECOOLING], [test "$with_cooling" = "COLIBRE"]) # Check if using EAGLE feedback -AM_CONDITIONAL([HAVEEAGLETHERMALFEEDBACK], [test "${with_feedback:0:13}" = "EAGLE thermal"]) -AM_CONDITIONAL([HAVEEAGLEKINETICFEEDBACK], [test "${with_feedback:0:13}" = "EAGLE kinetic"]) +AM_CONDITIONAL([HAVEEAGLETHERMALFEEDBACK], [test "${with_feedback%-thermal}" = "EAGLE"]) +AM_CONDITIONAL([HAVEEAGLEKINETICFEEDBACK], [test "$with_feedback" = "EAGLE-kinetic"]) # check if using grackle cooling -AM_CONDITIONAL([HAVEGRACKLECOOLING], [test "${with_cooling:0:7}" = "grackle"]) +AM_CONDITIONAL([HAVEGRACKLECOOLING], [test "$with_cooling" = "grackle"]) # check if using gear feedback AM_CONDITIONAL([HAVEGEARFEEDBACK], [test "$with_feedback" = "GEAR"]) @@ -2617,7 +2900,7 @@ AM_CONDITIONAL([HAVE_GADGET2], [test "$with_hydro" = "gadget2"]) AM_CONDITIONAL([HAVE_CHEMISTRY_NONE], [test "$with_chemistry" = "none"]) # check if using GEAR chemistry -AM_CONDITIONAL([HAVE_CHEMISTRY_GEAR], [test "${with_chemistry:0:5}" = "GEAR_"]) +AM_CONDITIONAL([HAVE_CHEMISTRY_GEAR], [test "$with_chemistry" = "GEAR" || test "$with_chemistry" = "GEAR_DIFFUSION"]) # check if using default stars AM_CONDITIONAL([HAVE_STARS_BASIC], [test "$with_stars" = "basic"]) @@ -2634,6 +2917,15 @@ AM_CONDITIONAL([HAVE_STAR_FORMATION_GEAR], [test "$with_star_formation" = "GEAR" # check if using multi softening gravity AM_CONDITIONAL([HAVE_GRAVITY_MULTISOFTENING], [test "$with_gravity" = "with-multi-softening"]) +# Check if using SPHM1RT radiative transfer +AM_CONDITIONAL([HAVESPHM1RTRT], [test "${with_rt:0:7}" = "SPHM1RT"]) + +# Check if using GEAR-RT radiative transfer +AM_CONDITIONAL([HAVEGEARRT], [test "${with_rt:0:4}" = "GEAR"]) + + + + # 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]) @@ -2651,6 +2943,8 @@ AC_CONFIG_FILES([tests/testInteractions.sh], [chmod +x tests/testInteractions.sh AC_CONFIG_FILES([tests/testParser.sh], [chmod +x tests/testParser.sh]) AC_CONFIG_FILES([tests/testSelectOutput.sh], [chmod +x tests/testSelectOutput.sh]) AC_CONFIG_FILES([tests/testFormat.sh], [chmod +x tests/testFormat.sh]) +AC_CONFIG_FILES([tests/testNeutrinoCosmology.sh], [chmod +x tests/testNeutrinoCosmology.sh]) +AC_CONFIG_FILES([tests/output_list_params.yml]) # Save the compilation options AC_DEFINE_UNQUOTED([SWIFT_CONFIG_FLAGS],["$swift_config_flags"],[Flags passed to configure]) @@ -2682,17 +2976,20 @@ AC_MSG_RESULT([ - parallel : $have_parallel_hdf5 METIS/ParMETIS : $have_metis / $have_parmetis FFTW3 enabled : $have_fftw - - threaded : $have_threaded_fftw + - threaded/openmp : $have_threaded_fftw / $have_openmp_fftw - MPI : $have_mpi_fftw - ARM : $have_arm_fftw GSL enabled : $have_gsl + HEALPix C enabled : $have_chealpix libNUMA enabled : $have_numa GRACKLE enabled : $have_grackle + Sundials enabled : $have_sundials Special allocators : $have_special_allocator CPU profiler : $have_profiler Pthread barriers : $have_pthread_barrier VELOCIraptor enabled : $have_velociraptor FoF activated: : $enable_fof + Lightcones enabled : $enable_lightcone Hydro scheme : $with_hydro Dimensionality : $with_dimension @@ -2700,6 +2997,7 @@ AC_MSG_RESULT([ Equation of state : $with_eos Adiabatic index : $with_gamma Riemann solver : $with_riemann + SPMHD scheme : $with_spmhd Gravity scheme : $with_gravity Multipole order : $with_multipole_order @@ -2710,16 +3008,17 @@ AC_MSG_RESULT([ Pressure floor : $with_pressure_floor Entropy floor : $with_entropy_floor - Cooling function : $with_cooling - Chemistry : $with_chemistry + Cooling function : $with_cooling_name + Chemistry : $with_chemistry_name Tracers : $with_tracers Stellar model : $with_stars Star formation model : $with_star_formation - Star feedback model : $with_feedback + Star feedback model : $with_feedback_name Sink particle model : $with_sink Black holes model : $with_black_holes - Radiative transfer : $with_rt$rt_extra_msg - + Radiative transfer : $with_rt + Extra i/o : $with_extra_io + Atomic operations in tasks : $enable_atomics_within_tasks Individual timers : $enable_timers Task debugging : $enable_task_debugging @@ -2734,6 +3033,7 @@ AC_MSG_RESULT([ Boundary particles : $boundary_particles Fixed boundary particles : $fixed_boundary_particles Planetary fixed entropy : $planetary_fixed_entropy + Ghost statistics : $ghost_stats Continuous Sim. Data Stream : $with_csds diff --git a/doc/Makefile.am b/doc/Makefile.am index dc88489ca4ac4ef2fe856d910420fee6a7e87c8a..ce540d4ddb652cbbd8b1ed7f889b8f519060cbc8 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,6 @@ # This file is part of SWIFT. # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), -# Matthieu Schaller (matthieu.schaller@durham.ac.uk). +# 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 General Public License as published by diff --git a/doc/RTD/Makefile b/doc/RTD/Makefile index 22ff80ae32739df7d7ca97d8d2a6c6c8159b8248..3148a13d592b7aec337bd2db4e05b45c06713be0 100644 --- a/doc/RTD/Makefile +++ b/doc/RTD/Makefile @@ -4,7 +4,7 @@ # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build -SPHINXPROJ = SWIFTSPHWIthFine-grainedinter-dependentTasking +SPHINXPROJ = SWIFT-documentation SOURCEDIR = source BUILDDIR = build @@ -17,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/RTD/README.md b/doc/RTD/README.md index 3394ce7b8b97a71a7f14fb235e49e2efb51b9f9f..1c2aff85989d50e4e6fc97b9faf887473a3d8a6b 100644 --- a/doc/RTD/README.md +++ b/doc/RTD/README.md @@ -1,15 +1,18 @@ SWIFT Documentation =================== -This is the main documentation for SWIFT that can be found on ReadTheDocs. +This is the main documentation for SWIFT that can be found on ReadTheDocs +or at `swiftsim.com/docs`. You will need the `sphinx` and `sphinx-autobuild` python packages (pip install them!) to build the documentation to html, as well as the `sphinx_rtd_theme` package which is used as the theme. -To build the documentation, `make html` and then it is available in -`build/html`. +To build the documentation (from this directory), `make html` and the output +files will be created in `build/html/`. -Please consider adding documentation when you add code! +To build the latex manual (from this directory), `make latexpdf` and the output +files will be `build/latex/SWIFT-user-manual.pdf`. +Please consider adding documentation when you add code! diff --git a/doc/RTD/source/AnalysisTools/index.rst b/doc/RTD/source/AnalysisTools/index.rst index ac254403b43a5dcddf82b769e4daf6aee599d580..15922608cf93fd0deb04769272b8d23307d5b74b 100644 --- a/doc/RTD/source/AnalysisTools/index.rst +++ b/doc/RTD/source/AnalysisTools/index.rst @@ -2,6 +2,7 @@ Loic Hausammann 20th March 2019 Peter W. Draper 28th March 2019 Mladen Ivkovic 18th March 2021 + Bert Vandenbroucke 31st February 2022 .. _Analysis_Tools: @@ -20,6 +21,20 @@ If you wish to have more dependency graphs, you can use the parameter ``Schedule While the initial graph is showing all the tasks/dependencies, the next ones are only showing the active tasks/dependencies. +Task dependencies for a single cell +----------------------------------- + +There is an option to additionally write the dependency graphs of the task dependencies for a single cell. +You can select which cell to write using the ``Scheduler:dependency_graph_cell: cellID`` parameter, where ``cellID`` is the cell ID of type long long. +This feature will create an individual file for each step specified by the ``Scheduler:dependency_graph_frequency`` and, differently from the full task graph, will create an individual file for each MPI rank that has this cell. + +Using this feature has several requirements: + +- You need to compile SWIFT including either ``--enable-debugging-checks`` or ``--enable-cell-graph``. Otherwise, cells won't have IDs. +- There is a limit on how many cell IDs SWIFT can handle while enforcing them to be reproduceably unique. That limit is up to 32 top level cells in any dimension, and up to 16 levels of depth. If any of these thresholds are exceeded, the cells will still have unique cell IDs, but the actual IDs will most likely vary between any two runs. + +To plot the task dependencies, you can use the same script as before: ``tools/plot_task_dependencies.py``. The dependency graph now may have some tasks with a pink-ish background colour: These tasks represent dependencies that are unlocked by some other task which is executed for the requested cell, but the cell itself doesn't have an (active) task of that type itself in that given step. + Task levels ----------------- @@ -41,30 +56,28 @@ It defines how many steps are done in between two task level output dumps. 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 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 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 - - npm install http-server -g - 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``. +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 at the frequency given by the parameter +``--cell-dumps=n``. The script ``tools/make_cell_hierarchy.py`` can be used to collate the files produced by +different MPI ranks and convert them into a web page that shows an interactive cell hierarchy. The script +takes the names of all the files you want to include as input, and requires an output prefix that will be used +to name the output files ``prefix.csv`` and ``prefix.html``. If the prefix path contains directories that do +not exist, the script will create those. + +The output files cannot be directly viewed from a browser, because they require a server connection to +interactively load the data. You can either copy them over to a server, or set up a local server yourself. The +latter can also be done directly by the script by using the optional parameter ``--serve``. + +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 appear to be idle. For really large simulations, the browser will give up and +will probably display an error message. + +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 file, 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 -------------------- @@ -179,12 +192,17 @@ that as well, some offer the ability to process all ranks, and others to select individual ranks. It is also possible to process a complete run of task data from all the -available steps using the ``process_plot_tasks`` and -``process_plot_tasks_MPI`` scripts, as appropriate, these have two arguments, -the number of concurrent processes to use and a time limit. Concurrent -processes are useful to speed up the analysis of a lot of task data and a -consistent time limit so that comparisons across plots is easy. The results -can be viewed in a single HTML document. +available steps using the ``process_plot_tasks.py`` and +``process_plot_tasks_MPI.py`` scripts, as appropriate. +These scripts have one required argument: a time limit to use on the horizontal +time axis. When set to 0, this limit is determined by the data for each step, +making it very hard to compare relative sizes of different steps. +The optional ``--files`` arguments allows more control over which steps are +included in the analysis. Large numbers of tasks can be analysed more +efficiently by using multiple processes (the optional ``--nproc`` argument), +and if sufficient memory is available, the parallel analysis can be optimised +by using the size of the task data files to schedule parallel processes more +effectively (the ``--weights`` argument). Live internal inspection using the dumper thread @@ -217,3 +235,50 @@ The ``.dump<.rank>`` files once seen are deleted, so dumping can be done more than once. For a non-MPI run the file is simply called ``.dump``, note for MPI you need to create one file per rank, so ``.dump.0``, ``.dump.1`` and so on. + +Neighbour search statistics +--------------------------- + +One of the core algorithms in SWIFT is an iterative neighbour search +whereby we try to find an appropriate radius around a particle's +position so that the weighted sum over neighbouring particles within +that radius is equal to some target value. The most obvious example of +this iterative neighbour search is the SPH density loop, but various +sub-grid models employ a very similar iterative neighbour search. The +computational cost of this iterative search is significantly affected by +the number of iterations that is required, and it can therefore be +useful to analyse the progression of the iterative scheme in detail. + +When configured with ``--enable-ghost-statistics=X``, SWIFT will be +compiled with additional diagnostics that statistically track the number +of iterations required to find a converged answer. Here, ``X`` is a +fixed number of bins to use to collect the required statistics +(``ghost`` refers to the fact that the iterations take place inside the +ghost tasks). In practice, this means that every cell in the SWIFT tree +will be equipped with an additional ``struct`` containing three sets of +``X`` bins (one set for each iterative neighbour loop: hydro, stellar +feedback, AGN feedback). For each bin ``i``, we store the number of +particles that required updating during iteration ``i``, the number of +particles that could not find a single neighbouring particle, the +minimum and maximum smoothing length of all particles that required +updating, and the sum of all their search radii and all their search +radii squared. This allows us to calculate the upper and lower limits, +as well as the mean and standard deviation on the search radius for each +iteration and for each cell. Note that there could be more iterations +required than the number of bins ``X``; in this case the additional +iterations will be accumulated in the final bin. At the end of each time +step, a text file is produced (one per MPI rank) that contains the +information for all cells that had any relevant activity. This text file +is named ``ghost_stats_ssss_rrrr.txt``, where ``ssss`` is the step +counter for that time step and ``rrrr`` is the MPI rank. + +The script ``tools/plot_ghost_stats.py`` takes one or multiple +``ghost_stats.txt`` files and computes global statistics for all the +cells in those files. The script also takes the name of an output file +where it will save those statistics as a set of plots, and an optional +label that will be displayed as the title of the plots. Note that there +are no restrictions on the number of input files or how they relate; +different files could represent different MPI ranks, but also different +time steps or even different simulations (which would make little +sense). It is up to the user to make sure that the input is actually +relevant. diff --git a/doc/RTD/source/CitingSWIFT/index.rst b/doc/RTD/source/CitingSWIFT/index.rst index 197941351f7182a5db3e05c1d9e8d20c92257835..bb5f9d1bbd23cc30e94d2e9b5545fe996ee8f38d 100644 --- a/doc/RTD/source/CitingSWIFT/index.rst +++ b/doc/RTD/source/CitingSWIFT/index.rst @@ -6,11 +6,6 @@ Disclaimer, Citing SWIFT & Giving Credit First of all, thank you for using SWIFT for your projects! -.. figure:: SWIFT_logo.png - :width: 300px - :align: center - :alt: SWIFT logo - Licensing ~~~~~~~~~ @@ -62,7 +57,7 @@ code. This corresponds to the following bibtex citation block: .. code-block:: bibtex @MISC{2018ascl.soft05020S, - author = {{Schaller}, M. et al.}, + author = {{Schaller}, M. and others}, title = "{SWIFT: SPH With Inter-dependent Fine-grained Tasking}", keywords = {Software }, howpublished = {Astrophysics Source Code Library}, diff --git a/doc/RTD/source/CommandLineOptions/index.rst b/doc/RTD/source/CommandLineOptions/index.rst index 60210ec5a91189479263d1a19c49ecd4d7466306..7900a90967f842769616db010208865c348e48bb 100644 --- a/doc/RTD/source/CommandLineOptions/index.rst +++ b/doc/RTD/source/CommandLineOptions/index.rst @@ -37,14 +37,16 @@ can be found by typing ``./swift -h``: -k, --sinks Run with sink particles. -u, --fof Run Friends-of-Friends algorithm to perform black hole seeding. + --lightcone Generate lightcone outputs. -x, --velociraptor Run with structure finding. --line-of-sight Run with line-of-sight outputs. --limiter Run with time-step limiter. --sync Run with time-step synchronization of particles hit by feedback events. - --csds Run with the Continuous Simulation Data Stream (CSDS). - -R, --radiation Run with radiative transfer. Work in - progress, currently has no effect. + --csds Run with the Continuous Simulation Data + Stream (CSDS). + -R, --radiation Run with radiative transfer. + --power Run with power spectrum outputs. Simulation meta-options: @@ -65,7 +67,8 @@ can be found by typing ``./swift -h``: Control options: -a, --pin Pin runners using processor affinity. - --interleave Interleave memory allocations over NUMA regions. + --interleave Interleave memory allocations across + NUMA regions. -d, --dry-run Dry run. Read the parameter file, allocates memory but does not read the particles from ICs. Exits before the start of time @@ -85,11 +88,11 @@ can be found by typing ``./swift -h``: read from the parameter file. Can be used more than once {sec:par:value}. -r, --restart Continue using restart files. - -t, --threads=<int> The number of task threads to use on each - MPI rank. Defaults to 1 if not specified. - --pool-threads=<int> The number of threads to use on each MPI - rank for the threadpool operations. - Defaults to the numbers of task threads + -t, --threads=<int> The number of task threads to use on each + MPI rank. Defaults to 1 if not specified. + --pool-threads=<int> The number of threads to use on each MPI + rank for the threadpool operations. + Defaults to the numbers of task threads if not specified. -T, --timers=<int> Print timers every time-step. -v, --verbose=<int> Run in verbose mode, in MPI mode 2 outputs diff --git a/doc/RTD/source/ExternalPotentials/index.rst b/doc/RTD/source/ExternalPotentials/index.rst index 329d33987209e986a5c3ca0675a285b73fc2e692..0454da53ab5a591452bb68bc1860859ecd83dc26 100644 --- a/doc/RTD/source/ExternalPotentials/index.rst +++ b/doc/RTD/source/ExternalPotentials/index.rst @@ -1,6 +1,7 @@ .. External potentials in SWIFT Folkert Nobels, 25th October 2018 Alejandro Benitez-Llambay, October 2019 + Matthieu Schaller, December 2021 External Potentials =================== @@ -12,44 +13,336 @@ potential in SWIFT. Implemented External Potentials ------------------------------- -Currently there are several potentials implemented in SWIFT. On this page we -give a short overview of the potentials that are implemented in the code: - -1. No potential (none) -2. Point mass potential (point-mass): classical point mass, can be placed at - a position with a mass. -3. Plummer potential (point-mass-softened): in the code a softened point mass - corresponds to a Plummer potential, can be placed at a position with a mass. -4. Isothermal potential (isothermal): An isothermal potential which corresponds - to a density profile which is :math:`\propto r^{-2}` and a potential which is - logarithmic. This potential has as free parameters the rotation velocity - and the position. -5. Hernquist potential (hernquist): A potential that is given by the Hernquist - potential: +Currently there are several potentials implemented in SWIFT. On this page we +give a short overview of the potentials that are implemented in the code. They +are all switched on at configuration time via the argument +``--with-ext-potential=XYZ`` and get their own independant section in the +parameter file. The name of the potential to pass to the configuration script is +given in the parenthesis of the titles below; for instance +``--with-ext-potential=herquist``. + +1. No potential (``none``) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is the default setup. No external potential is used. +There are no parameters associated with this model. + +2. Constant acceleration (``constant``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This "potential" just applies a constant acceleration vector at every position +:math:`\vec{x}` in the simulation volume. This is very common in idealised test +cases like the Rayleigh-Taylor instability or engineering applications relying +on Earth's constant gravity field. + + * :math:`\phi(\vec{x}) = -\vec{g} \cdot \vec{x}` + * :math:`\vec{a}(\vec{x}) = \vec{g}` + +The only parameter of this model is the vector :math:`\vec{g}` given in `cgs` +units: + +.. code:: YAML + + ConstantPotential: + g_cgs: [0., 0., -9,81] # Earth acceleration along z-axis (cgs units) + + +3. Point mass potential (``point-mass``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A simple single point mass that can be placed at any position :math:`\vec{p}`. +The position can be specified either absolutely or with respect to the centre of +the simulation volume. + + * :math:`\phi(\vec{x}) = -\displaystyle\frac{G_{\rm N}m}{|r|}` + * :math:`\vec{a}(\vec{x}) = -\displaystyle\frac{G_{\rm N}m}{|r|^3}\vec{r}`, + +where :math:`\vec{r} = \vec{x} - \vec{p}`. + +The code also imposes an extra limit of the size of the particles' +time-step. The time-step has to be shorter than :math:`\Delta t_{pot} = C +|\vec{a}(\vec{x})| / |\dot{\vec{a}}(\vec{x})|`. This criterion is designed to +make sure the changes in accelerations remain small. The jerk +(:math:`\dot{\vec{a}}\equiv\frac{d}{dt}\vec{a}`) is calculated from the +positions of the particles and only includes the contribution of the external +potential itself. The other criteria (CFL, self-gravity, ...) are applied on top +of this criterion. The dimensionless constant :math:`C` defaults to `FLT_MAX` if +left unspecified, effectively making SWIFT run without this time-step criterion. + +The parameters of the model are: + +.. code:: YAML + + PointMassPotential: + position: [3., 4., 5.] # Location of the point mass (internal units) + useabspos: 1 # Use absolute positions (0 for relative to centre) + mass: 5.972e24 # Mass of the point (internal units) + timestep_mult: 0.1 # (Optional) The dimensionless constant C in the time-step condition + +4. Plummer potential (``point-mass-softened``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A single point mass with a fixed softening length using a Plummer shape that can +be placed at any position :math:`\vec{p}`. The position can be specified either +absolutely or with respect to the centre of the simulation volume. + + * :math:`\phi(\vec{x}) = -\displaystyle\frac{G_{\rm N}m}{\sqrt{|r|^2 + \epsilon^2}}` + * :math:`\vec{a}(\vec{x}) = -\displaystyle\frac{G_{\rm N}m}{(|r|^2 + \epsilon^2)^{3/2}}\vec{r}`, + +where :math:`\vec{r} = \vec{x} - \vec{p}`. + +The code also imposes an extra limit of the size of the particles' +time-step. The time-step has to be shorter than :math:`\Delta t_{pot} = C +|\vec{a}(\vec{x})| / |\dot{\vec{a}}(\vec{x})|`. This criterion is designed to +make sure the changes in accelerations remain small. The jerk +(:math:`\dot{\vec{a}}\equiv\frac{d}{dt}\vec{a}`) is calculated from the +positions of the particles and only includes the contribution of the external +potential itself. The other criteria (CFL, self-gravity, ...) are applied on top +of this criterion. The dimensionless constant :math:`C` defaults to `FLT_MAX` if +left unspecified, effectively making SWIFT run without this time-step criterion. + +The parameters of the model are: + +.. code:: YAML + + PointMassPotential: + position: [3., 4., 5.] # Location of the point mass (internal units) + useabspos: 1 # Use absolute positions (0 for relative to centre) + mass: 5.972e24 # Mass of the point (internal units) + softening: 0.01 # Softening length (internal units) + timestep_mult: 0.1 # (Optional) The dimensionless constant C in the time-step condition + +5. Isothermal potential (``isothermal``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +An isothermal potential which corresponds to a density profile which is +:math:`\propto r^{-2}` and a potential which is logarithmic. This potential is +entirely set by its centre :math:`\vec{p}` and the (radially-constant) rotation +velocity of the potential :math:`v_{\rm rot}`. The centre of the potential is softened. + + * :math:`\phi(\vec{x}) = -\displaystyle\frac{1}{4\pi G_{\rm N}}\log(\sqrt{|r|^2 + \epsilon^2})` + * :math:`\vec{a}(\vec{x}) = -\displaystyle\frac{v_{\rm rot}^2} {|r|^2 + \epsilon^2}`, + +where :math:`\vec{r} = \vec{x} - \vec{p}`. + +The code also imposes an extra limit of the size of the particles' +time-step. The time-step has to be shorter than :math:`\Delta t_{pot} = C +|\vec{a}(\vec{x})| / |\dot{\vec{a}}(\vec{x})|`. This criterion is designed to +make sure the changes in accelerations remain small. The jerk +(:math:`\dot{\vec{a}}\equiv\frac{d}{dt}\vec{a}`) is calculated from the +positions of the particles and only includes the contribution of the external +potential itself. The other criteria (CFL, self-gravity, ...) are applied on top +of this criterion. The dimensionless constant :math:`C` defaults to `FLT_MAX` if +left unspecified, effectively making SWIFT run without this time-step criterion. + +The parameters of the model are: + +.. code:: YAML + + IsothermalPotential: + position: [3., 4., 5.] # Location of the centre of the profile (internal units) + useabspos: 1 # Use absolute positions (0 for relative to centre) + vrot: 200 # Rotation velocity of the profile (internal units) + softening: 0.01 # Softening length (internal units) + timestep_mult: 0.1 # (Optional) The dimensionless constant C in the time-step condition + + +6. Hernquist potential (``hernquist``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We can set up a potential as given by Hernquist (1990): + +:math:`\Phi(r) = - \frac{G_{\rm N}M}{r+a},` + +where :math:`M` is the total Hernquist mass and :math: `a` is the Hernquist- +equivalent scale radius of the potential. The potential can be set at any +position in the box. It adds an additional time step constraint that limits +the time step to a maximum of a specified fraction of the circular orbital +time at the current radius of the particle. The other criteria +(CFL, self-gravity, ...) are applied on top of this criterion. For example, a +fraction of 0.01 means that an orbital period would be resolved by 100 time steps. + +In the most basic version, the Hernquist potential can be run by providing +only the Hernquist mass, scale length, softening length and fraction of the +orbital time for the time stepping. In this case the model parameters may +look something like: + +.. code:: YAML + + HernquistPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.,0.,0.] # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units) + mass: 1e12 # Mass of the Hernquist potential + scalelength: 2.0 # scale length a (internal units) + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, determines the fraction of the orbital time we use to do the time integration; fraction of 0.01 means we resolve an orbit with 100 timesteps + epsilon: 0.2 # Softening size (internal units) + +Besides the basic version, it is also possible to run the Hernquist +potential for idealised disk galaxies that follow the approach of +`Springel, Di Matteo & Hernquist (2005) +<https://ui.adsabs.harvard.edu/abs/2005MNRAS.361..776S/abstract>`_. This +potential, however, uses a corrected value of the formulation that improves +the match with the NFW profile (below) with the same M200 (Nobels+ in prep). +Contrary to the above (idealizeddisk: 0 setup), the idealised disk setup runs +by specifying one out of :math:`M_{200}`, :math:`V_{200}`, or :math:`R_{200}`, +plus concentration and reduced Hubble constant. + +In this case, we don't provide the 'mass' and 'scalelength' parameters, but +'M200' (or 'V200', or 'R200') and 'concentration' :math:`c`, as well as reduced Hubble +constant :math:`h` to define the potential. The parameters of the model may look something like: + +.. code:: YAML + + HernquistPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.,0.,0.] # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units) + idealizeddisk: 1 # Run with an idealized galaxy disk + M200: 137.0 # M200 of the galaxy disk + h: 0.704 # reduced Hubble constant (value does not specify the used units!) + concentration: 9.0 # concentration of the Halo + diskfraction: 0.040 # Disk mass fraction + bulgefraction: 0.0 # Bulge mass fraction + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, determines the fraction of the orbital time we use to do the time integration; fraction of 0.01 means we resolve an orbit with 100 timesteps + epsilon: 0.2 # Softening size (internal units) + +The user should specify one out of 'M200', 'V200', or 'R200' to define +the potential. The reduced Hubble constant is then used to determine the +other two. Then, the scale radius is calculated as :math:`R_s = R_{200}/c`, +where :math:`c` is the concentration, and the Hernquist-equivalent scale-length +is calculated as: + +:math:`a = \frac{b+\sqrt{b}}{1-b} R_{200}`, + +where :math:`b = \frac{2}{c^2}(\ln(1+c) - \frac{c}{1+c})`. + +Two examples using the Hernquist potential can be found in ``swiftsim/examples/GravityTests/``. +The ``Hernquist_radialinfall`` example puts 5 particles with zero velocity in a Hernquist +potential, resulting in radial orbits. The ``Hernquist_circularorbit``example puts three +particles on a circular orbit in a Hernquist potential, one at the inner region, one at the +maximal circular velocity, and one in the outer region. To run these examples, SWIFT must +be configured with the flag ``--with-ext-potential=hernquist``, or ``hernquist-sdmh05`` +(see below). + +7. Hernquist SDMH05 potential (``hernquist-sdmh05``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is the same potential as Hernquist with the difference being the +way that the idealised disk part is calculated. This potential uses +exactly the same approach as `Springel, Di Matteo & Hernquist (2005) +<https://ui.adsabs.harvard.edu/abs/2005MNRAS.361..776S/abstract>`_, +which means that ICs generated with the original `MakeNewDisk` code can +be used with this potential. Contrary to the updated Hernquist +potential (above), it is not possible to have an identically matched +NFW potential in this case. + +The parameters needed for this potential are the same set of variables as +above, i.e. 'mass' and 'scalelength' when we don't use the idealised +disk, and 'concentration' plus one out of 'M200', 'V200', or 'R200' if +we do. As one of the three is provided, the reduced Hubble constant is +used to calculate the other two. Then, the scale radius is calculated +using the NFW definition, :math:`R_s = R_{200}/c`, and the Hernquist- +equivalent scale length is given by + +:math:`a = R_s \sqrt{2(\ln(1+c) - \frac{c}{1+c})}.` + +Runs that provide e.g. M200 and c (using idealised disk) are thus equivalent +to providing mass and scale length if calculated by the above equation (without +idealized disk). + - :math:`\Phi(r) = - \frac{GM}{r+a}.` +8. Navarro-Frenk-White potential (``nfw``): +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The most used potential to describe dark matter halos, the +potential is given by: + +:math:`\Phi(r) = - \frac{4\pi G_{\rm N} \rho_0 R_s^3}{r} \ln \left( 1+ +\frac{r}{R_s} \right).` + +This potential has as free parameters the concentration of the DM halo, the +virial mass (:math:`M_{200}`) and the critical density. The potential add +an additional time step constrain that limits the time step to a maximum of +a specified fraction of the orbital time. + +This potential in the centre and the enclosed mass at R200 are identical to +the Hernquist potential discussed above. - The free parameters of Hernquist potential are mass, scale length, - and softening. The potential can be set at any position in the box. -6. NFW potential (nfw): The most used potential to describe dark matter halos, the - potential is given by: +The parameters of the model are: - :math:`\Phi(r) = - \frac{4\pi G \rho_0 R_s^3}{r} \ln \left( 1+ - \frac{r}{R_s} \right).` +.. code:: YAML - This potential has as free parameters the concentration of the DM halo, the - virial mass (:math:`M_{200}`) and the critical density. -7. NFW poential + Miyamoto-Nagai potential (nfw_mn): This includes and NFW potential (identical to nfw) + NFWPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.,0.,0.] # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units) + M200: 137.0 # M200 of the galaxy disk + h: 0.704 # reduced Hubble constant (value does not specify the used units!) + concentration: 9.0 # concentration of the Halo + diskfraction: 0.040 # Disk mass fraction + bulgefraction: 0.0 # Bulge mass fraction + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration + epsilon: 0.2 # Softening size (internal units) + + +9. NFW poential + Miyamoto-Nagai potential (``nfw-mn``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + This includes and NFW potential (identical to nfw) plus an axisymmetric Miyamoto-Nagai potential. The Miyamoto-Nagai potential is given by: - :math:`\Phi(R,z) = - \frac{G M_{d}}{\sqrt{R^2 + \left ( R_d + \sqrt{z^2 + Z_d^2} \right )^2}}`, + :math:`\Phi(R,z) = - \frac{G_{\rm N} M_{d}}{\sqrt{R^2 + \left ( R_d + \sqrt{z^2 + Z_d^2} \right )^2}}`, where :math:`R^2 = x^2 + y^2` is the projected radius and :math:`M_d`, :math:`R_d`, :math:`Z_d` are the mass, scalelength and scaleheight of the disk (in internal units), respectively. + +The parameters of the model are: + +.. code:: YAML + + NFW_MNPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.,0.,0.] # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units) + M_200: 137.0 # M200 of the halo in internal units + critical_density: 123.4 # Critical density of the universe in internal units + concentration: 9.0 # concentration of the NFW halo + Mdisk: 3.0 # Mass of the disk in internal units + Rdisk: 3.0 # Disk scale-length in internal units + Zdisk: 3.0 # Disk scale-height in internal units + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration + -8. Sine wave (sine-wave) -9. Point mass ring (point-mass-ring) -10. Disc Patch (disc-patch) +10. Sine wave (``sine-wave``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This "potential" is designed for specific idealised tests. It is a basic (not +really physical!) sinusoid wave along the x-axis with a unit wavelength and a +tunable amplitude and growth time. + + * :math:`\phi(\vec{x}) = A \cos\left(2 \pi x_{0}\right) / 2\pi` + * :math:`a_0(\vec{x}) = A \sin\left(2 \pi x_{0}\right)`, + +where the 0 subscript indicates the x-component. The y- and z-components are zero. + +The amplitude :math:`A` can be growing linearly to its maximum over a fixed +time-scale. + +Optionally, a constant maximail time-step size can be used with this potential. + +The parameters of the model are: + +.. code:: YAML + + SineWavePotential: + amplitude: 2.5 # The amplitude of the wave (internal units) + growth_time: 1.2 # Time for the amplitude to grow to its final value (internal units) + timestep_limit: 5.0 # (Optional) The max time-step of the particles (internal units) + +11. Disc Patch (``disc-patch``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A potential corresponding to a vertical slice through a galactic disk. This +follows the definitions of `Creasey, Theuns & Bower (2013) +<https://adsabs.harvard.edu/abs/2013MNRAS.429.1922C>`_ equations (16) and (17). +The potential is implemented along the x-axis. How to implement your own potential diff --git a/doc/RTD/source/FriendsOfFriends/parameter_description.rst b/doc/RTD/source/FriendsOfFriends/parameter_description.rst index 934d0c57c31f245f16e11f7e67ff1e6b76d74414..d9820a74d2bea768904b5fdeb35aacd01e04efd1 100644 --- a/doc/RTD/source/FriendsOfFriends/parameter_description.rst +++ b/doc/RTD/source/FriendsOfFriends/parameter_description.rst @@ -104,3 +104,4 @@ A full FOF section of the YAML parameter file looks like: absolute_linking_length: -1. # (Optional) Absolute linking length (in internal units). group_id_default: 2147483647 # (Optional) Sets the group ID of particles in groups below the minimum size. group_id_offset: 1 # (Optional) Sets the offset of group ID labelling. Defaults to 1 if unspecified. + seed_black_holes_enabled: 0 # Do not seed black holes when running FOF diff --git a/doc/RTD/source/GettingStarted/compiling_code.rst b/doc/RTD/source/GettingStarted/compiling_code.rst index 297a2393db00498efc1590b1a02d6c3d95b8fc23..9e207faec21b2f1c4860efaa0a84fdf52ad3357c 100644 --- a/doc/RTD/source/GettingStarted/compiling_code.rst +++ b/doc/RTD/source/GettingStarted/compiling_code.rst @@ -21,7 +21,7 @@ We suggest: We have specific issues with the following compilers: -+ GCC 7.3.0 with the -mskylake-avx512 flag. ++ GCC 7.3.0 with the ``-mskylake-avx512`` flag. Dependencies ------------ @@ -34,7 +34,7 @@ HDF5 Version 1.8.x or higher is required. Input and output files are stored as HDF5 and are compatible with the existing GADGET-2 specification. Please consider using a build of parallel-HDF5, as SWIFT can leverage this when writing and -reading snapshots. We recommend using HDF5 > 1.10.x as this is `vastly superior` +reading snapshots. We recommend using HDF5 > 1.10.x as this is *vastly superior* in parallel. MPI @@ -63,10 +63,6 @@ ParMETIS or METIS ~~~~~~~~~~~~~~~~~ One is required for domain decomposition and load balancing. -libNUMA -~~~~~~~ -libNUMA is used to pin threads (but see INSTALL.swift). - GSL ~~~ The GSL is required for cosmological integration. @@ -75,7 +71,11 @@ The GSL is required for cosmological integration. Optional Dependencies --------------------- -There are also the following _optional_ dependencies. +There are also the following *optional* dependencies. + +libNUMA +~~~~~~~ +libNUMA is used to pin threads (but see INSTALL.swift). TCmalloc/Jemalloc ~~~~~~~~~~~~~~~~~ @@ -87,18 +87,28 @@ You can build documentation for SWIFT with DOXYGEN. Python ~~~~~~ -To run the examples, you will need python and some of the standard scientific libraries (numpy, matplotlib). Some examples use Python 2 scripts, but the more recent ones use Python 3 (this is specified in individual READMEs). +To run the examples, you will need python 3 and some of the standard scientific libraries (numpy, matplotlib). +Some examples make use of the `swiftsimio <https://swiftsimio.readthedocs.io/en/latest/>`_ library. GRACKLE ~~~~~~~ GRACKLE cooling is implemented in SWIFT. If you wish to take advantage of it, you will need it installed. +HEALPix C library +~~~~~~~~~~~~~~~~~~~ + +This is required for making light cone HEALPix maps. Note that by default HEALPix builds a static library which cannot be used to build the SWIFT shared library. Either HEALPix must be built as a shared library or -fPIC must be added to the C compiler flags when HEALPix is being configured. + +CFITSIO +~~~~~~~ + +This may be required as a dependency of HEALPix. + Initial Setup ------------- -We use autotools for setup. To get a basic running version of the code -(the binary is created in swiftsim/examples) on most platforms, run +We use autotools for setup. To get a basic running version of the code use: .. code-block:: bash @@ -106,6 +116,7 @@ We use autotools for setup. To get a basic running version of the code ./configure make +the executable binaries are found in the top directory. MacOS Specific Oddities ~~~~~~~~~~~~~~~~~~~~~~~ @@ -118,6 +129,9 @@ MacOS, so it is best to leave it out. To configure: ./configure --disable-compiler-warnings --disable-doxygen-doc +When using the clang compiler, the hand-written vectorized routines +have to be disabled. This is done at configuration time by adding +the flag ``--disable-hand-vec``. Trouble Finding Libraries ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -129,7 +143,7 @@ HDF5 library, .. code-block:: bash - ./configure --with-hdf5=/path/to/h5cc + ./configure --with-hdf5=/path/to/hdf5_root More information about what needs to be provided to these flags is given in ``./configure --help``. diff --git a/doc/RTD/source/GettingStarted/configuration_options.rst b/doc/RTD/source/GettingStarted/configuration_options.rst index 7dca5cddb0012b9a2146640b9373cfbe81c8dbdd..d3416d1ff96c6022ae8c571a9adb37a05d1e9439 100644 --- a/doc/RTD/source/GettingStarted/configuration_options.rst +++ b/doc/RTD/source/GettingStarted/configuration_options.rst @@ -13,7 +13,7 @@ configuration flag. A description of the available options of the below flags can be found by using ``./configure --help``. -``--with-hydro=gadget2`` +``--with-hydro=sphenix`` ~~~~~~~~~~~~~~~~~~~~~~~~ There are several hydrodynamical schemes available in SWIFT. You can choose between them at compile-time with this option. diff --git a/doc/RTD/source/GettingStarted/running_example.rst b/doc/RTD/source/GettingStarted/running_example.rst index a5614fb2a2e2068a8188d82182042feb95251a73..b35725e60de16cb0e1b10fb4394898f67bef822d 100644 --- a/doc/RTD/source/GettingStarted/running_example.rst +++ b/doc/RTD/source/GettingStarted/running_example.rst @@ -1,26 +1,34 @@ .. Running an Example Josh Borrow, 5th April 2018 + Mladen Ivkovic, Jan 2023 Running an Example ================== -Now that you have built the code, you will want to run an example! To do that, -you need to follow the following instructions (requires ``python2`` or -``python3`` with the ``h5py`` and other standard scientific packages, as well -as ``wget`` for grabbing the glass). +Now that you have built the code, you will want to run an example! +Let's start with a hydrodynamics only example, the 3D Sod Shock. + + +Sod Shock +~~~~~~~~~ + +To run the Sod Shock example, you need to follow the following instructions +(requires ``python3`` with the ``h5py`` and other standard scientific packages, +as well as ``wget`` for grabbing the glass). + .. code-block:: bash cd examples/HydroTests/SodShock_3D ./getGlass.sh - python makeIC.py + python3 makeIC.py ../swift --hydro --threads=4 sodShock.yml - python plotSolution.py 1 + python3 plotSolution.py 1 This will run the 'SodShock' in 3D and produce a nice plot that shows you how the density has varied. Try running with GIZMO-MFV (this will take -_significantly_ longer than with SPH) to see the difference. For that, you +*significantly* longer than with SPH) to see the difference. For that, you will need to reconfigure with the following options: .. code-block:: bash @@ -29,9 +37,55 @@ will need to reconfigure with the following options: --with-hydro=gizmo-mfv \ --with-riemann-solver=hllc - To see the results that you should get, you should check out our developer wiki at https://gitlab.cosma.dur.ac.uk/swift/swiftsim/wikis/Sod_3D. If you don't get these results, please contact us on our GitHub page at https://github.com/SWIFTSIM/swiftsim/issues. + + + + +Small Cosmological Volume +~~~~~~~~~~~~~~~~~~~~~~~~~ + +As a second example, we run a small cosmolgical +volume containing dark matter only starting at redshift :math:`z = 50`. +Like for the Sod Shock example, it suffices to configure (``./configure``) and +compile (``make``) the code without any extra flags. + +After downloading the initial conditions, we run the code with cosmology and +self-gravity: + +.. code-block:: bash + + cd examples/SmallCosmoVolume/SmallCosmoVolume_DM + ./getIC.sh + ../../../swift --cosmology --self-gravity --threads=8 small_cosmo_volume_dm.yml + + +We can plot the solution with the included python script +as follows: + +.. code-block:: bash + + python3 plotProjection.py 31 + + +The ``plotProjection.py`` script requires the `swiftsimio <https://swiftsimio.readthedocs.io/en/latest/>`_ +library, which is a dedicated and maintained visualisation and analysis +library for SWIFT. + + +An additional example containing both baryonic and dark matter is +``examples/SmallCosmoVolume/SmallCosmoVolume_hydro``. To run with +hydrodynamics, the ``--hydro`` flag needs to be provided as well: + +.. code-block:: bash + + cd examples/SmallCosmoVolume/SmallCosmoVolume_hydro + ./getIC.sh + ../../../swift --cosmology --self-gravity --hydro --threads=8 small_cosmo_volume.yml + +The solution can again be plotted using the ``plotProjection.py`` script. + diff --git a/doc/RTD/source/GettingStarted/running_on_large_systems.rst b/doc/RTD/source/GettingStarted/running_on_large_systems.rst index 42beedf790dc3d87895cd9bd6db13f25942b0a16..799525824958bd8b2027a207770f323622353a2b 100644 --- a/doc/RTD/source/GettingStarted/running_on_large_systems.rst +++ b/doc/RTD/source/GettingStarted/running_on_large_systems.rst @@ -29,13 +29,14 @@ system (i.e. over MPI on several nodes). Here are some recommendations: + Ensure that you compile with ParMETIS or METIS. These are required if want to load balance between MPI ranks. -Your batch script should look something like the following (to run on 16 nodes -each with 2x16 core processors for a total of 512 cores): + +Your batch script should look something like the following (to run on 8 nodes each +with 2x18 core processors for a total of 288 cores): .. code-block:: bash - #SBATCH -N 16 # Number of nodes to run on - #SBATCH --tasks-per-node=2 # This system has 2 chips per node - - mpirun -np 32 swift_mpi --threads=16 --pin parameter.yml + #SBATCH -N 8 # Number of nodes to run on + #SBATCH --tasks-per-node=2 # This system has 2 chips per node + + mpirun -n 16 swift_mpi --threads=18 --pin parameter.yml diff --git a/doc/RTD/source/GettingStarted/special_modes.rst b/doc/RTD/source/GettingStarted/special_modes.rst index bf19bc7f679eed7d37cefb38ab8f05972ac68b30..724d0e9f01d811a350cbeb67c601430083e4313b 100644 --- a/doc/RTD/source/GettingStarted/special_modes.rst +++ b/doc/RTD/source/GettingStarted/special_modes.rst @@ -98,7 +98,7 @@ To run SWIFT in this mode, configure the code with computed for every :math:`N^{th}` particle based on their ID (i.e., to compute the exact forces for all particles set ``N=1``). -Two `.dat` files will be output during each timestep, one containing the forces +Two ``.dat`` files will be output during each timestep, one containing the forces (really it is the accelerations that are stored) as computed by ``_swift_``, and another containing the ``_exact_`` forces. The total force (``a_swift``), along with the contributed force from each component of the tree (P2P, M2P and M2L) diff --git a/doc/RTD/source/GettingStarted/what_about_mpi.rst b/doc/RTD/source/GettingStarted/what_about_mpi.rst index 98141049f3e36506d6033259e7f5bb9394daf997..531032780159a126ee883df25afc20a7f46517b2 100644 --- a/doc/RTD/source/GettingStarted/what_about_mpi.rst +++ b/doc/RTD/source/GettingStarted/what_about_mpi.rst @@ -7,6 +7,35 @@ What about MPI? Running SWIFT on more than one node After compilation, you will be left with two binaries. One is called ``swift``, and the other ``swift_mpi``. Current wisdom is to run ``swift`` if you are only using one node (i.e. without any interconnect), and one MPI rank per NUMA -region using ``swift_mpi`` for anything larger. You will need some GADGET-2 -HDF5 initial conditions to run SWIFT, as well as a compatible yaml -parameter file. +region using ``swift_mpi`` for anything larger. You will need some initial +conditions in the GADGET-2 HDF5 format (see :ref:`Initial Conditions <Initial_Conditions_label>`) +to run SWIFT, as well as a compatible :ref:`yaml parameter file <Parameter_File_label>`. + + + +SLURM Submission Script Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +Below is an example submission script for the SLURM +batch system. This runs SWIFT with thread pinning, SPH, +and self-gravity. + +.. code-block:: bash + + #SBATCH --partition=<queue> + #SBATCH --account-name=<groupName> + #SBATCH --job-name=<jobName> + #SBATCH --nodes=<nNodes> + #SBATCH --ntasks-per-node=<nMPIRank> + #SBATCH --cpus-per-task=<nThreadsPerMPIRank> + #SBATCH --output=outFile.out + #SBATCH --error=errFile.err + + ## expected runtime + #SBATCH --time-<hh>:<mm>:<ss> + + ./swift_mpi --pin --threads=<nThreadsPerMPIRank> \ + --hydro --self-gravity \ + parameter_file.yml + diff --git a/doc/RTD/source/InitialConditions/index.rst b/doc/RTD/source/InitialConditions/index.rst index 552a4a3e95809f1c453523e4aba43b063559c14b..e325d7b88662c5042cae22ed1709bf6e72dbe550 100644 --- a/doc/RTD/source/InitialConditions/index.rst +++ b/doc/RTD/source/InitialConditions/index.rst @@ -1,6 +1,8 @@ .. Initial Conditions Josh Borrow, 5th April 2018 +.. _Initial_Conditions_label: + Initial Conditions ================== diff --git a/doc/RTD/source/LightCones/adding_outputs.rst b/doc/RTD/source/LightCones/adding_outputs.rst new file mode 100644 index 0000000000000000000000000000000000000000..a8e554fe9e1ff78cb9690c5676bf71ce40dd53c6 --- /dev/null +++ b/doc/RTD/source/LightCones/adding_outputs.rst @@ -0,0 +1,20 @@ +.. Light Cones + John Helly 29th April 2021 + +.. _lightcone_adding_outputs_label: + +Adding New Types of Output +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New particle properties can be added to the particle light cones as follows: + +* Add a field to the ``lightcone_<type>_data`` struct in ``lightcone_particle_io.h`` to store the new quantity +* Modify the ``lightcone_store_<type>`` function in ``lightcone_particle_io.c`` to set the new struct field from the particle data +* in ``lightcone_io_make_output_fields()``, add a call to ``lightcone_io_make_output_field()`` to define the new output + +Here, <type> is the particle type: gas, dark_matter, stars, black_hole or neutrino. + +To add a new type of HEALPIX map: + +* Add a function to compute the quantity in ``lightcone_map_types.c``. See ``lightcone_map_total_mass()`` for an example. +* Add a new entry to the ``lightcone_map_types`` array in lightcone_map_types.h. This should specify the name of the new map type, a pointer to the function to compute the quantity, and the units of the quantity. The last entry in the array is not used and must have a NULL function pointer to act as an end marker. diff --git a/doc/RTD/source/LightCones/algorithm_description.rst b/doc/RTD/source/LightCones/algorithm_description.rst new file mode 100644 index 0000000000000000000000000000000000000000..ceb4c9834ac0b420dcdc8d3c17b105d1790c78f4 --- /dev/null +++ b/doc/RTD/source/LightCones/algorithm_description.rst @@ -0,0 +1,27 @@ +.. Light Cones + John Helly 29th April 2021 + +.. _lightcone_algorithm_description_label: + +Light Cone Output Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In cosmological simulations it is possible to specify the location of +an observer in the simulation box and have SWIFT output information +about particles in the simulation as they cross the observer's past +light cone. + +Whenever a particle is drifted the code checks if any periodic copy of +the particle crosses the lightcone during the drift, and if so that +copy of the particle is buffered for output. As an optimization, at the +start of each time step the code computes which periodic copies of the +simulation box could contribute to the light cone and only those copies +are searched. When drifting the particles in a particular cell the list of +replications is further narrowed down using the spatial extent of the +cell. + +Particles can be output directly to HDF5 files or accumulated to healpix +maps corresponding to spherical shells centred on the observer. + + + diff --git a/doc/RTD/source/LightCones/index.rst b/doc/RTD/source/LightCones/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..54ec0194b51efe7938ffae82f2b34f768c63f829 --- /dev/null +++ b/doc/RTD/source/LightCones/index.rst @@ -0,0 +1,20 @@ +.. Light Cones + John Helly 29th April 2021 + +.. _Light_Cones_label: + +Light Cone Outputs +================== + +This section describes the light cone outputs +and related parameters + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + algorithm_description + lightcone_particle_output + lightcone_healpix_maps + running_with_lightcones + adding_outputs diff --git a/doc/RTD/source/LightCones/lightcone_healpix_maps.rst b/doc/RTD/source/LightCones/lightcone_healpix_maps.rst new file mode 100644 index 0000000000000000000000000000000000000000..42a63741fac6ff88015fe517aa8905ba081891d8 --- /dev/null +++ b/doc/RTD/source/LightCones/lightcone_healpix_maps.rst @@ -0,0 +1,36 @@ +.. Light Cones + John Helly 29th April 2021 + +.. _lightcone_healpix_maps_label: + +Light Cone HEALPix Maps +~~~~~~~~~~~~~~~~~~~~~~~ + +SWIFT can accumulate particle properties to HEALPix maps as they +cross the observer's past light cone. Each map corresponds to a +spherical shell centred on the observer. When a particle crosses +the lightcone its distance from the observer is calculated and the +particle's contribution is added to a buffer so that at the end of +the time step it can be added to the corresponding HEALPix map. + +Maps can be generated for multiple concentric shells and multiple +quantities can be accumulated for each shell. The HEALPix map for a +shell is allocated and zeroed out when the simulation first reaches +a redshift where particles could contribute to that map. The map is +written out and deallocated when the simulation advances to a point +where there can be no further contributions. In MPI runs the pixel +data for the maps are distributed across all MPI ranks. + +Updates to the maps are buffered in order to avoid the need for +communication during the time step. At the end of the step if any +MPI rank has a large amount of updates buffered then all pending +updates will be applied to the pixel data. + +For gas particles, the HEALPix maps are smoothed using a projected +version of the same kernel used for the hydro calculations. Other +particle types are not smoothed. + +The code writes one output file for each spherical shell. In MPI mode +all ranks write to the same file using parallel HDF5. If maps of +multiple quantities are being made they will be written to a single +file as separate 1D datasets with one element per pixel. diff --git a/doc/RTD/source/LightCones/lightcone_particle_output.rst b/doc/RTD/source/LightCones/lightcone_particle_output.rst new file mode 100644 index 0000000000000000000000000000000000000000..e4c2ec6349e75e56cafffc61351151cbc35bb698 --- /dev/null +++ b/doc/RTD/source/LightCones/lightcone_particle_output.rst @@ -0,0 +1,30 @@ +.. Light Cones + John Helly 29th June 2021 + +.. _lightcone_particle_output_label: + +Light Cone Particle Output +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +SWIFT can output particles to HDF5 output files (similar to the +snapshots) as they cross the observer's light cone. During each time +step, any particles which cross the light cone are added to a buffer. +If this buffer is large at the end of the step then its contents +are written to an output file. In MPI runs each MPI rank writes its +own output file and decides independently when to flush its particle +buffer. + +A new output file is started whenever restart files are written. This +allows the code to automatically continue from the point of the restart +dump if the run is interrupted. Any files written after the restart +dump will be overwritten when the simulation is resumed, preventing +duplication of particles in the light cone output. + +The output files have names of the form ``basename_XXXX.Y.hdf5``, where +XXXX numbers the files written by a single MPI rank and Y is the index +of the MPI rank. + +The output files contain one HDF5 group for each particle type. Within +each group there are datasets corresponding to particle properties in +a similar format to the snapshots. + diff --git a/doc/RTD/source/LightCones/running_with_lightcones.rst b/doc/RTD/source/LightCones/running_with_lightcones.rst new file mode 100644 index 0000000000000000000000000000000000000000..1df3c5b7a8a9e0897d934f4eec8f2130aa9b838f --- /dev/null +++ b/doc/RTD/source/LightCones/running_with_lightcones.rst @@ -0,0 +1,20 @@ +.. Light Cones + John Helly 29th April 2021 + +.. _lightcone_running_label: + +Running SWIFT with Light Cone Output +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To produce light cone particle output swift must be configured +with ``--enable-lightcone``. Additionally, making HEALPix maps +requires the HEALPix C library. If using MPI then parallel HDF5 +is also required. + +One lightcone is produced for each ``LightconeX`` section in the +parameter file, where X=0-7. This allows generation of up to 8 +light cones. See :ref:`Parameters_light_cone` for details. + +SWIFT must be run with the ``--lightcone`` flag to activate light +cone outputs, otherwise the Lightcone sections in the parameter file +are ignored. diff --git a/doc/RTD/source/ParameterFiles/lossy_filters.rst b/doc/RTD/source/ParameterFiles/lossy_filters.rst index e4c0fe6a9f9de1f9057ad0da3a29aa4d23e7ec6b..61915e990d190db75dce5d69f1b88e7a2e9fa5f0 100644 --- a/doc/RTD/source/ParameterFiles/lossy_filters.rst +++ b/doc/RTD/source/ParameterFiles/lossy_filters.rst @@ -43,6 +43,8 @@ sufficient (The extra "+1" is for the sign bit). SWIFT implements 5 variants of this filter: + * ``Nbit32`` stores the 32 most significant bits (Numbers up to + :math:`2\times10^{9}`, comp. ratio: 2) * ``Nbit36`` stores the 36 most significant bits (Numbers up to :math:`3.4\times10^{10}`, comp. ratio: 1.78) * ``Nbit40`` stores the 40 most significant bits (Numbers up to diff --git a/doc/RTD/source/ParameterFiles/output_selection.rst b/doc/RTD/source/ParameterFiles/output_selection.rst index 44fe7bcb362fb55cdd0f8c4c12b5e3e78a33523e..fb4e3a2a3375092ab7a475e20b8d46b709004997 100644 --- a/doc/RTD/source/ParameterFiles/output_selection.rst +++ b/doc/RTD/source/ParameterFiles/output_selection.rst @@ -5,7 +5,8 @@ Output List ~~~~~~~~~~~ -In the sections ``Snapshots``, ``Statistics`` and ``StructureFinding``, you can +In the sections ``Snapshots``, ``Statistics``, ``StructureFinding``, +``LineOfSight``, ``PowerSpectrum``, and ``FOF`` you can specify the options ``output_list_on`` and ``output_list`` which receive an int and a filename. The ``output_list_on`` enable or not the output list and ``output_list`` is the filename containing the output times. With the file diff --git a/doc/RTD/source/ParameterFiles/parameter_description.rst b/doc/RTD/source/ParameterFiles/parameter_description.rst index 3dfb42fc1ed544c0e45dbdbd0d2aecc76de54ecf..ebfd3f3faab167006410ed2fe18ef43aa4f7f8c6 100644 --- a/doc/RTD/source/ParameterFiles/parameter_description.rst +++ b/doc/RTD/source/ParameterFiles/parameter_description.rst @@ -53,13 +53,15 @@ a compulsory parameter is missing an error will be raised at start-up. Finally, SWIFT outputs two YAML files at the start of a run. The first one -``used_parameters.yml`` contains all the parameters that were used for this run, -**including all the optional parameters left unspecified with their default -values**. This file can be used to start an exact copy of the run. The second -file, ``unused_parameters.yml`` contains all the values that were not read from -the parameter file. This can be used to simplify the parameter file or check -that nothing important was ignored (for instance because the code is not -configured to use some options). +``used_parameters.yml`` contains all the parameters that were used for this +run, **including all the optional parameters left unspecified with their +default values**. This file can be used to start an exact copy of the run. The +second file, ``unused_parameters.yml`` contains all the values that were not +read from the parameter file. This can be used to simplify the parameter file +or check that nothing important was ignored (for instance because the code is +not configured to use some options). Note that on restart a new file +``used_parameters.yml.stepno`` is created and any changed parameters will be +written to it. The rest of this page describes all the SWIFT parameters, split by section. A list of all the possible parameters is kept in the file @@ -174,15 +176,15 @@ If unspecified these parameters default to the default The radiation density :math:`\Omega_r` can also be specified by setting an alternative optional parameter: -* The number of ultra-relativistic degrees of freedom :math:`N_\rm{ur}`: +* The number of ultra-relativistic degrees of freedom :math:`N_{\rm{ur}}`: ``N_ur``. The radiation density :math:`\Omega_r` is then automatically inferred from -:math:`N_\rm{ur}` and the present-day CMB temperature +:math:`N_{\rm{ur}}` and the present-day CMB temperature :math:`T_{\rm{CMB},0}=2.7255` Kelvin. This parametrization cannot be used together with :math:`\Omega_r`. If neither parameter is used, SWIFT -defaults to :math:`\Omega_r = 0`. Note that :math:`N_\rm{ur}` differs from -:math:`N_\rm{eff}`, the latter of which also includes massive neutrinos. +defaults to :math:`\Omega_r = 0`. Note that :math:`N_{\rm{ur}}` differs from +:math:`N_{\rm{eff}}`, the latter of which also includes massive neutrinos. Massive neutrinos can be included by specifying the optional parameters: @@ -194,7 +196,7 @@ Massive neutrinos can be included by specifying the optional parameters: When including massive neutrinos, only ``N_nu`` and ``M_nu_eV`` are necessary. By default, SWIFT will assume non-degenerate species and :math:`T_{\nu,0}=(4/11)^{1/3}T_{\rm{CMB},0}`. Neutrinos do not contribute to -:math:`\Omega_m = \Omega_\rm{cdm} + \Omega_b` in our conventions. +:math:`\Omega_m = \Omega_{\rm{cdm}} + \Omega_b` in our conventions. For a Planck+13 cosmological model (ignoring radiation density as is commonly done) and running from :math:`z=127` to :math:`z=0`, one would hence @@ -257,14 +259,16 @@ The accuracy of the gravity calculation is governed by the following four parame * The accuracy criterion used in the adaptive MAC: :math:`\epsilon_{\rm fmm}`: ``epsilon_fmm``, * The time-step size pre-factor :math:`\eta`: ``eta``, -The first three parameters govern the way the Fast-Multipole method -tree-walk is done (see the theory documents for full details). The ``MAC`` -parameter can take two values: ``adaptive`` or ``geometric``. In the first -case, the tree recursion decision is based on the estimated accelerations -that a given tree node will produce, trying to recurse to levels where the -fractional contribution of the accelerations to the cell is less than -:math:`\epsilon_{\rm fmm}`. In the second case, a fixed Barnes-Hut-like -opening angle :math:`\theta_{\rm cr}` is used. +The first three parameters govern the way the Fast-Multipole method tree-walk is +done (see the theory documents for full details). The ``MAC`` parameter can +take three values: ``adaptive``, ``geometric``, or ``gadget``. In the first +case, the tree recursion decision is based on the estimated accelerations that a +given tree node will produce, trying to recurse to levels where the fractional +contribution of the accelerations to the cell is less than :math:`\epsilon_{\rm +fmm}`. In the second case, a fixed Barnes-Hut-like opening angle +:math:`\theta_{\rm cr}` is used. The final case corresponds to the choice made +in the Gadget-4 code. It is an implementation using eq. 36 of `Springel et +al. (2021) <https://adsabs.harvard.edu/abs/2021MNRAS.506.2871S>`_. The time-step of a given particle is given by :math:`\Delta t = \sqrt{2\eta\epsilon_i/|\overrightarrow{a}_i|}`, where @@ -273,19 +277,32 @@ The time-step of a given particle is given by :math:`\Delta t = <http://adsabs.harvard.edu/abs/2003MNRAS.338...14P>`_ recommend using :math:`\eta=0.025`. -The last tree-related parameters are: +Two further parameters determine when the gravity tree is reconstructed: * The tree rebuild frequency: ``rebuild_frequency``. +* The fraction of active particles to trigger a rebuild: + ``rebuild_active_fraction``. + +The tree rebuild frequency is an optional parameter defaulting to +:math:`0.01`. It is used to trigger the re-construction of the tree every +time a fraction of the particles have been integrated (kicked) forward in +time. The second parameter is also optional and determines a separate rebuild +criterion, based on the fraction of particles that is active at the +beginning of a step. This can be seen as a forward-looking version of the +first criterion, which can be useful for runs with very fast particles. +The second criterion is not used for values :math:`>1`, which is the default +assumption. + + +The last tree-related parameters are: + * Whether or not to use the approximate gravity from the FMM tree below the softening scale: ``use_tree_below_softening`` (default: 0) * Whether or not the truncated force estimator in the adaptive tree-walk considers the exponential mesh-related cut-off: ``allow_truncation_in_MAC`` (default: 0) -The tree rebuild frequency is an optional parameter defaulting to -:math:`0.01`. It is used to trigger the re-construction of the tree every -time a fraction of the particles have been integrated (kicked) forward in -time. The other two parameters default to good all-around choices. See the +These parameters default to good all-around choices. See the theory documentation about their exact effects. Simulations using periodic boundary conditions use additional parameters for the @@ -293,6 +310,9 @@ Particle-Mesh part of the calculation. The last five are optional: * The number cells along each axis of the mesh :math:`N`: ``mesh_side_length``, * Whether or not to use a distributed mesh when running over MPI: ``distributed_mesh`` (default: ``0``), +* Whether or not to use local patches instead of direct atomic operations to + write to the mesh in the non-MPI case (this is a performance tuning + parameter): ``mesh_uses_local_patches`` (default: ``1``), * The mesh smoothing scale in units of the mesh cell-size :math:`a_{\rm smooth}`: ``a_smooth`` (default: ``1.25``), * The scale above which the short-range forces are assumed to be 0 (in units of @@ -686,6 +706,14 @@ creation of a compact range of IDs beyond which the new IDs generated by splitting can be safely drawn from. Note that, when ``remap_ids`` is switched on the ICs do not need to contain a ``ParticleIDs`` field. +Both replication and remapping explicitly overwrite any particle IDs +provided in the initial conditions. This may cause problems for runs +with neutrino particles, as some models assume that that the particle +ID was used as a random seed for the Fermi-Dirac momentum. In this case, +the ``Neutrino:generate_ics`` option can be used to generate new initial +conditions based on the replicated or remapped IDs. See :ref:`Neutrinos` +for details. + * Name of a HDF5 group to copy from the ICs file(s): ``metadata_group_name`` (default: ``ICs_parameters``) If the initial conditions generator writes a HDF5 group with the parameters @@ -711,20 +739,21 @@ be: Physical Constants ------------------ -For some idealised test it can be useful to overwrite the value of -some physical constants; in particular the value of the gravitational -constant. SWIFT offers an optional parameter to overwrite the value of -:math:`G_N`. +For some idealised test it can be useful to overwrite the value of some physical +constants; in particular the value of the gravitational constant and vacuum +permeability. SWIFT offers an optional parameter to overwrite the value of +:math:`G_N` and :math:`\mu_0`. .. code:: YAML PhysicalConstants: - G: 1 + G: 1 + mu_0: 1 Note that this set :math:`G` to the specified value in the internal system of units. Setting a value of `1` when using the system of units (10^10 Msun, Mpc, km/s) will mean that :math:`G_N=1` in these units [#f2]_ instead of the -normal value :math:`G_N=43.00927`. +normal value :math:`G_N=43.00927`. The same applies to :math:`\mu_0`. This option is only used for specific tests and debugging. This entire section of the YAML file can typically be left out. More constants may @@ -803,6 +832,14 @@ It is also possible to run the FOF algorithm just before writing each snapshot. See the section :ref:`Parameters_fof` for details of the FOF parameters. +It is also possible to run the power spectrum calculation just before writing +each snapshot. + +* Run PS every time a snapshot is dumped: ``invoke_ps`` + (default: ``0``). + +See the section :ref:`Parameters_ps` for details of the power spectrum parameters. + When running over MPI, users have the option to split the snapshot over more than one file. This can be useful if the parallel-io on a given system is slow but has the drawback of producing many files per time slice. This is activated @@ -888,6 +925,22 @@ output as a zero-padded integer. For example, if the base-name is "eagle" and snapshot 7 was just dumped, with ``dump_command`` set to ``./postprocess.sh``, then SWIFT will run ``./postprocess.sh eagle 0007``. +For some quantities, especially in the subgrid models, it can be advantageous to +start recording numbers at a fixed time before the dump of a snapshot. Classic +examples are an averaged star-formation rate or accretion rate onto BHs. For the +subgrid models that support it, the triggers can be specified by setting the +following parameters: + +* for gas: ``recording_triggers_part`` (no default, array of size set by each subgrid model) +* for stars: ``recording_triggers_spart`` (no default, array of size set by each subgrid model) +* for BHs: ``recording_triggers_bpart`` (no default, array of size set by each subgrid model) + +The time is specified in internal time units (See the :ref:`Parameters_units` +section) and a recording can be ignored by setting the parameter to ``-1``. Note +that the code will verify that the recording time is smaller than the gap in +between consecutive snapshot dumps and if the recording window is longer, it +will reduce it to the gap size between the snapshots. + Finally, it is possible to specify a different system of units for the snapshots than the one that was used internally by SWIFT. The format is identical to the one described above (See the :ref:`Parameters_units` section) and read: @@ -927,7 +980,7 @@ would have: invoke_fof: 1 compression: 3 distributed: 1 - lustre_OST_count: 48 # System has 48 Lustre OSTs to distribute the files over + lustre_OST_count: 48 # System has 48 Lustre OSTs to distribute the files over UnitLength_in_cgs: 1. # Use cm in outputs UnitMass_in_cgs: 1. # Use grams in outputs UnitVelocity_in_cgs: 1. # Use cm/s in outputs @@ -937,6 +990,12 @@ would have: subsample_fraction: [0, 0.01, 0, 0, 0, 0, 0.1] # Write 1% of the DM parts and 10% of the neutrinos run_on_dump: 1 dump_command: ./submit_analysis.sh + use_delta_from_edge: 1 + delta_from_edge: 1e-6 # Move particles away from the edge by 1-e6 of the length unit. + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_spart: [-1, -1] # No recording + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + Some additional specific options for the snapshot outputs are described in the following pages: @@ -975,6 +1034,182 @@ be processed by the ``SpecWizard`` tool range_when_shooting_down_y: 100. # Range along the y-axis of LoS along y range_when_shooting_down_z: 100. # Range along the z-axis of LoS along z + +.. _Parameters_light_cone: + +Light Cone Outputs +--------------------- + +One or more light cone outputs can be configured by including ``LightconeX`` sections +in the parameter file, where X is in the range 0-7. It is also possible to include a +``LightconeCommon`` section for parameters which are the same for all lightcones. The +parameters for each light cone are: + +* Switch to enable or disable a lightcone: ``enabled`` + +This should be set to 1 to enable the corresponding lightcone or 0 to disable it. +Has no effect if specified in the LightconeCommon section. + +* Directory in which to write light cone output: ``subdir`` + +All light cone output files will be written in the specified directory. + +* Base name for particle and HEALPix map outputs: ``basename``. + +Particles will be written to files ``<basename>_XXXX.Y.hdf5``, where XXXX numbers the files +written by a single MPI rank and Y is the MPI rank index. HEALPix maps are written to files +with names ``<basename>.shell_X.hdf5``, where X is the index of the shell. The basename must +be unique for each light cone so it cannot be specified in the LightconeCommon section. + +See :ref:`lightcone_adding_outputs_label` for information on adding new output quantities. + +* Location of the observer in the simulation box, in internal units: ``observer_position`` + +* Size of in memory chunks used to store particles and map updates: ``buffer_chunk_size`` + +During each time step buffered particles and HEALPix map updates are stored in a linked +list of chunks of ``buffer_chunk_size`` elements. Additional chunks are allocated as needed. +The map update process is parallelized over chunks so the chunks should be small enough that +each MPI rank typically has more chunks than threads. + +* Maximum amount of map updates (in MB) to send on each iteration: ``max_map_update_send_size_mb`` + +Flushing the map update buffer involves sending the updates to the MPI ranks with the affected +pixel data. Sending all updates at once can consume a large amount of memory so this parameter +allows updates to be applied over multiple iterations to reduce peak memory usage. + +* Redshift range to output each particle type: ``z_range_for_<type>`` + +A two element array with the minimum and maximum redshift at which particles of type ``<type>`` +will be output as they cross the lightcone. ``<type>`` can be Gas, DM, DMBackground, Stars, BH +or Neutrino. If this parameter is not present for a particular type then that type will not +be output. + +* The number of buffered particles which triggers a write to disk: ``max_particles_buffered`` + +If an MPI rank has at least max_particles_buffered particles which have crossed the lightcone, +it will write them to disk at the end of the current time step. + +* Size of chunks in the particle output file + +This sets the HDF5 chunk size. Particle outputs must be chunked because the number of particles +which will be written out is not known when the file is created. + +* Whether to use lossy compression in the particle outputs: ``particles_lossy_compression`` + +If this is 1 then the HDF5 lossy compression filter named in the definition of each particle +output field will be enabled. If this is 0 lossy compression is not applied. + +* Whether to use lossless compression in the particle outputs: ``particles_gzip_level`` + +If this is non-zero the HDF5 deflate filter will be applied to lightcone particle output with +the compression level set to the specified value. + +* HEALPix map resolution: ``nside`` + +* Name of the file with shell radii: ``radius_file.txt`` + +This specifies the name of a file with the inner and outer radii of the shells used to make +HEALPix maps. It should be a text file with a one line header and then two comma separated columns +of numbers with the inner and outer radii. The units are determined by the header. The header must +be one of the following: + +``# Minimum comoving distance, Maximum comoving distance``, +``# Minimum redshift, Maximum redshift``, or +``# Maximum expansion factor, Minimum expansion factor``. Comoving distances are in internal units. +The shells must be in ascending order of radius and must not overlap. + +* Number of pending HEALPix map updates before the buffers are flushed: ``max_updates_buffered`` + +In MPI mode applying updates to the HEALPix maps requires communication and forces synchronisation +of all MPI ranks, so it is not done every time step. If any MPI rank has at least +``max_updates_buffered`` pending updates at the end of a time step, then all ranks will apply +their updates to the HEALPix maps. + +* Which types of HEALPix maps to create: ``map_names_file`` + +This is the name of a file which specifies what quantities should be accumulated to HEALPix maps. +The possible map types are defined in the lightcone_map_types array in ``lightcone_map_types.h``. +See :ref:`lightcone_adding_outputs_label` if you'd like to add a new map type. + +* Whether to distribute HEALPix maps over multiple files: ``distributed_maps`` + +If this is 0 then the code uses HDF5 collective writes to write each map to a single file. If this +is 1 then each MPI rank writes its part of the HEALPix map to a separate file. + +The file contains two columns: the first column is the name of the map type and the second is the +name of the compression filter to apply to it. See io_compression.c for the list of compression +filter names. Set the filter name to ``on`` to disable compression. + +* Whether to use lossless compression in the HEALPix map outputs: ``maps_gzip_level`` + +If this is non-zero the HDF5 deflate filter will be applied to the lightcone map output with +the compression level set to the specified value. + +The following shows a full set of light cone parameters for the case where we're making two +light cones which only differ in the location of the observer: + +.. code:: YAML + + LightconeCommon: + + # Common parameters + subdir: lightcones + buffer_chunk_size: 100000 + max_particles_buffered: 1000000 + hdf5_chunk_size: 10000 + + # Redshift ranges for particle types + z_range_for_Gas: [0.0, 0.05] + z_range_for_DM: [0.0, 0.05] + z_range_for_DMBackground: [0.0, 0.05] + z_range_for_Stars: [0.0, 0.05] + z_range_for_BH: [0.0, 0.05] + z_range_for_Neutrino: [0.0, 0.05] + + # Healpix map parameters + nside: 512 + radius_file: ./shell_radii.txt + max_updates_buffered: 100000 + map_names_file: map_names.txt + max_map_update_send_size_mb: 1.0 + distributed_maps: 0 + + # Compression options + particles_lossy_compression: 0 + particles_gzip_level: 6 + maps_gzip_level: 6 + + Lightcone0: + + enabled: 1 + basename: lightcone0 + observer_position: [35.5, 78.12, 12.45] + + Lightcone0: + + enabled: 1 + basename: lightcone1 + observer_position: [74.2, 10.80, 53.59] + + +An example of the radius file:: + + # Minimum comoving distance, Maximum comoving distance + 0.0, 50.0 + 50.0, 100.0 + 150.0, 200.0 + 200.0, 400.0 + 400.0, 1000.0 + +An example of the map names file:: + + TotalMass on + SmoothedGasMass on + UnsmoothedGasMass on + DarkMatterMass on + + .. _Parameters_eos: Equation of State (EoS) @@ -1024,6 +1259,78 @@ parameter sets the thermal energy per unit mass. planetary_ANEOS_iron_table_file: ./EoSTables/ANEOS_iron_S20.txt planetary_ANEOS_Fe85Si15_table_file: ./EoSTables/ANEOS_Fe85Si15_S20.txt +.. _Parameters_ps: + +Power Spectra Calculation +------------------------- + + +SWIFT can compute a variety of auto- and cross- power spectra at user-specified +intervals. The behaviour of this output type is governed by the ``PowerSpectrum`` +section of the parameter file. The calculation is performed on a regular grid +(typically of size 256^3) and foldings are used to extend the range probed to +smaller scales. + +The options are: + + * The size of the base grid to perform the PS calculation: + ``grid_side_length``. + * The number of grid foldings to use: ``num_folds``. + * The factor by which to fold at each iteration: ``fold_factor`` (default: 4) + * The order of the window function: ``window_order`` (default: 3) + +The window order sets the way the particle properties get assigned to the mesh. +Order 1 corresponds to the nearest-grid-point (NGP), order 2 to cloud-in-cell +(CIC), and order 3 to triangular-shaped-cloud (TSC). Higher-order schemes are not +implemented. + +Finally, the quantities for which a PS should be computed are specified as a +list of pairs of values for the parameter ``requested_spectra``. Auto-spectra +are specified by using the same type for both pair members. The available values +listed in the following table: + ++---------------------+---------------------------------------------------+ +| Name | Description | ++=====================+===================================================+ +| ``matter`` | Mass density of all matter | ++---------------------+---------------------------------------------------+ +| ``cdm`` | Mass density of all dark matter | ++---------------------+---------------------------------------------------+ +| ``gas`` | Mass density of all gas | ++---------------------+---------------------------------------------------+ +| ``starBH`` | Mass density of all stars and BHs | ++---------------------+---------------------------------------------------+ +| ``neutrino`` | Mass density of all neutrinos | ++---------------------+---------------------------------------------------+ +| ``neutrino1`` | Mass density of a random half of the neutrinos | ++---------------------+---------------------------------------------------+ +| ``neutrino2`` | Mass density of a the other half of the neutrinos | ++---------------------+---------------------------------------------------+ +| ``pressure`` | Electron pressure | ++---------------------+---------------------------------------------------+ + +A dark matter mass density auto-spectrum is specified as ``cdm-cdm`` and a gas +density - electron pressure cross-spectrum as ``gas-pressure``. + +The ``neutrino1`` and ``neutrino2`` selections are based on the particle IDs and +are mutually exclusive. The particles selected in each half are different in +each output. Note that neutrino PS can only be computed when neutrinos are +simulated using particles. + +An example of a valid power-spectrum section of the parameter file looks like: + +.. code:: YAML + + PowerSpectrum: + grid_side_length: 256 + num_folds: 3 + requested_spectra: ["matter-matter", "cdm-cdm", "cdm-matter"] # Total-matter and CDM auto-spectra + CDM-total cross-spectrum + +Some additional specific options for the power-spectra outputs are described in the +following pages: + +* :ref:`Output_list_label` (to have PS not evenly spaced in time) + .. _Parameters_fof: diff --git a/doc/RTD/source/Planetary/equations_of_state.rst b/doc/RTD/source/Planetary/equations_of_state.rst index 58a374817f52ecee55fabdd0bccd3e7f1636c303..4b0d2dfd988234048350fccb22b7e7608e5c35bd 100644 --- a/doc/RTD/source/Planetary/equations_of_state.rst +++ b/doc/RTD/source/Planetary/equations_of_state.rst @@ -1,28 +1,28 @@ .. Planetary EoS - Jacob Kegerreis, 13th March 2020 + Jacob Kegerreis, 14th July 2022 .. _planetary_eos: Planetary Equations of State ============================ -Configuring SWIFT with the ``--with-equation-of-state=planetary`` and -``--with-hydro=planetary`` options enables the use of multiple +Configuring SWIFT with the ``--with-equation-of-state=planetary`` and +``--with-hydro=planetary`` options enables the use of multiple equations of state (EoS). -Every SPH particle then requires and carries the additional ``MaterialID`` flag -from the initial conditions file. This flag indicates the particle's material -and which EoS it should use. +Every SPH particle then requires and carries the additional ``MaterialID`` flag +from the initial conditions file. This flag indicates the particle's material +and which EoS it should use. -It is important to check that the EoS you use are appropriate +It is important to check that the EoS you use are appropriate for the conditions in the simulation that you run. -Please follow the original sources of these EoS for more information and +Please follow the original sources of these EoS for more information and to check the regions of validity. If an EoS sets particles to have a pressure of zero, then particles may end up overlapping, especially if the gravitational softening is very small. -So far, we have implemented several Tillotson, ANEOS, SESAME, +So far, we have implemented several Tillotson, ANEOS, SESAME, and Hubbard \& MacFarlane (1980) materials, with more on the way. -The material's ID is set by a somewhat arbitrary base type ID +The material's ID is set by a somewhat arbitrary base type ID (multiplied by 100) plus an individual value: + Ideal gas: ``0`` @@ -45,32 +45,56 @@ The material's ID is set by a somewhat arbitrary base type ID + Forsterite (Stewart et al. 2019): ``400`` + Iron (Stewart, zenodo.org/record/3866507): ``401`` + Fe85Si15 (Stewart, zenodo.org/record/3866550): ``402`` - -The data files for the tabulated EoS can be downloaded using + +The data files for the tabulated EoS can be downloaded using the ``examples/Planetary/EoSTables/get_eos_tables.sh`` script. To enable one or multiple EoS, the corresponding ``planetary_use_*:`` flag(s) must be set to ``1`` in the parameter file for a simulation, -along with the path to any table files, which are set by the +along with the path to any table files, which are set by the ``planetary_*_table_file:`` parameters, as detailed in :ref:`Parameters_eos` and ``examples/parameter_example.yml``. -The data files for the tabulated EoS can be downloaded using -the ``examples/EoSTables/get_eos_tables.sh`` script. - -Unlike the EoS for an ideal or isothermal gas, these more complicated materials -do not always include transformations between the internal energy, -temperature, and entropy. At the moment, we have implemented -\\(P(\\rho, u)\\) and \\(c_s(\\rho, u)\\), -which is sufficient for the :ref:`planetary_sph` hydro scheme, -but makes most materials currently incompatible with e.g. entropy-based schemes. +Unlike the EoS for an ideal or isothermal gas, these more complicated materials +do not always include transformations between the internal energy, +temperature, and entropy. At the moment, we have implemented +\\(P(\\rho, u)\\) and \\(c_s(\\rho, u)\\) (and more in some cases), +which is sufficient for the :ref:`planetary_sph` hydro scheme, +but some materials may thus currently be incompatible with +e.g. entropy-based schemes. -The Tillotson sound speed was derived using +The Tillotson sound speed was derived using \\(c_s^2 = \\left. ( \\partial P / \\partial \\rho ) \\right|_S \\) -as described in -`Kegerreis et al. (2019) <https://doi.org/10.1093/mnras/stz1606>`_. +as described in +`Kegerreis et al. (2019) <https://doi.org/10.1093/mnras/stz1606>`_. Note that there is a typo in the sign of \\(du = T dS - P dV = T dS + (P / \\rho^2) d\\rho \\) in the appendix; -the correct version was used in the derivation. +the correct version was used in the actual derivation. The ideal gas uses the same equations detailed in :ref:`equation_of_state`. + +The data files for the tabulated EoS can be downloaded using +the ``examples/EoSTables/get_eos_tables.sh`` script. + +The format of the data files for SESAME, ANEOS, and similar-EoS tables +is similar to the SESAME 301 (etc) style. The file contents are: + +.. code-block:: python + + # header (12 lines) + version_date (YYYYMMDD) + num_rho num_T + rho[0] rho[1] ... rho[num_rho] (kg/m^3) + T[0] T[1] ... T[num_T] (K) + u[0, 0] P[0, 0] c[0, 0] s[0, 0] (J/kg, Pa, m/s, J/K/kg) + u[1, 0] ... ... ... + ... ... ... ... + u[num_rho-1, 0] ... ... ... + u[0, 1] ... ... ... + ... ... ... ... + u[num_rho-1, num_T-1] ... ... s[num_rho-1, num_T-1] + +The ``version_date`` must match the value in the ``sesame.h`` ``SESAME_params`` +objects, so we can ensure that any version updates work with the git repository. +The header contains a first line that gives the material name, followed by the +same 11 lines printed here to describe the contents. diff --git a/doc/RTD/source/Planetary/hydro_scheme.rst b/doc/RTD/source/Planetary/hydro_scheme.rst index ae920722c1fff88d23bf26ed85d534b05d1e6a42..f0c787dce100142ecbe9b650fbee4d2110f34676 100644 --- a/doc/RTD/source/Planetary/hydro_scheme.rst +++ b/doc/RTD/source/Planetary/hydro_scheme.rst @@ -6,12 +6,15 @@ Planetary Hydro Scheme ====================== -This scheme is based on :ref:`minimal` but also allows multiple materials, -meaning that different SPH particles can be assigned different -`equations of state <equations_of_state.html>`_ (EoS). -Every SPH particle then requires and carries the additional ``MaterialID`` flag -from the initial conditions file. This flag indicates the particle's material -and which EoS it should use. +This scheme is based on :ref:`minimal` but also allows multiple materials, +meaning that different SPH particles can be assigned different +`equations of state <equations_of_state.html>`_ (EoS). +Every SPH particle then requires and carries the additional ``MaterialID`` flag +from the initial conditions file. This flag indicates the particle's material +and which EoS it should use. -The Balsara viscosity switch is used by default, but can be disabled by +The Balsara viscosity switch is used by default, but can be disabled by compiling SWIFT with ``make CFLAGS=-DPLANETARY_SPH_NO_BALSARA``. + +Note: the boundary-improvement methods presented in Ruiz-Bonilla+2022 are +in the process of being improved for release with better documentation etc soon. diff --git a/doc/RTD/source/Planetary/index.rst b/doc/RTD/source/Planetary/index.rst index f3d2134ea3537166cfe9167e6e6a6f31b9cca861..b851bc51144702511c31aea9f89c58e3e15ea3fa 100644 --- a/doc/RTD/source/Planetary/index.rst +++ b/doc/RTD/source/Planetary/index.rst @@ -1,25 +1,26 @@ .. Planetary Simulations - Jacob Kegerreis, 3rd October 2020 + Jacob Kegerreis, 14th July 2022 .. _planetary: - + Planetary Simulations ===================== SWIFT is also designed for running planetary simulations -with a current focus on giant impacts, as introduced in -`Kegerreis et al. (2019) <https://doi.org/10.1093/mnras/stz1606>`_, MNRAS 487:4. +such as of giant impacts, as introduced in Kegerreis+2019, +and any other types of simulations with more complicated equations of state +and/or multiple materials, etc. -Active development is ongoing of more features and examples for planetary -simulations, so please let us know if you are interested in using SWIFT -or have any implementation requests. +Active development is ongoing of more features and examples, +so please let us know if you are interested in using SWIFT +or have any implementation requests. You can find an example simulation in ``swiftsim/examples/Planetary/`` -under ``EarthImpact/``, as well as some hydro tests for comparison with other -schemes. The tabulated equation of state files can be downloaded using +under ``EarthImpact/``, as well as some hydro tests for comparison with other +schemes. The tabulated equation of state files can be downloaded using ``EoSTables/get_eos_tables.sh``. -Planetary simulations are currently intended to be run with +Planetary simulations are currently intended to be run with SWIFT configured to use the planetary hydrodynamics scheme and equations of state: ``--with-hydro=planetary`` and ``--with-equation-of-state=planetary``. These allow for multiple materials to be used, @@ -28,7 +29,7 @@ chosen from the several available equations of state. .. toctree:: :maxdepth: 1 :caption: More information: - + Hydro Scheme <hydro_scheme> Equations of State <equations_of_state> Initial Conditions <initial_conditions> diff --git a/doc/RTD/source/Planetary/initial_conditions.rst b/doc/RTD/source/Planetary/initial_conditions.rst index fd348a9fd134fca3a35be4a7eeb16669ea56534a..aad89d5060f0ea72864f5fc91c2b69d7ef5cff52 100755 --- a/doc/RTD/source/Planetary/initial_conditions.rst +++ b/doc/RTD/source/Planetary/initial_conditions.rst @@ -1,23 +1,22 @@ .. Planetary Initial Conditions - Jacob Kegerreis, 13th March 2020 + Jacob Kegerreis, 14th July 2022 .. _planetary_initial_conditions: - + Initial Conditions ================== -Tools for the creation of initial conditions are available via -the -`WoMa <https://github.com/srbonilla/WoMa>`_ and -`SEAGen <https://github.com/jkeger/seagen>`_ open-source python packages, +Tools for the creation of initial conditions are available via the +`WoMa <https://github.com/srbonilla/WoMa>`_ and +`SEAGen <https://github.com/jkeger/seagen>`_ open-source python packages, including: creating spherical or spinning planetary (or similar) profiles; placing particles to match arbitrary profiles with precise SPH densities; and setting the initial target and impactor positions and velocities, -as presented in +as presented in `Kegerreis et al. (2019) <https://doi.org/10.1093/mnras/stz1606>`_ and -Ruiz-Bonilla et al. (2020). +`Ruiz-Bonilla et al. (2020) <https://doi.org/10.1093/mnras/staa3385>`_ . -They are available with documentation and examples at +They are available with documentation and examples at https://github.com/srbonilla/WoMa and https://github.com/jkeger/seagen, or can be installed directly with ``pip`` (https://pypi.org/project/woma/, https://pypi.org/project/seagen/). @@ -26,13 +25,13 @@ or can be installed directly with ``pip`` Settling initial conditions with fixed entropies ------------------------------------------------ -If the particles' equations of state include specific entropies, +If the particles' equations of state include specific entropies, and the initial conditions file includes specific entropies for each particle -(in ``PartType0/Entropies``), +(in ``PartType0/Entropies``), then configuring SWIFT with ``--enable-planetary-fixed-entropy`` -will override the internal energy of each particle each step such that its -specific entropy remains constant. +will override the internal energy of each particle each step such that its +specific entropy remains constant. -This should be used with caution, but may be a convenient way to maintain an -entropy profile while initial conditions settle to equilibrium with their +This should be used with caution, but may be a convenient way to maintain an +entropy profile while initial conditions settle to equilibrium with their slightly different SPH densities. diff --git a/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst b/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst index 8187d3436eaed5bcbed70ab5230fbb9a2878b679..7fde36f0968b4b6e3b1515b99d3c5078c3affc83 100644 --- a/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst +++ b/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst @@ -22,51 +22,70 @@ Compiling for GEAR RT - You need to choose a Riemann solver for the RT equations. You can choose between the ``GLF`` and ``HLL`` solver. For the time being, I recommend sticking to the ``GLF`` solver as the ``HLL`` solver is more expensive, - but seemingly offers no advantage, although this remains to be comfirmed + but seemingly offers no advantage, although this remains to be confirmed in further testing. - GEAR RT is only compatible with the Meshless Finite Volume scheme. You'll need to compile using ``--with-hydro=gizmo-mfv``, which will also require you to select a hydro Riemann solver, e.g ``--with-riemann-solver=hllc``. - +- The thermochemistry requires the `grackle <https://github.com/grackle-project/grackle>`_ + library version 3.2. Grackle is a chemistry and cooling library presented in + `B. Smith et al. 2017 <https://ui.adsabs.harvard.edu/abs/2017MNRAS.466.2217S>`_. + Please note that the current implementation is not (yet) as + advanced as the :ref:`GEAR subgrid model grackle cooling <gear_grackle_cooling>`, + and the parameters listed as available there are not applicable for the + grackle cooling in combination with GEAR RT. You can however follow the Grackle + installation instructions documented there. Compulsory Runtime Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You need to provide the following runtime parameters in the yaml file: +You need to provide the following runtime parameters in the yaml file (which will +be further explained below): .. code:: yaml GEARRT: photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz - use_const_emission_rates: 1 - star_emission_rates_LSol: [1., 1., 1., 1.] # stellar emission rates for each photon - # frequency bin in units of solar luminosity + stellar_spectrum_type: 0 # Which radiation spectrum to use. + # 0: constant. + # 1: blackbody spectrum. + stellar_luminosity_model: const # Which luminosity model to use. + const_stellar_luminosities_LSol: [1., 1., 1.] # stellar emission rates for each photon + # frequency bin in units of solar luminosity + # for the 'const' luminosity model f_reduce_c: 1e-3 # reduce the speed of light by this factor CFL_condition: 0.9 # CFL condition for time integration hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the # metal-free portion of the gas - stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant. 1: blackbody spectrum. -The ``photon_groups`` need to be ``N - 1`` frequency edges (floats) to separate -the spectrum into ``N`` groups. The outer limits of zero and infinity are -assumed. + TimeIntegration: + max_nr_rt_subcycles: 128 # maximal number of RT subcycles per hydro step -At the moment, the only way to define star emission rates is to use constant -star emission rates that need to be provided in the parameter file. The star -emission rates need to be defined for each photon frequency group individually. -The first entry of the array is for the photon group with frequency -``[0, <first entry of photon_groups_Hz>)``. Each star particle will then emit -the given energies, independent of their other properties. -Furthermore, even though the parameter ``use_const_emission_rates`` is -intended to be optional in the future, **for now it needs to be set to 1.**, and -it requires you to manually set the stellar emission rates via the -``star_emission_rates_LSol`` parameter. +The ``photon_groups_Hz`` need to be ``N`` frequency edges (floats) to separate +the spectrum into ``N`` groups, where ``N`` is the same number you configured +with using ``--with_rt=GEAR_N``. The edges are **lower** edges of the bins, and +need to be sorted in increasing order. The final upper edge is defined in a +different manner, and depends on the stellar spectrum type you assume (see below +for more details). + +To specify the radiation emitted by stars, there are two main parameters: +``stellar_luminosity_model`` defines which model to use to obtain star +luminosities, while ``stellar_spectrum_type`` determines the spectrum of the +radiation. +At the moment, the only way to define star emission rates is to use constant +stellar luminosities by setting ``stellar_luminosity_model: const``. [#f3]_ +The constant star emission rates need to be provided in the parameter file and +to be defined for each photon frequency group individually using the +``const_stellar_luminosities_LSol`` parameter. The luminosities are expected to +be in units of solar luminosities. Each star particle will then emit the given +luminosities, independent of their other properties, e.g. the stellar age, +metallicity, redshift, etc. When solving the thermochemistry, we need to assume some form of stellar spectrum so we may integrate over frequency bins to obtain average interaction @@ -74,18 +93,106 @@ rates. The parameter ``stellar_spectrum_type`` is hence required, and allows you to select between: - constant spectrum (``stellar_spectrum_type: 0``) + - Assume same energy density for any frequency. - This choice additionally requires you to provide a maximal frequency for the spectrum after which it'll be cut off via the ``stellar_spectrum_const_max_frequency_Hz`` parameter - blackbody spectrum (``stellar_spectrum_type: 1``) + - Assume the spectrum is a blackbody spectrum - In this case, you need to provide also temperature of the blackbody via the ``stellar_spectrum_blackbody_temperature_K`` parameter. + - The assumed maximal considered frequency :math:`\nu_{max}` for this spectrum + is equal to 10 times :math:`\nu_{peak}`, the frequency at which the blackbody + spectrum has its maximum, i.e. + +.. math:: + + \nu_{peak} = 2.82144 \times k_{B} \times T / h_{Planck} + + \nu_{max} = 10 \times \nu_{peak} + + +.. warning:: + The ``stellar_spectrum_type`` parameter also determines the averaged photon + interaction cross sections, as they are being computed by integrating a + parametrization of the cross section multiplied by the assumed spectrum. See + e.g. equations 9 - 11 in `Rosdahl et al. 2013. + <https://ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R/abstract>`_ + +Finally, you will also need to provide an upper threshold for the number of +RT-subcycles w.r.t. a single hydro step via ``TimeIntegration:max_nr_rt_subcycles``. +For more details, refer to :ref:`the subcycling documentation <rt_subcycling>`. + + + + + + +Optional Runtime Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are several optional parameters which can be set in the ``GEARRT`` block in the +yaml parameter file: + +- ``f_limit_cooling_time``: (Default: ``0.6``) The cooling time computed by grackle + will be multipled by this factor when estimating the particle's RT time step + size. If set to ``0.0``, the computation of cooling time is turned off. +- ``skip_thermochemistry``: (Default: ``0``) If set to ``1``, the entire + thermochemistry part of radiative transfer will be skipped. This is intended + only for debugging and testing the radiation transport, as it breaks the + purpose of RT. +- ``stars_max_timestep``: (Default: ``-1.``) Restrict the maximal timestep of stars + to this value (in internal units). Set to negative to turn off. +- ``grackle_verbose``: (Default: ``0``) set grackle to verbose. +- ``case_B_recombination``: (Default: ``1``) If ``1``, use case B recombination rates. + Otherwise, case A recombination rates are used. +- ``max_tchem_recursion``: (Default: ``0``) If set to positive nonzero value, the + thermochemistry is computed using a "10% rule" *in addition to the ones grackle + already uses*. If during a thermochemistry step the internal energy :math:``u`` of + the gas changes by more than 10%, i.e. :math:`|u_{new}/u_{old} - 1| > 0.1`, then + the thermochemistry step is repeated twice using half the time step size. This + parameter sets the maximal recursion depth of halving the time step size and + repeating the entire thermochemistry step. + +There are some further optional parameters related to setting up initial ion mass +fractions which are detailed in the +:ref:`corresponding section of this documentation <rt_GEAR_set_ion_mass_fractions>`. + + + + + + + + + + +Choice of Internal Units +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The choice of internal units requires a bit of special attention. Part of the +reason is that the exponents of the gas and radiation variables can quickly +change by several dozens and cause overflows and other errors. Furthermore, the +grackle library may have some other troubles with the units, e.g. when trying to +find a converging solution. [#f2]_ + +For this reason, I **strongly encourage** you to run the Internal Units check for +GEAR-RT which you can find in the +`swiftsim-rt-tools <https://github.com/SWIFTSIM/swiftsim-rt-tools/GEARRTUnitCheck>`_ +repository under ``/GEARRTUnitsCheck``. The test should take no more than a +minute to run, and requires only two yaml parameter files: the yaml parameter +file that you intend to run your simulation with, and one that a provided script +can extract automatically from the initial conditions hdf5 file. This test can +save you a lot of headaches down the line. + + + + Initial Conditions ~~~~~~~~~~~~~~~~~~ - Setting Up Initial Conditions for RT ```````````````````````````````````` @@ -115,16 +222,18 @@ group: MassFractionHeIII -The ``PhotonEnergies*`` datasets need to have dimension ``nparts``, while the -``PhotonFluxesGroup*`` datasets need to have dimension ``(nparts, 3)``, where -``nparts`` is the number of hydro particles. If you are writing initial -conditions where the fields have units, then ``PhotonEnergies*`` are expected to -have units of energy :math:`[M L^2 T^{-2}]`), while the ``PhotonFluxes*`` fields -should be in units of energy flux (energy per unit time per unit area, :math:`[M -T^{-3}]`). -The ``MassFraction*`` datasets need to have dimension ``nparts`` as well, and -are all unitless. - +- The ``PhotonEnergies*`` datasets need to have dimension ``nparts``, while the + ``PhotonFluxesGroup*`` datasets need to have dimension ``(nparts, 3)``, where + ``nparts`` is the number of hydro particles. +- Note that the GEAR-RT scheme expects the ``PhotonEnergies*`` to be total + energies, not energy densities. +- If you are writing initial conditions where the fields have units [#f1]_, then + ``PhotonEnergies*`` are expected to have units of energy + :math:`[M L^2 T^{-2}]`), while the ``PhotonFluxes*`` fields should be in units + of energy times velocity (i.e. energy per unit time per unit area times volume, + :math:`[M L^3 T^{-3}]`). +- The ``MassFraction*`` datasets need to have dimension ``nparts`` as well, and + are all unitless. Example using Python and ``swiftsimio`` @@ -197,6 +306,8 @@ Here is an example: +.. _rt_GEAR_set_ion_mass_fractions: + Generate Ionization Mass Fractions Using SWIFT `````````````````````````````````````````````` @@ -233,3 +344,127 @@ for you assuming ionization equilibrium, following `Katz, et al. 1996 The ``hydrogen_mass_fraction`` (which is a compulsory argument in any case) will determine the hydrogen and helium mass fractions, while SWIFT will determine the equilibrium ionizations. + +.. warning:: If you have somewhat sophisticated initial conditions (e.g. proper galaxies + etc) it is strongly recommended to set up the initial mass fractions to equilibrium + values. Otherwise, the initial states can be too far off for GRACKLE's thermochemistry + to handle it well, leading to all sorts of troubles, including crashes. + + + + +Accessing Output Data +~~~~~~~~~~~~~~~~~~~~~~ + +We recommend using `swiftsimio <https://github.com/SWIFTSIM/swiftsimio>`_ to +access the RT related snapshot data. The compatibility is being maintained. +Here's an example how to access some specific quantities that you might find +useful: + + +.. code:: python + + #!/usr/bin/env python3 + + import swiftsimio + import unyt + + data = swiftsimio.load("output_0001.hdf5") + meta = data.metadata + + + + # Accessing RT Related Metadata + # --------------------------------- + + # get scheme name: "GEAR M1closure" + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + # number of photon groups used + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + + # get the reduced speed of light that was used. Will have unyts. + reduced_speed_of_light = meta.reduced_lightspeed + + + + + # Accessing Photon Data + # ------------------------ + + # accessing a photon group directly + # NOTE: group names start with 1 + group_1_photon_energies = data.gas.photon_energies.group1 + group_1_photon_fluxes_x = data.gas.photon_fluxes.Group1X + group_1_photon_fluxes_y = data.gas.photon_fluxes.Group1Y + group_1_photon_fluxes_z = data.gas.photon_fluxes.Group1Z + + # want to stack all fluxes into 1 array? + group1fluxes = swiftsimio.cosmo_array( + unyt.uvstack( + (group_1_photon_fluxes_x, group_1_photon_fluxes_y, group_1_photon_fluxes_z) + ), + group_1_photon_fluxes_x.units, + ).T + # group1fluxes.shape = (npart, 3) + + + # Load all photon energies in a list + photon_energies = [ + getattr(data.gas.photon_energies, "group" + str(g + 1)) for g in range(ngroups) + ] + + + + # Accessing Ion Mass Fractions + # ------------------------------- + fHI = data.gas.ion_mass_fractions.HI + fHII = data.gas.ion_mass_fractions.HII + fHeI = data.gas.ion_mass_fractions.HeI + fHeII = data.gas.ion_mass_fractions.HeII + fHeIII = data.gas.ion_mass_fractions.HeIII + + + + +.. rubric:: Footnotes + +.. [#f1] To avoid possible confusions, here are some notes and equations + regarding this choice of units. + + One of the RT equations solved by the GEAR RT is the zeroth moment of the + equation of radiative transfer for each photon frequency group :math:`i` : + + :math:`\frac{\partial E_i}{\partial t} + \nabla \cdot \mathbf{F}_i = 0` + + where + + - :math:`E_i` : photon energy density; with :math:`[E_i] = erg / cm^3 = M L^{-1} T^{-2}` + - :math:`F_i` : radiation flux (energy per unit time per unit surface); with :math:`[F_i] = erg / cm^2 / s = M T^{-3}` + + and we neglect possible source and sink terms in this footnote. + + These dimensions are also used internally when solving the equations. + For the initial conditions however, we require these quantities multiplied by + the particle volume. The reason for this choice is so that the photon + energies for each particle can be set by the users exactly, while the + particle volume computation can be left to SWIFT to worry about internally. + The addition of the particle volume term for the radiation flux was made so + that the initial conditions are compatible with the SPHM1RT conventions, and + both methods can run on the exact same ICs. + + +.. [#f2] For example, choosing cgs units as the internal units may lead to + trouble with grackle. (Trouble like a gas at 10^6K without any heating + sources heating up instead of cooling down.) The library is set up to work + with units geared towards cosmology. According to Britton Smith (private comm), + a decent rule of thumb is density_units ~ proton mass in g, time_units ~ 1 Myr + to 1 Gyr in s, length_units ~ 1 kpc to 1 Mpc in cm. This should keep you in a + relatively safe range. + This is the state of things at 08.2022, with grackle being at version 3.2 (commit + ``a089c837b8649c97b53ed3c51c84b1decf5073d8``) + +.. [#f3] Technically there is also the model used for "Test 4" from the + `I. Iliev et al. 2006 <https://ui.adsabs.harvard.edu/abs/2006MNRAS.369.1625I>`_ + paper, but that is very specialized and shouldn't have much use in real + applications. diff --git a/doc/RTD/source/RadiativeTransfer/RTTaskDependenciesFull-simplified.png b/doc/RTD/source/RadiativeTransfer/RTTaskDependenciesFull-simplified.png new file mode 100644 index 0000000000000000000000000000000000000000..349ab164179b4fe7c8ea240f881e9d821869647a Binary files /dev/null and b/doc/RTD/source/RadiativeTransfer/RTTaskDependenciesFull-simplified.png differ diff --git a/doc/RTD/source/RadiativeTransfer/RTWorkflow.png b/doc/RTD/source/RadiativeTransfer/RTWorkflow.png new file mode 100644 index 0000000000000000000000000000000000000000..f3956de04d3313e46deff26901f0840255caa302 Binary files /dev/null and b/doc/RTD/source/RadiativeTransfer/RTWorkflow.png differ diff --git a/doc/RTD/source/RadiativeTransfer/RT_notes_for_developers.rst b/doc/RTD/source/RadiativeTransfer/RT_notes_for_developers.rst new file mode 100644 index 0000000000000000000000000000000000000000..70a5a439ebe6ebad44c94b0904af9f5c4d5e321c --- /dev/null +++ b/doc/RTD/source/RadiativeTransfer/RT_notes_for_developers.rst @@ -0,0 +1,490 @@ +.. RT developer notes + Mladen Ivkovic 07.2022 + +.. _rt_dev: + +Notes for Developers +======================== + + +.. _rt_workflow: + +The Radiative Transfer Workflow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section is intended to give a superficial overview on the general workflow +that the RT tasks are going through. It should be sufficient to give you a general +idea of how things work, and allow you to plan and implement your own RT scheme +into SWIFT. + +For a more complete documentation on the tasking system used for RT, please refer +to the :ref:`subsequent section <rt_task_system>`. + +.. figure:: RTWorkflow.png + :width: 400px + :align: center + :figclass: align-center + :alt: Workflow for the Radiative Transfer in SWIFT. + + This figure shows the general RT workflow in SWIFT. + The green nodes are the ones related to the RT. + + +- There are some additions to star tasks: + + 1) During the star density loop, we gather additional neighbour information + that are required for the star-gas interaction. + + 2) In the star ghost tasks, stars compute how much radiation they will + inject in the surrounding gas. The radiation injection scheme follows + the philosophy of the stellar feedback, and only stars that are + currently active will inject radiation into neighbouring gas particles, + regardless of whether the gas particle is currently active (as opposed + to active gas particles "pulling" radiation from possibly inactive + stars). So the each star needs to compute how much radiation it will + emit during its current time step. + + 3) During the stellar feedback interaction loop, the stars inject radiation + onto neighbouring gas particles. + +- ``Ghost1`` tasks operate on individual particles, and are intended to finish up + any leftover work from the injection. + +- ``Gradient`` tasks are particle-particle interaction tasks, intended for + particles to gather data from its own neighbours, e.g. so we can estimate the + current local gradients. This is an interaction of "type 1", meaning that any + particle will only interact with neighbours which are within its own compact + support radius. + +- ``Ghost2`` tasks operate on individual particles, and are intended to finish + up any leftover work from the "gradients". + +- ``Transport`` tasks are particle-particle interaction tasks, intended to + propagate the radiation. This is an interaction of "type 2", meaning that any + particle will interact with any neighbours within either particles' compact + support radius. + +- ``thermochemistry`` tasks operate on individual particles, and are intended + to solve the thermochemistry equations. + + + + + + + +.. _rt_task_system: + +Current Task System +~~~~~~~~~~~~~~~~~~~~ + +Some RT tasks featured in the full task graphs below, like the +``rt_advance_cell_time``, ``rt_collect_times``, and ``rt_sorts``, have not been +mentioned in the previous section. They are necessary for internal machinations +of the RT subcycling scheme, and do not affect the RT scheme itself. If you are +implementing a new RT scheme into SWIFT, you should not need to touch those +tasks. For more documentation on them, please refer to the :ref:`subsequent +section <rt_subcycling_documentation>`. + + +.. figure:: RTTaskDependenciesFull-simplified.png + :width: 400px + :align: center + :figclass: align-center + :alt: Task dependencies for the sink scheme. + + This figure shows the task dependencies for the radiative transfer scheme. + Some tasks with little or no relevance to the RT scheme have been simplified + and condensed for clarity. + This was done with SWIFT v0.9.0. + + +.. figure:: full_dependency_graph_RT.png + :width: 400px + :align: center + :figclass: align-center + :alt: Task dependencies for the sink scheme. + + This figure shows the full task dependencies for the radiative transfer scheme + with self-gravity. + This was done with SWIFT v0.9.0. + + + + + + +.. _rt_subcycling_documentation: + +Notes on Subcycling +~~~~~~~~~~~~~~~~~~~~~ + +Note: This section is directed towards developers and maintainers, not +necessarily towards users. + +How it works +````````````````` + +A subcycle is basically a SWIFT time step where only radiative transfer is being +run. + +After a normal SWIFT time step (i.e. after a call to ``engine_launch()`` and the +global collection and communication) is complete, the starting time of the +following global time step is known. We also collect the current minimal RT time +step size, which allows us to determine how many sub-cycles we need to complete +before the next normal SWIFT time step is launched. Particles are not drifted +during a subcycle, and the propagation velocity (aka the speed of light) is +taken to be constant, so the number of subcycles is fixed at the end of a normal +step. For each subcycle, we then unskip the RT tasks, and make a new call to +``engine_launch()``. + +For the time integration to work correctly, the time integration variables of +particles like the time-bins are kept independently from the hydro ones. The same +goes for the respective quantities of cells, like the next integer end time of +the cell, or the minimal RT time step size in the cell. Furthermore, the global +time variables that are stored in the engine (e.g. current integer time, current +max active bin...) have a copy that is being kept up-to-date outside of normal +SWIFT steps in the same manner the non-RT variables are being updated each +normal step. The RT subcycling scheme never touches or changes any global time +integration related variable. + +Since the time stepping variables of particles and cells are taken to be +constant during subcycles (because there are no drifts, constant speed of +light), the ``timestep`` tasks are not being run during a sub-cycle. This +effectively means that the particle time bins can only be changed in a normal +step when the particle is also hydro-active. Furthermore, there are no MPI +communications after the tasks have finished executing to update any global +times etc. for the same reason. There are some functionalities of the +``timestep`` and the ``collect`` tasks which are still necessary though: + +- The ``timestep`` task also updates the cell's next integer end time after it + has been determined during the task. During a subcycle, the next end time is + simply the current time plus the minimal time step size of the cell, but we + need a task to actually update the cell at the end of each subcycle. The + ``rt_advance_cell_time`` task does exactly that, and in that sense does the + ``timestep`` task's job during subcycles. + +- The ``collect`` task propagates sub-cell data like the minimal end time or the + RT time step size from the super level to the top level. This functionality is + replaced with the ``rt_collect_times`` tasks during subcycles. Note that the + ``rt_collect_times`` tasks aren't being activated during normal steps, as the + ``collect`` tasks already do the job just fine. + +Something special about the ``rt_advance_cell_time`` tasks is that they are +also created and run on foreign cells. During a subcycle, the ``tend`` tasks +don't run and don't update the cell time variables from the original cell, so +during the subsequent unskipping, the data will be wrong, leading to all sorts +of trouble. We can do that on foreign cells during sub-cycles because all the +cell's time step sizes stay fixed between two regular SWIFT steps, and hence +the number of sub-cycles all the sub-cycles' end times are predictable. + + + + +RT Sorts +```````````````` + +The sorting of particles required for pair-type interaction tasks requires some +special attention. The issues arise because a subcycle step of a cell can +coincide with the main step of another cell. To illustrate, suppose we have two +cells, ``A`` and ``B``. Let cell ``A`` have a hydro time step of size 4, and +cell ``B`` a hydro time step of size 8. Let both cells do 2 RT subcycles per +hydro step each. In the graph below, an ``X`` represents when a cell will be +updated: + +.. code:: + + Cell A + Hydro active: X X X X X + RT active: X X X X X X X X X X + + Cell B + Hydro active: X X X + RT active: X X X X X + + + ------------------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| + t 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + + +Note that e.g. at ``t`` = 4, cell ``B`` is only RT active, while cell ``A`` is +also hydro active. It being hydro active means that we will have a SWIFT main +step at that time. + +Now suppose cell cells ``A`` and ``B`` are neighbouring cells that undergo hydro +interactions, but are on different MPI ranks. For the hydro interactions in the +normal SWIFT step at ``t`` = 4, cell ``B`` will be sent over to the rank of +cell ``A``. Once it was received, it will be sorted, because after being +received, the ``recv_xv`` task resets the arrived cell's sort tasks, and a hydro +sort is activated. This is the default hydrodynamics workflow. + + + +Complications however arise when several conditions coincide: + +- a foreign cell has been drifted on its "home" domain due to some reason other + than hydro, e.g. for gravity or for stellar feedback. +- the foreign cell and all of its neighbours are not hydro active at the current + main step, so no ``recv_xv`` task has been run on the local rank, no ``sort`` + task has been run, and the sorting flags have not been reset for the cell. +- the foreign cell is involved in an RT interaction that coincides with the + current main step, i.e. the cell or one of its neighbours is RT active during + a main step. (E.g. like cell ``A`` at ``t`` = 2 in the graph above.) + +If these conditions are met, the cell will undergo an interaction while +unsorted. Obviously that's a no-no. + + +To illustrate this problem, consider the following scenario. Say we have cells ``A``, +``B``, and ``C``. Cell ``A`` is "home" on MPI rank 0, while cell ``B`` is on MPI rank 1. +In the current step in this scenario, cell ``A`` and ``B`` are both active for RT, and +interact with each other. ``C`` has an active star, which inject energy into particles of +``B``. Cell ``A`` and ``C`` do *not* interact with each other: + + +.. code:: + + rank 0 | rank 1 + | + RT interaction Star Feedback + A <----------|---> B <------------------ C + | + | + + +Since ``A`` and ``B`` interact, but are separated between different MPI ranks, both ``A`` +and ``B`` will have local copies of each other available on their local ranks, respectively. +These cells are referred to as "foreign cells". Let's denote them with an additional ``f``: + +.. code:: + + rank 0 | rank 1 + | + RT interaction | RT interaction Star Feedback + A <-------------> Bf | Af <-------------> B <------------------ C + ^ | ^ + (this is a foreign cell) | | | (this is a foreign cell) + + +Now let's first have a look at the default workflow when hydrodynamics is involved. +Assume that all cells ``A``, ``B``, and ``C`` are hydro *and* RT active for this step. +Once again cells ``A`` and ``B`` interact, and ``B`` and ``C`` interact, but ``A`` +and ``C`` *don't* interact. Cell ``C`` contains an active star particle. + +**Without** MPI, the order of operations for each cell would look like this: (operations +where two cells interact with each other are marked with an arrow) + + +.. code:: + + A B C + ---------------------------------------------------- + Drift Drift Drift + Sort Sort Sort + Density Loop <----> Density Loop <----> Density Loop + Ghost Ghost Ghost + Force Loop <------> Force Loop <------> Force Loop + End Force End Force End Force + Kick2 Kick2 Kick2 + + Star Drift + Star Sort + (inactive) <------> Star Density + Star Ghost + (inactive) <------> Star Feedback + + RT Ghost1 RT Ghost1 RT Ghost1 + RT Gradient <-----> RT Gradient <-----> RT Gradient + RT Ghost2 RT Ghost2 RT Ghost2 + RT Transport <----> RT Transport <----> RT Transport + RT Tchem RT Tchem RT Tchem + Timestep Timestep Timestep + Kick1 Kick1 Kick1 + + +Now **with** MPI communications, cells ``A`` and ``B`` need to send over the +up-to-date data to their foreign counterparts ``Af`` and ``Bf``, respectively, +*before* each interaction type task (the ones with arrows in the sketch above). +The order of operations should look like this: +(The foreign cell ``Af`` on rank 1 is omitted for clarity, but follows the same +principle as ``Bf`` on rank 0) + +.. code:: + + rank 0 | rank 1 + A Bf | B C + ----------------------------------------|---------------------------------------- + Drift | Drift Drift + Recv XV <------------ Send XV + Sort Sort | Sort Sort + Density Loop <----> Density Loop | Density Loop <----> Density Loop + Ghost | Ghost Ghost + Recv Density <-------- Send Density + Force Loop <------> Force Loop | Force Loop <------> Force Loop + End Force | End Force End Force + Kick2 | Kick2 Kick2 + | + | Star Drift + | Star Sort + | (inactive) <------> Star Density + | Star Ghost + | (inactive) <------> Star Feedback + | + RT Ghost1 | RT Ghost1 RT Ghost1 + Recv RT Gradient <---- Send RT Gradient + RT Gradient <-----> RT Gradient | RT Gradient <-----> RT Gradient + RT Ghost2 | RT Ghost2 RT Ghost2 + Recv RT Transport <--- Send RT Transport + RT Transport <----> RT Transport | RT Transport <----> RT Transport + RT Tchem | RT Tchem RT Tchem + Timestep | Timestep Timestep + Recv tend <----------- Send tend + Kick1 | Kick1 Kick1 + + +Finally, let's look at the scenario which causes problems with the sort. This +is the case, as described above, when (a) cells ``A`` and ``B`` are RT active during +a main step (like in the sketch above), (b) aren't hydro active during a main step +(unlike what is drawn above), (c) one of these cells is foreign (in this case, ``Bf``), +while the "home" cell (cell ``B``) get drifted during a main step for some reason +other than hydrodynamics, e.g. because a star interaction with cell ``C`` requested it. + +In this case, the workflow looks like this: + +.. code:: + + rank 0 | rank 1 + A Bf | B C + ----------------------------------------|---------------------------------------- + | Drift Drift + | Sort Sort + | Kick2 + | + | Star Drift + | Star Sort + | (inactive) <------> Star Density + | Star Ghost + | (inactive) <------> Star Feedback + | + RT Ghost1 | RT Ghost1 RT Ghost1 + Recv RT Gradient <---- Send RT Gradient + RT Gradient <-----> RT Gradient | RT Gradient <-----> RT Gradient + RT Ghost2 | RT Ghost2 RT Ghost2 + Recv RT Transport <--- Send RT Transport + RT Transport <----> RT Transport | RT Transport <----> RT Transport + RT Tchem | RT Tchem RT Tchem + Timestep | Timestep Timestep + Recv tend <----------- Send tend + Kick1 | Kick1 Kick1 + +The issue is that with the missing hydro communication tasks, the first communication +between cell ``B`` and its foreign counterpart ``Bf`` is the ``recv_rt_gradient`` task. +Recall that during a communication, we always send over all particle data of a cell. +This includes all the particle positions, which may have been updated during a drift. +However, the sorting information is not stored in particles, but in the cell itself. +For this reason, a ``sort`` task is *always* run directly after a cell finishes the +``recv_xv`` task, which, until the sub-cycling was added, was always the first task any +foreign cell would run, with a subsequent ``sort``. + +The RT sub-cycling now however allows the ``recv_xv`` task to not run at all +during a main step, since it's not always necessary, as shown in the example above. +All the required data for the RT interactions can be sent over with +``send/recv_rt_gradient`` tasks. An unintended consequence however is that in a +scenario as sketched above, at the time of the ``A <---> Bf`` RT Gradient +interaction, cell ``Bf`` will not be sorted. That's a problem. + +To solve this issue, a new task has been added, named ``rt_sorts``. It is only +required for foreign cells, like cell ``Bf``, and only during normal/main steps +(as we don't drift during subcycles, there won't be any reason to re-sort.) On +local cells, each time a drift is activated for an interaction type task, the +sort task is also activated. So there is no need for ``rt_sorts`` tasks on local +cells. +An additional advantage to adding a new sort task like the ``rt_sorts`` is that +it allows us to sidestep possible deadlocks. Suppose that as an alternative to +the ``rt_sort`` tasks we instead use the regular hydro ``sort`` task. The default +hydro ``sort`` task is set up to run before the other hydro tasks, and in +particular before the ``kick2`` task. However the RT and star tasks are executed +*after* the ``kick2``. This means that there are scenarios where a cell with a +foreign counterpart like cells ``B`` and ``Bf`` can deadlock when ``Bf`` is waiting +for the ``recv_rt_gradient`` to arrive so it may sort the data, while ``B`` is +waiting for ``Bf`` to finish the sorting and proceed past the ``kick2`` stage so +it can run the ``send_rt_gradient`` data which would allow ``Bf`` to run the +sorts. + + +The ``rt_sorts`` tasks are executed after the first RT related ``recv``, in this +case the ``recv_rt_gradient``. The order of operations should now look like this: + + +.. code:: + + rank 0 | rank 1 + A Bf | B C + ----------------------------------------|---------------------------------------- + | Drift Drift + | Sort Sort + | Kick2 + | + | Star Drift + | Star Sort + | (inactive) <------> Star Density + | Star Ghost + | (inactive) <------> Star Feedback + | + RT Ghost1 | RT Ghost1 RT Ghost1 + Recv RT Gradient <---- Send RT Gradient + rt_sort | + RT Gradient <-----> RT Gradient | RT Gradient <-----> RT Gradient + RT Ghost2 | RT Ghost2 RT Ghost2 + Recv RT Transport <--- Send RT Transport + RT Transport <----> RT Transport | RT Transport <----> RT Transport + RT Tchem | RT Tchem RT Tchem + Timestep | Timestep Timestep + Recv tend <----------- Send tend + Kick1 | Kick1 Kick1 + + + +In order to minimize unnecessary work, three new cell flags concerning the RT +sorts have been added: + +- ``cell_flag_do_rt_sub_sort``: tracks whether we need an RT sub sort, which is + equivalent to the ``cell_flag_do_sub_sort`` flag for hydro. We can't use the + hydro flag though because the hydro flag is also used to early-exit walking up + the cell hierarchy when activating hydro subcell sorts. In particular, this + condition in ``cell_unskip.c:cell_activate_hydro_sorts_up():`` + +.. code:: + + void cell_activate_hydro_sorts_up(struct cell *c, struct scheduler *s) { + /* omitted lines */ + + for (struct cell *parent = c->parent; + parent != null && !cell_get_flag(parent, cell_flag_do_hydro_sub_sort); parent = parent->parent) { + /* !! this is the problem ---^ */ + + /* omitted lines */ + } + } + +The sort activation for RT and for hydro can run concurrently. So there is no +guarantee that when the hydro sort activation sets the +``cell_flag_do_hydro_sub_sort`` flag, the RT sorting tasks will be activated +correctly, which occurs at the top of the cell hierarchy tree walk. +So we need an independent flag for RT here to not abort the tree walk early +and in error. + +- ``cell_flag_do_rt_sort``: tracks whether the call to the + ``runner_do_hydro_sort()`` function was requested by an RT sort. (Both the (hydro) + ``sort`` and the ``rt_sort`` tasks call the same function.) This flag is used to + discriminate during the actual sorting in the ``runner_do_hydro_sort()`` + function if the internal check whether the cell is drifted to the current time + may be disregarded. When an RT subcycle coincides with a main step, the particles + won't necessarily be drifted to the current time as there is no need to drift them + for RT only. This is intended behaviour, so we allow ``runner_do_hydro_sort()`` to + skip this drift check in the case where the sorting was requested for RT purposes. + +- ``cell_flag_skip_rt_sort``: Tracks whether a regular hydro sort has been + activated for this cell. If it has, then there is no need to run an RT sort as + well, and we skip it. + diff --git a/doc/RTD/source/RadiativeTransfer/RT_subcycling.rst b/doc/RTD/source/RadiativeTransfer/RT_subcycling.rst new file mode 100644 index 0000000000000000000000000000000000000000..c8e3f546b2b57030d91e2fde0a8604be4bd8534d --- /dev/null +++ b/doc/RTD/source/RadiativeTransfer/RT_subcycling.rst @@ -0,0 +1,45 @@ +.. RT Subcycling + Mladen Ivkovic 07.2022 + +.. _rt_subcycling: + +RT Subcycling +------------- + +.. warning:: + The radiative transfer schemes are still in development and are not useable + at this moment. This page is currently a placeholder to document new + features and requirements as the code grows. + + +SWIFT allows to sub-cycle the solution of radiative transfer steps (both +photon propagation and thermochemistry) with respect to the hydrodynamics +time steps. Basically you can tell SWIFT to run up to X radiative transfer +steps during a single hydrodynamics step for all particles in the simulation. +The aim is to not waste time doing unnecessary hydrodynamics updates, which +typically allow for much higher time steps compared to radiation due to the +propagation speed of the respective advected quantity. + +You will need to provide an upper limit on how many RT subcycles per hydro +step you want to allow. That is governed by the + +.. code:: yaml + + TimeIntegration: + max_nr_rt_subcycles: 128 # maximal number of RT subcycles per hydro step + +parameter, which is mandatory for any RT runs. To turn off subcycling and +couple the radiative transfer and the hydrodynamics time steps one-to-one, +set this parameter to either 0 or 1. + +Due to the discretization of individual particle time steps in time bins +with a factor of 2 difference in time step size from a lower to a higher +time bin, the ``max_nr_rt_subcycles`` parameter itself is required to be +a power of 2 as well. + +Note that this parameter will set an upper limit to the number of subcycles +per hydro step. If the ratio of hydro-to-RT time step is greater than what +``max_nr_rt_subcycles`` allows for, then the hydro time step will be reduced +to fit the maximal threshold. If it is smaller, the particle will simply do +fewer subcycles. + diff --git a/doc/RTD/source/RadiativeTransfer/SPHM1_RT.rst b/doc/RTD/source/RadiativeTransfer/SPHM1_RT.rst new file mode 100644 index 0000000000000000000000000000000000000000..f5505357b83452f303f25c3109fe52e2010de6d4 --- /dev/null +++ b/doc/RTD/source/RadiativeTransfer/SPHM1_RT.rst @@ -0,0 +1,367 @@ +.. SPHM1RT Radiative Transfer + Tsang Keung Chan 01.2022 + +.. _rt_SPHM1: + +SPHM1 RT +-------- + +SPHM1RT is the first two-moment radiative transfer on smoothed particle hydrodynamics (`Chan et al. 2021 +<https://ui.adsabs.harvard.edu/abs/2021MNRAS.505.5784C/abstract>`_). It solves the radiation energy and flux equations with a modified Eddington tensor closure. It is adaptive, efficient, and easy to parallelize. + +.. warning:: + The radiative transfer schemes are still in development and are not useable + at this moment. This page is currently a placeholder to document new + features and requirements as the code grows. + + +Compiling for SPHM1-RT +~~~~~~~~~~~~~~~~~~~~~~ + +- To compile swift to be able to run with SPHM1-RT, you need to configure with + ``--with-rt=SPHM1RT_N`` where ``N`` is the integer number of photon groups that + you intend to use in your simulation. The number of photon groups for SPHM1RT has + to be four for now (since it is hard-coded in thermo-chemistry solver). + +- SPHM1-RT is compatible with any SPH scheme. You'll + need to compile using ``--with-hydro=sphenix`` or other SPH schemes, e.g. we have tested gadget2, minimal, and sphenix. + +- SPHM1-RT solves non-equilibrium with the `SUNDIALS <https://computing.llnl.gov/projects/sundials>` library, + which is SUite of Nonlinear and DIfferential/ALgebraic Equation Solvers. The SUNDIALS version has to be 5 . + You'll need to compile using ``--with-sundials=$SUNDIALS_ROOT`` + SUNDIALS_ROOT is the root directory that contains the lib and include directories, e.g. on cosma: + SUNDIALS_ROOT=/cosma/local/sundials/5.1.0/ + (IMPORTANT: Need SUNDIALS version = 5). + The instructions of installing Sundials can be found, e.g., + `here <https://sundials.readthedocs.io/en/latest/Install_link.html>` or in this `website + <https://richings.bitbucket.io/chimes/user_guide/GettingStarted/sundials.html>`. + + + +Runtime Parameters +~~~~~~~~~~~~~~~~~~ + +You need to provide the following runtime parameters in the yaml file: + +.. code:: yaml + + SPHM1RT: + cred: 2.99792458e10 # value of reduced speed of light for the RT solver in code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + chi: [0, 0, 0] # (Optional) initial opacity in code unit for all gas particles + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter + star_emission_rates: [1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates (internal unit: energy/time) for each photon frequency group to use if use_constant_emission_rates is set. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + stars_max_timestep: -1. # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + reinject: 1 # (Optional) gather energy around injection radius and re-inject the energy + + +The ``photon_groups_Hz`` need to be ``N - 1`` frequency edges (floats) to separate +the spectrum into ``N`` groups. The outer limits of zero and infinity are +assumed. + +At the moment, the only way to define star emission rates is to use constant +star emission rates that need to be provided in the parameter file. The star +emission rates need to be defined for each photon frequency group individually. +The first entry of the array is for the photon group with frequency +``[0, <first entry of photon_groups_Hz>)``. Each star particle will then emit +the given energies, independent of their other properties. + +Furthermore, even though the parameter ``use_const_emission_rates`` is +intended to be optional in the future, **for now it needs to be set to 1.**, and +it requires you to manually set the stellar emission rates via the +``star_emission_rates`` parameter. + +When solving the thermochemistry, we need to assume some form of stellar +spectrum so we may integrate over frequency bins to obtain average interaction +rates. The parameter ``stellar_spectrum_type`` is hence required, and allows you +to select between: + +- constant spectrum (``stellar_spectrum_type: 0``) + - This choice additionally requires you to provide a maximal frequency for + the spectrum after which it'll be cut off via the + ``stellar_spectrum_const_max_frequency_Hz`` parameter + +- blackbody spectrum (``stellar_spectrum_type: 1``) + - In this case, you need to provide also temperature of the blackbody via the + ``stellar_spectrum_blackbody_temperature_K`` parameter. + +If ``reinject`` is set to 1, then the star will correct the radiation energy and +flux within the injection radius and re-inject radiation. Combined with larger +smoothing length, this can increase the isotropy of the radiation and the central +radiation distribution. + + + +Thermo-chemistry parameters for RT +`````````````````````````````````` +.. code:: yaml + + relativeTolerance: 1e-3 # (Optional) Relative tolerance for SPHM1RT thermo-chemistry intergration + absoluteTolerance: 1e-10 # (Optional) Absolute tolerance for SPHM1RT thermo-chemistry integration + explicitTolerance: 0.1 # (Optional) Tolerance below which we use the explicit solution in SPHM1RT thermo-chemistry + ionizing_photon_energy_erg: [3.0208e-11, 5.61973e-11, 1.05154e-10] # (Optional) ionizing photon energy in erg averaged over frequency bins + skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + coolingon: 1 # (Optional) switch for cooling (and photoheating), but photo-ionization will be ongoing even if coolingon==0 + useparams: 1 # (Optional) switch to use thermo-chemistry parameters from the parameter file + sigma_cross: [8.13e-18, 1e-32, 1e-32] # (Conditional) (if useparams=1) The cross section of ionizing photons for hydrogen (cm^2) + alphaB: 2.59e-13 # (Conditional) (if useparams=1) The case B recombination coefficient for hydrogen (cgs) + beta: 3.1e-16 # (Conditional) (if useparams=1) The collisional ionization coefficient for hydrogen (cgs) + + +ionizing_photon_energy_erg is the photon energy averaged over frequency within a frequency bin, given the radiation spectrum. In the default case, +the first value corresponds to the bin from HI ionizing frequency to HeI ionizing frequency. +The second value is from HeI ionizing frequency to HeII ionizing frequency. +The third value is above HeII ionizing frequency. +The default values are calculated with T=1e5 K blackbody spectrum. +We currently assume the spectral shape is unchanged and universal, i.e. T=1e5 K blackbody spectrum everywhere. +ionizing_photon_energy_erg is used to convert photon energy density to photon number density, photo-heating, and photo-ionization. + + + +sigma_cross is also cross-section averaged within a frequency bin. + +Currently, SPHM1RT uses CVODE in SUNDIALS to solve non-equilibrium hydrogen and helium thermochemistry in three frequency bins, +from HI-HeII, HeII-HeIII and HeIII-inf. The precise coefficients will be published in Chan et al. in prep., +but they can be found in src/rt_cooling_rates.h + +Note that the first parameter in the thermo-chemistry array +corresponds to the second parameter in injection array. For example, if +star_emission_rates: [0.0, 1.0, 0.0, 0.0], +the star emits in the HI-HeII frequency and interacts with the first bin (8.13e-18): +sigma_cross: [8.13e-18, 0.0, 0.0] + +relativeTolerance, absoluteTolerance, and explicitTolerance are tolerances used in the CVODE calculation. +These tolerances can be relaxed to increase the calculation speed, which could sacrifice accuracy. + +We can also turn off thermochemistry or cooling for testing purpose by skip_thermochemistry and coolingon. +For testing purpose, we can also overwrite the thermo-chemistry parameters by setting useparams to 1 +Currently, useparams==1 only works for pure hydrogen gas. + + + + +Initial Conditions +~~~~~~~~~~~~~~~~~~ + + +Setting Up Initial Conditions for RT +```````````````````````````````````` + +Optionally, you may want to provide initial conditions for the radiation field +and/or the mass fraction of the ionizing species. +To do so, you need to add the following datasets to the ``/PartType0`` particle +group: + +.. code:: + + PhotonEnergiesGroup1 + PhotonEnergiesGroup2 + . + . + . + PhotonEnergiesGroupN + PhotonFluxesGroup1 + PhotonFluxesGroup2 + . + . + . + PhotonFluxesGroupN + + +The ``PhotonEnergies*`` datasets need to have dimension ``nparts``, while the +``PhotonFluxesGroup*`` datasets need to have dimension ``(nparts, 3)``, where +``nparts`` is the number of hydro particles. If you are writing initial +conditions where the fields have units, then ``PhotonEnergies*`` are expected to +have units of energy :math:`[M L^2 T^{-2}]`), while the ``PhotonFluxes*`` fields +should be in units of energy times speed, :math:`[M L^3 +T^{-3}]`). + + +Example using Python and ``swiftsimio`` +```````````````````````````````````````` + +If you are using `swiftsimio <https://github.com/SWIFTSIM/swiftsimio>`_ to write +the initial condition files, then the easiest way of adding the RT initial +conditions is to first use the swiftsimio routines to write a file, then open it +up again and write the additional RT fields again using ``h5py`` routines. + +Here is an example: + +.. code:: python + + from swiftsimio import Writer + import unyt + import numpy as np + import h5py + + # define unit system to use. + unitsystem = unyt.unit_systems.cgs_unit_system + + # number of photon groups + nPhotonGroups = 4 + + # filename of ICs to be generated + outputfilename = "my_rt_ICs.hdf5" + + # open a swiftsimio.Writer object + w = Writer(...) + + # do your IC setup for gas, gravity etc now + # ... + + # write the IC file without doing anything RT related. + w.write(outputfilename) + + # Now open file back up again and add RT data. + F = h5py.File(outputfilename, "r+") + header = F["Header"] + nparts = header.attrs["NumPart_ThisFile"][0] + parts = F["/PartType0"] + + # Create initial photon energies and fluxes. You can leave them unitless, + # the units have already been written down with w.write(). In this case, + # it's in cgs. + for grp in range(nPhotonGroups): + dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1) + energydata = np.ones((nparts), dtype=np.float32) * some_value_you_want + parts.create_dataset(dsetname, data=energydata) + + dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1) + fluxdata = np.zeros((nparts, 3), dtype=np.float32) * some_value_you_want + parts.create_dataset(dsetname, data=fluxdata) + + # Create initial element mass fractions. + # Can be overwritten in parameter file if init_mass_fraction_metal is not -1.f (or set) + # the element order: [Hydrogen, Helium] + mfracH = np.ones(numPart) + mfracHe = np.ones(numPart) * 0.0 + EMFdata = np.stack((mfracH, mfracHe), axis=-1) + parts.create_dataset("RtElementMassFractions", data=EMFdata) + + # Create initial species abundances. + # abundance is in n_X/n_H unit. + # Can be overwritten in parameter file if useabundances = 1 + # the abundance order: [e, HI, HII, HeI, HeII, HeIII] + Ae = np.ones(numPart) * 0.0 + AHI = np.ones(numPart) * 1.0 + AHII = np.ones(numPart) * 0.0 + AHeI = np.ones(numPart) * 0.0 + AHeII = np.ones(numPart) * 0.0 + AHeIII = np.ones(numPart) * 0.0 + SAdata = np.stack((Ae, AHI, AHII, AHeI, AHeII, AHeIII), axis=-1) + parts.create_dataset("RtSpeciesAbundances", data=SAdata) + + # close up, and we're done! + F.close() + + + +Generate Ionization Mass Fractions Using SWIFT +`````````````````````````````````````````````` + +.. warning:: Using SWIFT to generate initial ionization mass fractions will + overwrite the mass fractions that have been read in from the initial + conditions. + +Optionally, you can use SWIFT to generate the initial mass fractions of the +elements. To set the initial mass fractions of all particles to the same +value, use the following parameters in the yaml parameter file: + +.. code:: yaml + + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set or not equal to -1.F, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 1.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + +To set the species abundances of all particles to the same +value, use the following parameters in the yaml parameter file: + +.. code:: yaml + + useabundances: 1 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 1e-5 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 0.99999 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 1e-5 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.0 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 0.0 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 0.0 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) + + +Accessing Output Data +~~~~~~~~~~~~~~~~~~~~~~ + +We recommend using `swiftsimio <https://github.com/SWIFTSIM/swiftsimio>`_ to +access the RT related snapshot data. The compatibility is being maintained. +Here's an example how to access some specific quantities that you might find +useful: + + +.. code:: python + + #!/usr/bin/env python3 + + import swiftsimio + import unyt + + data = swiftsimio.load("output_0001.hdf5") + meta = data.metadata + + + + # Accessing RT Related Metadata + # --------------------------------- + + # get scheme name: "SPH M1closure" + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + # number of photon groups used + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + + # get the reduced speed of light that was used. Will have unyts. + reduced_speed_of_light = meta.reduced_lightspeed + + + + + # Accessing Photon Data + # ------------------------ + + # accessing a photon group directly + # NOTE: group names start with 1 + group_1_photon_energies = data.gas.photon_energies.group1 + group_1_photon_fluxes_x = data.gas.photon_fluxes.Group1X + group_1_photon_fluxes_y = data.gas.photon_fluxes.Group1Y + group_1_photon_fluxes_z = data.gas.photon_fluxes.Group1Z + + # want to stack all fluxes into 1 array? + group1fluxes = swiftsimio.cosmo_array( + unyt.uvstack( + (group_1_photon_fluxes_x, group_1_photon_fluxes_y, group_1_photon_fluxes_z) + ), + group_1_photon_fluxes_x.units, + ).T + # group1fluxes.shape = (npart, 3) + + + # Load all photon energies in a list + photon_energies = [ + getattr(data.gas.photon_energies, "group" + str(g + 1)) for g in range(ngroups) + ] + + + # Accessing Element mass fraction + fH = data.gas.rt_element_mass_fractions.hydrogen + fHe = data.gas.rt_element_mass_fractions.helium + + # Accessing Species Abundances + # abundance is in n_X/n_H unit. + # ------------------------------- + Ae = data.gas.rt_species_abundances.e + AHI = data.gas.rt_species_abundances.HI + AHII = data.gas.rt_species_abundances.HII + AHeI = data.gas.rt_species_abundances.HeI + AHeII = data.gas.rt_species_abundances.HeII + AHeIII = data.gas.rt_species_abundances.HeIII diff --git a/doc/RTD/source/RadiativeTransfer/additional_tools.rst b/doc/RTD/source/RadiativeTransfer/additional_tools.rst new file mode 100644 index 0000000000000000000000000000000000000000..bbfc5c356d422916f8469bc1125ffdbbe97f3768 --- /dev/null +++ b/doc/RTD/source/RadiativeTransfer/additional_tools.rst @@ -0,0 +1,15 @@ +.. GEAR Radiative Transfer + Mladen Ivkovic 08.2022 + +.. _rt_additional_tools: + + +Additional Tools +------------------- + +A plethora of additional RT related tools is hosted in the +`the swiftsim-rt-tools <https://github.com/SWIFTSIM/swiftsim-rt-tools>`_ +repository. For example, it contains tools to convert injected photon number +rates into luminosities, compute integrated interaction cross sections for +various spectra, some rudimentary thermochemistry python functions, etc. + diff --git a/doc/RTD/source/RadiativeTransfer/full_dependency_graph_RT.dot b/doc/RTD/source/RadiativeTransfer/full_dependency_graph_RT.dot new file mode 100644 index 0000000000000000000000000000000000000000..e2461ee9ec083226ac868412ee85d5d0be0930cd --- /dev/null +++ b/doc/RTD/source/RadiativeTransfer/full_dependency_graph_RT.dot @@ -0,0 +1,369 @@ +digraph task_dep { + # Header + label="Task dependencies for SWIFT v0.9.0-1189-g3424fa24"; + compound=true; + ratio=0.66; + node[nodesep=0.15, fontsize=18, penwidth=3.]; + edge[fontsize=12, penwidth=0.5]; + ranksep=0.8; + + # Special tasks + sort[color=blue3]; + self_density[color=blue3]; + self_gradient[color=blue3]; + self_force[color=blue3]; + self_grav[color=red3]; + self_stars_density[color=darkorange1]; + self_stars_feedback[color=darkorange1]; + self_rt_gradient[color=springgreen]; + self_rt_transport[color=springgreen]; + pair_density[color=blue3]; + pair_gradient[color=blue3]; + pair_force[color=blue3]; + pair_grav[color=red3]; + pair_stars_density[color=darkorange1]; + pair_stars_feedback[color=darkorange1]; + pair_rt_gradient[color=springgreen]; + pair_rt_transport[color=springgreen]; + sub_self_density[color=blue3]; + sub_self_gradient[color=blue3]; + sub_self_force[color=blue3]; + sub_self_stars_density[color=darkorange1]; + sub_self_stars_feedback[color=darkorange1]; + sub_self_rt_gradient[color=springgreen]; + sub_self_rt_transport[color=springgreen]; + sub_pair_density[color=blue3]; + sub_pair_gradient[color=blue3]; + sub_pair_force[color=blue3]; + sub_pair_stars_density[color=darkorange1]; + sub_pair_stars_feedback[color=darkorange1]; + sub_pair_rt_gradient[color=springgreen]; + sub_pair_rt_transport[color=springgreen]; + init_grav[color=red3]; + init_grav_out[style=filled,fillcolor=grey90,color=red3]; + ghost_in[style=filled,fillcolor=grey90,color=blue3]; + ghost[color=blue3]; + ghost_out[style=filled,fillcolor=grey90,color=blue3]; + extra_ghost[color=blue3]; + drift_part[color=blue3]; + drift_spart[color=darkorange1]; + drift_gpart[color=red3]; + drift_gpart_out[style=filled,fillcolor=grey90,color=red3]; + hydro_end_force[color=blue3]; + kick2[color=black]; + timestep[color=black]; + collect[color=black]; + send_gradient[shape=diamond,style=filled,fillcolor=azure,color=blue3]; + send_xv[shape=diamond,style=filled,fillcolor=azure,color=blue3]; + send_rho[shape=diamond,style=filled,fillcolor=azure,color=blue3]; + send_gpart[shape=diamond,style=filled,fillcolor=azure,color=red3]; + send_spart_density[shape=diamond,style=filled,fillcolor=azure,color=darkorange1]; + send_rt_gradient[shape=diamond,style=filled,fillcolor=azure,color=springgreen]; + send_rt_transport[shape=diamond,style=filled,fillcolor=azure,color=springgreen]; + recv_gradient[shape=diamond,style=filled,fillcolor=azure,color=blue3]; + recv_xv[shape=diamond,style=filled,fillcolor=azure,color=blue3]; + recv_rho[shape=diamond,style=filled,fillcolor=azure,color=blue3]; + recv_gpart[shape=diamond,style=filled,fillcolor=azure,color=red3]; + recv_spart_density[shape=diamond,style=filled,fillcolor=azure,color=darkorange1]; + recv_rt_gradient[shape=diamond,style=filled,fillcolor=azure,color=springgreen]; + recv_rt_transport[shape=diamond,style=filled,fillcolor=azure,color=springgreen]; + grav_long_range[color=red3]; + grav_mm[color=red3]; + grav_down_in[style=filled,fillcolor=grey90,color=red3]; + grav_down[color=red3]; + grav_end_force[color=red3]; + stars_in[style=filled,fillcolor=grey90,color=darkorange1]; + stars_out[style=filled,fillcolor=grey90,color=darkorange1]; + stars_density_ghost[color=darkorange1]; + stars_sort[color=darkorange1]; + rt_in[style=filled,fillcolor=grey90,color=springgreen]; + rt_out[style=filled,fillcolor=grey90,color=springgreen]; + rt_ghost1[color=springgreen]; + rt_ghost2[color=springgreen]; + rt_transport_out[style=filled,fillcolor=grey90,color=springgreen]; + rt_tchem[color=springgreen]; + rt_advance_cell_time[color=springgreen]; + rt_sorts[color=springgreen]; + recv_tend[shape=diamond,style=filled,fillcolor=azure,color=black]; + kick1[color=black]; + send_tend[shape=diamond,style=filled,fillcolor=azure,color=black]; + rt_collect_times[color=springgreen]; + + # Clusters + subgraph clusterDensity { + label=""; + bgcolor="grey99"; + pair_density; + self_density; + sub_pair_density; + sub_self_density; + }; + + subgraph clusterForce { + label=""; + bgcolor="grey99"; + pair_force; + self_force; + sub_pair_force; + sub_self_force; + }; + + subgraph clusterGradient { + label=""; + bgcolor="grey99"; + pair_gradient; + self_gradient; + sub_pair_gradient; + sub_self_gradient; + }; + + subgraph clusterGravity { + label=""; + bgcolor="grey99"; + grav_long_range; + grav_mm; + pair_grav; + self_grav; + }; + + subgraph clusterRTgradient { + label=""; + bgcolor="grey99"; + pair_rt_gradient; + self_rt_gradient; + sub_pair_rt_gradient; + sub_self_rt_gradient; + }; + + subgraph clusterRTtransport { + label=""; + bgcolor="grey99"; + pair_rt_transport; + self_rt_transport; + sub_pair_rt_transport; + sub_self_rt_transport; + }; + + subgraph clusterStarsDensity { + label=""; + bgcolor="grey99"; + pair_stars_density; + self_stars_density; + sub_pair_stars_density; + sub_self_stars_density; + }; + + subgraph clusterStarsFeedback { + label=""; + bgcolor="grey99"; + pair_stars_feedback; + self_stars_feedback; + sub_pair_stars_feedback; + sub_self_stars_feedback; + }; + + + # Dependencies + sort->pair_density[color=blue3,fontcolor=blue3] + sort->pair_force[color=blue3,fontcolor=blue3] + sort->pair_rt_transport[color=blue3,fontcolor=blue3] + sort->pair_stars_density[color=blue3,fontcolor=blue3] + sort->pair_rt_gradient[color=blue3,fontcolor=blue3] + sort->sub_pair_density[color=blue3,fontcolor=blue3] + sort->sub_pair_force[color=blue3,fontcolor=blue3] + sort->sub_pair_rt_transport[color=blue3,fontcolor=blue3] + sort->sub_pair_stars_density[color=blue3,fontcolor=blue3] + sort->sub_pair_rt_gradient[color=blue3,fontcolor=blue3] + sort->rt_sorts[color=blue3,fontcolor=blue3] + sort->recv_rt_gradient[color=blue3,fontcolor=blue3] + sort->recv_rho[color=blue3,fontcolor=blue3] + sort->recv_gradient[color=blue3,fontcolor=blue3] + sort->sub_self_density[color=blue3,fontcolor=blue3] + sort->sub_self_stars_density[color=blue3,fontcolor=blue3] + sort->sub_self_rt_gradient[color=blue3,fontcolor=blue3] + self_density->ghost_in[color=blue3,fontcolor=blue3] + self_gradient->extra_ghost[color=blue3,fontcolor=blue3] + self_force->hydro_end_force[color=blue3,fontcolor=blue3] + self_grav->grav_down_in[color=red3,fontcolor=red3] + self_stars_density->stars_density_ghost[color=darkorange1,fontcolor=darkorange1] + self_stars_feedback->stars_out[color=darkorange1,fontcolor=darkorange1] + self_rt_gradient->rt_ghost2[color=springgreen,fontcolor=springgreen] + self_rt_transport->rt_transport_out[color=springgreen,fontcolor=springgreen] + pair_density->ghost_in[color=blue3,fontcolor=blue3] + pair_density->recv_rho[color=blue3,fontcolor=blue3] + pair_gradient->extra_ghost[color=blue3,fontcolor=blue3] + pair_gradient->recv_gradient[color=blue3,fontcolor=blue3] + pair_force->hydro_end_force[color=blue3,fontcolor=blue3] + pair_force->recv_tend[color=blue3,fontcolor=blue3] + pair_force->recv_rt_gradient[color=blue3,fontcolor=blue3] + pair_grav->grav_down_in[color=red3,fontcolor=red3] + pair_grav->recv_tend[color=red3,fontcolor=red3] + pair_stars_density->stars_density_ghost[color=darkorange1,fontcolor=darkorange1] + pair_stars_density->recv_spart_density[color=darkorange1,fontcolor=darkorange1] + pair_stars_feedback->stars_out[color=darkorange1,fontcolor=darkorange1] + pair_stars_feedback->recv_tend[color=darkorange1,fontcolor=darkorange1] + pair_rt_gradient->rt_ghost2[color=springgreen,fontcolor=springgreen] + pair_rt_gradient->recv_rt_transport[color=springgreen,fontcolor=springgreen] + pair_rt_transport->rt_transport_out[color=springgreen,fontcolor=springgreen] + pair_rt_transport->recv_tend[color=springgreen,fontcolor=springgreen] + pair_rt_transport->rt_advance_cell_time[color=springgreen,fontcolor=springgreen] + sub_self_density->ghost_in[color=blue3,fontcolor=blue3] + sub_self_gradient->extra_ghost[color=blue3,fontcolor=blue3] + sub_self_force->hydro_end_force[color=blue3,fontcolor=blue3] + sub_self_stars_density->stars_density_ghost[color=darkorange1,fontcolor=darkorange1] + sub_self_stars_feedback->stars_out[color=darkorange1,fontcolor=darkorange1] + sub_self_rt_gradient->rt_ghost2[color=springgreen,fontcolor=springgreen] + sub_self_rt_transport->rt_transport_out[color=springgreen,fontcolor=springgreen] + sub_pair_density->ghost_in[color=blue3,fontcolor=blue3] + sub_pair_density->recv_rho[color=blue3,fontcolor=blue3] + sub_pair_gradient->extra_ghost[color=blue3,fontcolor=blue3] + sub_pair_gradient->recv_gradient[color=blue3,fontcolor=blue3] + sub_pair_force->hydro_end_force[color=blue3,fontcolor=blue3] + sub_pair_force->recv_tend[color=blue3,fontcolor=blue3] + sub_pair_force->recv_rt_gradient[color=blue3,fontcolor=blue3] + sub_pair_stars_density->stars_density_ghost[color=darkorange1,fontcolor=darkorange1] + sub_pair_stars_density->recv_spart_density[color=darkorange1,fontcolor=darkorange1] + sub_pair_stars_feedback->stars_out[color=darkorange1,fontcolor=darkorange1] + sub_pair_stars_feedback->recv_tend[color=darkorange1,fontcolor=darkorange1] + sub_pair_rt_gradient->rt_ghost2[color=springgreen,fontcolor=springgreen] + sub_pair_rt_gradient->recv_rt_transport[color=springgreen,fontcolor=springgreen] + sub_pair_rt_transport->rt_transport_out[color=springgreen,fontcolor=springgreen] + sub_pair_rt_transport->recv_tend[color=springgreen,fontcolor=springgreen] + sub_pair_rt_transport->rt_advance_cell_time[color=springgreen,fontcolor=springgreen] + init_grav->grav_long_range[color=red3,fontcolor=red3] + init_grav->init_grav_out[color=red3,fontcolor=red3] + init_grav_out->pair_grav[color=red3,fontcolor=red3] + init_grav_out->self_grav[color=red3,fontcolor=red3] + init_grav_out->init_grav_out[color=red3,fontcolor=red3] + init_grav_out->grav_mm[color=red3,fontcolor=red3] + ghost_in->ghost[color=blue3,fontcolor=blue3] + ghost->ghost_out[color=blue3,fontcolor=blue3] + ghost_out->pair_gradient[color=blue3,fontcolor=blue3] + ghost_out->sub_self_gradient[color=blue3,fontcolor=blue3] + ghost_out->sub_pair_gradient[color=blue3,fontcolor=blue3] + ghost_out->send_rho[color=blue3,fontcolor=blue3] + ghost_out->self_gradient[color=blue3,fontcolor=blue3] + extra_ghost->pair_force[color=blue3,fontcolor=blue3] + extra_ghost->sub_self_force[color=blue3,fontcolor=blue3] + extra_ghost->sub_pair_force[color=blue3,fontcolor=blue3] + extra_ghost->send_gradient[color=blue3,fontcolor=blue3] + extra_ghost->self_force[color=blue3,fontcolor=blue3] + drift_part->pair_density[color=blue3,fontcolor=blue3] + drift_part->pair_stars_density[color=blue3,fontcolor=blue3] + drift_part->pair_rt_gradient[color=blue3,fontcolor=blue3] + drift_part->sub_self_density[color=blue3,fontcolor=blue3] + drift_part->sub_self_stars_density[color=blue3,fontcolor=blue3] + drift_part->sub_self_rt_gradient[color=blue3,fontcolor=blue3] + drift_part->sub_pair_density[color=blue3,fontcolor=blue3] + drift_part->sub_pair_stars_density[color=blue3,fontcolor=blue3] + drift_part->sub_pair_rt_gradient[color=blue3,fontcolor=blue3] + drift_part->sort[color=blue3,fontcolor=blue3] + drift_part->send_rho[color=blue3,fontcolor=blue3] + drift_part->send_xv[color=blue3,fontcolor=blue3] + drift_part->send_rt_gradient[color=blue3,fontcolor=blue3] + drift_part->send_rt_transport[color=blue3,fontcolor=blue3] + drift_part->self_density[color=blue3,fontcolor=blue3] + drift_part->self_stars_density[color=blue3,fontcolor=blue3] + drift_part->self_rt_gradient[color=blue3,fontcolor=blue3] + drift_spart->kick2[color=darkorange1,fontcolor=darkorange1] + drift_spart->pair_stars_density[color=darkorange1,fontcolor=darkorange1] + drift_spart->sub_self_stars_density[color=darkorange1,fontcolor=darkorange1] + drift_spart->sub_pair_stars_density[color=darkorange1,fontcolor=darkorange1] + drift_spart->stars_sort[color=darkorange1,fontcolor=darkorange1] + drift_spart->send_spart_density[color=darkorange1,fontcolor=darkorange1] + drift_spart->self_stars_density[color=darkorange1,fontcolor=darkorange1] + drift_gpart->drift_gpart_out[color=red3,fontcolor=red3] + drift_gpart->send_gpart[color=red3,fontcolor=red3] + drift_gpart_out->pair_grav[color=red3,fontcolor=red3] + drift_gpart_out->self_grav[color=red3,fontcolor=red3] + drift_gpart_out->drift_gpart_out[color=red3,fontcolor=red3] + hydro_end_force->kick2[color=blue3,fontcolor=blue3] + kick2->timestep[color=black,fontcolor=black] + kick2->stars_in[color=black,fontcolor=black] + kick2->rt_in[color=black,fontcolor=black] + timestep->kick1[color=black,fontcolor=black] + timestep->collect[color=black,fontcolor=black] + collect->send_tend[color=black,fontcolor=black] + send_gradient->hydro_end_force[color=blue3,fontcolor=blue3] + send_xv->send_rho[color=blue3,fontcolor=blue3] + send_xv->ghost_in[color=blue3,fontcolor=blue3] + send_xv->send_rt_gradient[color=blue3,fontcolor=blue3] + send_rho->send_gradient[color=blue3,fontcolor=blue3] + send_rho->extra_ghost[color=blue3,fontcolor=blue3] + send_gpart->grav_down[color=red3,fontcolor=red3] + send_spart_density->stars_out[color=darkorange1,fontcolor=darkorange1] + send_rt_gradient->send_rt_transport[color=springgreen,fontcolor=springgreen] + send_rt_gradient->rt_ghost2[color=springgreen,fontcolor=springgreen] + send_rt_transport->rt_transport_out[color=springgreen,fontcolor=springgreen] + recv_gradient->recv_rt_gradient[color=blue3,fontcolor=blue3] + recv_gradient->pair_force[color=blue3,fontcolor=blue3] + recv_gradient->sub_pair_force[color=blue3,fontcolor=blue3] + recv_xv->recv_rho[color=blue3,fontcolor=blue3] + recv_xv->recv_gradient[color=blue3,fontcolor=blue3] + recv_xv->recv_rt_gradient[color=blue3,fontcolor=blue3] + recv_xv->sort[color=blue3,fontcolor=blue3] + recv_xv->pair_density[color=blue3,fontcolor=blue3] + recv_xv->sub_pair_density[color=blue3,fontcolor=blue3] + recv_rho->recv_gradient[color=blue3,fontcolor=blue3] + recv_rho->recv_rt_gradient[color=blue3,fontcolor=blue3] + recv_rho->pair_gradient[color=blue3,fontcolor=blue3] + recv_rho->pair_stars_density[color=blue3,fontcolor=blue3] + recv_rho->sub_pair_gradient[color=blue3,fontcolor=blue3] + recv_rho->sub_pair_stars_density[color=blue3,fontcolor=blue3] + recv_gpart->pair_grav[color=red3,fontcolor=red3] + recv_spart_density->stars_sort[color=darkorange1,fontcolor=darkorange1] + recv_spart_density->pair_stars_feedback[color=darkorange1,fontcolor=darkorange1] + recv_spart_density->sub_pair_stars_feedback[color=darkorange1,fontcolor=darkorange1] + recv_rt_gradient->rt_sorts[color=springgreen,fontcolor=springgreen] + recv_rt_gradient->recv_rt_transport[color=springgreen,fontcolor=springgreen] + recv_rt_gradient->rt_advance_cell_time[color=springgreen,fontcolor=springgreen] + recv_rt_gradient->pair_rt_gradient[color=springgreen,fontcolor=springgreen] + recv_rt_gradient->sub_pair_rt_gradient[color=springgreen,fontcolor=springgreen] + recv_rt_transport->rt_advance_cell_time[color=springgreen,fontcolor=springgreen] + recv_rt_transport->pair_rt_transport[color=springgreen,fontcolor=springgreen] + recv_rt_transport->sub_pair_rt_transport[color=springgreen,fontcolor=springgreen] + grav_long_range->grav_down[color=red3,fontcolor=red3] + grav_mm->grav_down_in[color=red3,fontcolor=red3] + grav_down_in->grav_down[color=red3,fontcolor=red3] + grav_down_in->grav_down_in[color=red3,fontcolor=red3] + grav_down->grav_end_force[color=red3,fontcolor=red3] + grav_end_force->kick2[color=red3,fontcolor=red3] + stars_in->pair_stars_density[color=darkorange1,fontcolor=darkorange1] + stars_in->sub_self_stars_density[color=darkorange1,fontcolor=darkorange1] + stars_in->sub_pair_stars_density[color=darkorange1,fontcolor=darkorange1] + stars_in->self_stars_density[color=darkorange1,fontcolor=darkorange1] + stars_out->timestep[color=darkorange1,fontcolor=darkorange1] + stars_out->rt_in[color=darkorange1,fontcolor=darkorange1] + stars_density_ghost->pair_stars_feedback[color=darkorange1,fontcolor=darkorange1] + stars_density_ghost->sub_self_stars_feedback[color=darkorange1,fontcolor=darkorange1] + stars_density_ghost->sub_pair_stars_feedback[color=darkorange1,fontcolor=darkorange1] + stars_density_ghost->send_spart_density[color=darkorange1,fontcolor=darkorange1] + stars_density_ghost->self_stars_feedback[color=darkorange1,fontcolor=darkorange1] + stars_sort->pair_stars_feedback[color=darkorange1,fontcolor=darkorange1] + stars_sort->sub_pair_stars_feedback[color=darkorange1,fontcolor=darkorange1] + stars_sort->pair_stars_density[color=darkorange1,fontcolor=darkorange1] + stars_sort->sub_self_stars_density[color=darkorange1,fontcolor=darkorange1] + stars_sort->sub_pair_stars_density[color=darkorange1,fontcolor=darkorange1] + rt_in->rt_ghost1[color=springgreen,fontcolor=springgreen] + rt_out->timestep[color=springgreen,fontcolor=springgreen] + rt_out->collect[color=springgreen,fontcolor=springgreen] + rt_ghost1->pair_rt_gradient[color=springgreen,fontcolor=springgreen] + rt_ghost1->sub_self_rt_gradient[color=springgreen,fontcolor=springgreen] + rt_ghost1->sub_pair_rt_gradient[color=springgreen,fontcolor=springgreen] + rt_ghost1->send_rt_gradient[color=springgreen,fontcolor=springgreen] + rt_ghost1->self_rt_gradient[color=springgreen,fontcolor=springgreen] + rt_ghost2->pair_rt_transport[color=springgreen,fontcolor=springgreen] + rt_ghost2->sub_self_rt_transport[color=springgreen,fontcolor=springgreen] + rt_ghost2->sub_pair_rt_transport[color=springgreen,fontcolor=springgreen] + rt_ghost2->send_rt_transport[color=springgreen,fontcolor=springgreen] + rt_ghost2->self_rt_transport[color=springgreen,fontcolor=springgreen] + rt_transport_out->rt_tchem[color=springgreen,fontcolor=springgreen] + rt_tchem->rt_advance_cell_time[color=springgreen,fontcolor=springgreen] + rt_advance_cell_time->rt_collect_times[color=springgreen,fontcolor=springgreen] + rt_advance_cell_time->rt_out[color=springgreen,fontcolor=springgreen] + rt_advance_cell_time->send_tend[color=springgreen,fontcolor=springgreen] + rt_advance_cell_time->recv_tend[color=springgreen,fontcolor=springgreen] + rt_sorts->recv_rt_transport[color=springgreen,fontcolor=springgreen] + rt_sorts->pair_rt_gradient[color=springgreen,fontcolor=springgreen] + rt_sorts->sub_pair_rt_gradient[color=springgreen,fontcolor=springgreen] +} \ No newline at end of file diff --git a/doc/RTD/source/RadiativeTransfer/index.rst b/doc/RTD/source/RadiativeTransfer/index.rst index 941d6ece1aa3f1f9e33c404f3a5b3fd947628931..2f465b735f34b9bb63c5bde0a006fba4eb1441e8 100644 --- a/doc/RTD/source/RadiativeTransfer/index.rst +++ b/doc/RTD/source/RadiativeTransfer/index.rst @@ -23,3 +23,7 @@ schemes. requirements GEAR_RT + SPHM1_RT + RT_subcycling + RT_notes_for_developers + additional_tools diff --git a/doc/RTD/source/RadiativeTransfer/make_graphs.py b/doc/RTD/source/RadiativeTransfer/make_graphs.py new file mode 100644 index 0000000000000000000000000000000000000000..db026ac078f0e38359504298004820bcd37b5c6f --- /dev/null +++ b/doc/RTD/source/RadiativeTransfer/make_graphs.py @@ -0,0 +1,5 @@ +import os + +# Yes... this is overkill... but the main sphinx conf script looks for python +# scripts to execute... So we piggy back on this. +os.system("dot -Tpng full_dependency_graph_RT.dot -o full_dependency_graph_RT.png") diff --git a/doc/RTD/source/Snapshots/index.rst b/doc/RTD/source/Snapshots/index.rst index b35bc14057244a3fba76e9c789a31b8adfa76b07..30cc87fa1d0e29311fab8c1c7fde7be374ece27b 100644 --- a/doc/RTD/source/Snapshots/index.rst +++ b/doc/RTD/source/Snapshots/index.rst @@ -38,6 +38,11 @@ number of sub-snapshot files (always 1 unless a distributed snapshot was asked for) and ``ThisFile`` the id of that specific file (always 0 unless a distributed snapshot was asked for). +The field ``TotalNumberOfParticles`` gives the total number of particles of each type +as a 64 bit integer. This allows the total number of particles to be read directly +with no calculation required even if there are 2^31 or more particles. This field is +equal to ``NumPart_ThisFile`` if the snapshot is not distributed over multiple files. + The field ``InitialMassTable`` contains the *mean* initial mass of each of the particle types present in the initial conditions. This can be used as estimator of the mass resolution of the run. The masses are expressed in internal units. @@ -73,7 +78,7 @@ time-line. This is the smallest time-step size that the code can use. This field is zero in non-cosmological runs. Similarly, the field ``TimeBase_dt`` contains the smallest time-step size (in internal units) that the code can take. This would be the increase in time a particle in the time-bin one would have. Note -that in cosmological runs this quantity evolves with redhsift as the (logarithm +that in cosmological runs this quantity evolves with redshift as the (logarithm of the) scale-factor is used on the integer time-line. The field ``SelectOutput`` will contain the name of the @@ -87,6 +92,13 @@ written to the snapshot. Note, however, that when sub-sampling the fields of particles actually written (i.e. after sub-sampling), not the total number of particles in the run. +The field ``CanHaveTypes`` contains information about whether a given particle +type is to be expected in snapshots of the run. For instance, a simulation with +star formation switched on, the code may not have formed a star yet but might in +future snapshots. This allows reading tools to distinguish fields they will +never expect to find in a given simulation from fields that may be present in +other outputs. + Meta-data about the code and run -------------------------------- @@ -379,9 +391,9 @@ expressed in the unit system used for the snapshots (see above) and are hence consistent with the particle positions themselves. Once the cell(s) containing the region of interest has been located, -users can use the ``/Cells/Offsets/PartTypeN/Files``, -``/Cells/Offsets/PartTypeN/Counts`` and -``/Cells/Offsets/PartTypeN/OffsetsInFile`` to retrieve the location of +users can use the ``/Cells/Files/PartTypeN/``, +``/Cells/Counts/PartTypeN/`` and +``/Cells/OffsetsInFile/PartTypeN/`` to retrieve the location of the particles of type ``N`` in the ``/PartTypeN`` arrays. These contain information about which file contains the particles of a given cell. It also gives the offset from the start of the ``/PartTypeN`` @@ -398,6 +410,15 @@ In the case of a single-file snapshot, the ``Files`` array is just an array of zeroes since all the particles will be in the 0-th file. Note also that in the case of a multi-files snapshot, a cell is always contained in a single file. +As noted above, particles can (slightly) drift out of their cells. This can be +problematic in cases where one wants to find precisely all the particles in a +given region. To help with this, the meta-data also contains a "cell bounding +box". The arrays ``/Cells/MinPositions/PartTypeN`` and +``/Cells/MaxPositions/PartTypeN`` contain the minimal (maximal) x,y,z +coordinates of all the particles of this type in the cells. Note that these +coordinates can be outside of the cell itself. When using periodic boundary +conditions, no box-wrapping is applied. + If a snapshot used a sub-sampled output, then the counts and offsets are adjusted accordingly and correspond to the actual content of the file (i.e. after the sub-sampling was applied). diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/index.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..d7c83ca5117f8be7a663a7e34eaf6167e0101a11 --- /dev/null +++ b/doc/RTD/source/SubgridModels/AGNSpinJets/index.rst @@ -0,0 +1,46 @@ +.. AGN spin and jet model + Filip Husko, 1 April 2022 + +.. AGN_spin_jet: + +AGN jets and black hole spin in hydrodynamical simulations +========================================================== + +Model summary +------------- + + +The main feature of this model, in terms of the effects on galaxy populations, is the addition of an AGN jet +mode of feedback. In order to launch realistic jets, black hole spin is tracked and evolved for all BH +particles in the simulation. + +Jet powers, in addition to depending on spin, also depend on which accretion state the black hole is in. We +include three accretion states: the thick, thin and slim disk. The thick disk appears at low accretion rates, +has very strong jets and is inefficient at spinning up the black hole. The thin disk, appearing at +intermediate accretion rates, has weak jets, strong radiation and efficiently spins up the black hole. The +slim disk, corresponding to super-Eddington accretion, has features of both, and has both strong radiation and +jets. Slim disks can be turned off in the model, but the thick and thin disks are intimitely tied to their +feedback modes (jets and radiation, respectively). + +In ``theory.rst`` we outline all of the theory which is implemented as part of the model. This includes when +the black holes transition from one state to another, the strength of feedback in each state, how spin is +evolved in terms of magnitude and direction, etc. In ``numerics.rst`` we discuss how jet launching is +implemented, and additional black hole time steps introduced into the code. In ``params.rst`` we list and +discuss all parameters used by the model. In ``output.rst`` we list additional arrays output for the BHs and +tracers. Below we outline how to configure and run the model. + +Compiling and running the model +------------------------------- + +The model can be run with either the EAGLE or COLIBRE models. You can configure the model with ``--with-black-holes=SPIN_JET`` in combination with other configure options, or you can configure the full EAGLE or COLIBRE models with the new spin/jet physics as ``--with-subgrid=SPIN_JET_EAGLE`` and ``--with-subgrid=SPIN_JET_COLIBRE``, respectively. The model will then run as long as ``--black-holes`` is among the runtime options. + +For cosmological simulations you do not need to do anything special, but for isolated runs (or any runs with black holes in the initial conditions), the ICs must include two new fields for all black holes: a scalar field representing black hole spins called ``Spins`` and a vector field representing the directions of the spin called ``AngularMomentumDirections``. The former should be between 0 and 1, while the latter should be normalized to 1. + +A full list of all relevant parameters of the model is in ``params.rst``. We also briefly describe the most important parameters which need to be set to run the model, as well as how to run it in different configurations. + +.. toctree:: + + theory + numerics + params + output diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/numerics.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/numerics.rst new file mode 100644 index 0000000000000000000000000000000000000000..6d37b8aec2510b37a40629f4eb12ff61609e586f --- /dev/null +++ b/doc/RTD/source/SubgridModels/AGNSpinJets/numerics.rst @@ -0,0 +1,31 @@ +.. AGN spin and jet model + Filip Husko, 1 April 2022 + +.. AGN_spin_jet: + +Jet launching algorithm +----------------------- + +In order to launch jets, we introduce a jet reservoir that functions identically to the thermal reservoir used in EAGLE and COLIBRE. When the jet reservoir exceeds the value :math:`N_\mathrm{j}\overline{m}_\mathrm{ngb}v_\mathrm{j}^2/2`, kicks are given out to :math:`N_\mathrm{j}` particles. Here, :math:`N_\mathrm{j}` is the target number of particles to kick per each kicking event (typically equal to 2, but should always be a multiple of 2 to ensure approximately symmetrical jets). :math:`\overline{m}_\mathrm{ngb}v_\mathrm{j}^2/2` is the average kinetic energy of one kicking event, with :math:`\overline{m}_\mathrm{ngb}` the mean neighbour mass of gas particles in the BH smoothing kernel and :math:`v_\mathrm{j}` the target kicking velocity. + +These kicks are handed out to particles in a symmetric way with respect to the spin vector of the BH. :math:`N_\mathrm{j}/2` particles are kicked from the 'upper' hemisphere relative to the spin vector, and the other half from the lower hemisphere. The particles to be kicked can be any in the smoothing kernel. We include four different choices: the particles kicked are: 1) the closest to the BH, 2) the farthest from the BH, 3) the ones of minimal density and 4) the ones closest to the spin axis, in terms of angular distance. Note that these sortings are done for each hemisphere seperately. + +The particles chosen are always given velocities based on the same algorithm, regardless of their positions in the kernel. We perform the actual kicks in the following way. Velocity kicks are chosen at random from a cone around the current spin vector with a (half-)opening angle of :math:`\theta_\mathrm{j}`. In particular, we first choose the kick vector around the z-axis as :math:`\vec{v}_\mathrm{kick}=(\sin\theta\cos\phi,\hspace{0.3mm}\sin\theta\sin\phi,\hspace{0.3mm}\cos \theta)`. Here, :math:`\cos\theta` is chosen uniformly from the interval :math:`[\cos\theta_\mathrm{j},1]`, and :math:`\sin\theta=\sqrt{1-\cos\theta^2}`. :math:`\phi` is chosen uniformly from :math:`[0,2\pi]`. This random vector, now representing a random kick within a cone around the z-axis, is rotated into the frame of the spin vector so that the cone is pointing in the right direction. For particles being kicked from the 'negative' side of the BH hemisphere, the final kick vector is simply multiplied by :math:`-1`. + +We then add the kick vector to the particle's current velocity. We do this in a way that conserves energy, so that the magnitude of the final velocity is computed from + +.. math:: + \frac{1}{2}m_\mathrm{gas}\vec{v}_\mathrm{fin}^2=\frac{1}{2}m_\mathrm{gas}\vec{v}_\mathrm{0}^2 + \frac{1}{2}m_\mathrm{gas}\vec{v}_\mathrm{kick}^2, + +while its direction is computed from conservation of momentum: + +.. math:: + m_\mathrm{gas}\vec{v}_\mathrm{fin}=m_\mathrm{gas}\vec{v}_\mathrm{0} + m_\mathrm{gas}\vec{v}_\mathrm{kick}. + +Black hole time steps +--------------------- + +Black holes will generally have time steps based on their gravitational interactions, but also based on their current accretion rate and the expected time interval until which the thermal feedback reservoir (representing radiative feedback) will have grown enough to heat 1 particle. We introduce a similar time step, but based on when the jet reservoir grows enough to kick :math:`N_\mathrm{j}` particles. We then take the minimum of those two. + +On top of that, we add a time step that makes sure the BH spin doesn't get evolved too much over one time step. Its magnitude is actually not a problem, since the growth of the spin magnitude is always tied with the growth of mass. However, the direction is more problematic (especially in the thin disk case, where alignment can occur very quickly, with little mass or spin growth, due to large warp radii). For this reason, we make sure that the amount of warp angular momentum interacting with the BH over the next time-step, :math:`\Delta_J=(J_\mathrm{warp}/M_\mathrm{warp})\Delta M`, is a small fraction of the current BH angular momentum :math:`J_\mathrm{BH}` (e.g. :math:`0.01`). + diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/output.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/output.rst new file mode 100644 index 0000000000000000000000000000000000000000..fe03f042cf3aafcb71e3fe6142dd9b9ce4f1b5d7 --- /dev/null +++ b/doc/RTD/source/SubgridModels/AGNSpinJets/output.rst @@ -0,0 +1,88 @@ +.. AGN spin and jet model + Filip Husko, 1 April 2022 + +.. AGN_spin_jet: + +Black holes +----------- + + ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| Name | Description | Units | Comments | ++=======================================+=====================================+===========+=============================+ +| ``AngularMomentumDirections`` | | The direction of the angular | [-] | | Array of length | +| | | momentum (spin) vector of the BH | | | 3 for each particle | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``AccretionDiscAspectRatios`` | | Aspect ratio, H/R, of the subgrid | [-] | | +| | | accretion disk surrounding each | | | +| | | black hole | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``AccretionModes`` | | Type of subgrid accretion disk | [-] | | +| | | surrounding the black holes | | | +| | | 0 - Thick disk, 1 - Thin disk, | | | +| | | 2 - Slim disk | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``CosAccretionDiskAngle`` | | Cosine of the angle between the | [-] | | +| | | spin vector and the gas angular | | | +| | | momentum vector around the BH | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``InjectedJetEnergies`` | | Total jet energy injected into | [U_M U_L | | +| | | surroundings of this BH | ^2 U_t^-2]| | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``JetEfficiencies`` | | The efficiency of jet launching, | [-] | | +| | | i.e. the jet power divided by the | | | +| | | accretion rate times c*c | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``JetReservoirs`` | | The remaining jet energy left to | [U_M U_L | | +| | | be launched from the BH | ^2 U_t^-2]| | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``JetTimeSteps`` | | Jet-limited time steps of the BHs | [U_t] | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``LastAGNJetScaleFactors`` | | Last AGN jet scale factors when | [-] | | +| | | the BH did jet feedback, if | | | +| | | cosmology is turned on | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``LastAGNJetTimes`` | | Last AGN jet times when the BH | [U_t] | | +| | | did jet feedback, if cosmology is | | | +| | | turned off | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``NumberOfAGNJetEvents`` | | Total number of times this BH did | [-] | | +| | | jet feedback | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``NumberOfJetParticlesLaunched`` | | Total number of times this BH | [-] | | +| | | launched particles as part of jet | | | +| | | feedback | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``Spins`` | | Dimensionless spin parameters of | [-] | | +| | | the black holes. Negative values | | | +| | | indicate retrograde accretion | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``RadiativeEfficiencies`` | | The efficiency of radiative | [-] | | +| | | feedback, i.e. the radiative | | | +| | | power divided by the accretion | | | +| | | rate times c*c | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ + + +Tracers (gas and stars) +----------------------- + ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| Name | Description | Units | Comments | ++=======================================+=====================================+===========+=============================+ +| ``EnergiesReceivedFromJetFeedback`` | | The total energy this particle | [U_M U_L | | +| | | has received by being kicked as | ^2 U_t^-2]| | +| | | part of jet feedback | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``LastAGNJetFeedbackScaleFactors`` | | Scale factor when the particle | [-] | | +| | | was last kicked as part of jet | | | +| | | feedback, if with cosmology | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``LastAGNJetFeedbackTimes`` | | Times when the particle was last | [U_t] | | +| | | last kicked as part of jet | | | +| | | feedback, if without cosmology | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``KickedByJetFeedback`` | | How many times this particle has | [-] | | +| | | been kicked as | | | +| | | part of jet feedback | | | ++---------------------------------------+-------------------------------------+-----------+-----------------------------+ diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/params.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/params.rst new file mode 100644 index 0000000000000000000000000000000000000000..c276229ffce96bc222c6403a1e810eb3bddd2822 --- /dev/null +++ b/doc/RTD/source/SubgridModels/AGNSpinJets/params.rst @@ -0,0 +1,83 @@ +.. AGN spin and jet model + Filip Husko, 1 April 2022 + +.. AGN_spin_jet: + +Model parameters +---------------- + +Below we give an example of parameter choices applicable for e.g. a 50 Mpc box. The new parameters are from ``include_jets`` and below. Their descriptions are given next to the parameters. + +.. code:: YAML + + SPINJETAGN: + subgrid_seed_mass_Msun: 1e5 # Black hole subgrid mass at creation time in solar masses. + use_subgrid_mass_from_ics: 1 # (Optional) Use subgrid masses specified in ICs [1, default], or initialise them to particle masses [0]? + with_subgrid_mass_check: 1 # (Optional) Verify that initial black hole subgrid masses are positive [1, default]. Only used if use_subgrid_mass_from_ics is 1. + use_multi_phase_bondi: 0 # Compute Bondi rates per neighbour particle [1] or for the smoothed ambient gas around the black hole [0]? + use_subgrid_gas_properties: 1 # Use subgrid density [1] or dynamical density [0] to calculate BH accretion rates? + use_krumholz: 1 # Use Krumholz et al. (2006) [1] or standard Bondi-Hoyle-Lyttleton formula [0] for black hole accretion rates? Only used if multi_phase_bondi is 0. + with_krumholz_vorticity: 0 # Include the vorticity term in Krumholz et al. formula? Only used if use_multi_phase_bondi is 0. + with_angmom_limiter: 0 # Are we applying the Rosas-Guevara (2015) viscous time-scale reduction term? + viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term. Only used if with_angmom_limiter is 1. + with_boost_factor: 0 # Are we using the model from Booth, Schaye (2009)? + boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth, Schaye 2009 accretion model. + boost_beta: 2. # Slope of the power law for the Booth, Schaye 2009 model, set beta to zero for constant alpha models. + boost_n_h_star_cm3: 0.1 # Normalization of the power law for the Booth Schaye 2009 model in cgs (cm^-3). + eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. + use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_delta_T_K: 1e8 # 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. + with_potential_correction: 1 # Subtract BH's own contribution to the potential of neighbours when determining repositioning targets. + max_reposition_mass_Msun: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. + max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. + with_reposition_velocity_threshold: 0 # Should we only reposition to particles that move slowly w.r.t. the black hole? + max_reposition_velocity_ratio: 0.25 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1. + min_reposition_velocity_threshold_km_p_s: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1. + set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + reposition_coefficient_upsilon: 0.001 # Repositioning speed normalisation [km/s/M_sun]. Only meaningful if set_reposition_speed is 1. + reposition_exponent_xi: 1.0 # (Optional) Scaling of repositioning velocity with BH subgrid mass (default: 1.0, linear). Only meaningful if set_reposition_speed is 1. + threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' + threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers (CircularVelocity as in EAGLE, EscapeVelocity, or DynamicalEscapeVelocity) + merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. + AGN_use_deterministic_feedback: 1 # Deterministic (1) or stochastic (0) AGN feedback model + AGN_feedback_model: Isotropic # AGN feedback model (Isotropic or MinimumDistance) + minimum_timestep_yr: 1000.0 # Minimum time-step of black-hole particles + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + include_jets: 1 # Global switch whether to include jet feedback [1] or not [0]. + turn_off_radiative_feedback: 0 # Global switch whether to turn off radiative (thermal) feedback [1] or not [0]. This should only be used if 'include_jets' is set to 1, since we want feedback in some form or another. + alpha_acc: 0.1 # Viscous alpha of the subgrid accretion disks. Likely to be within the 0.1-0.3 range. The main effect is that it sets the transition accretion rate between thin and thick disk, as dot(m) = 0.2 * alpha^2. + seed_spin: 0.01 # The (randomly-directed) black hole spin assigned to BHs when they are seeded. Should be strictly between 0 and 1. + AGN_jet_velocity_model: BlackHoleMass # How AGN jet velocities are calculated. If 'Constant', a single value is used. If 'BlackHoleMass', then an empirical relation between halo mass and black hole mass is used to calculate jet velocities. 'HaloMass' is currently not supported. + v_jet_km_p_s: 10000. # Jet velocity to use if 'AGN_jet_velocity_model' is 'Constant'. Units are km/s. + v_jet_cs_ratio: 10. # This sets the jet velocity to v_jet_cs_ratio times the sound speed of the hot gas of the parent halo the black hole is in. This is used if 'AGN_jet_velocity_model' is 'BlackHoleMass'. + v_jet_BH_mass_scaling_reference_mass_Msun: 3.4e3 # The reference mass used in the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'. + v_jet_BH_mass_scaling_slope: 0.65 # The slope of the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'. + v_jet_mass_loading: 400. # The constant mass loading to use if 'AGN_jet_velocity_model' is MassLoading. + v_jet_xi: 0.707 # The numerical multiplier by which the jet velocity formula is scaled, if 'AGN_jet_velocity_model' is 'Local' or 'SoundSpeed'. The appropriate values (to exactly obtain the formulas as derived) are 0.63 and 0.707 for the two, respectively. + v_jet_min_km_p_s: 500 # The minimal jet velocity. This is used if 'AGN_jet_velocity_model' is 'BlackHoleMass', 'MassLoading', 'Local' or 'SoundSpeed'. + opening_angle_in_degrees: 7.5 # The half-opening angle of the jet in degrees. Should use values < 15 unless for tests. + N_jet: 2 # Target number of particles to kick as part of a single jet feedback event. Should be a multiple of 2 to ensure approximate momentum conservation (we always kick particles in pairs, one from each 'side' of the BH, relative to the spin vector). + AGN_jet_feedback_model: MinimumDistance # Which particles to kick from the black hole smoothing kernels. Should be 'SpinAxis', 'MinimumDistance', 'MaximumDistance' or 'MinimumDensity' + eps_f_jet: 1. # Coupling efficiency for jet feedback. No reason to expect this to be less than 1. + fix_jet_efficiency: 0 # Global switch whether to fix jet efficiency to a particular value [1], or use a spin-dependant formula [0]. If used, jets will be launched exclusively along the z axis. Should be set to 1 only for tests. + jet_efficiency: 0.1 # The constant jet efficiency used if 'fix_jet_efficiency' is set to 1. + fix_radiative_efficiency: 0 # Global switch whether to fix the radiative efficiency to a particular value [1], or use a spin-dependant formula [0]. + radiative_efficiency: 0.1 # The constant jet efficiency used if 'fix_radiative_efficiency' is set to 1. Otherwise, this value is used to define the Eddington accretion rate. + TD_region: B # How to treat the subgrid accretion disk if it is thin, according to the Shakura & Sunyaev (1973) model. If set to B, region b will be used. If set to C, region c will be used. + include_GRMHD_spindown: 1 # Whether to include high jet spindown rates from GRMHD simulations [1], or use an analytical formula that assumes extraction of energy from the rotational mass/energy of the BH. + include_ADIOS_suppression: 0 # Whether to suppress the accretion rate in the fully thick disc regime [1] (Eddington rate below 0.2alpha^2) by the amount expected to be taken away by isotropic kinetic disk winds. + ADIOS_R_in: 30. # If include_ADIOS_accr_suppression is set to 1, this parameter controls the inner radius within which winds are not important. + ADIOS_s: 0.4 # Slope of the accretion rate - radius relationship if include_ADIOS_accr_suppression is set to 1. + turn_off_secondary_feedback: 1 # If set to 1, there will be only radiative (thermal) feedback in the thin disk mode, and only jets in the thick disk mode. + jet_h_r_slope: 1. # The slope of the dependence of jet efficiency on aspect ratio of the subgrid accretion disk, H/R. Default value is 1, and another reasonable value is 0 (same jet efficiency for all disks). Reality could be anything in between. This parameter is only used if turn_off_secondary_feedback is set to 0. + delta_ADAF: 0.2 # Electron heating parameter, which controls the strength of radiative feedback in thick disks. Should be between 0.1 and 0.5. This parameter is only used if turn_off_secondary_feedback is set to 0. + include_slim_disk: 0 # Global switch whether to include super-Eddington accretion, modeled as the slim disk. If set to 0, disks will be considered thin even at very large accretion rates. + TD_SD_eps_r_threshold: 0.75 # Parameter controlling the transition from thin to slim disk. Accretion disk will be slim if radiative efficiency satisfies eps_slim < TD_SD_eps_r_threshold * eps_thin. This parameter is only used if include_slim_disk is set to 1. + +Most of these parameters should work well generally, and should not be changed except for tests. We will discuss only some of the more important ones. You can choose whether to have only the thick and thin disk (low and high BH accretion rates, respectively), or you can also include the slim disk at super-Eddington rates with ``include_slim_disk``. You can control what type of feedback you (do not) want with ``include_jets`` and ``turn_off_radiative_feedback``. If you choose to turn off jets, everything will be modeled as a thin disk (regardless of accretion rate), since jets go hand-in-hand with the thick and the slim disk. Similarly, if you turn off radiation, everything will be treated as a thick disk. + +If you set ``use_var_v_jet: 0``, you will need to change ``v_jet``, the kicking velocity of particles, depending on what system you are simulating. You should typically choose values at least 10 times larger than the sound speed of the hot gas in your most massive haloes (e.g. 1500 km/s for a MW-type galaxy and 10 000 km/s for a :math:`10^{14}` :math:`\mathrm{M}_\odot` halo). If, on the other hand, you set ``use_var_v_jet: 1``, the launching velocities will vary on their own depending on the typical sound speed (virial velocity) of the hot gas in the haloes. You then need to set ``v_jet_cs_ratio`` to values :math:`\gg1` (10-30 works well) in order to have significant shocking. diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/plots.py b/doc/RTD/source/SubgridModels/AGNSpinJets/plots.py new file mode 100644 index 0000000000000000000000000000000000000000..6c4045d07be9a771ce00a7c3021517ddf044973e --- /dev/null +++ b/doc/RTD/source/SubgridModels/AGNSpinJets/plots.py @@ -0,0 +1,834 @@ +import os + +if ( + os.path.exists("efficiencies.png") + and os.path.exists("modes.png") + and os.path.exists("spec_ang_mom.png") + and os.path.exists("spinup.png") +): + # no need to rerun script + exit() + +import numpy as np +from scipy.optimize import fsolve + + +def Z1(x): + return 1 + (1 - x ** 2) ** 0.3333 * ( + (1 + np.absolute(x)) ** 0.3333 + (1 - np.absolute(x)) ** 0.3333 + ) + + +def Z2(x): + return np.sqrt(3 * x ** 2 + Z1(x) ** 2) + + +def r_hor(x): + return 1 + np.sqrt(1 - x ** 2) + + +def r_isco(x): + return 3 + Z2(x) - np.sign(x) * np.sqrt((3 - Z1(x)) * (3 + Z1(x) + 2 * Z2(x))) + + +def eps_NT(x): + return 1 - np.sqrt(1 - 2 / 3 * 1 / r_isco(x)) + + +def eff_sd(m, a, limit): + return limit - ( + 1 + / 10 + * 1 + / m + * ( + 0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / m) + + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / m) + ) + * (0.9663 - 0.9292 * a) ** -0.5693 + ) / (1 - np.sqrt(1 - 2 / (3 * r_isco(a)))) + + +def find_root(a, limit): + return fsolve(eff_sd, x0=1, args=(a, limit))[0] + + +def A(x): + return (0.9663 - 0.9292 * x) ** -0.5693 + + +def B(x): + return (4.627 - 4.445 * x) ** -0.5524 + + +def C(x): + return (827.3 - 718.1 * x) ** -0.706 + + +def m_dot_crit1(x, limit): + C1 = C(x) / B(x) + eps_1 = 16 * limit / (0.985 * A(x)) * eps_NT(x) + N1 = 0.015 / 0.985 + res1 = ( + 1.6 + / B(x) + * 1 + / (2 * C1 * eps_1) + * ( + np.sqrt( + (C1 * (1 - eps_1) + N1 - eps_1) ** 2 + 4 * eps_1 * C1 * (N1 - eps_1 + 1) + ) + + C1 * (1 - eps_1) + + N1 + - eps_1 + ) + ) + return res1 + + +def m_dot_crit2(x, limit): + C1 = C(x) / B(x) + eps_1 = 16 * limit / (0.985 * A(x)) * eps_NT(x) + N1 = 0.015 / 0.985 + res1 = ( + 1.6 + / B(x) + * 1 + / (2 * C1 * eps_1) + * ( + -1 + * np.sqrt( + (C1 * (1 - eps_1) + N1 - eps_1) ** 2 + 4 * eps_1 * C1 * (N1 - eps_1 + 1) + ) + + C1 * (eps_1 - 1) + + N1 + - eps_1 + ) + ) + return res1 + + +def beta(alfa): + return 1 / (1 + 2 * alfa) + + +def gamma(alpha): + return (8 - 3 * beta(alpha)) / (6 - 3 * beta(alpha)) + + +def t1(alpha): + return -0.2703 * gamma(alpha) + 1.3603 + + +def t2(alpha): + return -0.94 + 4.475 * (gamma(alpha) - 1.444) - 5.1402 * (gamma(alpha) - 1.444) ** 2 + + +def t3(alpha): + return -0.84 * np.log10(alpha) - 0.919 + 0.643 * np.exp(-0.209 / alpha) + + +def t4(x): + return (0.6365 * r_isco(x) - 0.4828) * (1 + 11.9 * np.exp(-0.838 * r_isco(x) ** 4)) + + +def t5(x): + return 1.444 * np.exp(-1.01 * r_isco(x) ** 0.86) + 0.1 + + +def T(x, alpha): + return ( + 0.31 + * (1 + (t4(x) / r_hor(x)) ** 0.9) ** (t2(alpha) + t3(alpha)) + * 1 + / (r_hor(x) - t5(x)) ** (t1(alpha)) + ) + + +def eta(x, alpha): + return 1 + gamma(alpha) / (gamma(alpha) - 1) * T(x, alpha) + + +def f1(x): + return 0.0871 * r_isco(x) - 0.1082 + + +def f2(alpha): + return 0.5 - 7.798 * (gamma(alpha) - 1.333) ** 1.26 + + +def f3(x): + return 0.153 * (r_isco(x) - 0.6) ** 0.3 + 0.105 + + +def f4(x, alpha): + return ( + f3(x) + * (0.9 * gamma(alpha) - 0.2996) + * (1.202 - 0.08 * (np.log10(alpha) + 2.5) ** 2.6) + ) + + +def f5(alpha): + return -1.8 * gamma(alpha) + 4.299 - 0.018 + 0.018 * (np.log10(alpha) + 2) ** 3.571 + + +def f6(x, alpha): + return ( + f4(x, alpha) + * (((0.14 * np.log10(r_hor(x) ** f5(alpha)) + 0.23) / f4(x, alpha)) ** 10 + 1) + ** 0.1 + ) + + +def L_adv(x, alpha): + return ( + f2(alpha) + + (f1(x) + 10 ** f6(x, alpha)) * (1.15 - 0.03 * (np.log10(alpha) + 3) ** 2.37) + ) / eta(x, alpha) + + +a = np.arange(-1, 1, 0.0001) +mdotcrit1 = m_dot_crit1(a, 0.5) +mdotcrit2 = m_dot_crit2(a, 0.5) +m_a_90 = [find_root(x, 0.9) for x in a] +m_a_75 = [find_root(x, 0.75) for x in a] +m_a_50 = [find_root(x, 0.5) for x in a] + +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec + +fig = plt.figure(figsize=(8, 6)) + +plt.style.use("classic") +plt.fill_between(a, [0.0001 for x in a], [0.028 for x in a], color="blue", alpha=0.2) +plt.fill_between(a, [0.028 for x in a], mdotcrit1, color="red", alpha=0.2) +plt.fill_between(a, mdotcrit1, [375 for x in a], color="orange", alpha=0.2) +plt.ylabel("$\dot{m}$", fontsize=24, usetex=True) +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.yscale("log") +plt.axis([-1, 1, 0.001, 100]) +plt.text(-0.22, 0.004, "Thick disc", fontsize=20) +plt.text(-0.2, 0.33, "Thin disc", fontsize=20) +plt.text(-0.2, 18, "Slim disc", fontsize=20) +plt.minorticks_on() +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) + +plt.savefig("modes.png", bbox_inches="tight") +plt.close() + +phi = -20.2 * a ** 3 - 14.9 * a ** 2 + 34.0 * a + 52.6 +horizon_ang_vel = a / (2 * (1 + np.sqrt(1 - a ** 2))) +jet_factor = ( + 0.05 + / (4.0 * np.pi) + * 1 + / 0.3 + * phi ** 2 + * horizon_ang_vel ** 2 + * (1.0 + 1.38 * horizon_ang_vel ** 2 - 9.2 * horizon_ang_vel ** 4) +) +Z1_j = np.array( + [ + 1 + (1 - x ** 2) ** 0.333 * ((1 + abs(x)) ** 0.333 + (1 - abs(x)) ** 0.333) + for x in a + ] +) +Z2_j = np.array(np.sqrt(3 * a ** 2 + Z1_j ** 2)) +r_iso = 3 + Z2_j - np.sign(np.array(a)) * np.sqrt((3 - Z1_j) * (3 + Z1_j + 2 * Z2_j)) +eps_TD = 1 - np.sqrt(1 - 2 / (3 * r_iso)) +eps_ADAF1 = 0.144 * (6 / r_iso) * eps_TD * min(1, 0.028 / 0.0044) +eps_ADAF2 = 0.144 * (6 / r_iso) * eps_TD * min(1, 0.001 / 0.0044) +Jet_ADAF = jet_factor * 0.3 +Jet_SD = 0.22 * jet_factor +Jet_TD1 = 10 ** -3 * 0.1 ** (-0.1) * 100 ** 0.2 * 10 ** (2 * 0.1) * jet_factor +Jet_TD2 = 10 ** -3 * 0.1 ** (-0.1) * 10 ** (-1 * 0.1) * jet_factor +eps_SD1 = ( + 1 + / 10 + * 1 + / 1 + * ( + 0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / 1) + + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / 1) + ) + * (0.9663 - 0.9292 * a) ** -0.5693 +) +eps_SD2 = ( + 1 + / 10 + * 1 + / 50 + * ( + 0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / 50) + + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / 50) + ) + * (0.9663 - 0.9292 * a) ** -0.5693 +) + +mdot_bh_ADAF1 = (1 - Jet_ADAF / 4.447) * (1 - eps_ADAF1 - Jet_ADAF) +mdot_bh_ADAF2 = (1 - Jet_ADAF / 4.447) * (1 - eps_ADAF2 - Jet_ADAF) +mdot_bh_ADAF3 = (1 - Jet_ADAF / 11.56) * (1 - eps_ADAF1 - Jet_ADAF) +mdot_bh_ADAF4 = (1 - Jet_ADAF / 11.56) * (1 - eps_ADAF2 - Jet_ADAF) +mdot_bh_TD1 = (1 - Jet_TD1 / 4.447) * (1 - eps_TD - Jet_TD1) +mdot_bh_TD2 = (1 - Jet_TD2 / 4.447) * (1 - eps_TD - Jet_TD2) + + +fig = plt.figure(figsize=(18, 4)) +fig.subplots_adjust(wspace=0, hspace=0, top=1, bottom=0) +gs = gridspec.GridSpec(1, 3, width_ratios=[1, 1, 1]) +plt.style.use("classic") + +plt.subplot(gs[0]) +plt.plot( + a, + eps_ADAF2, + linewidth=2, + label="$\epsilon_\mathrm{rad}(\dot{m}<0.0044)$", + color="red", +) +plt.plot( + a, + eps_ADAF1, + linewidth=2, + label="$\epsilon_\mathrm{rad}(\dot{m}=0.028)$", + color="red", + linestyle="--", +) +plt.plot(a, 0.97 * Jet_ADAF, linewidth=2, label="$\epsilon_\mathrm{jet}$", color="blue") +plt.fill_between(a, eps_ADAF1, eps_ADAF2, color="red", alpha=0.2) +plt.ylabel("$\epsilon_\mathrm{feedback}$", fontsize=24, usetex=True) +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.legend(loc="upper left", prop={"size": 13}) +plt.xticks([-1, -0.5, 0, 0.5, 1], [-1, -0.5, 0, 0.5, 1]) +plt.yticks( + [0.0001, 0.001, 0.01, 0.1, 1, 10, 100], + ["", 10 ** (-3), 10 ** (-2), 10 ** (-1), 10 ** (-0), 10 ** (1), 10 ** (2)], +) +plt.minorticks_on() +plt.axis([-1, 1, 0.0001, 10]) +plt.yscale("log") +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.title("Thick disc", fontsize=16) + +plt.subplot(gs[1]) +plt.plot(a, 0.97 * eps_TD, linewidth=2, label="$\epsilon_\mathrm{rad}$", color="red") +plt.plot( + a, + Jet_TD2, + linewidth=2, + label="$\epsilon_\mathrm{jet}(\dot{m}=0.028,M_\mathrm{BH}=10^9 \mathrm{M}_\odot)$", + color="blue", +) +plt.plot( + a, + Jet_TD1, + linewidth=2, + label="$\epsilon_\mathrm{jet}(\dot{m}=1,M_\mathrm{BH}=10^6 \mathrm{M}_\odot)$", + color="blue", + linestyle="--", +) +plt.fill_between(a, Jet_TD1, Jet_TD2, color="blue", alpha=0.2) +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.yscale("log") +plt.legend(loc="upper left", prop={"size": 13}) +plt.xticks([-1, -0.5, 0, 0.5, 1], ["", -0.5, 0, 0.5, 1]) +plt.axis([-1, 1, 0.0001, 10]) +plt.yticks([0.001, 0.01, 0.1, 1, 10], ["", "", "", "", ""]) +plt.minorticks_on() +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.title("Thin disc", fontsize=16) + +plt.subplot(gs[2]) +plt.plot( + a, eps_SD1, linewidth=2, label="$\epsilon_\mathrm{rad}(\dot{m}=1)$", color="red" +) +plt.plot( + a, + eps_SD2, + linewidth=2, + label="$\epsilon_\mathrm{rad}(\dot{m}=50)$", + color="red", + linestyle="--", +) +plt.plot(a, Jet_SD, linewidth=2, label="$\epsilon_\mathrm{jet}$", color="blue") +plt.fill_between(a, eps_SD1, eps_SD2, color="red", alpha=0.2) +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.yscale("log") +plt.legend(loc="upper left", prop={"size": 13}) +plt.xticks([-1, -0.5, 0, 0.5, 1], ["", -0.5, 0, 0.5, 1]) +plt.axis([-1, 1, 0.0001, 10]) +plt.yticks([0.001, 0.01, 0.1, 1, 10], ["", "", "", "", ""]) +plt.minorticks_on() +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.title("Slim disc", fontsize=16) + +plt.savefig("efficiencies.png", bbox_inches="tight") + +L_isco1 = [2 / 3 * 1 / np.sqrt(3) * (1 + 2 * np.sqrt(3 * r_isco(x) - 2)) for x in a] + +plt.style.use("classic") +fig = plt.figure(figsize=(8, 6), linewidth=4) +plt.plot(a, L_isco1, linewidth=2, label="$\ell_\mathrm{ISCO}$", color="red") +plt.plot( + a, + 0.45 * np.array(L_isco1), + linewidth=3, + linestyle="--", + label=r"$0.45\ell_\mathrm{ISCO}$", + color="red", +) +plt.plot( + a, + [L_adv(x, 0.1) for x in a], + linewidth=2, + label=r"$\ell_\mathrm{adv},\alpha=0.1$", + color="green", +) +plt.plot( + a, + [L_adv(x, 0.2) for x in a], + linewidth=2, + label=r"$\ell_\mathrm{adv},\alpha=0.2$", + color="teal", +) +plt.plot( + a, + [L_adv(x, 0.3) for x in a], + linewidth=2, + label=r"$\ell_\mathrm{adv},\alpha=0.3$", + color="purple", +) +plt.ylabel("$\ell_\mathrm{in}$", fontsize=24, usetex=True) +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.legend(loc="upper right", prop={"size": 14}) +plt.minorticks_on() +plt.axis([-1, 1, 0, 5]) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) + +plt.savefig("spec_ang_mom.png", bbox_inches="tight") +plt.close() + +z1 = np.array( + [ + 1 + (1 - x ** 2) ** 0.333 * ((1 + abs(x)) ** 0.333 + (1 - abs(x)) ** 0.333) + for x in a + ] +) +z2 = np.array(np.sqrt(3 * a ** 2 + z1 ** 2)) +r_iso = 3 + z2 - np.sign(np.array(a)) * np.sqrt((3 - z1) * (3 + z1 + 2 * z2)) + +phi = -20.2 * a ** 3 - 14.9 * a ** 2 + 34.0 * a + 52.6 +horizon_ang_vel = a / (2 * (1 + np.sqrt(1 - a ** 2))) +jet_factor = ( + 0.05 + / (4.0 * np.pi) + * 1 + / 0.3 + * phi ** 2 + * horizon_ang_vel ** 2 + * (1.0 + 1.38 * horizon_ang_vel ** 2 - 9.2 * horizon_ang_vel ** 4) +) + +da_TD_acc_only = 2 / 3 * 1 / np.sqrt(3) * ( + 1 + 2 * np.sqrt(3 * r_iso - 2) +) - 2 * a * np.sqrt(1 - 2 / (3 * r_iso)) +da_TD_Benson = ( + 2 / 3 * 1 / np.sqrt(3) * (1 + 2 * np.sqrt(3 * r_iso - 2)) + - 2 * a * np.sqrt(1 - 2 / (3 * r_iso)) + - (1.25 * 10 ** -3 * 0.1 ** (-0.1) * 100 ** 0.2 * 10 ** (2 * 0.1) * jet_factor) + * 2 + / a + * (np.sqrt(1 - a ** 2)) + * (1 + np.sqrt(1 - a ** 2)) +) +da_ADAF_acc_only = L_adv(a, 0.1) - 2 * a +da_ADAF_Benson = ( + L_adv(a, 0.1) + - 2 * a + - (jet_factor * 0.3) * 2 / a * (np.sqrt(1 - a ** 2)) * (1 + np.sqrt(1 - a ** 2)) +) +eps_SD = ( + 1 + / 10 + * 1 + / 10 + * ( + 0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / 10) + + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / 10) + ) + * (0.9663 - 0.9292 * a) ** -0.5693 +) +da_SD_acc_only = L_adv(a, 0.1) - 2 * a * (1 - eps_SD) +da_SD_Benson = ( + L_adv(a, 0.1) + - 2 * a * (1 - eps_SD) + - (jet_factor * 0.22) * 2 / a * (np.sqrt(1 - a ** 2)) * (1 + np.sqrt(1 - a ** 2)) +) + + +fig = plt.figure(figsize=(18, 4)) +fig.subplots_adjust(wspace=0, hspace=0, top=1, bottom=0) +gs = gridspec.GridSpec(1, 3, width_ratios=[1, 1, 1]) +plt.style.use("classic") + +plt.subplot(gs[0]) +plt.plot(a, da_TD_acc_only, linewidth=2, label="Accretion only", color="black") +plt.plot(a, da_TD_Benson, linewidth=1.5, label="Jet spindown included", color="blue") +plt.plot(a, [0 for x in a], linewidth=1.5, color="black", linestyle="--") +plt.ylabel("$\mathrm{d}a/\mathrm{d}\ln M_\mathrm{BH,0}$", fontsize=24, usetex=True) +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.legend(loc="lower left", prop={"size": 15}) +plt.minorticks_on() +plt.axis([-1, 1, -4, 7]) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.title("Thin disc", fontsize=16) + +plt.subplot(gs[1]) +plt.plot(a, da_ADAF_acc_only, linewidth=2, label="Accretion only", color="black") +plt.plot(a, da_ADAF_Benson, linewidth=1.5, label="Jet spindown included", color="blue") +plt.plot(a, [0 for x in a], linewidth=1.5, color="black", linestyle="--") +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.xticks([-1.0, -0.5, 0.0, 0.5, 1.0], ["", -0.5, 0.0, 0.5, 1.0]) +plt.yticks([-8, -6, -4, -2, 0, 2, 4, 6, 8], ["", "", "", "", "", "", "", "", ""]) +plt.minorticks_on() +plt.axis([-1, 1, -4, 7]) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.title("Thick disc", fontsize=16) + +plt.subplot(gs[2]) +plt.plot(a, da_SD_acc_only, linewidth=2, label="Accretion only", color="black") +plt.plot(a, da_SD_Benson, linewidth=1.5, label="Jet spindown included", color="blue") +plt.plot(a, [0 for x in a], linewidth=1.5, color="black", linestyle="--") +plt.xlabel("$a$", fontsize=24, usetex=True) +plt.tick_params(axis="y", right=True, direction="in") +plt.xticks([-1.0, -0.5, 0.0, 0.5, 1.0], ["", -0.5, 0.0, 0.5, 1.0]) +plt.yticks([-8, -6, -4, -2, 0, 2, 4, 6, 8], ["", "", "", "", "", "", "", "", ""]) +plt.minorticks_on() +plt.axis([-1, 1, -4, 7]) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=8, + width=1.2, + which="major", + labelsize=16, +) +plt.tick_params( + axis="x", + direction="in", + bottom=True, + top=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.tick_params( + axis="y", + direction="in", + left=True, + right=True, + length=4, + width=0.9, + which="minor", + labelsize=16, +) +plt.title("Slim disc", fontsize=16) + +plt.savefig("spinup.png", bbox_inches="tight") diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/theory.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/theory.rst new file mode 100644 index 0000000000000000000000000000000000000000..b61564aef0da52895d7b04a060a9eb25e8e6a58b --- /dev/null +++ b/doc/RTD/source/SubgridModels/AGNSpinJets/theory.rst @@ -0,0 +1,251 @@ +.. AGN spin and jet model + Filip Husko, 1 April 2022 + +.. AGN_spin_jet: + + +Model summary +------------- + +Here we provide a comprehensive summary of the model. In order to more easily visualize the model, it is recommended to run the python script in order to generate plots that show various aspects of the model. + +Any model for realistic AGN jets must include black hole spin since jet powers depend steeply on spin, and because including spin provides a well-defined direction for the jets to be launched in. The spin (angular momentum) of BHs is best represented through the dimensionlesss spin parameter :math:`a=J_\mathrm{BH}c/M_\mathrm{BH}^2 G`, where :math:`J_\mathrm{BH}` is its angular momentum. For theoretical reasons, its magnitude cannot grow above 1. It can be positive, representing prograde accretion, or negative, representing retrograde accretion. + +Jet powers, in addition to depending on spin, also depend on which accretion state the black hole is in. We refer to these states by the shape of the accretion disk that surrounds the BH. We include three accretion states: the thick (or advection-dominated accretion flow; ADAF), thin (standard) and slim disk. Our main reference points for these disks are the following papers: `Shakura & Sunyaev (1973) <https://ui.adsabs.harvard.edu/abs/1973A%26A....24..337S/abstract>`_, `Narayan & Yi (1994) <https://ui.adsabs.harvard.edu/abs/1994ApJ...428L..13N/abstract>`_ and `Wang & Zhou. (1999) <https://ui.adsabs.harvard.edu/abs/1999ApJ...516..420W/abstract>`_, respectively. + +The thick disk appears at low accretion rates, has very strong jets and is inefficient at spinning up the black hole. The thin disk, appearing at intermediate accretion rates, typically has weak jets, strong radiation and efficiently spins up the black hole. The slim disk, corresponding to super-Eddington accretion, has features of both: in terms of geometry, orbits and angular momentum, it is similar to the thick disk. It is optically thin, leading to strong radiation. However, it also has strong jets. We assume that each subgrid accretion disk launches jets and radiates at the same time, regardless of the type it is. However, we use expressions for the jet and radiative efficiencies that depend on the type of the disk, and which are physically motivated. + +Transitions from one accretion state to another +----------------------------------------------- + +.. figure:: modes.png + :width: 600px + :align: center + :figclass: align-center + :alt: Accretion regimes + + The type of accretion disk surrounding the BHs depending on their accretion rates and spins. The transition between the thick and thin disk is calculated assuming the viscosity parameter :math:`\alpha=0.2`, while the transition from thin to slim disk is assumed to occur when the latter is :math:`F=0.5` times as radiatively efficienct as the former. + +The state of the subgrid accretion disk depends mostly on the Eddington fraction, i.e. the (dimensionless) accretion rate of the BH in units of the Eddington accretion rate, which we denote as :math:`\dot{m}`. We assume that the subgrid accretion disk is thick for :math:`\dot{m}<0.03`, based on observations (`Russell et al. 2013 <https://ui.adsabs.harvard.edu/abs/2013MNRAS.432..530R/abstract>`_). This also allows us to constrain the value of one of the main parameters in any accretion model: the viscosity parameter :math:`\alpha` (which is related to the kinematic viscosity :math:`\nu`and sound speed :math:`c_\mathrm{s}` through :math:`\nu=\alpha c_\mathrm{s}H`, with :math:`c_\mathrm{s}` the sound speed and :math:`H` the disk half-thickness). Numerical calculations suggest that thick disks are present for :math:`\dot{m}<0.4\alpha^2` (`Yuan & Narayan 2014 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_), and this agrees with observations if :math:`\alpha=0.25-0.3`. These values agree very well with more direct observational estimates, which suggest :math:`\alpha=0.2-0.3` (`Martin et al. 2019 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_). + +The transition from the thin to the slim disk should occur around :math:`\dot{m}\approx 1`. However, the exact physics of this transition is not well understood. There is likely some spin dependence of the critical accretion rate, due to different radiative physics depending on spin. One of the main properties of slim disks is that they are less radiatively efficient than thin disks (`Sadowski et al. 2014 <https://ui.adsabs.harvard.edu/abs/2014MNRAS.439..503S/abstract>`_). We thus assume that the transition occurs when the radiative efficiency of a slim disk, :math:`\epsilon_\mathrm{r,SD}`, falls below some fraction of the radiative efficiency of a thin disk, :math:`\epsilon_\mathrm{r,TD}`. We quantify this as :math:`\epsilon_\mathrm{r,SD}<F\epsilon_\mathrm{r,SD}`, with :math:`F\approx 0.5` a free parameter. We give the expressions for both of the efficiencies below. + +Jet efficiencies +---------------- + +The jet efficiency is related to the jet power through :math:`\epsilon_\mathrm{j}=P_\mathrm{j}/\dot{M}_\mathrm{BH,0}c^2`, where :math:`\dot{M}_\mathrm{BH,0}` is the accretion rate measured in the simulation, e.g. the Bondi rate). We use the formula for the jet efficiency based on general-relativistic, magneto-hydrodynamical (GRMHD) simulations by `Tchekhovskoy et al. (2010) <https://ui.adsabs.harvard.edu/abs/2010ApJ...711...50T/abstract>`_: + +.. math:: + \epsilon_\mathrm{j}=\frac{\kappa}{4\pi}\bigg(\frac{H/R}{0.3}\bigg)^\eta \phi_\mathrm{BH}^2\Omega_\mathrm{BH}^2\big(1+1.38\Omega_\mathrm{BH}^2-9.2\Omega_\mathrm{BH}^4\big), + +where :math:`\kappa\approx0.05` is a numerical factor which depends on the initial geometry of the magnetic field, :math:`\phi_\mathrm{BH}` is the dimensionless magnetic flux threading the horizon (see original paper for precise definition), and :math:`\Omega_\mathrm{BH}=a/2r_\mathrm{H}` is the (dimensionless) angular velocity of the black hole event horizon. Here, :math:`r_\mathrm{H}=1+\sqrt{1-a^2}` is the radius of the horizon in units of the gravitational radius :math:`R_\mathrm{G}=M_\mathrm{BH}G/c^2`. The formula above, for the jet efficiency, agrees very well with the results from higher-resolution simulations performed by `Narayan et al. (2021) <https://ui.adsabs.harvard.edu/abs/2010ApJ...711...50T/abstract>`_, who provide the following fit for the magnetic flux as a function of spin: + +.. math:: + \phi_\mathrm{BH}(a)=-20.2a^3-14.9a^2+34a+52.6. + +The `Tchekhovskoy et al. (2010) <https://ui.adsabs.harvard.edu/abs/2010ApJ...711...50T/abstract>`_ jet efficiency depends very steeply on spin (:math:`\epsilon_\mathrm{j}\propto a^2` for small spin and :math:`\epsilon_\mathrm{j}\propto a^6` near :math:`a=1`). It can reach values above 100 per cent for large spins, and is also different (weaker) for negative spins. + +The dependence of the jet efficiency on the type of accretion disk comes through the factor that depends on the aspect ratio :math:`H/R`, since accretion disks differ in this quantity. Theoretical, self-similar models of thick disks suggest :math:`H/R=0.5` (`Narayan & Yi 1995b <https://ui.adsabs.harvard.edu/abs/1995ApJ...452..710N/abstract>`_), but we instead take :math:`H/R=0.3`, more in line with simulations. For slim disks, which have received less attention in simulations, we assume the value :math:`H/R=1/(2\sqrt{5})\approx 0.2` (based on the self-similar model by `Wang & Zhou. 1999 <https://ui.adsabs.harvard.edu/abs/1999ApJ...516..420W/abstract>`_). + +Thin disks are, not surprisingly, much thinner. The value of :math:`H/R` in this regime is not a constant, but rather depends on the BH mass and accretion rate, slightly on radius and also on the viscosity parameter :math:`\alpha`. Thin disks have three different regions in the `Shakura & Sunyaev (1973) <https://ui.adsabs.harvard.edu/abs/1973A%26A....24..337S/abstract>`_ model. For simplicity, we model the whole disk as being represented with only one region. In region a), the innermost one, radiation dominates over gas pressure. It is typically very small or doesn't exist at all, so we disregard it as a possibility. In regions b) and c), gas pressure dominates over radiation pressure. In b), electrons dominate in the opacity, while in c), free-free absorption dominates. We leave both regions as a possibility, and leave the choice as a free parameter in the model (not likely to lead to large differences in galaxy/BH evolution). The expressions for the aspect ratio in these regions are + +.. math:: + \bigg(\frac{H}{R}\bigg)_\mathrm{TD,b} = 1.25\times10^{-3} \alpha^{-1/10}\dot{m}^{1/5}\bigg(\frac{M_\mathrm{BH}}{10^8\hspace{0.5mm}\mathrm{M}_\odot}\bigg)^{-1/10}\bigg(\frac{R}{2R_\mathrm{G}}\bigg)^{1/20} + +in region b) and + +.. math:: + \bigg(\frac{H}{R}\bigg)_\mathrm{TD,c} = 1.15\times10^{-3} \alpha^{-1/10}\dot{m}^{3/20}\bigg(\frac{M_\mathrm{BH}}{10^8\hspace{0.5mm}\mathrm{M}_\odot}\bigg)^{-1/10}\bigg(\frac{R}{2R_\mathrm{G}}\bigg)^{1/8} + +in region c). + +The jet efficiency also depends on the slope :math:`\eta`. Classical jet theory (`Meier 2001 <https://ui.adsabs.harvard.edu/abs/2001Sci...291...84M/abstract>`_) suggests that jet powers depend on the aspect ratio linearly, so :math:`\eta=1`. This is also in line with some simulations finding a reduction in jet efficiencies with the aspect ratio (e.g. `Tchekhovskoy et al. 2014 <https://ui.adsabs.harvard.edu/abs/2014MNRAS.437.2744T/abstract>`_). In this scenario, jets launched from thin disks are of order :math:`\approx100` times less powerful than those launched from thick disks. On the other hand, some simulations of thin disks have found jet efficiencies similar to thick disk ones (e.g. `Liska et al. 2019 <https://ui.adsabs.harvard.edu/abs/2019MNRAS.487..550L/abstract>`_), which is supported by observations of blazars (`Ghisellini et al. 2014 <https://ui.adsabs.harvard.edu/abs/2014Natur.515..376G/abstract>`_). In this picture, thin jets are approximately as efficient as thick disk ones, which can in our case be implemented as :math:`\eta=0`. The reality is likely to be somwhere in between. Note that the choice of :math:`\eta` likely has a strong impact on the evolution of galaxies and BHs; our default choice is the classical picture in which :math:`\eta=1`. + +.. figure:: efficiencies.png + :width: 1200px + :align: center + :figclass: align-center + :alt: Efficiencies + + Feedback efficiencies (jet - blue, radiation - red) for all three accretion disk types. Shaded regions represent likely ranges of efficiencies (where the efficiencies depend on mass and/or accretion rate). The thin disk jet efficiencies were computed assuming the slope of the efficiency vs. aspect ratio relation is :math:`\eta=1`, and the aspect ratios were computed for region b) of the Shakura & Sunyaev solution. Radiative efficiencies in the thick disk were computed assuming the electron heating parameter :math:`\delta=0.2`. + +Radiative efficiencies +---------------------- + +In the EAGLE and COLIBRE models, all subgrid accretion disks are effectively thin, and the BH is always assumed to be in this regime. In our model, the radiative efficiency (defined in an analagous way to the jet efficiency, but using the luminosity) is no longer fixed at a value of order :math:`10` per cent. Instead, we use spin-dependant formulas that vary with the type of disk. In the thin disk, the radiative efficiency :math:`\epsilon_\mathrm{r,TD}` is related to the binding energy at the innermost stable circular orbit (ISCO) and is given by + +.. math:: + \epsilon_\mathrm{r,TD}(a) = 1-e_\mathrm{ISCO}(a)=1-\sqrt{1-\frac{2}{3r_\mathrm{ISCO}(a)}}. + +Here, :math:`r_\mathrm{ISCO}` is the radius of the ISCO in gravitational radii (see e.g. appendix B of `Fiacconi et al. 2018 <https://ui.adsabs.harvard.edu/abs/2018MNRAS.477.3807F/abstract>`_ for an expression giving the spin dependence). The radiative efficiency of the thin disk grows slowly from its minimum value of :math:`\approx4` per cent for :math:`a=-1` to :math:`\approx5.5` per cent for :math:`a=0`. For positive spins it grows more steeply; it is :math:`10` per cent by :math:`a=0.65`. Beyond that the dependence steepens even further, with values of :math:`20`, :math:`30` and :math:`40` per cent reached at :math:`a=0.95`, :math:`a=0.997` and :math:`a=1`, respectively. + +In the thick disk regime, radiative efficiencies are lower by a factor :math:`\approx100` than jet efficiencies. The formulas we use are based on results by `Mahadevan (1997) <https://ui.adsabs.harvard.edu/abs/1997ApJ...477..585M/abstract>`_, who studied cooling processes of electrons (which dominate in the radiation) in the context of the original thick disc solution. They found two different regimes: for :math:`\dot{m}<\dot{m}_\mathrm{crit,visc}`, viscous heating dominates the heating of electrons, whereas for :math:`\dot{m}_\mathrm{crit,visc}<\dot{m}<\dot{m}_\mathrm{crit,ADAF}`, it is dominated by ion-electron heating. Here, :math:`\dot{m}_\mathrm{crit,visc}` is the transitional value between the two thick disc (ADAF) regimes, and :math:`\dot{m}_\mathrm{crit,ADAF}=0.4\alpha^2` is the transitional accretion rate which separates thin and thick discs. The radiative efficiency in the viscous heating regime is given by + +.. math:: + \epsilon_\mathrm{r,ADAF}=0.0002\epsilon_\mathrm{r,TD}\bigg(\frac{\delta_\mathrm{ADAF}}{0.0005}\bigg)\bigg(\frac{1-\beta}{0.5}\bigg)\bigg(\frac{6}{r_\mathrm{ISCO}}\bigg), + +while in the ion-heating regime it is given by + +.. math:: + \epsilon_\mathrm{r,ADAF}=0.2\epsilon_\mathrm{r,TD}\bigg(\frac{\dot{m}}{\alpha^2}\bigg)\bigg(\frac{\beta}{0.5}\bigg)\bigg(\frac{6}{r_\mathrm{ISCO}}\bigg). + +Here, :math:`\beta` is the ratio of gas pressure and total pressure (which includes the magnetic pressure). `Yuan & Narayan (2014) <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_ define a somewhat different parameter, :math:`\beta_\mathrm{ADAF}`, as the ratio of gas pressure and magnetic pressure. The two parameters are related by :math:`\beta=\beta_\mathrm{ADAF}/(1+\beta_\mathrm{ADAF})`. :math:`\beta_\mathrm{ADAF}` is not an independent parameter; many simulations have found that :math:`\alpha\beta_\mathrm{ADAF}\approx0.5` (e.g. `Begelman et al. 2021 <https://ui.adsabs.harvard.edu/abs/2022MNRAS.511.2040B/abstract>`_, see also `Yuan & Narayan 2014 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_ for a review), which we adopt. :math:`\delta_\mathrm{ADAF}` represents the fraction of viscous energy transferred to the electrons, and is constrained in theoretical studies between 0.1 and 0.5 (`Yuan & Narayan 2014 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_, `Sharma et al. 2007 <https://ui.adsabs.harvard.edu/abs/2007ApJ...667..714S/abstract>`_). Observations imply a value close to 0.2 (`Yuan et al. 2003 <https://ui.adsabs.harvard.edu/abs/2003ApJ...598..301Y/abstract>`_, `Liu & Wu 2013 <https://ui.adsabs.harvard.edu/abs/2013ApJ...764...17L/abstract>`_). The critical accretion rate between the two thick disc regimes can be found by ensuring that both formulas presented above yield the same radiative efficiency (at that accretion rate). This gives an accretion rate equal to + +.. math:: + \dot{m}_\mathrm{crit,visc}=0.0002\bigg(\frac{\delta_\mathrm{ADAF}}{0.0005}\bigg)\bigg(\frac{1-\beta}{\beta}\bigg)\alpha^2. + +For slim disks we take the radiative efficiency based on GRMHD simulations of super-Eddington accretion (for various BH spins) performed by `Sadowski et al. (2014) <https://ui.adsabs.harvard.edu/abs/2014MNRAS.439..503S/abstract>`_. `Madau et al. (2014) <https://ui.adsabs.harvard.edu/abs/2014ApJ...784L..38M/abstract>`_ found the following fitting function which represents the `Sadowski et al. (2014) <https://ui.adsabs.harvard.edu/abs/2014MNRAS.439..503S/abstract>`_ results: + +.. math:: + \epsilon_\mathrm{r,SD}=\frac{0.1}{\dot{m}}A(a)\bigg( \frac{0.985}{1.6/\dot{m}+B(a)}+\frac{0.015}{1.6/\dot{m}+C(a)}\bigg), + +where the three spin-dependant functions are given by :math:`A(a)=(0.9663-0.9292a)^{-0.5639}`, :math:`B(a)=(4.627-4.445a)^{-0.5524}` and :math:`C(a)=(827.3-718.1a)^{-0.7060}`. The radiative efficiency of slim disks, based on this formula, matches the thin disk radiative efficiency (given at the beginning of the section) at low accretion rates. At high accretion rates (:math:`\dot{m}\gtrapprox1`, but depending on spin), the radiative efficiency drops. These two formulas are used to decide when a disk transitions from thin to slim. + +Evolution of the black hole spin magnitude +------------------------------------------ + +.. figure:: spec_ang_mom.png + :width: 600px + :align: center + :figclass: align-center + :alt: Angular momenta + + Dimensionless pecific angular momentum of the thin disk at the innermost stable circular orbit (ISCO, solid red line), compared with the specific angular momentum at the inner radius (the event horizon) for advection-dominated flows (the thick and slim disk) for a few values of the viscosity parameter :math:`\alpha`. The dashed red line shows that the latter can be approximated as :math:`45` per cent of the former. + +The BH spin (or angular momentum) is, naturally, a vector. However, due to Lense-thirring torques (we discuss these in more detail below), the accretion disk is always either aligned or counteraligned with the rotational axis of the black hole. This means that almost all relevant quantities, such as the efficiencies discussed above, can be expressed as depending only on the magnitude of spin, but also allowing for a negative sign to account for counteraligned disks (retrograde accretion). This is also true for the evolution of the magnitude of spin. + +In the absence of jet spindown, the evolution of angular momentum is given simply by :math:`\dot{J}_\mathrm{BH}=L_\mathrm{in}\dot{M}_\mathrm{BH}`, where :math:`L_\mathrm{in}` is the specific angular momentum at the inner radius of the accretion disk. This can be transformed into an equation for spin evolution, yielding + +.. math:: + \frac{\mathrm{d}a}{\mathrm{d}\ln M_\mathrm{BH,0}}=\ell_\mathrm{in}-2a e_\mathrm{in}, + +where :math:`\ell_\mathrm{in}` is the specific angular momentum in units where :math:`G` and :math:`c` are equal to unity, and :math:`\mathrm{d}\ln M_\mathrm{BH,0}=\mathrm{d}M_\mathrm{BH,0}/M_\mathrm{BH}` is the logarithmic change in mass, not including losses due to radiation (`Fanidakis et al. 2011 <https://ui.adsabs.harvard.edu/abs/2011MNRAS.410...53F/abstract>`_). The specific binding energy can be related to the radiative efficiency through :math:`e_\mathrm{in}=1-\epsilon_\mathrm{r}` for all three accretion states (for the thick disc, the radiative efficiency is negligible for this application). + +For the thin disc, the inner radius :math:`R_\mathrm{in}` can be taken to be the radius of the ISCO, since orbits should quickly degrade within it. We thus take :math:`\ell_\mathrm{in}` as the specific angular momentum at the ISCO for the thin disc (the expression for which is given in e.g. Appendix B of `Fiacconi et al. 2018 <https://ui.adsabs.harvard.edu/abs/2018MNRAS.477.3807F/abstract>`_). For the thin disk, the spinup function (the equation shown above) is always positive, meaning that the BH will always be spun up to positive values. This means that the BH will be spun down if spin is negative, or spun up to an equilibrium value of :math:`a_\mathrm{eq}=1` if spin is positive. For advection-dominated disks (the thick and slim disk), we assume that :math:`\ell_\mathrm{in}` is :math:`45` per cent of the ISCO value, based on numerical GR calculations by `Popham & Gammie (1998) <https://ui.adsabs.harvard.edu/abs/1998ApJ...504..419P/abstract>`_. We base this assumption on fits of the `Popham & Gammie (1998) <https://ui.adsabs.harvard.edu/abs/1998ApJ...504..419P/abstract>`_ results done by `Benson & Babul (2009) <https://ui.adsabs.harvard.edu/abs/2009MNRAS.397.1302B/abstract>`_. We independently compared these fits to the ISCO values and found :math:`\ell_\mathrm{in}\approx0.45\ell_\mathrm{ISCO}` with no more than :math:`10` per cent error for all values of spin and relevant values of :math:`\alpha=0.1-0.3`. + +For the thick and slim disk, these lower specific angular momenta lead to a non-zero equilibrium spin value :math:`a_\mathrm{eq}<1`. If :math:`a>a_\mathrm{eq}`, the BH will be spun down due to frame-dragging and viscosity; the frame-dragging rotationally accelerates any accreting gas (on account of the BH angular momentum), while viscosity carries away some of that angular momentum. Including jets into the model leads to further spindown. The jet spindown term (to be added to the spinup equation above) can be derived as + +.. math:: + \bigg(\frac{\mathrm{d}a}{\mathrm{d}\ln M_\mathrm{BH,0}}\bigg)_\mathrm{j}=-\epsilon_\mathrm{j}(a)\frac{\sqrt{1-a^2}}{a}\bigg[\Big(\sqrt{1-a^2}+1 \Big)^2+a^2 \bigg] + +(see `Benson & Babul 2009 <https://ui.adsabs.harvard.edu/abs/2009MNRAS.397.1302B/abstract>`_ for a derivation, which we have independently verified). Including jet spindown leads to even lower equilibrium spin values; e.g. for the thick disk this is only :math:`a_\mathrm{eq}\approx0.25`. + +.. figure:: spinup.png + :width: 1200px + :align: center + :figclass: align-center + :alt: Spinup/spindown function + + Spinup/spindown function (the rate of black hole spin evolution) as a function of spin for all three accretion disk types. Black lines show evolution with only accretion included, while blue lines show the total including jet spindown. These plots show that the thin disk is always spun up to to :math:`a_\mathrm{eq}=1`, even with jets (due to low jet efficiencies). The advection-dominated disks (thick and slim disk) are spun up to positive equilibrium values :math:`a_\mathrm{eq}<1`, or spun down to such an equilibrium value if :math:`a>a_\mathrm{eq}`. This is due to extraction of rotational energy from the BH by frame dragging and transport of the angular momentum to large distances through viscous forces. Including jet spindown pushes these equilibrium spins to even smaller values. + +Evolution of the black hole spin direction +------------------------------------------ + +In the previous section we claimed that the evolution of the magnitude of spin depends only on whether accretion is prograde or retrograde. The two remaining questions are: 1) what about its direction, and 2) how to decide whether accretion is prograde or retrograde. We will now address the first of these. + +Lense-Thirring torques (`Lense & Thirring 1918 <https://ui.adsabs.harvard.edu/abs/1918PhyZ...19..156L/abstract>`_) arise from additional GR forces that operate near spinning BHs, related to the frame-dragging of spacetime. In isolation, they cause the precession of a parcel of gas as it orbits around the BH. For accretion disks, their effects depend on the type of disk (see `Nixon & King 2016 <https://ui.adsabs.harvard.edu/abs/2016LNP...905...45N/abstract>`_ for a review). Lense-Thirring torques do not have a component in the direction of the BH spin vector, which is why they do not play a role in the evolution of the magnitude of spin. + +In all cases, Lense-Thirring torques are effective only within some radius :math:`R_\mathrm{warp}`, which marks the boundary between the outer disk and an inner region, within which the BH can 'communicate' through these torques with the disk. Within this radius, the disk is on average aligned or counteraligned with the BH, whereas outside it, it is aligned with some large-scale angular momentum direction (which we can measure in the simulation) - hence the name warp radius. Given some surface density, one can also define the warp mass :math:`M_\mathrm{warp}` and the warp angular momentum :math:`J_\mathrm{warp}` as the total mass and angular momentum within :math:`R_\mathrm{warp}`, respectively. We will discuss how all of these warp-related quantities are calculated in each of the accretion disks further below, but for now we focus on how these warped disks feature in our model. + +In terms of the evolution of the spin direction, the main assumption of our model is as follows (see `King et al. 2005 <https://ui.adsabs.harvard.edu/abs/2005MNRAS.363...49K/abstract>`_ for the original argument, and additional discussions in e.g. `Fanidakis et al. 2011 <https://ui.adsabs.harvard.edu/abs/2011MNRAS.410...53F/abstract>`_, `Fiacconi et al. 2018 <https://ui.adsabs.harvard.edu/abs/2018MNRAS.477.3807F/abstract>`_ and `Griffin et al. 2019a <https://ui.adsabs.harvard.edu/abs/2019MNRAS.487..198G/abstract>`_). All matter that flows through an accretion disk is aligned or counteraligned with the BH spin vector in the accretion process. Due to conservation of angular momentum, the spin vector itself also has to adjust to keep the total angular momentum conserved. In the process of consuming one warp mass :math:`M_\mathrm{warp}`, the direction of the BH spin vector is aligned to match the direction of the total angular momentum of the system comprising the BH and the disk out to the warp radius. The direction of the BH spin vector can then be determined from :math:`\vec{J}_\mathrm{warp}=\vec{J}_\mathrm{BH}+J_\mathrm{warp}\hat{J}_\mathrm{d}`, where :math:`\vec{J}_\mathrm{BH}` is the old BH angular momentum vector, and :math:`\hat{J}_\mathrm{d}` is the direction of the large-scale accretion disk (which we assume matches the direction of the angular momentum of the gas in the BH smoothing kernel). + +In practice, the BH will consume parcels of mass that differ from :math:`M_\mathrm{warp}`. We assume that any such parcel of mass :math:`\Delta M` (e.g. the mass to be consumed within a single time step) can be split up onto :math:`n=\Delta M / M_\mathrm{warp}` individual increments of accretion, so the total angular momentum of the system within that time step is :math:`\vec{J}_\mathrm{warp}=\vec{J}_\mathrm{BH}+n J_\mathrm{warp}\hat{J}_\mathrm{d}`, i.e. :math:`n` warp angular momenta are consumed, with an angular momentum of :math:`\Delta \vec{J}=n J_\mathrm{warp}\hat{J}_\mathrm{d}=(J_\mathrm{warp}/M_\mathrm{warp})\Delta M `. This can also be viewed as the BH consuming material with a specific angular momentum of :math:`L_\mathrm{warp}=J_\mathrm{warp}/M_\mathrm{warp}`. Note that this picture is only valid if the BH spin vector does not change much during this process (in both magnitude and direction), which can be ensured with wisely chosen time steps. + +Deciding whether accretion is prograde or retrograde +---------------------------------------------------- + +We now discuss how to decide whether the sign of spin is positive or negative. In the process of communicating with the inner disk through Lense-Thirring torques, the disk either aligns or counteraligns with the BH spin vector. The condition for which of the two occurs can be derived by assuming that the magnitude of the spin does not change during this alignment (`King et al. 2005 <https://ui.adsabs.harvard.edu/abs/2005MNRAS.363...49K/abstract>`_). Accretion is retrograde if + +.. math:: + \cos \theta<-\frac{J_{\mathrm{warp}}}{2 J_{\mathrm{BH}}}, + +where :math:`\cos \theta=\hat{J}_\mathrm{BH}\cdot\hat{J}_\mathrm{d}` is the angle between the initial spin vector and the large-scale angular momentum of the disk. If this condition is not fulfilled, accretion is assumed to be prograde. Note that retrograde accretion is only possible if the angle between the spin vector and the large-scale accretion disk is larger than :math:`90^\circ`, and if the warp angular momentum is comparable to the BH one. + +Structure of the warped and precessing accretion disk +----------------------------------------------------- + +As mentioned already, Lense-Thirring torques have different effects depending on the type of accretion disk. In particular, their effects depend on the ratio of the viscosity parameter :math:`\alpha` and the aspect ratio :math:`H/R`. For thin discs (:math:`\alpha\gg H/R`), the disk is exactly warped as in the manner described in the preceeding two sections (`Bardeen & Peterson 1975 <https://ui.adsabs.harvard.edu/abs/1975ApJ...195L..65B/abstract>`_). The radius :math:`R_\mathrm{warp}` which separates the inner and outer accretion disc can be calculated by equating the Lense-Thirring precession time-scale (:math:`t_\mathrm{p}=2\pi/\Omega_\mathrm{p}`, with :math:`\Omega_\mathrm{p}=2GJ_\mathrm{BH}/c^2R^3` the precession rate) and the vertical warp propagation time-scale (:math:`t_\mathrm{warp}=R^2/\nu_2`, with :math:`\nu_2` the kinematic viscosity in the vertical direction) (e.g. `Martin et al. 2007 <https://ui.adsabs.harvard.edu/abs/2007MNRAS.381.1617M/abstract>`_). The vertical kinematic viscosity :math:`\nu_2` can be related to the horizontal one, :math:`\nu_1`, by :math:`\nu_2=\xi\nu_1`, with :math:`\xi` a numerical parameter given by + +.. math:: + \xi=\frac{2}{\alpha^2}\frac{1+7\alpha^2}{4+\alpha^2} + +(`Ogilvie 1999 <https://ui.adsabs.harvard.edu/abs/1999MNRAS.304..557O/abstract>`_, see also `Lodato et al. 2010 <https://ui.adsabs.harvard.edu/abs/2010MNRAS.405.1212L/abstract>`_ for a detailed discussion). We use the relation :math:`\dot{M}=3\pi\nu_1 \Sigma` to calculate :math:`\nu_1`, and therefore :math:`\nu_2`. The warp radius will depend on which region of the thin disc we assume, with each having its own expression for :math:`\Sigma`. In region b) of the `Shakura & Sunyaev (1973) <https://ui.adsabs.harvard.edu/abs/1973A%26A....24..337S/abstract>`_ thin disk, the surface density can be expressed as + +.. math:: + \Sigma_\mathrm{TD,b}=6.84 \times 10^{5} \mathrm{~g} \mathrm{~cm}^{-2} \alpha^{-4 / 5} \dot{m}^{3 / 5}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{1 / 8}\left(\frac{R}{R_{\mathrm{S}}}\right)^{-3 / 5}, + +while in region c) we have + +.. math:: + \Sigma_\mathrm{TD,c}=3.41 \times 10^{4} \mathrm{~g} \mathrm{~cm}^{-2} \alpha^{-4 / 5} \dot{m}^{7/10}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{1 / 20}\left(\frac{R}{R_{\mathrm{S}}}\right)^{-3 / 4}. + +These relations lead to the following expressions for :math:`R_\mathrm{warp}`: + +.. math:: + R_{\text {warp,TD,b}}=3410 R_{S} a^{5 / 8} \xi^{-5/8}\alpha^{-1 / 2} \dot{m}^{-1 / 4}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{1 / 8} + +(in region b) and + +.. math:: + R_\mathrm{warp,TD,c}=2629R_\mathrm{S}a^{4/7}\xi^{-4/7}\alpha^{-16/35}\dot{m}^{-6/35}\bigg(\frac{M_\mathrm{BH}}{10^8\hspace{0.5mm}\mathrm{M}_\odot} \bigg)^{4/35}, + +(in region c), with :math:`R_\mathrm{S}=2R_\mathrm{G}` the Schwarzschild radius. These warp radii are generally of order :math:`\approx1000R_\mathrm{G}`, which can lead to fairly quick alignment of the thin disk with the large-scale angular momentum direction (quicker than any significant evolution in mass or spin magnitude, illustrating why the inclusion of the effects of Lense-Thirring torques is important). + +In the context of thin disks, there is a futher complication. The self-gravity of the disk may become important at large radii (see `Lodato 2007 <https://www.sif.it/riviste/sif/ncr/econtents/2007/030/07/article/0>`_ for a review). The disk will fragment in the region where the Toomre parameter is :math:`Q(R)>1`. We thus assume that the disk extends out to where :math:`Q(R_\mathrm{sg})=1`. The self-gravity radius :math:`R_\mathrm{sg}` can be calculated from this condition and the definition of the Toomre parameter :math:`Q=\Omega c_{\mathrm{s}} /(\pi G \Sigma)`, yielding + +.. math:: + R_{\text {sg,TD,b}}=6460 R_{S} \alpha^{28/51} \dot{m}^{-18/51}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{-49/51} + +in region b) and + +.. math:: + R_\mathrm{sg,TD,c}=2456 R_{S} \alpha^{28/45} \dot{m}^{-22/45}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{-52/45} + +in region c). In all our calculations involving :math:`R_\mathrm{warp}` (for deciding the sign of spin and evolving the direction of angular momentum, as described in the preceeding sections), we always take the minimum of :math:`R_\mathrm{warp}` and :math:`R_\mathrm{sg}`. This is because if :math:`R_\mathrm{sg}<R_\mathrm{warp}`, the entire disk of extent :math:`R_\mathrm{sg}` will be warped. + +The thick disk does not experience the Bardeen-Peterson effect, i.e. it is never truly aligned nor counter-aligned in its inner regions. Instead, the disk precesses out to several dozen :math:`R_\mathrm{G}`, as seen in simulations (e.g. `Fragile et al. 2007 <https://ui.adsabs.harvard.edu/abs/2007ApJ...668..417F/abstract>`_), and likely observations through quasi-periodic oscillations (QPO; e.g. `Ingram et al. 2012 <https://ui.adsabs.harvard.edu/abs/2012MNRAS.419.2369I/abstract>`_). The slim disk has received much less attention in both simulations and observations (it is both harder to simulate and observe), but its similarity to the thick disk in its geometric aspects likely means that it precesses in a similar manner. + +The exact behaviour of the thick and slim disk (which we will collectively call the advection-dominated disks) again depends on the ratio of :math:`\alpha` and :math:`H/R`. Unfortunately, the advection-dominated disks both satisfy :math:`\alpha\approx H/R`, and in this regime, the effects of Lense-Thirring torques are not well understood from a theoretical perspective. However, if :math:`\alpha\ll H/R` (the so-called bending-wave regime), Lense-Thirring torques are known to cause precession of the entire inner disk as a solid body, as seen in observations and simulations. For simplicity, we will thus assume this to be the case for advection-dominated disks. + +`Lubow et al. (2002) <https://ui.adsabs.harvard.edu/abs/2002MNRAS.337..706L/abstract>`_ studied the bending-wave regime. In the inner regions, the disk precesses around the spin axis, while in the outer regions, it is aligned with the large-scale angular momentum of the disk. Based on their results the transition radius between the precessing and non-precessing regions of the disk given by + +.. math:: + R_\mathrm{warp,adv}=R_\mathrm{G}\bigg(\frac{384a}{25(H/R)^2}\bigg)^{2/5}. + +In our model, we assume that the inner regions of the disks are on average aligned or counteraligned with the spin vector (one can think of this as averaging over the precession, which has periods of :math:`\approx`days, over long enough time scales). For simplicity, we also refer to the radii within which this is true as the warp radii. For both of the advection-dominated disks, these radii are only of order several :math:`R_\mathrm{G}`. Note that similar values are found if one assumes that the Bardeen-Peterson effect operates in these disks. While there are some uncertainties in the assumptions we have made, we point out that using any of these values is much more physically motivated than using thin disk equations (the warp radii of order thousands of :math:`R_\mathrm{G}`), which is what is often done (e.g. `Griffin et al. 2019a <https://ui.adsabs.harvard.edu/abs/2019MNRAS.487..198G/abstract>`_, `Dubois et al. 2012 <https://ui.adsabs.harvard.edu/abs/2014MNRAS.440.1590D/abstract>`_). + +In order to determine the sign of spin and evolve the angular momentum direction, expressions for the warp mass :math:`M_\mathrm{warp}` and warp angular momentum :math:`J_\mathrm{warp}` are also needed. We calculate this using surface integrals as + +.. math:: + M_\mathrm{warp}(R_\mathrm{warp})=2\pi\int_0^{R_\mathrm{warp}}\Sigma(R)R\mathrm{d}R, + +and + +.. math:: + J_\mathrm{warp}(R_\mathrm{warp})=2\pi\int_0^{R_\mathrm{warp}}L(R)\Sigma(R)R\mathrm{d}R, + +respectively. Here, :math:`L(R)` is the specific angular momentum. In the case of the thin disk, we assume Keplerian orbits, i.e. :math:`L(R)=\sqrt{M_\mathrm{BH}G R}`. For the advection-dominated disks, we assume that they are smaller by a numerical factor :math:`\Omega_0`, which is given in the self-similar solutions for the thick (`Narayan & Yi 1995b <https://ui.adsabs.harvard.edu/abs/1995ApJ...452..710N/abstract>`_) and slim disk (`Wang & Zhou 1999 <https://ui.adsabs.harvard.edu/abs/1999ApJ...516..420W/abstract>`_), seperately. The surface densities in both of these accretion disks are given by the same formula in the self-similar solutions, which is + +.. math:: + \Sigma_\mathrm{adv}=\frac{\dot{M}}{2\pi R\vert v_\mathrm{r} \vert}, + +where :math:`v_\mathrm{r}=-\alpha v_0 v_\mathrm{K}` is the radial velocity. Here, :math:`v_\mathrm{K}=\sqrt{M_\mathrm{BH}G/R}` is the Keplerian velocity, and :math:`v_0` is another numerical coefficient that differs between the two solutions. In the thick disk, the numerical coefficients are given by :math:`v_0=3/(5+2\varepsilon)` and :math:`\Omega_0=\sqrt{2\varepsilon/(5+2\varepsilon)}`, where :math:`\varepsilon=(5/3-\gamma)/(\gamma-1)`. The adiabatic index depends on how magnetized the disk is. In particular, it depends on the gas-to-total pressure ratio as :math:`\gamma = (8-3\beta)/(6-3\beta)`, and :math:`\beta` itself depends on :math:`\alpha` (see discussion above on radiative efficiency in the thin disk). :math:`v_0` varies weakly with :math:`\alpha`; for :math:`\alpha=0.05`, it is :math:`0.56`, whereas for :math:`\alpha=0.3`, it evaluates to 0.5. :math:`\Omega_0` depends on :math:`\alpha` somewhat more strongly; we obtain :math:`0.27` and :math:`0.41` for the same values of :math:`\alpha`. The latter value agrees well with the ratio of actual to Keplerian (ISCO) orbital velocity at the event horizon, which is :math:`0.45`. For the slim disc, :math:`v_0=\Omega_0=1/\sqrt{\gamma}`, with :math:`\gamma=5`. + +Black hole mergers +------------------ + +In the process of merging, BHs interact in a very complicated manner. Their final spin is not trivial to predict, and it can depend on a very large parameter space (including the mass ratio of the black holes and the relative orientation and magnitude of the spins). Orbital angular momentum plays a role in the merger as well. We use the fitting function found by `Rezzolla et al. (2007) <https://journals.aps.org/prd/abstract/10.1103/PhysRevD.78.044002>`_, whose results have been found to be very accurate in newer and more sophisticated studies that sweep the huge parameter space of possible merger configurations. The only flaw in these formulas is that they do not include the effects of gravitational radiation. However, the effects of this radiation is confined to a :math:`\approx10\%` level, and only if either of the spin vectors is aligned or counteraligned with the direction of the orbital angular momentum (if it is not, the fits are even more accurate). + +The final spin, according to `Rezzolla et al. (2007) <https://journals.aps.org/prd/abstract/10.1103/PhysRevD.78.044002>`_ can be calculated as + +.. math:: + \mathbf{a}_\mathrm{fin} = \frac{1}{(1+q)^2}(\mathbf{a}_1+\mathbf{a}_2q^2+\mathbf{l}q), + +where :math:`q=M_2/M_1` is the mass ratio (such that :math:`M_2<M_1`), :math:`\mathbf{a}_1` and :math:`\mathbf{a}_2` are the spin vectors, and :math:`\mathbf{l}` is a vector whose direction is the same as that of the orbital angular momentum :math:`\mathbf{L}` (in the centre-of-mass frame), while its magnitude is given by + +.. math:: + |\mathbf{l}|=\frac{s_{4}}{\left(1+q^{2}\right)^{2}}\left(\left|\mathbf{a}_{1}\right|^{2}+|\mathbf{a}|_{1}^{2} q^{4}+2\left|\mathbf{a}_{1} \| \mathbf{a}_{2}\right| q^{2} \cos \phi\right)+ \\ + \left(\frac{s_{5} \mu+t_{0}+2}{1+q^{2}}\right)\left(\left|\mathbf{a}_{1}\right| \cos \theta+\left|\mathbf{a}_{2}\right| q^{2} \cos \xi\right)+ \\ + 2 \sqrt{3}+t_{2} \mu+t_{3} \mu^{2}. + +Here, :math:`\mu=q/(1+q)^2` is the symmetric mass ratio, and :math:`s_4 = -0.129`, :math:`s_5 = -0.384`, :math:`t_0 = -2.686`, :math:`t_2 = -3.454`, :math:`t_3 = 2.353`. The three cosines depend on the angles between the different vectors which play a role in the merger: :math:`\cos \phi=\hat{\mathbf{a}_{1}} \cdot \hat{\mathbf{a}_{\mathbf{2}}}`, :math:`\cos \theta=\hat{\mathbf{a}_{1}} \cdot \hat{\mathbf{l}}`, :math:`\cos \xi=\hat{\mathbf{a}_{2}} \cdot \hat{\mathbf{l}}`. diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg deleted file mode 100644 index 398c2bd88331cff072cd82590d4691f0c48bc148..0000000000000000000000000000000000000000 --- a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg +++ /dev/null @@ -1,2353 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<!-- Created with matplotlib (https://matplotlib.org/) --> -<svg height="226.8pt" version="1.1" viewBox="0 0 226.8 226.8" width="226.8pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <defs> - <style type="text/css"> -*{stroke-linecap:butt;stroke-linejoin:round;} - </style> - </defs> - <g id="figure_1"> - <g id="patch_1"> - <path d="M 0 226.8 -L 226.8 226.8 -L 226.8 0 -L 0 0 -z -" style="fill:#ffffff;"/> - </g> - <g id="axes_1"> - <g id="patch_2"> - <path d="M 34.02 197.316 -L 224.532 197.316 -L 224.532 2.268 -L 34.02 2.268 -z -" style="fill:#ffffff;"/> - </g> - <g id="PathCollection_1"> - <defs> - <path d="M 0 1 -C 0.265203 1 0.51958 0.894634 0.707107 0.707107 -C 0.894634 0.51958 1 0.265203 1 0 -C 1 -0.265203 0.894634 -0.51958 0.707107 -0.707107 -C 0.51958 -0.894634 0.265203 -1 0 -1 -C -0.265203 -1 -0.51958 -0.894634 -0.707107 -0.707107 -C -0.894634 -0.51958 -1 -0.265203 -1 0 -C -1 0.265203 -0.894634 0.51958 -0.707107 0.707107 -C -0.51958 0.894634 -0.265203 1 0 1 -z -" id="m85090810e3" style="stroke:#000000;"/> - </defs> - <g clip-path="url(#p70b35b528f)"> - <use style="stroke:#000000;" x="146.991516" xlink:href="#m85090810e3" y="70.434351"/> - </g> - </g> - <g id="matplotlib.axis_1"> - <g id="xtick_1"> - <g id="line2d_1"> - <defs> - <path d="M 0 0 -L 0 3.5 -" id="m00a3d680a8" style="stroke:#000000;stroke-width:0.8;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="60.395152" xlink:href="#m00a3d680a8" y="197.316"/> - </g> - </g> - <g id="text_1"> - <!-- $\mathdefault{10^{-6}}$ --> - <defs> - <path d="M 8.90625 57.09375 -L 8.90625 60.203125 -Q 20.90625 60.203125 27.09375 66.59375 -Q 28.796875 66.59375 29.09375 66.1875 -Q 29.40625 65.796875 29.40625 64 -L 29.40625 7.90625 -Q 29.40625 4.90625 30.84375 4 -Q 32.296875 3.09375 38.703125 3.09375 -L 41.90625 3.09375 -L 41.90625 0 -Q 38.40625 0.296875 25.703125 0.296875 -Q 13 0.296875 9.5 0 -L 9.5 3.09375 -L 12.703125 3.09375 -Q 19 3.09375 20.5 4 -Q 22 4.90625 22 7.90625 -L 22 59.703125 -Q 16.796875 57.09375 8.90625 57.09375 -z -" id="CMUSerif-Roman-49"/> - <path d="M 3.90625 32 -Q 3.90625 46.703125 7.59375 54.703125 -Q 12.796875 66.59375 25 66.59375 -Q 27.59375 66.59375 30.296875 65.890625 -Q 33 65.203125 36.453125 62.5 -Q 39.90625 59.796875 42 55.40625 -Q 46 46.90625 46 32 -Q 46 17.40625 42.296875 9.40625 -Q 36.90625 -2.203125 24.90625 -2.203125 -Q 20.40625 -2.203125 15.84375 0.09375 -Q 11.296875 2.40625 8.40625 7.90625 -Q 3.90625 16.203125 3.90625 32 -z -M 12.203125 33.203125 -Q 12.203125 18.09375 13.296875 12.09375 -Q 14.5 5.59375 17.84375 2.796875 -Q 21.203125 0 24.90625 0 -Q 28.90625 0 32.25 3 -Q 35.59375 6 36.59375 12.5 -Q 37.703125 18.90625 37.703125 33.203125 -Q 37.703125 47.09375 36.703125 52.703125 -Q 35.40625 59.203125 31.90625 61.796875 -Q 28.40625 64.40625 24.90625 64.40625 -Q 23.59375 64.40625 22.1875 64 -Q 20.796875 63.59375 18.796875 62.5 -Q 16.796875 61.40625 15.25 58.59375 -Q 13.703125 55.796875 13 51.59375 -Q 12.203125 46.203125 12.203125 33.203125 -z -" id="CMUSerif-Roman-48"/> - <path d="M 1 18.59375 -L 1 24.5 -L 27.59375 24.5 -L 27.59375 18.59375 -z -" id="CMUSerif-Roman-45"/> - <path d="M 4.203125 31.59375 -Q 4.203125 47.296875 12.203125 56.9375 -Q 20.203125 66.59375 30.5 66.59375 -Q 36.5 66.59375 39.84375 63.546875 -Q 43.203125 60.5 43.203125 55.796875 -Q 43.203125 53.203125 41.703125 52.09375 -Q 40.203125 51 38.59375 51 -Q 36.796875 51 35.390625 52.203125 -Q 34 53.40625 34 55.59375 -Q 34 60.09375 39.5 60.09375 -Q 36.90625 64.09375 30.703125 64.09375 -Q 28.796875 64.09375 26.84375 63.546875 -Q 24.90625 63 22.34375 61.140625 -Q 19.796875 59.296875 17.84375 56.34375 -Q 15.90625 53.40625 14.546875 47.90625 -Q 13.203125 42.40625 13.203125 35.203125 -L 13.203125 32.796875 -Q 17.296875 42.703125 25.6875 42.703125 -Q 34.09375 42.703125 39.890625 36.296875 -Q 45.703125 29.90625 45.703125 20.40625 -Q 45.703125 10.703125 39.640625 4.25 -Q 33.59375 -2.203125 25.09375 -2.203125 -Q 21.296875 -2.203125 17.84375 -0.59375 -Q 14.40625 1 11.203125 4.59375 -Q 8 8.203125 6.09375 15.140625 -Q 4.203125 22.09375 4.203125 31.59375 -z -M 13.40625 22.59375 -Q 13.40625 12.796875 15.203125 8.09375 -Q 15.5 7.296875 16.140625 6.25 -Q 16.796875 5.203125 17.9375 3.796875 -Q 19.09375 2.40625 21 1.5 -Q 22.90625 0.59375 25.09375 0.59375 -Q 31.796875 0.59375 35 7.09375 -Q 36.703125 10.703125 36.703125 20.5 -Q 36.703125 30.5 34.90625 34.203125 -Q 31.796875 40.40625 25.59375 40.40625 -Q 21.40625 40.40625 18.5 37.5 -Q 15.59375 34.59375 14.5 30.75 -Q 13.40625 26.90625 13.40625 22.59375 -z -" id="CMUSerif-Roman-54"/> - </defs> - <g transform="translate(52.295152 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-54"/> - </g> - </g> - </g> - <g id="xtick_2"> - <g id="line2d_2"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="95.033698" xlink:href="#m00a3d680a8" y="197.316"/> - </g> - </g> - <g id="text_2"> - <!-- $\mathdefault{10^{-4}}$ --> - <defs> - <path d="M 2.796875 16.5 -L 2.796875 19.59375 -L 33.5 66.5 -Q 34.296875 67.703125 35.5 67.703125 -Q 36.59375 67.703125 36.84375 67.25 -Q 37.09375 66.796875 37.09375 65.09375 -L 37.09375 19.59375 -L 47.09375 19.59375 -L 47.09375 16.5 -L 37.09375 16.5 -L 37.09375 7.796875 -Q 37.09375 4.90625 38.296875 4 -Q 39.5 3.09375 44.703125 3.09375 -L 46.796875 3.09375 -L 46.796875 0 -Q 42.703125 0.296875 33.203125 0.296875 -Q 23.796875 0.296875 19.703125 0 -L 19.703125 3.09375 -L 21.796875 3.09375 -Q 27 3.09375 28.203125 4 -Q 29.40625 4.90625 29.40625 7.796875 -L 29.40625 16.5 -z -M 5.59375 19.59375 -L 30 19.59375 -L 30 56.90625 -z -" id="CMUSerif-Roman-52"/> - </defs> - <g transform="translate(86.933698 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-52"/> - </g> - </g> - </g> - <g id="xtick_3"> - <g id="line2d_3"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="129.672243" xlink:href="#m00a3d680a8" y="197.316"/> - </g> - </g> - <g id="text_3"> - <!-- $\mathdefault{10^{-2}}$ --> - <defs> - <path d="M 5 0 -Q 5 1.796875 5.140625 2.34375 -Q 5.296875 2.90625 6.09375 3.703125 -L 25.296875 25.09375 -Q 35.796875 36.90625 35.796875 47.203125 -Q 35.796875 53.90625 32.296875 58.703125 -Q 28.796875 63.5 22.40625 63.5 -Q 18 63.5 14.296875 60.796875 -Q 10.59375 58.09375 8.90625 53.296875 -Q 9.203125 53.40625 10.203125 53.40625 -Q 12.703125 53.40625 14.09375 51.84375 -Q 15.5 50.296875 15.5 48.203125 -Q 15.5 45.5 13.75 44.203125 -Q 12 42.90625 10.296875 42.90625 -Q 9.59375 42.90625 8.6875 43.046875 -Q 7.796875 43.203125 6.390625 44.59375 -Q 5 46 5 48.5 -Q 5 55.5 10.296875 61.046875 -Q 15.59375 66.59375 23.703125 66.59375 -Q 32.90625 66.59375 38.90625 61.140625 -Q 44.90625 55.703125 44.90625 47.203125 -Q 44.90625 44.203125 44 41.5 -Q 43.09375 38.796875 41.890625 36.6875 -Q 40.703125 34.59375 37.5 31.25 -Q 34.296875 27.90625 31.6875 25.5 -Q 29.09375 23.09375 23.296875 18 -L 12.703125 7.703125 -L 30.703125 7.703125 -Q 39.5 7.703125 40.203125 8.5 -Q 41.203125 9.90625 42.40625 17.40625 -L 44.90625 17.40625 -L 42.09375 0 -z -" id="CMUSerif-Roman-50"/> - </defs> - <g transform="translate(121.572243 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/> - </g> - </g> - </g> - <g id="xtick_4"> - <g id="line2d_4"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="164.310789" xlink:href="#m00a3d680a8" y="197.316"/> - </g> - </g> - <g id="text_4"> - <!-- $\mathdefault{10^{0}}$ --> - <g transform="translate(157.410789 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-48"/> - </g> - </g> - </g> - <g id="xtick_5"> - <g id="line2d_5"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="198.949334" xlink:href="#m00a3d680a8" y="197.316"/> - </g> - </g> - <g id="text_5"> - <!-- $\mathdefault{10^{2}}$ --> - <g transform="translate(192.049334 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/> - </g> - </g> - </g> - <g id="text_6"> - <!-- Hydrogen number density $n_{\rm H}$ [cm$^{-3}$] --> - <defs> - <path d="M 3.296875 0 -L 3.296875 3.09375 -L 5.703125 3.09375 -Q 11.09375 3.09375 12.34375 4 -Q 13.59375 4.90625 13.59375 7.796875 -L 13.59375 60.5 -Q 13.59375 63.40625 12.34375 64.296875 -Q 11.09375 65.203125 5.703125 65.203125 -L 3.296875 65.203125 -L 3.296875 68.296875 -Q 6.796875 68 18.09375 68 -Q 29.296875 68 32.796875 68.296875 -L 32.796875 65.203125 -L 30.40625 65.203125 -Q 25 65.203125 23.75 64.296875 -Q 22.5 63.40625 22.5 60.5 -L 22.5 37.09375 -L 52.40625 37.09375 -L 52.40625 60.5 -Q 52.40625 63.40625 51.15625 64.296875 -Q 49.90625 65.203125 44.5 65.203125 -L 42.09375 65.203125 -L 42.09375 68.296875 -Q 45.59375 68 56.90625 68 -Q 68.09375 68 71.59375 68.296875 -L 71.59375 65.203125 -L 69.203125 65.203125 -Q 63.796875 65.203125 62.546875 64.296875 -Q 61.296875 63.40625 61.296875 60.5 -L 61.296875 7.796875 -Q 61.296875 4.90625 62.546875 4 -Q 63.796875 3.09375 69.203125 3.09375 -L 71.59375 3.09375 -L 71.59375 0 -Q 68.09375 0.296875 56.796875 0.296875 -Q 45.59375 0.296875 42.09375 0 -L 42.09375 3.09375 -L 44.5 3.09375 -Q 49.90625 3.09375 51.15625 4 -Q 52.40625 4.90625 52.40625 7.796875 -L 52.40625 34 -L 22.5 34 -L 22.5 7.796875 -Q 22.5 4.90625 23.75 4 -Q 25 3.09375 30.40625 3.09375 -L 32.796875 3.09375 -L 32.796875 0 -Q 29.296875 0.296875 18 0.296875 -Q 6.796875 0.296875 3.296875 0 -z -" id="CMUSerif-Roman-72"/> - <path d="M 1.90625 -12.40625 -Q 1.90625 -10.296875 3.15625 -9.1875 -Q 4.40625 -8.09375 6.09375 -8.09375 -Q 7.90625 -8.09375 9.09375 -9.25 -Q 10.296875 -10.40625 10.296875 -12.296875 -Q 10.296875 -16 6.40625 -16.5 -Q 8.296875 -18.296875 11.09375 -18.296875 -Q 14.09375 -18.296875 16.5 -16.09375 -Q 18.90625 -13.90625 19.953125 -11.796875 -Q 21 -9.703125 22.5 -5.90625 -Q 23.90625 -2.90625 25 0 -L 10 36.5 -Q 9 38.90625 7.5 39.453125 -Q 6 40 1.90625 40 -L 1.90625 43.09375 -Q 6.40625 42.796875 11.59375 42.796875 -Q 14.703125 42.796875 22.5 43.09375 -L 22.5 40 -Q 16.90625 40 16.90625 37.40625 -Q 16.90625 37.09375 17.5 35.59375 -L 28.59375 8.703125 -L 38.703125 33.296875 -Q 39.296875 34.703125 39.296875 35.703125 -Q 39.296875 39.796875 34.59375 40 -L 34.59375 43.09375 -Q 41.203125 42.796875 43.296875 42.796875 -Q 47.40625 42.796875 50.796875 43.09375 -L 50.796875 40 -Q 44.09375 40 41.5 33.59375 -L 23.90625 -9.09375 -Q 19.09375 -20.5 11.09375 -20.5 -Q 7.296875 -20.5 4.59375 -18.140625 -Q 1.90625 -15.796875 1.90625 -12.40625 -z -" id="CMUSerif-Roman-121"/> - <path d="M 3.40625 21.5 -Q 3.40625 31 10.046875 37.59375 -Q 16.703125 44.203125 25.703125 44.203125 -Q 33.296875 44.203125 38.296875 38 -L 38.296875 59.59375 -Q 38.296875 63.296875 37 64.25 -Q 35.703125 65.203125 30.5 65.203125 -L 30.5 68.296875 -L 44.90625 69.40625 -L 44.90625 8.703125 -Q 44.90625 5 46.203125 4.046875 -Q 47.5 3.09375 52.703125 3.09375 -L 52.703125 0 -L 38 -1.09375 -L 38 5.5 -Q 32.796875 -1.09375 24.59375 -1.09375 -Q 16 -1.09375 9.703125 5.5 -Q 3.40625 12.09375 3.40625 21.5 -z -M 11.703125 21.40625 -Q 11.703125 12.09375 14.59375 7.5 -Q 18.59375 1.09375 25.09375 1.09375 -Q 32.5 1.09375 36.90625 8.09375 -Q 38 9.796875 38 11.796875 -L 38 32.296875 -Q 38 34.296875 36.90625 36 -Q 32.796875 42 26.09375 42 -Q 19.09375 42 14.796875 35.59375 -Q 11.703125 30.796875 11.703125 21.40625 -z -" id="CMUSerif-Roman-100"/> - <path d="M 2.796875 0 -L 2.796875 3.09375 -Q 8.09375 3.09375 9.34375 3.75 -Q 10.59375 4.40625 10.59375 7.59375 -L 10.59375 34.40625 -Q 10.59375 38.09375 9.296875 39.046875 -Q 8 40 2.796875 40 -L 2.796875 43.09375 -L 16.703125 44.203125 -L 16.703125 33.203125 -Q 18.09375 37.5 21.09375 40.84375 -Q 24.09375 44.203125 29 44.203125 -Q 32.203125 44.203125 34.296875 42.390625 -Q 36.40625 40.59375 36.40625 38.09375 -Q 36.40625 35.90625 35.046875 34.796875 -Q 33.703125 33.703125 32.09375 33.703125 -Q 30.296875 33.703125 29.046875 34.84375 -Q 27.796875 36 27.796875 38 -Q 27.796875 39.203125 28.34375 40.140625 -Q 28.90625 41.09375 29.34375 41.4375 -Q 29.796875 41.796875 30.09375 41.90625 -Q 29.90625 42 29 42 -Q 23.5 42 20.34375 36.5 -Q 17.203125 31 17.203125 23.203125 -L 17.203125 7.796875 -Q 17.203125 4.90625 18.390625 4 -Q 19.59375 3.09375 24.796875 3.09375 -L 26.90625 3.09375 -L 26.90625 0 -Q 22.90625 0.296875 14.203125 0.296875 -Q 13 0.296875 11.09375 0.25 -Q 9.203125 0.203125 6.703125 0.09375 -Q 4.203125 0 2.796875 0 -z -" id="CMUSerif-Roman-114"/> - <path d="M 16 -1.09375 -Q 2.796875 11.90625 2.796875 21.40625 -Q 2.796875 30.90625 9.25 37.84375 -Q 15.703125 44.796875 25 44.796875 -Q 34.09375 44.796875 40.59375 37.890625 -Q 47.09375 31 47.09375 21.40625 -Q 47.09375 12 40.546875 5.453125 -Q 34 -1.09375 24.90625 -1.09375 -Q 16 -1.09375 2.796875 11.90625 -z -M 11.09375 22.203125 -Q 11.09375 12.5 13.59375 8.09375 -Q 17.5 1.40625 25 1.40625 -Q 28.703125 1.40625 31.796875 3.40625 -Q 34.90625 5.40625 36.59375 8.796875 -Q 38.796875 13.203125 38.796875 22.203125 -Q 38.796875 31.796875 36.203125 36.09375 -Q 32.296875 42.59375 24.90625 42.59375 -Q 21.703125 42.59375 18.546875 40.890625 -Q 15.40625 39.203125 13.5 35.90625 -Q 11.09375 31.5 11.09375 22.203125 -z -" id="CMUSerif-Roman-111"/> - <path d="M 2.796875 -7.90625 -Q 2.796875 -4.703125 5.25 -1.890625 -Q 7.703125 0.90625 12 2.09375 -Q 7.59375 4.90625 7.59375 11 -Q 7.59375 15.703125 10.703125 19.296875 -Q 6 23.203125 6 29.59375 -Q 6 35.5 10.703125 39.84375 -Q 15.40625 44.203125 22.203125 44.203125 -Q 28.203125 44.203125 32.796875 40.59375 -Q 37.59375 45.296875 43.40625 45.296875 -Q 46 45.296875 47.25 43.6875 -Q 48.5 42.09375 48.5 40.40625 -Q 48.5 38.90625 47.546875 38.15625 -Q 46.59375 37.40625 45.59375 37.40625 -Q 44.40625 37.40625 43.546875 38.203125 -Q 42.703125 39 42.703125 40.296875 -Q 42.703125 42.40625 44.296875 43 -Q 44 43.09375 43.296875 43.09375 -Q 38.40625 43.09375 34.296875 39.203125 -Q 38.40625 35.40625 38.40625 29.5 -Q 38.40625 23.59375 33.703125 19.25 -Q 29 14.90625 22.203125 14.90625 -Q 16.59375 14.90625 12.296875 18 -Q 10.59375 16 10.59375 13.296875 -Q 10.59375 10.796875 12.09375 8.84375 -Q 13.59375 6.90625 15.90625 6.59375 -Q 16.59375 6.5 23.40625 6.5 -Q 27.40625 6.5 29.59375 6.390625 -Q 31.796875 6.296875 34.9375 5.640625 -Q 38.09375 5 40.59375 3.703125 -Q 47.09375 0.09375 47.09375 -7.703125 -Q 47.09375 -13.40625 40.546875 -17 -Q 34 -20.59375 24.90625 -20.59375 -Q 15.703125 -20.59375 9.25 -16.9375 -Q 2.796875 -13.296875 2.796875 -7.90625 -z -M 8 -7.90625 -Q 8 -12 12.84375 -15.140625 -Q 17.703125 -18.296875 25 -18.296875 -Q 32.203125 -18.296875 37.046875 -15.1875 -Q 41.90625 -12.09375 41.90625 -7.90625 -Q 41.90625 -4.90625 40.203125 -3 -Q 38.5 -1.09375 35 -0.34375 -Q 31.5 0.40625 29.046875 0.546875 -Q 26.59375 0.703125 22.09375 0.703125 -L 16.203125 0.703125 -Q 12.796875 0.5 10.390625 -2 -Q 8 -4.5 8 -7.90625 -z -M 13.5 29.5 -Q 13.5 17.203125 22.203125 17.203125 -Q 26.59375 17.203125 29.296875 21.203125 -Q 30.90625 23.90625 30.90625 29.59375 -Q 30.90625 41.90625 22.203125 41.90625 -Q 17.796875 41.90625 15.09375 37.90625 -Q 13.5 35.203125 13.5 29.5 -z -" id="CMUSerif-Roman-103"/> - <path d="M 2.796875 22 -Q 2.796875 31.40625 8.84375 38.09375 -Q 14.90625 44.796875 23.59375 44.796875 -Q 32.40625 44.796875 36.953125 39.09375 -Q 41.5 33.40625 41.5 25.203125 -Q 41.5 23.703125 41.09375 23.390625 -Q 40.703125 23.09375 39 23.09375 -L 11.09375 23.09375 -Q 11.09375 12.90625 14.09375 8.09375 -Q 18.296875 1.40625 25.40625 1.40625 -Q 26.40625 1.40625 27.546875 1.59375 -Q 28.703125 1.796875 31.09375 2.640625 -Q 33.5 3.5 35.59375 5.796875 -Q 37.703125 8.09375 38.90625 11.703125 -Q 39.203125 13.09375 40.203125 13.09375 -Q 41.5 13.09375 41.5 11.90625 -Q 41.5 11 40.546875 9.046875 -Q 39.59375 7.09375 37.796875 4.75 -Q 36 2.40625 32.5 0.65625 -Q 29 -1.09375 24.796875 -1.09375 -Q 16 -1.09375 9.390625 5.546875 -Q 2.796875 12.203125 2.796875 22 -z -M 11.203125 25.203125 -L 34.90625 25.203125 -Q 34.90625 27.296875 34.546875 29.640625 -Q 34.203125 32 33.140625 35.25 -Q 32.09375 38.5 29.640625 40.546875 -Q 27.203125 42.59375 23.59375 42.59375 -Q 22 42.59375 20.25 41.890625 -Q 18.5 41.203125 16.390625 39.546875 -Q 14.296875 37.90625 12.84375 34.15625 -Q 11.40625 30.40625 11.203125 25.203125 -z -" id="CMUSerif-Roman-101"/> - <path d="M 3.203125 0 -L 3.203125 3.09375 -Q 8.5 3.09375 9.75 3.75 -Q 11 4.40625 11 7.59375 -L 11 34.40625 -Q 11 38.09375 9.703125 39.046875 -Q 8.40625 40 3.203125 40 -L 3.203125 43.09375 -L 17.296875 44.203125 -L 17.296875 33.703125 -Q 22 44.203125 32.09375 44.203125 -Q 39.59375 44.203125 42.59375 40.5 -Q 44.796875 38 45.25 35.203125 -Q 45.703125 32.40625 45.703125 25.203125 -L 45.703125 6.09375 -Q 45.796875 4 47.390625 3.546875 -Q 49 3.09375 53.5 3.09375 -L 53.5 0 -Q 43.296875 0.296875 42.296875 0.296875 -Q 41.5 0.296875 31 0 -L 31 3.09375 -Q 36.296875 3.09375 37.546875 3.75 -Q 38.796875 4.40625 38.796875 7.59375 -L 38.796875 30.90625 -Q 38.796875 36 37.25 39 -Q 35.703125 42 31.40625 42 -Q 26.203125 42 22.046875 37.640625 -Q 17.90625 33.296875 17.90625 26 -L 17.90625 7.59375 -Q 17.90625 4.40625 19.15625 3.75 -Q 20.40625 3.09375 25.703125 3.09375 -L 25.703125 0 -Q 15.5 0.296875 14.5 0.296875 -Q 13.703125 0.296875 3.203125 0 -z -" id="CMUSerif-Roman-110"/> - <path id="CMUSerif-Roman-32"/> - <path d="M 3.203125 40 -L 3.203125 43.09375 -L 17.90625 44.203125 -L 17.90625 11 -Q 17.90625 8.59375 18.09375 7.1875 -Q 18.296875 5.796875 19.09375 4.1875 -Q 19.90625 2.59375 21.796875 1.84375 -Q 23.703125 1.09375 26.703125 1.09375 -Q 32.09375 1.09375 35.4375 5.546875 -Q 38.796875 10 38.796875 16.59375 -L 38.796875 34.40625 -Q 38.796875 38.09375 37.5 39.046875 -Q 36.203125 40 31 40 -L 31 43.09375 -L 45.703125 44.203125 -L 45.703125 8.703125 -Q 45.703125 5 47 4.046875 -Q 48.296875 3.09375 53.5 3.09375 -L 53.5 0 -L 39.09375 -1.09375 -L 39.09375 7.90625 -Q 34.90625 -1.09375 26.203125 -1.09375 -Q 21.796875 -1.09375 18.796875 0 -Q 15.796875 1.09375 14.296875 2.5 -Q 12.796875 3.90625 12 6.59375 -Q 11.203125 9.296875 11.09375 10.9375 -Q 11 12.59375 11 15.796875 -L 11 30.796875 -Q 11 37.59375 10 38.796875 -Q 9 40 3.203125 40 -z -" id="CMUSerif-Roman-117"/> - <path d="M 3.203125 0 -L 3.203125 3.09375 -Q 8.5 3.09375 9.75 3.75 -Q 11 4.40625 11 7.59375 -L 11 34.40625 -Q 11 38.09375 9.703125 39.046875 -Q 8.40625 40 3.203125 40 -L 3.203125 43.09375 -L 17.296875 44.203125 -L 17.296875 33.703125 -Q 22 44.203125 32.09375 44.203125 -Q 43.796875 44.203125 45.40625 34.40625 -Q 47.09375 38.203125 50.6875 41.203125 -Q 54.296875 44.203125 59.90625 44.203125 -Q 67.40625 44.203125 70.40625 40.5 -Q 72.59375 38 73.046875 35.203125 -Q 73.5 32.40625 73.5 25.203125 -L 73.5 6.09375 -Q 73.59375 4 75.1875 3.546875 -Q 76.796875 3.09375 81.296875 3.09375 -L 81.296875 0 -Q 71.09375 0.296875 70.09375 0.296875 -Q 69.296875 0.296875 58.796875 0 -L 58.796875 3.09375 -Q 64.09375 3.09375 65.34375 3.75 -Q 66.59375 4.40625 66.59375 7.59375 -L 66.59375 30.90625 -Q 66.59375 36 65.046875 39 -Q 63.5 42 59.203125 42 -Q 54 42 49.84375 37.640625 -Q 45.703125 33.296875 45.703125 26 -L 45.703125 7.59375 -Q 45.703125 4.40625 46.953125 3.75 -Q 48.203125 3.09375 53.5 3.09375 -L 53.5 0 -Q 43.296875 0.296875 42.296875 0.296875 -Q 41.5 0.296875 31 0 -L 31 3.09375 -Q 36.296875 3.09375 37.546875 3.75 -Q 38.796875 4.40625 38.796875 7.59375 -L 38.796875 30.90625 -Q 38.796875 36 37.25 39 -Q 35.703125 42 31.40625 42 -Q 26.203125 42 22.046875 37.640625 -Q 17.90625 33.296875 17.90625 26 -L 17.90625 7.59375 -Q 17.90625 4.40625 19.15625 3.75 -Q 20.40625 3.09375 25.703125 3.09375 -L 25.703125 0 -Q 15.5 0.296875 14.5 0.296875 -Q 13.703125 0.296875 3.203125 0 -z -" id="CMUSerif-Roman-109"/> - <path d="M 2.796875 65.203125 -L 2.796875 68.296875 -L 17.203125 69.40625 -L 17.203125 37.703125 -Q 23 44.203125 30.90625 44.203125 -Q 39.5 44.203125 45.796875 37.59375 -Q 52.09375 31 52.09375 21.59375 -Q 52.09375 12.09375 45.5 5.5 -Q 38.90625 -1.09375 29.796875 -1.09375 -Q 21.5 -1.09375 16.703125 6.203125 -Q 13.203125 0.09375 13.09375 0 -L 10.59375 0 -L 10.59375 59.59375 -Q 10.59375 63.296875 9.296875 64.25 -Q 8 65.203125 2.796875 65.203125 -z -M 17.5 11.40625 -Q 17.5 9.296875 18.90625 7.203125 -Q 22.90625 1.09375 29.40625 1.09375 -Q 36.40625 1.09375 40.703125 7.5 -Q 43.796875 12.296875 43.796875 21.703125 -Q 43.796875 31 40.90625 35.59375 -Q 36.90625 42 30.40625 42 -Q 23.09375 42 18.59375 35.59375 -Q 17.5 34 17.5 32 -z -" id="CMUSerif-Roman-98"/> - <path d="M 3.296875 1.296875 -L 3.296875 14.5 -Q 3.296875 15.59375 3.34375 16 -Q 3.40625 16.40625 3.703125 16.703125 -Q 4 17 4.59375 17 -Q 5.296875 17 5.546875 16.703125 -Q 5.796875 16.40625 6 15.296875 -Q 7.5 8.40625 10.75 4.75 -Q 14 1.09375 19.90625 1.09375 -Q 25.5 1.09375 28.34375 3.59375 -Q 31.203125 6.09375 31.203125 10.203125 -Q 31.203125 17.5 20.796875 19.40625 -Q 14.796875 20.59375 12.296875 21.390625 -Q 9.796875 22.203125 7.59375 24 -Q 3.296875 27.5 3.296875 32.5 -Q 3.296875 37.5 7.09375 41.140625 -Q 10.90625 44.796875 19.296875 44.796875 -Q 24.90625 44.796875 28.703125 42 -Q 29.796875 42.90625 30.40625 43.59375 -Q 31.703125 44.796875 32.40625 44.796875 -Q 33.203125 44.796875 33.34375 44.296875 -Q 33.5 43.796875 33.5 42.40625 -L 33.5 32.296875 -Q 33.5 31.203125 33.453125 30.796875 -Q 33.40625 30.40625 33.09375 30.15625 -Q 32.796875 29.90625 32.203125 29.90625 -Q 31.09375 29.90625 31 30.796875 -Q 30.203125 42.90625 19.296875 42.90625 -Q 13.40625 42.90625 10.75 40.65625 -Q 8.09375 38.40625 8.09375 35.296875 -Q 8.09375 33.59375 8.890625 32.296875 -Q 9.703125 31 10.75 30.25 -Q 11.796875 29.5 13.75 28.796875 -Q 15.703125 28.09375 16.890625 27.84375 -Q 18.09375 27.59375 20.40625 27.09375 -Q 28.40625 25.59375 31.796875 22.296875 -Q 36 18.09375 36 12.796875 -Q 36 6.90625 32 2.90625 -Q 28 -1.09375 19.90625 -1.09375 -Q 13.40625 -1.09375 8.90625 3.203125 -Q 8.296875 2.59375 7.84375 2.09375 -Q 7.40625 1.59375 7.25 1.390625 -Q 7.09375 1.203125 7.046875 1.09375 -Q 7 1 6.90625 0.90625 -Q 4.90625 -1.09375 4.40625 -1.09375 -Q 3.59375 -1.09375 3.4375 -0.59375 -Q 3.296875 -0.09375 3.296875 1.296875 -z -" id="CMUSerif-Roman-115"/> - <path d="M 3.296875 0 -L 3.296875 3.09375 -Q 8.59375 3.09375 9.84375 3.75 -Q 11.09375 4.40625 11.09375 7.59375 -L 11.09375 34.5 -Q 11.09375 38.203125 9.84375 39.09375 -Q 8.59375 40 3.703125 40 -L 3.703125 43.09375 -L 17.703125 44.203125 -L 17.703125 7.5 -Q 17.703125 4.5 18.75 3.796875 -Q 19.796875 3.09375 24.703125 3.09375 -L 24.703125 0 -Q 14.5 0.296875 14.296875 0.296875 -Q 12.90625 0.296875 3.296875 0 -z -M 7.5 61.59375 -Q 7.5 63.59375 9.046875 65.25 -Q 10.59375 66.90625 12.796875 66.90625 -Q 15 66.90625 16.546875 65.40625 -Q 18.09375 63.90625 18.09375 61.59375 -Q 18.09375 59.296875 16.546875 57.796875 -Q 15 56.296875 12.796875 56.296875 -Q 10.5 56.296875 9 57.890625 -Q 7.5 59.5 7.5 61.59375 -z -" id="CMUSerif-Roman-105"/> - <path d="M 1.90625 40 -L 1.90625 42.203125 -Q 6.5 42.40625 9.546875 45.65625 -Q 12.59375 48.90625 13.640625 52.90625 -Q 14.703125 56.90625 14.796875 61.5 -L 17.296875 61.5 -L 17.296875 43.09375 -L 31.59375 43.09375 -L 31.59375 40 -L 17.296875 40 -L 17.296875 12.203125 -Q 17.296875 1.40625 24 1.40625 -Q 26.90625 1.40625 28.796875 4.34375 -Q 30.703125 7.296875 30.703125 12.59375 -L 30.703125 18.09375 -L 33.203125 18.09375 -L 33.203125 12.40625 -Q 33.203125 7 30.703125 2.953125 -Q 28.203125 -1.09375 23.296875 -1.09375 -Q 21.5 -1.09375 19.703125 -0.640625 -Q 17.90625 -0.203125 15.59375 1 -Q 13.296875 2.203125 11.84375 5.140625 -Q 10.40625 8.09375 10.40625 12.40625 -L 10.40625 40 -z -" id="CMUSerif-Roman-116"/> - <path d="M 7.71875 1.703125 -Q 7.71875 2.296875 7.8125 2.59375 -L 15.28125 32.421875 -Q 16.015625 35.203125 16.015625 37.3125 -Q 16.015625 41.609375 13.09375 41.609375 -Q 9.96875 41.609375 8.453125 37.859375 -Q 6.9375 34.125 5.515625 28.421875 -Q 5.515625 28.125 5.21875 27.953125 -Q 4.9375 27.78125 4.6875 27.78125 -L 3.515625 27.78125 -Q 3.171875 27.78125 2.921875 28.140625 -Q 2.6875 28.515625 2.6875 28.8125 -Q 3.765625 33.15625 4.765625 36.171875 -Q 5.765625 39.203125 7.890625 41.6875 -Q 10.015625 44.1875 13.1875 44.1875 -Q 16.9375 44.1875 19.8125 41.8125 -Q 22.703125 39.453125 22.703125 35.796875 -Q 25.6875 39.703125 29.6875 41.9375 -Q 33.6875 44.1875 38.1875 44.1875 -Q 41.75 44.1875 44.328125 42.96875 -Q 46.921875 41.75 48.359375 39.28125 -Q 49.8125 36.8125 49.8125 33.40625 -Q 49.8125 29.296875 47.96875 23.484375 -Q 46.140625 17.671875 43.40625 10.5 -Q 42 7.234375 42 4.5 -Q 42 1.515625 44.28125 1.515625 -Q 48.1875 1.515625 50.796875 5.703125 -Q 53.421875 9.90625 54.5 14.703125 -Q 54.6875 15.28125 55.328125 15.28125 -L 56.5 15.28125 -Q 56.890625 15.28125 57.15625 15.03125 -Q 57.421875 14.796875 57.421875 14.40625 -Q 57.421875 14.3125 57.328125 14.109375 -Q 55.953125 8.453125 52.5625 3.65625 -Q 49.171875 -1.125 44.09375 -1.125 -Q 40.578125 -1.125 38.078125 1.296875 -Q 35.59375 3.71875 35.59375 7.171875 -Q 35.59375 9.03125 36.375 11.078125 -Q 37.640625 14.359375 39.28125 18.890625 -Q 40.921875 23.4375 41.96875 27.578125 -Q 43.015625 31.734375 43.015625 34.90625 -Q 43.015625 37.703125 41.859375 39.65625 -Q 40.71875 41.609375 37.984375 41.609375 -Q 34.328125 41.609375 31.25 39.984375 -Q 28.171875 38.375 25.875 35.71875 -Q 23.578125 33.0625 21.6875 29.390625 -L 14.890625 2.203125 -Q 14.546875 0.828125 13.34375 -0.140625 -Q 12.15625 -1.125 10.6875 -1.125 -Q 9.46875 -1.125 8.59375 -0.34375 -Q 7.71875 0.4375 7.71875 1.703125 -z -" id="Cmmi10-110"/> - <path d="M 3.078125 0 -L 3.078125 3.515625 -Q 13.375 3.515625 13.375 6.6875 -L 13.375 61.625 -Q 13.375 64.796875 3.078125 64.796875 -L 3.078125 68.3125 -L 33.015625 68.3125 -L 33.015625 64.796875 -Q 22.703125 64.796875 22.703125 61.625 -L 22.703125 37.3125 -L 52.203125 37.3125 -L 52.203125 61.625 -Q 52.203125 64.796875 41.890625 64.796875 -L 41.890625 68.3125 -L 71.78125 68.3125 -L 71.78125 64.796875 -Q 61.53125 64.796875 61.53125 61.625 -L 61.53125 6.6875 -Q 61.53125 3.515625 71.78125 3.515625 -L 71.78125 0 -L 41.890625 0 -L 41.890625 3.515625 -Q 52.203125 3.515625 52.203125 6.6875 -L 52.203125 33.796875 -L 22.703125 33.796875 -L 22.703125 6.6875 -Q 22.703125 3.515625 33.015625 3.515625 -L 33.015625 0 -z -" id="Cmr10-72"/> - <path d="M 10.40625 -25 -L 10.40625 75 -L 25.5 75 -L 25.5 72.703125 -L 17.09375 72.703125 -L 17.09375 -22.703125 -L 25.5 -22.703125 -L 25.5 -25 -z -" id="CMUSerif-Roman-91"/> - <path d="M 16.09375 -1.09375 -Q 3.40625 12.09375 3.40625 21.59375 -Q 3.40625 31.09375 9.65625 37.9375 -Q 15.90625 44.796875 25.09375 44.796875 -Q 31.203125 44.796875 35.796875 41.890625 -Q 40.40625 39 40.40625 34.09375 -Q 40.40625 31.90625 39.09375 30.65625 -Q 37.796875 29.40625 35.796875 29.40625 -Q 33.703125 29.40625 32.453125 30.703125 -Q 31.203125 32 31.203125 34 -Q 31.203125 34.90625 31.5 35.75 -Q 31.796875 36.59375 32.890625 37.546875 -Q 34 38.5 35.90625 38.59375 -Q 32.296875 42.296875 25.203125 42.296875 -Q 20.09375 42.296875 15.890625 37.5 -Q 11.703125 32.703125 11.703125 21.796875 -Q 11.703125 16.09375 13.09375 11.890625 -Q 14.5 7.703125 16.796875 5.546875 -Q 19.09375 3.40625 21.34375 2.40625 -Q 23.59375 1.40625 25.796875 1.40625 -Q 35.59375 1.40625 38.90625 11.90625 -Q 39.203125 12.90625 40.203125 12.90625 -Q 41.5 12.90625 41.5 11.90625 -Q 41.5 11.40625 41.09375 10.15625 -Q 40.703125 8.90625 39.5 6.90625 -Q 38.296875 4.90625 36.546875 3.15625 -Q 34.796875 1.40625 31.75 0.15625 -Q 28.703125 -1.09375 24.90625 -1.09375 -Q 16.09375 -1.09375 3.40625 12.09375 -z -" id="CMUSerif-Roman-99"/> - <path d="M 10.203125 23 -Q 9.375 23 8.828125 23.625 -Q 8.296875 24.265625 8.296875 25 -Q 8.296875 25.734375 8.828125 26.359375 -Q 9.375 27 10.203125 27 -L 67.578125 27 -Q 68.359375 27 68.875 26.359375 -Q 69.390625 25.734375 69.390625 25 -Q 69.390625 24.265625 68.875 23.625 -Q 68.359375 23 67.578125 23 -z -" id="Cmsy10-161"/> - <path d="M 9.515625 7.71875 -Q 11.859375 4.296875 15.8125 2.640625 -Q 19.78125 0.984375 24.3125 0.984375 -Q 30.125 0.984375 32.5625 5.9375 -Q 35.015625 10.890625 35.015625 17.1875 -Q 35.015625 20.015625 34.5 22.84375 -Q 33.984375 25.6875 32.765625 28.125 -Q 31.546875 30.5625 29.421875 32.03125 -Q 27.296875 33.5 24.21875 33.5 -L 17.578125 33.5 -Q 16.703125 33.5 16.703125 34.421875 -L 16.703125 35.296875 -Q 16.703125 36.078125 17.578125 36.078125 -L 23.09375 36.53125 -Q 26.609375 36.53125 28.921875 39.15625 -Q 31.25 41.796875 32.328125 45.578125 -Q 33.40625 49.359375 33.40625 52.78125 -Q 33.40625 57.5625 31.15625 60.640625 -Q 28.90625 63.71875 24.3125 63.71875 -Q 20.515625 63.71875 17.046875 62.28125 -Q 13.578125 60.84375 11.53125 57.90625 -Q 11.71875 57.953125 11.859375 57.984375 -Q 12.015625 58.015625 12.203125 58.015625 -Q 14.453125 58.015625 15.96875 56.453125 -Q 17.484375 54.890625 17.484375 52.6875 -Q 17.484375 50.53125 15.96875 48.96875 -Q 14.453125 47.40625 12.203125 47.40625 -Q 10.015625 47.40625 8.453125 48.96875 -Q 6.890625 50.53125 6.890625 52.6875 -Q 6.890625 56.984375 9.46875 60.15625 -Q 12.0625 63.328125 16.140625 64.96875 -Q 20.21875 66.609375 24.3125 66.609375 -Q 27.34375 66.609375 30.703125 65.703125 -Q 34.078125 64.796875 36.8125 63.109375 -Q 39.546875 61.421875 41.28125 58.78125 -Q 43.015625 56.15625 43.015625 52.78125 -Q 43.015625 48.578125 41.140625 45.015625 -Q 39.265625 41.453125 35.984375 38.859375 -Q 32.71875 36.28125 28.8125 35.015625 -Q 33.15625 34.1875 37.0625 31.734375 -Q 40.96875 29.296875 43.328125 25.484375 -Q 45.703125 21.6875 45.703125 17.28125 -Q 45.703125 11.765625 42.671875 7.296875 -Q 39.65625 2.828125 34.71875 0.3125 -Q 29.78125 -2.203125 24.3125 -2.203125 -Q 19.625 -2.203125 14.90625 -0.40625 -Q 10.203125 1.375 7.203125 4.9375 -Q 4.203125 8.5 4.203125 13.484375 -Q 4.203125 15.96875 5.859375 17.625 -Q 7.515625 19.28125 10.015625 19.28125 -Q 11.625 19.28125 12.96875 18.53125 -Q 14.3125 17.78125 15.0625 16.40625 -Q 15.828125 15.046875 15.828125 13.484375 -Q 15.828125 11.03125 14.109375 9.375 -Q 12.40625 7.71875 10.015625 7.71875 -z -" id="Cmr10-51"/> - <path d="M 2.09375 -22.703125 -L 10.5 -22.703125 -L 10.5 72.703125 -L 2.09375 72.703125 -L 2.09375 75 -L 17.203125 75 -L 17.203125 -25 -L 2.09375 -25 -z -" id="CMUSerif-Roman-93"/> - </defs> - <g transform="translate(48.976 221.69725)scale(0.1 -0.1)"> - <use transform="translate(0 0.109375)" xlink:href="#CMUSerif-Roman-72"/> - <use transform="translate(74.999985 0.109375)" xlink:href="#CMUSerif-Roman-121"/> - <use transform="translate(127.699982 0.109375)" xlink:href="#CMUSerif-Roman-100"/> - <use transform="translate(183.199966 0.109375)" xlink:href="#CMUSerif-Roman-114"/> - <use transform="translate(222.299957 0.109375)" xlink:href="#CMUSerif-Roman-111"/> - <use transform="translate(272.299942 0.109375)" xlink:href="#CMUSerif-Roman-103"/> - <use transform="translate(322.299927 0.109375)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(366.699921 0.109375)" xlink:href="#CMUSerif-Roman-110"/> - <use transform="translate(422.199905 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(455.499893 0.109375)" xlink:href="#CMUSerif-Roman-110"/> - <use transform="translate(510.999878 0.109375)" xlink:href="#CMUSerif-Roman-117"/> - <use transform="translate(566.499863 0.109375)" xlink:href="#CMUSerif-Roman-109"/> - <use transform="translate(649.79985 0.109375)" xlink:href="#CMUSerif-Roman-98"/> - <use transform="translate(705.299835 0.109375)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(749.699829 0.109375)" xlink:href="#CMUSerif-Roman-114"/> - <use transform="translate(788.79982 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(822.099808 0.109375)" xlink:href="#CMUSerif-Roman-100"/> - <use transform="translate(877.599792 0.109375)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(921.999786 0.109375)" xlink:href="#CMUSerif-Roman-110"/> - <use transform="translate(977.499771 0.109375)" xlink:href="#CMUSerif-Roman-115"/> - <use transform="translate(1016.899765 0.109375)" xlink:href="#CMUSerif-Roman-105"/> - <use transform="translate(1044.599762 0.109375)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(1083.39975 0.109375)" xlink:href="#CMUSerif-Roman-121"/> - <use transform="translate(1136.099747 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(1169.399734 0.109375)" xlink:href="#Cmmi10-110"/> - <use transform="translate(1229.4095 -16.896875)scale(0.7)" xlink:href="#Cmr10-72"/> - <use transform="translate(1288.286844 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(1321.586832 0.109375)" xlink:href="#CMUSerif-Roman-91"/> - <use transform="translate(1349.386819 0.109375)" xlink:href="#CMUSerif-Roman-99"/> - <use transform="translate(1393.786813 0.109375)" xlink:href="#CMUSerif-Roman-109"/> - <use transform="translate(1481.550942 38.373438)scale(0.7)" xlink:href="#Cmsy10-161"/> - <use transform="translate(1535.930825 38.373438)scale(0.7)" xlink:href="#Cmr10-51"/> - <use transform="translate(1577.308168 0.109375)" xlink:href="#CMUSerif-Roman-93"/> - </g> - </g> - </g> - <g id="matplotlib.axis_2"> - <g id="ytick_1"> - <g id="line2d_6"> - <defs> - <path d="M 0 0 -L -3.5 0 -" id="mbe9d5df88a" style="stroke:#000000;stroke-width:0.8;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="163.232825"/> - </g> - </g> - <g id="text_7"> - <!-- $\mathdefault{10^{2}}$ --> - <g transform="translate(13.22 166.703137)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/> - </g> - </g> - </g> - <g id="ytick_2"> - <g id="line2d_7"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="114.470825"/> - </g> - </g> - <g id="text_8"> - <!-- $\mathdefault{10^{3}}$ --> - <defs> - <path d="M 4.203125 13.5 -Q 4.203125 16.5 5.890625 17.890625 -Q 7.59375 19.296875 9.796875 19.296875 -Q 12.09375 19.296875 13.75 17.796875 -Q 15.40625 16.296875 15.40625 13.703125 -Q 15.40625 10.90625 13.453125 9.34375 -Q 11.5 7.796875 8.796875 8.203125 -Q 11.203125 4.203125 15.59375 2.390625 -Q 20 0.59375 24.09375 0.59375 -Q 28.40625 0.59375 31.90625 4.296875 -Q 35.40625 8 35.40625 17.09375 -Q 35.40625 24.796875 32.40625 29.25 -Q 29.40625 33.703125 23.5 33.703125 -L 19.09375 33.703125 -Q 17.59375 33.703125 17.140625 33.84375 -Q 16.703125 34 16.703125 34.796875 -Q 16.703125 35.796875 18.203125 36 -Q 19.703125 36 22.09375 36.296875 -Q 27.90625 36.5 31 41.5 -Q 33.796875 46.203125 33.796875 52.90625 -Q 33.796875 59 30.890625 61.546875 -Q 28 64.09375 24.203125 64.09375 -Q 20.703125 64.09375 16.84375 62.640625 -Q 13 61.203125 10.90625 57.90625 -Q 17.09375 57.90625 17.09375 52.90625 -Q 17.09375 50.703125 15.6875 49.25 -Q 14.296875 47.796875 12 47.796875 -Q 9.796875 47.796875 8.34375 49.1875 -Q 6.90625 50.59375 6.90625 53 -Q 6.90625 58.703125 12 62.640625 -Q 17.09375 66.59375 24.59375 66.59375 -Q 32 66.59375 37.5 62.6875 -Q 43 58.796875 43 52.796875 -Q 43 46.90625 39.09375 42.046875 -Q 35.203125 37.203125 29 35.203125 -Q 36.59375 33.703125 41.140625 28.546875 -Q 45.703125 23.40625 45.703125 17.09375 -Q 45.703125 9.296875 39.546875 3.546875 -Q 33.40625 -2.203125 24.40625 -2.203125 -Q 16.09375 -2.203125 10.140625 2.296875 -Q 4.203125 6.796875 4.203125 13.5 -z -" id="CMUSerif-Roman-51"/> - </defs> - <g transform="translate(13.22 117.941137)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-51"/> - </g> - </g> - </g> - <g id="ytick_3"> - <g id="line2d_8"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="65.708825"/> - </g> - </g> - <g id="text_9"> - <!-- $\mathdefault{10^{4}}$ --> - <g transform="translate(13.22 69.179137)scale(0.1 -0.1)"> - <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-52"/> - </g> - </g> - </g> - <g id="ytick_4"> - <g id="line2d_9"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="16.946825"/> - </g> - </g> - <g id="text_10"> - <!-- $\mathdefault{10^{5}}$ --> - <defs> - <path d="M 5 16.09375 -Q 5 19.09375 6.59375 20.25 -Q 8.203125 21.40625 9.90625 21.40625 -Q 12.203125 21.40625 13.546875 19.953125 -Q 14.90625 18.5 14.90625 16.5 -Q 14.90625 14.5 13.546875 13.046875 -Q 12.203125 11.59375 9.90625 11.59375 -Q 8.796875 11.59375 8.203125 11.796875 -Q 9.5 7.203125 13.546875 3.890625 -Q 17.59375 0.59375 22.90625 0.59375 -Q 29.59375 0.59375 33.59375 7.09375 -Q 36 11.296875 36 20.796875 -Q 36 29.203125 34.203125 33.40625 -Q 31.40625 39.796875 25.703125 39.796875 -Q 17.59375 39.796875 12.796875 32.796875 -Q 12.203125 31.90625 11.5 31.90625 -Q 10.5 31.90625 10.296875 32.453125 -Q 10.09375 33 10.09375 34.5 -L 10.09375 64.09375 -Q 10.09375 66.5 11.09375 66.5 -Q 11.5 66.5 12.296875 66.203125 -Q 18.59375 63.40625 25.59375 63.40625 -Q 32.796875 63.40625 39.203125 66.296875 -Q 39.703125 66.59375 40 66.59375 -Q 41 66.59375 41 65.5 -Q 41 65.09375 40.203125 63.9375 -Q 39.40625 62.796875 37.703125 61.296875 -Q 36 59.796875 33.796875 58.390625 -Q 31.59375 57 28.390625 56.046875 -Q 25.203125 55.09375 21.703125 55.09375 -Q 17.5 55.09375 13.203125 56.40625 -L 13.203125 36.90625 -Q 18.40625 42 25.90625 42 -Q 33.90625 42 39.40625 35.546875 -Q 44.90625 29.09375 44.90625 20.09375 -Q 44.90625 10.703125 38.40625 4.25 -Q 31.90625 -2.203125 23.09375 -2.203125 -Q 15.09375 -2.203125 10.046875 3.5 -Q 5 9.203125 5 16.09375 -z -" id="CMUSerif-Roman-53"/> - </defs> - <g transform="translate(13.22 20.417137)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-53"/> - </g> - </g> - </g> - <g id="ytick_5"> - <g id="line2d_10"> - <defs> - <path d="M 0 0 -L -2 0 -" id="m1f4f509585" style="stroke:#000000;stroke-width:0.6;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="197.316"/> - </g> - </g> - </g> - <g id="ytick_6"> - <g id="line2d_11"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="188.729438"/> - </g> - </g> - </g> - <g id="ytick_7"> - <g id="line2d_12"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="182.637175"/> - </g> - </g> - </g> - <g id="ytick_8"> - <g id="line2d_13"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="177.911649"/> - </g> - </g> - </g> - <g id="ytick_9"> - <g id="line2d_14"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="174.050613"/> - </g> - </g> - </g> - <g id="ytick_10"> - <g id="line2d_15"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="170.786154"/> - </g> - </g> - </g> - <g id="ytick_11"> - <g id="line2d_16"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="167.958351"/> - </g> - </g> - </g> - <g id="ytick_12"> - <g id="line2d_17"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="165.464051"/> - </g> - </g> - </g> - <g id="ytick_13"> - <g id="line2d_18"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="148.554"/> - </g> - </g> - </g> - <g id="ytick_14"> - <g id="line2d_19"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="139.967438"/> - </g> - </g> - </g> - <g id="ytick_15"> - <g id="line2d_20"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="133.875175"/> - </g> - </g> - </g> - <g id="ytick_16"> - <g id="line2d_21"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="129.149649"/> - </g> - </g> - </g> - <g id="ytick_17"> - <g id="line2d_22"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="125.288613"/> - </g> - </g> - </g> - <g id="ytick_18"> - <g id="line2d_23"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="122.024154"/> - </g> - </g> - </g> - <g id="ytick_19"> - <g id="line2d_24"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="119.196351"/> - </g> - </g> - </g> - <g id="ytick_20"> - <g id="line2d_25"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="116.702051"/> - </g> - </g> - </g> - <g id="ytick_21"> - <g id="line2d_26"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="99.792"/> - </g> - </g> - </g> - <g id="ytick_22"> - <g id="line2d_27"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="91.205438"/> - </g> - </g> - </g> - <g id="ytick_23"> - <g id="line2d_28"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="85.113175"/> - </g> - </g> - </g> - <g id="ytick_24"> - <g id="line2d_29"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="80.387649"/> - </g> - </g> - </g> - <g id="ytick_25"> - <g id="line2d_30"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="76.526613"/> - </g> - </g> - </g> - <g id="ytick_26"> - <g id="line2d_31"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="73.262154"/> - </g> - </g> - </g> - <g id="ytick_27"> - <g id="line2d_32"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="70.434351"/> - </g> - </g> - </g> - <g id="ytick_28"> - <g id="line2d_33"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="67.940051"/> - </g> - </g> - </g> - <g id="ytick_29"> - <g id="line2d_34"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="51.03"/> - </g> - </g> - </g> - <g id="ytick_30"> - <g id="line2d_35"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="42.443438"/> - </g> - </g> - </g> - <g id="ytick_31"> - <g id="line2d_36"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="36.351175"/> - </g> - </g> - </g> - <g id="ytick_32"> - <g id="line2d_37"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="31.625649"/> - </g> - </g> - </g> - <g id="ytick_33"> - <g id="line2d_38"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="27.764613"/> - </g> - </g> - </g> - <g id="ytick_34"> - <g id="line2d_39"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="24.500154"/> - </g> - </g> - </g> - <g id="ytick_35"> - <g id="line2d_40"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="21.672351"/> - </g> - </g> - </g> - <g id="ytick_36"> - <g id="line2d_41"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="19.178051"/> - </g> - </g> - </g> - <g id="ytick_37"> - <g id="line2d_42"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="2.268"/> - </g> - </g> - </g> - <g id="text_11"> - <!-- Temperature $T$ [K] --> - <defs> - <path d="M 3.59375 45.203125 -L 5.5 67.703125 -L 66.59375 67.703125 -L 68.5 45.203125 -L 66 45.203125 -Q 65.59375 49.703125 65.25 52.296875 -Q 64.90625 54.90625 64.046875 57.34375 -Q 63.203125 59.796875 62.140625 61 -Q 61.09375 62.203125 59.09375 63.140625 -Q 57.09375 64.09375 54.5 64.34375 -Q 51.90625 64.59375 48 64.59375 -Q 43.40625 64.59375 42.296875 64.40625 -Q 41.09375 64.09375 40.796875 63.25 -Q 40.5 62.40625 40.5 60.59375 -L 40.5 7.90625 -Q 40.5 5.90625 40.890625 5.09375 -Q 41.296875 4.296875 43.59375 3.6875 -Q 45.90625 3.09375 51 3.09375 -L 55 3.09375 -L 55 0 -Q 50.90625 0.296875 36 0.296875 -Q 21.203125 0.296875 17.09375 0 -L 17.09375 3.09375 -L 21.09375 3.09375 -Q 26.203125 3.09375 28.5 3.6875 -Q 30.796875 4.296875 31.1875 5.09375 -Q 31.59375 5.90625 31.59375 7.90625 -L 31.59375 60.59375 -Q 31.59375 62 31.5 62.59375 -Q 31.40625 63.203125 30.953125 63.703125 -Q 30.5 64.203125 29.5 64.390625 -Q 28.5 64.59375 24.09375 64.59375 -Q 20.203125 64.59375 17.59375 64.34375 -Q 15 64.09375 13 63.140625 -Q 11 62.203125 9.953125 61 -Q 8.90625 59.796875 8.046875 57.34375 -Q 7.203125 54.90625 6.84375 52.296875 -Q 6.5 49.703125 6.09375 45.203125 -z -" id="CMUSerif-Roman-84"/> - <path d="M 2.796875 -16.296875 -Q 8.09375 -16.296875 9.34375 -15.640625 -Q 10.59375 -15 10.59375 -11.796875 -L 10.59375 35 -Q 10.59375 38.296875 9.34375 39.140625 -Q 8.09375 40 2.796875 40 -L 2.796875 43.09375 -L 17.203125 44.203125 -L 17.203125 37.59375 -Q 23.203125 44.203125 31.203125 44.203125 -Q 39.703125 44.203125 45.890625 37.59375 -Q 52.09375 31 52.09375 21.59375 -Q 52.09375 12.09375 45.5 5.5 -Q 38.90625 -1.09375 29.796875 -1.09375 -Q 24.796875 -1.09375 21.4375 1.453125 -Q 18.09375 4 17.5 5.90625 -L 17.5 5 -L 17.5 -11.796875 -Q 17.5 -15 18.75 -15.640625 -Q 20 -16.296875 25.296875 -16.296875 -L 25.296875 -19.40625 -Q 14.796875 -19.09375 14 -19.09375 -Q 13 -19.09375 2.796875 -19.40625 -z -M 17.5 11.40625 -Q 17.5 9.90625 17.703125 9.34375 -Q 17.90625 8.796875 18.90625 7.203125 -Q 22.90625 1.09375 29.40625 1.09375 -Q 35.09375 1.09375 39.4375 6.9375 -Q 43.796875 12.796875 43.796875 21.59375 -Q 43.796875 30 39.84375 35.84375 -Q 35.90625 41.703125 30.40625 41.703125 -Q 26.5 41.703125 23.09375 39.59375 -Q 19.703125 37.5 17.5 33.703125 -z -" id="CMUSerif-Roman-112"/> - <path d="M 4.203125 9.5 -Q 4.203125 18 14.203125 22.5 -Q 20.203125 25.40625 32.59375 26.09375 -L 32.59375 29.796875 -Q 32.59375 36 29.34375 39.296875 -Q 26.09375 42.59375 22 42.59375 -Q 14.703125 42.59375 11.203125 38 -Q 14.203125 37.90625 15.25 36.40625 -Q 16.296875 34.90625 16.296875 33.40625 -Q 16.296875 31.40625 15.046875 30.09375 -Q 13.796875 28.796875 11.703125 28.796875 -Q 9.703125 28.796875 8.390625 30.046875 -Q 7.09375 31.296875 7.09375 33.5 -Q 7.09375 38.40625 11.5 41.59375 -Q 15.90625 44.796875 22.203125 44.796875 -Q 30.40625 44.796875 35.90625 39.296875 -Q 37.59375 37.59375 38.4375 35.390625 -Q 39.296875 33.203125 39.390625 31.75 -Q 39.5 30.296875 39.5 27.5 -L 39.5 7.5 -Q 39.5 6.90625 39.703125 5.953125 -Q 39.90625 5 40.796875 3.75 -Q 41.703125 2.5 43.203125 2.5 -Q 46.796875 2.5 46.796875 8.90625 -L 46.796875 14.5 -L 49.296875 14.5 -L 49.296875 8.90625 -Q 49.296875 3.59375 46.5 1.5 -Q 43.703125 -0.59375 41.09375 -0.59375 -Q 37.796875 -0.59375 35.6875 1.84375 -Q 33.59375 4.296875 33.296875 7.59375 -Q 31.796875 3.796875 28.34375 1.34375 -Q 24.90625 -1.09375 20.203125 -1.09375 -Q 16.59375 -1.09375 13.1875 -0.1875 -Q 9.796875 0.703125 7 3.203125 -Q 4.203125 5.703125 4.203125 9.5 -z -M 11.90625 9.59375 -Q 11.90625 5.90625 14.546875 3.5 -Q 17.203125 1.09375 20.90625 1.09375 -Q 25.09375 1.09375 28.84375 4.34375 -Q 32.59375 7.59375 32.59375 14 -L 32.59375 24 -Q 21.5 23.59375 16.703125 19.1875 -Q 11.90625 14.796875 11.90625 9.59375 -z -" id="CMUSerif-Roman-97"/> - <path d="M 4.59375 1.3125 -Q 4.640625 1.5625 4.8125 2.1875 -Q 4.984375 2.828125 5.25 3.171875 -Q 5.515625 3.515625 6 3.515625 -Q 14.59375 3.515625 17.390625 4 -Q 20.0625 4.6875 20.609375 6.890625 -L 34.28125 61.8125 -Q 34.71875 63.03125 34.71875 64.015625 -Q 34.71875 64.796875 31.203125 64.796875 -L 25.390625 64.796875 -Q 18.703125 64.796875 15.0625 62.734375 -Q 11.421875 60.6875 9.71875 57.3125 -Q 8.015625 53.953125 5.328125 46.296875 -Q 4.984375 45.40625 4.296875 45.40625 -L 3.421875 45.40625 -Q 2.390625 45.40625 2.390625 46.6875 -L 9.515625 67.390625 -Q 9.71875 68.3125 10.5 68.3125 -L 69.578125 68.3125 -Q 70.609375 68.3125 70.609375 67 -L 67.28125 46.296875 -Q 67.28125 46 66.9375 45.703125 -Q 66.609375 45.40625 66.3125 45.40625 -L 65.375 45.40625 -Q 64.40625 45.40625 64.40625 46.6875 -Q 65.484375 53.765625 65.484375 56.6875 -Q 65.484375 60.203125 64.015625 62 -Q 62.546875 63.8125 60.203125 64.296875 -Q 57.859375 64.796875 54.109375 64.796875 -L 48.1875 64.796875 -Q 45.515625 64.796875 44.578125 64.296875 -Q 43.65625 63.8125 43.015625 61.375 -L 29.296875 6.5 -Q 29.25 6.296875 29.21875 6.09375 -Q 29.203125 5.90625 29.109375 5.609375 -Q 29.109375 4.34375 30.609375 4 -Q 33.203125 3.515625 41.703125 3.515625 -Q 42.671875 3.515625 42.671875 2.203125 -Q 42.328125 0.78125 42.125 0.390625 -Q 41.9375 0 41.015625 0 -L 5.609375 0 -Q 4.59375 0 4.59375 1.3125 -z -" id="Cmmi10-84"/> - <path d="M 3.296875 0 -L 3.296875 3.09375 -L 5.703125 3.09375 -Q 11.09375 3.09375 12.34375 4 -Q 13.59375 4.90625 13.59375 7.796875 -L 13.59375 60.5 -Q 13.59375 63.40625 12.34375 64.296875 -Q 11.09375 65.203125 5.703125 65.203125 -L 3.296875 65.203125 -L 3.296875 68.296875 -Q 6.796875 68 18.09375 68 -Q 29.296875 68 32.796875 68.296875 -L 32.796875 65.203125 -L 30.40625 65.203125 -Q 25 65.203125 23.75 64.296875 -Q 22.5 63.40625 22.5 60.5 -L 22.5 28.703125 -L 53.796875 58.59375 -Q 55.59375 60.203125 55.59375 61.90625 -Q 55.59375 62.5 55.34375 63.140625 -Q 55.09375 63.796875 53.9375 64.5 -Q 52.796875 65.203125 51 65.203125 -L 51 68.296875 -Q 54.40625 68 63.703125 68 -Q 69.59375 68 72.203125 68.296875 -L 72.203125 65.203125 -Q 63.90625 65.09375 58 59.203125 -L 40 41.90625 -L 63.09375 7.90625 -Q 65.203125 4.796875 67.25 3.9375 -Q 69.296875 3.09375 73.59375 3.09375 -L 73.59375 0 -Q 67.296875 0.296875 62.09375 0.296875 -Q 51.296875 0.296875 47.796875 0 -L 47.796875 3.09375 -Q 53.703125 3.09375 53.703125 6.09375 -Q 53.703125 7.203125 52.203125 9.5 -L 34.09375 36.296875 -L 22.5 25.296875 -L 22.5 7.796875 -Q 22.5 4.90625 23.75 4 -Q 25 3.09375 30.40625 3.09375 -L 32.796875 3.09375 -L 32.796875 0 -Q 29.296875 0.296875 18 0.296875 -Q 6.796875 0.296875 3.296875 0 -z -" id="CMUSerif-Roman-75"/> - </defs> - <g transform="translate(8.72 141.042)rotate(-90)scale(0.1 -0.1)"> - <use xlink:href="#CMUSerif-Roman-84"/> - <use transform="translate(72.199982 0)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(116.599976 0)" xlink:href="#CMUSerif-Roman-109"/> - <use transform="translate(199.899963 0)" xlink:href="#CMUSerif-Roman-112"/> - <use transform="translate(255.399948 0)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(299.799942 0)" xlink:href="#CMUSerif-Roman-114"/> - <use transform="translate(338.899933 0)" xlink:href="#CMUSerif-Roman-97"/> - <use transform="translate(388.899918 0)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(427.699905 0)" xlink:href="#CMUSerif-Roman-117"/> - <use transform="translate(483.19989 0)" xlink:href="#CMUSerif-Roman-114"/> - <use transform="translate(522.299881 0)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(566.699875 0)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(599.999863 0)" xlink:href="#Cmmi10-84"/> - <use transform="translate(658.3983 0)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(691.698288 0)" xlink:href="#CMUSerif-Roman-91"/> - <use transform="translate(719.498276 0)" xlink:href="#CMUSerif-Roman-75"/> - <use transform="translate(797.198257 0)" xlink:href="#CMUSerif-Roman-93"/> - </g> - </g> - </g> - <g id="line2d_43"> - <path clip-path="url(#p70b35b528f)" d="M -1 209.323214 -L 223.107611 -1 -L 223.107611 -1 -" style="fill:none;stroke:#000000;stroke-linecap:square;"/> - </g> - <g id="line2d_44"> - <path clip-path="url(#p70b35b528f)" d="M -1 70.434351 -L 146.991516 70.434351 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_45"> - <path clip-path="url(#p70b35b528f)" d="M 146.991516 197.316 -L 146.991516 70.434351 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_46"> - <path clip-path="url(#p70b35b528f)" d="M 146.991516 51.03 -L 181.630061 18.522 -" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="patch_3"> - <path d="M 34.02 197.316 -L 34.02 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_4"> - <path d="M 224.532 197.316 -L 224.532 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_5"> - <path d="M 34.02 197.316 -L 224.532 197.316 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_6"> - <path d="M 34.02 2.268 -L 224.532 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="text_12"> - <!-- $n_{\rm H}$^EOS_gamma_effective --> - <defs> - <path d="M 34.421875 72.90625 -L 56.6875 45.703125 -L 48 45.703125 -L 30.078125 64.984375 -L 12.203125 45.703125 -L 3.515625 45.703125 -L 25.78125 72.90625 -z -" id="DejaVuSansMono-94"/> - <path d="M 9.625 72.90625 -L 52.875 72.90625 -L 52.875 64.59375 -L 19.484375 64.59375 -L 19.484375 43.015625 -L 51.421875 43.015625 -L 51.421875 34.71875 -L 19.484375 34.71875 -L 19.484375 8.296875 -L 53.8125 8.296875 -L 53.8125 0 -L 9.625 0 -z -" id="DejaVuSansMono-69"/> - <path d="M 44.1875 36.375 -Q 44.1875 52.4375 40.890625 59.328125 -Q 37.59375 66.21875 30.078125 66.21875 -Q 22.609375 66.21875 19.3125 59.328125 -Q 16.015625 52.4375 16.015625 36.375 -Q 16.015625 20.359375 19.3125 13.46875 -Q 22.609375 6.59375 30.078125 6.59375 -Q 37.59375 6.59375 40.890625 13.453125 -Q 44.1875 20.3125 44.1875 36.375 -z -M 54.5 36.375 -Q 54.5 17.328125 48.46875 7.953125 -Q 42.4375 -1.421875 30.078125 -1.421875 -Q 17.71875 -1.421875 11.71875 7.90625 -Q 5.71875 17.234375 5.71875 36.375 -Q 5.71875 55.46875 11.75 64.84375 -Q 17.78125 74.21875 30.078125 74.21875 -Q 42.4375 74.21875 48.46875 64.84375 -Q 54.5 55.46875 54.5 36.375 -z -" id="DejaVuSansMono-79"/> - <path d="M 49.421875 70.40625 -L 49.421875 60.40625 -Q 44.921875 63.28125 40.40625 64.75 -Q 35.890625 66.21875 31.296875 66.21875 -Q 24.3125 66.21875 20.265625 62.96875 -Q 16.21875 59.71875 16.21875 54.203125 -Q 16.21875 49.359375 18.875 46.8125 -Q 21.53125 44.28125 28.8125 42.578125 -L 33.984375 41.40625 -Q 44.234375 39.015625 48.921875 33.890625 -Q 53.609375 28.765625 53.609375 19.921875 -Q 53.609375 9.515625 47.15625 4.046875 -Q 40.71875 -1.421875 28.421875 -1.421875 -Q 23.296875 -1.421875 18.109375 -0.3125 -Q 12.9375 0.78125 7.71875 2.984375 -L 7.71875 13.484375 -Q 13.328125 9.90625 18.328125 8.25 -Q 23.34375 6.59375 28.421875 6.59375 -Q 35.890625 6.59375 40.03125 9.9375 -Q 44.1875 13.28125 44.1875 19.28125 -Q 44.1875 24.75 41.328125 27.625 -Q 38.484375 30.515625 31.390625 32.078125 -L 26.125 33.296875 -Q 15.96875 35.59375 11.375 40.234375 -Q 6.78125 44.875 6.78125 52.6875 -Q 6.78125 62.453125 13.34375 68.328125 -Q 19.921875 74.21875 30.8125 74.21875 -Q 35.015625 74.21875 39.640625 73.265625 -Q 44.28125 72.3125 49.421875 70.40625 -z -" id="DejaVuSansMono-83"/> - <path d="M 60.203125 -19.671875 -L 60.203125 -23.578125 -L 0 -23.578125 -L 0 -19.671875 -z -" id="DejaVuSansMono-95"/> - <path d="M 41.890625 27.78125 -Q 41.890625 37.890625 38.59375 43.140625 -Q 35.296875 48.390625 29 48.390625 -Q 22.40625 48.390625 18.9375 43.140625 -Q 15.484375 37.890625 15.484375 27.78125 -Q 15.484375 17.671875 18.96875 12.375 -Q 22.46875 7.078125 29.109375 7.078125 -Q 35.296875 7.078125 38.59375 12.390625 -Q 41.890625 17.71875 41.890625 27.78125 -z -M 50.875 3.515625 -Q 50.875 -8.796875 45.0625 -15.140625 -Q 39.265625 -21.484375 27.984375 -21.484375 -Q 24.265625 -21.484375 20.203125 -20.796875 -Q 16.15625 -20.125 12.109375 -18.796875 -L 12.109375 -9.90625 -Q 16.890625 -12.15625 20.796875 -13.234375 -Q 24.703125 -14.3125 27.984375 -14.3125 -Q 35.25 -14.3125 38.5625 -10.34375 -Q 41.890625 -6.390625 41.890625 2.203125 -L 41.890625 2.59375 -L 41.890625 8.6875 -Q 39.75 4.109375 36.03125 1.859375 -Q 32.328125 -0.390625 27 -0.390625 -Q 17.4375 -0.390625 11.71875 7.265625 -Q 6 14.9375 6 27.78125 -Q 6 40.671875 11.71875 48.328125 -Q 17.4375 56 27 56 -Q 32.28125 56 35.9375 53.90625 -Q 39.59375 51.8125 41.890625 47.40625 -L 41.890625 54.5 -L 50.875 54.5 -z -" id="DejaVuSansMono-103"/> - <path d="M 34.28125 27.484375 -L 31.296875 27.484375 -Q 23.4375 27.484375 19.453125 24.71875 -Q 15.484375 21.96875 15.484375 16.5 -Q 15.484375 11.578125 18.453125 8.84375 -Q 21.4375 6.109375 26.703125 6.109375 -Q 34.125 6.109375 38.375 11.25 -Q 42.625 16.40625 42.671875 25.484375 -L 42.671875 27.484375 -z -M 51.703125 31.203125 -L 51.703125 0 -L 42.671875 0 -L 42.671875 8.109375 -Q 39.796875 3.21875 35.421875 0.890625 -Q 31.0625 -1.421875 24.8125 -1.421875 -Q 16.453125 -1.421875 11.46875 3.296875 -Q 6.5 8.015625 6.5 15.921875 -Q 6.5 25.046875 12.625 29.78125 -Q 18.75 34.515625 30.609375 34.515625 -L 42.671875 34.515625 -L 42.671875 35.9375 -Q 42.625 42.484375 39.34375 45.4375 -Q 36.078125 48.390625 28.90625 48.390625 -Q 24.3125 48.390625 19.625 47.0625 -Q 14.9375 45.75 10.5 43.21875 -L 10.5 52.203125 -Q 15.484375 54.109375 20.046875 55.046875 -Q 24.609375 56 28.90625 56 -Q 35.6875 56 40.5 54 -Q 45.3125 52 48.296875 48 -Q 50.140625 45.5625 50.921875 41.96875 -Q 51.703125 38.375 51.703125 31.203125 -z -" id="DejaVuSansMono-97"/> - <path d="M 33.015625 49.125 -Q 34.671875 52.640625 37.234375 54.3125 -Q 39.796875 56 43.40625 56 -Q 50 56 52.703125 50.890625 -Q 55.421875 45.796875 55.421875 31.6875 -L 55.421875 0 -L 47.21875 0 -L 47.21875 31.296875 -Q 47.21875 42.875 45.921875 45.671875 -Q 44.625 48.484375 41.21875 48.484375 -Q 37.3125 48.484375 35.859375 45.484375 -Q 34.421875 42.484375 34.421875 31.296875 -L 34.421875 0 -L 26.21875 0 -L 26.21875 31.296875 -Q 26.21875 43.015625 24.828125 45.75 -Q 23.4375 48.484375 19.828125 48.484375 -Q 16.265625 48.484375 14.875 45.484375 -Q 13.484375 42.484375 13.484375 31.296875 -L 13.484375 0 -L 5.328125 0 -L 5.328125 54.6875 -L 13.484375 54.6875 -L 13.484375 50 -Q 15.09375 52.9375 17.5 54.46875 -Q 19.921875 56 23 56 -Q 26.703125 56 29.171875 54.296875 -Q 31.640625 52.59375 33.015625 49.125 -z -" id="DejaVuSansMono-109"/> - <path d="M 54.296875 29.59375 -L 54.296875 25.203125 -L 15.375 25.203125 -L 15.375 24.90625 -Q 15.375 15.96875 20.03125 11.078125 -Q 24.703125 6.203125 33.203125 6.203125 -Q 37.5 6.203125 42.1875 7.5625 -Q 46.875 8.9375 52.203125 11.71875 -L 52.203125 2.78125 -Q 47.078125 0.6875 42.3125 -0.359375 -Q 37.546875 -1.421875 33.109375 -1.421875 -Q 20.359375 -1.421875 13.171875 6.21875 -Q 6 13.875 6 27.296875 -Q 6 40.375 13.03125 48.1875 -Q 20.0625 56 31.78125 56 -Q 42.234375 56 48.265625 48.921875 -Q 54.296875 41.84375 54.296875 29.59375 -z -M 45.3125 32.234375 -Q 45.125 40.140625 41.578125 44.265625 -Q 38.03125 48.390625 31.390625 48.390625 -Q 24.90625 48.390625 20.703125 44.09375 -Q 16.5 39.796875 15.71875 32.171875 -z -" id="DejaVuSansMono-101"/> - <path d="M 51.90625 75.984375 -L 51.90625 68.5 -L 41.703125 68.5 -Q 36.859375 68.5 34.984375 66.515625 -Q 33.109375 64.546875 33.109375 59.515625 -L 33.109375 54.6875 -L 51.90625 54.6875 -L 51.90625 47.703125 -L 33.109375 47.703125 -L 33.109375 0 -L 24.125 0 -L 24.125 47.703125 -L 9.515625 47.703125 -L 9.515625 54.6875 -L 24.125 54.6875 -L 24.125 58.5 -Q 24.125 67.484375 28.25 71.734375 -Q 32.375 75.984375 41.109375 75.984375 -z -" id="DejaVuSansMono-102"/> - <path d="M 51.8125 2.78125 -Q 48.1875 0.6875 44.359375 -0.359375 -Q 40.53125 -1.421875 36.53125 -1.421875 -Q 23.828125 -1.421875 16.671875 6.1875 -Q 9.515625 13.8125 9.515625 27.296875 -Q 9.515625 40.765625 16.671875 48.375 -Q 23.828125 56 36.53125 56 -Q 40.484375 56 44.234375 54.96875 -Q 48 53.953125 51.8125 51.8125 -L 51.8125 42.390625 -Q 48.25 45.5625 44.65625 46.96875 -Q 41.0625 48.390625 36.53125 48.390625 -Q 28.078125 48.390625 23.53125 42.921875 -Q 19 37.453125 19 27.296875 -Q 19 17.1875 23.5625 11.6875 -Q 28.125 6.203125 36.53125 6.203125 -Q 41.21875 6.203125 44.921875 7.640625 -Q 48.640625 9.078125 51.8125 12.109375 -z -" id="DejaVuSansMono-99"/> - <path d="M 29.984375 70.21875 -L 29.984375 54.6875 -L 50.390625 54.6875 -L 50.390625 47.703125 -L 29.984375 47.703125 -L 29.984375 18.015625 -Q 29.984375 11.96875 32.28125 9.5625 -Q 34.578125 7.171875 40.28125 7.171875 -L 50.390625 7.171875 -L 50.390625 0 -L 39.40625 0 -Q 29.296875 0 25.140625 4.046875 -Q 21 8.109375 21 18.015625 -L 21 47.703125 -L 6.390625 47.703125 -L 6.390625 54.6875 -L 21 54.6875 -L 21 70.21875 -z -" id="DejaVuSansMono-116"/> - <path d="M 12.5 54.6875 -L 35.5 54.6875 -L 35.5 6.984375 -L 53.328125 6.984375 -L 53.328125 0 -L 8.6875 0 -L 8.6875 6.984375 -L 26.515625 6.984375 -L 26.515625 47.703125 -L 12.5 47.703125 -z -M 26.515625 75.984375 -L 35.5 75.984375 -L 35.5 64.59375 -L 26.515625 64.59375 -z -" id="DejaVuSansMono-105"/> - <path d="M 4.890625 54.6875 -L 14.203125 54.6875 -L 30.078125 8.796875 -L 46 54.6875 -L 55.328125 54.6875 -L 35.890625 0 -L 24.3125 0 -z -" id="DejaVuSansMono-118"/> - </defs> - <g transform="translate(145.146967 64.529336)rotate(-43)scale(0.065 -0.065)"> - <use transform="translate(0 0.015625)" xlink:href="#Cmmi10-110"/> - <use transform="translate(60.009766 -16.990625)scale(0.7)" xlink:href="#Cmr10-72"/> - <use transform="translate(118.887109 0.015625)" xlink:href="#DejaVuSansMono-94"/> - <use transform="translate(179.092187 0.015625)" xlink:href="#DejaVuSansMono-69"/> - <use transform="translate(239.297266 0.015625)" xlink:href="#DejaVuSansMono-79"/> - <use transform="translate(299.502344 0.015625)" xlink:href="#DejaVuSansMono-83"/> - <use transform="translate(359.707422 0.015625)" xlink:href="#DejaVuSansMono-95"/> - <use transform="translate(419.9125 0.015625)" xlink:href="#DejaVuSansMono-103"/> - <use transform="translate(480.117578 0.015625)" xlink:href="#DejaVuSansMono-97"/> - <use transform="translate(540.322656 0.015625)" xlink:href="#DejaVuSansMono-109"/> - <use transform="translate(600.527734 0.015625)" xlink:href="#DejaVuSansMono-109"/> - <use transform="translate(660.732813 0.015625)" xlink:href="#DejaVuSansMono-97"/> - <use transform="translate(720.937891 0.015625)" xlink:href="#DejaVuSansMono-95"/> - <use transform="translate(781.142969 0.015625)" xlink:href="#DejaVuSansMono-101"/> - <use transform="translate(841.348047 0.015625)" xlink:href="#DejaVuSansMono-102"/> - <use transform="translate(901.553125 0.015625)" xlink:href="#DejaVuSansMono-102"/> - <use transform="translate(961.758203 0.015625)" xlink:href="#DejaVuSansMono-101"/> - <use transform="translate(1021.963281 0.015625)" xlink:href="#DejaVuSansMono-99"/> - <use transform="translate(1082.168359 0.015625)" xlink:href="#DejaVuSansMono-116"/> - <use transform="translate(1142.373437 0.015625)" xlink:href="#DejaVuSansMono-105"/> - <use transform="translate(1202.578516 0.015625)" xlink:href="#DejaVuSansMono-118"/> - <use transform="translate(1262.783594 0.015625)" xlink:href="#DejaVuSansMono-101"/> - </g> - </g> - <g id="text_13"> - <!-- EOS_density_norm_H_p_cm3 --> - <defs> - <path d="M 41.890625 47.703125 -L 41.890625 75.984375 -L 50.875 75.984375 -L 50.875 0 -L 41.890625 0 -L 41.890625 6.890625 -Q 39.65625 2.828125 35.90625 0.703125 -Q 32.171875 -1.421875 27.296875 -1.421875 -Q 17.390625 -1.421875 11.6875 6.265625 -Q 6 13.96875 6 27.484375 -Q 6 40.828125 11.71875 48.40625 -Q 17.4375 56 27.296875 56 -Q 32.234375 56 35.984375 53.875 -Q 39.75 51.765625 41.890625 47.703125 -z -M 15.484375 27.296875 -Q 15.484375 16.84375 18.796875 11.515625 -Q 22.125 6.203125 28.609375 6.203125 -Q 35.109375 6.203125 38.5 11.5625 -Q 41.890625 16.9375 41.890625 27.296875 -Q 41.890625 37.703125 38.5 43.046875 -Q 35.109375 48.390625 28.609375 48.390625 -Q 22.125 48.390625 18.796875 43.0625 -Q 15.484375 37.75 15.484375 27.296875 -z -" id="DejaVuSansMono-100"/> - <path d="M 51.3125 33.890625 -L 51.3125 0 -L 42.28125 0 -L 42.28125 33.890625 -Q 42.28125 41.265625 39.6875 44.71875 -Q 37.109375 48.1875 31.59375 48.1875 -Q 25.296875 48.1875 21.890625 43.71875 -Q 18.5 39.265625 18.5 30.90625 -L 18.5 0 -L 9.515625 0 -L 9.515625 54.6875 -L 18.5 54.6875 -L 18.5 46.484375 -Q 20.90625 51.171875 25 53.578125 -Q 29.109375 56 34.71875 56 -Q 43.0625 56 47.1875 50.5 -Q 51.3125 45.015625 51.3125 33.890625 -z -" id="DejaVuSansMono-110"/> - <path d="M 47.515625 52.78125 -L 47.515625 44 -Q 43.65625 46.234375 39.75 47.359375 -Q 35.84375 48.484375 31.78125 48.484375 -Q 25.6875 48.484375 22.671875 46.5 -Q 19.671875 44.53125 19.671875 40.484375 -Q 19.671875 36.8125 21.921875 35 -Q 24.171875 33.203125 33.109375 31.5 -L 36.71875 30.8125 -Q 43.40625 29.546875 46.84375 25.734375 -Q 50.296875 21.921875 50.296875 15.828125 -Q 50.296875 7.71875 44.53125 3.140625 -Q 38.765625 -1.421875 28.515625 -1.421875 -Q 24.46875 -1.421875 20.015625 -0.5625 -Q 15.578125 0.296875 10.40625 2 -L 10.40625 11.28125 -Q 15.4375 8.6875 20.015625 7.390625 -Q 24.609375 6.109375 28.71875 6.109375 -Q 34.671875 6.109375 37.9375 8.515625 -Q 41.21875 10.9375 41.21875 15.28125 -Q 41.21875 21.53125 29.25 23.921875 -L 28.859375 24.03125 -L 25.484375 24.703125 -Q 17.71875 26.21875 14.15625 29.8125 -Q 10.59375 33.40625 10.59375 39.59375 -Q 10.59375 47.46875 15.90625 51.734375 -Q 21.234375 56 31.109375 56 -Q 35.5 56 39.546875 55.1875 -Q 43.609375 54.390625 47.515625 52.78125 -z -" id="DejaVuSansMono-115"/> - <path d="M 41.890625 17.578125 -Q 39.65625 11.859375 36.1875 2.546875 -Q 31.34375 -10.359375 29.6875 -13.1875 -Q 27.4375 -17 24.0625 -18.890625 -Q 20.703125 -20.796875 16.21875 -20.796875 -L 8.984375 -20.796875 -L 8.984375 -13.28125 -L 14.3125 -13.28125 -Q 18.265625 -13.28125 20.5 -10.984375 -Q 22.75 -8.6875 26.21875 0.875 -L 5.078125 54.6875 -L 14.59375 54.6875 -L 30.8125 11.921875 -L 46.78125 54.6875 -L 56.296875 54.6875 -z -" id="DejaVuSansMono-121"/> - <path d="M 30.078125 48.390625 -Q 23.25 48.390625 19.734375 43.0625 -Q 16.21875 37.75 16.21875 27.296875 -Q 16.21875 16.890625 19.734375 11.546875 -Q 23.25 6.203125 30.078125 6.203125 -Q 36.96875 6.203125 40.484375 11.546875 -Q 44 16.890625 44 27.296875 -Q 44 37.75 40.484375 43.0625 -Q 36.96875 48.390625 30.078125 48.390625 -z -M 30.078125 56 -Q 41.453125 56 47.484375 48.625 -Q 53.515625 41.265625 53.515625 27.296875 -Q 53.515625 13.28125 47.5 5.921875 -Q 41.5 -1.421875 30.078125 -1.421875 -Q 18.703125 -1.421875 12.6875 5.921875 -Q 6.6875 13.28125 6.6875 27.296875 -Q 6.6875 41.265625 12.6875 48.625 -Q 18.703125 56 30.078125 56 -z -" id="DejaVuSansMono-111"/> - <path d="M 56.390625 43.40625 -Q 53.515625 45.65625 50.53125 46.671875 -Q 47.5625 47.703125 44 47.703125 -Q 35.59375 47.703125 31.140625 42.421875 -Q 26.703125 37.15625 26.703125 27.203125 -L 26.703125 0 -L 17.671875 0 -L 17.671875 54.6875 -L 26.703125 54.6875 -L 26.703125 44 -Q 28.953125 49.8125 33.609375 52.90625 -Q 38.28125 56 44.671875 56 -Q 48 56 50.875 55.171875 -Q 53.765625 54.34375 56.390625 52.59375 -z -" id="DejaVuSansMono-114"/> - <path d="M 6.6875 72.90625 -L 16.609375 72.90625 -L 16.609375 43.015625 -L 43.609375 43.015625 -L 43.609375 72.90625 -L 53.515625 72.90625 -L 53.515625 0 -L 43.609375 0 -L 43.609375 34.71875 -L 16.609375 34.71875 -L 16.609375 0 -L 6.6875 0 -z -" id="DejaVuSansMono-72"/> - <path d="M 18.3125 6.890625 -L 18.3125 -20.796875 -L 9.28125 -20.796875 -L 9.28125 54.6875 -L 18.3125 54.6875 -L 18.3125 47.703125 -Q 20.5625 51.765625 24.296875 53.875 -Q 28.03125 56 32.90625 56 -Q 42.828125 56 48.46875 48.328125 -Q 54.109375 40.671875 54.109375 27.09375 -Q 54.109375 13.765625 48.4375 6.171875 -Q 42.78125 -1.421875 32.90625 -1.421875 -Q 27.9375 -1.421875 24.1875 0.703125 -Q 20.453125 2.828125 18.3125 6.890625 -z -M 44.671875 27.296875 -Q 44.671875 37.75 41.375 43.0625 -Q 38.09375 48.390625 31.59375 48.390625 -Q 25.046875 48.390625 21.671875 43.046875 -Q 18.3125 37.703125 18.3125 27.296875 -Q 18.3125 16.9375 21.671875 11.5625 -Q 25.046875 6.203125 31.59375 6.203125 -Q 38.09375 6.203125 41.375 11.515625 -Q 44.671875 16.84375 44.671875 27.296875 -z -" id="DejaVuSansMono-112"/> - <path d="M 37.890625 39.015625 -Q 45.0625 37.109375 48.875 32.25 -Q 52.6875 27.390625 52.6875 20.125 -Q 52.6875 10.0625 45.921875 4.3125 -Q 39.15625 -1.421875 27.203125 -1.421875 -Q 22.171875 -1.421875 16.9375 -0.484375 -Q 11.71875 0.4375 6.6875 2.203125 -L 6.6875 12.015625 -Q 11.671875 9.421875 16.5 8.15625 -Q 21.34375 6.890625 26.125 6.890625 -Q 34.234375 6.890625 38.578125 10.546875 -Q 42.921875 14.203125 42.921875 21.09375 -Q 42.921875 27.4375 38.578125 31.171875 -Q 34.234375 34.90625 26.8125 34.90625 -L 19.28125 34.90625 -L 19.28125 43.015625 -L 26.8125 43.015625 -Q 33.59375 43.015625 37.40625 45.984375 -Q 41.21875 48.96875 41.21875 54.296875 -Q 41.21875 59.90625 37.671875 62.90625 -Q 34.125 65.921875 27.59375 65.921875 -Q 23.25 65.921875 18.609375 64.9375 -Q 13.96875 63.96875 8.890625 62.015625 -L 8.890625 71.09375 -Q 14.796875 72.65625 19.40625 73.4375 -Q 24.03125 74.21875 27.59375 74.21875 -Q 38.234375 74.21875 44.609375 68.875 -Q 50.984375 63.53125 50.984375 54.6875 -Q 50.984375 48.6875 47.625 44.671875 -Q 44.28125 40.671875 37.890625 39.015625 -z -" id="DejaVuSansMono-51"/> - </defs> - <g transform="translate(144.955236 192.590474)rotate(-90)scale(0.07 -0.07)"> - <use xlink:href="#DejaVuSansMono-69"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-79"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-83"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-95"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-100"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-101"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-110"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-115"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-105"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-116"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-121"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-95"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-110"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-111"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-114"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-109"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-95"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-72"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-95"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-112"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-95"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-99"/> - <use x="1324.511719" xlink:href="#DejaVuSansMono-109"/> - <use x="1384.716797" xlink:href="#DejaVuSansMono-51"/> - </g> - </g> - <g id="text_14"> - <!-- EOS_temperature_norm_K --> - <defs> - <path d="M 9.515625 20.703125 -L 9.515625 54.59375 -L 18.5 54.59375 -L 18.5 20.703125 -Q 18.5 13.328125 21.109375 9.859375 -Q 23.734375 6.390625 29.203125 6.390625 -Q 35.546875 6.390625 38.90625 10.859375 -Q 42.28125 15.328125 42.28125 23.6875 -L 42.28125 54.59375 -L 51.3125 54.59375 -L 51.3125 0 -L 42.28125 0 -L 42.28125 8.203125 -Q 39.890625 3.46875 35.765625 1.015625 -Q 31.640625 -1.421875 26.125 -1.421875 -Q 17.71875 -1.421875 13.609375 4.078125 -Q 9.515625 9.578125 9.515625 20.703125 -z -" id="DejaVuSansMono-117"/> - <path d="M 6.6875 72.90625 -L 16.609375 72.90625 -L 16.609375 40.484375 -L 47.40625 72.90625 -L 58.984375 72.90625 -L 30.609375 43.109375 -L 59.8125 0 -L 47.90625 0 -L 24.125 36.53125 -L 16.609375 28.515625 -L 16.609375 0 -L 6.6875 0 -z -" id="DejaVuSansMono-75"/> - </defs> - <g transform="translate(37.862259 67.750649)scale(0.07 -0.07)"> - <use xlink:href="#DejaVuSansMono-69"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-79"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-83"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-95"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-116"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-101"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-109"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-112"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-101"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-114"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-97"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-116"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-117"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-114"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-101"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-95"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-110"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-111"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-114"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-109"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-95"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-75"/> - </g> - </g> - </g> - </g> - <defs> - <clipPath id="p70b35b528f"> - <rect height="195.048" width="190.512" x="34.02" y="2.268"/> - </clipPath> - </defs> -</svg> diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_Z_dep.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_Z_dep.svg deleted file mode 100644 index 5314e6c83b0e394cc1262eb59fbfaf763151923a..0000000000000000000000000000000000000000 --- a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_Z_dep.svg +++ /dev/null @@ -1,2649 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<!-- Created with matplotlib (https://matplotlib.org/) --> -<svg height="226.8pt" version="1.1" viewBox="0 0 226.8 226.8" width="226.8pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <defs> - <style type="text/css"> -*{stroke-linecap:butt;stroke-linejoin:round;} - </style> - </defs> - <g id="figure_1"> - <g id="patch_1"> - <path d="M 0 226.8 -L 226.8 226.8 -L 226.8 0 -L 0 0 -z -" style="fill:#ffffff;"/> - </g> - <g id="axes_1"> - <g id="patch_2"> - <path d="M 34.02 197.316 -L 224.532 197.316 -L 224.532 2.268 -L 34.02 2.268 -z -" style="fill:#ffffff;"/> - </g> - <g id="PathCollection_1"> - <defs> - <path d="M 0 1 -C 0.265203 1 0.51958 0.894634 0.707107 0.707107 -C 0.894634 0.51958 1 0.265203 1 0 -C 1 -0.265203 0.894634 -0.51958 0.707107 -0.707107 -C 0.51958 -0.894634 0.265203 -1 0 -1 -C -0.265203 -1 -0.51958 -0.894634 -0.707107 -0.707107 -C -0.894634 -0.51958 -1 -0.265203 -1 0 -C -1 0.265203 -0.894634 0.51958 -0.707107 0.707107 -C -0.51958 0.894634 -0.265203 1 0 1 -z -" id="mfeca7ee311" style="stroke:#000000;"/> - </defs> - <g clip-path="url(#p23ab05f3c8)"> - <use style="stroke:#000000;" x="151.076832" xlink:href="#mfeca7ee311" y="99.792"/> - </g> - </g> - <g id="matplotlib.axis_1"> - <g id="xtick_1"> - <g id="line2d_1"> - <defs> - <path d="M 0 0 -L 0 3.5 -" id="mf7651b78e9" style="stroke:#000000;stroke-width:0.8;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mf7651b78e9" y="197.316"/> - </g> - </g> - <g id="text_1"> - <!-- $\mathdefault{10^{-7}}$ --> - <defs> - <path d="M 8.90625 57.09375 -L 8.90625 60.203125 -Q 20.90625 60.203125 27.09375 66.59375 -Q 28.796875 66.59375 29.09375 66.1875 -Q 29.40625 65.796875 29.40625 64 -L 29.40625 7.90625 -Q 29.40625 4.90625 30.84375 4 -Q 32.296875 3.09375 38.703125 3.09375 -L 41.90625 3.09375 -L 41.90625 0 -Q 38.40625 0.296875 25.703125 0.296875 -Q 13 0.296875 9.5 0 -L 9.5 3.09375 -L 12.703125 3.09375 -Q 19 3.09375 20.5 4 -Q 22 4.90625 22 7.90625 -L 22 59.703125 -Q 16.796875 57.09375 8.90625 57.09375 -z -" id="CMUSerif-Roman-49"/> - <path d="M 3.90625 32 -Q 3.90625 46.703125 7.59375 54.703125 -Q 12.796875 66.59375 25 66.59375 -Q 27.59375 66.59375 30.296875 65.890625 -Q 33 65.203125 36.453125 62.5 -Q 39.90625 59.796875 42 55.40625 -Q 46 46.90625 46 32 -Q 46 17.40625 42.296875 9.40625 -Q 36.90625 -2.203125 24.90625 -2.203125 -Q 20.40625 -2.203125 15.84375 0.09375 -Q 11.296875 2.40625 8.40625 7.90625 -Q 3.90625 16.203125 3.90625 32 -z -M 12.203125 33.203125 -Q 12.203125 18.09375 13.296875 12.09375 -Q 14.5 5.59375 17.84375 2.796875 -Q 21.203125 0 24.90625 0 -Q 28.90625 0 32.25 3 -Q 35.59375 6 36.59375 12.5 -Q 37.703125 18.90625 37.703125 33.203125 -Q 37.703125 47.09375 36.703125 52.703125 -Q 35.40625 59.203125 31.90625 61.796875 -Q 28.40625 64.40625 24.90625 64.40625 -Q 23.59375 64.40625 22.1875 64 -Q 20.796875 63.59375 18.796875 62.5 -Q 16.796875 61.40625 15.25 58.59375 -Q 13.703125 55.796875 13 51.59375 -Q 12.203125 46.203125 12.203125 33.203125 -z -" id="CMUSerif-Roman-48"/> - <path d="M 1 18.59375 -L 1 24.5 -L 27.59375 24.5 -L 27.59375 18.59375 -z -" id="CMUSerif-Roman-45"/> - <path d="M 5.59375 47 -L 8.90625 67.59375 -L 11.40625 67.59375 -Q 11.703125 66.40625 12.203125 65.84375 -Q 12.703125 65.296875 15.546875 64.84375 -Q 18.40625 64.40625 24.203125 64.40625 -L 48.5 64.40625 -Q 48.5 62.09375 47.59375 60.90625 -L 34.09375 41.90625 -Q 29.796875 35.90625 27.90625 24.796875 -Q 26.796875 17.40625 26.796875 8.40625 -L 26.796875 3.296875 -Q 26.796875 -2.203125 22.1875 -2.203125 -Q 17.59375 -2.203125 17.59375 3.296875 -Q 17.59375 22.40625 29.90625 40.90625 -Q 31.203125 42.796875 36.046875 49.59375 -Q 40.90625 56.40625 41.09375 56.703125 -L 20.40625 56.703125 -Q 11.296875 56.703125 10.59375 56.09375 -Q 9.59375 55.203125 8.09375 47 -z -" id="CMUSerif-Roman-55"/> - </defs> - <g transform="translate(25.92 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.51875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.51875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.684375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.684375)scale(0.7)" xlink:href="#CMUSerif-Roman-55"/> - </g> - </g> - </g> - <g id="xtick_2"> - <g id="line2d_2"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="88.452" xlink:href="#mf7651b78e9" y="197.316"/> - </g> - </g> - <g id="text_2"> - <!-- $\mathdefault{10^{-5}}$ --> - <defs> - <path d="M 5 16.09375 -Q 5 19.09375 6.59375 20.25 -Q 8.203125 21.40625 9.90625 21.40625 -Q 12.203125 21.40625 13.546875 19.953125 -Q 14.90625 18.5 14.90625 16.5 -Q 14.90625 14.5 13.546875 13.046875 -Q 12.203125 11.59375 9.90625 11.59375 -Q 8.796875 11.59375 8.203125 11.796875 -Q 9.5 7.203125 13.546875 3.890625 -Q 17.59375 0.59375 22.90625 0.59375 -Q 29.59375 0.59375 33.59375 7.09375 -Q 36 11.296875 36 20.796875 -Q 36 29.203125 34.203125 33.40625 -Q 31.40625 39.796875 25.703125 39.796875 -Q 17.59375 39.796875 12.796875 32.796875 -Q 12.203125 31.90625 11.5 31.90625 -Q 10.5 31.90625 10.296875 32.453125 -Q 10.09375 33 10.09375 34.5 -L 10.09375 64.09375 -Q 10.09375 66.5 11.09375 66.5 -Q 11.5 66.5 12.296875 66.203125 -Q 18.59375 63.40625 25.59375 63.40625 -Q 32.796875 63.40625 39.203125 66.296875 -Q 39.703125 66.59375 40 66.59375 -Q 41 66.59375 41 65.5 -Q 41 65.09375 40.203125 63.9375 -Q 39.40625 62.796875 37.703125 61.296875 -Q 36 59.796875 33.796875 58.390625 -Q 31.59375 57 28.390625 56.046875 -Q 25.203125 55.09375 21.703125 55.09375 -Q 17.5 55.09375 13.203125 56.40625 -L 13.203125 36.90625 -Q 18.40625 42 25.90625 42 -Q 33.90625 42 39.40625 35.546875 -Q 44.90625 29.09375 44.90625 20.09375 -Q 44.90625 10.703125 38.40625 4.25 -Q 31.90625 -2.203125 23.09375 -2.203125 -Q 15.09375 -2.203125 10.046875 3.5 -Q 5 9.203125 5 16.09375 -z -" id="CMUSerif-Roman-53"/> - </defs> - <g transform="translate(80.352 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-53"/> - </g> - </g> - </g> - <g id="xtick_3"> - <g id="line2d_3"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="142.884" xlink:href="#mf7651b78e9" y="197.316"/> - </g> - </g> - <g id="text_3"> - <!-- $\mathdefault{10^{-3}}$ --> - <defs> - <path d="M 4.203125 13.5 -Q 4.203125 16.5 5.890625 17.890625 -Q 7.59375 19.296875 9.796875 19.296875 -Q 12.09375 19.296875 13.75 17.796875 -Q 15.40625 16.296875 15.40625 13.703125 -Q 15.40625 10.90625 13.453125 9.34375 -Q 11.5 7.796875 8.796875 8.203125 -Q 11.203125 4.203125 15.59375 2.390625 -Q 20 0.59375 24.09375 0.59375 -Q 28.40625 0.59375 31.90625 4.296875 -Q 35.40625 8 35.40625 17.09375 -Q 35.40625 24.796875 32.40625 29.25 -Q 29.40625 33.703125 23.5 33.703125 -L 19.09375 33.703125 -Q 17.59375 33.703125 17.140625 33.84375 -Q 16.703125 34 16.703125 34.796875 -Q 16.703125 35.796875 18.203125 36 -Q 19.703125 36 22.09375 36.296875 -Q 27.90625 36.5 31 41.5 -Q 33.796875 46.203125 33.796875 52.90625 -Q 33.796875 59 30.890625 61.546875 -Q 28 64.09375 24.203125 64.09375 -Q 20.703125 64.09375 16.84375 62.640625 -Q 13 61.203125 10.90625 57.90625 -Q 17.09375 57.90625 17.09375 52.90625 -Q 17.09375 50.703125 15.6875 49.25 -Q 14.296875 47.796875 12 47.796875 -Q 9.796875 47.796875 8.34375 49.1875 -Q 6.90625 50.59375 6.90625 53 -Q 6.90625 58.703125 12 62.640625 -Q 17.09375 66.59375 24.59375 66.59375 -Q 32 66.59375 37.5 62.6875 -Q 43 58.796875 43 52.796875 -Q 43 46.90625 39.09375 42.046875 -Q 35.203125 37.203125 29 35.203125 -Q 36.59375 33.703125 41.140625 28.546875 -Q 45.703125 23.40625 45.703125 17.09375 -Q 45.703125 9.296875 39.546875 3.546875 -Q 33.40625 -2.203125 24.40625 -2.203125 -Q 16.09375 -2.203125 10.140625 2.296875 -Q 4.203125 6.796875 4.203125 13.5 -z -" id="CMUSerif-Roman-51"/> - </defs> - <g transform="translate(134.784 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-51"/> - </g> - </g> - </g> - <g id="xtick_4"> - <g id="line2d_4"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="197.316" xlink:href="#mf7651b78e9" y="197.316"/> - </g> - </g> - <g id="text_4"> - <!-- $\mathdefault{10^{-1}}$ --> - <g transform="translate(189.216 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-49"/> - </g> - </g> - </g> - <g id="text_5"> - <!-- Metallicity (metal mass fraction) $Z$ [-] --> - <defs> - <path d="M 3.703125 0 -L 3.703125 3.09375 -Q 7.296875 3.09375 9.59375 3.796875 -Q 11.90625 4.5 12.75 5.84375 -Q 13.59375 7.203125 13.796875 8.09375 -Q 14 9 14 10.5 -L 14 60.5 -Q 14 63.40625 12.75 64.296875 -Q 11.5 65.203125 6.09375 65.203125 -L 3.703125 65.203125 -L 3.703125 68.296875 -L 20.59375 68.296875 -Q 22.40625 68.296875 22.90625 68 -Q 23.40625 67.703125 24.09375 66.09375 -L 45.796875 10.09375 -L 67.703125 66.40625 -Q 68.203125 67.796875 68.640625 68.046875 -Q 69.09375 68.296875 71 68.296875 -L 87.90625 68.296875 -L 87.90625 65.203125 -L 85.5 65.203125 -Q 80.09375 65.203125 78.84375 64.296875 -Q 77.59375 63.40625 77.59375 60.5 -L 77.59375 7.796875 -Q 77.59375 4.90625 78.84375 4 -Q 80.09375 3.09375 85.5 3.09375 -L 87.90625 3.09375 -L 87.90625 0 -Q 84.203125 0.296875 73.59375 0.296875 -Q 62.90625 0.296875 59.203125 0 -L 59.203125 3.09375 -L 61.59375 3.09375 -Q 67 3.09375 68.25 4 -Q 69.5 4.90625 69.5 7.796875 -L 69.5 65.203125 -L 69.40625 65.203125 -L 44.796875 1.90625 -Q 44.09375 0 43 0 -Q 41.796875 0 41 2.203125 -L 16.90625 64.40625 -L 16.796875 64.40625 -L 16.796875 10.5 -Q 16.796875 9 17 8.09375 -Q 17.203125 7.203125 18.046875 5.84375 -Q 18.90625 4.5 21.203125 3.796875 -Q 23.5 3.09375 27.09375 3.09375 -L 27.09375 0 -Q 16.59375 0.296875 15.390625 0.296875 -Q 14.203125 0.296875 3.703125 0 -z -" id="CMUSerif-Roman-77"/> - <path d="M 2.796875 22 -Q 2.796875 31.40625 8.84375 38.09375 -Q 14.90625 44.796875 23.59375 44.796875 -Q 32.40625 44.796875 36.953125 39.09375 -Q 41.5 33.40625 41.5 25.203125 -Q 41.5 23.703125 41.09375 23.390625 -Q 40.703125 23.09375 39 23.09375 -L 11.09375 23.09375 -Q 11.09375 12.90625 14.09375 8.09375 -Q 18.296875 1.40625 25.40625 1.40625 -Q 26.40625 1.40625 27.546875 1.59375 -Q 28.703125 1.796875 31.09375 2.640625 -Q 33.5 3.5 35.59375 5.796875 -Q 37.703125 8.09375 38.90625 11.703125 -Q 39.203125 13.09375 40.203125 13.09375 -Q 41.5 13.09375 41.5 11.90625 -Q 41.5 11 40.546875 9.046875 -Q 39.59375 7.09375 37.796875 4.75 -Q 36 2.40625 32.5 0.65625 -Q 29 -1.09375 24.796875 -1.09375 -Q 16 -1.09375 9.390625 5.546875 -Q 2.796875 12.203125 2.796875 22 -z -M 11.203125 25.203125 -L 34.90625 25.203125 -Q 34.90625 27.296875 34.546875 29.640625 -Q 34.203125 32 33.140625 35.25 -Q 32.09375 38.5 29.640625 40.546875 -Q 27.203125 42.59375 23.59375 42.59375 -Q 22 42.59375 20.25 41.890625 -Q 18.5 41.203125 16.390625 39.546875 -Q 14.296875 37.90625 12.84375 34.15625 -Q 11.40625 30.40625 11.203125 25.203125 -z -" id="CMUSerif-Roman-101"/> - <path d="M 1.90625 40 -L 1.90625 42.203125 -Q 6.5 42.40625 9.546875 45.65625 -Q 12.59375 48.90625 13.640625 52.90625 -Q 14.703125 56.90625 14.796875 61.5 -L 17.296875 61.5 -L 17.296875 43.09375 -L 31.59375 43.09375 -L 31.59375 40 -L 17.296875 40 -L 17.296875 12.203125 -Q 17.296875 1.40625 24 1.40625 -Q 26.90625 1.40625 28.796875 4.34375 -Q 30.703125 7.296875 30.703125 12.59375 -L 30.703125 18.09375 -L 33.203125 18.09375 -L 33.203125 12.40625 -Q 33.203125 7 30.703125 2.953125 -Q 28.203125 -1.09375 23.296875 -1.09375 -Q 21.5 -1.09375 19.703125 -0.640625 -Q 17.90625 -0.203125 15.59375 1 -Q 13.296875 2.203125 11.84375 5.140625 -Q 10.40625 8.09375 10.40625 12.40625 -L 10.40625 40 -z -" id="CMUSerif-Roman-116"/> - <path d="M 4.203125 9.5 -Q 4.203125 18 14.203125 22.5 -Q 20.203125 25.40625 32.59375 26.09375 -L 32.59375 29.796875 -Q 32.59375 36 29.34375 39.296875 -Q 26.09375 42.59375 22 42.59375 -Q 14.703125 42.59375 11.203125 38 -Q 14.203125 37.90625 15.25 36.40625 -Q 16.296875 34.90625 16.296875 33.40625 -Q 16.296875 31.40625 15.046875 30.09375 -Q 13.796875 28.796875 11.703125 28.796875 -Q 9.703125 28.796875 8.390625 30.046875 -Q 7.09375 31.296875 7.09375 33.5 -Q 7.09375 38.40625 11.5 41.59375 -Q 15.90625 44.796875 22.203125 44.796875 -Q 30.40625 44.796875 35.90625 39.296875 -Q 37.59375 37.59375 38.4375 35.390625 -Q 39.296875 33.203125 39.390625 31.75 -Q 39.5 30.296875 39.5 27.5 -L 39.5 7.5 -Q 39.5 6.90625 39.703125 5.953125 -Q 39.90625 5 40.796875 3.75 -Q 41.703125 2.5 43.203125 2.5 -Q 46.796875 2.5 46.796875 8.90625 -L 46.796875 14.5 -L 49.296875 14.5 -L 49.296875 8.90625 -Q 49.296875 3.59375 46.5 1.5 -Q 43.703125 -0.59375 41.09375 -0.59375 -Q 37.796875 -0.59375 35.6875 1.84375 -Q 33.59375 4.296875 33.296875 7.59375 -Q 31.796875 3.796875 28.34375 1.34375 -Q 24.90625 -1.09375 20.203125 -1.09375 -Q 16.59375 -1.09375 13.1875 -0.1875 -Q 9.796875 0.703125 7 3.203125 -Q 4.203125 5.703125 4.203125 9.5 -z -M 11.90625 9.59375 -Q 11.90625 5.90625 14.546875 3.5 -Q 17.203125 1.09375 20.90625 1.09375 -Q 25.09375 1.09375 28.84375 4.34375 -Q 32.59375 7.59375 32.59375 14 -L 32.59375 24 -Q 21.5 23.59375 16.703125 19.1875 -Q 11.90625 14.796875 11.90625 9.59375 -z -" id="CMUSerif-Roman-97"/> - <path d="M 3.296875 0 -L 3.296875 3.09375 -Q 8.59375 3.09375 9.84375 3.75 -Q 11.09375 4.40625 11.09375 7.59375 -L 11.09375 59.59375 -Q 11.09375 63.296875 9.796875 64.25 -Q 8.5 65.203125 3.296875 65.203125 -L 3.296875 68.296875 -L 17.703125 69.40625 -L 17.703125 7.59375 -Q 17.703125 4.40625 18.953125 3.75 -Q 20.203125 3.09375 25.5 3.09375 -L 25.5 0 -Q 24.296875 0 21.75 0.09375 -Q 19.203125 0.203125 17.34375 0.25 -Q 15.5 0.296875 14.40625 0.296875 -Q 13.203125 0.296875 3.296875 0 -z -" id="CMUSerif-Roman-108"/> - <path d="M 3.296875 0 -L 3.296875 3.09375 -Q 8.59375 3.09375 9.84375 3.75 -Q 11.09375 4.40625 11.09375 7.59375 -L 11.09375 34.5 -Q 11.09375 38.203125 9.84375 39.09375 -Q 8.59375 40 3.703125 40 -L 3.703125 43.09375 -L 17.703125 44.203125 -L 17.703125 7.5 -Q 17.703125 4.5 18.75 3.796875 -Q 19.796875 3.09375 24.703125 3.09375 -L 24.703125 0 -Q 14.5 0.296875 14.296875 0.296875 -Q 12.90625 0.296875 3.296875 0 -z -M 7.5 61.59375 -Q 7.5 63.59375 9.046875 65.25 -Q 10.59375 66.90625 12.796875 66.90625 -Q 15 66.90625 16.546875 65.40625 -Q 18.09375 63.90625 18.09375 61.59375 -Q 18.09375 59.296875 16.546875 57.796875 -Q 15 56.296875 12.796875 56.296875 -Q 10.5 56.296875 9 57.890625 -Q 7.5 59.5 7.5 61.59375 -z -" id="CMUSerif-Roman-105"/> - <path d="M 16.09375 -1.09375 -Q 3.40625 12.09375 3.40625 21.59375 -Q 3.40625 31.09375 9.65625 37.9375 -Q 15.90625 44.796875 25.09375 44.796875 -Q 31.203125 44.796875 35.796875 41.890625 -Q 40.40625 39 40.40625 34.09375 -Q 40.40625 31.90625 39.09375 30.65625 -Q 37.796875 29.40625 35.796875 29.40625 -Q 33.703125 29.40625 32.453125 30.703125 -Q 31.203125 32 31.203125 34 -Q 31.203125 34.90625 31.5 35.75 -Q 31.796875 36.59375 32.890625 37.546875 -Q 34 38.5 35.90625 38.59375 -Q 32.296875 42.296875 25.203125 42.296875 -Q 20.09375 42.296875 15.890625 37.5 -Q 11.703125 32.703125 11.703125 21.796875 -Q 11.703125 16.09375 13.09375 11.890625 -Q 14.5 7.703125 16.796875 5.546875 -Q 19.09375 3.40625 21.34375 2.40625 -Q 23.59375 1.40625 25.796875 1.40625 -Q 35.59375 1.40625 38.90625 11.90625 -Q 39.203125 12.90625 40.203125 12.90625 -Q 41.5 12.90625 41.5 11.90625 -Q 41.5 11.40625 41.09375 10.15625 -Q 40.703125 8.90625 39.5 6.90625 -Q 38.296875 4.90625 36.546875 3.15625 -Q 34.796875 1.40625 31.75 0.15625 -Q 28.703125 -1.09375 24.90625 -1.09375 -Q 16.09375 -1.09375 3.40625 12.09375 -z -" id="CMUSerif-Roman-99"/> - <path d="M 1.90625 -12.40625 -Q 1.90625 -10.296875 3.15625 -9.1875 -Q 4.40625 -8.09375 6.09375 -8.09375 -Q 7.90625 -8.09375 9.09375 -9.25 -Q 10.296875 -10.40625 10.296875 -12.296875 -Q 10.296875 -16 6.40625 -16.5 -Q 8.296875 -18.296875 11.09375 -18.296875 -Q 14.09375 -18.296875 16.5 -16.09375 -Q 18.90625 -13.90625 19.953125 -11.796875 -Q 21 -9.703125 22.5 -5.90625 -Q 23.90625 -2.90625 25 0 -L 10 36.5 -Q 9 38.90625 7.5 39.453125 -Q 6 40 1.90625 40 -L 1.90625 43.09375 -Q 6.40625 42.796875 11.59375 42.796875 -Q 14.703125 42.796875 22.5 43.09375 -L 22.5 40 -Q 16.90625 40 16.90625 37.40625 -Q 16.90625 37.09375 17.5 35.59375 -L 28.59375 8.703125 -L 38.703125 33.296875 -Q 39.296875 34.703125 39.296875 35.703125 -Q 39.296875 39.796875 34.59375 40 -L 34.59375 43.09375 -Q 41.203125 42.796875 43.296875 42.796875 -Q 47.40625 42.796875 50.796875 43.09375 -L 50.796875 40 -Q 44.09375 40 41.5 33.59375 -L 23.90625 -9.09375 -Q 19.09375 -20.5 11.09375 -20.5 -Q 7.296875 -20.5 4.59375 -18.140625 -Q 1.90625 -15.796875 1.90625 -12.40625 -z -" id="CMUSerif-Roman-121"/> - <path id="CMUSerif-Roman-32"/> - <path d="M 9.90625 25 -Q 9.90625 41.90625 16.203125 55.5 -Q 18.90625 61.203125 22.65625 66 -Q 26.40625 70.796875 28.90625 72.890625 -Q 31.40625 75 32.09375 75 -Q 33.09375 75 33.09375 74 -Q 33.09375 73.5 31.796875 72.296875 -Q 15.703125 55.90625 15.703125 25 -Q 15.703125 -6 31.40625 -21.796875 -Q 33.09375 -23.5 33.09375 -24 -Q 33.09375 -25 32.09375 -25 -Q 31.40625 -25 29 -23 -Q 26.59375 -21 22.890625 -16.390625 -Q 19.203125 -11.796875 16.5 -6.203125 -Q 9.90625 7.40625 9.90625 25 -z -" id="CMUSerif-Roman-40"/> - <path d="M 3.203125 0 -L 3.203125 3.09375 -Q 8.5 3.09375 9.75 3.75 -Q 11 4.40625 11 7.59375 -L 11 34.40625 -Q 11 38.09375 9.703125 39.046875 -Q 8.40625 40 3.203125 40 -L 3.203125 43.09375 -L 17.296875 44.203125 -L 17.296875 33.703125 -Q 22 44.203125 32.09375 44.203125 -Q 43.796875 44.203125 45.40625 34.40625 -Q 47.09375 38.203125 50.6875 41.203125 -Q 54.296875 44.203125 59.90625 44.203125 -Q 67.40625 44.203125 70.40625 40.5 -Q 72.59375 38 73.046875 35.203125 -Q 73.5 32.40625 73.5 25.203125 -L 73.5 6.09375 -Q 73.59375 4 75.1875 3.546875 -Q 76.796875 3.09375 81.296875 3.09375 -L 81.296875 0 -Q 71.09375 0.296875 70.09375 0.296875 -Q 69.296875 0.296875 58.796875 0 -L 58.796875 3.09375 -Q 64.09375 3.09375 65.34375 3.75 -Q 66.59375 4.40625 66.59375 7.59375 -L 66.59375 30.90625 -Q 66.59375 36 65.046875 39 -Q 63.5 42 59.203125 42 -Q 54 42 49.84375 37.640625 -Q 45.703125 33.296875 45.703125 26 -L 45.703125 7.59375 -Q 45.703125 4.40625 46.953125 3.75 -Q 48.203125 3.09375 53.5 3.09375 -L 53.5 0 -Q 43.296875 0.296875 42.296875 0.296875 -Q 41.5 0.296875 31 0 -L 31 3.09375 -Q 36.296875 3.09375 37.546875 3.75 -Q 38.796875 4.40625 38.796875 7.59375 -L 38.796875 30.90625 -Q 38.796875 36 37.25 39 -Q 35.703125 42 31.40625 42 -Q 26.203125 42 22.046875 37.640625 -Q 17.90625 33.296875 17.90625 26 -L 17.90625 7.59375 -Q 17.90625 4.40625 19.15625 3.75 -Q 20.40625 3.09375 25.703125 3.09375 -L 25.703125 0 -Q 15.5 0.296875 14.5 0.296875 -Q 13.703125 0.296875 3.203125 0 -z -" id="CMUSerif-Roman-109"/> - <path d="M 3.296875 1.296875 -L 3.296875 14.5 -Q 3.296875 15.59375 3.34375 16 -Q 3.40625 16.40625 3.703125 16.703125 -Q 4 17 4.59375 17 -Q 5.296875 17 5.546875 16.703125 -Q 5.796875 16.40625 6 15.296875 -Q 7.5 8.40625 10.75 4.75 -Q 14 1.09375 19.90625 1.09375 -Q 25.5 1.09375 28.34375 3.59375 -Q 31.203125 6.09375 31.203125 10.203125 -Q 31.203125 17.5 20.796875 19.40625 -Q 14.796875 20.59375 12.296875 21.390625 -Q 9.796875 22.203125 7.59375 24 -Q 3.296875 27.5 3.296875 32.5 -Q 3.296875 37.5 7.09375 41.140625 -Q 10.90625 44.796875 19.296875 44.796875 -Q 24.90625 44.796875 28.703125 42 -Q 29.796875 42.90625 30.40625 43.59375 -Q 31.703125 44.796875 32.40625 44.796875 -Q 33.203125 44.796875 33.34375 44.296875 -Q 33.5 43.796875 33.5 42.40625 -L 33.5 32.296875 -Q 33.5 31.203125 33.453125 30.796875 -Q 33.40625 30.40625 33.09375 30.15625 -Q 32.796875 29.90625 32.203125 29.90625 -Q 31.09375 29.90625 31 30.796875 -Q 30.203125 42.90625 19.296875 42.90625 -Q 13.40625 42.90625 10.75 40.65625 -Q 8.09375 38.40625 8.09375 35.296875 -Q 8.09375 33.59375 8.890625 32.296875 -Q 9.703125 31 10.75 30.25 -Q 11.796875 29.5 13.75 28.796875 -Q 15.703125 28.09375 16.890625 27.84375 -Q 18.09375 27.59375 20.40625 27.09375 -Q 28.40625 25.59375 31.796875 22.296875 -Q 36 18.09375 36 12.796875 -Q 36 6.90625 32 2.90625 -Q 28 -1.09375 19.90625 -1.09375 -Q 13.40625 -1.09375 8.90625 3.203125 -Q 8.296875 2.59375 7.84375 2.09375 -Q 7.40625 1.59375 7.25 1.390625 -Q 7.09375 1.203125 7.046875 1.09375 -Q 7 1 6.90625 0.90625 -Q 4.90625 -1.09375 4.40625 -1.09375 -Q 3.59375 -1.09375 3.4375 -0.59375 -Q 3.296875 -0.09375 3.296875 1.296875 -z -" id="CMUSerif-Roman-115"/> - <path d="M 3.296875 40 -L 3.296875 43.09375 -L 11.203125 43.09375 -L 11.203125 54.59375 -Q 11.203125 62 16 66.25 -Q 20.796875 70.5 26.703125 70.5 -Q 30.59375 70.5 33.140625 68.390625 -Q 35.703125 66.296875 35.703125 63.5 -Q 35.703125 61.59375 34.546875 60.34375 -Q 33.40625 59.09375 31.296875 59.09375 -Q 29.296875 59.09375 28.140625 60.34375 -Q 27 61.59375 27 63.40625 -Q 27 66.59375 30 67.59375 -Q 28.5 68.296875 26.703125 68.296875 -Q 23.09375 68.296875 20.296875 64.59375 -Q 17.5 60.90625 17.5 54.703125 -L 17.5 43.09375 -L 29.203125 43.09375 -L 29.203125 40 -L 17.796875 40 -L 17.796875 7.796875 -Q 17.796875 4.90625 19 4 -Q 20.203125 3.09375 25.40625 3.09375 -L 27.5 3.09375 -L 27.5 0 -Q 23.5 0.296875 14.796875 0.296875 -Q 13.59375 0.296875 11.6875 0.25 -Q 9.796875 0.203125 7.296875 0.09375 -Q 4.796875 0 3.40625 0 -L 3.40625 3.09375 -Q 8.703125 3.09375 9.953125 3.75 -Q 11.203125 4.40625 11.203125 7.59375 -L 11.203125 40 -z -" id="CMUSerif-Roman-102"/> - <path d="M 2.796875 0 -L 2.796875 3.09375 -Q 8.09375 3.09375 9.34375 3.75 -Q 10.59375 4.40625 10.59375 7.59375 -L 10.59375 34.40625 -Q 10.59375 38.09375 9.296875 39.046875 -Q 8 40 2.796875 40 -L 2.796875 43.09375 -L 16.703125 44.203125 -L 16.703125 33.203125 -Q 18.09375 37.5 21.09375 40.84375 -Q 24.09375 44.203125 29 44.203125 -Q 32.203125 44.203125 34.296875 42.390625 -Q 36.40625 40.59375 36.40625 38.09375 -Q 36.40625 35.90625 35.046875 34.796875 -Q 33.703125 33.703125 32.09375 33.703125 -Q 30.296875 33.703125 29.046875 34.84375 -Q 27.796875 36 27.796875 38 -Q 27.796875 39.203125 28.34375 40.140625 -Q 28.90625 41.09375 29.34375 41.4375 -Q 29.796875 41.796875 30.09375 41.90625 -Q 29.90625 42 29 42 -Q 23.5 42 20.34375 36.5 -Q 17.203125 31 17.203125 23.203125 -L 17.203125 7.796875 -Q 17.203125 4.90625 18.390625 4 -Q 19.59375 3.09375 24.796875 3.09375 -L 26.90625 3.09375 -L 26.90625 0 -Q 22.90625 0.296875 14.203125 0.296875 -Q 13 0.296875 11.09375 0.25 -Q 9.203125 0.203125 6.703125 0.09375 -Q 4.203125 0 2.796875 0 -z -" id="CMUSerif-Roman-114"/> - <path d="M 16 -1.09375 -Q 2.796875 11.90625 2.796875 21.40625 -Q 2.796875 30.90625 9.25 37.84375 -Q 15.703125 44.796875 25 44.796875 -Q 34.09375 44.796875 40.59375 37.890625 -Q 47.09375 31 47.09375 21.40625 -Q 47.09375 12 40.546875 5.453125 -Q 34 -1.09375 24.90625 -1.09375 -Q 16 -1.09375 2.796875 11.90625 -z -M 11.09375 22.203125 -Q 11.09375 12.5 13.59375 8.09375 -Q 17.5 1.40625 25 1.40625 -Q 28.703125 1.40625 31.796875 3.40625 -Q 34.90625 5.40625 36.59375 8.796875 -Q 38.796875 13.203125 38.796875 22.203125 -Q 38.796875 31.796875 36.203125 36.09375 -Q 32.296875 42.59375 24.90625 42.59375 -Q 21.703125 42.59375 18.546875 40.890625 -Q 15.40625 39.203125 13.5 35.90625 -Q 11.09375 31.5 11.09375 22.203125 -z -" id="CMUSerif-Roman-111"/> - <path d="M 3.203125 0 -L 3.203125 3.09375 -Q 8.5 3.09375 9.75 3.75 -Q 11 4.40625 11 7.59375 -L 11 34.40625 -Q 11 38.09375 9.703125 39.046875 -Q 8.40625 40 3.203125 40 -L 3.203125 43.09375 -L 17.296875 44.203125 -L 17.296875 33.703125 -Q 22 44.203125 32.09375 44.203125 -Q 39.59375 44.203125 42.59375 40.5 -Q 44.796875 38 45.25 35.203125 -Q 45.703125 32.40625 45.703125 25.203125 -L 45.703125 6.09375 -Q 45.796875 4 47.390625 3.546875 -Q 49 3.09375 53.5 3.09375 -L 53.5 0 -Q 43.296875 0.296875 42.296875 0.296875 -Q 41.5 0.296875 31 0 -L 31 3.09375 -Q 36.296875 3.09375 37.546875 3.75 -Q 38.796875 4.40625 38.796875 7.59375 -L 38.796875 30.90625 -Q 38.796875 36 37.25 39 -Q 35.703125 42 31.40625 42 -Q 26.203125 42 22.046875 37.640625 -Q 17.90625 33.296875 17.90625 26 -L 17.90625 7.59375 -Q 17.90625 4.40625 19.15625 3.75 -Q 20.40625 3.09375 25.703125 3.09375 -L 25.703125 0 -Q 15.5 0.296875 14.5 0.296875 -Q 13.703125 0.296875 3.203125 0 -z -" id="CMUSerif-Roman-110"/> - <path d="M 5.703125 -24 -Q 5.703125 -23.5 7 -22.296875 -Q 23.09375 -5.90625 23.09375 25 -Q 23.09375 56 7.59375 71.703125 -Q 5.703125 73.5 5.703125 74 -Q 5.703125 75 6.703125 75 -Q 7.40625 75 9.796875 73 -Q 12.203125 71 15.890625 66.390625 -Q 19.59375 61.796875 22.296875 56.203125 -Q 28.90625 42.59375 28.90625 25 -Q 28.90625 8.09375 22.59375 -5.5 -Q 19.90625 -11.203125 16.15625 -16 -Q 12.40625 -20.796875 9.90625 -22.890625 -Q 7.40625 -25 6.703125 -25 -Q 5.703125 -25 5.703125 -24 -z -" id="CMUSerif-Roman-41"/> - <path d="M 5.8125 0.78125 -Q 5.8125 2.25 6.5 3.078125 -L 61.53125 64.796875 -L 46.78125 64.796875 -Q 39.453125 64.796875 34.640625 63.0625 -Q 29.828125 61.328125 26.671875 57.296875 -Q 23.53125 53.265625 21.390625 46.296875 -Q 21.1875 45.40625 20.40625 45.40625 -L 19.484375 45.40625 -Q 18.5 45.40625 18.5 46.6875 -L 24.8125 67.390625 -Q 25 68.3125 25.78125 68.3125 -L 71.390625 68.3125 -Q 72.21875 68.3125 72.21875 67.484375 -Q 72.21875 66.015625 71.578125 65.375 -L 16.796875 3.8125 -L 31.984375 3.8125 -Q 40.828125 3.8125 45.875 5.984375 -Q 50.921875 8.15625 53.875 12.84375 -Q 56.84375 17.53125 59.625 26.21875 -Q 59.71875 26.5625 59.984375 26.828125 -Q 60.25 27.09375 60.59375 27.09375 -L 61.53125 27.09375 -Q 62.5 27.09375 62.5 25.78125 -L 54.59375 0.875 -Q 54.15625 0 53.609375 0 -L 6.6875 0 -Q 5.8125 0 5.8125 0.78125 -z -" id="Cmmi10-90"/> - <path d="M 10.40625 -25 -L 10.40625 75 -L 25.5 75 -L 25.5 72.703125 -L 17.09375 72.703125 -L 17.09375 -22.703125 -L 25.5 -22.703125 -L 25.5 -25 -z -" id="CMUSerif-Roman-91"/> - <path d="M 2.09375 -22.703125 -L 10.5 -22.703125 -L 10.5 72.703125 -L 2.09375 72.703125 -L 2.09375 75 -L 17.203125 75 -L 17.203125 -25 -L 2.09375 -25 -z -" id="CMUSerif-Roman-93"/> - </defs> - <g transform="translate(45.976 222.69725)scale(0.1 -0.1)"> - <use xlink:href="#CMUSerif-Roman-77"/> - <use transform="translate(91.599991 0)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(135.999985 0)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(174.799973 0)" xlink:href="#CMUSerif-Roman-97"/> - <use transform="translate(224.799957 0)" xlink:href="#CMUSerif-Roman-108"/> - <use transform="translate(252.499954 0)" xlink:href="#CMUSerif-Roman-108"/> - <use transform="translate(280.199951 0)" xlink:href="#CMUSerif-Roman-105"/> - <use transform="translate(307.899948 0)" xlink:href="#CMUSerif-Roman-99"/> - <use transform="translate(352.299942 0)" xlink:href="#CMUSerif-Roman-105"/> - <use transform="translate(379.999939 0)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(418.799927 0)" xlink:href="#CMUSerif-Roman-121"/> - <use transform="translate(471.499924 0)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(504.799911 0)" xlink:href="#CMUSerif-Roman-40"/> - <use transform="translate(543.599899 0)" xlink:href="#CMUSerif-Roman-109"/> - <use transform="translate(626.899887 0)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(671.299881 0)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(710.099869 0)" xlink:href="#CMUSerif-Roman-97"/> - <use transform="translate(760.099854 0)" xlink:href="#CMUSerif-Roman-108"/> - <use transform="translate(787.79985 0)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(821.099838 0)" xlink:href="#CMUSerif-Roman-109"/> - <use transform="translate(904.399826 0)" xlink:href="#CMUSerif-Roman-97"/> - <use transform="translate(954.399811 0)" xlink:href="#CMUSerif-Roman-115"/> - <use transform="translate(993.799805 0)" xlink:href="#CMUSerif-Roman-115"/> - <use transform="translate(1033.199799 0)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(1066.499786 0)" xlink:href="#CMUSerif-Roman-102"/> - <use transform="translate(1096.999771 0)" xlink:href="#CMUSerif-Roman-114"/> - <use transform="translate(1136.099762 0)" xlink:href="#CMUSerif-Roman-97"/> - <use transform="translate(1186.099747 0)" xlink:href="#CMUSerif-Roman-99"/> - <use transform="translate(1230.499741 0)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(1269.299728 0)" xlink:href="#CMUSerif-Roman-105"/> - <use transform="translate(1296.999725 0)" xlink:href="#CMUSerif-Roman-111"/> - <use transform="translate(1346.99971 0)" xlink:href="#CMUSerif-Roman-110"/> - <use transform="translate(1402.499695 0)" xlink:href="#CMUSerif-Roman-41"/> - <use transform="translate(1441.299683 0)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(1474.59967 0)" xlink:href="#Cmmi10-90"/> - <use transform="translate(1542.812561 0)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(1576.112549 0)" xlink:href="#CMUSerif-Roman-91"/> - <use transform="translate(1603.912537 0)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(1637.212524 0)" xlink:href="#CMUSerif-Roman-93"/> - </g> - </g> - </g> - <g id="matplotlib.axis_2"> - <g id="ytick_1"> - <g id="line2d_5"> - <defs> - <path d="M 0 0 -L -3.5 0 -" id="m7bcf80c517" style="stroke:#000000;stroke-width:0.8;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="172.059569"/> - </g> - </g> - <g id="text_6"> - <!-- $\mathdefault{10^{-3}}$ --> - <g transform="translate(10.82 175.529881)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-51"/> - </g> - </g> - </g> - <g id="ytick_2"> - <g id="line2d_6"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="135.925784"/> - </g> - </g> - <g id="text_7"> - <!-- $\mathdefault{10^{-2}}$ --> - <defs> - <path d="M 5 0 -Q 5 1.796875 5.140625 2.34375 -Q 5.296875 2.90625 6.09375 3.703125 -L 25.296875 25.09375 -Q 35.796875 36.90625 35.796875 47.203125 -Q 35.796875 53.90625 32.296875 58.703125 -Q 28.796875 63.5 22.40625 63.5 -Q 18 63.5 14.296875 60.796875 -Q 10.59375 58.09375 8.90625 53.296875 -Q 9.203125 53.40625 10.203125 53.40625 -Q 12.703125 53.40625 14.09375 51.84375 -Q 15.5 50.296875 15.5 48.203125 -Q 15.5 45.5 13.75 44.203125 -Q 12 42.90625 10.296875 42.90625 -Q 9.59375 42.90625 8.6875 43.046875 -Q 7.796875 43.203125 6.390625 44.59375 -Q 5 46 5 48.5 -Q 5 55.5 10.296875 61.046875 -Q 15.59375 66.59375 23.703125 66.59375 -Q 32.90625 66.59375 38.90625 61.140625 -Q 44.90625 55.703125 44.90625 47.203125 -Q 44.90625 44.203125 44 41.5 -Q 43.09375 38.796875 41.890625 36.6875 -Q 40.703125 34.59375 37.5 31.25 -Q 34.296875 27.90625 31.6875 25.5 -Q 29.09375 23.09375 23.296875 18 -L 12.703125 7.703125 -L 30.703125 7.703125 -Q 39.5 7.703125 40.203125 8.5 -Q 41.203125 9.90625 42.40625 17.40625 -L 44.90625 17.40625 -L 42.09375 0 -z -" id="CMUSerif-Roman-50"/> - </defs> - <g transform="translate(10.82 139.396097)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/> - </g> - </g> - </g> - <g id="ytick_3"> - <g id="line2d_7"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="99.792"/> - </g> - </g> - <g id="text_8"> - <!-- $\mathdefault{10^{-1}}$ --> - <g transform="translate(10.82 103.262312)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-49"/> - </g> - </g> - </g> - <g id="ytick_4"> - <g id="line2d_8"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="63.658216"/> - </g> - </g> - <g id="text_9"> - <!-- $\mathdefault{10^{0}}$ --> - <g transform="translate(13.22 67.128528)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-48"/> - </g> - </g> - </g> - <g id="ytick_5"> - <g id="line2d_9"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="27.524431"/> - </g> - </g> - <g id="text_10"> - <!-- $\mathdefault{10^{1}}$ --> - <g transform="translate(13.22 30.994744)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-49"/> - </g> - </g> - </g> - <g id="ytick_6"> - <g id="line2d_10"> - <defs> - <path d="M 0 0 -L -2 0 -" id="mb9ef9ffb1f" style="stroke:#000000;stroke-width:0.6;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="197.316"/> - </g> - </g> - </g> - <g id="ytick_7"> - <g id="line2d_11"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="190.953156"/> - </g> - </g> - </g> - <g id="ytick_8"> - <g id="line2d_12"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="186.438647"/> - </g> - </g> - </g> - <g id="ytick_9"> - <g id="line2d_13"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="182.936922"/> - </g> - </g> - </g> - <g id="ytick_10"> - <g id="line2d_14"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="180.075803"/> - </g> - </g> - </g> - <g id="ytick_11"> - <g id="line2d_15"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="177.656763"/> - </g> - </g> - </g> - <g id="ytick_12"> - <g id="line2d_16"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="175.561294"/> - </g> - </g> - </g> - <g id="ytick_13"> - <g id="line2d_17"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="173.71296"/> - </g> - </g> - </g> - <g id="ytick_14"> - <g id="line2d_18"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="161.182216"/> - </g> - </g> - </g> - <g id="ytick_15"> - <g id="line2d_19"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="154.819372"/> - </g> - </g> - </g> - <g id="ytick_16"> - <g id="line2d_20"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="150.304863"/> - </g> - </g> - </g> - <g id="ytick_17"> - <g id="line2d_21"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="146.803137"/> - </g> - </g> - </g> - <g id="ytick_18"> - <g id="line2d_22"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="143.942019"/> - </g> - </g> - </g> - <g id="ytick_19"> - <g id="line2d_23"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="141.522978"/> - </g> - </g> - </g> - <g id="ytick_20"> - <g id="line2d_24"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="139.42751"/> - </g> - </g> - </g> - <g id="ytick_21"> - <g id="line2d_25"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="137.579176"/> - </g> - </g> - </g> - <g id="ytick_22"> - <g id="line2d_26"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="125.048431"/> - </g> - </g> - </g> - <g id="ytick_23"> - <g id="line2d_27"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="118.685588"/> - </g> - </g> - </g> - <g id="ytick_24"> - <g id="line2d_28"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="114.171078"/> - </g> - </g> - </g> - <g id="ytick_25"> - <g id="line2d_29"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="110.669353"/> - </g> - </g> - </g> - <g id="ytick_26"> - <g id="line2d_30"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="107.808235"/> - </g> - </g> - </g> - <g id="ytick_27"> - <g id="line2d_31"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="105.389194"/> - </g> - </g> - </g> - <g id="ytick_28"> - <g id="line2d_32"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="103.293726"/> - </g> - </g> - </g> - <g id="ytick_29"> - <g id="line2d_33"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="101.445391"/> - </g> - </g> - </g> - <g id="ytick_30"> - <g id="line2d_34"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="88.914647"/> - </g> - </g> - </g> - <g id="ytick_31"> - <g id="line2d_35"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="82.551803"/> - </g> - </g> - </g> - <g id="ytick_32"> - <g id="line2d_36"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="78.037294"/> - </g> - </g> - </g> - <g id="ytick_33"> - <g id="line2d_37"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="74.535569"/> - </g> - </g> - </g> - <g id="ytick_34"> - <g id="line2d_38"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="71.674451"/> - </g> - </g> - </g> - <g id="ytick_35"> - <g id="line2d_39"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="69.25541"/> - </g> - </g> - </g> - <g id="ytick_36"> - <g id="line2d_40"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="67.159941"/> - </g> - </g> - </g> - <g id="ytick_37"> - <g id="line2d_41"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="65.311607"/> - </g> - </g> - </g> - <g id="ytick_38"> - <g id="line2d_42"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="52.780863"/> - </g> - </g> - </g> - <g id="ytick_39"> - <g id="line2d_43"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="46.418019"/> - </g> - </g> - </g> - <g id="ytick_40"> - <g id="line2d_44"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="41.90351"/> - </g> - </g> - </g> - <g id="ytick_41"> - <g id="line2d_45"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="38.401784"/> - </g> - </g> - </g> - <g id="ytick_42"> - <g id="line2d_46"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="35.540666"/> - </g> - </g> - </g> - <g id="ytick_43"> - <g id="line2d_47"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="33.121625"/> - </g> - </g> - </g> - <g id="ytick_44"> - <g id="line2d_48"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="31.026157"/> - </g> - </g> - </g> - <g id="ytick_45"> - <g id="line2d_49"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="29.177823"/> - </g> - </g> - </g> - <g id="ytick_46"> - <g id="line2d_50"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="16.647078"/> - </g> - </g> - </g> - <g id="ytick_47"> - <g id="line2d_51"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="10.284235"/> - </g> - </g> - </g> - <g id="ytick_48"> - <g id="line2d_52"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="5.769726"/> - </g> - </g> - </g> - <g id="ytick_49"> - <g id="line2d_53"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="2.268"/> - </g> - </g> - </g> - <g id="text_11"> - <!-- SF threshold number density $n_{\rm H, thresh}$ [cm$^{-3}$] --> - <defs> - <path d="M 5.59375 0.203125 -L 5.59375 20.203125 -Q 5.59375 21.296875 5.640625 21.6875 -Q 5.703125 22.09375 6 22.390625 -Q 6.296875 22.703125 6.90625 22.703125 -Q 8 22.703125 8.09375 21.703125 -Q 8.40625 11.796875 14.296875 6.296875 -Q 20.09375 0.90625 30.09375 0.90625 -Q 36.09375 0.90625 39.796875 5.203125 -Q 43.5 9.5 43.5 15.203125 -Q 43.5 20.59375 40.40625 24.40625 -Q 39.09375 26.09375 37.4375 27.1875 -Q 35.796875 28.296875 34.796875 28.640625 -Q 33.796875 29 31.90625 29.5 -Q 19.5 32.5 18.703125 32.796875 -Q 12.796875 34.796875 9.1875 39.84375 -Q 5.59375 44.90625 5.59375 51.09375 -Q 5.59375 59.09375 11.25 64.796875 -Q 16.90625 70.5 25.09375 70.5 -Q 29.203125 70.5 32.640625 69.203125 -Q 36.09375 67.90625 37.5 66.75 -Q 38.90625 65.59375 41.09375 63.5 -L 44.59375 69.203125 -Q 45.40625 70.5 46.09375 70.5 -Q 46.90625 70.5 47.046875 70.046875 -Q 47.203125 69.59375 47.203125 68.09375 -L 47.203125 48 -Q 47.203125 46.90625 47.140625 46.5 -Q 47.09375 46.09375 46.796875 45.84375 -Q 46.5 45.59375 45.90625 45.59375 -Q 44.90625 45.59375 44.703125 46.796875 -Q 41.90625 67.703125 25.203125 67.703125 -Q 19.5 67.703125 15.75 63.84375 -Q 12 60 12 54.59375 -Q 12 50.203125 14.75 46.59375 -Q 17.5 43 22.09375 41.90625 -L 34.90625 38.796875 -Q 41.296875 37.296875 45.59375 31.640625 -Q 49.90625 26 49.90625 18.59375 -Q 49.90625 10.09375 44.34375 3.9375 -Q 38.796875 -2.203125 30.203125 -2.203125 -Q 24 -2.203125 19.203125 -0.140625 -Q 14.40625 1.90625 11.796875 4.796875 -Q 9.796875 1.5 8.5 -0.40625 -L 8.203125 -0.90625 -Q 7.40625 -2.203125 6.703125 -2.203125 -Q 5.90625 -2.203125 5.75 -1.703125 -Q 5.59375 -1.203125 5.59375 0.203125 -z -" id="CMUSerif-Roman-83"/> - <path d="M 3.296875 0 -L 3.296875 3.09375 -L 5.703125 3.09375 -Q 11.09375 3.09375 12.34375 4 -Q 13.59375 4.90625 13.59375 7.796875 -L 13.59375 60.203125 -Q 13.59375 63.09375 12.34375 64 -Q 11.09375 64.90625 5.703125 64.90625 -L 3.296875 64.90625 -L 3.296875 68 -L 58.203125 68 -L 61 45.5 -L 58.5 45.5 -Q 57.796875 51.203125 56.890625 54.453125 -Q 56 57.703125 53.953125 60.296875 -Q 51.90625 62.90625 48.453125 63.90625 -Q 45 64.90625 39.40625 64.90625 -L 27.40625 64.90625 -Q 24 64.90625 23.25 64.203125 -Q 22.5 63.5 22.5 60.90625 -L 22.5 35.59375 -L 31.09375 35.59375 -Q 37.90625 35.59375 39.796875 38.046875 -Q 41.703125 40.5 41.703125 47.296875 -L 44.203125 47.296875 -L 44.203125 20.796875 -L 41.703125 20.796875 -Q 41.703125 27.703125 39.796875 30.09375 -Q 37.90625 32.5 31.09375 32.5 -L 22.5 32.5 -L 22.5 7.90625 -Q 22.5 5.796875 22.84375 5 -Q 23.203125 4.203125 25.296875 3.640625 -Q 27.40625 3.09375 32 3.09375 -L 35.296875 3.09375 -L 35.296875 0 -Q 31.703125 0.296875 18.5 0.296875 -Q 6.796875 0.296875 3.296875 0 -z -" id="CMUSerif-Roman-70"/> - <path d="M 3.203125 0 -L 3.203125 3.09375 -Q 8.5 3.09375 9.75 3.75 -Q 11 4.40625 11 7.59375 -L 11 59.59375 -Q 11 63.296875 9.703125 64.25 -Q 8.40625 65.203125 3.203125 65.203125 -L 3.203125 68.296875 -L 17.59375 69.40625 -L 17.59375 34.59375 -L 17.703125 34.59375 -Q 19.296875 38.203125 22.890625 41.203125 -Q 26.5 44.203125 32.09375 44.203125 -Q 39.59375 44.203125 42.59375 40.5 -Q 44.796875 38 45.25 35.203125 -Q 45.703125 32.40625 45.703125 25.203125 -L 45.703125 6.09375 -Q 45.796875 4 47.390625 3.546875 -Q 49 3.09375 53.5 3.09375 -L 53.5 0 -Q 43.296875 0.296875 42.296875 0.296875 -Q 41.5 0.296875 31 0 -L 31 3.09375 -Q 36.296875 3.09375 37.546875 3.75 -Q 38.796875 4.40625 38.796875 7.59375 -L 38.796875 30.90625 -Q 38.796875 36 37.25 39 -Q 35.703125 42 31.40625 42 -Q 26.203125 42 22.046875 37.640625 -Q 17.90625 33.296875 17.90625 26 -L 17.90625 7.59375 -Q 17.90625 4.40625 19.15625 3.75 -Q 20.40625 3.09375 25.703125 3.09375 -L 25.703125 0 -Q 15.5 0.296875 14.5 0.296875 -Q 13.703125 0.296875 3.203125 0 -z -" id="CMUSerif-Roman-104"/> - <path d="M 3.40625 21.5 -Q 3.40625 31 10.046875 37.59375 -Q 16.703125 44.203125 25.703125 44.203125 -Q 33.296875 44.203125 38.296875 38 -L 38.296875 59.59375 -Q 38.296875 63.296875 37 64.25 -Q 35.703125 65.203125 30.5 65.203125 -L 30.5 68.296875 -L 44.90625 69.40625 -L 44.90625 8.703125 -Q 44.90625 5 46.203125 4.046875 -Q 47.5 3.09375 52.703125 3.09375 -L 52.703125 0 -L 38 -1.09375 -L 38 5.5 -Q 32.796875 -1.09375 24.59375 -1.09375 -Q 16 -1.09375 9.703125 5.5 -Q 3.40625 12.09375 3.40625 21.5 -z -M 11.703125 21.40625 -Q 11.703125 12.09375 14.59375 7.5 -Q 18.59375 1.09375 25.09375 1.09375 -Q 32.5 1.09375 36.90625 8.09375 -Q 38 9.796875 38 11.796875 -L 38 32.296875 -Q 38 34.296875 36.90625 36 -Q 32.796875 42 26.09375 42 -Q 19.09375 42 14.796875 35.59375 -Q 11.703125 30.796875 11.703125 21.40625 -z -" id="CMUSerif-Roman-100"/> - <path d="M 3.203125 40 -L 3.203125 43.09375 -L 17.90625 44.203125 -L 17.90625 11 -Q 17.90625 8.59375 18.09375 7.1875 -Q 18.296875 5.796875 19.09375 4.1875 -Q 19.90625 2.59375 21.796875 1.84375 -Q 23.703125 1.09375 26.703125 1.09375 -Q 32.09375 1.09375 35.4375 5.546875 -Q 38.796875 10 38.796875 16.59375 -L 38.796875 34.40625 -Q 38.796875 38.09375 37.5 39.046875 -Q 36.203125 40 31 40 -L 31 43.09375 -L 45.703125 44.203125 -L 45.703125 8.703125 -Q 45.703125 5 47 4.046875 -Q 48.296875 3.09375 53.5 3.09375 -L 53.5 0 -L 39.09375 -1.09375 -L 39.09375 7.90625 -Q 34.90625 -1.09375 26.203125 -1.09375 -Q 21.796875 -1.09375 18.796875 0 -Q 15.796875 1.09375 14.296875 2.5 -Q 12.796875 3.90625 12 6.59375 -Q 11.203125 9.296875 11.09375 10.9375 -Q 11 12.59375 11 15.796875 -L 11 30.796875 -Q 11 37.59375 10 38.796875 -Q 9 40 3.203125 40 -z -" id="CMUSerif-Roman-117"/> - <path d="M 2.796875 65.203125 -L 2.796875 68.296875 -L 17.203125 69.40625 -L 17.203125 37.703125 -Q 23 44.203125 30.90625 44.203125 -Q 39.5 44.203125 45.796875 37.59375 -Q 52.09375 31 52.09375 21.59375 -Q 52.09375 12.09375 45.5 5.5 -Q 38.90625 -1.09375 29.796875 -1.09375 -Q 21.5 -1.09375 16.703125 6.203125 -Q 13.203125 0.09375 13.09375 0 -L 10.59375 0 -L 10.59375 59.59375 -Q 10.59375 63.296875 9.296875 64.25 -Q 8 65.203125 2.796875 65.203125 -z -M 17.5 11.40625 -Q 17.5 9.296875 18.90625 7.203125 -Q 22.90625 1.09375 29.40625 1.09375 -Q 36.40625 1.09375 40.703125 7.5 -Q 43.796875 12.296875 43.796875 21.703125 -Q 43.796875 31 40.90625 35.59375 -Q 36.90625 42 30.40625 42 -Q 23.09375 42 18.59375 35.59375 -Q 17.5 34 17.5 32 -z -" id="CMUSerif-Roman-98"/> - <path d="M 7.71875 1.703125 -Q 7.71875 2.296875 7.8125 2.59375 -L 15.28125 32.421875 -Q 16.015625 35.203125 16.015625 37.3125 -Q 16.015625 41.609375 13.09375 41.609375 -Q 9.96875 41.609375 8.453125 37.859375 -Q 6.9375 34.125 5.515625 28.421875 -Q 5.515625 28.125 5.21875 27.953125 -Q 4.9375 27.78125 4.6875 27.78125 -L 3.515625 27.78125 -Q 3.171875 27.78125 2.921875 28.140625 -Q 2.6875 28.515625 2.6875 28.8125 -Q 3.765625 33.15625 4.765625 36.171875 -Q 5.765625 39.203125 7.890625 41.6875 -Q 10.015625 44.1875 13.1875 44.1875 -Q 16.9375 44.1875 19.8125 41.8125 -Q 22.703125 39.453125 22.703125 35.796875 -Q 25.6875 39.703125 29.6875 41.9375 -Q 33.6875 44.1875 38.1875 44.1875 -Q 41.75 44.1875 44.328125 42.96875 -Q 46.921875 41.75 48.359375 39.28125 -Q 49.8125 36.8125 49.8125 33.40625 -Q 49.8125 29.296875 47.96875 23.484375 -Q 46.140625 17.671875 43.40625 10.5 -Q 42 7.234375 42 4.5 -Q 42 1.515625 44.28125 1.515625 -Q 48.1875 1.515625 50.796875 5.703125 -Q 53.421875 9.90625 54.5 14.703125 -Q 54.6875 15.28125 55.328125 15.28125 -L 56.5 15.28125 -Q 56.890625 15.28125 57.15625 15.03125 -Q 57.421875 14.796875 57.421875 14.40625 -Q 57.421875 14.3125 57.328125 14.109375 -Q 55.953125 8.453125 52.5625 3.65625 -Q 49.171875 -1.125 44.09375 -1.125 -Q 40.578125 -1.125 38.078125 1.296875 -Q 35.59375 3.71875 35.59375 7.171875 -Q 35.59375 9.03125 36.375 11.078125 -Q 37.640625 14.359375 39.28125 18.890625 -Q 40.921875 23.4375 41.96875 27.578125 -Q 43.015625 31.734375 43.015625 34.90625 -Q 43.015625 37.703125 41.859375 39.65625 -Q 40.71875 41.609375 37.984375 41.609375 -Q 34.328125 41.609375 31.25 39.984375 -Q 28.171875 38.375 25.875 35.71875 -Q 23.578125 33.0625 21.6875 29.390625 -L 14.890625 2.203125 -Q 14.546875 0.828125 13.34375 -0.140625 -Q 12.15625 -1.125 10.6875 -1.125 -Q 9.46875 -1.125 8.59375 -0.34375 -Q 7.71875 0.4375 7.71875 1.703125 -z -" id="Cmmi10-110"/> - <path d="M 3.078125 0 -L 3.078125 3.515625 -Q 13.375 3.515625 13.375 6.6875 -L 13.375 61.625 -Q 13.375 64.796875 3.078125 64.796875 -L 3.078125 68.3125 -L 33.015625 68.3125 -L 33.015625 64.796875 -Q 22.703125 64.796875 22.703125 61.625 -L 22.703125 37.3125 -L 52.203125 37.3125 -L 52.203125 61.625 -Q 52.203125 64.796875 41.890625 64.796875 -L 41.890625 68.3125 -L 71.78125 68.3125 -L 71.78125 64.796875 -Q 61.53125 64.796875 61.53125 61.625 -L 61.53125 6.6875 -Q 61.53125 3.515625 71.78125 3.515625 -L 71.78125 0 -L 41.890625 0 -L 41.890625 3.515625 -Q 52.203125 3.515625 52.203125 6.6875 -L 52.203125 33.796875 -L 22.703125 33.796875 -L 22.703125 6.6875 -Q 22.703125 3.515625 33.015625 3.515625 -L 33.015625 0 -z -" id="Cmr10-72"/> - <path d="M 9.90625 -18.015625 -Q 9.90625 -17.578125 10.296875 -17.1875 -Q 13.921875 -13.71875 15.921875 -9.171875 -Q 17.921875 -4.640625 17.921875 0.390625 -L 17.921875 1.609375 -Q 16.3125 0 13.921875 0 -Q 11.625 0 10.015625 1.609375 -Q 8.40625 3.21875 8.40625 5.515625 -Q 8.40625 7.859375 10.015625 9.421875 -Q 11.625 10.984375 13.921875 10.984375 -Q 17.484375 10.984375 19 7.6875 -Q 20.515625 4.390625 20.515625 0.390625 -Q 20.515625 -5.171875 18.28125 -10.171875 -Q 16.0625 -15.1875 12.015625 -19.1875 -Q 11.625 -19.390625 11.375 -19.390625 -Q 10.890625 -19.390625 10.390625 -18.9375 -Q 9.90625 -18.5 9.90625 -18.015625 -z -" id="Cmmi10-59"/> - <path d="M 10.203125 12.015625 -L 10.203125 39.59375 -L 1.90625 39.59375 -L 1.90625 42.1875 -Q 8.453125 42.1875 11.515625 48.28125 -Q 14.59375 54.390625 14.59375 61.53125 -L 17.484375 61.53125 -L 17.484375 43.109375 -L 31.59375 43.109375 -L 31.59375 39.59375 -L 17.484375 39.59375 -L 17.484375 12.203125 -Q 17.484375 8.0625 18.875 4.9375 -Q 20.265625 1.8125 23.875 1.8125 -Q 27.296875 1.8125 28.8125 5.109375 -Q 30.328125 8.40625 30.328125 12.203125 -L 30.328125 18.109375 -L 33.203125 18.109375 -L 33.203125 12.015625 -Q 33.203125 8.890625 32.046875 5.828125 -Q 30.90625 2.78125 28.65625 0.828125 -Q 26.421875 -1.125 23.1875 -1.125 -Q 17.1875 -1.125 13.6875 2.46875 -Q 10.203125 6.0625 10.203125 12.015625 -z -" id="Cmr10-116"/> - <path d="M 2.984375 0 -L 2.984375 3.515625 -Q 6.390625 3.515625 8.59375 4.046875 -Q 10.796875 4.59375 10.796875 6.6875 -L 10.796875 59.1875 -Q 10.796875 61.859375 9.984375 63.0625 -Q 9.1875 64.265625 7.671875 64.53125 -Q 6.15625 64.796875 2.984375 64.796875 -L 2.984375 68.3125 -L 17.828125 69.390625 -L 17.828125 35.015625 -Q 19.921875 39.15625 23.671875 41.671875 -Q 27.4375 44.1875 31.984375 44.1875 -Q 38.921875 44.1875 42.40625 40.859375 -Q 45.90625 37.546875 45.90625 30.71875 -L 45.90625 6.6875 -Q 45.90625 4.59375 48.09375 4.046875 -Q 50.296875 3.515625 53.71875 3.515625 -L 53.71875 0 -L 30.8125 0 -L 30.8125 3.515625 -Q 34.234375 3.515625 36.421875 4.046875 -Q 38.625 4.59375 38.625 6.6875 -L 38.625 30.421875 -Q 38.625 35.296875 37.203125 38.453125 -Q 35.796875 41.609375 31.390625 41.609375 -Q 25.59375 41.609375 21.84375 36.96875 -Q 18.109375 32.328125 18.109375 26.421875 -L 18.109375 6.6875 -Q 18.109375 4.59375 20.3125 4.046875 -Q 22.515625 3.515625 25.875 3.515625 -L 25.875 0 -z -" id="Cmr10-104"/> - <path d="M 2.59375 0 -L 2.59375 3.515625 -Q 6 3.515625 8.203125 4.046875 -Q 10.40625 4.59375 10.40625 6.6875 -L 10.40625 33.984375 -Q 10.40625 36.671875 9.59375 37.859375 -Q 8.796875 39.0625 7.28125 39.328125 -Q 5.765625 39.59375 2.59375 39.59375 -L 2.59375 43.109375 -L 16.890625 44.1875 -L 16.890625 34.421875 -Q 18.5 38.765625 21.484375 41.46875 -Q 24.46875 44.1875 28.71875 44.1875 -Q 31.6875 44.1875 34.03125 42.421875 -Q 36.375 40.671875 36.375 37.796875 -Q 36.375 35.984375 35.078125 34.640625 -Q 33.796875 33.296875 31.890625 33.296875 -Q 30.03125 33.296875 28.703125 34.609375 -Q 27.390625 35.9375 27.390625 37.796875 -Q 27.390625 40.484375 29.296875 41.609375 -L 28.71875 41.609375 -Q 24.65625 41.609375 22.09375 38.671875 -Q 19.53125 35.75 18.453125 31.390625 -Q 17.390625 27.046875 17.390625 23.09375 -L 17.390625 6.6875 -Q 17.390625 3.515625 27.09375 3.515625 -L 27.09375 0 -z -" id="Cmr10-114"/> - <path d="M 24.90625 -1.125 -Q 18.796875 -1.125 13.6875 2.078125 -Q 8.59375 5.28125 5.6875 10.625 -Q 2.78125 15.96875 2.78125 21.921875 -Q 2.78125 27.78125 5.4375 33.046875 -Q 8.109375 38.328125 12.859375 41.578125 -Q 17.625 44.828125 23.484375 44.828125 -Q 28.078125 44.828125 31.46875 43.28125 -Q 34.859375 41.75 37.0625 39.015625 -Q 39.265625 36.28125 40.375 32.5625 -Q 41.5 28.859375 41.5 24.421875 -Q 41.5 23.09375 40.484375 23.09375 -L 11.53125 23.09375 -L 11.53125 22.015625 -Q 11.53125 13.71875 14.875 7.765625 -Q 18.21875 1.8125 25.78125 1.8125 -Q 28.859375 1.8125 31.46875 3.171875 -Q 34.078125 4.546875 36 6.984375 -Q 37.9375 9.421875 38.625 12.203125 -Q 38.71875 12.546875 38.984375 12.8125 -Q 39.265625 13.09375 39.59375 13.09375 -L 40.484375 13.09375 -Q 41.5 13.09375 41.5 11.8125 -Q 40.09375 6.15625 35.40625 2.515625 -Q 30.71875 -1.125 24.90625 -1.125 -z -M 11.625 25.59375 -L 34.421875 25.59375 -Q 34.421875 29.34375 33.375 33.203125 -Q 32.328125 37.0625 29.875 39.625 -Q 27.4375 42.1875 23.484375 42.1875 -Q 17.828125 42.1875 14.71875 36.890625 -Q 11.625 31.59375 11.625 25.59375 -z -" id="Cmr10-101"/> - <path d="M 3.328125 -0.296875 -L 3.328125 16.015625 -Q 3.328125 16.796875 4.203125 16.796875 -L 5.421875 16.796875 -Q 6 16.796875 6.203125 16.015625 -Q 8.984375 1.515625 19.671875 1.515625 -Q 24.421875 1.515625 27.609375 3.65625 -Q 30.8125 5.8125 30.8125 10.296875 -Q 30.8125 13.53125 28.3125 15.796875 -Q 25.828125 18.0625 22.40625 18.890625 -L 15.71875 20.21875 -Q 12.359375 20.953125 9.59375 22.453125 -Q 6.84375 23.96875 5.078125 26.484375 -Q 3.328125 29 3.328125 32.328125 -Q 3.328125 36.71875 5.640625 39.515625 -Q 7.953125 42.328125 11.65625 43.578125 -Q 15.375 44.828125 19.671875 44.828125 -Q 24.8125 44.828125 28.609375 42.09375 -L 31.5 44.578125 -Q 31.5 44.828125 31.984375 44.828125 -L 32.71875 44.828125 -Q 33.015625 44.828125 33.25 44.546875 -Q 33.5 44.28125 33.5 44 -L 33.5 30.90625 -Q 33.5 29.984375 32.71875 29.984375 -L 31.5 29.984375 -Q 30.609375 29.984375 30.609375 30.90625 -Q 30.609375 36.140625 27.703125 39.3125 -Q 24.8125 42.484375 19.578125 42.484375 -Q 15.09375 42.484375 11.796875 40.8125 -Q 8.5 39.15625 8.5 35.109375 -Q 8.5 32.328125 10.859375 30.546875 -Q 13.234375 28.765625 16.40625 27.984375 -L 23.1875 26.703125 -Q 26.609375 25.921875 29.5625 24.0625 -Q 32.515625 22.21875 34.25 19.375 -Q 35.984375 16.546875 35.984375 12.984375 -Q 35.984375 9.375 34.734375 6.703125 -Q 33.5 4.046875 31.265625 2.28125 -Q 29.046875 0.53125 26.015625 -0.296875 -Q 23 -1.125 19.671875 -1.125 -Q 13.421875 -1.125 8.984375 3.078125 -L 5.328125 -0.875 -Q 5.328125 -1.125 4.78125 -1.125 -L 4.203125 -1.125 -Q 3.328125 -1.125 3.328125 -0.296875 -z -" id="Cmr10-115"/> - <path d="M 10.203125 23 -Q 9.375 23 8.828125 23.625 -Q 8.296875 24.265625 8.296875 25 -Q 8.296875 25.734375 8.828125 26.359375 -Q 9.375 27 10.203125 27 -L 67.578125 27 -Q 68.359375 27 68.875 26.359375 -Q 69.390625 25.734375 69.390625 25 -Q 69.390625 24.265625 68.875 23.625 -Q 68.359375 23 67.578125 23 -z -" id="Cmsy10-161"/> - <path d="M 9.515625 7.71875 -Q 11.859375 4.296875 15.8125 2.640625 -Q 19.78125 0.984375 24.3125 0.984375 -Q 30.125 0.984375 32.5625 5.9375 -Q 35.015625 10.890625 35.015625 17.1875 -Q 35.015625 20.015625 34.5 22.84375 -Q 33.984375 25.6875 32.765625 28.125 -Q 31.546875 30.5625 29.421875 32.03125 -Q 27.296875 33.5 24.21875 33.5 -L 17.578125 33.5 -Q 16.703125 33.5 16.703125 34.421875 -L 16.703125 35.296875 -Q 16.703125 36.078125 17.578125 36.078125 -L 23.09375 36.53125 -Q 26.609375 36.53125 28.921875 39.15625 -Q 31.25 41.796875 32.328125 45.578125 -Q 33.40625 49.359375 33.40625 52.78125 -Q 33.40625 57.5625 31.15625 60.640625 -Q 28.90625 63.71875 24.3125 63.71875 -Q 20.515625 63.71875 17.046875 62.28125 -Q 13.578125 60.84375 11.53125 57.90625 -Q 11.71875 57.953125 11.859375 57.984375 -Q 12.015625 58.015625 12.203125 58.015625 -Q 14.453125 58.015625 15.96875 56.453125 -Q 17.484375 54.890625 17.484375 52.6875 -Q 17.484375 50.53125 15.96875 48.96875 -Q 14.453125 47.40625 12.203125 47.40625 -Q 10.015625 47.40625 8.453125 48.96875 -Q 6.890625 50.53125 6.890625 52.6875 -Q 6.890625 56.984375 9.46875 60.15625 -Q 12.0625 63.328125 16.140625 64.96875 -Q 20.21875 66.609375 24.3125 66.609375 -Q 27.34375 66.609375 30.703125 65.703125 -Q 34.078125 64.796875 36.8125 63.109375 -Q 39.546875 61.421875 41.28125 58.78125 -Q 43.015625 56.15625 43.015625 52.78125 -Q 43.015625 48.578125 41.140625 45.015625 -Q 39.265625 41.453125 35.984375 38.859375 -Q 32.71875 36.28125 28.8125 35.015625 -Q 33.15625 34.1875 37.0625 31.734375 -Q 40.96875 29.296875 43.328125 25.484375 -Q 45.703125 21.6875 45.703125 17.28125 -Q 45.703125 11.765625 42.671875 7.296875 -Q 39.65625 2.828125 34.71875 0.3125 -Q 29.78125 -2.203125 24.3125 -2.203125 -Q 19.625 -2.203125 14.90625 -0.40625 -Q 10.203125 1.375 7.203125 4.9375 -Q 4.203125 8.5 4.203125 13.484375 -Q 4.203125 15.96875 5.859375 17.625 -Q 7.515625 19.28125 10.015625 19.28125 -Q 11.625 19.28125 12.96875 18.53125 -Q 14.3125 17.78125 15.0625 16.40625 -Q 15.828125 15.046875 15.828125 13.484375 -Q 15.828125 11.03125 14.109375 9.375 -Q 12.40625 7.71875 10.015625 7.71875 -z -" id="Cmr10-51"/> - </defs> - <g transform="translate(8.72 198.042)rotate(-90)scale(0.1 -0.1)"> - <use transform="translate(0 0.109375)" xlink:href="#CMUSerif-Roman-83"/> - <use transform="translate(55.499985 0.109375)" xlink:href="#CMUSerif-Roman-70"/> - <use transform="translate(120.699982 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(153.999969 0.109375)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(192.799957 0.109375)" xlink:href="#CMUSerif-Roman-104"/> - <use transform="translate(248.299942 0.109375)" xlink:href="#CMUSerif-Roman-114"/> - <use transform="translate(287.399933 0.109375)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(331.799927 0.109375)" xlink:href="#CMUSerif-Roman-115"/> - <use transform="translate(371.199921 0.109375)" xlink:href="#CMUSerif-Roman-104"/> - <use transform="translate(426.699905 0.109375)" xlink:href="#CMUSerif-Roman-111"/> - <use transform="translate(476.69989 0.109375)" xlink:href="#CMUSerif-Roman-108"/> - <use transform="translate(504.399887 0.109375)" xlink:href="#CMUSerif-Roman-100"/> - <use transform="translate(559.899872 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(593.19986 0.109375)" xlink:href="#CMUSerif-Roman-110"/> - <use transform="translate(648.699844 0.109375)" xlink:href="#CMUSerif-Roman-117"/> - <use transform="translate(704.199829 0.109375)" xlink:href="#CMUSerif-Roman-109"/> - <use transform="translate(787.499817 0.109375)" xlink:href="#CMUSerif-Roman-98"/> - <use transform="translate(842.999802 0.109375)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(887.399796 0.109375)" xlink:href="#CMUSerif-Roman-114"/> - <use transform="translate(926.499786 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(959.799774 0.109375)" xlink:href="#CMUSerif-Roman-100"/> - <use transform="translate(1015.299759 0.109375)" xlink:href="#CMUSerif-Roman-101"/> - <use transform="translate(1059.699753 0.109375)" xlink:href="#CMUSerif-Roman-110"/> - <use transform="translate(1115.199738 0.109375)" xlink:href="#CMUSerif-Roman-115"/> - <use transform="translate(1154.599731 0.109375)" xlink:href="#CMUSerif-Roman-105"/> - <use transform="translate(1182.299728 0.109375)" xlink:href="#CMUSerif-Roman-116"/> - <use transform="translate(1221.099716 0.109375)" xlink:href="#CMUSerif-Roman-121"/> - <use transform="translate(1273.799713 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(1307.099701 0.109375)" xlink:href="#Cmmi10-110"/> - <use transform="translate(1367.109467 -16.896875)scale(0.7)" xlink:href="#Cmr10-72"/> - <use transform="translate(1419.609467 -16.896875)scale(0.7)" xlink:href="#Cmmi10-59"/> - <use transform="translate(1450.651459 -16.896875)scale(0.7)" xlink:href="#Cmr10-116"/> - <use transform="translate(1477.82431 -16.896875)scale(0.7)" xlink:href="#Cmr10-104"/> - <use transform="translate(1516.686615 -16.896875)scale(0.7)" xlink:href="#Cmr10-114"/> - <use transform="translate(1544.064545 -16.896875)scale(0.7)" xlink:href="#Cmr10-101"/> - <use transform="translate(1575.133881 -16.896875)scale(0.7)" xlink:href="#Cmr10-115"/> - <use transform="translate(1602.716888 -16.896875)scale(0.7)" xlink:href="#Cmr10-104"/> - <use transform="translate(1647.956537 0.109375)" xlink:href="#CMUSerif-Roman-32"/> - <use transform="translate(1681.256525 0.109375)" xlink:href="#CMUSerif-Roman-91"/> - <use transform="translate(1709.056512 0.109375)" xlink:href="#CMUSerif-Roman-99"/> - <use transform="translate(1753.456506 0.109375)" xlink:href="#CMUSerif-Roman-109"/> - <use transform="translate(1841.220635 38.373438)scale(0.7)" xlink:href="#Cmsy10-161"/> - <use transform="translate(1895.600518 38.373438)scale(0.7)" xlink:href="#Cmr10-51"/> - <use transform="translate(1936.977861 0.109375)" xlink:href="#CMUSerif-Roman-93"/> - </g> - </g> - </g> - <g id="line2d_54"> - <path clip-path="url(#p23ab05f3c8)" d="M 6.804 27.524431 -L 66.139784 27.620407 -L 227.8 164.984202 -L 227.8 164.984202 -" style="fill:none;stroke:#000000;stroke-linecap:square;"/> - </g> - <g id="line2d_55"> - <path clip-path="url(#p23ab05f3c8)" d="M 128.653332 63.658216 -L 183.085332 109.90946 -" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_56"> - <path clip-path="url(#p23ab05f3c8)" d="M -1 27.524431 -L 227.8 27.524431 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_57"> - <path clip-path="url(#p23ab05f3c8)" d="M 151.076832 227.8 -L 151.076832 99.792 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_58"> - <path clip-path="url(#p23ab05f3c8)" d="M -1 99.792 -L 151.076832 99.792 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="patch_3"> - <path d="M 34.02 197.316 -L 34.02 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_4"> - <path d="M 224.532 197.316 -L 224.532 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_5"> - <path d="M 34.02 197.316 -L 224.532 197.316 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_6"> - <path d="M 34.02 2.268 -L 224.532 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_7"> - <path d="M 174.027021 190.953156 -Q 174.027021 184.004135 174.027021 177.055114 -L 172.577021 177.055114 -Q 173.327021 174.557342 174.077021 172.059569 -Q 174.827021 174.557342 175.577021 177.055114 -L 174.127021 177.055114 -Q 174.127021 184.004135 174.127021 190.953156 -L 174.027021 190.953156 -z -" style="stroke:#000000;stroke-linecap:round;"/> - </g> - <g id="text_12"> - <!-- ${Z_\odot}$ --> - <defs> - <path d="M 38.921875 -8.296875 -Q 32.078125 -8.296875 26.0625 -5.703125 -Q 20.0625 -3.125 15.40625 1.484375 -Q 10.75 6.109375 8.171875 12.1875 -Q 5.609375 18.265625 5.609375 25 -Q 5.609375 31.84375 8.140625 37.84375 -Q 10.6875 43.84375 15.375 48.53125 -Q 20.0625 53.21875 26.0625 55.75 -Q 32.078125 58.296875 38.921875 58.296875 -Q 45.703125 58.296875 51.75 55.703125 -Q 57.8125 53.125 62.40625 48.5 -Q 67 43.890625 69.5625 37.828125 -Q 72.125 31.78125 72.125 25 -Q 72.125 18.3125 69.53125 12.1875 -Q 66.9375 6.0625 62.375 1.484375 -Q 57.8125 -3.078125 51.75 -5.6875 -Q 45.703125 -8.296875 38.921875 -8.296875 -z -M 38.921875 -5.421875 -Q 44.96875 -5.421875 50.53125 -3.046875 -Q 56.109375 -0.6875 60.296875 3.515625 -Q 64.5 7.71875 66.84375 13.25 -Q 69.1875 18.796875 69.1875 25 -Q 69.1875 31.203125 66.8125 36.796875 -Q 64.453125 42.390625 60.34375 46.515625 -Q 56.25 50.640625 50.703125 53.03125 -Q 45.171875 55.421875 38.921875 55.421875 -Q 32.671875 55.421875 27.09375 53.03125 -Q 21.53125 50.640625 17.421875 46.53125 -Q 13.328125 42.4375 10.90625 36.71875 -Q 8.5 31 8.5 25 -Q 8.5 19 10.859375 13.359375 -Q 13.234375 7.71875 17.453125 3.515625 -Q 21.6875 -0.6875 27.265625 -3.046875 -Q 32.859375 -5.421875 38.921875 -5.421875 -z -M 38.921875 17.828125 -Q 36.03125 17.828125 33.859375 19.953125 -Q 31.6875 22.078125 31.6875 25 -Q 31.6875 27 32.65625 28.609375 -Q 33.640625 30.21875 35.296875 31.1875 -Q 36.96875 32.171875 38.921875 32.171875 -Q 40.765625 32.171875 42.40625 31.1875 -Q 44.046875 30.21875 45.015625 28.609375 -Q 46 27 46 25 -Q 46 22.078125 43.875 19.953125 -Q 41.75 17.828125 38.921875 17.828125 -z -" id="Cmsy10-175"/> - </defs> - <g transform="translate(175.655329 188.534116)scale(0.09 -0.09)"> - <use transform="translate(0 0.6875)" xlink:href="#Cmmi10-90"/> - <use transform="translate(80.503906 -16.31875)scale(0.7)" xlink:href="#Cmsy10-175"/> - </g> - </g> - <g id="text_13"> - <!-- Z^threshold_slope --> - <defs> - <path d="M 8.6875 72.90625 -L 56 72.90625 -L 56 65.375 -L 17.921875 8.296875 -L 57.078125 8.296875 -L 57.078125 0 -L 7.625 0 -L 7.625 7.515625 -L 44.671875 64.59375 -L 8.6875 64.59375 -z -" id="DejaVuSansMono-90"/> - <path d="M 34.421875 72.90625 -L 56.6875 45.703125 -L 48 45.703125 -L 30.078125 64.984375 -L 12.203125 45.703125 -L 3.515625 45.703125 -L 25.78125 72.90625 -z -" id="DejaVuSansMono-94"/> - <path d="M 29.984375 70.21875 -L 29.984375 54.6875 -L 50.390625 54.6875 -L 50.390625 47.703125 -L 29.984375 47.703125 -L 29.984375 18.015625 -Q 29.984375 11.96875 32.28125 9.5625 -Q 34.578125 7.171875 40.28125 7.171875 -L 50.390625 7.171875 -L 50.390625 0 -L 39.40625 0 -Q 29.296875 0 25.140625 4.046875 -Q 21 8.109375 21 18.015625 -L 21 47.703125 -L 6.390625 47.703125 -L 6.390625 54.6875 -L 21 54.6875 -L 21 70.21875 -z -" id="DejaVuSansMono-116"/> - <path d="M 51.3125 33.890625 -L 51.3125 0 -L 42.28125 0 -L 42.28125 33.890625 -Q 42.28125 41.265625 39.6875 44.71875 -Q 37.109375 48.1875 31.59375 48.1875 -Q 25.296875 48.1875 21.890625 43.71875 -Q 18.5 39.265625 18.5 30.90625 -L 18.5 0 -L 9.515625 0 -L 9.515625 75.984375 -L 18.5 75.984375 -L 18.5 46.484375 -Q 20.90625 51.171875 25 53.578125 -Q 29.109375 56 34.71875 56 -Q 43.0625 56 47.1875 50.5 -Q 51.3125 45.015625 51.3125 33.890625 -z -" id="DejaVuSansMono-104"/> - <path d="M 56.390625 43.40625 -Q 53.515625 45.65625 50.53125 46.671875 -Q 47.5625 47.703125 44 47.703125 -Q 35.59375 47.703125 31.140625 42.421875 -Q 26.703125 37.15625 26.703125 27.203125 -L 26.703125 0 -L 17.671875 0 -L 17.671875 54.6875 -L 26.703125 54.6875 -L 26.703125 44 -Q 28.953125 49.8125 33.609375 52.90625 -Q 38.28125 56 44.671875 56 -Q 48 56 50.875 55.171875 -Q 53.765625 54.34375 56.390625 52.59375 -z -" id="DejaVuSansMono-114"/> - <path d="M 54.296875 29.59375 -L 54.296875 25.203125 -L 15.375 25.203125 -L 15.375 24.90625 -Q 15.375 15.96875 20.03125 11.078125 -Q 24.703125 6.203125 33.203125 6.203125 -Q 37.5 6.203125 42.1875 7.5625 -Q 46.875 8.9375 52.203125 11.71875 -L 52.203125 2.78125 -Q 47.078125 0.6875 42.3125 -0.359375 -Q 37.546875 -1.421875 33.109375 -1.421875 -Q 20.359375 -1.421875 13.171875 6.21875 -Q 6 13.875 6 27.296875 -Q 6 40.375 13.03125 48.1875 -Q 20.0625 56 31.78125 56 -Q 42.234375 56 48.265625 48.921875 -Q 54.296875 41.84375 54.296875 29.59375 -z -M 45.3125 32.234375 -Q 45.125 40.140625 41.578125 44.265625 -Q 38.03125 48.390625 31.390625 48.390625 -Q 24.90625 48.390625 20.703125 44.09375 -Q 16.5 39.796875 15.71875 32.171875 -z -" id="DejaVuSansMono-101"/> - <path d="M 47.515625 52.78125 -L 47.515625 44 -Q 43.65625 46.234375 39.75 47.359375 -Q 35.84375 48.484375 31.78125 48.484375 -Q 25.6875 48.484375 22.671875 46.5 -Q 19.671875 44.53125 19.671875 40.484375 -Q 19.671875 36.8125 21.921875 35 -Q 24.171875 33.203125 33.109375 31.5 -L 36.71875 30.8125 -Q 43.40625 29.546875 46.84375 25.734375 -Q 50.296875 21.921875 50.296875 15.828125 -Q 50.296875 7.71875 44.53125 3.140625 -Q 38.765625 -1.421875 28.515625 -1.421875 -Q 24.46875 -1.421875 20.015625 -0.5625 -Q 15.578125 0.296875 10.40625 2 -L 10.40625 11.28125 -Q 15.4375 8.6875 20.015625 7.390625 -Q 24.609375 6.109375 28.71875 6.109375 -Q 34.671875 6.109375 37.9375 8.515625 -Q 41.21875 10.9375 41.21875 15.28125 -Q 41.21875 21.53125 29.25 23.921875 -L 28.859375 24.03125 -L 25.484375 24.703125 -Q 17.71875 26.21875 14.15625 29.8125 -Q 10.59375 33.40625 10.59375 39.59375 -Q 10.59375 47.46875 15.90625 51.734375 -Q 21.234375 56 31.109375 56 -Q 35.5 56 39.546875 55.1875 -Q 43.609375 54.390625 47.515625 52.78125 -z -" id="DejaVuSansMono-115"/> - <path d="M 30.078125 48.390625 -Q 23.25 48.390625 19.734375 43.0625 -Q 16.21875 37.75 16.21875 27.296875 -Q 16.21875 16.890625 19.734375 11.546875 -Q 23.25 6.203125 30.078125 6.203125 -Q 36.96875 6.203125 40.484375 11.546875 -Q 44 16.890625 44 27.296875 -Q 44 37.75 40.484375 43.0625 -Q 36.96875 48.390625 30.078125 48.390625 -z -M 30.078125 56 -Q 41.453125 56 47.484375 48.625 -Q 53.515625 41.265625 53.515625 27.296875 -Q 53.515625 13.28125 47.5 5.921875 -Q 41.5 -1.421875 30.078125 -1.421875 -Q 18.703125 -1.421875 12.6875 5.921875 -Q 6.6875 13.28125 6.6875 27.296875 -Q 6.6875 41.265625 12.6875 48.625 -Q 18.703125 56 30.078125 56 -z -" id="DejaVuSansMono-111"/> - <path d="M 31.203125 19.828125 -Q 31.203125 13.765625 33.421875 10.6875 -Q 35.640625 7.625 39.984375 7.625 -L 50.484375 7.625 -L 50.484375 0 -L 39.109375 0 -Q 31.0625 0 26.640625 5.171875 -Q 22.21875 10.359375 22.21875 19.828125 -L 22.21875 69.484375 -L 7.8125 69.484375 -L 7.8125 76.515625 -L 31.203125 76.515625 -z -" id="DejaVuSansMono-108"/> - <path d="M 41.890625 47.703125 -L 41.890625 75.984375 -L 50.875 75.984375 -L 50.875 0 -L 41.890625 0 -L 41.890625 6.890625 -Q 39.65625 2.828125 35.90625 0.703125 -Q 32.171875 -1.421875 27.296875 -1.421875 -Q 17.390625 -1.421875 11.6875 6.265625 -Q 6 13.96875 6 27.484375 -Q 6 40.828125 11.71875 48.40625 -Q 17.4375 56 27.296875 56 -Q 32.234375 56 35.984375 53.875 -Q 39.75 51.765625 41.890625 47.703125 -z -M 15.484375 27.296875 -Q 15.484375 16.84375 18.796875 11.515625 -Q 22.125 6.203125 28.609375 6.203125 -Q 35.109375 6.203125 38.5 11.5625 -Q 41.890625 16.9375 41.890625 27.296875 -Q 41.890625 37.703125 38.5 43.046875 -Q 35.109375 48.390625 28.609375 48.390625 -Q 22.125 48.390625 18.796875 43.0625 -Q 15.484375 37.75 15.484375 27.296875 -z -" id="DejaVuSansMono-100"/> - <path d="M 60.203125 -19.671875 -L 60.203125 -23.578125 -L 0 -23.578125 -L 0 -19.671875 -z -" id="DejaVuSansMono-95"/> - <path d="M 18.3125 6.890625 -L 18.3125 -20.796875 -L 9.28125 -20.796875 -L 9.28125 54.6875 -L 18.3125 54.6875 -L 18.3125 47.703125 -Q 20.5625 51.765625 24.296875 53.875 -Q 28.03125 56 32.90625 56 -Q 42.828125 56 48.46875 48.328125 -Q 54.109375 40.671875 54.109375 27.09375 -Q 54.109375 13.765625 48.4375 6.171875 -Q 42.78125 -1.421875 32.90625 -1.421875 -Q 27.9375 -1.421875 24.1875 0.703125 -Q 20.453125 2.828125 18.3125 6.890625 -z -M 44.671875 27.296875 -Q 44.671875 37.75 41.375 43.0625 -Q 38.09375 48.390625 31.59375 48.390625 -Q 25.046875 48.390625 21.671875 43.046875 -Q 18.3125 37.703125 18.3125 27.296875 -Q 18.3125 16.9375 21.671875 11.5625 -Q 25.046875 6.203125 31.59375 6.203125 -Q 38.09375 6.203125 41.375 11.515625 -Q 44.671875 16.84375 44.671875 27.296875 -z -" id="DejaVuSansMono-112"/> - </defs> - <g transform="translate(129.714233 61.930385)rotate(-320)scale(0.07 -0.07)"> - <use xlink:href="#DejaVuSansMono-90"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-94"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-116"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-104"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-114"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-101"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-115"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-104"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-111"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-108"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-100"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-95"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-115"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-108"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-111"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-112"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-101"/> - </g> - </g> - <g id="text_14"> - <!-- threshold_max_density_H_p_cm3 --> - <defs> - <path d="M 33.015625 49.125 -Q 34.671875 52.640625 37.234375 54.3125 -Q 39.796875 56 43.40625 56 -Q 50 56 52.703125 50.890625 -Q 55.421875 45.796875 55.421875 31.6875 -L 55.421875 0 -L 47.21875 0 -L 47.21875 31.296875 -Q 47.21875 42.875 45.921875 45.671875 -Q 44.625 48.484375 41.21875 48.484375 -Q 37.3125 48.484375 35.859375 45.484375 -Q 34.421875 42.484375 34.421875 31.296875 -L 34.421875 0 -L 26.21875 0 -L 26.21875 31.296875 -Q 26.21875 43.015625 24.828125 45.75 -Q 23.4375 48.484375 19.828125 48.484375 -Q 16.265625 48.484375 14.875 45.484375 -Q 13.484375 42.484375 13.484375 31.296875 -L 13.484375 0 -L 5.328125 0 -L 5.328125 54.6875 -L 13.484375 54.6875 -L 13.484375 50 -Q 15.09375 52.9375 17.5 54.46875 -Q 19.921875 56 23 56 -Q 26.703125 56 29.171875 54.296875 -Q 31.640625 52.59375 33.015625 49.125 -z -" id="DejaVuSansMono-109"/> - <path d="M 34.28125 27.484375 -L 31.296875 27.484375 -Q 23.4375 27.484375 19.453125 24.71875 -Q 15.484375 21.96875 15.484375 16.5 -Q 15.484375 11.578125 18.453125 8.84375 -Q 21.4375 6.109375 26.703125 6.109375 -Q 34.125 6.109375 38.375 11.25 -Q 42.625 16.40625 42.671875 25.484375 -L 42.671875 27.484375 -z -M 51.703125 31.203125 -L 51.703125 0 -L 42.671875 0 -L 42.671875 8.109375 -Q 39.796875 3.21875 35.421875 0.890625 -Q 31.0625 -1.421875 24.8125 -1.421875 -Q 16.453125 -1.421875 11.46875 3.296875 -Q 6.5 8.015625 6.5 15.921875 -Q 6.5 25.046875 12.625 29.78125 -Q 18.75 34.515625 30.609375 34.515625 -L 42.671875 34.515625 -L 42.671875 35.9375 -Q 42.625 42.484375 39.34375 45.4375 -Q 36.078125 48.390625 28.90625 48.390625 -Q 24.3125 48.390625 19.625 47.0625 -Q 14.9375 45.75 10.5 43.21875 -L 10.5 52.203125 -Q 15.484375 54.109375 20.046875 55.046875 -Q 24.609375 56 28.90625 56 -Q 35.6875 56 40.5 54 -Q 45.3125 52 48.296875 48 -Q 50.140625 45.5625 50.921875 41.96875 -Q 51.703125 38.375 51.703125 31.203125 -z -" id="DejaVuSansMono-97"/> - <path d="M 54.59375 54.6875 -L 35.015625 28.515625 -L 56.5 0 -L 46.09375 0 -L 30.078125 21.921875 -L 14.109375 0 -L 3.71875 0 -L 25.203125 28.515625 -L 5.609375 54.6875 -L 15.578125 54.6875 -L 30.078125 34.90625 -L 44.484375 54.6875 -z -" id="DejaVuSansMono-120"/> - <path d="M 51.3125 33.890625 -L 51.3125 0 -L 42.28125 0 -L 42.28125 33.890625 -Q 42.28125 41.265625 39.6875 44.71875 -Q 37.109375 48.1875 31.59375 48.1875 -Q 25.296875 48.1875 21.890625 43.71875 -Q 18.5 39.265625 18.5 30.90625 -L 18.5 0 -L 9.515625 0 -L 9.515625 54.6875 -L 18.5 54.6875 -L 18.5 46.484375 -Q 20.90625 51.171875 25 53.578125 -Q 29.109375 56 34.71875 56 -Q 43.0625 56 47.1875 50.5 -Q 51.3125 45.015625 51.3125 33.890625 -z -" id="DejaVuSansMono-110"/> - <path d="M 12.5 54.6875 -L 35.5 54.6875 -L 35.5 6.984375 -L 53.328125 6.984375 -L 53.328125 0 -L 8.6875 0 -L 8.6875 6.984375 -L 26.515625 6.984375 -L 26.515625 47.703125 -L 12.5 47.703125 -z -M 26.515625 75.984375 -L 35.5 75.984375 -L 35.5 64.59375 -L 26.515625 64.59375 -z -" id="DejaVuSansMono-105"/> - <path d="M 41.890625 17.578125 -Q 39.65625 11.859375 36.1875 2.546875 -Q 31.34375 -10.359375 29.6875 -13.1875 -Q 27.4375 -17 24.0625 -18.890625 -Q 20.703125 -20.796875 16.21875 -20.796875 -L 8.984375 -20.796875 -L 8.984375 -13.28125 -L 14.3125 -13.28125 -Q 18.265625 -13.28125 20.5 -10.984375 -Q 22.75 -8.6875 26.21875 0.875 -L 5.078125 54.6875 -L 14.59375 54.6875 -L 30.8125 11.921875 -L 46.78125 54.6875 -L 56.296875 54.6875 -z -" id="DejaVuSansMono-121"/> - <path d="M 6.6875 72.90625 -L 16.609375 72.90625 -L 16.609375 43.015625 -L 43.609375 43.015625 -L 43.609375 72.90625 -L 53.515625 72.90625 -L 53.515625 0 -L 43.609375 0 -L 43.609375 34.71875 -L 16.609375 34.71875 -L 16.609375 0 -L 6.6875 0 -z -" id="DejaVuSansMono-72"/> - <path d="M 51.8125 2.78125 -Q 48.1875 0.6875 44.359375 -0.359375 -Q 40.53125 -1.421875 36.53125 -1.421875 -Q 23.828125 -1.421875 16.671875 6.1875 -Q 9.515625 13.8125 9.515625 27.296875 -Q 9.515625 40.765625 16.671875 48.375 -Q 23.828125 56 36.53125 56 -Q 40.484375 56 44.234375 54.96875 -Q 48 53.953125 51.8125 51.8125 -L 51.8125 42.390625 -Q 48.25 45.5625 44.65625 46.96875 -Q 41.0625 48.390625 36.53125 48.390625 -Q 28.078125 48.390625 23.53125 42.921875 -Q 19 37.453125 19 27.296875 -Q 19 17.1875 23.5625 11.6875 -Q 28.125 6.203125 36.53125 6.203125 -Q 41.21875 6.203125 44.921875 7.640625 -Q 48.640625 9.078125 51.8125 12.109375 -z -" id="DejaVuSansMono-99"/> - <path d="M 37.890625 39.015625 -Q 45.0625 37.109375 48.875 32.25 -Q 52.6875 27.390625 52.6875 20.125 -Q 52.6875 10.0625 45.921875 4.3125 -Q 39.15625 -1.421875 27.203125 -1.421875 -Q 22.171875 -1.421875 16.9375 -0.484375 -Q 11.71875 0.4375 6.6875 2.203125 -L 6.6875 12.015625 -Q 11.671875 9.421875 16.5 8.15625 -Q 21.34375 6.890625 26.125 6.890625 -Q 34.234375 6.890625 38.578125 10.546875 -Q 42.921875 14.203125 42.921875 21.09375 -Q 42.921875 27.4375 38.578125 31.171875 -Q 34.234375 34.90625 26.8125 34.90625 -L 19.28125 34.90625 -L 19.28125 43.015625 -L 26.8125 43.015625 -Q 33.59375 43.015625 37.40625 45.984375 -Q 41.21875 48.96875 41.21875 54.296875 -Q 41.21875 59.90625 37.671875 62.90625 -Q 34.125 65.921875 27.59375 65.921875 -Q 23.25 65.921875 18.609375 64.9375 -Q 13.96875 63.96875 8.890625 62.015625 -L 8.890625 71.09375 -Q 14.796875 72.65625 19.40625 73.4375 -Q 24.03125 74.21875 27.59375 74.21875 -Q 38.234375 74.21875 44.609375 68.875 -Q 50.984375 63.53125 50.984375 54.6875 -Q 50.984375 48.6875 47.625 44.671875 -Q 44.28125 40.671875 37.890625 39.015625 -z -" id="DejaVuSansMono-51"/> - </defs> - <g transform="translate(101.437332 24.663313)scale(0.07 -0.07)"> - <use xlink:href="#DejaVuSansMono-116"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-104"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-114"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-101"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-115"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-104"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-111"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-108"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-100"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-95"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-109"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-97"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-120"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-95"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-100"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-101"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-110"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-115"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-105"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-116"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-121"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-95"/> - <use x="1324.511719" xlink:href="#DejaVuSansMono-72"/> - <use x="1384.716797" xlink:href="#DejaVuSansMono-95"/> - <use x="1444.921875" xlink:href="#DejaVuSansMono-112"/> - <use x="1505.126953" xlink:href="#DejaVuSansMono-95"/> - <use x="1565.332031" xlink:href="#DejaVuSansMono-99"/> - <use x="1625.537109" xlink:href="#DejaVuSansMono-109"/> - <use x="1685.742188" xlink:href="#DejaVuSansMono-51"/> - </g> - </g> - <g id="text_15"> - <!-- threshold_norm_H_p_cm3 --> - <g transform="translate(47.005332 96.930882)scale(0.07 -0.07)"> - <use xlink:href="#DejaVuSansMono-116"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-104"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-114"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-101"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-115"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-104"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-111"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-108"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-100"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-95"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-110"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-111"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-114"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-109"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-95"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-72"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-95"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-112"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-95"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-99"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-109"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-51"/> - </g> - </g> - <g id="text_16"> - <!-- threshold_Z0 --> - <defs> - <path d="M 23.578125 36.625 -Q 23.578125 39.3125 25.453125 41.265625 -Q 27.34375 43.21875 29.984375 43.21875 -Q 32.71875 43.21875 34.671875 41.265625 -Q 36.625 39.3125 36.625 36.625 -Q 36.625 33.890625 34.6875 31.984375 -Q 32.765625 30.078125 29.984375 30.078125 -Q 27.25 30.078125 25.40625 31.9375 -Q 23.578125 33.796875 23.578125 36.625 -z -M 30.078125 66.40625 -Q 23.1875 66.40625 19.796875 58.984375 -Q 16.40625 51.5625 16.40625 36.375 -Q 16.40625 21.234375 19.796875 13.8125 -Q 23.1875 6.390625 30.078125 6.390625 -Q 37.015625 6.390625 40.40625 13.8125 -Q 43.796875 21.234375 43.796875 36.375 -Q 43.796875 51.5625 40.40625 58.984375 -Q 37.015625 66.40625 30.078125 66.40625 -z -M 30.078125 74.21875 -Q 41.75 74.21875 47.734375 64.640625 -Q 53.71875 55.078125 53.71875 36.375 -Q 53.71875 17.71875 47.734375 8.140625 -Q 41.75 -1.421875 30.078125 -1.421875 -Q 18.40625 -1.421875 12.453125 8.140625 -Q 6.5 17.71875 6.5 36.375 -Q 6.5 55.078125 12.453125 64.640625 -Q 18.40625 74.21875 30.078125 74.21875 -z -" id="DejaVuSansMono-48"/> - </defs> - <g transform="translate(148.181028 186.438647)rotate(-90)scale(0.07 -0.07)"> - <use xlink:href="#DejaVuSansMono-116"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-104"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-114"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-101"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-115"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-104"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-111"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-108"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-100"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-95"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-90"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-48"/> - </g> - </g> - </g> - </g> - <defs> - <clipPath id="p23ab05f3c8"> - <rect height="195.048" width="190.512" x="34.02" y="2.268"/> - </clipPath> - </defs> -</svg> diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg deleted file mode 100644 index a5ee0fe738d924018986b00a80bdec2e2311afd4..0000000000000000000000000000000000000000 --- a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg +++ /dev/null @@ -1,2540 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<!-- Created with matplotlib (http://matplotlib.org/) --> -<svg height="226pt" version="1.1" viewBox="0 0 226 226" width="226pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <defs> - <style type="text/css"> -*{stroke-linecap:butt;stroke-linejoin:round;} - </style> - </defs> - <g id="figure_1"> - <g id="patch_1"> - <path d="M 0 226.8 -L 226.8 226.8 -L 226.8 0 -L 0 0 -z -" style="fill:#ffffff;"/> - </g> - <g id="axes_1"> - <g id="patch_2"> - <path d="M 34.02 197.316 -L 224.532 197.316 -L 224.532 2.268 -L 34.02 2.268 -z -" style="fill:#ffffff;"/> - </g> - <g id="PolyCollection_1"> - <defs> - <path d="M 77.714425 -156.365649 -L 77.714425 -14.805175 -L 250.907152 -14.805175 -L 250.907152 -156.365649 -L 250.907152 -156.365649 -L 77.714425 -156.365649 -z -" id="m97efab0c60" style="stroke:#e6e6e6;"/> - </defs> - <g clip-path="url(#p5e3fe66b1e)"> - <use style="fill:#e6e6e6;stroke:#e6e6e6;" x="0" xlink:href="#m97efab0c60" y="226.8"/> - </g> - </g> - <g id="PolyCollection_2"> - <path clip-path="url(#p5e3fe66b1e)" d="M 146.991516 49022.756825 -L 146.991516 85.113175 -L 250.907152 -12.410825 -L 250.907152 49022.756825 -L 250.907152 49022.756825 -L 146.991516 49022.756825 -z -" style="fill:#e6e6e6;stroke:#e6e6e6;"/> - </g> - <g id="PathCollection_1"> - <defs> - <path d="M 0 1 -C 0.265203 1 0.51958 0.894634 0.707107 0.707107 -C 0.894634 0.51958 1 0.265203 1 0 -C 1 -0.265203 0.894634 -0.51958 0.707107 -0.707107 -C 0.51958 -0.894634 0.265203 -1 0 -1 -C -0.265203 -1 -0.51958 -0.894634 -0.707107 -0.707107 -C -0.894634 -0.51958 -1 -0.265203 -1 0 -C -1 0.265203 -0.894634 0.51958 -0.707107 0.707107 -C -0.51958 0.894634 -0.265203 1 0 1 -z -" id="me4af860824" style="stroke:#000000;"/> - </defs> - <g clip-path="url(#p5e3fe66b1e)"> - <use style="stroke:#000000;" x="77.714425" xlink:href="#me4af860824" y="70.434351"/> - </g> - </g> - <g id="PathCollection_2"> - <g clip-path="url(#p5e3fe66b1e)"> - <use style="stroke:#000000;" x="146.991516" xlink:href="#me4af860824" y="85.113175"/> - </g> - </g> - <g id="matplotlib.axis_1"> - <g id="xtick_1"> - <g id="line2d_1"> - <defs> - <path d="M 0 0 -L 0 3.5 -" id="m680b56acf8" style="stroke:#000000;stroke-width:0.8;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="60.395152" xlink:href="#m680b56acf8" y="197.316"/> - </g> - </g> - <g id="text_1"> - <!-- $\mathdefault{10^{-6}}$ --> - <defs> - <path d="M 8.90625 57.09375 -L 8.90625 60.203125 -Q 20.90625 60.203125 27.09375 66.59375 -Q 28.796875 66.59375 29.09375 66.1875 -Q 29.40625 65.796875 29.40625 64 -L 29.40625 7.90625 -Q 29.40625 4.90625 30.84375 4 -Q 32.296875 3.09375 38.703125 3.09375 -L 41.90625 3.09375 -L 41.90625 0 -Q 38.40625 0.296875 25.703125 0.296875 -Q 13 0.296875 9.5 0 -L 9.5 3.09375 -L 12.703125 3.09375 -Q 19 3.09375 20.5 4 -Q 22 4.90625 22 7.90625 -L 22 59.703125 -Q 16.796875 57.09375 8.90625 57.09375 -z -" id="CMUSerif-Roman-31"/> - <path d="M 3.90625 32 -Q 3.90625 46.703125 7.59375 54.703125 -Q 12.796875 66.59375 25 66.59375 -Q 27.59375 66.59375 30.296875 65.890625 -Q 33 65.203125 36.453125 62.5 -Q 39.90625 59.796875 42 55.40625 -Q 46 46.90625 46 32 -Q 46 17.40625 42.296875 9.40625 -Q 36.90625 -2.203125 24.90625 -2.203125 -Q 20.40625 -2.203125 15.84375 0.09375 -Q 11.296875 2.40625 8.40625 7.90625 -Q 3.90625 16.203125 3.90625 32 -z -M 12.203125 33.203125 -Q 12.203125 18.09375 13.296875 12.09375 -Q 14.5 5.59375 17.84375 2.796875 -Q 21.203125 0 24.90625 0 -Q 28.90625 0 32.25 3 -Q 35.59375 6 36.59375 12.5 -Q 37.703125 18.90625 37.703125 33.203125 -Q 37.703125 47.09375 36.703125 52.703125 -Q 35.40625 59.203125 31.90625 61.796875 -Q 28.40625 64.40625 24.90625 64.40625 -Q 23.59375 64.40625 22.1875 64 -Q 20.796875 63.59375 18.796875 62.5 -Q 16.796875 61.40625 15.25 58.59375 -Q 13.703125 55.796875 13 51.59375 -Q 12.203125 46.203125 12.203125 33.203125 -z -" id="CMUSerif-Roman-30"/> - <path d="M 1 18.59375 -L 1 24.5 -L 27.59375 24.5 -L 27.59375 18.59375 -z -" id="CMUSerif-Roman-2d"/> - <path d="M 4.203125 31.59375 -Q 4.203125 47.296875 12.203125 56.9375 -Q 20.203125 66.59375 30.5 66.59375 -Q 36.5 66.59375 39.84375 63.546875 -Q 43.203125 60.5 43.203125 55.796875 -Q 43.203125 53.203125 41.703125 52.09375 -Q 40.203125 51 38.59375 51 -Q 36.796875 51 35.390625 52.203125 -Q 34 53.40625 34 55.59375 -Q 34 60.09375 39.5 60.09375 -Q 36.90625 64.09375 30.703125 64.09375 -Q 28.796875 64.09375 26.84375 63.546875 -Q 24.90625 63 22.34375 61.140625 -Q 19.796875 59.296875 17.84375 56.34375 -Q 15.90625 53.40625 14.546875 47.90625 -Q 13.203125 42.40625 13.203125 35.203125 -L 13.203125 32.796875 -Q 17.296875 42.703125 25.6875 42.703125 -Q 34.09375 42.703125 39.890625 36.296875 -Q 45.703125 29.90625 45.703125 20.40625 -Q 45.703125 10.703125 39.640625 4.25 -Q 33.59375 -2.203125 25.09375 -2.203125 -Q 21.296875 -2.203125 17.84375 -0.59375 -Q 14.40625 1 11.203125 4.59375 -Q 8 8.203125 6.09375 15.140625 -Q 4.203125 22.09375 4.203125 31.59375 -z -M 13.40625 22.59375 -Q 13.40625 12.796875 15.203125 8.09375 -Q 15.5 7.296875 16.140625 6.25 -Q 16.796875 5.203125 17.9375 3.796875 -Q 19.09375 2.40625 21 1.5 -Q 22.90625 0.59375 25.09375 0.59375 -Q 31.796875 0.59375 35 7.09375 -Q 36.703125 10.703125 36.703125 20.5 -Q 36.703125 30.5 34.90625 34.203125 -Q 31.796875 40.40625 25.59375 40.40625 -Q 21.40625 40.40625 18.5 37.5 -Q 15.59375 34.59375 14.5 30.75 -Q 13.40625 26.90625 13.40625 22.59375 -z -" id="CMUSerif-Roman-36"/> - </defs> - <g transform="translate(52.295152 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-2d"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-36"/> - </g> - </g> - </g> - <g id="xtick_2"> - <g id="line2d_2"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="95.033698" xlink:href="#m680b56acf8" y="197.316"/> - </g> - </g> - <g id="text_2"> - <!-- $\mathdefault{10^{-4}}$ --> - <defs> - <path d="M 2.796875 16.5 -L 2.796875 19.59375 -L 33.5 66.5 -Q 34.296875 67.703125 35.5 67.703125 -Q 36.59375 67.703125 36.84375 67.25 -Q 37.09375 66.796875 37.09375 65.09375 -L 37.09375 19.59375 -L 47.09375 19.59375 -L 47.09375 16.5 -L 37.09375 16.5 -L 37.09375 7.796875 -Q 37.09375 4.90625 38.296875 4 -Q 39.5 3.09375 44.703125 3.09375 -L 46.796875 3.09375 -L 46.796875 0 -Q 42.703125 0.296875 33.203125 0.296875 -Q 23.796875 0.296875 19.703125 0 -L 19.703125 3.09375 -L 21.796875 3.09375 -Q 27 3.09375 28.203125 4 -Q 29.40625 4.90625 29.40625 7.796875 -L 29.40625 16.5 -z -M 5.59375 19.59375 -L 30 19.59375 -L 30 56.90625 -z -" id="CMUSerif-Roman-34"/> - </defs> - <g transform="translate(86.933698 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-2d"/> - <use transform="translate(124.064102 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-34"/> - </g> - </g> - </g> - <g id="xtick_3"> - <g id="line2d_3"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="129.672243" xlink:href="#m680b56acf8" y="197.316"/> - </g> - </g> - <g id="text_3"> - <!-- $\mathdefault{10^{-2}}$ --> - <defs> - <path d="M 5 0 -Q 5 1.796875 5.140625 2.34375 -Q 5.296875 2.90625 6.09375 3.703125 -L 25.296875 25.09375 -Q 35.796875 36.90625 35.796875 47.203125 -Q 35.796875 53.90625 32.296875 58.703125 -Q 28.796875 63.5 22.40625 63.5 -Q 18 63.5 14.296875 60.796875 -Q 10.59375 58.09375 8.90625 53.296875 -Q 9.203125 53.40625 10.203125 53.40625 -Q 12.703125 53.40625 14.09375 51.84375 -Q 15.5 50.296875 15.5 48.203125 -Q 15.5 45.5 13.75 44.203125 -Q 12 42.90625 10.296875 42.90625 -Q 9.59375 42.90625 8.6875 43.046875 -Q 7.796875 43.203125 6.390625 44.59375 -Q 5 46 5 48.5 -Q 5 55.5 10.296875 61.046875 -Q 15.59375 66.59375 23.703125 66.59375 -Q 32.90625 66.59375 38.90625 61.140625 -Q 44.90625 55.703125 44.90625 47.203125 -Q 44.90625 44.203125 44 41.5 -Q 43.09375 38.796875 41.890625 36.6875 -Q 40.703125 34.59375 37.5 31.25 -Q 34.296875 27.90625 31.6875 25.5 -Q 29.09375 23.09375 23.296875 18 -L 12.703125 7.703125 -L 30.703125 7.703125 -Q 39.5 7.703125 40.203125 8.5 -Q 41.203125 9.90625 42.40625 17.40625 -L 44.90625 17.40625 -L 42.09375 0 -z -" id="CMUSerif-Roman-32"/> - </defs> - <g transform="translate(121.572243 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-2d"/> - <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-32"/> - </g> - </g> - </g> - <g id="xtick_4"> - <g id="line2d_4"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="164.310789" xlink:href="#m680b56acf8" y="197.316"/> - </g> - </g> - <g id="text_4"> - <!-- $\mathdefault{10^{0}}$ --> - <g transform="translate(157.410789 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-30"/> - </g> - </g> - </g> - <g id="xtick_5"> - <g id="line2d_5"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="198.949334" xlink:href="#m680b56acf8" y="197.316"/> - </g> - </g> - <g id="text_5"> - <!-- $\mathdefault{10^{2}}$ --> - <g transform="translate(192.049334 211.256625)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-32"/> - </g> - </g> - </g> - <g id="text_6"> - <!-- Hydrogen number density $n_{\rm H}$ [cm$^{-3}$] --> - <defs> - <path d="M 3.296875 0 -L 3.296875 3.09375 -L 5.703125 3.09375 -Q 11.09375 3.09375 12.34375 4 -Q 13.59375 4.90625 13.59375 7.796875 -L 13.59375 60.5 -Q 13.59375 63.40625 12.34375 64.296875 -Q 11.09375 65.203125 5.703125 65.203125 -L 3.296875 65.203125 -L 3.296875 68.296875 -Q 6.796875 68 18.09375 68 -Q 29.296875 68 32.796875 68.296875 -L 32.796875 65.203125 -L 30.40625 65.203125 -Q 25 65.203125 23.75 64.296875 -Q 22.5 63.40625 22.5 60.5 -L 22.5 37.09375 -L 52.40625 37.09375 -L 52.40625 60.5 -Q 52.40625 63.40625 51.15625 64.296875 -Q 49.90625 65.203125 44.5 65.203125 -L 42.09375 65.203125 -L 42.09375 68.296875 -Q 45.59375 68 56.90625 68 -Q 68.09375 68 71.59375 68.296875 -L 71.59375 65.203125 -L 69.203125 65.203125 -Q 63.796875 65.203125 62.546875 64.296875 -Q 61.296875 63.40625 61.296875 60.5 -L 61.296875 7.796875 -Q 61.296875 4.90625 62.546875 4 -Q 63.796875 3.09375 69.203125 3.09375 -L 71.59375 3.09375 -L 71.59375 0 -Q 68.09375 0.296875 56.796875 0.296875 -Q 45.59375 0.296875 42.09375 0 -L 42.09375 3.09375 -L 44.5 3.09375 -Q 49.90625 3.09375 51.15625 4 -Q 52.40625 4.90625 52.40625 7.796875 -L 52.40625 34 -L 22.5 34 -L 22.5 7.796875 -Q 22.5 4.90625 23.75 4 -Q 25 3.09375 30.40625 3.09375 -L 32.796875 3.09375 -L 32.796875 0 -Q 29.296875 0.296875 18 0.296875 -Q 6.796875 0.296875 3.296875 0 -z -" id="CMUSerif-Roman-48"/> - <path d="M 1.90625 -12.40625 -Q 1.90625 -10.296875 3.15625 -9.1875 -Q 4.40625 -8.09375 6.09375 -8.09375 -Q 7.90625 -8.09375 9.09375 -9.25 -Q 10.296875 -10.40625 10.296875 -12.296875 -Q 10.296875 -16 6.40625 -16.5 -Q 8.296875 -18.296875 11.09375 -18.296875 -Q 14.09375 -18.296875 16.5 -16.09375 -Q 18.90625 -13.90625 19.953125 -11.796875 -Q 21 -9.703125 22.5 -5.90625 -Q 23.90625 -2.90625 25 0 -L 10 36.5 -Q 9 38.90625 7.5 39.453125 -Q 6 40 1.90625 40 -L 1.90625 43.09375 -Q 6.40625 42.796875 11.59375 42.796875 -Q 14.703125 42.796875 22.5 43.09375 -L 22.5 40 -Q 16.90625 40 16.90625 37.40625 -Q 16.90625 37.09375 17.5 35.59375 -L 28.59375 8.703125 -L 38.703125 33.296875 -Q 39.296875 34.703125 39.296875 35.703125 -Q 39.296875 39.796875 34.59375 40 -L 34.59375 43.09375 -Q 41.203125 42.796875 43.296875 42.796875 -Q 47.40625 42.796875 50.796875 43.09375 -L 50.796875 40 -Q 44.09375 40 41.5 33.59375 -L 23.90625 -9.09375 -Q 19.09375 -20.5 11.09375 -20.5 -Q 7.296875 -20.5 4.59375 -18.140625 -Q 1.90625 -15.796875 1.90625 -12.40625 -z -" id="CMUSerif-Roman-79"/> - <path d="M 3.40625 21.5 -Q 3.40625 31 10.046875 37.59375 -Q 16.703125 44.203125 25.703125 44.203125 -Q 33.296875 44.203125 38.296875 38 -L 38.296875 59.59375 -Q 38.296875 63.296875 37 64.25 -Q 35.703125 65.203125 30.5 65.203125 -L 30.5 68.296875 -L 44.90625 69.40625 -L 44.90625 8.703125 -Q 44.90625 5 46.203125 4.046875 -Q 47.5 3.09375 52.703125 3.09375 -L 52.703125 0 -L 38 -1.09375 -L 38 5.5 -Q 32.796875 -1.09375 24.59375 -1.09375 -Q 16 -1.09375 9.703125 5.5 -Q 3.40625 12.09375 3.40625 21.5 -z -M 11.703125 21.40625 -Q 11.703125 12.09375 14.59375 7.5 -Q 18.59375 1.09375 25.09375 1.09375 -Q 32.5 1.09375 36.90625 8.09375 -Q 38 9.796875 38 11.796875 -L 38 32.296875 -Q 38 34.296875 36.90625 36 -Q 32.796875 42 26.09375 42 -Q 19.09375 42 14.796875 35.59375 -Q 11.703125 30.796875 11.703125 21.40625 -z -" id="CMUSerif-Roman-64"/> - <path d="M 2.796875 0 -L 2.796875 3.09375 -Q 8.09375 3.09375 9.34375 3.75 -Q 10.59375 4.40625 10.59375 7.59375 -L 10.59375 34.40625 -Q 10.59375 38.09375 9.296875 39.046875 -Q 8 40 2.796875 40 -L 2.796875 43.09375 -L 16.703125 44.203125 -L 16.703125 33.203125 -Q 18.09375 37.5 21.09375 40.84375 -Q 24.09375 44.203125 29 44.203125 -Q 32.203125 44.203125 34.296875 42.390625 -Q 36.40625 40.59375 36.40625 38.09375 -Q 36.40625 35.90625 35.046875 34.796875 -Q 33.703125 33.703125 32.09375 33.703125 -Q 30.296875 33.703125 29.046875 34.84375 -Q 27.796875 36 27.796875 38 -Q 27.796875 39.203125 28.34375 40.140625 -Q 28.90625 41.09375 29.34375 41.4375 -Q 29.796875 41.796875 30.09375 41.90625 -Q 29.90625 42 29 42 -Q 23.5 42 20.34375 36.5 -Q 17.203125 31 17.203125 23.203125 -L 17.203125 7.796875 -Q 17.203125 4.90625 18.390625 4 -Q 19.59375 3.09375 24.796875 3.09375 -L 26.90625 3.09375 -L 26.90625 0 -Q 22.90625 0.296875 14.203125 0.296875 -Q 13 0.296875 11.09375 0.25 -Q 9.203125 0.203125 6.703125 0.09375 -Q 4.203125 0 2.796875 0 -z -" id="CMUSerif-Roman-72"/> - <path d="M 16 -1.09375 -Q 2.796875 11.90625 2.796875 21.40625 -Q 2.796875 30.90625 9.25 37.84375 -Q 15.703125 44.796875 25 44.796875 -Q 34.09375 44.796875 40.59375 37.890625 -Q 47.09375 31 47.09375 21.40625 -Q 47.09375 12 40.546875 5.453125 -Q 34 -1.09375 24.90625 -1.09375 -Q 16 -1.09375 2.796875 11.90625 -z -M 11.09375 22.203125 -Q 11.09375 12.5 13.59375 8.09375 -Q 17.5 1.40625 25 1.40625 -Q 28.703125 1.40625 31.796875 3.40625 -Q 34.90625 5.40625 36.59375 8.796875 -Q 38.796875 13.203125 38.796875 22.203125 -Q 38.796875 31.796875 36.203125 36.09375 -Q 32.296875 42.59375 24.90625 42.59375 -Q 21.703125 42.59375 18.546875 40.890625 -Q 15.40625 39.203125 13.5 35.90625 -Q 11.09375 31.5 11.09375 22.203125 -z -" id="CMUSerif-Roman-6f"/> - <path d="M 2.796875 -7.90625 -Q 2.796875 -4.703125 5.25 -1.890625 -Q 7.703125 0.90625 12 2.09375 -Q 7.59375 4.90625 7.59375 11 -Q 7.59375 15.703125 10.703125 19.296875 -Q 6 23.203125 6 29.59375 -Q 6 35.5 10.703125 39.84375 -Q 15.40625 44.203125 22.203125 44.203125 -Q 28.203125 44.203125 32.796875 40.59375 -Q 37.59375 45.296875 43.40625 45.296875 -Q 46 45.296875 47.25 43.6875 -Q 48.5 42.09375 48.5 40.40625 -Q 48.5 38.90625 47.546875 38.15625 -Q 46.59375 37.40625 45.59375 37.40625 -Q 44.40625 37.40625 43.546875 38.203125 -Q 42.703125 39 42.703125 40.296875 -Q 42.703125 42.40625 44.296875 43 -Q 44 43.09375 43.296875 43.09375 -Q 38.40625 43.09375 34.296875 39.203125 -Q 38.40625 35.40625 38.40625 29.5 -Q 38.40625 23.59375 33.703125 19.25 -Q 29 14.90625 22.203125 14.90625 -Q 16.59375 14.90625 12.296875 18 -Q 10.59375 16 10.59375 13.296875 -Q 10.59375 10.796875 12.09375 8.84375 -Q 13.59375 6.90625 15.90625 6.59375 -Q 16.59375 6.5 23.40625 6.5 -Q 27.40625 6.5 29.59375 6.390625 -Q 31.796875 6.296875 34.9375 5.640625 -Q 38.09375 5 40.59375 3.703125 -Q 47.09375 0.09375 47.09375 -7.703125 -Q 47.09375 -13.40625 40.546875 -17 -Q 34 -20.59375 24.90625 -20.59375 -Q 15.703125 -20.59375 9.25 -16.9375 -Q 2.796875 -13.296875 2.796875 -7.90625 -z -M 8 -7.90625 -Q 8 -12 12.84375 -15.140625 -Q 17.703125 -18.296875 25 -18.296875 -Q 32.203125 -18.296875 37.046875 -15.1875 -Q 41.90625 -12.09375 41.90625 -7.90625 -Q 41.90625 -4.90625 40.203125 -3 -Q 38.5 -1.09375 35 -0.34375 -Q 31.5 0.40625 29.046875 0.546875 -Q 26.59375 0.703125 22.09375 0.703125 -L 16.203125 0.703125 -Q 12.796875 0.5 10.390625 -2 -Q 8 -4.5 8 -7.90625 -z -M 13.5 29.5 -Q 13.5 17.203125 22.203125 17.203125 -Q 26.59375 17.203125 29.296875 21.203125 -Q 30.90625 23.90625 30.90625 29.59375 -Q 30.90625 41.90625 22.203125 41.90625 -Q 17.796875 41.90625 15.09375 37.90625 -Q 13.5 35.203125 13.5 29.5 -z -" id="CMUSerif-Roman-67"/> - <path d="M 2.796875 22 -Q 2.796875 31.40625 8.84375 38.09375 -Q 14.90625 44.796875 23.59375 44.796875 -Q 32.40625 44.796875 36.953125 39.09375 -Q 41.5 33.40625 41.5 25.203125 -Q 41.5 23.703125 41.09375 23.390625 -Q 40.703125 23.09375 39 23.09375 -L 11.09375 23.09375 -Q 11.09375 12.90625 14.09375 8.09375 -Q 18.296875 1.40625 25.40625 1.40625 -Q 26.40625 1.40625 27.546875 1.59375 -Q 28.703125 1.796875 31.09375 2.640625 -Q 33.5 3.5 35.59375 5.796875 -Q 37.703125 8.09375 38.90625 11.703125 -Q 39.203125 13.09375 40.203125 13.09375 -Q 41.5 13.09375 41.5 11.90625 -Q 41.5 11 40.546875 9.046875 -Q 39.59375 7.09375 37.796875 4.75 -Q 36 2.40625 32.5 0.65625 -Q 29 -1.09375 24.796875 -1.09375 -Q 16 -1.09375 9.390625 5.546875 -Q 2.796875 12.203125 2.796875 22 -z -M 11.203125 25.203125 -L 34.90625 25.203125 -Q 34.90625 27.296875 34.546875 29.640625 -Q 34.203125 32 33.140625 35.25 -Q 32.09375 38.5 29.640625 40.546875 -Q 27.203125 42.59375 23.59375 42.59375 -Q 22 42.59375 20.25 41.890625 -Q 18.5 41.203125 16.390625 39.546875 -Q 14.296875 37.90625 12.84375 34.15625 -Q 11.40625 30.40625 11.203125 25.203125 -z -" id="CMUSerif-Roman-65"/> - <path d="M 3.203125 0 -L 3.203125 3.09375 -Q 8.5 3.09375 9.75 3.75 -Q 11 4.40625 11 7.59375 -L 11 34.40625 -Q 11 38.09375 9.703125 39.046875 -Q 8.40625 40 3.203125 40 -L 3.203125 43.09375 -L 17.296875 44.203125 -L 17.296875 33.703125 -Q 22 44.203125 32.09375 44.203125 -Q 39.59375 44.203125 42.59375 40.5 -Q 44.796875 38 45.25 35.203125 -Q 45.703125 32.40625 45.703125 25.203125 -L 45.703125 6.09375 -Q 45.796875 4 47.390625 3.546875 -Q 49 3.09375 53.5 3.09375 -L 53.5 0 -Q 43.296875 0.296875 42.296875 0.296875 -Q 41.5 0.296875 31 0 -L 31 3.09375 -Q 36.296875 3.09375 37.546875 3.75 -Q 38.796875 4.40625 38.796875 7.59375 -L 38.796875 30.90625 -Q 38.796875 36 37.25 39 -Q 35.703125 42 31.40625 42 -Q 26.203125 42 22.046875 37.640625 -Q 17.90625 33.296875 17.90625 26 -L 17.90625 7.59375 -Q 17.90625 4.40625 19.15625 3.75 -Q 20.40625 3.09375 25.703125 3.09375 -L 25.703125 0 -Q 15.5 0.296875 14.5 0.296875 -Q 13.703125 0.296875 3.203125 0 -z -" id="CMUSerif-Roman-6e"/> - <path id="CMUSerif-Roman-20"/> - <path d="M 3.203125 40 -L 3.203125 43.09375 -L 17.90625 44.203125 -L 17.90625 11 -Q 17.90625 8.59375 18.09375 7.1875 -Q 18.296875 5.796875 19.09375 4.1875 -Q 19.90625 2.59375 21.796875 1.84375 -Q 23.703125 1.09375 26.703125 1.09375 -Q 32.09375 1.09375 35.4375 5.546875 -Q 38.796875 10 38.796875 16.59375 -L 38.796875 34.40625 -Q 38.796875 38.09375 37.5 39.046875 -Q 36.203125 40 31 40 -L 31 43.09375 -L 45.703125 44.203125 -L 45.703125 8.703125 -Q 45.703125 5 47 4.046875 -Q 48.296875 3.09375 53.5 3.09375 -L 53.5 0 -L 39.09375 -1.09375 -L 39.09375 7.90625 -Q 34.90625 -1.09375 26.203125 -1.09375 -Q 21.796875 -1.09375 18.796875 0 -Q 15.796875 1.09375 14.296875 2.5 -Q 12.796875 3.90625 12 6.59375 -Q 11.203125 9.296875 11.09375 10.9375 -Q 11 12.59375 11 15.796875 -L 11 30.796875 -Q 11 37.59375 10 38.796875 -Q 9 40 3.203125 40 -z -" id="CMUSerif-Roman-75"/> - <path d="M 3.203125 0 -L 3.203125 3.09375 -Q 8.5 3.09375 9.75 3.75 -Q 11 4.40625 11 7.59375 -L 11 34.40625 -Q 11 38.09375 9.703125 39.046875 -Q 8.40625 40 3.203125 40 -L 3.203125 43.09375 -L 17.296875 44.203125 -L 17.296875 33.703125 -Q 22 44.203125 32.09375 44.203125 -Q 43.796875 44.203125 45.40625 34.40625 -Q 47.09375 38.203125 50.6875 41.203125 -Q 54.296875 44.203125 59.90625 44.203125 -Q 67.40625 44.203125 70.40625 40.5 -Q 72.59375 38 73.046875 35.203125 -Q 73.5 32.40625 73.5 25.203125 -L 73.5 6.09375 -Q 73.59375 4 75.1875 3.546875 -Q 76.796875 3.09375 81.296875 3.09375 -L 81.296875 0 -Q 71.09375 0.296875 70.09375 0.296875 -Q 69.296875 0.296875 58.796875 0 -L 58.796875 3.09375 -Q 64.09375 3.09375 65.34375 3.75 -Q 66.59375 4.40625 66.59375 7.59375 -L 66.59375 30.90625 -Q 66.59375 36 65.046875 39 -Q 63.5 42 59.203125 42 -Q 54 42 49.84375 37.640625 -Q 45.703125 33.296875 45.703125 26 -L 45.703125 7.59375 -Q 45.703125 4.40625 46.953125 3.75 -Q 48.203125 3.09375 53.5 3.09375 -L 53.5 0 -Q 43.296875 0.296875 42.296875 0.296875 -Q 41.5 0.296875 31 0 -L 31 3.09375 -Q 36.296875 3.09375 37.546875 3.75 -Q 38.796875 4.40625 38.796875 7.59375 -L 38.796875 30.90625 -Q 38.796875 36 37.25 39 -Q 35.703125 42 31.40625 42 -Q 26.203125 42 22.046875 37.640625 -Q 17.90625 33.296875 17.90625 26 -L 17.90625 7.59375 -Q 17.90625 4.40625 19.15625 3.75 -Q 20.40625 3.09375 25.703125 3.09375 -L 25.703125 0 -Q 15.5 0.296875 14.5 0.296875 -Q 13.703125 0.296875 3.203125 0 -z -" id="CMUSerif-Roman-6d"/> - <path d="M 2.796875 65.203125 -L 2.796875 68.296875 -L 17.203125 69.40625 -L 17.203125 37.703125 -Q 23 44.203125 30.90625 44.203125 -Q 39.5 44.203125 45.796875 37.59375 -Q 52.09375 31 52.09375 21.59375 -Q 52.09375 12.09375 45.5 5.5 -Q 38.90625 -1.09375 29.796875 -1.09375 -Q 21.5 -1.09375 16.703125 6.203125 -Q 13.203125 0.09375 13.09375 0 -L 10.59375 0 -L 10.59375 59.59375 -Q 10.59375 63.296875 9.296875 64.25 -Q 8 65.203125 2.796875 65.203125 -z -M 17.5 11.40625 -Q 17.5 9.296875 18.90625 7.203125 -Q 22.90625 1.09375 29.40625 1.09375 -Q 36.40625 1.09375 40.703125 7.5 -Q 43.796875 12.296875 43.796875 21.703125 -Q 43.796875 31 40.90625 35.59375 -Q 36.90625 42 30.40625 42 -Q 23.09375 42 18.59375 35.59375 -Q 17.5 34 17.5 32 -z -" id="CMUSerif-Roman-62"/> - <path d="M 3.296875 1.296875 -L 3.296875 14.5 -Q 3.296875 15.59375 3.34375 16 -Q 3.40625 16.40625 3.703125 16.703125 -Q 4 17 4.59375 17 -Q 5.296875 17 5.546875 16.703125 -Q 5.796875 16.40625 6 15.296875 -Q 7.5 8.40625 10.75 4.75 -Q 14 1.09375 19.90625 1.09375 -Q 25.5 1.09375 28.34375 3.59375 -Q 31.203125 6.09375 31.203125 10.203125 -Q 31.203125 17.5 20.796875 19.40625 -Q 14.796875 20.59375 12.296875 21.390625 -Q 9.796875 22.203125 7.59375 24 -Q 3.296875 27.5 3.296875 32.5 -Q 3.296875 37.5 7.09375 41.140625 -Q 10.90625 44.796875 19.296875 44.796875 -Q 24.90625 44.796875 28.703125 42 -Q 29.796875 42.90625 30.40625 43.59375 -Q 31.703125 44.796875 32.40625 44.796875 -Q 33.203125 44.796875 33.34375 44.296875 -Q 33.5 43.796875 33.5 42.40625 -L 33.5 32.296875 -Q 33.5 31.203125 33.453125 30.796875 -Q 33.40625 30.40625 33.09375 30.15625 -Q 32.796875 29.90625 32.203125 29.90625 -Q 31.09375 29.90625 31 30.796875 -Q 30.203125 42.90625 19.296875 42.90625 -Q 13.40625 42.90625 10.75 40.65625 -Q 8.09375 38.40625 8.09375 35.296875 -Q 8.09375 33.59375 8.890625 32.296875 -Q 9.703125 31 10.75 30.25 -Q 11.796875 29.5 13.75 28.796875 -Q 15.703125 28.09375 16.890625 27.84375 -Q 18.09375 27.59375 20.40625 27.09375 -Q 28.40625 25.59375 31.796875 22.296875 -Q 36 18.09375 36 12.796875 -Q 36 6.90625 32 2.90625 -Q 28 -1.09375 19.90625 -1.09375 -Q 13.40625 -1.09375 8.90625 3.203125 -Q 8.296875 2.59375 7.84375 2.09375 -Q 7.40625 1.59375 7.25 1.390625 -Q 7.09375 1.203125 7.046875 1.09375 -Q 7 1 6.90625 0.90625 -Q 4.90625 -1.09375 4.40625 -1.09375 -Q 3.59375 -1.09375 3.4375 -0.59375 -Q 3.296875 -0.09375 3.296875 1.296875 -z -" id="CMUSerif-Roman-73"/> - <path d="M 3.296875 0 -L 3.296875 3.09375 -Q 8.59375 3.09375 9.84375 3.75 -Q 11.09375 4.40625 11.09375 7.59375 -L 11.09375 34.5 -Q 11.09375 38.203125 9.84375 39.09375 -Q 8.59375 40 3.703125 40 -L 3.703125 43.09375 -L 17.703125 44.203125 -L 17.703125 7.5 -Q 17.703125 4.5 18.75 3.796875 -Q 19.796875 3.09375 24.703125 3.09375 -L 24.703125 0 -Q 14.5 0.296875 14.296875 0.296875 -Q 12.90625 0.296875 3.296875 0 -z -M 7.5 61.59375 -Q 7.5 63.59375 9.046875 65.25 -Q 10.59375 66.90625 12.796875 66.90625 -Q 15 66.90625 16.546875 65.40625 -Q 18.09375 63.90625 18.09375 61.59375 -Q 18.09375 59.296875 16.546875 57.796875 -Q 15 56.296875 12.796875 56.296875 -Q 10.5 56.296875 9 57.890625 -Q 7.5 59.5 7.5 61.59375 -z -" id="CMUSerif-Roman-69"/> - <path d="M 1.90625 40 -L 1.90625 42.203125 -Q 6.5 42.40625 9.546875 45.65625 -Q 12.59375 48.90625 13.640625 52.90625 -Q 14.703125 56.90625 14.796875 61.5 -L 17.296875 61.5 -L 17.296875 43.09375 -L 31.59375 43.09375 -L 31.59375 40 -L 17.296875 40 -L 17.296875 12.203125 -Q 17.296875 1.40625 24 1.40625 -Q 26.90625 1.40625 28.796875 4.34375 -Q 30.703125 7.296875 30.703125 12.59375 -L 30.703125 18.09375 -L 33.203125 18.09375 -L 33.203125 12.40625 -Q 33.203125 7 30.703125 2.953125 -Q 28.203125 -1.09375 23.296875 -1.09375 -Q 21.5 -1.09375 19.703125 -0.640625 -Q 17.90625 -0.203125 15.59375 1 -Q 13.296875 2.203125 11.84375 5.140625 -Q 10.40625 8.09375 10.40625 12.40625 -L 10.40625 40 -z -" id="CMUSerif-Roman-74"/> - <path d="M 7.71875 1.703125 -Q 7.71875 2.296875 7.8125 2.59375 -L 15.28125 32.421875 -Q 16.015625 35.203125 16.015625 37.3125 -Q 16.015625 41.609375 13.09375 41.609375 -Q 9.96875 41.609375 8.453125 37.859375 -Q 6.9375 34.125 5.515625 28.421875 -Q 5.515625 28.125 5.21875 27.953125 -Q 4.9375 27.78125 4.6875 27.78125 -L 3.515625 27.78125 -Q 3.171875 27.78125 2.921875 28.140625 -Q 2.6875 28.515625 2.6875 28.8125 -Q 3.765625 33.15625 4.765625 36.171875 -Q 5.765625 39.203125 7.890625 41.6875 -Q 10.015625 44.1875 13.1875 44.1875 -Q 16.9375 44.1875 19.8125 41.8125 -Q 22.703125 39.453125 22.703125 35.796875 -Q 25.6875 39.703125 29.6875 41.9375 -Q 33.6875 44.1875 38.1875 44.1875 -Q 41.75 44.1875 44.328125 42.96875 -Q 46.921875 41.75 48.359375 39.28125 -Q 49.8125 36.8125 49.8125 33.40625 -Q 49.8125 29.296875 47.96875 23.484375 -Q 46.140625 17.671875 43.40625 10.5 -Q 42 7.234375 42 4.5 -Q 42 1.515625 44.28125 1.515625 -Q 48.1875 1.515625 50.796875 5.703125 -Q 53.421875 9.90625 54.5 14.703125 -Q 54.6875 15.28125 55.328125 15.28125 -L 56.5 15.28125 -Q 56.890625 15.28125 57.15625 15.03125 -Q 57.421875 14.796875 57.421875 14.40625 -Q 57.421875 14.3125 57.328125 14.109375 -Q 55.953125 8.453125 52.5625 3.65625 -Q 49.171875 -1.125 44.09375 -1.125 -Q 40.578125 -1.125 38.078125 1.296875 -Q 35.59375 3.71875 35.59375 7.171875 -Q 35.59375 9.03125 36.375 11.078125 -Q 37.640625 14.359375 39.28125 18.890625 -Q 40.921875 23.4375 41.96875 27.578125 -Q 43.015625 31.734375 43.015625 34.90625 -Q 43.015625 37.703125 41.859375 39.65625 -Q 40.71875 41.609375 37.984375 41.609375 -Q 34.328125 41.609375 31.25 39.984375 -Q 28.171875 38.375 25.875 35.71875 -Q 23.578125 33.0625 21.6875 29.390625 -L 14.890625 2.203125 -Q 14.546875 0.828125 13.34375 -0.140625 -Q 12.15625 -1.125 10.6875 -1.125 -Q 9.46875 -1.125 8.59375 -0.34375 -Q 7.71875 0.4375 7.71875 1.703125 -z -" id="Cmmi10-6e"/> - <path d="M 3.078125 0 -L 3.078125 3.515625 -Q 13.375 3.515625 13.375 6.6875 -L 13.375 61.625 -Q 13.375 64.796875 3.078125 64.796875 -L 3.078125 68.3125 -L 33.015625 68.3125 -L 33.015625 64.796875 -Q 22.703125 64.796875 22.703125 61.625 -L 22.703125 37.3125 -L 52.203125 37.3125 -L 52.203125 61.625 -Q 52.203125 64.796875 41.890625 64.796875 -L 41.890625 68.3125 -L 71.78125 68.3125 -L 71.78125 64.796875 -Q 61.53125 64.796875 61.53125 61.625 -L 61.53125 6.6875 -Q 61.53125 3.515625 71.78125 3.515625 -L 71.78125 0 -L 41.890625 0 -L 41.890625 3.515625 -Q 52.203125 3.515625 52.203125 6.6875 -L 52.203125 33.796875 -L 22.703125 33.796875 -L 22.703125 6.6875 -Q 22.703125 3.515625 33.015625 3.515625 -L 33.015625 0 -z -" id="Cmr10-48"/> - <path d="M 10.40625 -25 -L 10.40625 75 -L 25.5 75 -L 25.5 72.703125 -L 17.09375 72.703125 -L 17.09375 -22.703125 -L 25.5 -22.703125 -L 25.5 -25 -z -" id="CMUSerif-Roman-5b"/> - <path d="M 16.09375 -1.09375 -Q 3.40625 12.09375 3.40625 21.59375 -Q 3.40625 31.09375 9.65625 37.9375 -Q 15.90625 44.796875 25.09375 44.796875 -Q 31.203125 44.796875 35.796875 41.890625 -Q 40.40625 39 40.40625 34.09375 -Q 40.40625 31.90625 39.09375 30.65625 -Q 37.796875 29.40625 35.796875 29.40625 -Q 33.703125 29.40625 32.453125 30.703125 -Q 31.203125 32 31.203125 34 -Q 31.203125 34.90625 31.5 35.75 -Q 31.796875 36.59375 32.890625 37.546875 -Q 34 38.5 35.90625 38.59375 -Q 32.296875 42.296875 25.203125 42.296875 -Q 20.09375 42.296875 15.890625 37.5 -Q 11.703125 32.703125 11.703125 21.796875 -Q 11.703125 16.09375 13.09375 11.890625 -Q 14.5 7.703125 16.796875 5.546875 -Q 19.09375 3.40625 21.34375 2.40625 -Q 23.59375 1.40625 25.796875 1.40625 -Q 35.59375 1.40625 38.90625 11.90625 -Q 39.203125 12.90625 40.203125 12.90625 -Q 41.5 12.90625 41.5 11.90625 -Q 41.5 11.40625 41.09375 10.15625 -Q 40.703125 8.90625 39.5 6.90625 -Q 38.296875 4.90625 36.546875 3.15625 -Q 34.796875 1.40625 31.75 0.15625 -Q 28.703125 -1.09375 24.90625 -1.09375 -Q 16.09375 -1.09375 3.40625 12.09375 -z -" id="CMUSerif-Roman-63"/> - <path d="M 10.203125 23 -Q 9.375 23 8.828125 23.625 -Q 8.296875 24.265625 8.296875 25 -Q 8.296875 25.734375 8.828125 26.359375 -Q 9.375 27 10.203125 27 -L 67.578125 27 -Q 68.359375 27 68.875 26.359375 -Q 69.390625 25.734375 69.390625 25 -Q 69.390625 24.265625 68.875 23.625 -Q 68.359375 23 67.578125 23 -z -" id="Cmsy10-a1"/> - <path d="M 9.515625 7.71875 -Q 11.859375 4.296875 15.8125 2.640625 -Q 19.78125 0.984375 24.3125 0.984375 -Q 30.125 0.984375 32.5625 5.9375 -Q 35.015625 10.890625 35.015625 17.1875 -Q 35.015625 20.015625 34.5 22.84375 -Q 33.984375 25.6875 32.765625 28.125 -Q 31.546875 30.5625 29.421875 32.03125 -Q 27.296875 33.5 24.21875 33.5 -L 17.578125 33.5 -Q 16.703125 33.5 16.703125 34.421875 -L 16.703125 35.296875 -Q 16.703125 36.078125 17.578125 36.078125 -L 23.09375 36.53125 -Q 26.609375 36.53125 28.921875 39.15625 -Q 31.25 41.796875 32.328125 45.578125 -Q 33.40625 49.359375 33.40625 52.78125 -Q 33.40625 57.5625 31.15625 60.640625 -Q 28.90625 63.71875 24.3125 63.71875 -Q 20.515625 63.71875 17.046875 62.28125 -Q 13.578125 60.84375 11.53125 57.90625 -Q 11.71875 57.953125 11.859375 57.984375 -Q 12.015625 58.015625 12.203125 58.015625 -Q 14.453125 58.015625 15.96875 56.453125 -Q 17.484375 54.890625 17.484375 52.6875 -Q 17.484375 50.53125 15.96875 48.96875 -Q 14.453125 47.40625 12.203125 47.40625 -Q 10.015625 47.40625 8.453125 48.96875 -Q 6.890625 50.53125 6.890625 52.6875 -Q 6.890625 56.984375 9.46875 60.15625 -Q 12.0625 63.328125 16.140625 64.96875 -Q 20.21875 66.609375 24.3125 66.609375 -Q 27.34375 66.609375 30.703125 65.703125 -Q 34.078125 64.796875 36.8125 63.109375 -Q 39.546875 61.421875 41.28125 58.78125 -Q 43.015625 56.15625 43.015625 52.78125 -Q 43.015625 48.578125 41.140625 45.015625 -Q 39.265625 41.453125 35.984375 38.859375 -Q 32.71875 36.28125 28.8125 35.015625 -Q 33.15625 34.1875 37.0625 31.734375 -Q 40.96875 29.296875 43.328125 25.484375 -Q 45.703125 21.6875 45.703125 17.28125 -Q 45.703125 11.765625 42.671875 7.296875 -Q 39.65625 2.828125 34.71875 0.3125 -Q 29.78125 -2.203125 24.3125 -2.203125 -Q 19.625 -2.203125 14.90625 -0.40625 -Q 10.203125 1.375 7.203125 4.9375 -Q 4.203125 8.5 4.203125 13.484375 -Q 4.203125 15.96875 5.859375 17.625 -Q 7.515625 19.28125 10.015625 19.28125 -Q 11.625 19.28125 12.96875 18.53125 -Q 14.3125 17.78125 15.0625 16.40625 -Q 15.828125 15.046875 15.828125 13.484375 -Q 15.828125 11.03125 14.109375 9.375 -Q 12.40625 7.71875 10.015625 7.71875 -z -" id="Cmr10-33"/> - <path d="M 2.09375 -22.703125 -L 10.5 -22.703125 -L 10.5 72.703125 -L 2.09375 72.703125 -L 2.09375 75 -L 17.203125 75 -L 17.203125 -25 -L 2.09375 -25 -z -" id="CMUSerif-Roman-5d"/> - </defs> - <g transform="translate(48.976 221.69725)scale(0.1 -0.1)"> - <use transform="translate(0 0.109375)" xlink:href="#CMUSerif-Roman-48"/> - <use transform="translate(74.999985 0.109375)" xlink:href="#CMUSerif-Roman-79"/> - <use transform="translate(127.699982 0.109375)" xlink:href="#CMUSerif-Roman-64"/> - <use transform="translate(183.199966 0.109375)" xlink:href="#CMUSerif-Roman-72"/> - <use transform="translate(222.299957 0.109375)" xlink:href="#CMUSerif-Roman-6f"/> - <use transform="translate(272.299942 0.109375)" xlink:href="#CMUSerif-Roman-67"/> - <use transform="translate(322.299927 0.109375)" xlink:href="#CMUSerif-Roman-65"/> - <use transform="translate(366.699921 0.109375)" xlink:href="#CMUSerif-Roman-6e"/> - <use transform="translate(422.199905 0.109375)" xlink:href="#CMUSerif-Roman-20"/> - <use transform="translate(455.499893 0.109375)" xlink:href="#CMUSerif-Roman-6e"/> - <use transform="translate(510.999878 0.109375)" xlink:href="#CMUSerif-Roman-75"/> - <use transform="translate(566.499863 0.109375)" xlink:href="#CMUSerif-Roman-6d"/> - <use transform="translate(649.79985 0.109375)" xlink:href="#CMUSerif-Roman-62"/> - <use transform="translate(705.299835 0.109375)" xlink:href="#CMUSerif-Roman-65"/> - <use transform="translate(749.699829 0.109375)" xlink:href="#CMUSerif-Roman-72"/> - <use transform="translate(788.79982 0.109375)" xlink:href="#CMUSerif-Roman-20"/> - <use transform="translate(822.099808 0.109375)" xlink:href="#CMUSerif-Roman-64"/> - <use transform="translate(877.599792 0.109375)" xlink:href="#CMUSerif-Roman-65"/> - <use transform="translate(921.999786 0.109375)" xlink:href="#CMUSerif-Roman-6e"/> - <use transform="translate(977.499771 0.109375)" xlink:href="#CMUSerif-Roman-73"/> - <use transform="translate(1016.899765 0.109375)" xlink:href="#CMUSerif-Roman-69"/> - <use transform="translate(1044.599762 0.109375)" xlink:href="#CMUSerif-Roman-74"/> - <use transform="translate(1083.39975 0.109375)" xlink:href="#CMUSerif-Roman-79"/> - <use transform="translate(1136.099747 0.109375)" xlink:href="#CMUSerif-Roman-20"/> - <use transform="translate(1169.399734 0.109375)" xlink:href="#Cmmi10-6e"/> - <use transform="translate(1229.4095 -16.896875)scale(0.7)" xlink:href="#Cmr10-48"/> - <use transform="translate(1288.286844 0.109375)" xlink:href="#CMUSerif-Roman-20"/> - <use transform="translate(1321.586832 0.109375)" xlink:href="#CMUSerif-Roman-5b"/> - <use transform="translate(1349.386819 0.109375)" xlink:href="#CMUSerif-Roman-63"/> - <use transform="translate(1393.786813 0.109375)" xlink:href="#CMUSerif-Roman-6d"/> - <use transform="translate(1481.550942 38.373438)scale(0.7)" xlink:href="#Cmsy10-a1"/> - <use transform="translate(1535.930825 38.373438)scale(0.7)" xlink:href="#Cmr10-33"/> - <use transform="translate(1577.308168 0.109375)" xlink:href="#CMUSerif-Roman-5d"/> - </g> - </g> - </g> - <g id="matplotlib.axis_2"> - <g id="ytick_1"> - <g id="line2d_6"> - <defs> - <path d="M 0 0 -L -3.5 0 -" id="mefc29cf603" style="stroke:#000000;stroke-width:0.8;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="163.232825"/> - </g> - </g> - <g id="text_7"> - <!-- $\mathdefault{10^{2}}$ --> - <g transform="translate(13.22 166.703137)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-32"/> - </g> - </g> - </g> - <g id="ytick_2"> - <g id="line2d_7"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="114.470825"/> - </g> - </g> - <g id="text_8"> - <!-- $\mathdefault{10^{3}}$ --> - <defs> - <path d="M 4.203125 13.5 -Q 4.203125 16.5 5.890625 17.890625 -Q 7.59375 19.296875 9.796875 19.296875 -Q 12.09375 19.296875 13.75 17.796875 -Q 15.40625 16.296875 15.40625 13.703125 -Q 15.40625 10.90625 13.453125 9.34375 -Q 11.5 7.796875 8.796875 8.203125 -Q 11.203125 4.203125 15.59375 2.390625 -Q 20 0.59375 24.09375 0.59375 -Q 28.40625 0.59375 31.90625 4.296875 -Q 35.40625 8 35.40625 17.09375 -Q 35.40625 24.796875 32.40625 29.25 -Q 29.40625 33.703125 23.5 33.703125 -L 19.09375 33.703125 -Q 17.59375 33.703125 17.140625 33.84375 -Q 16.703125 34 16.703125 34.796875 -Q 16.703125 35.796875 18.203125 36 -Q 19.703125 36 22.09375 36.296875 -Q 27.90625 36.5 31 41.5 -Q 33.796875 46.203125 33.796875 52.90625 -Q 33.796875 59 30.890625 61.546875 -Q 28 64.09375 24.203125 64.09375 -Q 20.703125 64.09375 16.84375 62.640625 -Q 13 61.203125 10.90625 57.90625 -Q 17.09375 57.90625 17.09375 52.90625 -Q 17.09375 50.703125 15.6875 49.25 -Q 14.296875 47.796875 12 47.796875 -Q 9.796875 47.796875 8.34375 49.1875 -Q 6.90625 50.59375 6.90625 53 -Q 6.90625 58.703125 12 62.640625 -Q 17.09375 66.59375 24.59375 66.59375 -Q 32 66.59375 37.5 62.6875 -Q 43 58.796875 43 52.796875 -Q 43 46.90625 39.09375 42.046875 -Q 35.203125 37.203125 29 35.203125 -Q 36.59375 33.703125 41.140625 28.546875 -Q 45.703125 23.40625 45.703125 17.09375 -Q 45.703125 9.296875 39.546875 3.546875 -Q 33.40625 -2.203125 24.40625 -2.203125 -Q 16.09375 -2.203125 10.140625 2.296875 -Q 4.203125 6.796875 4.203125 13.5 -z -" id="CMUSerif-Roman-33"/> - </defs> - <g transform="translate(13.22 117.941137)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-33"/> - </g> - </g> - </g> - <g id="ytick_3"> - <g id="line2d_8"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="65.708825"/> - </g> - </g> - <g id="text_9"> - <!-- $\mathdefault{10^{4}}$ --> - <g transform="translate(13.22 69.179137)scale(0.1 -0.1)"> - <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-34"/> - </g> - </g> - </g> - <g id="ytick_4"> - <g id="line2d_9"> - <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="16.946825"/> - </g> - </g> - <g id="text_10"> - <!-- $\mathdefault{10^{5}}$ --> - <defs> - <path d="M 5 16.09375 -Q 5 19.09375 6.59375 20.25 -Q 8.203125 21.40625 9.90625 21.40625 -Q 12.203125 21.40625 13.546875 19.953125 -Q 14.90625 18.5 14.90625 16.5 -Q 14.90625 14.5 13.546875 13.046875 -Q 12.203125 11.59375 9.90625 11.59375 -Q 8.796875 11.59375 8.203125 11.796875 -Q 9.5 7.203125 13.546875 3.890625 -Q 17.59375 0.59375 22.90625 0.59375 -Q 29.59375 0.59375 33.59375 7.09375 -Q 36 11.296875 36 20.796875 -Q 36 29.203125 34.203125 33.40625 -Q 31.40625 39.796875 25.703125 39.796875 -Q 17.59375 39.796875 12.796875 32.796875 -Q 12.203125 31.90625 11.5 31.90625 -Q 10.5 31.90625 10.296875 32.453125 -Q 10.09375 33 10.09375 34.5 -L 10.09375 64.09375 -Q 10.09375 66.5 11.09375 66.5 -Q 11.5 66.5 12.296875 66.203125 -Q 18.59375 63.40625 25.59375 63.40625 -Q 32.796875 63.40625 39.203125 66.296875 -Q 39.703125 66.59375 40 66.59375 -Q 41 66.59375 41 65.5 -Q 41 65.09375 40.203125 63.9375 -Q 39.40625 62.796875 37.703125 61.296875 -Q 36 59.796875 33.796875 58.390625 -Q 31.59375 57 28.390625 56.046875 -Q 25.203125 55.09375 21.703125 55.09375 -Q 17.5 55.09375 13.203125 56.40625 -L 13.203125 36.90625 -Q 18.40625 42 25.90625 42 -Q 33.90625 42 39.40625 35.546875 -Q 44.90625 29.09375 44.90625 20.09375 -Q 44.90625 10.703125 38.40625 4.25 -Q 31.90625 -2.203125 23.09375 -2.203125 -Q 15.09375 -2.203125 10.046875 3.5 -Q 5 9.203125 5 16.09375 -z -" id="CMUSerif-Roman-35"/> - </defs> - <g transform="translate(13.22 20.417137)scale(0.1 -0.1)"> - <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/> - <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/> - <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-35"/> - </g> - </g> - </g> - <g id="ytick_5"> - <g id="line2d_10"> - <defs> - <path d="M 0 0 -L -2 0 -" id="m75a05ee26c" style="stroke:#000000;stroke-width:0.6;"/> - </defs> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="197.316"/> - </g> - </g> - </g> - <g id="ytick_6"> - <g id="line2d_11"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="188.729438"/> - </g> - </g> - </g> - <g id="ytick_7"> - <g id="line2d_12"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="182.637175"/> - </g> - </g> - </g> - <g id="ytick_8"> - <g id="line2d_13"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="177.911649"/> - </g> - </g> - </g> - <g id="ytick_9"> - <g id="line2d_14"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="174.050613"/> - </g> - </g> - </g> - <g id="ytick_10"> - <g id="line2d_15"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="170.786154"/> - </g> - </g> - </g> - <g id="ytick_11"> - <g id="line2d_16"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="167.958351"/> - </g> - </g> - </g> - <g id="ytick_12"> - <g id="line2d_17"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="165.464051"/> - </g> - </g> - </g> - <g id="ytick_13"> - <g id="line2d_18"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="148.554"/> - </g> - </g> - </g> - <g id="ytick_14"> - <g id="line2d_19"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="139.967438"/> - </g> - </g> - </g> - <g id="ytick_15"> - <g id="line2d_20"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="133.875175"/> - </g> - </g> - </g> - <g id="ytick_16"> - <g id="line2d_21"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="129.149649"/> - </g> - </g> - </g> - <g id="ytick_17"> - <g id="line2d_22"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="125.288613"/> - </g> - </g> - </g> - <g id="ytick_18"> - <g id="line2d_23"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="122.024154"/> - </g> - </g> - </g> - <g id="ytick_19"> - <g id="line2d_24"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="119.196351"/> - </g> - </g> - </g> - <g id="ytick_20"> - <g id="line2d_25"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="116.702051"/> - </g> - </g> - </g> - <g id="ytick_21"> - <g id="line2d_26"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="99.792"/> - </g> - </g> - </g> - <g id="ytick_22"> - <g id="line2d_27"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="91.205438"/> - </g> - </g> - </g> - <g id="ytick_23"> - <g id="line2d_28"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="85.113175"/> - </g> - </g> - </g> - <g id="ytick_24"> - <g id="line2d_29"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="80.387649"/> - </g> - </g> - </g> - <g id="ytick_25"> - <g id="line2d_30"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="76.526613"/> - </g> - </g> - </g> - <g id="ytick_26"> - <g id="line2d_31"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="73.262154"/> - </g> - </g> - </g> - <g id="ytick_27"> - <g id="line2d_32"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="70.434351"/> - </g> - </g> - </g> - <g id="ytick_28"> - <g id="line2d_33"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="67.940051"/> - </g> - </g> - </g> - <g id="ytick_29"> - <g id="line2d_34"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="51.03"/> - </g> - </g> - </g> - <g id="ytick_30"> - <g id="line2d_35"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="42.443438"/> - </g> - </g> - </g> - <g id="ytick_31"> - <g id="line2d_36"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="36.351175"/> - </g> - </g> - </g> - <g id="ytick_32"> - <g id="line2d_37"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="31.625649"/> - </g> - </g> - </g> - <g id="ytick_33"> - <g id="line2d_38"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="27.764613"/> - </g> - </g> - </g> - <g id="ytick_34"> - <g id="line2d_39"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="24.500154"/> - </g> - </g> - </g> - <g id="ytick_35"> - <g id="line2d_40"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="21.672351"/> - </g> - </g> - </g> - <g id="ytick_36"> - <g id="line2d_41"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="19.178051"/> - </g> - </g> - </g> - <g id="ytick_37"> - <g id="line2d_42"> - <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="2.268"/> - </g> - </g> - </g> - <g id="text_11"> - <!-- Temperature $T$ [K] --> - <defs> - <path d="M 3.59375 45.203125 -L 5.5 67.703125 -L 66.59375 67.703125 -L 68.5 45.203125 -L 66 45.203125 -Q 65.59375 49.703125 65.25 52.296875 -Q 64.90625 54.90625 64.046875 57.34375 -Q 63.203125 59.796875 62.140625 61 -Q 61.09375 62.203125 59.09375 63.140625 -Q 57.09375 64.09375 54.5 64.34375 -Q 51.90625 64.59375 48 64.59375 -Q 43.40625 64.59375 42.296875 64.40625 -Q 41.09375 64.09375 40.796875 63.25 -Q 40.5 62.40625 40.5 60.59375 -L 40.5 7.90625 -Q 40.5 5.90625 40.890625 5.09375 -Q 41.296875 4.296875 43.59375 3.6875 -Q 45.90625 3.09375 51 3.09375 -L 55 3.09375 -L 55 0 -Q 50.90625 0.296875 36 0.296875 -Q 21.203125 0.296875 17.09375 0 -L 17.09375 3.09375 -L 21.09375 3.09375 -Q 26.203125 3.09375 28.5 3.6875 -Q 30.796875 4.296875 31.1875 5.09375 -Q 31.59375 5.90625 31.59375 7.90625 -L 31.59375 60.59375 -Q 31.59375 62 31.5 62.59375 -Q 31.40625 63.203125 30.953125 63.703125 -Q 30.5 64.203125 29.5 64.390625 -Q 28.5 64.59375 24.09375 64.59375 -Q 20.203125 64.59375 17.59375 64.34375 -Q 15 64.09375 13 63.140625 -Q 11 62.203125 9.953125 61 -Q 8.90625 59.796875 8.046875 57.34375 -Q 7.203125 54.90625 6.84375 52.296875 -Q 6.5 49.703125 6.09375 45.203125 -z -" id="CMUSerif-Roman-54"/> - <path d="M 2.796875 -16.296875 -Q 8.09375 -16.296875 9.34375 -15.640625 -Q 10.59375 -15 10.59375 -11.796875 -L 10.59375 35 -Q 10.59375 38.296875 9.34375 39.140625 -Q 8.09375 40 2.796875 40 -L 2.796875 43.09375 -L 17.203125 44.203125 -L 17.203125 37.59375 -Q 23.203125 44.203125 31.203125 44.203125 -Q 39.703125 44.203125 45.890625 37.59375 -Q 52.09375 31 52.09375 21.59375 -Q 52.09375 12.09375 45.5 5.5 -Q 38.90625 -1.09375 29.796875 -1.09375 -Q 24.796875 -1.09375 21.4375 1.453125 -Q 18.09375 4 17.5 5.90625 -L 17.5 5 -L 17.5 -11.796875 -Q 17.5 -15 18.75 -15.640625 -Q 20 -16.296875 25.296875 -16.296875 -L 25.296875 -19.40625 -Q 14.796875 -19.09375 14 -19.09375 -Q 13 -19.09375 2.796875 -19.40625 -z -M 17.5 11.40625 -Q 17.5 9.90625 17.703125 9.34375 -Q 17.90625 8.796875 18.90625 7.203125 -Q 22.90625 1.09375 29.40625 1.09375 -Q 35.09375 1.09375 39.4375 6.9375 -Q 43.796875 12.796875 43.796875 21.59375 -Q 43.796875 30 39.84375 35.84375 -Q 35.90625 41.703125 30.40625 41.703125 -Q 26.5 41.703125 23.09375 39.59375 -Q 19.703125 37.5 17.5 33.703125 -z -" id="CMUSerif-Roman-70"/> - <path d="M 4.203125 9.5 -Q 4.203125 18 14.203125 22.5 -Q 20.203125 25.40625 32.59375 26.09375 -L 32.59375 29.796875 -Q 32.59375 36 29.34375 39.296875 -Q 26.09375 42.59375 22 42.59375 -Q 14.703125 42.59375 11.203125 38 -Q 14.203125 37.90625 15.25 36.40625 -Q 16.296875 34.90625 16.296875 33.40625 -Q 16.296875 31.40625 15.046875 30.09375 -Q 13.796875 28.796875 11.703125 28.796875 -Q 9.703125 28.796875 8.390625 30.046875 -Q 7.09375 31.296875 7.09375 33.5 -Q 7.09375 38.40625 11.5 41.59375 -Q 15.90625 44.796875 22.203125 44.796875 -Q 30.40625 44.796875 35.90625 39.296875 -Q 37.59375 37.59375 38.4375 35.390625 -Q 39.296875 33.203125 39.390625 31.75 -Q 39.5 30.296875 39.5 27.5 -L 39.5 7.5 -Q 39.5 6.90625 39.703125 5.953125 -Q 39.90625 5 40.796875 3.75 -Q 41.703125 2.5 43.203125 2.5 -Q 46.796875 2.5 46.796875 8.90625 -L 46.796875 14.5 -L 49.296875 14.5 -L 49.296875 8.90625 -Q 49.296875 3.59375 46.5 1.5 -Q 43.703125 -0.59375 41.09375 -0.59375 -Q 37.796875 -0.59375 35.6875 1.84375 -Q 33.59375 4.296875 33.296875 7.59375 -Q 31.796875 3.796875 28.34375 1.34375 -Q 24.90625 -1.09375 20.203125 -1.09375 -Q 16.59375 -1.09375 13.1875 -0.1875 -Q 9.796875 0.703125 7 3.203125 -Q 4.203125 5.703125 4.203125 9.5 -z -M 11.90625 9.59375 -Q 11.90625 5.90625 14.546875 3.5 -Q 17.203125 1.09375 20.90625 1.09375 -Q 25.09375 1.09375 28.84375 4.34375 -Q 32.59375 7.59375 32.59375 14 -L 32.59375 24 -Q 21.5 23.59375 16.703125 19.1875 -Q 11.90625 14.796875 11.90625 9.59375 -z -" id="CMUSerif-Roman-61"/> - <path d="M 4.59375 1.3125 -Q 4.640625 1.5625 4.8125 2.1875 -Q 4.984375 2.828125 5.25 3.171875 -Q 5.515625 3.515625 6 3.515625 -Q 14.59375 3.515625 17.390625 4 -Q 20.0625 4.6875 20.609375 6.890625 -L 34.28125 61.8125 -Q 34.71875 63.03125 34.71875 64.015625 -Q 34.71875 64.796875 31.203125 64.796875 -L 25.390625 64.796875 -Q 18.703125 64.796875 15.0625 62.734375 -Q 11.421875 60.6875 9.71875 57.3125 -Q 8.015625 53.953125 5.328125 46.296875 -Q 4.984375 45.40625 4.296875 45.40625 -L 3.421875 45.40625 -Q 2.390625 45.40625 2.390625 46.6875 -L 9.515625 67.390625 -Q 9.71875 68.3125 10.5 68.3125 -L 69.578125 68.3125 -Q 70.609375 68.3125 70.609375 67 -L 67.28125 46.296875 -Q 67.28125 46 66.9375 45.703125 -Q 66.609375 45.40625 66.3125 45.40625 -L 65.375 45.40625 -Q 64.40625 45.40625 64.40625 46.6875 -Q 65.484375 53.765625 65.484375 56.6875 -Q 65.484375 60.203125 64.015625 62 -Q 62.546875 63.8125 60.203125 64.296875 -Q 57.859375 64.796875 54.109375 64.796875 -L 48.1875 64.796875 -Q 45.515625 64.796875 44.578125 64.296875 -Q 43.65625 63.8125 43.015625 61.375 -L 29.296875 6.5 -Q 29.25 6.296875 29.21875 6.09375 -Q 29.203125 5.90625 29.109375 5.609375 -Q 29.109375 4.34375 30.609375 4 -Q 33.203125 3.515625 41.703125 3.515625 -Q 42.671875 3.515625 42.671875 2.203125 -Q 42.328125 0.78125 42.125 0.390625 -Q 41.9375 0 41.015625 0 -L 5.609375 0 -Q 4.59375 0 4.59375 1.3125 -z -" id="Cmmi10-54"/> - <path d="M 3.296875 0 -L 3.296875 3.09375 -L 5.703125 3.09375 -Q 11.09375 3.09375 12.34375 4 -Q 13.59375 4.90625 13.59375 7.796875 -L 13.59375 60.5 -Q 13.59375 63.40625 12.34375 64.296875 -Q 11.09375 65.203125 5.703125 65.203125 -L 3.296875 65.203125 -L 3.296875 68.296875 -Q 6.796875 68 18.09375 68 -Q 29.296875 68 32.796875 68.296875 -L 32.796875 65.203125 -L 30.40625 65.203125 -Q 25 65.203125 23.75 64.296875 -Q 22.5 63.40625 22.5 60.5 -L 22.5 28.703125 -L 53.796875 58.59375 -Q 55.59375 60.203125 55.59375 61.90625 -Q 55.59375 62.5 55.34375 63.140625 -Q 55.09375 63.796875 53.9375 64.5 -Q 52.796875 65.203125 51 65.203125 -L 51 68.296875 -Q 54.40625 68 63.703125 68 -Q 69.59375 68 72.203125 68.296875 -L 72.203125 65.203125 -Q 63.90625 65.09375 58 59.203125 -L 40 41.90625 -L 63.09375 7.90625 -Q 65.203125 4.796875 67.25 3.9375 -Q 69.296875 3.09375 73.59375 3.09375 -L 73.59375 0 -Q 67.296875 0.296875 62.09375 0.296875 -Q 51.296875 0.296875 47.796875 0 -L 47.796875 3.09375 -Q 53.703125 3.09375 53.703125 6.09375 -Q 53.703125 7.203125 52.203125 9.5 -L 34.09375 36.296875 -L 22.5 25.296875 -L 22.5 7.796875 -Q 22.5 4.90625 23.75 4 -Q 25 3.09375 30.40625 3.09375 -L 32.796875 3.09375 -L 32.796875 0 -Q 29.296875 0.296875 18 0.296875 -Q 6.796875 0.296875 3.296875 0 -z -" id="CMUSerif-Roman-4b"/> - </defs> - <g transform="translate(8.72 141.042)rotate(-90)scale(0.1 -0.1)"> - <use xlink:href="#CMUSerif-Roman-54"/> - <use transform="translate(72.199982 0)" xlink:href="#CMUSerif-Roman-65"/> - <use transform="translate(116.599976 0)" xlink:href="#CMUSerif-Roman-6d"/> - <use transform="translate(199.899963 0)" xlink:href="#CMUSerif-Roman-70"/> - <use transform="translate(255.399948 0)" xlink:href="#CMUSerif-Roman-65"/> - <use transform="translate(299.799942 0)" xlink:href="#CMUSerif-Roman-72"/> - <use transform="translate(338.899933 0)" xlink:href="#CMUSerif-Roman-61"/> - <use transform="translate(388.899918 0)" xlink:href="#CMUSerif-Roman-74"/> - <use transform="translate(427.699905 0)" xlink:href="#CMUSerif-Roman-75"/> - <use transform="translate(483.19989 0)" xlink:href="#CMUSerif-Roman-72"/> - <use transform="translate(522.299881 0)" xlink:href="#CMUSerif-Roman-65"/> - <use transform="translate(566.699875 0)" xlink:href="#CMUSerif-Roman-20"/> - <use transform="translate(599.999863 0)" xlink:href="#Cmmi10-54"/> - <use transform="translate(658.3983 0)" xlink:href="#CMUSerif-Roman-20"/> - <use transform="translate(691.698288 0)" xlink:href="#CMUSerif-Roman-5b"/> - <use transform="translate(719.498276 0)" xlink:href="#CMUSerif-Roman-4b"/> - <use transform="translate(797.198257 0)" xlink:href="#CMUSerif-Roman-5d"/> - </g> - </g> - </g> - <g id="line2d_43"> - <path clip-path="url(#p5e3fe66b1e)" d="M 77.714425 70.434351 -L 227.8 70.434351 -L 227.8 70.434351 -" style="fill:none;stroke:#000000;stroke-linecap:square;"/> - </g> - <g id="line2d_44"> - <path clip-path="url(#p5e3fe66b1e)" d="M 146.991516 85.113175 -L 227.8 9.275054 -L 227.8 9.275054 -" style="fill:none;stroke:#000000;stroke-linecap:square;"/> - </g> - <g id="line2d_45"> - <path clip-path="url(#p5e3fe66b1e)" d="M -1 70.434351 -L 77.714425 70.434351 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_46"> - <path clip-path="url(#p5e3fe66b1e)" d="M -1 85.113175 -L 146.991516 85.113175 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_47"> - <path clip-path="url(#p5e3fe66b1e)" d="M 77.714425 197.316 -L 77.714425 70.434351 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_48"> - <path clip-path="url(#p5e3fe66b1e)" d="M 146.991516 197.316 -L 146.991516 85.113175 -" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_49"> - <path clip-path="url(#p5e3fe66b1e)" d="M 68.658545 43.904505 -L 103.297091 43.904505 -" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="line2d_50"> - <path clip-path="url(#p5e3fe66b1e)" d="M 155.254909 57.122263 -L 189.893455 24.614263 -" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/> - </g> - <g id="patch_3"> - <path d="M 34.02 197.316 -L 34.02 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_4"> - <path d="M 224.532 197.316 -L 224.532 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_5"> - <path d="M 34.02 197.316 -L 224.532 197.316 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="patch_6"> - <path d="M 34.02 2.268 -L 224.532 2.268 -" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> - </g> - <g id="text_12"> - <!-- $n_{\rm H}$^Cool_gamma_effective - 1 --> - <defs> - <path d="M 34.421875 72.90625 -L 56.6875 45.703125 -L 48 45.703125 -L 30.078125 64.984375 -L 12.203125 45.703125 -L 3.515625 45.703125 -L 25.78125 72.90625 -z -" id="DejaVuSansMono-5e"/> - <path d="M 52.390625 2.59375 -Q 48.640625 0.59375 44.671875 -0.40625 -Q 40.71875 -1.421875 36.28125 -1.421875 -Q 22.265625 -1.421875 14.515625 8.484375 -Q 6.78125 18.40625 6.78125 36.375 -Q 6.78125 54.25 14.5625 64.234375 -Q 22.359375 74.21875 36.28125 74.21875 -Q 40.71875 74.21875 44.671875 73.21875 -Q 48.640625 72.21875 52.390625 70.21875 -L 52.390625 60.109375 -Q 48.78125 63.09375 44.625 64.65625 -Q 40.484375 66.21875 36.28125 66.21875 -Q 26.65625 66.21875 21.875 58.796875 -Q 17.09375 51.375 17.09375 36.375 -Q 17.09375 21.4375 21.875 14.015625 -Q 26.65625 6.59375 36.28125 6.59375 -Q 40.578125 6.59375 44.703125 8.15625 -Q 48.828125 9.71875 52.390625 12.703125 -z -" id="DejaVuSansMono-43"/> - <path d="M 30.078125 48.390625 -Q 23.25 48.390625 19.734375 43.0625 -Q 16.21875 37.75 16.21875 27.296875 -Q 16.21875 16.890625 19.734375 11.546875 -Q 23.25 6.203125 30.078125 6.203125 -Q 36.96875 6.203125 40.484375 11.546875 -Q 44 16.890625 44 27.296875 -Q 44 37.75 40.484375 43.0625 -Q 36.96875 48.390625 30.078125 48.390625 -z -M 30.078125 56 -Q 41.453125 56 47.484375 48.625 -Q 53.515625 41.265625 53.515625 27.296875 -Q 53.515625 13.28125 47.5 5.921875 -Q 41.5 -1.421875 30.078125 -1.421875 -Q 18.703125 -1.421875 12.6875 5.921875 -Q 6.6875 13.28125 6.6875 27.296875 -Q 6.6875 41.265625 12.6875 48.625 -Q 18.703125 56 30.078125 56 -z -" id="DejaVuSansMono-6f"/> - <path d="M 31.203125 19.828125 -Q 31.203125 13.765625 33.421875 10.6875 -Q 35.640625 7.625 39.984375 7.625 -L 50.484375 7.625 -L 50.484375 0 -L 39.109375 0 -Q 31.0625 0 26.640625 5.171875 -Q 22.21875 10.359375 22.21875 19.828125 -L 22.21875 69.484375 -L 7.8125 69.484375 -L 7.8125 76.515625 -L 31.203125 76.515625 -z -" id="DejaVuSansMono-6c"/> - <path d="M 60.203125 -19.671875 -L 60.203125 -23.578125 -L 0 -23.578125 -L 0 -19.671875 -z -" id="DejaVuSansMono-5f"/> - <path d="M 41.890625 27.78125 -Q 41.890625 37.890625 38.59375 43.140625 -Q 35.296875 48.390625 29 48.390625 -Q 22.40625 48.390625 18.9375 43.140625 -Q 15.484375 37.890625 15.484375 27.78125 -Q 15.484375 17.671875 18.96875 12.375 -Q 22.46875 7.078125 29.109375 7.078125 -Q 35.296875 7.078125 38.59375 12.390625 -Q 41.890625 17.71875 41.890625 27.78125 -z -M 50.875 3.515625 -Q 50.875 -8.796875 45.0625 -15.140625 -Q 39.265625 -21.484375 27.984375 -21.484375 -Q 24.265625 -21.484375 20.203125 -20.796875 -Q 16.15625 -20.125 12.109375 -18.796875 -L 12.109375 -9.90625 -Q 16.890625 -12.15625 20.796875 -13.234375 -Q 24.703125 -14.3125 27.984375 -14.3125 -Q 35.25 -14.3125 38.5625 -10.34375 -Q 41.890625 -6.390625 41.890625 2.203125 -L 41.890625 2.59375 -L 41.890625 8.6875 -Q 39.75 4.109375 36.03125 1.859375 -Q 32.328125 -0.390625 27 -0.390625 -Q 17.4375 -0.390625 11.71875 7.265625 -Q 6 14.9375 6 27.78125 -Q 6 40.671875 11.71875 48.328125 -Q 17.4375 56 27 56 -Q 32.28125 56 35.9375 53.90625 -Q 39.59375 51.8125 41.890625 47.40625 -L 41.890625 54.5 -L 50.875 54.5 -z -" id="DejaVuSansMono-67"/> - <path d="M 34.28125 27.484375 -L 31.296875 27.484375 -Q 23.4375 27.484375 19.453125 24.71875 -Q 15.484375 21.96875 15.484375 16.5 -Q 15.484375 11.578125 18.453125 8.84375 -Q 21.4375 6.109375 26.703125 6.109375 -Q 34.125 6.109375 38.375 11.25 -Q 42.625 16.40625 42.671875 25.484375 -L 42.671875 27.484375 -z -M 51.703125 31.203125 -L 51.703125 0 -L 42.671875 0 -L 42.671875 8.109375 -Q 39.796875 3.21875 35.421875 0.890625 -Q 31.0625 -1.421875 24.8125 -1.421875 -Q 16.453125 -1.421875 11.46875 3.296875 -Q 6.5 8.015625 6.5 15.921875 -Q 6.5 25.046875 12.625 29.78125 -Q 18.75 34.515625 30.609375 34.515625 -L 42.671875 34.515625 -L 42.671875 35.9375 -Q 42.625 42.484375 39.34375 45.4375 -Q 36.078125 48.390625 28.90625 48.390625 -Q 24.3125 48.390625 19.625 47.0625 -Q 14.9375 45.75 10.5 43.21875 -L 10.5 52.203125 -Q 15.484375 54.109375 20.046875 55.046875 -Q 24.609375 56 28.90625 56 -Q 35.6875 56 40.5 54 -Q 45.3125 52 48.296875 48 -Q 50.140625 45.5625 50.921875 41.96875 -Q 51.703125 38.375 51.703125 31.203125 -z -" id="DejaVuSansMono-61"/> - <path d="M 33.015625 49.125 -Q 34.671875 52.640625 37.234375 54.3125 -Q 39.796875 56 43.40625 56 -Q 50 56 52.703125 50.890625 -Q 55.421875 45.796875 55.421875 31.6875 -L 55.421875 0 -L 47.21875 0 -L 47.21875 31.296875 -Q 47.21875 42.875 45.921875 45.671875 -Q 44.625 48.484375 41.21875 48.484375 -Q 37.3125 48.484375 35.859375 45.484375 -Q 34.421875 42.484375 34.421875 31.296875 -L 34.421875 0 -L 26.21875 0 -L 26.21875 31.296875 -Q 26.21875 43.015625 24.828125 45.75 -Q 23.4375 48.484375 19.828125 48.484375 -Q 16.265625 48.484375 14.875 45.484375 -Q 13.484375 42.484375 13.484375 31.296875 -L 13.484375 0 -L 5.328125 0 -L 5.328125 54.6875 -L 13.484375 54.6875 -L 13.484375 50 -Q 15.09375 52.9375 17.5 54.46875 -Q 19.921875 56 23 56 -Q 26.703125 56 29.171875 54.296875 -Q 31.640625 52.59375 33.015625 49.125 -z -" id="DejaVuSansMono-6d"/> - <path d="M 54.296875 29.59375 -L 54.296875 25.203125 -L 15.375 25.203125 -L 15.375 24.90625 -Q 15.375 15.96875 20.03125 11.078125 -Q 24.703125 6.203125 33.203125 6.203125 -Q 37.5 6.203125 42.1875 7.5625 -Q 46.875 8.9375 52.203125 11.71875 -L 52.203125 2.78125 -Q 47.078125 0.6875 42.3125 -0.359375 -Q 37.546875 -1.421875 33.109375 -1.421875 -Q 20.359375 -1.421875 13.171875 6.21875 -Q 6 13.875 6 27.296875 -Q 6 40.375 13.03125 48.1875 -Q 20.0625 56 31.78125 56 -Q 42.234375 56 48.265625 48.921875 -Q 54.296875 41.84375 54.296875 29.59375 -z -M 45.3125 32.234375 -Q 45.125 40.140625 41.578125 44.265625 -Q 38.03125 48.390625 31.390625 48.390625 -Q 24.90625 48.390625 20.703125 44.09375 -Q 16.5 39.796875 15.71875 32.171875 -z -" id="DejaVuSansMono-65"/> - <path d="M 51.90625 75.984375 -L 51.90625 68.5 -L 41.703125 68.5 -Q 36.859375 68.5 34.984375 66.515625 -Q 33.109375 64.546875 33.109375 59.515625 -L 33.109375 54.6875 -L 51.90625 54.6875 -L 51.90625 47.703125 -L 33.109375 47.703125 -L 33.109375 0 -L 24.125 0 -L 24.125 47.703125 -L 9.515625 47.703125 -L 9.515625 54.6875 -L 24.125 54.6875 -L 24.125 58.5 -Q 24.125 67.484375 28.25 71.734375 -Q 32.375 75.984375 41.109375 75.984375 -z -" id="DejaVuSansMono-66"/> - <path d="M 51.8125 2.78125 -Q 48.1875 0.6875 44.359375 -0.359375 -Q 40.53125 -1.421875 36.53125 -1.421875 -Q 23.828125 -1.421875 16.671875 6.1875 -Q 9.515625 13.8125 9.515625 27.296875 -Q 9.515625 40.765625 16.671875 48.375 -Q 23.828125 56 36.53125 56 -Q 40.484375 56 44.234375 54.96875 -Q 48 53.953125 51.8125 51.8125 -L 51.8125 42.390625 -Q 48.25 45.5625 44.65625 46.96875 -Q 41.0625 48.390625 36.53125 48.390625 -Q 28.078125 48.390625 23.53125 42.921875 -Q 19 37.453125 19 27.296875 -Q 19 17.1875 23.5625 11.6875 -Q 28.125 6.203125 36.53125 6.203125 -Q 41.21875 6.203125 44.921875 7.640625 -Q 48.640625 9.078125 51.8125 12.109375 -z -" id="DejaVuSansMono-63"/> - <path d="M 29.984375 70.21875 -L 29.984375 54.6875 -L 50.390625 54.6875 -L 50.390625 47.703125 -L 29.984375 47.703125 -L 29.984375 18.015625 -Q 29.984375 11.96875 32.28125 9.5625 -Q 34.578125 7.171875 40.28125 7.171875 -L 50.390625 7.171875 -L 50.390625 0 -L 39.40625 0 -Q 29.296875 0 25.140625 4.046875 -Q 21 8.109375 21 18.015625 -L 21 47.703125 -L 6.390625 47.703125 -L 6.390625 54.6875 -L 21 54.6875 -L 21 70.21875 -z -" id="DejaVuSansMono-74"/> - <path d="M 12.5 54.6875 -L 35.5 54.6875 -L 35.5 6.984375 -L 53.328125 6.984375 -L 53.328125 0 -L 8.6875 0 -L 8.6875 6.984375 -L 26.515625 6.984375 -L 26.515625 47.703125 -L 12.5 47.703125 -z -M 26.515625 75.984375 -L 35.5 75.984375 -L 35.5 64.59375 -L 26.515625 64.59375 -z -" id="DejaVuSansMono-69"/> - <path d="M 4.890625 54.6875 -L 14.203125 54.6875 -L 30.078125 8.796875 -L 46 54.6875 -L 55.328125 54.6875 -L 35.890625 0 -L 24.3125 0 -z -" id="DejaVuSansMono-76"/> - <path id="DejaVuSansMono-20"/> - <path d="M 17.390625 31.390625 -L 42.828125 31.390625 -L 42.828125 23.390625 -L 17.390625 23.390625 -z -" id="DejaVuSansMono-2d"/> - <path d="M 13.1875 8.296875 -L 28.515625 8.296875 -L 28.515625 64.015625 -L 12.015625 60.296875 -L 12.015625 69.28125 -L 28.421875 72.90625 -L 38.28125 72.90625 -L 38.28125 8.296875 -L 53.421875 8.296875 -L 53.421875 0 -L 13.1875 0 -z -" id="DejaVuSansMono-31"/> - </defs> - <g transform="translate(60.395152 52.770701)scale(0.055 -0.055)"> - <use transform="translate(0 0.484375)" xlink:href="#Cmmi10-6e"/> - <use transform="translate(60.009766 -16.521875)scale(0.7)" xlink:href="#Cmr10-48"/> - <use transform="translate(118.887109 0.484375)" xlink:href="#DejaVuSansMono-5e"/> - <use transform="translate(179.092187 0.484375)" xlink:href="#DejaVuSansMono-43"/> - <use transform="translate(239.297266 0.484375)" xlink:href="#DejaVuSansMono-6f"/> - <use transform="translate(299.502344 0.484375)" xlink:href="#DejaVuSansMono-6f"/> - <use transform="translate(359.707422 0.484375)" xlink:href="#DejaVuSansMono-6c"/> - <use transform="translate(419.9125 0.484375)" xlink:href="#DejaVuSansMono-5f"/> - <use transform="translate(480.117578 0.484375)" xlink:href="#DejaVuSansMono-67"/> - <use transform="translate(540.322656 0.484375)" xlink:href="#DejaVuSansMono-61"/> - <use transform="translate(600.527734 0.484375)" xlink:href="#DejaVuSansMono-6d"/> - <use transform="translate(660.732813 0.484375)" xlink:href="#DejaVuSansMono-6d"/> - <use transform="translate(720.937891 0.484375)" xlink:href="#DejaVuSansMono-61"/> - <use transform="translate(781.142969 0.484375)" xlink:href="#DejaVuSansMono-5f"/> - <use transform="translate(841.348047 0.484375)" xlink:href="#DejaVuSansMono-65"/> - <use transform="translate(901.553125 0.484375)" xlink:href="#DejaVuSansMono-66"/> - <use transform="translate(961.758203 0.484375)" xlink:href="#DejaVuSansMono-66"/> - <use transform="translate(1021.963281 0.484375)" xlink:href="#DejaVuSansMono-65"/> - <use transform="translate(1082.168359 0.484375)" xlink:href="#DejaVuSansMono-63"/> - <use transform="translate(1142.373437 0.484375)" xlink:href="#DejaVuSansMono-74"/> - <use transform="translate(1202.578516 0.484375)" xlink:href="#DejaVuSansMono-69"/> - <use transform="translate(1262.783594 0.484375)" xlink:href="#DejaVuSansMono-76"/> - <use transform="translate(1322.988672 0.484375)" xlink:href="#DejaVuSansMono-65"/> - <use transform="translate(1383.19375 0.484375)" xlink:href="#DejaVuSansMono-20"/> - <use transform="translate(1443.398828 0.484375)" xlink:href="#DejaVuSansMono-2d"/> - <use transform="translate(1503.603906 0.484375)" xlink:href="#DejaVuSansMono-20"/> - <use transform="translate(1563.808984 0.484375)" xlink:href="#DejaVuSansMono-31"/> - </g> - </g> - <g id="text_13"> - <!-- $n_{\rm H}$^Jeans_gamma_effective - 1 --> - <defs> - <path d="M 5.328125 2.984375 -L 5.328125 14.5 -Q 9.765625 10.546875 14.5 8.5625 -Q 19.234375 6.59375 24.3125 6.59375 -Q 31.296875 6.59375 34.046875 10.234375 -Q 36.8125 13.875 36.8125 23.78125 -L 36.8125 64.59375 -L 18.21875 64.59375 -L 18.21875 72.90625 -L 46.6875 72.90625 -L 46.6875 23.78125 -Q 46.6875 10.015625 41.53125 4.296875 -Q 36.375 -1.421875 24.3125 -1.421875 -Q 19.625 -1.421875 14.984375 -0.34375 -Q 10.359375 0.734375 5.328125 2.984375 -z -" id="DejaVuSansMono-4a"/> - <path d="M 51.3125 33.890625 -L 51.3125 0 -L 42.28125 0 -L 42.28125 33.890625 -Q 42.28125 41.265625 39.6875 44.71875 -Q 37.109375 48.1875 31.59375 48.1875 -Q 25.296875 48.1875 21.890625 43.71875 -Q 18.5 39.265625 18.5 30.90625 -L 18.5 0 -L 9.515625 0 -L 9.515625 54.6875 -L 18.5 54.6875 -L 18.5 46.484375 -Q 20.90625 51.171875 25 53.578125 -Q 29.109375 56 34.71875 56 -Q 43.0625 56 47.1875 50.5 -Q 51.3125 45.015625 51.3125 33.890625 -z -" id="DejaVuSansMono-6e"/> - <path d="M 47.515625 52.78125 -L 47.515625 44 -Q 43.65625 46.234375 39.75 47.359375 -Q 35.84375 48.484375 31.78125 48.484375 -Q 25.6875 48.484375 22.671875 46.5 -Q 19.671875 44.53125 19.671875 40.484375 -Q 19.671875 36.8125 21.921875 35 -Q 24.171875 33.203125 33.109375 31.5 -L 36.71875 30.8125 -Q 43.40625 29.546875 46.84375 25.734375 -Q 50.296875 21.921875 50.296875 15.828125 -Q 50.296875 7.71875 44.53125 3.140625 -Q 38.765625 -1.421875 28.515625 -1.421875 -Q 24.46875 -1.421875 20.015625 -0.5625 -Q 15.578125 0.296875 10.40625 2 -L 10.40625 11.28125 -Q 15.4375 8.6875 20.015625 7.390625 -Q 24.609375 6.109375 28.71875 6.109375 -Q 34.671875 6.109375 37.9375 8.515625 -Q 41.21875 10.9375 41.21875 15.28125 -Q 41.21875 21.53125 29.25 23.921875 -L 28.859375 24.03125 -L 25.484375 24.703125 -Q 17.71875 26.21875 14.15625 29.8125 -Q 10.59375 33.40625 10.59375 39.59375 -Q 10.59375 47.46875 15.90625 51.734375 -Q 21.234375 56 31.109375 56 -Q 35.5 56 39.546875 55.1875 -Q 43.609375 54.390625 47.515625 52.78125 -z -" id="DejaVuSansMono-73"/> - </defs> - <g transform="translate(151.213631 69.615498)rotate(-43)scale(0.055 -0.055)"> - <use transform="translate(0 0.015625)" xlink:href="#Cmmi10-6e"/> - <use transform="translate(60.009766 -16.990625)scale(0.7)" xlink:href="#Cmr10-48"/> - <use transform="translate(118.887109 0.015625)" xlink:href="#DejaVuSansMono-5e"/> - <use transform="translate(179.092187 0.015625)" xlink:href="#DejaVuSansMono-4a"/> - <use transform="translate(239.297266 0.015625)" xlink:href="#DejaVuSansMono-65"/> - <use transform="translate(299.502344 0.015625)" xlink:href="#DejaVuSansMono-61"/> - <use transform="translate(359.707422 0.015625)" xlink:href="#DejaVuSansMono-6e"/> - <use transform="translate(419.9125 0.015625)" xlink:href="#DejaVuSansMono-73"/> - <use transform="translate(480.117578 0.015625)" xlink:href="#DejaVuSansMono-5f"/> - <use transform="translate(540.322656 0.015625)" xlink:href="#DejaVuSansMono-67"/> - <use transform="translate(600.527734 0.015625)" xlink:href="#DejaVuSansMono-61"/> - <use transform="translate(660.732813 0.015625)" xlink:href="#DejaVuSansMono-6d"/> - <use transform="translate(720.937891 0.015625)" xlink:href="#DejaVuSansMono-6d"/> - <use transform="translate(781.142969 0.015625)" xlink:href="#DejaVuSansMono-61"/> - <use transform="translate(841.348047 0.015625)" xlink:href="#DejaVuSansMono-5f"/> - <use transform="translate(901.553125 0.015625)" xlink:href="#DejaVuSansMono-65"/> - <use transform="translate(961.758203 0.015625)" xlink:href="#DejaVuSansMono-66"/> - <use transform="translate(1021.963281 0.015625)" xlink:href="#DejaVuSansMono-66"/> - <use transform="translate(1082.168359 0.015625)" xlink:href="#DejaVuSansMono-65"/> - <use transform="translate(1142.373437 0.015625)" xlink:href="#DejaVuSansMono-63"/> - <use transform="translate(1202.578516 0.015625)" xlink:href="#DejaVuSansMono-74"/> - <use transform="translate(1262.783594 0.015625)" xlink:href="#DejaVuSansMono-69"/> - <use transform="translate(1322.988672 0.015625)" xlink:href="#DejaVuSansMono-76"/> - <use transform="translate(1383.19375 0.015625)" xlink:href="#DejaVuSansMono-65"/> - <use transform="translate(1443.398828 0.015625)" xlink:href="#DejaVuSansMono-20"/> - <use transform="translate(1503.603906 0.015625)" xlink:href="#DejaVuSansMono-2d"/> - <use transform="translate(1563.808984 0.015625)" xlink:href="#DejaVuSansMono-20"/> - <use transform="translate(1624.014062 0.015625)" xlink:href="#DejaVuSansMono-31"/> - </g> - </g> - <g id="text_14"> - <!-- Cool_density_threshold_H_p_cm3 --> - <defs> - <path d="M 41.890625 47.703125 -L 41.890625 75.984375 -L 50.875 75.984375 -L 50.875 0 -L 41.890625 0 -L 41.890625 6.890625 -Q 39.65625 2.828125 35.90625 0.703125 -Q 32.171875 -1.421875 27.296875 -1.421875 -Q 17.390625 -1.421875 11.6875 6.265625 -Q 6 13.96875 6 27.484375 -Q 6 40.828125 11.71875 48.40625 -Q 17.4375 56 27.296875 56 -Q 32.234375 56 35.984375 53.875 -Q 39.75 51.765625 41.890625 47.703125 -z -M 15.484375 27.296875 -Q 15.484375 16.84375 18.796875 11.515625 -Q 22.125 6.203125 28.609375 6.203125 -Q 35.109375 6.203125 38.5 11.5625 -Q 41.890625 16.9375 41.890625 27.296875 -Q 41.890625 37.703125 38.5 43.046875 -Q 35.109375 48.390625 28.609375 48.390625 -Q 22.125 48.390625 18.796875 43.0625 -Q 15.484375 37.75 15.484375 27.296875 -z -" id="DejaVuSansMono-64"/> - <path d="M 41.890625 17.578125 -Q 39.65625 11.859375 36.1875 2.546875 -Q 31.34375 -10.359375 29.6875 -13.1875 -Q 27.4375 -17 24.0625 -18.890625 -Q 20.703125 -20.796875 16.21875 -20.796875 -L 8.984375 -20.796875 -L 8.984375 -13.28125 -L 14.3125 -13.28125 -Q 18.265625 -13.28125 20.5 -10.984375 -Q 22.75 -8.6875 26.21875 0.875 -L 5.078125 54.6875 -L 14.59375 54.6875 -L 30.8125 11.921875 -L 46.78125 54.6875 -L 56.296875 54.6875 -z -" id="DejaVuSansMono-79"/> - <path d="M 51.3125 33.890625 -L 51.3125 0 -L 42.28125 0 -L 42.28125 33.890625 -Q 42.28125 41.265625 39.6875 44.71875 -Q 37.109375 48.1875 31.59375 48.1875 -Q 25.296875 48.1875 21.890625 43.71875 -Q 18.5 39.265625 18.5 30.90625 -L 18.5 0 -L 9.515625 0 -L 9.515625 75.984375 -L 18.5 75.984375 -L 18.5 46.484375 -Q 20.90625 51.171875 25 53.578125 -Q 29.109375 56 34.71875 56 -Q 43.0625 56 47.1875 50.5 -Q 51.3125 45.015625 51.3125 33.890625 -z -" id="DejaVuSansMono-68"/> - <path d="M 56.390625 43.40625 -Q 53.515625 45.65625 50.53125 46.671875 -Q 47.5625 47.703125 44 47.703125 -Q 35.59375 47.703125 31.140625 42.421875 -Q 26.703125 37.15625 26.703125 27.203125 -L 26.703125 0 -L 17.671875 0 -L 17.671875 54.6875 -L 26.703125 54.6875 -L 26.703125 44 -Q 28.953125 49.8125 33.609375 52.90625 -Q 38.28125 56 44.671875 56 -Q 48 56 50.875 55.171875 -Q 53.765625 54.34375 56.390625 52.59375 -z -" id="DejaVuSansMono-72"/> - <path d="M 6.6875 72.90625 -L 16.609375 72.90625 -L 16.609375 43.015625 -L 43.609375 43.015625 -L 43.609375 72.90625 -L 53.515625 72.90625 -L 53.515625 0 -L 43.609375 0 -L 43.609375 34.71875 -L 16.609375 34.71875 -L 16.609375 0 -L 6.6875 0 -z -" id="DejaVuSansMono-48"/> - <path d="M 18.3125 6.890625 -L 18.3125 -20.796875 -L 9.28125 -20.796875 -L 9.28125 54.6875 -L 18.3125 54.6875 -L 18.3125 47.703125 -Q 20.5625 51.765625 24.296875 53.875 -Q 28.03125 56 32.90625 56 -Q 42.828125 56 48.46875 48.328125 -Q 54.109375 40.671875 54.109375 27.09375 -Q 54.109375 13.765625 48.4375 6.171875 -Q 42.78125 -1.421875 32.90625 -1.421875 -Q 27.9375 -1.421875 24.1875 0.703125 -Q 20.453125 2.828125 18.3125 6.890625 -z -M 44.671875 27.296875 -Q 44.671875 37.75 41.375 43.0625 -Q 38.09375 48.390625 31.59375 48.390625 -Q 25.046875 48.390625 21.671875 43.046875 -Q 18.3125 37.703125 18.3125 27.296875 -Q 18.3125 16.9375 21.671875 11.5625 -Q 25.046875 6.203125 31.59375 6.203125 -Q 38.09375 6.203125 41.375 11.515625 -Q 44.671875 16.84375 44.671875 27.296875 -z -" id="DejaVuSansMono-70"/> - <path d="M 37.890625 39.015625 -Q 45.0625 37.109375 48.875 32.25 -Q 52.6875 27.390625 52.6875 20.125 -Q 52.6875 10.0625 45.921875 4.3125 -Q 39.15625 -1.421875 27.203125 -1.421875 -Q 22.171875 -1.421875 16.9375 -0.484375 -Q 11.71875 0.4375 6.6875 2.203125 -L 6.6875 12.015625 -Q 11.671875 9.421875 16.5 8.15625 -Q 21.34375 6.890625 26.125 6.890625 -Q 34.234375 6.890625 38.578125 10.546875 -Q 42.921875 14.203125 42.921875 21.09375 -Q 42.921875 27.4375 38.578125 31.171875 -Q 34.234375 34.90625 26.8125 34.90625 -L 19.28125 34.90625 -L 19.28125 43.015625 -L 26.8125 43.015625 -Q 33.59375 43.015625 37.40625 45.984375 -Q 41.21875 48.96875 41.21875 54.296875 -Q 41.21875 59.90625 37.671875 62.90625 -Q 34.125 65.921875 27.59375 65.921875 -Q 23.25 65.921875 18.609375 64.9375 -Q 13.96875 63.96875 8.890625 62.015625 -L 8.890625 71.09375 -Q 14.796875 72.65625 19.40625 73.4375 -Q 24.03125 74.21875 27.59375 74.21875 -Q 38.234375 74.21875 44.609375 68.875 -Q 50.984375 63.53125 50.984375 54.6875 -Q 50.984375 48.6875 47.625 44.671875 -Q 44.28125 40.671875 37.890625 39.015625 -z -" id="DejaVuSansMono-33"/> - </defs> - <g transform="translate(76.031817 194.356252)rotate(-90)scale(0.055 -0.055)"> - <use xlink:href="#DejaVuSansMono-43"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-6f"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-6f"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-6c"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-5f"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-64"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-65"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-6e"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-73"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-69"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-74"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-79"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-5f"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-74"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-68"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-72"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-65"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-73"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-68"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-6f"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-6c"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-64"/> - <use x="1324.511719" xlink:href="#DejaVuSansMono-5f"/> - <use x="1384.716797" xlink:href="#DejaVuSansMono-48"/> - <use x="1444.921875" xlink:href="#DejaVuSansMono-5f"/> - <use x="1505.126953" xlink:href="#DejaVuSansMono-70"/> - <use x="1565.332031" xlink:href="#DejaVuSansMono-5f"/> - <use x="1625.537109" xlink:href="#DejaVuSansMono-63"/> - <use x="1685.742188" xlink:href="#DejaVuSansMono-6d"/> - <use x="1745.947266" xlink:href="#DejaVuSansMono-33"/> - </g> - </g> - <g id="text_15"> - <!-- Jeans_density_threshold_H_p_cm3 --> - <g transform="translate(145.308908 194.356252)rotate(-90)scale(0.055 -0.055)"> - <use xlink:href="#DejaVuSansMono-4a"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-65"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-61"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-6e"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-73"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-5f"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-64"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-65"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-6e"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-73"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-69"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-74"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-79"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-5f"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-74"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-68"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-72"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-65"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-73"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-68"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-6f"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-6c"/> - <use x="1324.511719" xlink:href="#DejaVuSansMono-64"/> - <use x="1384.716797" xlink:href="#DejaVuSansMono-5f"/> - <use x="1444.921875" xlink:href="#DejaVuSansMono-48"/> - <use x="1505.126953" xlink:href="#DejaVuSansMono-5f"/> - <use x="1565.332031" xlink:href="#DejaVuSansMono-70"/> - <use x="1625.537109" xlink:href="#DejaVuSansMono-5f"/> - <use x="1685.742188" xlink:href="#DejaVuSansMono-63"/> - <use x="1745.947266" xlink:href="#DejaVuSansMono-6d"/> - <use x="1806.152344" xlink:href="#DejaVuSansMono-33"/> - </g> - </g> - <g id="text_16"> - <!-- Cool_temperature_norm_K --> - <defs> - <path d="M 9.515625 20.703125 -L 9.515625 54.59375 -L 18.5 54.59375 -L 18.5 20.703125 -Q 18.5 13.328125 21.109375 9.859375 -Q 23.734375 6.390625 29.203125 6.390625 -Q 35.546875 6.390625 38.90625 10.859375 -Q 42.28125 15.328125 42.28125 23.6875 -L 42.28125 54.59375 -L 51.3125 54.59375 -L 51.3125 0 -L 42.28125 0 -L 42.28125 8.203125 -Q 39.890625 3.46875 35.765625 1.015625 -Q 31.640625 -1.421875 26.125 -1.421875 -Q 17.71875 -1.421875 13.609375 4.078125 -Q 9.515625 9.578125 9.515625 20.703125 -z -" id="DejaVuSansMono-75"/> - <path d="M 6.6875 72.90625 -L 16.609375 72.90625 -L 16.609375 40.484375 -L 47.40625 72.90625 -L 58.984375 72.90625 -L 30.609375 43.109375 -L 59.8125 0 -L 47.90625 0 -L 24.125 36.53125 -L 16.609375 28.515625 -L 16.609375 0 -L 6.6875 0 -z -" id="DejaVuSansMono-4b"/> - </defs> - <g transform="translate(37.862259 67.119164)scale(0.055 -0.055)"> - <use xlink:href="#DejaVuSansMono-43"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-6f"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-6f"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-6c"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-5f"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-74"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-65"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-6d"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-70"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-65"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-72"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-61"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-74"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-75"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-72"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-65"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-5f"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-6e"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-6f"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-72"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-6d"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-5f"/> - <use x="1324.511719" xlink:href="#DejaVuSansMono-4b"/> - </g> - </g> - <g id="text_17"> - <!-- Jeans_temperature_norm_K --> - <g transform="translate(37.862259 81.797988)scale(0.055 -0.055)"> - <use xlink:href="#DejaVuSansMono-4a"/> - <use x="60.205078" xlink:href="#DejaVuSansMono-65"/> - <use x="120.410156" xlink:href="#DejaVuSansMono-61"/> - <use x="180.615234" xlink:href="#DejaVuSansMono-6e"/> - <use x="240.820312" xlink:href="#DejaVuSansMono-73"/> - <use x="301.025391" xlink:href="#DejaVuSansMono-5f"/> - <use x="361.230469" xlink:href="#DejaVuSansMono-74"/> - <use x="421.435547" xlink:href="#DejaVuSansMono-65"/> - <use x="481.640625" xlink:href="#DejaVuSansMono-6d"/> - <use x="541.845703" xlink:href="#DejaVuSansMono-70"/> - <use x="602.050781" xlink:href="#DejaVuSansMono-65"/> - <use x="662.255859" xlink:href="#DejaVuSansMono-72"/> - <use x="722.460938" xlink:href="#DejaVuSansMono-61"/> - <use x="782.666016" xlink:href="#DejaVuSansMono-74"/> - <use x="842.871094" xlink:href="#DejaVuSansMono-75"/> - <use x="903.076172" xlink:href="#DejaVuSansMono-72"/> - <use x="963.28125" xlink:href="#DejaVuSansMono-65"/> - <use x="1023.486328" xlink:href="#DejaVuSansMono-5f"/> - <use x="1083.691406" xlink:href="#DejaVuSansMono-6e"/> - <use x="1143.896484" xlink:href="#DejaVuSansMono-6f"/> - <use x="1204.101562" xlink:href="#DejaVuSansMono-72"/> - <use x="1264.306641" xlink:href="#DejaVuSansMono-6d"/> - <use x="1324.511719" xlink:href="#DejaVuSansMono-5f"/> - <use x="1384.716797" xlink:href="#DejaVuSansMono-4b"/> - </g> - </g> - </g> - </g> - <defs> - <clipPath id="p5e3fe66b1e"> - <rect height="195.048" width="190.512" x="34.02" y="2.268"/> - </clipPath> - </defs> -</svg> diff --git a/doc/RTD/source/SubgridModels/EAGLE/index.rst b/doc/RTD/source/SubgridModels/EAGLE/index.rst index 4bb21f3b9478247c01adfe4c32c7d6c99ea3a6ba..25272911352a41ecb10f4144ab54153a2afeb29a 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/index.rst +++ b/doc/RTD/source/SubgridModels/EAGLE/index.rst @@ -33,7 +33,7 @@ used to prevent the fragmentation of high-density gas into clumps that cannot be resolved by the coupled hydro+gravity solver. The two limits are sketched on the following figure. -.. figure:: EAGLE_entropy_floor.svg +.. figure:: EAGLE_entropy_floor.png :width: 400px :align: center :figclass: align-center @@ -443,7 +443,7 @@ designed such that star formation threshold decreases with increasing metallicity. This relationship with the YAML parameters defining it is shown on the figure below. -.. figure:: EAGLE_SF_Z_dep.svg +.. figure:: EAGLE_SF_Z_dep.png :width: 400px :align: center :figclass: align-center @@ -470,7 +470,7 @@ densities. Note that unlike the entropy floor, this is applied at *all* densities and not only above a certain threshold. This equation of state with the relevant YAML parameters defining it is shown on the figure below. -.. figure:: EAGLE_SF_EOS.svg +.. figure:: EAGLE_SF_EOS.png :width: 400px :align: center :figclass: align-center diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py index 88ca56d750bf716fea8b4bf72b70c2b598953ac7..a24788915816bdc57fbf27d5e7a906b4ccdfe9e1 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py +++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py @@ -1,3 +1,9 @@ +import os + +if os.path.exists("EAGLE_SF_EOS.png"): + # do not generate plot again + exit() + import matplotlib matplotlib.use("Agg") @@ -75,4 +81,4 @@ xlim(3e-8, 3e3) ylim(20.0, 2e5) -savefig("EAGLE_SF_EOS.svg", dpi=200) +savefig("EAGLE_SF_EOS.png", dpi=200) diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py index 26ae522947ecc9e75f6f287bafed0bb9acb9134a..7ae006c90dce4f7bb3d79d6255897193366a8e9f 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py +++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py @@ -1,3 +1,9 @@ +import os + +if os.path.exists("EAGLE_SF_Z_dep.png"): + # do not generate plot again + exit() + import matplotlib matplotlib.use("Agg") @@ -96,4 +102,4 @@ ylabel("SF threshold number density $n_{\\rm H, thresh}$ [cm$^{-3}$]", labelpad= xlim(1e-7, 1.0) ylim(0.0002, 50) -savefig("EAGLE_SF_Z_dep.svg", dpi=200) +savefig("EAGLE_SF_Z_dep.png", dpi=200) diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py index 00de0e382e1e7f04e7508703addea3464edccd43..a643ed81252ed76377dbf834c9d27777d2e85565 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py +++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py @@ -1,3 +1,9 @@ +import os + +if os.path.exists("EAGLE_entropy_floor.png"): + # do not generate plot again + exit() + import matplotlib matplotlib.use("Agg") @@ -109,4 +115,4 @@ ylabel("Temperature $T$ [K]", labelpad=2) xlim(3e-8, 3e3) ylim(20.0, 2e5) -savefig("EAGLE_entropy_floor.svg", dpi=200) +savefig("EAGLE_entropy_floor.png", dpi=200) diff --git a/doc/RTD/source/SubgridModels/GEAR/index.rst b/doc/RTD/source/SubgridModels/GEAR/index.rst index 9faaef245b48b19e49c3a5caff38c61a1d3f70e5..135402407650e72074ed70396b9fc6435b5e8243 100644 --- a/doc/RTD/source/SubgridModels/GEAR/index.rst +++ b/doc/RTD/source/SubgridModels/GEAR/index.rst @@ -37,10 +37,12 @@ Here the :math:`x, y` are simple weights that should never have the pressure flo +.. _gear_grackle_cooling: + Cooling: Grackle ~~~~~~~~~~~~~~~~ -Grackle is a chemistry and cooling library presented in `B. Smith et al. 2016 <https://arxiv.org/abs/1610.09591>`_ +Grackle is a chemistry and cooling library presented in `B. Smith et al. 2017 <https://ui.adsabs.harvard.edu/abs/2017MNRAS.466.2217S>`_ (do not forget to cite if used). Four different modes are available: equilibrium, 6 species network (H, H\\( ^+ \\), e\\( ^- \\), He, He\\( ^+ \\) and He\\( ^{++} \\)), 9 species network (adds H\\(^-\\), H\\(_2\\) and @@ -55,7 +57,7 @@ When starting a simulation without providing the different element fractions in The code uses an iterative method in order to find the correct initial composition and this method can be tuned with two parameters. ``GrackleCooling:max_steps`` defines the maximal number of steps to reach the convergence and ``GrackleCooling:convergence_limit`` defines the tolerance in the relative error. In order to compile SWIFT with Grackle, you need to provide the options ``with-chemistry=GEAR`` and ``with-grackle=$GRACKLE_ROOT`` -where ``$GRACKLE_ROOT`` is the root of the install directory (not the ``lib``). +where ``$GRACKLE_ROOT`` is the root of the install directory (not the ``lib``). You will need a Grackle version later than 3.1.1. To compile it, run the following commands from the root directory of Grackle: @@ -63,6 +65,8 @@ the following commands from the root directory of Grackle: Update the variables ``LOCAL_HDF5_INSTALL`` and ``MACH_INSTALL_PREFIX`` in the file ``src/clib/Make.mach.linux-gnu``. Finish with ``make machine-linux-gnu; make && make install``. +Note that we require the 64 bit float version of Grackle, which should be the default setting. +(The precision can be set while compiling grackle with ``make precision-64``). If you encounter any problem, you can look at the `Grackle documentation <https://grackle.readthedocs.io/en/latest/>`_ You can now provide the path given for ``MACH_INSTALL_PREFIX`` to ``with-grackle``. diff --git a/doc/RTD/source/SubgridModels/index.rst b/doc/RTD/source/SubgridModels/index.rst index 9dba18c3f5595dc3b2f8275c7610c9bb32ce2b43..d835e6baf442ed6988cafbe70c92fe5992f04ca2 100644 --- a/doc/RTD/source/SubgridModels/index.rst +++ b/doc/RTD/source/SubgridModels/index.rst @@ -17,3 +17,4 @@ be use as an empty canvas to be copied to create additional models. QuickLymanAlpha/index GEAR/index Basic/index + AGNSpinJets/index diff --git a/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst b/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst index f8163741477974bad0d7a54cafa0e73a8def2ba7..937421eb7e80e136c5d66a0b2706a9d104d94212 100644 --- a/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst +++ b/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst @@ -482,7 +482,7 @@ Then we also need a ``runner_doiact_my_suff.c`` file where the functions declare ``runner_doiact_my_suff.h`` are defined by including them with ``FUNCTION`` defined:: - #include "../config.h" + #include <config.h> /* other includes too... */ /* Import the new interaction loop functions. */ diff --git a/doc/RTD/source/Task/current_dependencies.rst b/doc/RTD/source/Task/current_dependencies.rst index 2b6d9031ec0d597e97b649fb04a86b3b80f268fe..47ff74c6afaab23a87079c9824b53c6a8d5f5479 100644 --- a/doc/RTD/source/Task/current_dependencies.rst +++ b/doc/RTD/source/Task/current_dependencies.rst @@ -54,3 +54,20 @@ As the hydrodynamics are described in :ref:`hydro`, we are only showing the grav The first rectangle groups the tasks that compute the smoothing length of the stars and the second one the tasks that deposit the energy into the surrounding gas. This was done with SWIFT v0.9.0. + + +.. figure:: sink.png + :width: 400px + :align: center + :figclass: align-center + :alt: Task dependencies for the sink scheme. + + This figure shows the task dependencies for the sink scheme. + The first rectangle groups the tasks that determine if sink particles will swallow other + sink particles or gas particles. + In the second one, the gas particles tagged as "to be swallowed" are effectively swallowed. + In the third one, the sink particles tagged as "to be swallowed" are effectively swallowed. + This was done with SWIFT v0.9.0. + +For documentation on the radiative transfer tasking system, please refer to its +:ref:`own page <rt_task_system>`. diff --git a/doc/RTD/source/Task/sink.dot b/doc/RTD/source/Task/sink.dot new file mode 100644 index 0000000000000000000000000000000000000000..da679f2562d50a0f0369e4bd05808d399d42d8dc --- /dev/null +++ b/doc/RTD/source/Task/sink.dot @@ -0,0 +1,93 @@ +digraph task_dep { + # Header + compound=true; + ratio=0.66; + node[nodesep=0.15, fontsize=18, penwidth=3.]; + edge[fontsize=12, penwidth=0.5]; + ranksep=0.8; + + # Special tasks + self_sink_do_sink_swallow[color=forestgreen]; + self_sink_swallow[color=forestgreen]; + self_sink_do_gas_swallow[color=forestgreen]; + pair_sink_do_sink_swallow[color=forestgreen]; + pair_sink_swallow[color=forestgreen]; + pair_sink_do_gas_swallow[color=forestgreen]; + sub_self_sink_do_sink_swallow[color=forestgreen]; + sub_self_sink_swallow[color=forestgreen]; + sub_self_sink_do_gas_swallow[color=forestgreen]; + sub_pair_sink_do_sink_swallow[color=forestgreen]; + sub_pair_sink_swallow[color=forestgreen]; + sub_pair_sink_do_gas_swallow[color=forestgreen]; + drift_sink[color=lightseagreen]; + star_formation_sink[color=lightseagreen]; + sink_in[style=filled,fillcolor=grey90,color=lightseagreen]; + sink_ghost1[style=filled,fillcolor=grey90,color=lightseagreen]; + sink_ghost2[style=filled,fillcolor=grey90,color=lightseagreen]; + sink_out[style=filled,fillcolor=grey90,color=lightseagreen]; + sink_formation[color=lightseagreen]; + + # Clusters + subgraph clusterSinkAccretion { + label=""; + bgcolor="grey99"; + pair_sink_do_gas_swallow; + self_sink_do_gas_swallow; + sub_pair_sink_do_gas_swallow; + sub_self_sink_do_gas_swallow; + }; + + subgraph clusterSinkFormation { + label=""; + bgcolor="grey99"; + pair_sink_swallow; + self_sink_swallow; + sub_pair_sink_swallow; + sub_self_sink_swallow; + }; + + subgraph clusterSinkMerger { + label=""; + bgcolor="grey99"; + pair_sink_do_sink_swallow; + self_sink_do_sink_swallow; + sub_pair_sink_do_sink_swallow; + sub_self_sink_do_sink_swallow; + }; + + + # Dependencies + self_sink_do_sink_swallow->sink_out[fontcolor=forestgreen] + self_sink_swallow->sink_ghost1[fontcolor=forestgreen] + self_sink_do_gas_swallow->sink_ghost2[fontcolor=forestgreen] + pair_sink_do_sink_swallow->sink_out[fontcolor=forestgreen] + pair_sink_swallow->sink_ghost1[fontcolor=forestgreen] + pair_sink_do_gas_swallow->sink_ghost2[fontcolor=forestgreen] + sub_self_sink_do_sink_swallow->sink_out[fontcolor=forestgreen] + sub_self_sink_swallow->sink_ghost1[fontcolor=forestgreen] + sub_self_sink_do_gas_swallow->sink_ghost2[fontcolor=forestgreen] + sub_pair_sink_do_sink_swallow->sink_out[fontcolor=forestgreen] + sub_pair_sink_swallow->sink_ghost1[fontcolor=forestgreen] + sub_pair_sink_do_gas_swallow->sink_ghost2[fontcolor=forestgreen] + drift_part->sink_formation[fontcolor=blue3] + drift_sink->kick2[fontcolor=lightseagreen] + drift_sink->sink_formation[fontcolor=lightseagreen] + kick2->sink_in[fontcolor=black] + kick2->star_formation_sink[fontcolor=black] + star_formation_sink->timestep[fontcolor=lightseagreen] + sink_in->sink_formation[fontcolor=lightseagreen] + sink_ghost1->self_sink_do_gas_swallow[fontcolor=lightseagreen] + sink_ghost1->sub_self_sink_do_gas_swallow[fontcolor=lightseagreen] + sink_ghost1->sub_pair_sink_do_gas_swallow[fontcolor=lightseagreen] + sink_ghost1->pair_sink_do_gas_swallow[fontcolor=lightseagreen] + sink_ghost2->self_sink_do_sink_swallow[fontcolor=lightseagreen] + sink_ghost2->sub_self_sink_do_sink_swallow[fontcolor=lightseagreen] + sink_ghost2->sub_pair_sink_do_sink_swallow[fontcolor=lightseagreen] + sink_ghost2->pair_sink_do_sink_swallow[fontcolor=lightseagreen] + sink_out->timestep[fontcolor=lightseagreen] + sink_out->star_formation_sink[fontcolor=lightseagreen] + sink_formation->self_sink_swallow[fontcolor=lightseagreen] + sink_formation->sub_self_sink_swallow[fontcolor=lightseagreen] + sink_formation->sub_pair_sink_swallow[fontcolor=lightseagreen] + sink_formation->pair_sink_swallow[fontcolor=lightseagreen] +} \ No newline at end of file diff --git a/doc/RTD/source/Task/sink.png b/doc/RTD/source/Task/sink.png new file mode 100644 index 0000000000000000000000000000000000000000..bc4f368c3274eb35c2ebaa5c2647263f6fc52bef Binary files /dev/null and b/doc/RTD/source/Task/sink.png differ diff --git a/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst index f09c2759dd2cf6088ac371b7b1ca7577c167327b..ad6c0afa81a07e95eab36592c6266d7160257882 100644 --- a/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst +++ b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst @@ -73,6 +73,18 @@ VELOCIraptor twice and create two builds, then use the ``--with-velociraptor-mpi`` configure option to point to the MPI build and the ``--with-velociraptor`` option to point at the non-MPI build. In general ``./configure`` can be run with other options as desired. + +If you see reports about missing references to ``MPI::Comm`` and +similar functions, then you will also need to include the C++ version +of the MPI library in ``LIBS`` variable as part of the configuration +(or by setting the ``LIBS`` environment variable), as in: + +.. code:: bash + + ./configure --with-velociraptor=/path/to/VELOCIraptor-STF/build/src LIBS=-lmpi++ + +other names and libraries may be necessary as these are MPI flavour determined. + After this we can run SWIFT with VELOCIraptor, but for this we first need to add several lines to the yaml file of our simulation diff --git a/doc/RTD/source/conf.py b/doc/RTD/source/conf.py index 24e89bdd11a2affec726838bf6e984687cb77e44..59c8ba737102291177bd901e7d9fd262c0e71f53 100644 --- a/doc/RTD/source/conf.py +++ b/doc/RTD/source/conf.py @@ -19,7 +19,7 @@ # -- Project information ----------------------------------------------------- project = "SWIFT: SPH With Inter-dependent Fine-grained Tasking" -copyright = "2014-2021, SWIFT Collaboration" +copyright = "2014-2023, SWIFT Collaboration" author = "SWIFT Team" # The short X.Y version @@ -27,6 +27,20 @@ version = "0.9" # The full version, including alpha/beta/rc tags release = "0.9.0" +# -- Find additional scripts to run as part of the documentation build ------- +import glob +import os +import subprocess + +additional_scripts = glob.glob("**/*.py", recursive=True) +# remove this script +additional_scripts.remove("conf.py") +for additional_script in additional_scripts: + wdir, script = os.path.split(additional_script) + print(f"Running {additional_script}...") + status = subprocess.run(f"python3 {script}", shell=True, cwd=wdir) + if not status.returncode == 0: + raise RuntimeError(f"Could not run script!") # -- General configuration --------------------------------------------------- @@ -63,7 +77,7 @@ master_doc = "index" # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -114,7 +128,7 @@ htmlhelp_basename = "SWIFTSPHWIthFine-grainedinter-dependentTaskingdoc" latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # - # 'papersize': 'letterpaper', + "papersize": "a4paper", # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', @@ -132,9 +146,9 @@ latex_elements = { latex_documents = [ ( master_doc, - "SWIFTSPHWIthFine-grainedinter-dependentTasking.tex", - "SWIFT: SPH WIth Fine-grained inter-dependent Tasking Documentation", - "Josh Borrow", + "SWIFT-user-manual.tex", + "SWIFT user \& developer documentation", + "SWIFT Collaboration", "manual", ) ] diff --git a/doc/RTD/source/index.rst b/doc/RTD/source/index.rst index b079710d7df407740eae2d0771d8b250ea0b5c07..6b03d77368f4599eed6e9ea9c70f572946ac97db 100644 --- a/doc/RTD/source/index.rst +++ b/doc/RTD/source/index.rst @@ -7,9 +7,22 @@ Welcome to SWIFT: SPH With Inter-dependent Fine-grained Tasking's documentation! ================================================================================ Want to get started using SWIFT? Check out the on-boarding guide available -here. SWIFT can be used as a drop-in replacement for Gadget-2 and initial -conditions in hdf5 format for Gadget can directly be read by SWIFT. The only -difference is the parameter file that will need to be adapted for SWIFT. +`here <https://swift.strw.leidenuniv.nl/onboarding.pdf>`_. + +.. figure:: CitingSWIFT/SWIFT_logo.png + :width: 300px + :align: center + :alt: SWIFT logo + +SWIFT is an open-source cosmological and astrophysical numerical +solver designed to run efficiently on modern hardware. A comprehensive +and extensive set of models for galaxy formation as well as planetary +physics are provided alongside a large series of examples. + +This users' and developers' documentation is best enjoyed with a glass +of Amarone and Rachmaninoff's second piano concerto in the +background. We note that good results have also been reported using a +tumbler of 16yrs old Lagavulin. .. toctree:: :maxdepth: 2 @@ -27,6 +40,7 @@ difference is the parameter file that will need to be adapted for SWIFT. FriendsOfFriends/index VELOCIraptorInterface/index LineOfSights/index + LightCones/index EquationOfState/index ExternalPotentials/index Neutrinos/index diff --git a/doc/onboardingGuide/Makefile b/doc/onboardingGuide/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b9fa825763f21a1574d394ce5d58b8b4c25b5b8f --- /dev/null +++ b/doc/onboardingGuide/Makefile @@ -0,0 +1,21 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + diff --git a/doc/onboardingGuide/README.md b/doc/onboardingGuide/README.md new file mode 100644 index 0000000000000000000000000000000000000000..53ab61249453e58faa7bcec715eeef9ef85c0d5e --- /dev/null +++ b/doc/onboardingGuide/README.md @@ -0,0 +1,11 @@ +SWIFT Onboarding Guide +====================== + +This is an onboarding guide for SWIFT that can be found on `swiftsim.com/onboarding.pdf`. + +You will need the `sphinx` and python package (pip install it), as well as a working +TeX distribution. + +To build the documentation (from this directory), run ``make latexpdf`` and the output +files will be created in `build/latex/`. + diff --git a/doc/onboardingGuide/source/conf.py b/doc/onboardingGuide/source/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..09be4bc3c1d300b3dcd855f5502265eba87d794e --- /dev/null +++ b/doc/onboardingGuide/source/conf.py @@ -0,0 +1,172 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = "SWIFT Onboarding Guide" +copyright = "2023, SWIFT Collaboration" +author = "SWIFT Collaboration" + +# The full version, including alpha/beta/rc tags +release = "0.0" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "alabaster" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + + +# -- Options for LaTeX-PDF output -------------------------------------------- +# See https://www.sphinx-doc.org/en/master/latex.html for more options + +latex_theme = "howto" + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + "papersize": "a4paper", + # The font size ('10pt', '11pt' or '12pt'). + "pointsize": "10pt", + # Font package inclusion. + "fontpkg": r""" +\usepackage[sfdefault]{AlegreyaSans} %% Option 'black' gives heavier bold face +\renewcommand*\oldstylenums[1]{{\AlegreyaSansOsF #1}} +""", + # Other possible choices: + # \usepackage[sfdefault]{ClearSans} %% option 'sfdefault' activates Clear Sans as the default text font + # \usepackage[sfdefault,light]{merriweather} %% Option 'black' gives heavier bold face + # %\usepackage[sfdefault,book]{FiraSans} %% option 'sfdefault' activates Fira Sans as the default text font + # %\usepackage{marcellus} %% option 'sfdefault' activates Fira Sans as the default text font + # ------------------------------------------ + # override use of fncychap + "fncychap": r""" +""", + # pass options to packages sphinx already loads + "passoptionstopackages": r""" +\PassOptionsToPackage{top=6.1cm, bottom=1cm, left=0.6cm, right=0.6cm}{geometry} +""", + # Additional stuff for the LaTeX preamble. + "preamble": r""" +\usepackage{multicol} % use two columns throughout the document + +\setcounter{secnumdepth}{0} % turn off chapter numbering + +% Reduce spacing after headings +%------------------------------------ +% https://tex.stackexchange.com/questions/53338/reducing-spacing-after-headings +\usepackage{titlesec} + +\titlespacing\title{0pt}{0pt plus 0pt minus 0pt}{0pt plus 0pt minus 0pt} +\titlespacing\chapter{0pt}{0pt plus 0pt minus 0pt}{0pt plus 0pt minus 0pt} +\titlespacing\section{0pt}{4pt plus 0pt minus 8pt}{4pt plus 0pt minus 4pt} +\titlespacing\subsection{0pt}{1pt plus 0pt minus 4pt}{0pt plus 0pt minus 8pt} +\titlespacing\subsubsection{0pt}{1pt plus 0pt minus 2pt}{0pt plus 0pt minus 8pt} + +% Reduce section font sizes +%------------------------------------ +\titleformat*{\section}{\Large\bf} +\titleformat*{\subsection}{\normalsize\bf} +\titleformat*{\subsubsection}{\small\bf} + + + +% Modify the way inline verbatim behaves. +%------------------------------------------ +% this version changes the text color. +%\definecolor{inlineVerbatimTextColor}{rgb}{0.6, 0.4, 0.5} +%\protect\renewcommand{\sphinxcode}[1]{\textcolor{inlineVerbatimTextColor}{\texttt{#1}}} + +% this version keeps the text color, but adds a colorful box. +\definecolor{inlineVerbatimBorderColor}{rgb}{0.90, 1.00, 0.95} + +\protect\renewcommand{\sphinxcode}[1]{\colorbox{inlineVerbatimBorderColor}{\texttt{#1}}} + + +% Drawing and image positioning +%--------------------------------- +\usepackage{tikz} +\usetikzlibrary{positioning} + + +% Reduce space between \item +%---------------------------------- + +\let\tempone\itemize +\let\temptwo\enditemize +\renewenvironment{itemize}{\tempone\addtolength{\itemsep}{-0.5\baselineskip}}{\temptwo} +""", + # last thing before \begin{document}: + "makeindex": r""" +\pagestyle{empty} +""", + # override title making. + "maketitle": r""" +\begin{multicols}{2} % make two columns +""", + # additional footer content + "atendofbody": r""" +\end{multicols} % make two columns +""", + # override ToC. + "tableofcontents": r""" +""", + "extraclassoptions": r""" +""", + # sphinx related stuff + "sphinxsetup": r""" +VerbatimColor={rgb}{0.90, 1.00, 0.95}, +verbatimwithframe=false, +hmargin={0.6cm, 0.6cm}, +vmargin={5.2cm, 1cm}, +TitleColor={rgb}{0.40,0.00,0.33}, +OuterLinkColor={rgb}{0., 0.40, 0.27}, +""", +} + +latex_documents = [ + ( + "index", # startdocname + "onboarding.tex", # targetname + "SWIFT Onboarding Guide", # title + "SWIFT Collaboration", # author + "howto", # theme + False, # toctree only + ) +] diff --git a/doc/onboardingGuide/source/dependencies.rst b/doc/onboardingGuide/source/dependencies.rst new file mode 100644 index 0000000000000000000000000000000000000000..ba63589f8a47f52929d8fca57e3d06f6b1dbb748 --- /dev/null +++ b/doc/onboardingGuide/source/dependencies.rst @@ -0,0 +1,74 @@ +.. dependencies + +Dependencies +============ + +To compile SWIFT, you will need the following libraries: + +HDF5 +~~~~ + +Version 1.8.x or higher is required. Input and output files are stored as HDF5 +and are compatible with the GADGET-2 specification. A parallel-HDF5 build +and HDF5 > 1.10.x is strongly recommended when running over MPI. + +MPI +~~~ +A recent implementation of MPI, such as Open MPI (v3.x or higher), is required, +or any library that implements at least the MPI 3 standard. + +Libtool +~~~~~~~ +The build system depends on libtool. + +FFTW +~~~~ +Version 3.3.x or higher is required for periodic gravity. + +ParMETIS or METIS +~~~~~~~~~~~~~~~~~ +One is required for domain decomposition and load balancing. + +GSL +~~~ +The GSL 2.x is required for cosmological integration. + + + +Optional Dependencies +===================== + +There are also the following *optional* dependencies: + +libNUMA +~~~~~~~ +libNUMA is used to pin threads. + +TCmalloc/Jemalloc/TBBmalloc +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +TCmalloc/Jemalloc/TBBmalloc are used for faster memory allocations when available. + +DOXYGEN +~~~~~~~ +You can build documentation for SWIFT with DOXYGEN. + +Python +~~~~~~ +To run the examples, you will need python 3 and some of the standard scientific libraries (numpy, matplotlib). +Some examples make use of the `swiftsimio <https://swiftsimio.readthedocs.io/en/latest/>`_ library, +which is a dedicated and maintained visualisation and analysis library for SWIFT. + +GRACKLE +~~~~~~~ +GRACKLE cooling is implemented in SWIFT. If you wish to take advantage of it, you will need it installed. + +HEALPix C library +~~~~~~~~~~~~~~~~~ +This is required for making light cone HEALPix maps. + +CFITSIO +~~~~~~~ +This may be required as a dependency of HEALPix. + + + diff --git a/doc/onboardingGuide/source/getting_help.rst b/doc/onboardingGuide/source/getting_help.rst new file mode 100644 index 0000000000000000000000000000000000000000..d2fe40be5d404e17272e0cfda47bed59b5aab4ea --- /dev/null +++ b/doc/onboardingGuide/source/getting_help.rst @@ -0,0 +1,14 @@ +.. getting help + +Getting Help +============ + + +Feel free to contact us on `Gitter (gitter.im/swiftsim) <https://gitter.im/swiftsim>`_ +or on our `GitHub (github.com/swiftsim/swiftsim) <https://github.com/swiftsim/swiftsim>`_ +by creating an issue. + +The code documentation is available on `swiftsim.com/docs <https://swiftsim.com/docs>`_, and +is also shipped along with the code in the ``docs/RTD`` directory. +This onboarding guide is available online as well on +`swiftsim.com/onboarding.pdf <http://www.swiftsim.com/onboarding.pdf>`_ diff --git a/doc/onboardingGuide/source/getting_the_code.rst b/doc/onboardingGuide/source/getting_the_code.rst new file mode 100644 index 0000000000000000000000000000000000000000..2447486dd781e5bb5e339ca5ed3fb2a359bc45c6 --- /dev/null +++ b/doc/onboardingGuide/source/getting_the_code.rst @@ -0,0 +1,13 @@ +.. Getting The Code + + +Getting The Code +================= + +The code is available from our GitLab (core developers) and GitHub (public mirror) +repositories. You can download it over https from the following locations: + ++ https://github.com/swiftsim/swiftsim.git ++ https://gitlab.cosma.dur.ac.uk/swift/swiftsim.git + + diff --git a/doc/onboardingGuide/source/header_first_page.rst b/doc/onboardingGuide/source/header_first_page.rst new file mode 100644 index 0000000000000000000000000000000000000000..c1b6b4b55d24fdf148bd5b218b20cbee08e58f42 --- /dev/null +++ b/doc/onboardingGuide/source/header_first_page.rst @@ -0,0 +1,16 @@ +.. Produce the header title on the first page. + +.. raw:: latex + + % remove all headers/footers + \pagestyle{empty} + + % Add image + \tikz[remember picture,overlay] + \node[opacity=1.0,inner sep=0pt, anchor=north] at (current page.north) + {\includegraphics[width=\paperwidth,height=5cm]{../../source/header_page1.jpg}}; + + % for whatever reason, an ugly spacing is added. Remove it here. + \vspace{-1.8\baselineskip} + + diff --git a/doc/onboardingGuide/source/header_page1.jpg b/doc/onboardingGuide/source/header_page1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b032908a9a82fa9e5e74d2cb0a8ff338573bafe9 Binary files /dev/null and b/doc/onboardingGuide/source/header_page1.jpg differ diff --git a/doc/onboardingGuide/source/header_page2.jpg b/doc/onboardingGuide/source/header_page2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04d69f146a7d7731c7c05deb292bcef5e022e55f Binary files /dev/null and b/doc/onboardingGuide/source/header_page2.jpg differ diff --git a/doc/onboardingGuide/source/header_second_page.rst b/doc/onboardingGuide/source/header_second_page.rst new file mode 100644 index 0000000000000000000000000000000000000000..3a84f4f0ec80f2ac2d23aa2fef6094f7e2b2114e --- /dev/null +++ b/doc/onboardingGuide/source/header_second_page.rst @@ -0,0 +1,20 @@ +.. Produce the header title on the second page. + + +.. raw:: latex + + % remove all headers/footers + \pagestyle{empty} + + % Add background image + \tikz[remember picture,overlay] + \node[opacity=1.0,inner sep=0pt, anchor=north] at (current page.north) + {\includegraphics[width=\paperwidth,height=5cm]{../../source/header_page2.jpg}}; + + % Add logo + \tikz[remember picture,overlay] + \node[below left = -1.9cm and -1.5cm] at (current page.north) + {\includegraphics[height=8cm]{../../source/logo/BirdW.pdf}}; + + % for whatever reason, an ugly spacing is added. Remove it here. + \vspace{-3.3\baselineskip} diff --git a/doc/onboardingGuide/source/index.rst b/doc/onboardingGuide/source/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..fa7c1ee6954b1392cd1b7a995a2e3ace6301e280 --- /dev/null +++ b/doc/onboardingGuide/source/index.rst @@ -0,0 +1,50 @@ +.. Onboarding Guide + Mladen Ivkovic, January 2023 + +====================== +SWIFT Onboarding Guide +====================== + +.. Note: There seems to be some issue with sphinx, where the first and highest + heading in the hierarchy will be skipped when generating pdfs. We don't make + a title for this guide anyway, so add the title of the guide above so the + first section will be displayed correctly. + + +.. This is the first page of the onboarding guide + ----------------------------------------------------------------- + +.. include:: header_first_page.rst + +.. include:: what_swift_can_do.rst +.. include:: getting_the_code.rst +.. include:: getting_help.rst +.. include:: initial_setup.rst + +.. raw:: latex + + % fill column. + \vfill\null + +.. include:: dependencies.rst + + + + +.. This is the second page of the onboarding guide + ----------------------------------------------------------------- + +.. raw:: latex + + \vfill\null + \newpage + +.. include:: header_second_page.rst + +.. include:: useful_configuration_flags.rst +.. include:: runtime_options.rst +.. include:: running_example.rst +.. include:: submission_script.rst + + + diff --git a/doc/onboardingGuide/source/initial_setup.rst b/doc/onboardingGuide/source/initial_setup.rst new file mode 100644 index 0000000000000000000000000000000000000000..947aa1973a2ba656ef0a7d0e4e24fb154f4e5fb6 --- /dev/null +++ b/doc/onboardingGuide/source/initial_setup.rst @@ -0,0 +1,29 @@ +Initial Setup +============= + +We use autotools for setup. To get a basic running version of the code (the executable binaries are found in the top directory), use: + +.. code-block:: bash + + ./autogen.sh + ./configure + make + + +MacOS Specific Oddities +~~~~~~~~~~~~~~~~~~~~~~~ + +To build on MacOS you will need to disable compiler warnings due to an +incomplete implementation of pthread barriers. DOXYGEN also has some issues on +MacOS, so it is best to leave it out. To configure: + +.. code-block:: bash + + ./configure --disable-compiler-warnings \ + --disable-doxygen-doc + +When using the ``clang`` compiler, the hand-written vectorized routines +have to be disabled. This is done at configuration time by adding +the flag ``--disable-hand-vec``. + + diff --git a/doc/onboardingGuide/source/logo/BirdBW.pdf b/doc/onboardingGuide/source/logo/BirdBW.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1f7edcc34222b693f8660f7e4b4c0476d60887e9 Binary files /dev/null and b/doc/onboardingGuide/source/logo/BirdBW.pdf differ diff --git a/doc/onboardingGuide/source/logo/BirdW.pdf b/doc/onboardingGuide/source/logo/BirdW.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fb1a27a726c328f295f4100bbcfb5eb6d714f23a Binary files /dev/null and b/doc/onboardingGuide/source/logo/BirdW.pdf differ diff --git a/doc/onboardingGuide/source/logo/JustTheBird.pdf b/doc/onboardingGuide/source/logo/JustTheBird.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2c3584f01df3fc6ec02bdcf6d1763e134dceae8b Binary files /dev/null and b/doc/onboardingGuide/source/logo/JustTheBird.pdf differ diff --git a/doc/onboardingGuide/source/running_example.rst b/doc/onboardingGuide/source/running_example.rst new file mode 100644 index 0000000000000000000000000000000000000000..cbd41644f5882f5b315231bf820bdf03f6994b2a --- /dev/null +++ b/doc/onboardingGuide/source/running_example.rst @@ -0,0 +1,88 @@ +.. Running an Example + Josh Borrow, 5th April 2018 + Mladen Ivkovic, Jan 2023 + +Running an Example +================== + +SWIFT provides a number of examples that you can run in the ``examples/`` directory. +Many are detailed in their respective ``README`` files, and contain python scripts +(files with the suffix ``.py``) to both generate initial conditions and plot results. +The python scripts usually contain their respective documentation at the top of the +script file itself. + + +Sod Shock +~~~~~~~~~ + +In this example, we will run the 3D SodShock test. You will need to configure and +compile the code as follows: + +.. code-block:: bash + + ./configure + make + +Then to run the code, we first download and build the +initial conditions: + +.. code-block:: bash + + cd examples/HydroTests/SodShock_3D + ./getGlass.sh + python3 makeIC.py + ../../../swift --hydro --threads=4 sodShock.yml + +We can plot the solution with the included python script +as follows: + +.. code-block:: bash + + python3 plotSolution.py 1 + +The argument ``1`` tells the python plotting script to use the snapshot with number +1 for the plot. + + + +Small Cosmological Volume +~~~~~~~~~~~~~~~~~~~~~~~~~ + +As a second example, we run a small cosmolgical +volume containing dark matter only starting at redshift :math:`z = 50`. +Like for the Sod Shock example, it suffices to configure (``./configure``) and +compile (``make``) the code without any extra flags. + +After downloading the initial conditions, we run the code with cosmology and +self-gravity: + +.. code-block:: bash + + cd examples/SmallCosmoVolume/SmallCosmoVolume_DM + ./getIC.sh + ../../../swift --cosmology --self-gravity \ + --threads=8 small_cosmo_volume_dm.yml + + +We can plot the solution with the included python script +as follows: + +.. code-block:: bash + + python3 plotProjection.py 31 + +The ``plotProjection.py`` script requires the +`swiftsimio <https://swiftsimio.readthedocs.io/en/latest/>`_ +library. + +An example containing both baryonic and dark matter is +``examples/SmallCosmoVolume/SmallCosmoVolume_hydro``. To run with +hydrodynamics, the ``--hydro`` flag needs to be provided as well: + +.. code-block:: bash + + ../../../swift --cosmology --self-gravity \ + --hydro --threads=8 small_cosmo_volume.yml + + + diff --git a/doc/onboardingGuide/source/runtime_options.rst b/doc/onboardingGuide/source/runtime_options.rst new file mode 100644 index 0000000000000000000000000000000000000000..f0166a58d6dd0270599918b33cec98e7a2794f86 --- /dev/null +++ b/doc/onboardingGuide/source/runtime_options.rst @@ -0,0 +1,22 @@ +.. Runtime Options + Josh Borrow, 5th April 2018 + +Runtime Options and Parameter Files +=================================== + +SWIFT requires a number of runtime options to run and get any sensible output. +For instance, just running the ``swift`` binary will not use any SPH or gravity; +the particles will just sit still! + +A list of command line options can be found by running the compiled binary with +the ``-h`` or ``--help`` flag: + +.. code-block:: bash + + ./swift --help + + +You will also need to specify a number of runtime parameters that are dependent +on your compile-time configuration in a parameter file. A list of all of these +parameters can be found in ``examples/parameter_example.yml``, and you can check +out examples in the ``examples/`` directory. diff --git a/doc/onboardingGuide/source/submission_script.rst b/doc/onboardingGuide/source/submission_script.rst new file mode 100644 index 0000000000000000000000000000000000000000..500038b725a98bffa0be635cce349e7dc0b5e028 --- /dev/null +++ b/doc/onboardingGuide/source/submission_script.rst @@ -0,0 +1,23 @@ +.. Submission Script + +Submission Script +================= + + +Below is an example submission script for the SLURM batch system. This runs +SWIFT with MPI, thread pinning, hydrodynamics, and self-gravity. + +.. code-block:: bash + + #SBATCH --partition=<queue> + #SBATCH --account-name=<groupName> + #SBATCH --job-name=<jobName> + #SBATCH --nodes=<nNodes> + #SBATCH --ntasks-per-node=<nMPIRank> + #SBATCH --cpus-per-task=<nThreadsPerMPIRank> + #SBATCH --time=<hh>:<mm>:<ss> + + srun -n $SLURM_NTASKS ./swift_mpi \ + --threads=$SLURM_CPUS_PER_TASK --pin \ + --hydro --self-gravity parameter_file.yml + diff --git a/doc/onboardingGuide/source/useful_configuration_flags.rst b/doc/onboardingGuide/source/useful_configuration_flags.rst new file mode 100644 index 0000000000000000000000000000000000000000..586a5222c2bcfb442c703a0109f863c76fd51449 --- /dev/null +++ b/doc/onboardingGuide/source/useful_configuration_flags.rst @@ -0,0 +1,46 @@ +.. Configuration Options + Josh Borrow, 5th April 2018 + +Useful Configuration Flags +========================== + +A description of the available options for all flags including the examples +below can be found by using +``./configure --help``. + +``--with-hydro=sphenix`` +~~~~~~~~~~~~~~~~~~~~~~~~ +There are several hydrodynamical schemes available in SWIFT. You can choose +between them at compile-time with this option. + +``--with-riemann-solver=none`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Some hydrodynamical schemes, for example GIZMO, require a Riemann solver. + +``--with-kernel=cubic-spline`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Several kernels are made available for use with the hydrodynamical schemes. +Choose between them with this compile-time flag. + +``--with-hydro-dimension=3`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Run problems in 1, 2, and 3 (default) dimensions. + +``--with-equation-of-state=ideal-gas`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Several equations of state are made available with this flag. + +``--with-cooling=none`` +~~~~~~~~~~~~~~~~~~~~~~~ +Several cooling implementations (including GRACKLE) are available. + +``--with-ext-potential=none`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Many external potentials are available for use with SWIFT. + +.. You can choose + between them at compile time. Some examples include a central potential, a + softened central potential, and a sinusoidal potential. You will need to + configure, for example, the mass in your parameter file at runtime. + + diff --git a/doc/onboardingGuide/source/what_swift_can_do.rst b/doc/onboardingGuide/source/what_swift_can_do.rst new file mode 100644 index 0000000000000000000000000000000000000000..cf97c7a2cb947e68a8487cae4304b2a183f8debd --- /dev/null +++ b/doc/onboardingGuide/source/what_swift_can_do.rst @@ -0,0 +1,21 @@ +What SWIFT can do +================= + +SWIFT can solve a variety of problems aimed at cosmological and +astrophysical applications. SWIFT's features include: + ++ Hydrodynamics, using a variety of particle methods ++ Planetary science, with e.g. multiple equations of state ++ Dark Matter ++ Neutrinos ++ Gravity: self-gravity and external potentials ++ Cosmology ++ Radiative cooling ++ Radiative transfer ++ On-the-fly analysis: halo finding (FOF), power spectra ++ And more! + +To enable and use these features, SWIFT needs to be compiled accordingly +and corresponding flags need to be passed at runtime. Please consult the +instructions provided in the `documentation <https://swiftsim.com/docs>`_ +for full details. diff --git a/examples/Cooling/ConstantCosmoTempEvolution/run.sh b/examples/Cooling/ConstantCosmoTempEvolution/run.sh index 710613ec9a431a007ed7b83e2d5634ee24eea7ec..2bf33de8a0217f0b7e1c2a70f0446678355529cd 100755 --- a/examples/Cooling/ConstantCosmoTempEvolution/run.sh +++ b/examples/Cooling/ConstantCosmoTempEvolution/run.sh @@ -20,7 +20,7 @@ then fi # Run SWIFT -../../swift --hydro --cosmology --cooling --threads=4 const_cosmo_temp_evol.yml 2>&1 | tee output.log +../../../swift --hydro --cosmology --cooling --threads=4 const_cosmo_temp_evol.yml 2>&1 | tee output.log # Plot the result python3 plot_thermal_history.py cooling_box diff --git a/examples/Cooling/CoolingBox/coolingBox.yml b/examples/Cooling/CoolingBox/coolingBox.yml index 3907f16d6efab9a71318eace116445d08f6227a0..7f4393781496ecc71e81de2544638dcda4d45c58 100644 --- a/examples/Cooling/CoolingBox/coolingBox.yml +++ b/examples/Cooling/CoolingBox/coolingBox.yml @@ -23,6 +23,9 @@ Snapshots: Statistics: delta_time: 1e-3 # Time between statistics output +Scheduler: + tasks_per_cell: 64 + # 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). @@ -78,3 +81,13 @@ EAGLEChemistry: # Solar abundances GEARPressureFloor: jeans_factor: 0. # Number of particles required to suppose a resolved clump and avoid the pressure floor. + +COLIBRECooling: + dir_name: ./UV_dust1_CR1_G1_shield1.hdf5 # Location of the cooling tables + H_reion_z: 7.5 # Redshift of Hydrogen re-ionization (Planck 2018) + 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 + delta_logTEOS_subgrid_properties: 0.3 # delta log T above the EOS below which the subgrid properties use Teq assumption + rapid_cooling_threshold: 0.333333 # Switch to rapid cooling regime for dt / t_cool above this threshold. diff --git a/examples/Cooling/CoolingBox/makeIC.py b/examples/Cooling/CoolingBox/makeIC.py index d027dcdd74adb30e0338edac3528bda3ed790ca1..d32c40e8a32745418a5e650b8c95cad7488942dc 100644 --- a/examples/Cooling/CoolingBox/makeIC.py +++ b/examples/Cooling/CoolingBox/makeIC.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2016 Stefan Arridge (stefan.arridge@durhama.ac.uk) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 diff --git a/examples/Cooling/CoolingBox/plotEnergy.py b/examples/Cooling/CoolingBox/plotEnergy.py index 0f767cd1bee739b1cf7b07203ce6a668f63526f4..64d34c6127244e6ed920cc7f4b44676f034a8904 100644 --- a/examples/Cooling/CoolingBox/plotEnergy.py +++ b/examples/Cooling/CoolingBox/plotEnergy.py @@ -1,46 +1,43 @@ -from h5py import File -import numpy as np +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 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/>. +# +############################################################################## import matplotlib -from glob import glob matplotlib.use("Agg") import matplotlib.pyplot as plt +import numpy as np +from glob import glob +import h5py -# 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": (5, 5), - "figure.subplot.left": 0.145, - "figure.subplot.right": 0.99, - "figure.subplot.bottom": 0.11, - "figure.subplot.top": 0.99, - "figure.subplot.wspace": 0.15, - "figure.subplot.hspace": 0.12, - "lines.markersize": 6, - "lines.linewidth": 3.0, -} -plt.rcParams.update(params) - +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") # Some constants in cgs units k_b_cgs = 1.38e-16 # boltzmann m_h_cgs = 1.67e-24 # proton mass - # File containing the total energy -stats_filename = "./energy.txt" +stats_filename = "./statistics.txt" # First snapshot snap_filename = "coolingBox_0000.hdf5" # Read the initial state of the gas -f = File(snap_filename, "r") +f = h5py.File(snap_filename, "r") # Read the units parameters from the snapshot units = f["InternalCodeUnits"] @@ -60,12 +57,13 @@ def energyUnits(u): # Read energy and time arrays array = np.genfromtxt(stats_filename, skip_header=1) -time = array[:, 0] * unit_time -total_mass = array[:, 1] -total_energy = array[:, 2] -kinetic_energy = array[:, 3] -internal_energy = array[:, 4] -radiated_energy = array[:, 8] +time = array[:, 1] * unit_time +total_mass = array[:, 4] +kinetic_energy = array[:, 13] +internal_energy = array[:, 14] +potential_energy = array[:, 15] +radiated_energy = array[:, 16] +total_energy = kinetic_energy + internal_energy + potential_energy initial_energy = total_energy[0] # Conversions to cgs @@ -87,7 +85,7 @@ N = len(files) temp_snap = np.zeros(N) time_snap_cgs = np.zeros(N) for i in range(N): - snap = File(files[i], "r") + snap = h5py.File(files[i], "r") u = snap["/PartType0/InternalEnergies"][:] * snap["/PartType0/Masses"][:] u = sum(u) / total_mass[0] temp_snap[i] = energyUnits(u) @@ -96,12 +94,13 @@ for i in range(N): plt.figure() -Myr_in_yr = 3.15e13 -plt.plot(time, total_energy_cgs, "r-", lw=1.6, label="Gas total energy") -plt.plot(time_snap_cgs, temp_snap, "rD", ms=3) -plt.plot(time, radiated_energy_cgs, "g-", lw=1.6, label="Radiated energy") +Myr_in_s = 1e6 * 365.25 * 24.0 * 60.0 * 60.0 +plt.plot(time / Myr_in_s, total_energy_cgs, "r-", lw=1.6, label="Gas total energy") +# statistics and snapshots may not be at same timestep and frequency +plt.plot(time_snap_cgs / Myr_in_s, temp_snap, "rD", ms=3) +plt.plot(time / Myr_in_s, radiated_energy_cgs, "g-", lw=1.6, label="Radiated energy") plt.plot( - time, + time / Myr_in_s, total_energy_cgs + radiated_energy_cgs, "b-", lw=0.6, @@ -112,4 +111,6 @@ plt.legend(loc="right", fontsize=8, frameon=False, handlelength=3, ncol=1) plt.xlabel("${\\rm{Time~[Myr]}}$", labelpad=0) plt.ylabel("${\\rm{Internal ~Energy ~(u ~m_H / k_B) ~[K]}}$") +plt.tight_layout() + plt.savefig("energy.png", dpi=200) diff --git a/examples/Cooling/CoolingBox/run.sh b/examples/Cooling/CoolingBox/run.sh index ae5b6d361e3364028864882d3400e702c8e670fb..d83974dd34c71310d1eca7c7376de83fd20d3210 100755 --- a/examples/Cooling/CoolingBox/run.sh +++ b/examples/Cooling/CoolingBox/run.sh @@ -10,7 +10,7 @@ fi if [ ! -e coolingBox.hdf5 ] then echo "Generating initial conditions for the cooling box example..." - python makeIC.py + python3 makeIC.py fi # Get the Grackle cooling table @@ -21,7 +21,7 @@ then fi # Run SWIFT -../../swift --hydro --cooling --threads=4 -n 1000 coolingBox.yml +../../../swift --hydro --cooling --threads=4 -n 1000 coolingBox.yml # Check energy conservation and cooling rate -python plotEnergy.py +python3 plotEnergy.py diff --git a/examples/Cooling/CoolingHalo/makeIC.py b/examples/Cooling/CoolingHalo/makeIC.py index 8008995bd8f654695bafc721c0383af7bbf07e9c..0693a98aaa55ebb0678bf969c553a8ffea82ef62 100644 --- a/examples/Cooling/CoolingHalo/makeIC.py +++ b/examples/Cooling/CoolingHalo/makeIC.py @@ -24,7 +24,7 @@ import math import random # Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) -# usage: python makeIC.py 1000: generate 1000 particles +# usage: python3 makeIC.py 1000: generate 1000 particles # Some constants diff --git a/examples/Cooling/CoolingHalo/makeIC_random_box.py b/examples/Cooling/CoolingHalo/makeIC_random_box.py index f6f4e33911fd21b7b78f1b7b84ad2fe3cbefdca9..4b9bc17b7e4ec48e590df4b7765f4fc68bd32f8c 100644 --- a/examples/Cooling/CoolingHalo/makeIC_random_box.py +++ b/examples/Cooling/CoolingHalo/makeIC_random_box.py @@ -24,7 +24,7 @@ import math import random # Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) -# usage: python makeIC.py 1000: generate 1000 particles +# usage: python3 makeIC.py 1000: generate 1000 particles # Some constants diff --git a/examples/Cooling/CoolingHalo/run.sh b/examples/Cooling/CoolingHalo/run.sh index ca086fc93d5c4c34567c4405d7f3670972bbde9d..e22a3c50d166873c060a723533c8e5b7ace08367 100755 --- a/examples/Cooling/CoolingHalo/run.sh +++ b/examples/Cooling/CoolingHalo/run.sh @@ -2,12 +2,12 @@ # Generate the initial conditions if they are not present. echo "Generating initial conditions for the isothermal potential box example..." -python makeIC.py 10000 +python3 makeIC.py 10000 -../../swift --external-gravity --hydro --cooling --threads=16 cooling_halo.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --cooling --threads=16 cooling_halo.yml 2>&1 | tee output.log -python radial_profile.py 2. 200 100 +python3 radial_profile.py 2. 200 100 -python internal_energy_profile.py 2. 200 100 +python3 internal_energy_profile.py 2. 200 100 -python test_energy_conservation.py 2. 200 100 +python3 test_energy_conservation.py 2. 200 100 diff --git a/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml b/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml index 1b29e1376e47ad32beacaf9bfb5408b8ff4d3191..1f73ad57b5a7396d96402746d2296784bd4ec551 100644 --- a/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml +++ b/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml @@ -10,13 +10,16 @@ InternalUnitSystem: 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-5 # The minimal time-step size of the simulation (in internal units). + dt_min: 1e-8 # The minimal time-step size of the simulation (in internal units). dt_max: 1e-1 # The maximal time-step size of the simulation (in internal units). # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output - + +Scheduler: + tasks_per_cell: 200 + # Parameters governing the snapshots Snapshots: basename: CoolingHalo # Common part of the name of output files @@ -39,8 +42,10 @@ IsothermalPotential: vrot: 200. # Rotation speed of isothermal potential in internal units timestep_mult: 0.03 # Controls time step epsilon: 1.0 # Softening for the isothermal potential + useabspos: 0 + position: [0., 0., 0.] # Cooling parameters LambdaCooling: - lambda_nH2_cgs: 1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) + lambda_nH2_cgs: 1e-23 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) cooling_tstep_mult: 0.1 # Dimensionless pre-factor for the time-step condition diff --git a/examples/Cooling/CoolingHaloWithSpin/density_profile.py b/examples/Cooling/CoolingHaloWithSpin/density_profile.py index e9db59a6dd9d7e1dc2ef023756b3385663973703..a35f9b563dd287c1eb56eb93031c0ceada74df2a 100644 --- a/examples/Cooling/CoolingHaloWithSpin/density_profile.py +++ b/examples/Cooling/CoolingHaloWithSpin/density_profile.py @@ -30,8 +30,8 @@ unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"] unit_time_cgs = unit_length_cgs / unit_velocity_cgs v_c = float(params.attrs["IsothermalPotential:vrot"]) v_c_cgs = v_c * unit_velocity_cgs -lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"]) -X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"]) +lambda_cgs = float(params.attrs["LambdaCooling:lambda_nH2_cgs"]) +X_H = float(params.attrs["SPH:H_mass_fraction"]) header = f["Header"] N = header.attrs["NumPart_Total"][0] box_centre = np.array(header.attrs["BoxSize"]) diff --git a/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py b/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py index 581a48482a0c0304a69325505aa9c248468fb12f..495564b2cd35ea8c9feeabfafa4fb08653b86e15 100644 --- a/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py +++ b/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py @@ -51,8 +51,8 @@ unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"] unit_time_cgs = unit_length_cgs / unit_velocity_cgs v_c = float(params.attrs["IsothermalPotential:vrot"]) v_c_cgs = v_c * unit_velocity_cgs -lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"]) -X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"]) +lambda_cgs = float(params.attrs["LambdaCooling:lambda_nH2_cgs"]) +X_H = float(params.attrs["SPH:H_mass_fraction"]) header = f["Header"] N = header.attrs["NumPart_Total"][0] box_centre = np.array(header.attrs["BoxSize"]) @@ -79,7 +79,7 @@ for i in range(n_snaps): radius_over_virial_radius = radius_cgs / r_vir_cgs # get the internal energies - u_dset = f["PartType0/InternalEnergy"] + u_dset = f["PartType0/InternalEnergies"] u = np.array(u_dset) # make dimensionless diff --git a/examples/Cooling/CoolingHaloWithSpin/makeIC.py b/examples/Cooling/CoolingHaloWithSpin/makeIC.py index b81c1585e55b65ed02dc1dbbe52c8b34025caba4..db6b1454e181a7b84850aa2780cf9739d275ac73 100644 --- a/examples/Cooling/CoolingHaloWithSpin/makeIC.py +++ b/examples/Cooling/CoolingHaloWithSpin/makeIC.py @@ -24,7 +24,7 @@ import math import random # Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) -# usage: python makeIC.py 1000: generate 1000 particles +# usage: python3 makeIC.py 1000: generate 1000 particles # Some constants diff --git a/examples/Cooling/CoolingHaloWithSpin/makeIC_random_box.py b/examples/Cooling/CoolingHaloWithSpin/makeIC_random_box.py index f6f4e33911fd21b7b78f1b7b84ad2fe3cbefdca9..4b9bc17b7e4ec48e590df4b7765f4fc68bd32f8c 100644 --- a/examples/Cooling/CoolingHaloWithSpin/makeIC_random_box.py +++ b/examples/Cooling/CoolingHaloWithSpin/makeIC_random_box.py @@ -24,7 +24,7 @@ import math import random # Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) -# usage: python makeIC.py 1000: generate 1000 particles +# usage: python3 makeIC.py 1000: generate 1000 particles # Some constants diff --git a/examples/Cooling/CoolingHaloWithSpin/run.sh b/examples/Cooling/CoolingHaloWithSpin/run.sh index 17ec5251a1071e36413ba926d14a179c1d6ed36b..1c7346b2b478e3a7c67403a5ecd56689f75b029a 100755 --- a/examples/Cooling/CoolingHaloWithSpin/run.sh +++ b/examples/Cooling/CoolingHaloWithSpin/run.sh @@ -2,13 +2,13 @@ # Generate the initial conditions if they are not present. echo "Generating initial conditions for the isothermal potential box example..." -python makeIC.py 10000 +python3 makeIC.py 10000 # Run SWIFT with external potential, SPH and cooling -../../swift --external-gravity --hydro --cooling --threads=1 cooling_halo.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --cooling --threads=1 cooling_halo.yml 2>&1 | tee output.log -# python radial_profile.py 10 +# python3 radial_profile.py 10 -# python internal_energy_profile.py 10 +# python3 internal_energy_profile.py 10 -# python test_energy_conservation.py +# python3 test_energy_conservation.py diff --git a/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py b/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py index acd44f6560297a6dcb83bd349ac0e1481feb3640..7d94a2cc537b9825fb8c713dacc367004658be4c 100644 --- a/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py +++ b/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py @@ -116,6 +116,6 @@ for i in range(n_snaps): % (snap_time_cgs, N, v_c) ) plt.ylim((0, 2)) - plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" % i + plot_filename = "./velocity_profile_%03d.png" % i plt.savefig(plot_filename, format="png") plt.close() diff --git a/examples/Cooling/CoolingRates/Makefile.am b/examples/Cooling/CoolingRates/Makefile.am index 7fa7d5f6cad1f3a8c5512722d5afe3c994e1619f..604a4691a4983ac206f482c0002b2f5c472ad0bc 100644 --- a/examples/Cooling/CoolingRates/Makefile.am +++ b/examples/Cooling/CoolingRates/Makefile.am @@ -1,5 +1,5 @@ # tHIS FIle is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk). +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,12 +15,12 @@ # 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_builddir)/examples $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(OPENMP_CFLAGS) +AM_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/examples $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(OPENMP_CFLAGS) $(CHEALPIX_CFLAGS) AM_LDFLAGS = $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) # Extra libraries. -EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS) $(GSL_LIBS) +EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS) $(GSL_LIBS) $(CHEALPIX_LIBS) # Programs. bin_PROGRAMS = cooling_rates diff --git a/examples/Cooling/CoolingRates/README b/examples/Cooling/CoolingRates/README index 7ac84c0e0aa64dc75d54a520c9997750b4977a39..bf6f7e9eaec4f7c4785e2ad21248b4e0f42074a2 100644 --- a/examples/Cooling/CoolingRates/README +++ b/examples/Cooling/CoolingRates/README @@ -3,12 +3,12 @@ rate from each of the elements depending on internal energy, density and redshift based on the EAGLE tables. To do so, the function in src/cooling/EAGLE returning the cooling rate is run for multiple values of the internal energy. The resulting cooling rates are written -to files and plotted with a python script (cooling_rates_plot.py). +to files and plotted with a python3 script (cooling_rates_plot.py). The test may be run by: ./getCoolingTables.sh ./cooling_rates -z X -d Y -python plot_cooling_rates.py +python3 plot_cooling_rates.py where X is the redshift at which the cooling rates are evaluated and Y is the base 10 logarithm of the hydrogen number density expressed in diff --git a/examples/Cooling/CoolingRates/cooling_rates.c b/examples/Cooling/CoolingRates/cooling_rates.c index 07d82b3056223b0a36498c4815e08db7c764b911..55528b5abfa9fed828ee5f3c9edddacd0ea3579a 100644 --- a/examples/Cooling/CoolingRates/cooling_rates.c +++ b/examples/Cooling/CoolingRates/cooling_rates.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> diff --git a/examples/Cooling/CoolingRedshiftDependence/plotSolution.py b/examples/Cooling/CoolingRedshiftDependence/plotSolution.py index cb624be3cfb1f0f803cfce7a14fb1a772cccf515..2fe095ec948f78a8575561c62596bb078f3b67f0 100644 --- a/examples/Cooling/CoolingRedshiftDependence/plotSolution.py +++ b/examples/Cooling/CoolingRedshiftDependence/plotSolution.py @@ -36,6 +36,9 @@ def get_data_dump(metadata): + "$\\bf{Hydrodynamics}$\n" + metadata.hydro_info + "\n\n" + + "$\\bf{Cooling}$\n" + + metadata.subgrid_scheme["Cooling Model"].decode("utf-8") + + "\n\n" + "$\\bf{Viscosity}$\n" + viscosity + "\n\n" @@ -68,8 +71,6 @@ def setup_axes(): for a in ax[:-1]: a.set_xlim(0, 100) - fig.tight_layout(pad=0.5) - return fig, ax @@ -103,12 +104,15 @@ def get_data(handle: float, n_snaps: int): try: energies.append( np.mean( - (data.gas.internal_energies * data.gas.masses).to(erg).value + (data.gas.internal_energies * data.gas.masses) + .astype(np.float64) + .to(erg) + .value ) * data.metadata.scale_factor ** (2) ) radiated_energies.append( - np.mean(data.gas.radiated_energy.to(erg).value) + np.mean(data.gas.radiated_energy.astype(np.float64).to(erg).value) ) except AttributeError: continue @@ -152,7 +156,7 @@ def plot_single_data( label_radiated = "" label_sum = "" - ax[2].plot(data[0], data[3], label=label_energy, ls="dotted", C=f"C{run}") + ax[2].plot(data[0], data[3], label=label_energy, ls="dotted", color=f"C{run}") # ax[2].plot(data[0], data[4], label=label_radiated, ls="dashed", C=f"C{run}") @@ -222,4 +226,5 @@ if __name__ == "__main__": fig, ax = make_plot(handles, names, timestep_names) + fig.tight_layout(pad=0.5) fig.savefig("redshift_dependence.png", dpi=300) diff --git a/examples/Cooling/CoolingRedshiftDependence/run.sh b/examples/Cooling/CoolingRedshiftDependence/run.sh index c9bf2aea7edd33886219dd4db822d930baff7d47..d771ac8aaa0cbfbe5c25fedc72117228efc910b4 100755 --- a/examples/Cooling/CoolingRedshiftDependence/run.sh +++ b/examples/Cooling/CoolingRedshiftDependence/run.sh @@ -14,7 +14,7 @@ then python3 makeIC.py fi -swift_location="../../swift" +swift_location="../../../swift" rm data/redshift_dependence_*_z_*.hdf5 diff --git a/examples/Cooling/CoolingSedovBlast_3D/README b/examples/Cooling/CoolingSedovBlast_3D/README new file mode 100644 index 0000000000000000000000000000000000000000..361cdd69eac8108a5603cc3c2501ae1ae425d912 --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/README @@ -0,0 +1,29 @@ +Realistic SNe Sedov-Taylor explosion (realistic in the sense that it uses the +right order of magnitude for all variables). Meant to be run with const Lambda +cooling, but should also work with other cooling (the workflow also obtains +the EAGLE cooling tables). + +The included Makefile (run.make) contains the full workflow that runs the +simulation and creates a plot of the energy evolution, the evolution of the +shock radius and velocity, and a movie of the density, velocity and temperature +profile. + +To run the test, use + make -f run.make +this will run the simulation in a subdirectory called "run". + +To run in a different subdirectory (useful for comparisons), use + make -f run.make folder=my_subdirectory_name + +The energy and shock evolution scripts (plot_energy.py and plot_time_profile.py) +can also be run in comparison mode: + python3 plot_energy.py \ + subdirectory1/statistics.txt subdirectory1/statistics.txt \ + energy_1_vs_2.png \ + --names "Run 1" "Run 2" +and + python3 plot_time_evolution.py \ + subdirectory1/profile.txt subdirectory1/profile.txt \ + time_evolution_1_vs_2.png \ + --names "Run 1" "Run 2" +(there is not limit on the number of runs that can be included). diff --git a/examples/Cooling/CoolingSedovBlast_3D/getGlass.sh b/examples/Cooling/CoolingSedovBlast_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/Cooling/CoolingSedovBlast_3D/get_time_profile.py b/examples/Cooling/CoolingSedovBlast_3D/get_time_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..00a5bb4817fc45088f4af60cdd95475d1d0e25cd --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/get_time_profile.py @@ -0,0 +1,69 @@ +import numpy as np +import h5py +import argparse +import scipy.stats as stats +import unyt + +argparser = argparse.ArgumentParser() +argparser.add_argument("input", nargs="+") +argparser.add_argument("output") +args = argparser.parse_args() + +nfile = len(args.input) + +time = np.zeros(nfile) +radius = np.zeros(nfile) +velocity = np.zeros(nfile) + +for ifile, file in enumerate(args.input): + with h5py.File(file, "r") as handle: + coords = handle["PartType0/Coordinates"][:] + rho = handle["PartType0/Densities"][:] + vs = handle["PartType0/Velocities"][:] + t = handle["Header"].attrs["Time"][0] + box = handle["Header"].attrs["BoxSize"][:] + + units = dict(handle["Units"].attrs) + uM = (units["Unit mass in cgs (U_M)"][0] * unyt.g).in_base("galactic") + uL = (units["Unit length in cgs (U_L)"][0] * unyt.cm).in_base("galactic") + ut = (units["Unit time in cgs (U_t)"][0] * unyt.s).in_base("galactic") + + coords = (coords * uL).in_base("galactic") + rho = (rho * uM / uL ** 3).in_base("galactic") + vs = (vs * uL / ut).in_base("galactic") + t = (t * ut).in_base("galactic") + box = (box * uL).in_base("galactic") + + coords -= 0.5 * box[None, :] + + x = np.sqrt((coords ** 2).sum(axis=1)) + v = (coords * vs).sum(axis=1) / x + + x.convert_to_units("kpc") + v.convert_to_units("km/s") + t.convert_to_units("Myr") + + rhohist, _, _ = stats.binned_statistic(x, rho, statistic="median", bins=100) + vhist, edges, _ = stats.binned_statistic(x, v, statistic="mean", bins=100) + mids = 0.5 * (edges[1:] + edges[:-1]) + + rhohist[np.isnan(rhohist)] = 0.0 + imax = np.argmax(rhohist) + + time[ifile] = t + radius[ifile] = mids[imax] + velocity[ifile] = vhist[imax] + +isort = np.argsort(time) + +time = time[isort] +radius = radius[isort] +velocity = velocity[isort] + +with open(args.output, "w") as handle: + handle.write("# Radial profile of shock wave\n") + handle.write("# Column 0: time (Myr)\n") + handle.write("# Column 1: radius (kpc)\n") + handle.write("# Column 2: velocity (km/s)\n") + for t, r, v in zip(time, radius, velocity): + handle.write(f"{t:.5e}\t{r:.5e}\t{v:.5e}\n") diff --git a/examples/Cooling/CoolingSedovBlast_3D/makeIC.py b/examples/Cooling/CoolingSedovBlast_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..3449d73253422428a6c4367cc33d08f429560da9 --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/makeIC.py @@ -0,0 +1,149 @@ +############################################################################### +# 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 * + +# Generates a swift IC file for the Sedov blast test in a periodic cubic box + +# Parameters +gamma = 5.0 / 3.0 # Gas adiabatic index +n0 = 0.1 # cm^-3 +mp = 1.67e-24 # g +kB = 1.381e-16 # erg K^-1 +pc = 3.086e18 # cm +rho0 = n0 * mp # Background density +T0 = 1.0e4 # Background pressure +E0 = 1.0e51 # Energy of the explosion +N_inject = 32 # Number of particles in which to inject energy +L = 256.0 * pc +fileName = "sedov.hdf5" + +print(rho0, "cm s^-3") + +uL = 1.0e3 * pc +uM = 1.99e30 +uv = 1.0e10 +ut = uL / uv +uU = uv ** 2 +print("ut:", ut / 3.154e7, "yr") + +# --------------------------------------------------- +glass = h5py.File("glassCube_64.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:, :] +h = glass["/PartType0/SmoothingLength"][:] + +pos *= L +h *= L + +for i in range(3): + pos[pos[:, i] < 0.0, i] += L + pos[pos[:, i] >= L, i] -= L + +numPart = size(h) + +# newpos = zeros((numPart*8,3)) +# newh = zeros(numPart*8) +# for ix in range(2): +# for iy in range(2): +# for iz in range(2): +# offset = ix*4+iy*2+iz +# newpos[offset*numPart:(offset+1)*numPart,:] = 0.5*pos[:,:] +# newpos[offset*numPart:(offset+1)*numPart,0] += 0.5*ix*L +# newpos[offset*numPart:(offset+1)*numPart,1] += 0.5*iy*L +# newpos[offset*numPart:(offset+1)*numPart,2] += 0.5*iz*L +# newh[offset*numPart:(offset+1)*numPart] = 0.5*h[:] + +# pos = newpos +# h = newh +print(h, h.min(), h.max()) + +numPart = size(h) +vol = L ** 3 + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) +r = zeros(numPart) + +r = sqrt( + (pos[:, 0] - 0.5 * L) ** 2 + (pos[:, 1] - 0.5 * L) ** 2 + (pos[:, 2] - 0.5 * L) ** 2 +) +m[:] = rho0 * vol / numPart +# u[:] = P0 / (rho0 * (gamma - 1)) +u[:] = kB * T0 / ((gamma - 1.0) * mp) + +print(u.mean(), E0 / (N_inject * m[0])) +print(E0 / (N_inject * m[0]) / u.mean()) + +# Make the central particle detonate +index = argsort(r) +u[index[0:N_inject]] = E0 / (N_inject * m[0]) + +pos /= uL +h /= uL +L /= uL +m /= uM +u /= uU + +print("L:", L) +print("m:", m.mean()) +print("h:", h.mean()) +print("u:", u.mean(), u.min(), u.max()) +print("pos:", pos.min(), pos.max()) + +# -------------------------------------------------- + +# File +file = h5py.File(fileName, "w") + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [L, L, L] +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["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 + +# Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = uL +grp.attrs["Unit mass in cgs (U_M)"] = uM +grp.attrs["Unit time in cgs (U_t)"] = ut +grp.attrs["Unit current in cgs (U_I)"] = 1.0 +grp.attrs["Unit temperature in cgs (U_T)"] = 1.0 + +# 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") + +file.close() diff --git a/examples/Cooling/CoolingSedovBlast_3D/make_visualisations.make b/examples/Cooling/CoolingSedovBlast_3D/make_visualisations.make new file mode 100644 index 0000000000000000000000000000000000000000..8c31a20fc3a0fcfbff7ce282bccd3a0dd452d1a3 --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/make_visualisations.make @@ -0,0 +1,20 @@ +folder=. +snaps=$(shell ls ${folder}/sedov_*.hdf5) +imgs=$(patsubst ${folder}/sedov_%.hdf5,${folder}/SedovCooling_%.png,$(snaps)) + +all: ${folder}/profile.png ${folder}/SedovCooling.mp4 ${folder}/energy.png + +${folder}/energy.png: ${folder}/statistics.txt + python3 plot_energy.py ${folder}/statistics.txt ${folder}/energy.png + +${folder}/profile.png: ${folder}/profile.txt + python3 plot_time_profile.py ${folder}/profile.txt ${folder}/profile.png + +${folder}/profile.txt: $(snaps) + python3 get_time_profile.py $(snaps) ${folder}/profile.txt + +${folder}/SedovCooling.mp4: $(imgs) + ffmpeg -y -framerate 10 -pattern_type glob -i "${folder}/SedovCooling_*.png" -c:v libx264 ${folder}/SedovCooling.mp4 + +${folder}/SedovCooling_%.png: ${folder}/sedov_%.hdf5 + python3 plot_profile.py $< $@ diff --git a/examples/Cooling/CoolingSedovBlast_3D/plot_energy.py b/examples/Cooling/CoolingSedovBlast_3D/plot_energy.py new file mode 100644 index 0000000000000000000000000000000000000000..1c1e8a9d5394f3ae0270acbaa0210188f0100044 --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/plot_energy.py @@ -0,0 +1,49 @@ +import numpy as np +import argparse +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as pl + +argparser = argparse.ArgumentParser() +argparser.add_argument("input", nargs="+") +argparser.add_argument("output") +argparser.add_argument("--names", "-n", nargs="+") +args = argparser.parse_args() + +for ifile, file in enumerate(args.input): + + data = np.loadtxt( + file, + usecols=(1, 13, 14, 16), + dtype=[ + ("Time", np.float32), + ("Ekin", np.float32), + ("Etherm", np.float32), + ("Erad", np.float32), + ], + ) + + color = f"C{ifile}" + + if args.names: + pl.plot([], [], "-", color=color, label=args.names[ifile]) + + pl.plot(data["Time"], data["Ekin"], "-.", color=color) + pl.plot(data["Time"], data["Etherm"], "--", color=color) + pl.plot(data["Time"], data["Erad"], ":", color=color) + pl.plot( + data["Time"], data["Ekin"] + data["Etherm"] + data["Erad"], "-", color=color + ) + +pl.plot([], [], "k-", label="Total energy") +pl.plot([], [], "k-.", label="Kinetic energy") +pl.plot([], [], "k--", label="Thermal energy") +pl.plot([], [], "k:", label="Radiated energy") +pl.ylabel("Energy") +pl.xlabel("Time") + +pl.legend(loc="best", ncol=3) + +pl.tight_layout() +pl.savefig(args.output, dpi=300) diff --git a/examples/Cooling/CoolingSedovBlast_3D/plot_profile.py b/examples/Cooling/CoolingSedovBlast_3D/plot_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..4c242da8bb3bc6cb2865cd792328c45686ad1db9 --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/plot_profile.py @@ -0,0 +1,93 @@ +import numpy as np +import h5py +import argparse +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as pl +import scipy.stats as stats +import unyt + + +def bin_vals(x, y, log=True): + stat = "median" if log else "mean" + hist, edges, _ = stats.binned_statistic(x, y, statistic=stat, bins=100) + mids = 0.5 * (edges[1:] + edges[:-1]) + return mids, hist + + +argparser = argparse.ArgumentParser() +argparser.add_argument("input") +argparser.add_argument("output") +args = argparser.parse_args() + +x = None +rho = None +T = None +v = None +time = None +with h5py.File(args.input, "r") as handle: + coords = handle["PartType0/Coordinates"][:] + rho = handle["PartType0/Densities"][:] + T = handle["PartType0/Temperatures"][:] + vs = handle["PartType0/Velocities"][:] + time = handle["Header"].attrs["Time"][0] + box = handle["Header"].attrs["BoxSize"][:] + + units = dict(handle["Units"].attrs) + uM = (units["Unit mass in cgs (U_M)"][0] * unyt.g).in_base("galactic") + uL = (units["Unit length in cgs (U_L)"][0] * unyt.cm).in_base("galactic") + ut = (units["Unit time in cgs (U_t)"][0] * unyt.s).in_base("galactic") + + coords = (coords * uL).in_base("galactic") + rho = (rho * uM / uL ** 3).in_base("galactic") + T = T * unyt.K + vs = (vs * uL / ut).in_base("galactic") + time = (time * ut).in_base("galactic") + box = (box * uL).in_base("galactic") + + coords -= 0.5 * box[None, :] + + x = np.sqrt((coords ** 2).sum(axis=1)) + v = (coords * vs).sum(axis=1) / x + +rhohist, edges, _ = stats.binned_statistic(x, rho, statistic="median", bins=100) +mids = 0.5 * (edges[1:] + edges[:-1]) +rhohist[np.isnan(rhohist)] = 0.0 +imax = np.argmax(rhohist) +xmax = mids[imax] + +x.name = "radius" +x.convert_to_units("kpc") +v.name = "radial velocity" +v.convert_to_units("km/s") +rho.name = "density" +rho.convert_to_units("g/cm**3") +T.name = "temperature" + +fig, ax = pl.subplots(1, 3, figsize=(8, 4), sharex=True) + +with unyt.matplotlib_support: + ax[0].semilogy(x, rho, ".") + b, h = bin_vals(x, rho) + ax[0].semilogy(b, h, "--") + + ax[1].plot(x, v, ".") + b, h = bin_vals(x, v, log=False) + ax[1].plot(b, h, "--") + + ax[2].semilogy(x, T, ".") + b, h = bin_vals(x, T) + ax[2].semilogy(b, h, "--") + +for a in ax: + a.axvline(x=xmax, color="k", linestyle="--") + +ax[0].set_ylim(1.0e-28, 1.0e-24) +ax[1].set_ylim(-10.0, 200.0) +ax[2].set_ylim(10.0, 1.0e8) + +ax[1].set_title(f"t = {time:.2e}") + +pl.tight_layout() +pl.savefig(args.output, dpi=300) diff --git a/examples/Cooling/CoolingSedovBlast_3D/plot_time_profile.py b/examples/Cooling/CoolingSedovBlast_3D/plot_time_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..424850f3fef6544c9027733adb223d059cd3261e --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/plot_time_profile.py @@ -0,0 +1,41 @@ +import numpy as np +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as pl +import argparse +import unyt + +argparser = argparse.ArgumentParser() +argparser.add_argument("input", nargs="+") +argparser.add_argument("output") +argparser.add_argument("--names", "-n", nargs="+") +args = argparser.parse_args() + +fig, ax = pl.subplots(1, 2, sharex=True) + +for ifile, file in enumerate(args.input): + data = np.loadtxt( + file, + dtype=[("time", np.float32), ("radius", np.float32), ("velocity", np.float32)], + ) + + t = data["time"] * unyt.Myr + t.name = "time" + r = data["radius"] * unyt.kpc + r.name = "radius" + v = data["velocity"] * unyt.km / unyt.s + v.name = "velocity" + + label = None + if args.names: + label = args.names[ifile] + with unyt.matplotlib_support: + ax[0].plot(t, r, "-", label=label) + ax[1].plot(t, v, "-", label=label) + +if args.names: + ax[1].legend(loc="best") + +pl.tight_layout() +pl.savefig(args.output, dpi=300) diff --git a/examples/Cooling/CoolingSedovBlast_3D/run.make b/examples/Cooling/CoolingSedovBlast_3D/run.make new file mode 100644 index 0000000000000000000000000000000000000000..976962e80e071cdaea273d4ff86a2e986f3d101e --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/run.make @@ -0,0 +1,29 @@ +folder=run +pwd=$(shell pwd) + +${folder}/profile.png: ${folder}/sedov_0100.hdf5 + make -f make_visualisations.make -j 16 folder=${folder} + +${folder}/sedov_0100.hdf5: ${folder}/swift ${folder}/sedov.hdf5 ${folder}/coolingtables/redshifts.dat ${folder}/sedov.yml + cd ${folder}; ./swift --threads 8 --hydro --cooling --limiter sedov.yml + +${folder}/sedov.hdf5: sedov.hdf5 + ln -s ${pwd}/sedov.hdf5 ${folder}/sedov.hdf5 + +${folder}/coolingtables/redshifts.dat: coolingtables/redshifts.dat + ln -s ${pwd}/coolingtables ${folder}/coolingtables + +${folder}/swift: ../../../swift + mkdir -p ${folder}; ln -s ${pwd}/../../../swift ${folder}/swift + +${folder}/sedov.yml: sedov.yml + ln -s ${pwd}/sedov.yml ${folder}/sedov.yml + +sedov.hdf5: glassCube_64.hdf5 + python3 makeIC.py + +glassCube_64.hdf5: + bash getGlass.sh + +coolingtables/redshifts.dat: + bash ../getEagleCoolingTable.sh diff --git a/examples/Cooling/CoolingSedovBlast_3D/sedov.yml b/examples/Cooling/CoolingSedovBlast_3D/sedov.yml new file mode 100644 index 0000000000000000000000000000000000000000..352686068fb9d5a34d3617ab869b1a7ecd92eebf --- /dev/null +++ b/examples/Cooling/CoolingSedovBlast_3D/sedov.yml @@ -0,0 +1,99 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.99e33 # g + UnitLength_in_cgs: 3.086e21 # cm + UnitVelocity_in_cgs: 1.e5 # 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: 1.e-3 # The end time of the simulation (in internal units). + dt_min: 1.e-13 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: sedov # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1.e-5 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-5 # 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: 1.e2 + max_ghost_iterations: 1000 + h_max: 0.04 # Maximum smoothing length. Since the density in the post-shock region becomes very low, we need to make sure that this does not exceed 1/3 of the periodic box (including kernel gamma ~ 2) + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./sedov.hdf5 # The file to read + periodic: 1 + +EAGLECooling: + 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 + dir_name: ./coolingtables/ + +EAGLEChemistry: + init_abundance_metal: 0.014 # Mass fraction in *all* metals + init_abundance_Hydrogen: 0.70649785 # Mass fraction in Hydrogen + init_abundance_Helium: 0.28055534 # Mass fraction in Helium + init_abundance_Carbon: 2.0665436e-3 # Mass fraction in Carbon + init_abundance_Nitrogen: 8.3562563e-4 # Mass fraction in Nitrogen + init_abundance_Oxygen: 5.4926244e-3 # Mass fraction in Oxygen + init_abundance_Neon: 1.4144605e-3 # Mass fraction in Neon + init_abundance_Magnesium: 5.907064e-4 # Mass fraction in Magnesium + init_abundance_Silicon: 6.825874e-4 # Mass fraction in Silicon + init_abundance_Iron: 1.1032152e-3 # Mass fraction in Iron + +EoS: + isothermal_internal_energy: 1240419161676.6465 + +LambdaTTableCooling: + file_name: cooling.dat + +ConstCooling: + cooling_rate: 1.e6 + min_energy: 0.01 + cooling_tstep_mult: 2 + +LambdaCooling: + lambda_nH2_cgs: 2.e-23 + cooling_tstep_mult: 0.1 + rapid_cooling: 1 + +Scheduler: + max_top_level_cells: 12 + cell_max_size: 8000000 + cell_sub_size_pair_hydro: 256000000 + cell_sub_size_self_hydro: 32000 + cell_sub_size_pair_stars: 256000000 + cell_sub_size_self_stars: 32000 + cell_sub_size_pair_grav: 256000000 + cell_sub_size_self_grav: 32000 + cell_split_size: 400 + cell_subdepth_diff_grav: 4 + cell_extra_parts: 0 + cell_extra_sparts: 100 + cell_extra_gparts: 0 + cell_extra_bparts: 0 + cell_extra_sinks: 0 + engine_max_parts_per_ghost: 1000 + engine_max_sparts_per_ghost: 1000 + engine_max_parts_per_cooling: 10000 + nr_queues: 4 + dependency_graph_frequency: 0 + task_level_output_frequency: 0 + tasks_per_cell: 100 + links_per_tasks: 25 + mpi_message_limit: 4 diff --git a/examples/Cooling/FeedbackEvent_3D/feedback.yml b/examples/Cooling/FeedbackEvent_3D/feedback.yml index 5e4ddfc618ca64e6633772f155abbdc021cedc0e..2b61d6c3c2bf51278000f1f7c73f9cd3d58315db 100644 --- a/examples/Cooling/FeedbackEvent_3D/feedback.yml +++ b/examples/Cooling/FeedbackEvent_3D/feedback.yml @@ -44,18 +44,19 @@ LambdaCooling: # 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 + cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + with_UV_background: 0 # Enable or not the UV background + redshift: 0 # Redshift to use (-1 means time based redshift) + with_metal_cooling: 1 # Enable or not the metal cooling + provide_volumetric_heating_rates: 0 # User provide volumetric heating rates + provide_specific_heating_rates: 0 # User provide specific heating rates + self_shielding_method: 0 # Grackle (<= 3) or Gear self shielding method + max_steps: 1000 + convergence_limit: 1e-2 + thermal_time_myr: 5 # (optional) Time (in Myr) for adiabatic cooling after a feedback event. GearChemistry: - InitialMetallicity: 0.01295 + initial_metallicity: 0.01295 EAGLEChemistry: # Solar abundances init_abundance_metal: 0.014 diff --git a/examples/Cooling/FeedbackEvent_3D/plotEnergy.py b/examples/Cooling/FeedbackEvent_3D/plotEnergy.py index f4d1b6bdc34b2fae81ff9abb99a2802ec74099e9..81719dbeeaa1e972658855b776067e904fb0b6bf 100644 --- a/examples/Cooling/FeedbackEvent_3D/plotEnergy.py +++ b/examples/Cooling/FeedbackEvent_3D/plotEnergy.py @@ -1,5 +1,5 @@ """ -Plots the energy from the energy.txt file for this simulation. +Plots the energy from the statistics.txt file for this simulation. """ import matplotlib.pyplot as plt @@ -29,16 +29,17 @@ 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 +data = np.loadtxt("statistics.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 +time = data[1] * units.time +mass = data[5] * units.mass +kinetic_energy = data[13] * energy_units +thermal_energy = data[14] * energy_units +potential_energy = data[15] * energy_units +radiative_cool = data[16] * energy_units +total_energy = kinetic_energy + thermal_energy + potential_energy # Now we have to figure out how much energy we actually 'injected' background_internal_energy = ( diff --git a/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py b/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py index 6370d8b1be78892b4c12869a9a8051ea97722d82..be94a7492fa4900a757ca155d83b306e31a6ecc6 100644 --- a/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py +++ b/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py @@ -1,5 +1,5 @@ """ -Plots the energy from the energy.txt file for this simulation. +Plots the energy from the statistics.txt file for this simulation. """ import matplotlib.pyplot as plt @@ -40,16 +40,17 @@ for diffusion in diffusion_parameters: units = snapshot.metadata.units energy_units = units.mass * units.length ** 2 / (units.time ** 2) - data = np.loadtxt(f"{directory_name}/energy.txt").T + data = np.loadtxt(f"{directory_name}/statistics.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 + time = data[1] * units.time + mass = data[5] * units.mass + kinetic_energy = data[13] * energy_units + thermal_energy = data[14] * energy_units + potential_energy = data[15] * energy_units + radiative_cool = data[16] * energy_units + total_energy = kinetic_energy + thermal_energy + potential_energy # Now we have to figure out how much energy we actually 'injected' background_internal_energy = ( diff --git a/examples/Cooling/FeedbackEvent_3D/plotSolution.py b/examples/Cooling/FeedbackEvent_3D/plotSolution.py index 6631fff2244e73244d0311f4e823c42dfcea7609..5d282ffd6c256549edd5e7700d8c6e7760b6e686 100644 --- a/examples/Cooling/FeedbackEvent_3D/plotSolution.py +++ b/examples/Cooling/FeedbackEvent_3D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of the ANARCHY paper. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2019 Josh Borrow (joshua.boorrow@durham.ac.uk) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/Cooling/FeedbackEvent_3D/run.sh b/examples/Cooling/FeedbackEvent_3D/run.sh index 9111b8e8cf2cb9b0b333a1b97c04dac3975fac5c..c0bb534c40c42c8085e9ef9ffb45ca606484f07a 100755 --- a/examples/Cooling/FeedbackEvent_3D/run.sh +++ b/examples/Cooling/FeedbackEvent_3D/run.sh @@ -1,8 +1,19 @@ #!/bin/bash +set -e + +if [ ! -f glassCube_32.hdf5 ]; then + ./getGlass.sh +fi + +if [ ! -f feedback.hdf5 ]; then + echo "Generating ICs" + python3 ./makeIC.py +fi + # Run SWIFT -../../swift --hydro --cooling --limiter --threads=4 feedback.yml 2>&1 | tee output.log +../../../swift --hydro --cooling --limiter --threads=4 feedback.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 5 -python plotEnergy.py +python3 plotSolution.py 5 +python3 plotEnergy.py diff --git a/examples/Cooling/FeedbackEvent_3D/runs.sh b/examples/Cooling/FeedbackEvent_3D/runs.sh index 5e3853f1e196a2bedfcb32579209fd2f614cfadc..e2ff84caa59af30ad50977e676e7106075957a32 100644 --- a/examples/Cooling/FeedbackEvent_3D/runs.sh +++ b/examples/Cooling/FeedbackEvent_3D/runs.sh @@ -17,7 +17,7 @@ do 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 + ../../../../swift --hydro --cooling --limiter --threads=16 --param="SPH:diffusion_alpha_max:${diffusion_alpha_max}" ../feedback.yml 2>&1 | tee output.log cd .. @@ -25,7 +25,7 @@ do 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 + ../../../../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/makeIC.py b/examples/Cosmology/ComovingSodShock_1D/makeIC.py index ee3cbb679e2c6973351041f805767ed360e1aafc..18627be83985993b41c6f6fdab5c6ad319755218 100644 --- a/examples/Cosmology/ComovingSodShock_1D/makeIC.py +++ b/examples/Cosmology/ComovingSodShock_1D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/Cosmology/ComovingSodShock_1D/plotSolution.py b/examples/Cosmology/ComovingSodShock_1D/plotSolution.py index 04c5489254b7b66f712697d415fae5b97b5474ef..384367f2cb52894a60c8d5f8ae446b3d99fdd3bf 100644 --- a/examples/Cosmology/ComovingSodShock_1D/plotSolution.py +++ b/examples/Cosmology/ComovingSodShock_1D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -35,6 +35,10 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state +import sys + +sys.path.append("../../HydroTests/") +from riemannSolver import RiemannSolver import matplotlib @@ -42,29 +46,7 @@ matplotlib.use("Agg") from pylab import * import h5py -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -76,11 +58,11 @@ anow = sim["/Header"].attrs["Scale-factor"] a_i = sim["/Cosmology"].attrs["a_beg"] H_0 = sim["/Cosmology"].attrs["H0 [internal units]"] time = 2.0 * (1.0 / np.sqrt(a_i) - 1.0 / np.sqrt(anow)) / H_0 -scheme = str(sim["/HydroScheme"].attrs["Scheme"]) -kernel = str(sim["/HydroScheme"].attrs["Kernel function"]) +scheme = sim["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = sim["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] eta = sim["/HydroScheme"].attrs["Kernel eta"] -git = str(sim["Code"].attrs["Git Revision"]) +git = sim["Code"].attrs["Git Revision"].decode("utf-8") x = sim["/PartType0/Coordinates"][:, 0] v = sim["/PartType0/Velocities"][:, 0] * anow @@ -100,156 +82,23 @@ x_max = 1.0 x += x_min -# --------------------------------------------------------------- -# Don't touch anything after this. -# --------------------------------------------------------------- - -c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave -c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front - -# Helpful variable -Gama = (gas_gamma - 1.0) / (gas_gamma + 1.0) -beta = (gas_gamma - 1.0) / (2.0 * gas_gamma) - -# Characteristic function and its derivative, following Toro (2009) -def compute_f(P_3, P, c): - u = P_3 / P - if u > 1: - term1 = gas_gamma * ((gas_gamma + 1.0) * u + gas_gamma - 1.0) - term2 = sqrt(2.0 / term1) - fp = (u - 1.0) * c * term2 - dfdp = ( - c * term2 / P - + (u - 1.0) - * c - / term2 - * (-1.0 / term1 ** 2) - * gas_gamma - * (gas_gamma + 1.0) - / P - ) - else: - fp = (u ** beta - 1.0) * (2.0 * c / (gas_gamma - 1.0)) - dfdp = 2.0 * c / (gas_gamma - 1.0) * beta * u ** (beta - 1.0) / P - return (fp, dfdp) - - -# Solution of the Riemann problem following Toro (2009) -def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): - P_new = ( - (c_L + c_R + (v_L - v_R) * 0.5 * (gas_gamma - 1.0)) - / (c_L / P_L ** beta + c_R / P_R ** beta) - ) ** (1.0 / beta) - P_3 = 0.5 * (P_R + P_L) - f_L = 1.0 - while fabs(P_3 - P_new) > 1e-6: - P_3 = P_new - (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) - (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) - f = f_L + f_R + (v_R - v_L) - df = dfdp_L + dfdp_R - dp = -f / df - prnew = P_3 + dp - v_3 = v_L - f_L - return (P_new, v_3) - - -# Solve Riemann problem for post-shock region -(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) - -# Check direction of shocks and wave -shock_R = P_3 > P_R -shock_L = P_3 > P_L - -# Velocity of shock front and and rarefaction wave -if shock_R: - v_right = v_R + c_R ** 2 * (P_3 / P_R - 1.0) / (gas_gamma * (v_3 - v_R)) -else: - v_right = c_R + 0.5 * (gas_gamma + 1.0) * v_3 - 0.5 * (gas_gamma - 1.0) * v_R - -if shock_L: - v_left = v_L + c_L ** 2 * (P_3 / p_L - 1.0) / (gas_gamma * (v_3 - v_L)) -else: - v_left = c_L - 0.5 * (gas_gamma + 1.0) * v_3 + 0.5 * (gas_gamma - 1.0) * v_L - -# Compute position of the transitions -x_23 = -fabs(v_left) * time -if shock_L: - x_12 = -fabs(v_left) * time -else: - x_12 = -(c_L - v_L) * time - -x_34 = v_3 * time +# Prepare reference solution +solver = RiemannSolver(gas_gamma) -x_45 = fabs(v_right) * time -if shock_R: - x_56 = fabs(v_right) * time -else: - x_56 = (c_R + v_R) * time - - -# Prepare arrays delta_x = (x_max - x_min) / N -x_s = arange(x_min, x_max, delta_x) -rho_s = zeros(N) -P_s = zeros(N) -v_s = zeros(N) - -# Compute solution in the different regions -for i in range(N): - if x_s[i] <= x_12: - rho_s[i] = rho_L - P_s[i] = P_L - v_s[i] = v_L - if x_s[i] >= x_12 and x_s[i] < x_23: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1.0 + Gama * P_3 / P_L) - P_s[i] = P_3 - v_s[i] = v_3 - else: - rho_s[i] = rho_L * ( - Gama * (0.0 - x_s[i]) / (c_L * time) + Gama * v_L / c_L + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = P_L * (rho_s[i] / rho_L) ** gas_gamma - v_s[i] = (1.0 - Gama) * (c_L - (0.0 - x_s[i]) / time) + Gama * v_L - if x_s[i] >= x_23 and x_s[i] < x_34: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1 + Gama * P_3 / p_L) - else: - rho_s[i] = rho_L * (P_3 / P_L) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_34 and x_s[i] < x_45: - if shock_R: - rho_s[i] = rho_R * (Gama + P_3 / P_R) / (1.0 + Gama * P_3 / P_R) - else: - rho_s[i] = rho_R * (P_3 / P_R) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_45 and x_s[i] < x_56: - if shock_R: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - else: - rho_s[i] = rho_R * ( - Gama * (x_s[i]) / (c_R * time) - Gama * v_R / c_R + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = p_R * (rho_s[i] / rho_R) ** gas_gamma - v_s[i] = (1.0 - Gama) * (-c_R - (-x_s[i]) / time) + Gama * v_R - if x_s[i] >= x_56: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) # Additional arrays u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function +# Shock position (since we want to overplot it in the viscosity/diffusion plot +c_R = sqrt(gas_gamma * P_R / rho_R) +x_shock = (c_R + v_R) * time # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) # Velocity profile -------------------------------- subplot(231) @@ -294,7 +143,7 @@ if plot_alpha: plot(x, alpha, ".", color="r", ms=4.0) ylabel(r"${\rm{Viscosity}}~\alpha$", labelpad=0) # Show location of shock - plot([x_56, x_56], [-100, 100], color="k", alpha=0.5, ls="dashed", lw=1.2) + axvline(x=x_shock, color="k", alpha=0.5, ls="dashed", lw=1.2) ylim(0, 1) else: plot(x, S, ".", color="r", ms=4.0) @@ -308,32 +157,39 @@ xlim(-0.5, 0.5) # Information ------------------------------------- subplot(236, frameon=False) +text_fontsize = 5 + z_now = 1.0 / anow - 1.0 text( -0.49, 0.9, - "Sod shock with $\\gamma=%.3f$ in 1D at $z=%.2f$" % (gas_gamma, z_now), - fontsize=10, + "Sod shock with $\\gamma=%.3f$ in 1D at $z=%.2f$" % (gas_gamma, z_now), + fontsize=text_fontsize, ) text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, ) text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, ) z_i = 1.0 / a_i - 1.0 -text(-0.49, 0.6, "Initial redshift: $%.2f$" % z_i, fontsize=10) +text(-0.49, 0.6, "Initial redshift: $%.2f$" % z_i, fontsize=text_fontsize) plot([-0.49, 0.1], [0.52, 0.52], "k-", lw=1) -text(-0.49, 0.4, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.3, scheme, fontsize=10) -text(-0.49, 0.2, kernel, fontsize=10) -text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) +text(-0.49, 0.4, "SWIFT %s" % git, fontsize=text_fontsize) +text(-0.49, 0.3, scheme, fontsize=text_fontsize) +text(-0.49, 0.2, kernel, fontsize=text_fontsize) +text( + -0.49, + 0.1, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) xlim(-0.5, 0.5) ylim(0, 1) xticks([]) diff --git a/examples/Cosmology/ComovingSodShock_1D/run.sh b/examples/Cosmology/ComovingSodShock_1D/run.sh index 2eae1729e007ac087654cb7b04d0701542cb4c75..34b0dffd8d5f5dde8314c23d00cc2bb8ad08577a 100755 --- a/examples/Cosmology/ComovingSodShock_1D/run.sh +++ b/examples/Cosmology/ComovingSodShock_1D/run.sh @@ -4,11 +4,11 @@ if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the 1D SodShock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --cosmology --hydro --threads=1 sodShock.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --threads=1 sodShock.yml 2>&1 | tee output.log # Plot the result -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/Cosmology/ComovingSodShock_2D/makeIC.py b/examples/Cosmology/ComovingSodShock_2D/makeIC.py index edf46bcdc860dd4d7fce8bf373832ea965559d7a..3d2f90ebb76b6745a11bed84aed163644079cafc 100644 --- a/examples/Cosmology/ComovingSodShock_2D/makeIC.py +++ b/examples/Cosmology/ComovingSodShock_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/Cosmology/ComovingSodShock_2D/plotSolution.py b/examples/Cosmology/ComovingSodShock_2D/plotSolution.py index 8dc83a645466497c9fd04ab4e1f619666cdfd6f1..02fcc49d2e4902abf9fa0622906b90850d104ac4 100644 --- a/examples/Cosmology/ComovingSodShock_2D/plotSolution.py +++ b/examples/Cosmology/ComovingSodShock_2D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -35,6 +35,10 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state +import sys + +sys.path.append("../../HydroTests/") +from riemannSolver import RiemannSolver import matplotlib @@ -43,29 +47,7 @@ from pylab import * from scipy import stats import h5py -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -77,11 +59,11 @@ anow = sim["/Header"].attrs["Scale-factor"] a_i = sim["/Cosmology"].attrs["a_beg"] H_0 = sim["/Cosmology"].attrs["H0 [internal units]"] time = 2.0 * (1.0 / np.sqrt(a_i) - 1.0 / np.sqrt(anow)) / H_0 -scheme = sim["/HydroScheme"].attrs["Scheme"] -kernel = sim["/HydroScheme"].attrs["Kernel function"] +scheme = sim["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = sim["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] eta = sim["/HydroScheme"].attrs["Kernel eta"] -git = sim["Code"].attrs["Git Revision"] +git = sim["Code"].attrs["Git Revision"].decode("utf-8") x = sim["/PartType0/Coordinates"][:, 0] v = sim["/PartType0/Velocities"][:, 0] * anow @@ -96,7 +78,7 @@ x_max = 1.0 x += x_min -# Bin te data +# Bin the data x_bin_edge = np.arange(-0.6, 0.6, 0.02) x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge) @@ -116,153 +98,19 @@ S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2) -# Analytic solution -c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave -c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front - -# Helpful variable -Gama = (gas_gamma - 1.0) / (gas_gamma + 1.0) -beta = (gas_gamma - 1.0) / (2.0 * gas_gamma) - -# Characteristic function and its derivative, following Toro (2009) -def compute_f(P_3, P, c): - u = P_3 / P - if u > 1: - term1 = gas_gamma * ((gas_gamma + 1.0) * u + gas_gamma - 1.0) - term2 = sqrt(2.0 / term1) - fp = (u - 1.0) * c * term2 - dfdp = ( - c * term2 / P - + (u - 1.0) - * c - / term2 - * (-1.0 / term1 ** 2) - * gas_gamma - * (gas_gamma + 1.0) - / P - ) - else: - fp = (u ** beta - 1.0) * (2.0 * c / (gas_gamma - 1.0)) - dfdp = 2.0 * c / (gas_gamma - 1.0) * beta * u ** (beta - 1.0) / P - return (fp, dfdp) - - -# Solution of the Riemann problem following Toro (2009) -def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): - P_new = ( - (c_L + c_R + (v_L - v_R) * 0.5 * (gas_gamma - 1.0)) - / (c_L / P_L ** beta + c_R / P_R ** beta) - ) ** (1.0 / beta) - P_3 = 0.5 * (P_R + P_L) - f_L = 1.0 - while fabs(P_3 - P_new) > 1e-6: - P_3 = P_new - (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) - (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) - f = f_L + f_R + (v_R - v_L) - df = dfdp_L + dfdp_R - dp = -f / df - prnew = P_3 + dp - v_3 = v_L - f_L - return (P_new, v_3) - - -# Solve Riemann problem for post-shock region -(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) - -# Check direction of shocks and wave -shock_R = P_3 > P_R -shock_L = P_3 > P_L - -# Velocity of shock front and and rarefaction wave -if shock_R: - v_right = v_R + c_R ** 2 * (P_3 / P_R - 1.0) / (gas_gamma * (v_3 - v_R)) -else: - v_right = c_R + 0.5 * (gas_gamma + 1.0) * v_3 - 0.5 * (gas_gamma - 1.0) * v_R - -if shock_L: - v_left = v_L + c_L ** 2 * (P_3 / p_L - 1.0) / (gas_gamma * (v_3 - v_L)) -else: - v_left = c_L - 0.5 * (gas_gamma + 1.0) * v_3 + 0.5 * (gas_gamma - 1.0) * v_L +# Prepare reference solution +solver = RiemannSolver(gas_gamma) -# Compute position of the transitions -x_23 = -fabs(v_left) * time -if shock_L: - x_12 = -fabs(v_left) * time -else: - x_12 = -(c_L - v_L) * time - -x_34 = v_3 * time - -x_45 = fabs(v_right) * time -if shock_R: - x_56 = fabs(v_right) * time -else: - x_56 = (c_R + v_R) * time - - -# Prepare arrays delta_x = (x_max - x_min) / N -x_s = arange(x_min, x_max, delta_x) -rho_s = zeros(N) -P_s = zeros(N) -v_s = zeros(N) - -# Compute solution in the different regions -for i in range(N): - if x_s[i] <= x_12: - rho_s[i] = rho_L - P_s[i] = P_L - v_s[i] = v_L - if x_s[i] >= x_12 and x_s[i] < x_23: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1.0 + Gama * P_3 / P_L) - P_s[i] = P_3 - v_s[i] = v_3 - else: - rho_s[i] = rho_L * ( - Gama * (0.0 - x_s[i]) / (c_L * time) + Gama * v_L / c_L + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = P_L * (rho_s[i] / rho_L) ** gas_gamma - v_s[i] = (1.0 - Gama) * (c_L - (0.0 - x_s[i]) / time) + Gama * v_L - if x_s[i] >= x_23 and x_s[i] < x_34: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1 + Gama * P_3 / p_L) - else: - rho_s[i] = rho_L * (P_3 / P_L) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_34 and x_s[i] < x_45: - if shock_R: - rho_s[i] = rho_R * (Gama + P_3 / P_R) / (1.0 + Gama * P_3 / P_R) - else: - rho_s[i] = rho_R * (P_3 / P_R) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_45 and x_s[i] < x_56: - if shock_R: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - else: - rho_s[i] = rho_R * ( - Gama * (x_s[i]) / (c_R * time) - Gama * v_R / c_R + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = p_R * (rho_s[i] / rho_R) ** gas_gamma - v_s[i] = (1.0 - Gama) * (-c_R - (-x_s[i]) / time) + Gama * v_R - if x_s[i] >= x_56: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) # Additional arrays u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function - # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) # Velocity profile -------------------------------- subplot(231) @@ -317,32 +165,39 @@ ylim(0.8, 3.8) # Information ------------------------------------- subplot(236, frameon=False) +text_fontsize = 5 + z_now = 1.0 / anow - 1.0 text( -0.49, 0.9, - "Sod shock with $\\gamma=%.3f$ in 2D at $z=%.2f$" % (gas_gamma, z_now), - fontsize=10, + "Sod shock with $\\gamma=%.3f$ in 2D at $z=%.2f$" % (gas_gamma, z_now), + fontsize=text_fontsize, ) text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, ) text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, ) z_i = 1.0 / a_i - 1.0 -text(-0.49, 0.6, "Initial redshift: $%.2f$" % z_i, fontsize=10) +text(-0.49, 0.6, "Initial redshift: $%.2f$" % z_i, fontsize=text_fontsize) plot([-0.49, 0.1], [0.52, 0.52], "k-", lw=1) -text(-0.49, 0.4, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.3, scheme, fontsize=10) -text(-0.49, 0.2, kernel, fontsize=10) -text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) +text(-0.49, 0.4, "SWIFT %s" % git, fontsize=text_fontsize) +text(-0.49, 0.3, scheme, fontsize=text_fontsize) +text(-0.49, 0.2, kernel, fontsize=text_fontsize) +text( + -0.49, + 0.1, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) xlim(-0.5, 0.5) ylim(0, 1) xticks([]) diff --git a/examples/Cosmology/ComovingSodShock_2D/run.sh b/examples/Cosmology/ComovingSodShock_2D/run.sh index 1723153e3eff1f7d49970ed4e3d5f69b39b67a1a..73afff14c7e399be5062b56901848a11a226eee1 100755 --- a/examples/Cosmology/ComovingSodShock_2D/run.sh +++ b/examples/Cosmology/ComovingSodShock_2D/run.sh @@ -9,10 +9,10 @@ fi if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the Sod shock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/Cosmology/ComovingSodShock_3D/makeIC.py b/examples/Cosmology/ComovingSodShock_3D/makeIC.py index 7538f29005a8a8037ecc822f6ac8a7424e0e7c75..62e1bb081422c6696869af9f28e2373a0197f480 100644 --- a/examples/Cosmology/ComovingSodShock_3D/makeIC.py +++ b/examples/Cosmology/ComovingSodShock_3D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/Cosmology/ComovingSodShock_3D/plotSolution.py b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py index 046f577c1f9a1ea8a522105e601423137cbe17d1..9334ec4792cccdd74c2f7754a7c3f5a038e80984 100644 --- a/examples/Cosmology/ComovingSodShock_3D/plotSolution.py +++ b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -35,6 +35,10 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state +import sys + +sys.path.append("../../HydroTests/") +from riemannSolver import RiemannSolver import matplotlib @@ -43,29 +47,7 @@ from pylab import * from scipy import stats import h5py -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -77,11 +59,11 @@ anow = sim["/Header"].attrs["Scale-factor"] a_i = sim["/Cosmology"].attrs["a_beg"] H_0 = sim["/Cosmology"].attrs["H0 [internal units]"] time = 2.0 * (1.0 / np.sqrt(a_i) - 1.0 / np.sqrt(anow)) / H_0 -scheme = sim["/HydroScheme"].attrs["Scheme"] -kernel = sim["/HydroScheme"].attrs["Kernel function"] +scheme = sim["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = sim["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] eta = sim["/HydroScheme"].attrs["Kernel eta"] -git = sim["Code"].attrs["Git Revision"] +git = sim["Code"].attrs["Git Revision"].decode("utf-8") x = sim["/PartType0/Coordinates"][:, 0] v = sim["/PartType0/Velocities"][:, 0] * anow @@ -107,7 +89,7 @@ x_max = 1.0 x += x_min N = 1000 -# Bin te data +# Bin the data x_bin_edge = np.arange(-0.6, 0.6, 0.02) x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge) @@ -144,152 +126,19 @@ if plot_viscosity: ) alpha_visc_sigma_bin = np.sqrt(alpha2_visc_bin - alpha_visc_bin ** 2) -# Analytic solution -c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave -c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front - -# Helpful variable -Gama = (gas_gamma - 1.0) / (gas_gamma + 1.0) -beta = (gas_gamma - 1.0) / (2.0 * gas_gamma) - -# Characteristic function and its derivative, following Toro (2009) -def compute_f(P_3, P, c): - u = P_3 / P - if u > 1: - term1 = gas_gamma * ((gas_gamma + 1.0) * u + gas_gamma - 1.0) - term2 = sqrt(2.0 / term1) - fp = (u - 1.0) * c * term2 - dfdp = ( - c * term2 / P - + (u - 1.0) - * c - / term2 - * (-1.0 / term1 ** 2) - * gas_gamma - * (gas_gamma + 1.0) - / P - ) - else: - fp = (u ** beta - 1.0) * (2.0 * c / (gas_gamma - 1.0)) - dfdp = 2.0 * c / (gas_gamma - 1.0) * beta * u ** (beta - 1.0) / P - return (fp, dfdp) - - -# Solution of the Riemann problem following Toro (2009) -def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): - P_new = ( - (c_L + c_R + (v_L - v_R) * 0.5 * (gas_gamma - 1.0)) - / (c_L / P_L ** beta + c_R / P_R ** beta) - ) ** (1.0 / beta) - P_3 = 0.5 * (P_R + P_L) - f_L = 1.0 - while fabs(P_3 - P_new) > 1e-6: - P_3 = P_new - (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) - (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) - f = f_L + f_R + (v_R - v_L) - df = dfdp_L + dfdp_R - dp = -f / df - prnew = P_3 + dp - v_3 = v_L - f_L - return (P_new, v_3) - - -# Solve Riemann problem for post-shock region -(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) - -# Check direction of shocks and wave -shock_R = P_3 > P_R -shock_L = P_3 > P_L - -# Velocity of shock front and and rarefaction wave -if shock_R: - v_right = v_R + c_R ** 2 * (P_3 / P_R - 1.0) / (gas_gamma * (v_3 - v_R)) -else: - v_right = c_R + 0.5 * (gas_gamma + 1.0) * v_3 - 0.5 * (gas_gamma - 1.0) * v_R - -if shock_L: - v_left = v_L + c_L ** 2 * (P_3 / p_L - 1.0) / (gas_gamma * (v_3 - v_L)) -else: - v_left = c_L - 0.5 * (gas_gamma + 1.0) * v_3 + 0.5 * (gas_gamma - 1.0) * v_L - -# Compute position of the transitions -x_23 = -fabs(v_left) * time -if shock_L: - x_12 = -fabs(v_left) * time -else: - x_12 = -(c_L - v_L) * time - -x_34 = v_3 * time +# Prepare reference solution +solver = RiemannSolver(gas_gamma) -x_45 = fabs(v_right) * time -if shock_R: - x_56 = fabs(v_right) * time -else: - x_56 = (c_R + v_R) * time - - -# Prepare arrays delta_x = (x_max - x_min) / N -x_s = arange(x_min, x_max, delta_x) -rho_s = zeros(N) -P_s = zeros(N) -v_s = zeros(N) - -# Compute solution in the different regions -for i in range(N): - if x_s[i] <= x_12: - rho_s[i] = rho_L - P_s[i] = P_L - v_s[i] = v_L - if x_s[i] >= x_12 and x_s[i] < x_23: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1.0 + Gama * P_3 / P_L) - P_s[i] = P_3 - v_s[i] = v_3 - else: - rho_s[i] = rho_L * ( - Gama * (0.0 - x_s[i]) / (c_L * time) + Gama * v_L / c_L + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = P_L * (rho_s[i] / rho_L) ** gas_gamma - v_s[i] = (1.0 - Gama) * (c_L - (0.0 - x_s[i]) / time) + Gama * v_L - if x_s[i] >= x_23 and x_s[i] < x_34: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1 + Gama * P_3 / p_L) - else: - rho_s[i] = rho_L * (P_3 / P_L) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_34 and x_s[i] < x_45: - if shock_R: - rho_s[i] = rho_R * (Gama + P_3 / P_R) / (1.0 + Gama * P_3 / P_R) - else: - rho_s[i] = rho_R * (P_3 / P_R) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_45 and x_s[i] < x_56: - if shock_R: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - else: - rho_s[i] = rho_R * ( - Gama * (x_s[i]) / (c_R * time) - Gama * v_R / c_R + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = p_R * (rho_s[i] / rho_R) ** gas_gamma - v_s[i] = (1.0 - Gama) * (-c_R - (-x_s[i]) / time) + Gama * v_R - if x_s[i] >= x_56: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) # Additional arrays u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) # Velocity profile -------------------------------- subplot(231) @@ -375,35 +224,43 @@ else: # Information ------------------------------------- subplot(236, frameon=False) +text_fontsize = 5 + znow = 1.0 / anow - 1.0 text( -0.49, 0.9, - "Sod shock with $\\gamma=%.3f$ in 3D at $z=%.2f$" % (gas_gamma, znow), - fontsize=10, + "Sod shock with $\\gamma=%.3f$ in 3D at $z=%.2f$" % (gas_gamma, znow), + fontsize=text_fontsize, ) text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, ) text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, ) z_i = 1.0 / a_i - 1.0 -text(-0.49, 0.6, "Initial redshift: $%.2f$" % z_i, fontsize=10) +text(-0.49, 0.6, "Initial redshift: $%.2f$" % z_i, fontsize=text_fontsize) plot([-0.49, 0.1], [0.52, 0.52], "k-", lw=1) -text(-0.49, 0.4, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.3, scheme, fontsize=10) -text(-0.49, 0.2, kernel, fontsize=10) -text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) +text(-0.49, 0.4, "SWIFT %s" % git, fontsize=text_fontsize) +text(-0.49, 0.3, scheme, fontsize=text_fontsize) +text(-0.49, 0.2, kernel, fontsize=text_fontsize) +text( + -0.49, + 0.1, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) xlim(-0.5, 0.5) ylim(0, 1) xticks([]) yticks([]) +tight_layout() savefig("SodShock.png", dpi=200) diff --git a/examples/Cosmology/ComovingSodShock_3D/run.sh b/examples/Cosmology/ComovingSodShock_3D/run.sh index b2ee90ecf8d7eabacc8b2b848b406a233953c9e6..42ee57ddfbd62c45752314ac43e39d943c6acbb8 100755 --- a/examples/Cosmology/ComovingSodShock_3D/run.sh +++ b/examples/Cosmology/ComovingSodShock_3D/run.sh @@ -9,10 +9,10 @@ fi if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the Sod shock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/Cosmology/ComovingToroTest2_1D/makeIC.py b/examples/Cosmology/ComovingToroTest2_1D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..22508c975302aec1459fe10a0e1db6b009332269 --- /dev/null +++ b/examples/Cosmology/ComovingToroTest2_1D/makeIC.py @@ -0,0 +1,112 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 * + +# Generates a swift IC file for the 1D Toro (2009) test 2 in a periodic box +# Fun fact: because of the periodic boundaries, we get a second test for free + +# Parameters +gamma = 5.0 / 3.0 # Gas adiabatic index +numPart = 1000 # Number of particles +x_min = -1.0 +x_max = 1.0 +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state +fileName = "toroTest2.hdf5" + +unit_l_in_cgs = 3.086e18 +unit_m_in_cgs = 2.94e55 +unit_t_in_cgs = 3.086e18 +a_beg = 0.001 + +# --------------------------------------------------- + +boxSize = x_max - x_min +delta = boxSize / numPart + +# Build the arrays +coords = zeros((numPart, 3)) +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +h = zeros(numPart) +u = zeros(numPart) + +# Set the particles on the left +for i in range(numPart // 2): + coords[i, 0] = x_min + (i + 0.5) * delta + u[i] = P_L / (rho_L * (gamma - 1.0)) + h[i] = 1.2348 * delta + m[i] = delta * rho_L + v[i, 0] = v_L + +# Set the particles on the right +for j in range(numPart // 2): + i = numPart // 2 + j + coords[i, 0] = x_min + (i + 0.5) * delta + u[i] = P_R / (rho_R * (gamma - 1.0)) + h[i] = 1.2348 * delta + m[i] = delta * rho_R + v[i, 0] = v_R + +u /= a_beg ** (3.0 * (gamma - 1.0)) +v /= a_beg + +# Shift particles +coords[:, 0] -= x_min + +# File +file = h5py.File(fileName, "w") + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +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["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"] = 1 + +# Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = unit_l_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = unit_m_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = unit_t_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1.0 +grp.attrs["Unit temperature in cgs (U_T)"] = 1.0 + +# Particle group +grp = file.create_group("/PartType0") +grp.create_dataset("Coordinates", data=coords, 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") + +file.close() diff --git a/examples/Cosmology/ComovingToroTest2_1D/plotSolution.py b/examples/Cosmology/ComovingToroTest2_1D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..1aeea592856462eea01669179c96a48f7884d198 --- /dev/null +++ b/examples/Cosmology/ComovingToroTest2_1D/plotSolution.py @@ -0,0 +1,188 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 Toro (2009) test 2 and plots the SPH answer + +# Generates the analytical solution for the Toro (2009) test case 2 +# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t. +# This follows the solution given in (Toro, 2009) + +# Parameters +gas_gamma = 5.0 / 3.0 # Polytropic index +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state + +import sys + +sys.path.append("../../HydroTests/") +from riemannSolver import RiemannSolver + +import matplotlib + +matplotlib.use("Agg") +from pylab import * +import h5py + +style.use("../../../tools/stylesheets/mnras.mplstyle") + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("toroTest2_%04d.hdf5" % snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +anow = sim["/Header"].attrs["Scale-factor"] +a_i = sim["/Cosmology"].attrs["a_beg"] +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"] +time = 2.0 * (1.0 / np.sqrt(a_i) - 1.0 / np.sqrt(anow)) / H_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"] + +x = sim["/PartType0/Coordinates"][:, 0] +v = sim["/PartType0/Velocities"][:, 0] * anow +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] + +N = 1000 # Number of points +x_min = -1.0 +x_max = 1.0 + +x += x_min + +# Prepare reference solution +solver = RiemannSolver(gas_gamma) + +delta_x = (x_max - x_min) / N +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) +rho_s2, v_s2, P_s2, _ = solver.solve(rho_R, v_R, P_R, rho_L, v_L, P_L, x_s / time) +x_s2 = np.array(x_s) +x_s2 += 1.0 +s2neg = x_s2 > 1.0 +s2pos = ~s2neg +x_s2[s2neg] -= 2.0 + +# Additional arrays +u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy +s_s = P_s / rho_s ** gas_gamma # entropic function +u_s2 = P_s2 / (rho_s2 * (gas_gamma - 1.0)) # internal energy +s_s2 = P_s2 / rho_s2 ** gas_gamma # entropic function + + +# Plot the interesting quantities +figure(figsize=(7, 7 / 1.6)) + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, ".", color="r", ms=4.0) +plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], v_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], v_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, ".", color="r", ms=4.0) +plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], rho_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], rho_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, ".", color="r", ms=4.0) +plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], P_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], P_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, ".", color="r", ms=4.0) +plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], u_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], u_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) + +# Entropy profile --------------------------------- +subplot(235) + +plot(x, S, ".", color="r", ms=4.0) +plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], s_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], s_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) + + +# Information ------------------------------------- +subplot(236, frameon=False) + +text_fontsize = 5 + +z_now = 1.0 / anow - 1.0 +text( + -0.49, + 0.9, + "Toro (2009) test 2 with $\\gamma=%.3f$ in 1D at $z=%.2f$" % (gas_gamma, z_now), + fontsize=text_fontsize, +) +text( + -0.49, + 0.8, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, +) +text( + -0.49, + 0.7, + "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), + fontsize=text_fontsize, +) +plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) +text(-0.49, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +text( + -0.49, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() + +savefig("ToroTest2.png", dpi=200) diff --git a/examples/Cosmology/ComovingToroTest2_1D/run.sh b/examples/Cosmology/ComovingToroTest2_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..29b8de6a975a0800b5b580b06e946a79e984156e --- /dev/null +++ b/examples/Cosmology/ComovingToroTest2_1D/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e toroTest2.hdf5 ] +then + echo "Generating initial conditions for the 1D Toro (2009) test 2 example..." + python3 makeIC.py +fi + +# Run SWIFT +../../../swift --cosmology --hydro --threads=1 toroTest2.yml 2>&1 | tee output.log + +# Plot the result +python3 plotSolution.py 1 diff --git a/examples/Cosmology/ComovingToroTest2_1D/toroTest2.yml b/examples/Cosmology/ComovingToroTest2_1D/toroTest2.yml new file mode 100644 index 0000000000000000000000000000000000000000..dd2abc63acd7cf17435f9d3aab8ad5a700e02a84 --- /dev/null +++ b/examples/Cosmology/ComovingToroTest2_1D/toroTest2.yml @@ -0,0 +1,41 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 2.94e55 # Weird mass unit + UnitLength_in_cgs: 3.086e18 # pc + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: toroTest2 # Common part of the name of output files + delta_time: 1.06638 # Time difference between consecutive outputs (in internal units) + scale_factor_first: 0.001 + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 # 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: ./toroTest2.hdf5 # The file to read + periodic: 1 + +Cosmology: + Omega_cdm: 0. + Omega_lambda: 0. + Omega_b: 1. + h: 1. + a_begin: 0.001 + a_end: 0.00106638 diff --git a/examples/Cosmology/ConstantCosmoVolume/makeIC.py b/examples/Cosmology/ConstantCosmoVolume/makeIC.py index 48c91482eb6cf078c260402883d89551d0389240..5ee875199762d700242a440fdf4793c8b37a1008 100644 --- a/examples/Cosmology/ConstantCosmoVolume/makeIC.py +++ b/examples/Cosmology/ConstantCosmoVolume/makeIC.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published diff --git a/examples/Cosmology/ConstantCosmoVolume/plotSolution.py b/examples/Cosmology/ConstantCosmoVolume/plotSolution.py index 37f9926c939975700e5630151210ff53a5c03f59..cc25919e0ff30e32f2996f3f9205ca6d47d90833 100644 --- a/examples/Cosmology/ConstantCosmoVolume/plotSolution.py +++ b/examples/Cosmology/ConstantCosmoVolume/plotSolution.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -25,6 +25,7 @@ T_i = 100.0 # Initial temperature of the gas (in K) z_c = 1.0 # Redshift of caustic formation (non-linear collapse) z_i = 100.0 # Initial redshift gas_gamma = 5.0 / 3.0 # Gas adiabatic index +N_output = 119 # Physical constants needed for internal energy to temperature conversion kB_in_SI = 1.38064852e-23 @@ -33,32 +34,11 @@ mH_in_kg = 1.6737236e-27 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py -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.06, - "figure.subplot.right": 0.99, - "figure.subplot.bottom": 0.06, - "figure.subplot.top": 0.99, - "figure.subplot.wspace": 0.21, - "figure.subplot.hspace": 0.13, - "lines.markersize": 6, - "lines.linewidth": 3.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") # Read the simulation data sim = h5py.File("box_0000.hdf5", "r") @@ -87,25 +67,25 @@ u_0 *= a ** (-3 * (1 - gas_gamma)) S_0 = (gas_gamma - 1.0) * u_0 * rho_0 ** (-(gas_gamma - 1.0)) # Mean quantities over time -z = np.zeros(119) -a = np.zeros(119) -S_mean = np.zeros(119) -S_std = np.zeros(119) -u_mean = np.zeros(119) -u_std = np.zeros(119) -P_mean = np.zeros(119) -P_std = np.zeros(119) -rho_mean = np.zeros(119) -rho_std = np.zeros(119) - -vx_mean = np.zeros(119) -vy_mean = np.zeros(119) -vz_mean = np.zeros(119) -vx_std = np.zeros(119) -vy_std = np.zeros(119) -vz_std = np.zeros(119) - -for i in range(119): +z = np.zeros(N_output) +a = np.zeros(N_output) +S_mean = np.zeros(N_output) +S_std = np.zeros(N_output) +u_mean = np.zeros(N_output) +u_std = np.zeros(N_output) +P_mean = np.zeros(N_output) +P_std = np.zeros(N_output) +rho_mean = np.zeros(N_output) +rho_std = np.zeros(N_output) + +vx_mean = np.zeros(N_output) +vy_mean = np.zeros(N_output) +vz_mean = np.zeros(N_output) +vx_std = np.zeros(N_output) +vy_std = np.zeros(N_output) +vz_std = np.zeros(N_output) + +for i in range(N_output): sim = h5py.File("box_%04d.hdf5" % i, "r") z[i] = sim["/Cosmology"].attrs["Redshift"][0] @@ -140,117 +120,177 @@ rho_mean_phys = rho_mean / a ** 3 u_mean_phys = u_mean / a ** (3 * (gas_gamma - 1.0)) S_mean_phys = S_mean -# Solution in physical coordinates -# T_solution = np.ones(T) / a - -figure() +vx_mean_phys = vx_mean / a +vy_mean_phys = vy_mean / a +vz_mean_phys = vz_mean / a +vx_std_phys = vx_std / a +vy_std_phys = vy_std / a +vz_std_phys = vz_std / a + +# Plot the interesting quantities +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=4, + markeredgecolor="none", + alpha=0.2, + zorder=-1, + rasterized=True, + linestyle="none", +) # Density evolution -------------------------------- -subplot(231) # , yscale="log") -semilogx(a, rho_mean / rho_0, "-", color="r", lw=1) -semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0) -semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0) -semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0) -text(1e-2, 1.0105, "+1\\%", color="0.6", fontsize=9) -text(1e-2, 0.9895, "-1\\%", color="0.6", fontsize=9, va="top") -text(1e-2, 1.015, "$\\rho_0=%.3f$" % rho_0) -ylim(0.98, 1.02) -xlim(8e-3, 1.1) -xlabel("${\\rm Scale-factor}$", labelpad=0.0) -ylabel("${\\rm Comoving~density}~\\rho / \\rho_0$", labelpad=0.0) +plt.subplot(231) # , yscale="log") +plt.semilogx(a, rho_mean / rho_0, **scatter_props) +plt.semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0) +plt.semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0) +plt.semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0) +plt.text(1e-2, 1.0105, "+1%", color="0.6", fontsize=9) +plt.text(1e-2, 0.9895, "-1%", color="0.6", fontsize=9, va="top") +plt.text(1e-2, 1.015, "$\\rho_0=%.3f$" % rho_0) +plt.ylim(0.98, 1.02) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0) +plt.ylabel("${\\rm Comoving~density}~\\rho / \\rho_0$", labelpad=0.0) # Thermal energy evolution -------------------------------- -subplot(232) # , yscale="log") -semilogx(a, u_mean / u_0, "-", color="r", lw=1) -semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0) -semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0) -semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0) -text(1e-2, 1.0105, "+1\\%", color="0.6", fontsize=9) -text(1e-2, 0.9895, "-1\\%", color="0.6", fontsize=9, va="top") -text(1e-2, 1.015, "$u_0=%.3e$" % (u_0)) -ylim(0.98, 1.02) -xlim(8e-3, 1.1) -xlabel("${\\rm Scale-factor}$", labelpad=0.0) -ylabel("${\\rm Comoving~internal~energy}~u / u_0$", labelpad=0.0) +plt.subplot(232) # , yscale="log") +plt.semilogx(a, u_mean / u_0, **scatter_props) +plt.semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0) +plt.semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0) +plt.semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0) +plt.text(1e-2, 1.0105, "+1%", color="0.6", fontsize=9) +plt.text(1e-2, 0.9895, "-1%", color="0.6", fontsize=9, va="top") +plt.text(1e-2, 1.015, "$u_0=%.3e$" % (u_0)) +plt.ylim(0.98, 1.02) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0) +plt.ylabel("${\\rm Comoving~internal~energy}~u / u_0$", labelpad=0.0) # Entropy evolution -------------------------------- -subplot(233) # , yscale="log") -semilogx(a, S_mean / S_0, "-", color="r", lw=1) -semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0) -semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0) -semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0) -text(1e-2, 1.0105, "+1\\%", color="0.6", fontsize=9) -text(1e-2, 0.9895, "-1\\%", color="0.6", fontsize=9, va="top") -text(1e-2, 1.015, "$A_0=%.3e$" % (S_0)) -ylim(0.98, 1.02) -xlim(8e-3, 1.1) -xlabel("${\\rm Scale-factor}$", labelpad=0.0) -ylabel("${\\rm Comoving~entropy}~A / A_0$", labelpad=0.0) +plt.subplot(233) # , yscale="log") +plt.semilogx(a, S_mean / S_0, **scatter_props) +plt.semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0) +plt.semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0) +plt.semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0) +plt.text(1e-2, 1.0105, "+1%", color="0.6", fontsize=9) +plt.text(1e-2, 0.9895, "-1%", color="0.6", fontsize=9, va="top") +plt.text(1e-2, 1.015, "$A_0=%.3e$" % (S_0)) +plt.ylim(0.98, 1.02) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0) +plt.ylabel("${\\rm Comoving~entropy}~A / A_0$", labelpad=0.0) # Peculiar velocity evolution --------------------- -subplot(234) -semilogx(a, vx_mean, "-", color="r", lw=1) -semilogx(a, vy_mean, "-", color="g", lw=1) -semilogx(a, vz_mean, "-", color="b", lw=1) -xlabel("${\\rm Scale-factor}$", labelpad=0.0) -ylabel("${\\rm Peculiar~velocity~mean}$", labelpad=-5.0) +plt.subplot(234) +plt.semilogx(a, vx_mean, **scatter_props) +plt.semilogx(a, vy_mean, **scatter_props) +plt.semilogx(a, vz_mean, **scatter_props) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0) +plt.ylabel("${\\rm Peculiar~velocity~mean}$", labelpad=-5.0) # Peculiar velocity evolution --------------------- -subplot(235) -semilogx(a, vx_std, "--", color="r", lw=1) -semilogx(a, vy_std, "--", color="g", lw=1) -semilogx(a, vz_std, "--", color="b", lw=1) -xlabel("${\\rm Scale-factor}$", labelpad=0.0) -ylabel("${\\rm Peculiar~velocity~std-dev}$", labelpad=0.0) +plt.subplot(235) +plt.semilogx(a, vx_std, **scatter_props) +plt.semilogx(a, vy_std, **scatter_props) +plt.semilogx(a, vz_std, **scatter_props) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0) +plt.ylabel("${\\rm Peculiar~velocity~std-dev}$", labelpad=0.0) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) + +text_fontsize = 5 + +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) +plt.tight_layout() -savefig("ConstantBox_comoving.png", dpi=200) +plt.savefig("ConstantBox_comoving.png", dpi=200) -figure() +plt.figure(figsize=(7, 7 / 1.6)) # Density evolution -------------------------------- -subplot(231) # , yscale="log") -loglog(a, rho_mean_phys, "-", color="r", lw=1) -xlabel("${\\rm Scale-factor}$") -ylabel("${\\rm Physical~density}$") +plt.subplot(231) # , yscale="log") +plt.loglog(a, rho_mean_phys, **scatter_props) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$") +plt.ylabel("${\\rm Physical~density}$") # Thermal energy evolution -------------------------------- -subplot(232) # , yscale="log") -loglog(a, u_mean_phys, "-", color="r", lw=1) -xlabel("${\\rm Scale-factor}$") -ylabel("${\\rm Physical~internal~energy}$") +plt.subplot(232) # , yscale="log") +plt.loglog(a, u_mean_phys, **scatter_props) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$") +plt.ylabel("${\\rm Physical~internal~energy}$") # Entropy evolution -------------------------------- -subplot(233) # , yscale="log") -semilogx(a, S_mean_phys, "-", color="r", lw=1) -xlabel("${\\rm Scale-factor}$") -ylabel("${\\rm Physical~entropy}$") +plt.subplot(233) # , yscale="log") +plt.semilogx(a, S_mean_phys, **scatter_props) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$") +plt.ylabel("${\\rm Physical~entropy}$") + +# Peculiar velocity evolution --------------------- +plt.subplot(234) +plt.semilogx(a, vx_mean_phys, **scatter_props) +plt.semilogx(a, vy_mean_phys, **scatter_props) +plt.semilogx(a, vz_mean_phys, **scatter_props) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0) +plt.ylabel("${\\rm Peculiar~velocity~mean}$", labelpad=-5.0) + +# Peculiar velocity evolution --------------------- +plt.subplot(235) +plt.semilogx(a, vx_std_phys, **scatter_props) +plt.semilogx(a, vy_std_phys, **scatter_props) +plt.semilogx(a, vz_std_phys, **scatter_props) +plt.xlim(8e-3, 1.1) +plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0) +plt.ylabel("${\\rm Peculiar~velocity~std-dev}$", labelpad=0.0) # Information ------------------------------------- -subplot(236, frameon=False) - -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - -savefig("ConstantBox_physical.png", dpi=200) +plt.subplot(236, frameon=False) + +text_fontsize = 5 + +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("ConstantBox_physical.png", dpi=200) diff --git a/examples/Cosmology/ConstantCosmoVolume/run.sh b/examples/Cosmology/ConstantCosmoVolume/run.sh index 9c0e0ccdcbbdd52b14230d98393637e99d9ad80f..384744e0d791dbc8f2ce82e94d7a0753b288eb92 100755 --- a/examples/Cosmology/ConstantCosmoVolume/run.sh +++ b/examples/Cosmology/ConstantCosmoVolume/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../../swift --hydro --cosmology --self-gravity --threads=8 constant_volume.yml 2>&1 | tee output.log +../../../swift --hydro --cosmology --self-gravity --threads=8 constant_volume.yml 2>&1 | tee output.log # Plot the result python3 plotSolution.py $i diff --git a/examples/Cosmology/NeutrinoCosmo/run.sh b/examples/Cosmology/NeutrinoCosmo/run.sh index d0122d103199d7c2468a6081d1b2921620929e02..e070ed6e3ad3697333a61dce5514b39339b76c12 100755 --- a/examples/Cosmology/NeutrinoCosmo/run.sh +++ b/examples/Cosmology/NeutrinoCosmo/run.sh @@ -8,4 +8,4 @@ then fi # Run SWIFT -../../swift -c -G --threads=8 neutrino_cosmo.yml 2>&1 | tee output.log +../../../swift -c -G --threads=8 neutrino_cosmo.yml 2>&1 | tee output.log diff --git a/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py b/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py index 5b65f7252913fe539bd06dc9d89ad1bd151c689a..293f93792a2cf4d7d0af86557a15603700df07e5 100644 --- a/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py +++ b/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py @@ -32,32 +32,13 @@ mH_in_kg = 1.6737236e-27 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py +import sys 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.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, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -86,7 +67,7 @@ P = sim["/PartType0/Pressures"][:] rho = sim["/PartType0/Densities"][:] m = sim["/PartType0/Masses"][:] try: - phi = sim["/PartType0/Potential"][:] + phi = sim["/PartType0/Potentials"][:] except KeyError: # We didn't write the potential, try to go on without print("Couldn't find potential in your output file") @@ -120,12 +101,12 @@ lambda_i = boxSize # wavelength of the perturbation # Solution taken from Springel 2010. Eqs. 127 - 130 -q = linspace(-0.5 * lambda_i, 0.5 * lambda_i, 256) -k_i = 2.0 * pi / lambda_i +q = np.linspace(-0.5 * lambda_i, 0.5 * lambda_i, 256) +k_i = 2.0 * np.pi / lambda_i zfac = (1.0 + z_c) / (1.0 + redshift) -x_s = q - zfac * sin(k_i * q) / k_i -rho_s = rho_0 / (1.0 - zfac * cos(k_i * q)) -v_s = -H_0 * (1.0 + z_c) / sqrt(1.0 + redshift) * sin(k_i * q) / k_i +x_s = q - zfac * np.sin(k_i * q) / k_i +rho_s = rho_0 / (1.0 - zfac * np.cos(k_i * q)) +v_s = -H_0 * (1.0 + z_c) / np.sqrt(1.0 + redshift) * np.sin(k_i * q) / k_i T_s = T_i * (((1.0 + redshift) / (1.0 + z_i)) ** 3.0 * rho_s / rho_0) ** (2.0 / 3.0) @@ -138,37 +119,51 @@ unit_mass_in_si = 0.001 * unit_mass_in_cgs unit_time_in_si = unit_time_in_cgs # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=4, + markeredgecolor="none", + alpha=0.2, + zorder=-1, + rasterized=True, + linestyle="none", +) # Velocity profile -------------------------------- -subplot(231) +plt.subplot(231) if np.size(x_g) > 1: - plot(x_g, v_g, "s", color="g", alpha=0.8, lw=1.2, ms=4) -plot(x, v, ".", color="r", ms=4.0) -plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) -ylabel("${\\rm{Peculiar~velocity}}~v_x$", labelpad=0) + plt.plot(x_g, v_g, "s", color="g", alpha=0.8, lw=1.2, ms=4) +plt.plot(x, v, **scatter_props) +plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Peculiar~velocity}}~v_x$", labelpad=0) # Density profile -------------------------------- -subplot(232) # , yscale="log") +plt.subplot(232) # , yscale="log") if np.size(x_g) > 1: - plot(x_g, rho_g / rho_0, "s", color="g", alpha=0.8, lw=1.2, ms=4) -plot(x, rho / rho_0, ".", color="r", ms=4.0) -plot(x_s, rho_s / rho_0, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho / \\rho_0$", labelpad=0) + plt.plot(x_g, rho_g / rho_0, "s", color="g", alpha=0.8, lw=1.2, ms=4) +plt.plot(x, rho / rho_0, **scatter_props) +plt.plot(x_s, rho_s / rho_0, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho / \\rho_0$", labelpad=0) # Potential profile -------------------------------- -subplot(233) +plt.subplot(233) if np.size(x_g) > 1: - plot(x_g, phi_g, "s", color="g", alpha=0.8, lw=1.2, ms=4) -plot(x, phi, ".", color="r", ms=4.0) -xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) -ylabel("${\\rm{Potential}}~\\phi$", labelpad=0) + plt.plot(x_g, phi_g, "s", color="g", alpha=0.8, lw=1.2, ms=4) +plt.plot(x, phi, **scatter_props) +plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Potential}}~\\phi$", labelpad=0) # Temperature profile ------------------------- -subplot(234) # , yscale="log") +plt.subplot(234) # , yscale="log") u *= unit_length_in_si ** 2 / unit_time_in_si ** 2 u_g *= unit_length_in_si ** 2 / unit_time_in_si ** 2 u /= a ** (3 * (gas_gamma - 1.0)) @@ -177,30 +172,41 @@ T = (gas_gamma - 1.0) * u * mH_in_kg / k_in_J_K T_g = (gas_gamma - 1.0) * u_g * mH_in_kg / k_in_J_K print("z = {0:.2f}, T_avg = {1:.2f}".format(redshift, T.mean())) if np.size(x_g) > 1: - plot(x_g, T_g, "s", color="g", alpha=0.8, lw=1.2, ms=4) -plot(x, T, ".", color="r", ms=4.0) -plot(x_s, T_s, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) -ylabel("${\\rm{Temperature}}~T$", labelpad=0) + plt.plot(x_g, T_g, "s", color="g", alpha=0.8, lw=1.2, ms=4) +plt.plot(x, T, **scatter_props) +plt.plot(x_s, T_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Temperature}}~T$", labelpad=0) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) + +text_fontsize = 5 -text(-0.49, 0.9, "Zeldovich pancake at z=%.2f " % (redshift), fontsize=10) -text( - -0.49, +plt.text( + -0.45, 0.9, "Zeldovich pancake at z=%.2f " % (redshift), fontsize=text_fontsize +) +plt.text( + -0.45, 0.8, "adiabatic index $\\gamma=%.2f$, viscosity $\\alpha=%.2f$" % (gas_gamma, alpha), - fontsize=10, + fontsize=text_fontsize, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - -savefig("ZeldovichPancake_%.4d.png" % snap, dpi=200) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("ZeldovichPancake_%.4d.png" % snap, dpi=200) diff --git a/examples/Cosmology/ZeldovichPancake_3D/run.sh b/examples/Cosmology/ZeldovichPancake_3D/run.sh index 0be82b2f003143f3a783b2939a4ae932952d02c0..4eef7c46471f1d4189a595b4b3b9cb6980fe379f 100755 --- a/examples/Cosmology/ZeldovichPancake_3D/run.sh +++ b/examples/Cosmology/ZeldovichPancake_3D/run.sh @@ -4,14 +4,14 @@ if [ ! -e zeldovichPancake.hdf5 ] then echo "Generating initial conditions for the 3D Zeldovich pancake example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --cosmology --self-gravity --threads=8 zeldovichPancake.yml 2>&1 | tee output.log +../../../swift --hydro --cosmology --self-gravity --threads=8 zeldovichPancake.yml 2>&1 | tee output.log # Plot the result for i in {0..119} do - python plotSolution.py $i + python3 plotSolution.py $i done 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 9042d2d1c07b35adaa5b747d314c225a8a540b9e..a8e8651238636c5eff9fdc5f530de8e3ed32ffcb 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 @@ -20,7 +20,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 1e-2 # The end time of the simulation (in internal units). dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). Scheduler: max_top_level_cells: 64 @@ -42,10 +42,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric + MAC: adaptive + epsilon_fmm: 0.001 theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 256 + mesh_side_length: 1024 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). diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh index be3cdd8e3cbc4cdbb0d8ab039bbbaa0f8e9ce2a1..5c118c26dd2c2226abfff15f6930e0903671de8b 100755 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --self-gravity --threads=16 eagle_100.yml 2>&1 | tee output.log +../../../swift --cosmology --self-gravity --threads=16 eagle_100.yml 2>&1 | tee output.log 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 bddb5e35baf4e1fed97b79254ba00d5811fac126..a6b000aec330fd729a06a6ce3ad684962f481341 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 @@ -20,7 +20,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 1e-2 # The end time of the simulation (in internal units). dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). Scheduler: max_top_level_cells: 8 @@ -42,10 +42,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric + MAC: adaptive + epsilon_fmm: 0.001 theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 32 + 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). diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh index 669fa49823cd65ff336c60964e5e565e925c53c5..c065cef8f612709480ab62b842a7beb1d2fe58a4 100755 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --self-gravity --threads=16 eagle_12.yml 2>&1 | tee output.log +../../../swift --cosmology --self-gravity --threads=16 eagle_12.yml 2>&1 | tee output.log 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 4fd5d226735ba6c49055de9b381b908c0beb5c52..4bee668c90fc5c964fae46e21ec048064ccb4526 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 @@ -20,7 +20,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 1e-2 # The end time of the simulation (in internal units). dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). Scheduler: max_top_level_cells: 16 @@ -42,10 +42,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric + MAC: adaptive + epsilon_fmm: 0.001 theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 64 + 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). diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh index 6d96edda655c7bf2d18ab8312417722f78bdb7e0..fd6e36b1db16ebdc375abfb1150cb5f06d4c060e 100755 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --self-gravity --threads=16 eagle_25.yml 2>&1 | tee output.log +../../../swift --cosmology --self-gravity --threads=16 eagle_25.yml 2>&1 | tee output.log 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 6f19042e6850b6960f03817116f9c2ea73c70bf5..cc4f5852382487e0f6862b449a8e3b5c5fa9139e 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 @@ -20,7 +20,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 1e-2 # The end time of the simulation (in internal units). dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). Scheduler: max_top_level_cells: 32 @@ -41,10 +41,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric + MAC: adaptive + epsilon_fmm: 0.001 theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 128 + 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). diff --git a/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh index 8a08d0b0408c39f58a53aeff660123f6afc5777b..069e0efc31d623e7e8fd58a711ea647b95f09626 100755 --- a/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --self-gravity --threads=16 eagle_50.yml 2>&1 | tee output.log +../../../swift --cosmology --self-gravity --threads=16 eagle_50.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_ICs/EAGLE_100/eagle_100.yml b/examples/EAGLE_ICs/EAGLE_100/eagle_100.yml index 247cba2a3582c83cab3c25e3003786919858ddab..55d97e3376553d880d534f0be7bb96b1080e17dd 100644 --- a/examples/EAGLE_ICs/EAGLE_100/eagle_100.yml +++ b/examples/EAGLE_ICs/EAGLE_100/eagle_100.yml @@ -1,6 +1,6 @@ # Define some meta-data about the simulation MetaData: - run_name: EAGLE-L0100N1504-Ref + run_name: SWIFT-EAGLE-L0100N1504-Ref # Define the system of units to use internally. InternalUnitSystem: @@ -29,6 +29,9 @@ Snapshots: basename: eagle # Common part of the name of output files output_list_on: 1 output_list: ./output_list.txt + compression: 4 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +41,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric # Use the geometric opening angle condition + MAC: adaptive # Use the geometric opening angle condition theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 0 - mesh_side_length: 512 + epsilon_fmm: 0.001 # Adaptive opening angle + mesh_side_length: 1024 comoving_DM_softening: 0.003320 # Comoving softening for DM (3.32 ckpc) max_physical_DM_softening: 0.001300 # Physical softening for DM (1.30 pkpc) comoving_baryon_softening: 0.001790 # Comoving softening for baryons (1.79 ckpc) @@ -183,14 +186,13 @@ EAGLEFeedback: SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). - SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). - 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: 1.4588 # 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. + SNII_energy_fraction_function: EAGLE # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.388 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 7.37 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.00134 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). Note the value we use is 0.1 Zsun. + SNII_energy_fraction_n_0_H_p_cm3: 0.412 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_sigma_Z: 0.311 # Width of the sigmoid for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_sigma_n: 0.428 # Width of the sigmoid for the birth density dependance of the SNII energy fraction. SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. @@ -218,9 +220,9 @@ EAGLEAGN: use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term - with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? - boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? - boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + with_boost_factor: 1 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 1 # If using the boost factor, are we using a constant boost only? + boost_alpha: 0.645 # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? @@ -228,14 +230,14 @@ EAGLEAGN: fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. - coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + coupling_efficiency: 0.035 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? - use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. - AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). @@ -243,7 +245,7 @@ EAGLEAGN: AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). - AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_delta_T_K: 4.207e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). @@ -253,8 +255,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/EAGLE_ICs/EAGLE_100/run.sh b/examples/EAGLE_ICs/EAGLE_100/run.sh index 8b40bdda3674e646219bf44f906cf3ce1b299940..7b2d0e95bc83036cddba847e0acb405a198dbb13 100755 --- a/examples/EAGLE_ICs/EAGLE_100/run.sh +++ b/examples/EAGLE_ICs/EAGLE_100/run.sh @@ -35,7 +35,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --eagle \ --threads=16 --pin \ eagle_100.yml diff --git a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml index 18bc4c9fa3669e6a1cfcc95a717eb7a25e63f45d..d37e62d290d63712aad0daa250e0e4289045beee 100644 --- a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml @@ -1,6 +1,6 @@ # Define some meta-data about the simulation MetaData: - run_name: EAGLE-L0012N0188-Ref + run_name: SWIFT-EAGLE-L0012N0188-Ref # Define the system of units to use internally. InternalUnitSystem: @@ -29,6 +29,9 @@ Snapshots: basename: eagle # Common part of the name of output files output_list_on: 1 output_list: ./output_list.txt + compression: 4 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +41,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric # Use the geometric opening angle condition + MAC: adaptive # Use the geometric opening angle condition theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 0 - mesh_side_length: 64 + epsilon_fmm: 0.001 # Adaptive opening angle + mesh_side_length: 128 comoving_DM_softening: 0.003320 # Comoving softening for DM (3.32 ckpc) max_physical_DM_softening: 0.001300 # Physical softening for DM (1.30 pkpc) comoving_baryon_softening: 0.001790 # Comoving softening for baryons (1.79 ckpc) @@ -182,14 +185,13 @@ EAGLEFeedback: SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). - SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). - 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: 1.4588 # 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. + SNII_energy_fraction_function: EAGLE # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.388 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 7.37 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.00134 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). Note the value we use is 0.1 Zsun. + SNII_energy_fraction_n_0_H_p_cm3: 0.412 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_sigma_Z: 0.311 # Width of the sigmoid for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_sigma_n: 0.428 # Width of the sigmoid for the birth density dependance of the SNII energy fraction. SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. @@ -217,9 +219,9 @@ EAGLEAGN: use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term - with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? - boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? - boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + with_boost_factor: 1 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 1 # If using the boost factor, are we using a constant boost only? + boost_alpha: 0.645 # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? @@ -227,14 +229,14 @@ EAGLEAGN: fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. - coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + coupling_efficiency: 0.035 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? - use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. - AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). @@ -242,7 +244,7 @@ EAGLEAGN: AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). - AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_delta_T_K: 4.207e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). @@ -252,8 +254,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/EAGLE_ICs/EAGLE_12/run.sh b/examples/EAGLE_ICs/EAGLE_12/run.sh index aaba58f266ffd6dc77707c45be0bec0218282328..0140f598a2697209756731934121186b946fcdfa 100755 --- a/examples/EAGLE_ICs/EAGLE_12/run.sh +++ b/examples/EAGLE_ICs/EAGLE_12/run.sh @@ -35,7 +35,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --eagle \ --threads=16 --pin \ eagle_12.yml diff --git a/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml index ac8f5e28db543c892594c7bc3b73f8438f0d5c3b..739f4a2a769e49f3151742b81945afe86b208ccd 100644 --- a/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml @@ -1,6 +1,6 @@ # Define some meta-data about the simulation MetaData: - run_name: EAGLE-L0025N0376-Ref + run_name: SWIFT-EAGLE-L0025N0376-Ref # Define the system of units to use internally. InternalUnitSystem: @@ -29,6 +29,9 @@ Snapshots: basename: eagle # Common part of the name of output files output_list_on: 1 output_list: ./output_list.txt + compression: 4 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +41,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric # Use the geometric opening angle condition + MAC: adaptive # Use the geometric opening angle condition theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 0 - mesh_side_length: 128 + epsilon_fmm: 0.001 # Adaptive opening angle + mesh_side_length: 256 comoving_DM_softening: 0.003320 # Comoving softening for DM (3.32 ckpc) max_physical_DM_softening: 0.001300 # Physical softening for DM (1.30 pkpc) comoving_baryon_softening: 0.001790 # Comoving softening for baryons (1.79 ckpc) @@ -182,14 +185,13 @@ EAGLEFeedback: SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). - SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). - 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: 1.4588 # 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. + SNII_energy_fraction_function: EAGLE # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.388 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 7.37 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.00134 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). Note the value we use is 0.1 Zsun. + SNII_energy_fraction_n_0_H_p_cm3: 0.412 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_sigma_Z: 0.311 # Width of the sigmoid for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_sigma_n: 0.428 # Width of the sigmoid for the birth density dependance of the SNII energy fraction. SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. @@ -217,9 +219,9 @@ EAGLEAGN: use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term - with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? - boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? - boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + with_boost_factor: 1 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 1 # If using the boost factor, are we using a constant boost only? + boost_alpha: 0.645 # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? @@ -227,14 +229,14 @@ EAGLEAGN: fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. - coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + coupling_efficiency: 0.035 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? - use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. - AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). @@ -242,7 +244,7 @@ EAGLEAGN: AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). - AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_delta_T_K: 4.207e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). @@ -252,8 +254,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/EAGLE_ICs/EAGLE_25/run.sh b/examples/EAGLE_ICs/EAGLE_25/run.sh index 1e7d79fe57e18ff6ec1a43696675867caba8202c..04635c149fa19181386880f79034f413c96f8848 100755 --- a/examples/EAGLE_ICs/EAGLE_25/run.sh +++ b/examples/EAGLE_ICs/EAGLE_25/run.sh @@ -35,7 +35,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --eagle \ --threads=16 --pin \ eagle_25.yml diff --git a/examples/EAGLE_ICs/EAGLE_25_low_res/eagle_25.yml b/examples/EAGLE_ICs/EAGLE_25_low_res/eagle_25.yml index d854ad9239c69b1b3cdb02445a6a9e2a9d462ff1..2958bb531f203b02023eb2f38b6d36d3e46d996e 100644 --- a/examples/EAGLE_ICs/EAGLE_25_low_res/eagle_25.yml +++ b/examples/EAGLE_ICs/EAGLE_25_low_res/eagle_25.yml @@ -1,6 +1,6 @@ # Define some meta-data about the simulation MetaData: - run_name: EAGLE-L0025N0188-Ref + run_name: SWIFT-EAGLE-L0025N0188-Ref # Define the system of units to use internally. InternalUnitSystem: @@ -29,6 +29,9 @@ Snapshots: basename: eagle # Common part of the name of output files output_list_on: 1 output_list: ./output_list.txt + compression: 4 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -38,9 +41,9 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric # Use the geometric opening angle condition + MAC: adaptive # Use the geometric opening angle condition theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 0 + epsilon_fmm: 0.001 # Adaptive opening angle mesh_side_length: 128 comoving_DM_softening: 0.006640 # Comoving softening for DM (6.67 ckpc) max_physical_DM_softening: 0.002600 # Physical softening for DM (2.60 pkpc) @@ -179,14 +182,13 @@ EAGLEFeedback: SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). - SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). - 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: 1.4588 # 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. + SNII_energy_fraction_function: EAGLE # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.388 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 7.37 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.00134 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). Note the value we use is 0.1 Zsun. + SNII_energy_fraction_n_0_H_p_cm3: 0.412 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_sigma_Z: 0.311 # Width of the sigmoid for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_sigma_n: 0.428 # Width of the sigmoid for the birth density dependance of the SNII energy fraction. SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. @@ -214,9 +216,9 @@ EAGLEAGN: use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term - with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? - boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? - boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + with_boost_factor: 1 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 1 # If using the boost factor, are we using a constant boost only? + boost_alpha: 0.645 # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? @@ -224,14 +226,14 @@ EAGLEAGN: fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 7.2e6 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. - coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + coupling_efficiency: 0.035 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? - use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. - AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). @@ -239,7 +241,7 @@ EAGLEAGN: AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). - AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_delta_T_K: 4.207e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). @@ -249,8 +251,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/EAGLE_ICs/EAGLE_25_low_res/run.sh b/examples/EAGLE_ICs/EAGLE_25_low_res/run.sh index 1db1a6e96f1d58c1ad0a594b17bb3b192450a9e6..200fab538cb38515c0df665c9191139fd9cd9960 100755 --- a/examples/EAGLE_ICs/EAGLE_25_low_res/run.sh +++ b/examples/EAGLE_ICs/EAGLE_25_low_res/run.sh @@ -35,7 +35,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --eagle \ --threads=16 --pin \ eagle_25.yml diff --git a/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml index 3b1c64a48b2b6a1b926d1f2b919328a83bb4ae43..99120ad01d6c18ebfaeb074851b900e846cda6fc 100644 --- a/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml @@ -1,6 +1,6 @@ # Define some meta-data about the simulation MetaData: - run_name: EAGLE-L0050N0752-Ref + run_name: SWIFT-EAGLE-L0050N0752-Ref # Define the system of units to use internally. InternalUnitSystem: @@ -29,6 +29,9 @@ Snapshots: basename: eagle # Common part of the name of output files output_list_on: 1 output_list: ./output_list.txt + compression: 4 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +41,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric # Use the geometric opening angle condition + MAC: adaptive # Use the geometric opening angle condition theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 0 - mesh_side_length: 256 + epsilon_fmm: 0.001 # Adaptive opening angle + mesh_side_length: 512 comoving_DM_softening: 0.003320 # Comoving softening for DM (3.32 ckpc) max_physical_DM_softening: 0.001300 # Physical softening for DM (1.30 pkpc) comoving_baryon_softening: 0.001790 # Comoving softening for baryons (1.79 ckpc) @@ -180,14 +183,13 @@ EAGLEFeedback: SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). - SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). - 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: 1.4588 # 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. + SNII_energy_fraction_function: EAGLE # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.388 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 7.37 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.00134 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). Note the value we use is 0.1 Zsun. + SNII_energy_fraction_n_0_H_p_cm3: 0.412 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_sigma_Z: 0.311 # Width of the sigmoid for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_sigma_n: 0.428 # Width of the sigmoid for the birth density dependance of the SNII energy fraction. SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. @@ -215,9 +217,9 @@ EAGLEAGN: use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term - with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? - boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? - boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + with_boost_factor: 1 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 1 # If using the boost factor, are we using a constant boost only? + boost_alpha: 0.645 # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? @@ -225,14 +227,14 @@ EAGLEAGN: fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. - coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + coupling_efficiency: 0.035 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? - use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. - AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). @@ -240,7 +242,7 @@ EAGLEAGN: AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). - AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_delta_T_K: 4.207e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). @@ -250,8 +252,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/EAGLE_ICs/EAGLE_50/run.sh b/examples/EAGLE_ICs/EAGLE_50/run.sh index e49cc93f2d4ded7f98ece06fe86b533d6a6a7969..026019b44d05d8d8272b49c5baa2d7c2b8f4ef6c 100755 --- a/examples/EAGLE_ICs/EAGLE_50/run.sh +++ b/examples/EAGLE_ICs/EAGLE_50/run.sh @@ -35,7 +35,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --eagle \ --threads=16 --pin \ eagle_50.yml diff --git a/examples/EAGLE_ICs/EAGLE_50_low_res/eagle_50.yml b/examples/EAGLE_ICs/EAGLE_50_low_res/eagle_50.yml index 2f88ae340b612cdd3df6964584c9056efeae1fa2..f6a3ec3049ddc9036e35fa28fecdbc22786f3bdb 100644 --- a/examples/EAGLE_ICs/EAGLE_50_low_res/eagle_50.yml +++ b/examples/EAGLE_ICs/EAGLE_50_low_res/eagle_50.yml @@ -1,6 +1,6 @@ # Define some meta-data about the simulation MetaData: - run_name: EAGLE-L0050N0376-Ref + run_name: SWIFT-EAGLE-L0050N0376-Ref # Define the system of units to use internally. InternalUnitSystem: @@ -29,6 +29,9 @@ Snapshots: basename: eagle # Common part of the name of output files output_list_on: 1 output_list: ./output_list.txt + compression: 4 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +41,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric # Use the geometric opening angle condition + MAC: adaptive # Use the geometric opening angle condition theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 0 - mesh_side_length: 128 + epsilon_fmm: 0.001 # Adaptive opening angle + mesh_side_length: 256 comoving_DM_softening: 0.006640 # Comoving softening for DM (6.67 ckpc) max_physical_DM_softening: 0.002600 # Physical softening for DM (2.60 pkpc) comoving_baryon_softening: 0.003580 # Comoving softening for baryons (3.58 ckpc) @@ -179,14 +182,13 @@ EAGLEFeedback: SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). - SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). - 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: 1.4588 # 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. + SNII_energy_fraction_function: EAGLE # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.388 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 7.37 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.00134 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). Note the value we use is 0.1 Zsun. + SNII_energy_fraction_n_0_H_p_cm3: 0.412 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_sigma_Z: 0.311 # Width of the sigmoid for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_sigma_n: 0.428 # Width of the sigmoid for the birth density dependance of the SNII energy fraction. SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. @@ -214,9 +216,9 @@ EAGLEAGN: use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term - with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? - boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? - boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + with_boost_factor: 1 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 1 # If using the boost factor, are we using a constant boost only? + boost_alpha: 0.645 # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? @@ -224,14 +226,14 @@ EAGLEAGN: fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 7.2e6 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. - coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + coupling_efficiency: 0.035 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? - use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. - AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). @@ -239,7 +241,7 @@ EAGLEAGN: AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). - AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_delta_T_K: 4.207e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). @@ -249,8 +251,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/EAGLE_ICs/EAGLE_50_low_res/run.sh b/examples/EAGLE_ICs/EAGLE_50_low_res/run.sh index 9d4cca708513cc89218547ddde27482e052b7e7c..83f388aeb01c968aa4aac2cd50b9065993cee96b 100755 --- a/examples/EAGLE_ICs/EAGLE_50_low_res/run.sh +++ b/examples/EAGLE_ICs/EAGLE_50_low_res/run.sh @@ -35,7 +35,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --eagle \ --threads=16 --pin \ eagle_50.yml diff --git a/examples/EAGLE_ICs/EAGLE_6/eagle_6.yml b/examples/EAGLE_ICs/EAGLE_6/eagle_6.yml index cdfca09704c35e3a72294f2106e1e02328e1a553..5fdc200155d3912b7da12efc07ffbd1241225fb3 100644 --- a/examples/EAGLE_ICs/EAGLE_6/eagle_6.yml +++ b/examples/EAGLE_ICs/EAGLE_6/eagle_6.yml @@ -1,6 +1,6 @@ # Define some meta-data about the simulation MetaData: - run_name: EAGLE-L0006N0094-Ref + run_name: SWIFT-EAGLE-L0006N0094-Ref # Define the system of units to use internally. InternalUnitSystem: @@ -29,6 +29,9 @@ Snapshots: basename: eagle # Common part of the name of output files output_list_on: 1 output_list: ./output_list.txt + compression: 4 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -38,10 +41,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric # Use the geometric opening angle condition + MAC: adaptive # Use the geometric opening angle condition theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 0 - mesh_side_length: 32 + epsilon_fmm: 0.001 # Adaptive opening angle + mesh_side_length: 64 comoving_DM_softening: 0.003320 # Comoving softening for DM (3.32 ckpc) max_physical_DM_softening: 0.001300 # Physical softening for DM (1.30 pkpc) comoving_baryon_softening: 0.001790 # Comoving softening for baryons (1.79 ckpc) @@ -182,14 +185,13 @@ EAGLEFeedback: SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). - SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. - SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). - 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: 1.4588 # 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. + SNII_energy_fraction_function: EAGLE # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.388 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 7.37 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.00134 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). Note the value we use is 0.1 Zsun. + SNII_energy_fraction_n_0_H_p_cm3: 0.412 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_sigma_Z: 0.311 # Width of the sigmoid for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_sigma_n: 0.428 # Width of the sigmoid for the birth density dependance of the SNII energy fraction. SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. @@ -217,9 +219,9 @@ EAGLEAGN: use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term - with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? - boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? - boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + with_boost_factor: 1 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 1 # If using the boost factor, are we using a constant boost only? + boost_alpha: 0.645 # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? @@ -227,14 +229,14 @@ EAGLEAGN: fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. - coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + coupling_efficiency: 0.035 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? - use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. - AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). @@ -242,7 +244,7 @@ EAGLEAGN: AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). - AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_delta_T_K: 4.207e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). @@ -252,8 +254,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/EAGLE_ICs/EAGLE_6/output_list.txt b/examples/EAGLE_ICs/EAGLE_6/output_list.txt index 151f609a9ad4dfbb142e28e721c1a706facdb511..592ab8483d015fe1bfafe5cc603fabc230b25589 100644 --- a/examples/EAGLE_ICs/EAGLE_6/output_list.txt +++ b/examples/EAGLE_ICs/EAGLE_6/output_list.txt @@ -1,5 +1,4 @@ # Redshift -126 18.08 15.28 13.06 diff --git a/examples/EAGLE_ICs/EAGLE_6/run.sh b/examples/EAGLE_ICs/EAGLE_6/run.sh index 51f7295d982798fe123e160345301713adad374b..aa1064d380a97740c918ee961a7316822b5b8f43 100755 --- a/examples/EAGLE_ICs/EAGLE_6/run.sh +++ b/examples/EAGLE_ICs/EAGLE_6/run.sh @@ -35,7 +35,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --eagle \ --threads=16 --pin \ eagle_6.yml diff --git a/examples/EAGLE_ICs/README b/examples/EAGLE_ICs/README index b0b07cc7bf606717384d59b64e5bf1aecf0c9274..0ef04690337001875f8a81cf7bf86e759ad6336a 100644 --- a/examples/EAGLE_ICs/README +++ b/examples/EAGLE_ICs/README @@ -56,8 +56,6 @@ the following changes have been made (at standard resolution): (was a random set of particles in the kernel). - The SNII feedback delay is done by sampling the stellar age distribution and not using a fixed delay of 30 Myr any more. - - The energy range for the SNII variable f_th is now 0.5 - 5.0 - (from 0.3 - 3.0). - The density and metallicity used for the f_th scaling of the SNII feedback are now computed at the time of feedback from the gas neighbours and are not the birth quantities any more. @@ -83,7 +81,6 @@ the following changes have been made (at standard resolution): - Gas particles can be nibbled down to 50% of their initial mass. - The angular momentum term in the BH accretion model of Rosas-Guevara et al. (2015) is now switched off. - - The coupling efficency of the BH feedback is now 0.1 (was 0.15). - The BHs compute a time-step length based on their accretion rate with a minimum time-step length of 10^5 years. - The AGN feedback heats the particles closest to the BH particle @@ -91,12 +88,11 @@ the following changes have been made (at standard resolution): - The AGN does not use a combination of probability and reservoir any more. Energy is accumulated and then used in a deterministic way once the threshold for feedback is reached. - - The AGN feedback temperature jump is now a function of the BH - subgrid mass (was a constant at 10^8.5K). The temperature varies - between 10^7 and 3*10^9 K with a power-law of the subgrid mass - exponent of 2/3 and a reference point of 10^8 Msun. The temperature - jump also has to be larger than the T_crit for efficiency given - by the Dalla Vecchia & Schaye 2012 criterion. + - The free parameters in the SN feedback efficiency function, boost + of the AGN accretion, coupling efficieny, and AGN heating temperatures + have been calibrated to match the z=0 stellar mass function, and + mass-size relation. The procedure used to obtain the values of these + parameters is described in Borrow et al. (in prep.) The scripts in this directory download the tables required to run the EAGLE model. Plotting scripts are also provided diff --git a/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml b/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml index 802b25f34e57be37c44ae9bc61e7ae1e24a051bd..be81abba92bed23b33c1cfb362e71ae4111a704c 100644 --- a/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml +++ b/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml @@ -23,7 +23,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 1e-2 # The end time of the simulation (in internal units). dt_min: 1e-12 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). Scheduler: max_top_level_cells: 32 @@ -34,6 +34,8 @@ Snapshots: scale_factor_first: 0.91 # Scale-factor of the first snaphot (cosmological run) time_first: 0.01 # Time of the first output (non-cosmological run) (in internal units) delta_time: 1.01 # Time difference between consecutive outputs (in internal units) + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -43,11 +45,11 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric - theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 256 + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: adaptive + epsilon_fmm: 0.001 + theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) + mesh_side_length: 1024 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). diff --git a/examples/EAGLE_low_z/EAGLE_100/run.sh b/examples/EAGLE_low_z/EAGLE_100/run.sh index 28571d4803cb8c26bf67d84870c10a2e7dcf534c..2e22a655c855dea8aaa3cc175ee1d6b102db6eb2 100755 --- a/examples/EAGLE_low_z/EAGLE_100/run.sh +++ b/examples/EAGLE_low_z/EAGLE_100/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_100.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_100.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_low_z/EAGLE_12/README b/examples/EAGLE_low_z/EAGLE_12/README index 0530021202191bac74690e47106ca3488011d1b5..2e9a053c20ea6859ea3ce0869797f6aabfe25213 100644 --- a/examples/EAGLE_low_z/EAGLE_12/README +++ b/examples/EAGLE_low_z/EAGLE_12/README @@ -14,3 +14,21 @@ running these ICs on 8 cores. MD5 checksum of the ICs: 6f96624175df28f2c4a02265b871b3d5 EAGLE_ICs_12.hdf5 + + +A second example is set up to run with `./run_rt_test.sh`. It makes use +of the `eagle_12_rt_test.yml` file, and is intended to stress-test the +RT implementation rather than produce actual physically meaningful results. + + +o To use GEAR-RT, configure SWIFT with + + --with-stars=basic --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-feedback=none + +[technically, any other feedback scheme should work as well.] + + +o To use the DEBUG RT scheme, configure SWIFT with + --with-stars=basic --with-rt=debug --with-feedback=none + +[technically, any other feedback scheme should work as well.] diff --git a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml index 073f66060ee74afdafd2e2f9ecae2b90626765ef..4c0a6496de31a30f5b532c98c5af526a159d9776 100644 --- a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml @@ -32,6 +32,8 @@ Snapshots: time_first: 0.01 # Time of the first output (non-cosmological run) (in internal units) delta_time: 1.01 # Time difference between consecutive outputs (in internal units) compression: 1 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot Scheduler: links_per_tasks: 500 @@ -45,10 +47,10 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric + MAC: adaptive + epsilon_fmm: 0.001 theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 32 + 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). @@ -209,7 +211,7 @@ EAGLEAGN: fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. @@ -228,8 +230,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. use_subgrid_mass_from_ics: 0 # Use the dynamical mass as the subgrid mass since we don't have subgrid masses in the ICs. diff --git a/examples/EAGLE_low_z/EAGLE_12/eagle_12_rt_test.yml b/examples/EAGLE_low_z/EAGLE_12/eagle_12_rt_test.yml new file mode 100644 index 0000000000000000000000000000000000000000..bd5c4139fc2a52cd57d41c6c61f7906029262ba6 --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_12/eagle_12_rt_test.yml @@ -0,0 +1,102 @@ +MetaData: + run_name: EAGLE-L0012N0188-Ref-RT-TEST + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841e43 # 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.9090909 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_cdm: 0.2587481 # Cold Dark Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0482519 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 32 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 1e-2 # The end time of the simulation (in internal units). + dt_min: 1e-12 # 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: eagle # Common part of the name of output files + scale_factor_first: 0.91 # Scale-factor of the first snaphot (cosmological run) + time_first: 0.01 # Time of the first output (non-cosmological run) (in internal units) + delta_time: 1.01 # Time difference between consecutive outputs (in internal units) + compression: 1 + +Scheduler: + links_per_tasks: 500 + dependency_graph_frequency: 0 + +# Parameters governing the conserved quantities statistics +Statistics: + scale_factor_first: 0.92 # Scale-factor of the first stat dump (cosmological run) + time_first: 0.01 # Time of the first stat dump (non-cosmological run) (in internal units) + delta_time: 1.05 # Time between statistics output + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: adaptive + epsilon_fmm: 0.001 + theta_cr: 0.7 # 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: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + h_min_ratio: 0.1 # Minimal smoothing length in units of softening. + h_max: 0.5 # Maximal smoothing length in co-moving internal units. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # (internal units) + particle_splitting: 0 + particle_splitting_mass_threshold: 7e-4 # Internal units (i.e. 7e6 Msun ~ 4 times the initial gas mass) + +# 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 related to the initial conditions +InitialConditions: + file_name: ./EAGLE_ICs_12.hdf5 # The file to read + periodic: 1 + cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget + cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget + +GEARRT: + f_reduce_c: 0.01 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 + f_limit_cooling_time: 0.1 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + photon_groups_Hz: [3.288e15, 5.944e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). Outer edges of zero and infinity are assumed. + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1.764e+04, 3.631e+04, 8.037e+03 ] # (Conditional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.75 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.75 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.25 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + stars_max_timestep: 5.468750e-05 # (Optional) restrict the maximal timestep of stars to this value (in internal units) + + diff --git a/examples/EAGLE_low_z/EAGLE_12/run.sh b/examples/EAGLE_low_z/EAGLE_12/run.sh index bceddf338ae797abcc32c24fb2642320d9091ba9..80c76970b75909fc6ea50a95af051bbe8846e78e 100755 --- a/examples/EAGLE_low_z/EAGLE_12/run.sh +++ b/examples/EAGLE_low_z/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 --threads=16 eagle_12.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_low_z/EAGLE_12/run_rt_test.sh b/examples/EAGLE_low_z/EAGLE_12/run_rt_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..eed49dc5227a7615fa30556d1e26caa93c3c6524 --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_12/run_rt_test.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Run a RT test with EAGLE ICs. +# +# To use GEAR-RT, configure SWIFT with +# +# --with-stars=basic --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-feedback=none +# [technically, any other feedback scheme should work as well.] +# +# +# To use the DEBUG RT scheme, configure SWIFT with +# --with-stars=basic --with-rt=debug --with-feedback=none +# [technically, any other feedback scheme should work as well.] + + +# Generate the initial conditions if they are not present. +if [ ! -e EAGLE_ICs_12.hdf5 ] +then + echo "Fetching initial conditions for the EAGLE 12Mpc example..." + ./getIC.sh +fi + +../../../swift \ + --hydro --threads=8 --stars --self-gravity \ + --feedback --radiation \ + eagle_12_rt_test.yml + diff --git a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml index 550833213bb6187938361cbbcee2c363008e3f73..03ab1520bebcd1a6c91bd520ad33728551f63462 100644 --- a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml @@ -42,6 +42,8 @@ Snapshots: scale_factor_first: 0.91 # Scale-factor of the first snaphot (cosmological run) time_first: 0.01 # Time of the first output (non-cosmological run) (in internal units) delta_time: 1.01 # Time difference between consecutive outputs (in internal units) + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -51,11 +53,11 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric - theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 64 + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: adaptive + epsilon_fmm: 0.001 + theta_cr: 0.7 # 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). @@ -217,7 +219,7 @@ EAGLEAGN: fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. @@ -236,8 +238,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. use_subgrid_mass_from_ics: 0 # Use the dynamical mass as the subgrid mass since we don't have subgrid masses in the ICs. diff --git a/examples/EAGLE_low_z/EAGLE_25/run.sh b/examples/EAGLE_low_z/EAGLE_25/run.sh index ea14dbde3293bb28f98de29eb035d53bc7caa1e6..aae44b01a1c116407293a22ca5c79dd9f6ef551a 100755 --- a/examples/EAGLE_low_z/EAGLE_25/run.sh +++ b/examples/EAGLE_low_z/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 --threads=16 eagle_25.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml index 61d27aabb8cd1c73fb0c9f2127794933f9a85696..d93d2d8cefdf1e5f92ecbffe81d91daff021bb25 100644 --- a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml @@ -34,6 +34,8 @@ Snapshots: scale_factor_first: 0.91 # Scale-factor of the first snaphot (cosmological run) time_first: 0.01 # Time of the first output (non-cosmological run) (in internal units) delta_time: 1.01 # Time difference between consecutive outputs (in internal units) + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -43,11 +45,11 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric - theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 128 + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: adaptive + epsilon_fmm: 0.001 + theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) + 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). comoving_baryon_softening: 0.0026994 # Comoving DM softening length (in internal units). @@ -208,7 +210,7 @@ EAGLEAGN: fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. @@ -227,8 +229,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. use_subgrid_mass_from_ics: 0 # Use the dynamical mass as the subgrid mass since we don't have subgrid masses in the ICs. diff --git a/examples/EAGLE_low_z/EAGLE_50/run.sh b/examples/EAGLE_low_z/EAGLE_50/run.sh index e2f2836900bacf612acb289154de973cda90eccb..5a053a4054a4397939530209428100b8080e241c 100755 --- a/examples/EAGLE_low_z/EAGLE_50/run.sh +++ b/examples/EAGLE_low_z/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 --threads=16 eagle_50.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml index 910c7311bae217a4f5731fa505e60c9c9a7319fe..963821e177f8baec2ef6c78f58552b213be16fe3 100644 --- a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml +++ b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml @@ -45,6 +45,8 @@ Snapshots: delta_time: 1.01 # Time difference between consecutive outputs (in internal units) compression: 1 distributed: 1 + recording_triggers_part: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot + recording_triggers_bpart: [1.0227e-4, 1.0227e-5] # Recording starts 100M and 10M years before a snapshot # Parameters governing the conserved quantities statistics Statistics: @@ -54,11 +56,11 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: geometric - theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) - use_tree_below_softening: 1 - mesh_side_length: 16 + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: adaptive + epsilon_fmm: 0.001 + theta_cr: 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). @@ -220,7 +222,7 @@ EAGLEAGN: fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. @@ -230,8 +232,8 @@ EAGLEAGN: use_variable_delta_T: 0 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. AGN_with_locally_adaptive_delta_T: 0 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). - AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. + AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). max_reposition_mass: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. @@ -239,8 +241,9 @@ EAGLEAGN: max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1. min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1. set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. use_subgrid_mass_from_ics: 0 # Use the dynamical mass as the subgrid mass since we don't have subgrid masses in the ICs. diff --git a/examples/EAGLE_low_z/EAGLE_6/run.sh b/examples/EAGLE_low_z/EAGLE_6/run.sh index aec7449e2dbec7f5aadc5e075fd1f75a31da5d83..f7db75e6e3667f20c8fb50003955a0f82c19a32c 100755 --- a/examples/EAGLE_low_z/EAGLE_6/run.sh +++ b/examples/EAGLE_low_z/EAGLE_6/run.sh @@ -7,7 +7,5 @@ then ./getIC.sh fi -../../swift --hydro --threads=4 -n 16 -y 1 eagle_6.yml \ - -PInitialConditions:metadata_group_name:NoRuntimePars \ - | tee output.log +../../../swift -v 1 --interleave --hydro --threads=4 -n 16 -y 1 eagle_6.yml | tee output.log diff --git a/examples/EAGLE_low_z/EAGLE_6/testVELOCIraptor.sh b/examples/EAGLE_low_z/EAGLE_6/testVELOCIraptor.sh index 3f0ae1d6f0da9736b867f53b898752efbfd50324..f0438966ed61e966318c03621e2450935bd7f037 100755 --- a/examples/EAGLE_low_z/EAGLE_6/testVELOCIraptor.sh +++ b/examples/EAGLE_low_z/EAGLE_6/testVELOCIraptor.sh @@ -36,20 +36,20 @@ if [ "$RUN_DM" = "1" ]; then rm $VEL_OUTPUT/vel_$TEST* # Run test using SWIFT + VELOCIraptor - echo "Running: mpirun -np $NUM_MPI_PROC ../swift_mpi --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly" - mpirun -np $NUM_MPI_PROC ../swift_mpi --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly + echo "Running: mpirun -np $NUM_MPI_PROC ../../../swift_mpi --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly" + mpirun -np $NUM_MPI_PROC ../../../swift_mpi --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly # Run test using VELOCIraptor echo "Running: mpirun -np $NUM_MPI_PROC $VELOCIRAPTOR_PATH/bin/stf-gas -I 2 -i eagle_dmonly_0000 -C $VELOCIRAPTOR_PATH/stf_input_$TEST.cfg -o ./$VEL_OUTPUT/vel_$TEST" mpirun -np $NUM_MPI_PROC $VELOCIRAPTOR_PATH/bin/stf-gas -I 2 -i eagle_dmonly_0000 -C ./stf_input_$TEST.cfg -o ./$VEL_OUTPUT/vel_$TEST - # Create info file for python comparison script + # Create info file for python3 comparison script echo -e $INFO_FILE_TEXT > infoFile_$TEST.txt echo "vel_$TEST $VEL_OUTPUT/vel_$TEST" >> infoFile_$TEST.txt echo "stf_$TEST $OUTPUT/stf_0000.VELOCIraptor" >> infoFile_$TEST.txt # Run comparison script on VELOCIraptor output - if python $SCRIPT_PATH/catalogcomparisontolerancecheck.py infoFile_$TEST.txt toIfile.txt + if python3 $SCRIPT_PATH/catalogcomparisontolerancecheck.py infoFile_$TEST.txt toIfile.txt then echo "Catalog comparison passed: "$TEST else @@ -80,20 +80,20 @@ if [ "$RUN_GAS" = "1" ]; then rm $VEL_OUTPUT/vel_$TEST* # Run test using SWIFT + VELOCIraptor - echo "Running: mpirun -np $NUM_MPI_PROC ../swift_mpi --hydro --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas" - mpirun -np $NUM_MPI_PROC ../swift_mpi --hydro --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas + echo "Running: mpirun -np $NUM_MPI_PROC ../../../swift_mpi --hydro --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas" + mpirun -np $NUM_MPI_PROC ../../../swift_mpi --hydro --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas # Run test using VELOCIraptor echo "Running: mpirun -np $NUM_MPI_PROC $VELOCIRAPTOR_PATH/bin/stf-gas -I 2 -i eagle_gas_0000 -C ./stf_input_$TEST.cfg -o ./$VEL_OUTPUT/vel_$TEST" mpirun -np $NUM_MPI_PROC $VELOCIRAPTOR_PATH/bin/stf-gas -I 2 -i eagle_gas_0000 -C ./stf_input_$TEST.cfg -o ./$VEL_OUTPUT/vel_$TEST - # Create info file for python comparison script + # Create info file for python3 comparison script echo -e $INFO_FILE_TEXT > infoFile_$TEST.txt echo "vel_$TEST $VEL_OUTPUT/vel_$TEST" >> infoFile_$TEST.txt echo "stf_$TEST $OUTPUT/stf_0000.VELOCIraptor" >> infoFile_$TEST.txt # Run comparison script on VELOCIraptor output - if python $SCRIPT_PATH/catalogcomparisontolerancecheck.py infoFile_$TEST.txt toIfile.txt + if python3 $SCRIPT_PATH/catalogcomparisontolerancecheck.py infoFile_$TEST.txt toIfile.txt then echo "Catalog comparison passed: "$TEST else diff --git a/examples/GEAR/AgoraCosmo/README b/examples/GEAR/AgoraCosmo/README index 3c9f77ed58d2131af5dafbd913b092d9efefe7b2..d30e9bf8a23204b1bb39a042a1db7ec84ed3675b 100644 --- a/examples/GEAR/AgoraCosmo/README +++ b/examples/GEAR/AgoraCosmo/README @@ -1,7 +1,7 @@ Initial conditions for the AGORA simulation presented in Roca-Fabrega et al. 2021. The initial conditions are generated by MUSIC (and thus the path to the binary should be set in run.sh). The initial conditions are generated for AREPO and -then converted into SWIFT compatible IC with a python script. +then converted into SWIFT compatible IC with a python3 script. We will use SWIFT to cancel the h- and a-factors from the ICs. The IC consists in a halo evolving to a virial mass of ∼ 1e12 Msun at z = 0 with a relatively diff --git a/examples/GEAR/AgoraCosmo/run.sh b/examples/GEAR/AgoraCosmo/run.sh index 6d8609cb0fd26b5daa5bf6593b8e5c929abcc5d7..0ff0482c5c9285074e35e4aff93ff85d325506e4 100644 --- a/examples/GEAR/AgoraCosmo/run.sh +++ b/examples/GEAR/AgoraCosmo/run.sh @@ -26,4 +26,4 @@ echo "Converting the initial conditions into a SWIFT compatible format" python3 convert_ic.py echo "Running SWIFT" -../../swift --cooling --feedback --cosmology --limiter --sync --self-gravity --hydro --stars --star-formation --threads=24 agora_cosmo.yml 2>&1 | tee output.log +../../../swift --cooling --feedback --cosmology --limiter --sync --self-gravity --hydro --stars --star-formation --threads=24 agora_cosmo.yml 2>&1 | tee output.log diff --git a/examples/GEAR/AgoraDisk/run.sh b/examples/GEAR/AgoraDisk/run.sh index b2956e086d61a5042fb131bdc061c9b8950899fc..9a32b86a479f2c32108ec973b0f1ccc2dd308b73 100755 --- a/examples/GEAR/AgoraDisk/run.sh +++ b/examples/GEAR/AgoraDisk/run.sh @@ -36,7 +36,7 @@ cp $sim.hdf5 agora_disk.hdf5 python3 changeType.py agora_disk.hdf5 # Run SWIFT -../../swift --sync --limiter --cooling --hydro --self-gravity --star-formation --feedback --stars --threads=8 agora_disk.yml 2>&1 | tee output.log +../../../swift --sync --limiter --cooling --hydro --self-gravity --star-formation --feedback --stars --threads=8 agora_disk.yml 2>&1 | tee output.log echo "Changing smoothing length to be Gadget compatible" diff --git a/examples/GEAR/ZoomIn/run.sh b/examples/GEAR/ZoomIn/run.sh index 8760786e1ebee86037c9ac8575415d8a10498e62..141e6af26f5a1052690c665dab3fdf676a31f34d 100755 --- a/examples/GEAR/ZoomIn/run.sh +++ b/examples/GEAR/ZoomIn/run.sh @@ -3,4 +3,4 @@ echo "Fetching initial conditions for the zoom in example..." ./getIC.sh -../../swift --cooling --feedback --cosmology --limiter --sync --self-gravity --hydro --stars --star-formation --threads=8 zoom_in.yml 2>&1 | tee output.log +../../../swift --cooling --feedback --cosmology --limiter --sync --self-gravity --hydro --stars --star-formation --threads=8 zoom_in.yml 2>&1 | tee output.log diff --git a/examples/GravityTests/DiscPatch/GravityOnly/makeIC.py b/examples/GravityTests/DiscPatch/GravityOnly/makeIC.py index 993882cbb2fc5f4358cc0f16b7daeea34227e066..7c4613af135c4ad337fac7eb73599371a608f5fc 100644 --- a/examples/GravityTests/DiscPatch/GravityOnly/makeIC.py +++ b/examples/GravityTests/DiscPatch/GravityOnly/makeIC.py @@ -38,7 +38,7 @@ import math # to obtain the 1/ch^2(x/b) profile from a uniform profile (a glass, say, or a uniform random variable), note that, when integrating in x # \int 0^x dx/ch^2(x) = tanh(x)-tanh(0) = \int_0^u du = u (where the last integral refers to a uniform density distribution), so that x = atanh(u) # -# usage: python makeIC.py 1000 +# usage: python3 makeIC.py 1000 # physical constants in cgs NEWTON_GRAVITY_CGS = 6.67430e-8 diff --git a/examples/GravityTests/DiscPatch/GravityOnly/run.sh b/examples/GravityTests/DiscPatch/GravityOnly/run.sh index 3b7c51683d8c5cfa98318af067e40bdc27d9f5a1..2986b8a7ea938da7e1cde6b8d7605d95600f74c8 100755 --- a/examples/GravityTests/DiscPatch/GravityOnly/run.sh +++ b/examples/GravityTests/DiscPatch/GravityOnly/run.sh @@ -4,11 +4,11 @@ if [ ! -e Disc-Patch.hdf5 ] then echo "Generating initial conditions for the disc-patch example..." - python makeIC.py 1000 + python3 makeIC.py 1000 fi # Run SWIFT -../../../swift --external-gravity --threads=2 disc-patch.yml +../../../../swift --external-gravity --threads=2 disc-patch.yml # Verify energy conservation python3 test.py diff --git a/examples/GravityTests/DiscPatch/HydroStatic/makeIC.py b/examples/GravityTests/DiscPatch/HydroStatic/makeIC.py index 571642237cb4c6ce9639493c8742e5a3f5b41bba..9e63ec8478e68695b7bc9c1290b43cbcbc741b71 100644 --- a/examples/GravityTests/DiscPatch/HydroStatic/makeIC.py +++ b/examples/GravityTests/DiscPatch/HydroStatic/makeIC.py @@ -2,7 +2,7 @@ # This file is part of SWIFT. # Copyright (c) 2016 John A. Regan (john.a.regan@durham.ac.uk) # Tom Theuns (tom.theuns@durham.ac.uk) -# 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Matthieu Schaller (schaller@strw.leidenuniv.nl) # Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/GravityTests/DiscPatch/HydroStatic/plotSolution.py b/examples/GravityTests/DiscPatch/HydroStatic/plotSolution.py index eeee3f46906b8e43ec025346db430e4eafe577d3..bb6719b87a1e05f9008e8beb9a1b179a5ecab811 100644 --- a/examples/GravityTests/DiscPatch/HydroStatic/plotSolution.py +++ b/examples/GravityTests/DiscPatch/HydroStatic/plotSolution.py @@ -1,7 +1,7 @@ ################################################################################ # This file is part of SWIFT. # Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 diff --git a/examples/GravityTests/DiscPatch/HydroStatic/run.sh b/examples/GravityTests/DiscPatch/HydroStatic/run.sh index 44b933f79c1b0f699010932f285c62396de37a72..73cc77029ef1b5812850147e6fe146b45c88634c 100755 --- a/examples/GravityTests/DiscPatch/HydroStatic/run.sh +++ b/examples/GravityTests/DiscPatch/HydroStatic/run.sh @@ -9,10 +9,10 @@ fi if [ ! -e Disc-Patch.hdf5 ] then echo "Generating initial conditions for the disc patch example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../../swift --external-gravity --hydro --threads=4 disc-patch-icc.yml 2>&1 | tee output.log +../../../../swift --external-gravity --hydro --threads=4 disc-patch-icc.yml 2>&1 | tee output.log -python plotSolution.py +python3 plotSolution.py diff --git a/examples/GravityTests/DiscPatch/HydroStatic_1D/makeIC.py b/examples/GravityTests/DiscPatch/HydroStatic_1D/makeIC.py index cf54a740330968b142bb83323faea4fbdf550155..27db5be378b4257e1f482e6d68703b9a5a87821e 100644 --- a/examples/GravityTests/DiscPatch/HydroStatic_1D/makeIC.py +++ b/examples/GravityTests/DiscPatch/HydroStatic_1D/makeIC.py @@ -2,7 +2,7 @@ # This file is part of SWIFT. # Copyright (c) 2016 John A. Regan (john.a.regan@durham.ac.uk) # Tom Theuns (tom.theuns@durham.ac.uk) -# 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Matthieu Schaller (schaller@strw.leidenuniv.nl) # Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/GravityTests/DiscPatch/HydroStatic_1D/plotSolution.py b/examples/GravityTests/DiscPatch/HydroStatic_1D/plotSolution.py index 9c3db4858f799cfa80b5cce2908580da5a1f6581..88948bd05896513b68123237da34330159c2e846 100644 --- a/examples/GravityTests/DiscPatch/HydroStatic_1D/plotSolution.py +++ b/examples/GravityTests/DiscPatch/HydroStatic_1D/plotSolution.py @@ -1,7 +1,7 @@ ################################################################################ # This file is part of SWIFT. # Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 diff --git a/examples/GravityTests/DiscPatch/HydroStatic_1D/run.sh b/examples/GravityTests/DiscPatch/HydroStatic_1D/run.sh index a76db9422e12ee18251083ee8cf26dd28e861e69..54e48c923c198cf461a106ad98a990c95b3391b6 100755 --- a/examples/GravityTests/DiscPatch/HydroStatic_1D/run.sh +++ b/examples/GravityTests/DiscPatch/HydroStatic_1D/run.sh @@ -4,10 +4,10 @@ if [ ! -e Disc-Patch.hdf5 ] then echo "Generating initial conditions for the disc patch example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../../swift --external-gravity --hydro --threads=4 disc-patch-icc.yml 2>&1 | tee output.log +../../../../swift --external-gravity --hydro --threads=4 disc-patch-icc.yml 2>&1 | tee output.log -python plotSolution.py +python3 plotSolution.py diff --git a/examples/GravityTests/ExternalPointMass/energy_plot.py b/examples/GravityTests/ExternalPointMass/energy_plot.py index 1f563ba1f3e3a01ce88f81ddd1606919c8ad31c6..95bf32e9d3d40b5c21d7661a44082390f97238f6 100644 --- a/examples/GravityTests/ExternalPointMass/energy_plot.py +++ b/examples/GravityTests/ExternalPointMass/energy_plot.py @@ -22,10 +22,8 @@ params = { "figure.subplot.hspace": 0.12, "lines.markersize": 6, "lines.linewidth": 3.0, - "text.latex.unicode": True, } rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) import numpy as np diff --git a/examples/GravityTests/ExternalPointMass/run.sh b/examples/GravityTests/ExternalPointMass/run.sh index 6f96200e45ceabcece487005560c293cdc084780..5b4973ab7967a06f8f1c7939224f0c636f6d5aac 100755 --- a/examples/GravityTests/ExternalPointMass/run.sh +++ b/examples/GravityTests/ExternalPointMass/run.sh @@ -4,10 +4,10 @@ if [ ! -e PointMass.hdf5 ] then echo "Generating initial conditions for the point mass potential box example..." - python makeIC.py 10000 + python3 makeIC.py 10000 fi rm -rf pointMass_*.hdf5 -../../swift --external-gravity --threads=1 externalPointMass.yml 2>&1 | tee output.log +../../../swift --external-gravity --threads=1 externalPointMass.yml 2>&1 | tee output.log -python energy_plot.py +python3 energy_plot.py diff --git a/examples/GravityTests/Gravity_glass/makeIC.py b/examples/GravityTests/Gravity_glass/makeIC.py index 73696dc689ae4c0b038a6d0f3f11e0fb223708ed..30f442b07369eab017224de1843b6f5786249df1 100644 --- a/examples/GravityTests/Gravity_glass/makeIC.py +++ b/examples/GravityTests/Gravity_glass/makeIC.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 diff --git a/examples/GravityTests/Hernquist_circularorbit/plotprog.py b/examples/GravityTests/Hernquist_circularorbit/plotprog.py index a19c66e7f30e0e4012a23a4d38dd23045deea6e2..881c74301ec4a466f6984ee081b7ac6e3d555f5b 100755 --- a/examples/GravityTests/Hernquist_circularorbit/plotprog.py +++ b/examples/GravityTests/Hernquist_circularorbit/plotprog.py @@ -22,7 +22,7 @@ import h5py import matplotlib.pyplot as plt from scipy.integrate import odeint -t = np.linspace(0, 40, 1e5) +t = np.linspace(0, 40, 100000) y0 = [0, 10] a = 30.0 G = 4.300927e-06 diff --git a/examples/GravityTests/Hernquist_circularorbit/run.sh b/examples/GravityTests/Hernquist_circularorbit/run.sh index 15cd929a5d2ba62efd4f7af378c9cad4788e7c5f..2b015311b1b10c3e03b638f49d8c2babfdb64faf 100755 --- a/examples/GravityTests/Hernquist_circularorbit/run.sh +++ b/examples/GravityTests/Hernquist_circularorbit/run.sh @@ -6,18 +6,18 @@ then if command -v python3 &>/dev/null; then python3 makeIC.py else - python makeIC.py + python3 makeIC.py fi fi # self gravity G, external potential g, hydro s, threads t and high verbosity v -../../swift --external-gravity --threads=6 hernquistcirc.yml 2>&1 | tee output.log +../../../swift --external-gravity --threads=6 hernquistcirc.yml 2>&1 | tee output.log echo "Save plots of the circular orbits" if command -v python3 &>/dev/null; then python3 plotprog.py else - python plotprog.py + python3 plotprog.py fi diff --git a/examples/GravityTests/Hernquist_radialinfall/makeIC.py b/examples/GravityTests/Hernquist_radialinfall/makeIC.py index 567e15a95302bc8848c1d026b82dc5be54c7a0c6..5e586d4c898a2c87985c2187b51e5b8ad955e5ca 100644 --- a/examples/GravityTests/Hernquist_radialinfall/makeIC.py +++ b/examples/GravityTests/Hernquist_radialinfall/makeIC.py @@ -25,8 +25,8 @@ import random import numpy as np # Generates N particles in a spherical distribution centred on [0,0,0], to be moved in an isothermal potential -# usage: python makeIC.py 1000 0 : generate 1000 particles on circular orbits -# python makeIC.py 1000 1 : generate 1000 particles with Lz/L uniform in [0,1] +# usage: python3 makeIC.py 1000 0 : generate 1000 particles on circular orbits +# python3 makeIC.py 1000 1 : generate 1000 particles with Lz/L uniform in [0,1] # all particles move in the xy plane, and start at y=0 # physical constants in cgs diff --git a/examples/GravityTests/Hernquist_radialinfall/run.sh b/examples/GravityTests/Hernquist_radialinfall/run.sh index 7381f3ecd4f8aa6711d33607cec52d5cdfedccaf..9b04385a5b186a6088f8061a903d90d391dac62c 100755 --- a/examples/GravityTests/Hernquist_radialinfall/run.sh +++ b/examples/GravityTests/Hernquist_radialinfall/run.sh @@ -7,12 +7,12 @@ then if command -v python3 &>/dev/null; then python3 makeIC.py else - python makeIC.py + python3 makeIC.py fi fi rm -rf hernquist_*.hdf5 -../../swift --external-gravity --threads=1 hernquist.yml 2>&1 | tee output.log +../../../swift --external-gravity --threads=1 hernquist.yml 2>&1 | tee output.log @@ -20,5 +20,5 @@ echo "Make plots of the radially free falling particles" if command -v python3 &>/dev/null; then python3 plotprog.py else - python plotprog.py + python3 plotprog.py fi diff --git a/examples/GravityTests/HydrostaticHalo/makeIC.py b/examples/GravityTests/HydrostaticHalo/makeIC.py index 0b0aec691d63661049d67ab0354cc2350d3be7b3..a7441b453d013ff076301e8d25922dd44bb78a95 100644 --- a/examples/GravityTests/HydrostaticHalo/makeIC.py +++ b/examples/GravityTests/HydrostaticHalo/makeIC.py @@ -24,7 +24,7 @@ import math import random # Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) -# usage: python makeIC.py 1000: generate 1000 particles +# usage: python3 makeIC.py 1000: generate 1000 particles # Some constants diff --git a/examples/GravityTests/HydrostaticHalo/run.sh b/examples/GravityTests/HydrostaticHalo/run.sh index a3f8b04b1115e316736c1177ecfd8288ed2a045e..ed7aea4cee33dd33bc38e5172e59dd173b3728d6 100755 --- a/examples/GravityTests/HydrostaticHalo/run.sh +++ b/examples/GravityTests/HydrostaticHalo/run.sh @@ -4,24 +4,24 @@ if [ ! -e Hydrostatic.hdf5 ] then echo "Generating initial conditions for the isothermal potential box example..." - python makeIC.py 100000 + python3 makeIC.py 100000 fi # Run for 10 dynamical times -../../swift --external-gravity --hydro --threads=1 hydrostatic.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --threads=1 hydrostatic.yml 2>&1 | tee output.log echo "Plotting density profiles" mkdir plots mkdir plots/density_profile -python density_profile.py 2. 200 300 +python3 density_profile.py 2. 200 300 echo "Plotting internal energy profiles" mkdir plots/internal_energy -python internal_energy_profile.py 2. 200 300 +python3 internal_energy_profile.py 2. 200 300 echo "Plotting radial velocity profiles" mkdir plots/radial_velocity_profile -python velocity_profile.py 2. 200 300 +python3 velocity_profile.py 2. 200 300 echo "Plotting energy as a function of time" -python test_energy_conservation.py 300 +python3 test_energy_conservation.py 300 diff --git a/examples/GravityTests/IsothermalPotential/energy_plot.py b/examples/GravityTests/IsothermalPotential/energy_plot.py index 6f76eb6a444479060f6bc386bb7d3715904ae2a2..4ee92840ca6f732a8fe3681ed308693885300518 100644 --- a/examples/GravityTests/IsothermalPotential/energy_plot.py +++ b/examples/GravityTests/IsothermalPotential/energy_plot.py @@ -22,10 +22,8 @@ params = { "figure.subplot.hspace": 0.12, "lines.markersize": 6, "lines.linewidth": 3.0, - "text.latex.unicode": True, } rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) import numpy as np diff --git a/examples/GravityTests/IsothermalPotential/makeIC.py b/examples/GravityTests/IsothermalPotential/makeIC.py index 844182327a1cbe0abadda01532a471fa9446281b..d348cf2e574b2449f0c1adeb5cd880baf61a857d 100644 --- a/examples/GravityTests/IsothermalPotential/makeIC.py +++ b/examples/GravityTests/IsothermalPotential/makeIC.py @@ -25,8 +25,8 @@ import math import random # Generates N particles in a spherical distribution centred on [0,0,0], to be moved in an isothermal potential -# usage: python makeIC.py 1000 0 : generate 1000 particles on circular orbits -# python makeIC.py 1000 1 : generate 1000 particles with Lz/L uniform in [0,1] +# usage: python3 makeIC.py 1000 0 : generate 1000 particles on circular orbits +# python3 makeIC.py 1000 1 : generate 1000 particles with Lz/L uniform in [0,1] # all particles move in the xy plane, and start at y=0 # physical constants in cgs diff --git a/examples/GravityTests/IsothermalPotential/run.sh b/examples/GravityTests/IsothermalPotential/run.sh index 4e6a502eddcc081549bc1c967cde9edab9f0b835..5fba6714f7cf83fa0e65552dbede7465ac26c234 100755 --- a/examples/GravityTests/IsothermalPotential/run.sh +++ b/examples/GravityTests/IsothermalPotential/run.sh @@ -4,10 +4,10 @@ if [ ! -e Isothermal.hdf5 ] then echo "Generating initial conditions for the isothermal potential box example..." - python makeIC.py 1000 0 + python3 makeIC.py 1000 0 fi rm -rf Isothermal_*.hdf5 -../../swift --external-gravity --threads=1 isothermal.yml 2>&1 | tee output.log +../../../swift --external-gravity --threads=1 isothermal.yml 2>&1 | tee output.log -python energy_plot.py +python3 energy_plot.py diff --git a/examples/GravityTests/JeansFragmentation/jeansfragmentation.yml b/examples/GravityTests/JeansFragmentation/jeansfragmentation.yml new file mode 100644 index 0000000000000000000000000000000000000000..6801c27e5f3b43f46b55020ed3a7aa5d4de6ca95 --- /dev/null +++ b/examples/GravityTests/JeansFragmentation/jeansfragmentation.yml @@ -0,0 +1,53 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9891E43 # 10^10 solar masses + UnitLength_in_cgs: 3.08567758E21 # 1 kpc + UnitVelocity_in_cgs: 1E5 # km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters for the self-gravity scheme +Gravity: + MAC: adaptive # Choice of mulitpole acceptance criterion: 'adaptive' OR 'geometric'. + epsilon_fmm: 0.001 # Tolerance parameter for the adaptive multipole acceptance criterion. + theta_cr: 0.7 # Opening angle for the purely gemoetric criterion. + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion). + max_physical_baryon_softening: 0.01 # Physical softening length (in internal units) + mesh_side_length: 32 + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.5 # The end time of the simulation (in internal units). + dt_min: 1e-6 # 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: + subdir: snap + basename: snapshot # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.1 # Time difference between consecutive outputs (in internal units) + +Scheduler: + max_top_level_cells: 3 + + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-1 # Time between statistics output + time_first: 0. # (Optional) Time of the first stats output if non-cosmological time-integration (in internal units) + +# Parameters related to the initial conditions +InitialConditions: + file_name: jeansbox.hdf5 # The file to read + periodic: 1 # Are we running with periodic ICs? + +# 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. + h_max: 0.2 + minimal_temperature: 1 + diff --git a/examples/GravityTests/JeansFragmentation/makeIC.py b/examples/GravityTests/JeansFragmentation/makeIC.py new file mode 100755 index 0000000000000000000000000000000000000000..12cd6ff3ca584690c174037976786e9cfdb03830 --- /dev/null +++ b/examples/GravityTests/JeansFragmentation/makeIC.py @@ -0,0 +1,191 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2022 Yves Revaz (yves.revaz@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 optparse import OptionParser +from astropy import units +from astropy import constants + + +def parse_options(): + + usage = "usage: %prog [options] file" + parser = OptionParser(usage=usage) + + parser.add_option( + "--lJ", + action="store", + dest="lJ", + type="float", + default=0.250, + help="Jeans wavelength in box size unit", + ) + + parser.add_option( + "--rho", + action="store", + dest="rho", + type="float", + default=0.1, + help="Mean gas density in atom/cm3", + ) + + parser.add_option( + "--mass", + action="store", + dest="mass", + type="float", + default=50, + help="Gas particle mass in solar mass", + ) + + parser.add_option( + "--level", + action="store", + dest="level", + type="int", + default=5, + help="Resolution level: N = (2**l)**3", + ) + + parser.add_option( + "-o", + action="store", + dest="outputfilename", + type="string", + default="box.dat", + help="output filename", + ) + + (options, args) = parser.parse_args() + + files = args + + return files, options + + +######################################## +# main +######################################## + +files, opt = parse_options() + +# define standard units +UnitMass_in_cgs = 1.989e43 # 10^10 M_sun in grams +UnitLength_in_cgs = 3.085678e21 # kpc in centimeters +UnitVelocity_in_cgs = 1e5 # km/s in centimeters per second +UnitCurrent_in_cgs = 1 # Amperes +UnitTemp_in_cgs = 1 # Kelvin +UnitTime_in_cgs = UnitLength_in_cgs / UnitVelocity_in_cgs + +UnitMass = UnitMass_in_cgs * units.g +UnitLength = UnitLength_in_cgs * units.cm +UnitTime = UnitTime_in_cgs * units.s +UnitVelocity = UnitVelocity_in_cgs * units.cm / units.s + + +# Number of particles +N = (2 ** opt.level) ** 3 # number of particles + +# Mean density +rho = opt.rho # atom/cc +rho = rho * constants.m_p / units.cm ** 3 + +# Gas particle mass +m = opt.mass # in solar mass +m = m * units.Msun + +# Gas mass in the box +M = N * m + +# Size of the box +L = (M / rho) ** (1 / 3.0) + +# Jeans wavelength in box size unit +lJ = opt.lJ +lJ = lJ * L + +# Gravitational constant +G = constants.G + +# Jeans wave number +kJ = 2 * np.pi / lJ +# Velocity dispersion +sigma = np.sqrt(4 * np.pi * G * rho) / kJ + + +print("Number of particles : {}".format(N)) +print("Equivalent velocity dispertion : {}".format(sigma.to(units.m / units.s))) + +# Convert to code units +m = m.to(UnitMass).value +L = L.to(UnitLength).value +rho = rho.to(UnitMass / UnitLength ** 3).value +sigma = sigma.to(UnitVelocity).value + +# Generate the particles +pos = np.random.random([N, 3]) * np.array([L, L, L]) +vel = np.zeros(N) +mass = np.ones(N) * m +u = np.ones(N) * sigma ** 2 +ids = np.arange(N) +h = np.ones(N) * 3 * L / N ** (1 / 3.0) +rho = np.ones(N) * rho + +print("Inter-particle distance (code unit) : {}".format(L / N ** (1 / 3.0))) + + +# File +fileOutput = h5py.File(opt.outputfilename, "w") +print("{} saved.".format(opt.outputfilename)) + +# Header +grp = fileOutput.create_group("/Header") +grp.attrs["BoxSize"] = [L, L, L] +grp.attrs["NumPart_Total"] = [N, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 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, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + + +# Units +grp = fileOutput.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = UnitLength_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = UnitMass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = UnitTime_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = UnitCurrent_in_cgs +grp.attrs["Unit temperature in cgs (U_T)"] = UnitTemp_in_cgs + + +# Particle group +grp = fileOutput.create_group("/PartType0") +grp.create_dataset("Coordinates", data=pos, dtype="d") +grp.create_dataset("Velocities", data=vel, dtype="f") +grp.create_dataset("Masses", data=mass, 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") +grp.create_dataset("Densities", data=rho, dtype="f") + +fileOutput.close() diff --git a/examples/GravityTests/JeansFragmentation/run.sh b/examples/GravityTests/JeansFragmentation/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..bab705720d7787b3abda704b70c88f15bf3014b2 --- /dev/null +++ b/examples/GravityTests/JeansFragmentation/run.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e jeansbox.hdf5 ] +then + echo "Generating initial conditions for the adiabatic Jeans fragmentation example..." + python3 makeIC.py --lJ 0.5 -o jeansbox.hdf5 +fi + +# create output directory +if [ ! -e snap ] +then + mkdir snap +fi + +# Run for some sound crossing time +../../../swift --hydro --self-gravity --threads=1 jeansfragmentation.yml 2>&1 | tee output.log diff --git a/examples/GravityTests/NFW_Halo/run.sh b/examples/GravityTests/NFW_Halo/run.sh index 7f16def75f760d2e4cef4a9303b7c370ebdd1916..6d9fb639b5903b0b953e49ccfb86ae98f2af6cd6 100755 --- a/examples/GravityTests/NFW_Halo/run.sh +++ b/examples/GravityTests/NFW_Halo/run.sh @@ -6,15 +6,15 @@ then if command -v python3 &>/dev/null; then python3 makeIC.py else - python makeIC.py + python3 makeIC.py fi fi # self gravity G, external potential g, hydro s, threads t and high verbosity v -../../swift --external-gravity --threads=6 test.yml 2>&1 | tee output.log +../../../swift --external-gravity --threads=6 test.yml 2>&1 | tee output.log if command -v python3 &>/dev/null; then python3 makePlots.py else - python makePlots.py + python3 makePlots.py fi diff --git a/examples/GravityTests/Plummer_Selfgravitating/README b/examples/GravityTests/Plummer_Selfgravitating/README new file mode 100644 index 0000000000000000000000000000000000000000..b6a061b5d45c1b20778613d7c63c9aa5ca3e7cbe --- /dev/null +++ b/examples/GravityTests/Plummer_Selfgravitating/README @@ -0,0 +1,50 @@ +This example generates a self consistent realization of an anisotropic Plummer model, by sampling +the distribution function derived by (Dejonghe 1987). + +The parameters that control the ICs are essentially: +* a: The Plummer softening length +* M: The total mass of the system +* q: A parameter that controls the radial anisotropy of the system, + ranging from -inf to 2, where -inf corresponds to the "Einstein Sphere", + a model with only circular orbits and 2 to a model with only radial orbits. + q is related to the traditional beta parameter (which measures anisotropy) via + beta(r) = q/2 * r^2/(1+r^2) +* N: The Number of particles in the system + +Some additional parameters are: +* bound: A given cut radius above which particles are not included in the IC file + (this may reduce the effective number of particles to N_eff < N). N_eff is + printed out when the script is executed. +* use_parallel: Use python's multiprocessing library for sampling +* nb_threads: Number of threads to use for the above +* pickle_ics: Also output .pkl files of the pos, vel and mass arrays + +All other variables should be self-explanatory. + +The radii of the particles are renerated through inverse transform sampling, exploiting +the analiticity of the inverse of M(<r) for the Plummer model. (Masses are drawn uniformly +between 0 and M, and their corresponding radii are computed via r(M)) + +The radial and tangential components of the velocities are generated trough rejection +sampling on the DF of the model. For each radius sampled above, a max on the DF is computed +numerically to achieve this. + +The masses of all the particles are simply given by M/N. + +The script will also print out a guiding estimate of a good softening length to use, given by the radius +of a sphere which contains on average one particle at the number density at the softening length a. + +NOTE: the positions of the particles produced by this script are centered at the origin, and must +be shifted by some distance in x,y,z to be accepted by SWIFT. This will depend on the box size and +the bound parameter described above. + +## USAGE ## +Set the parameters described above as desired in plummerIC.py +Run plummerIC.py to produce .hdf5 initial conditions +Set suitable parameters in params.yml +Run SWIFT with --self-gravity on + +To display results, another script (plotdensity.py) is provided. The basic usage is +python3 plotdensity.py [path to snapshot files to plot (max 20)] -a [softening value] -M [mass value] -shift [shift applied in params.yml] + +A run with default values, 10k particles and subsequent density plotting can be automated with the provided run.sh script. diff --git a/examples/GravityTests/Plummer_Selfgravitating/params.yml b/examples/GravityTests/Plummer_Selfgravitating/params.yml new file mode 100755 index 0000000000000000000000000000000000000000..584a0f73eaa513ea99e956380733f9f68f0e9307 --- /dev/null +++ b/examples/GravityTests/Plummer_Selfgravitating/params.yml @@ -0,0 +1,38 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.988e+43 # 10^10 Solar masses + UnitLength_in_cgs: 3.086e+21 # kpc + UnitVelocity_in_cgs: 1e5 # km / s + 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: 2.0 # The end time of the simulation (in internal units). + dt_min: 1e-11 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.002 # Constant dimensionless multiplier for time integration. + MAC: geometric # Choice of mulitpole acceptance criterion: 'adaptive' OR 'geometric'. + epsilon_fmm: 0.001 # Tolerance parameter for the adaptive multipole acceptance criterion. + theta_cr: 0.7 # Opening angle for the purely gemoetric criterion. + max_physical_DM_softening: 0.005 # Physical softening length (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: snapshot # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 2e-1 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-3 # Time between statistics output + +# Parameters related to the initial conditions +InitialConditions: + file_name: plummer.hdf5 # The file to read + shift: [2.,2.,2.] + periodic: 0 diff --git a/examples/GravityTests/Plummer_Selfgravitating/plotdensity.py b/examples/GravityTests/Plummer_Selfgravitating/plotdensity.py new file mode 100644 index 0000000000000000000000000000000000000000..27903fd1412aa9ff6838a90b38c276d87b7207ef --- /dev/null +++ b/examples/GravityTests/Plummer_Selfgravitating/plotdensity.py @@ -0,0 +1,107 @@ +import numpy as np +from matplotlib import pyplot as plt +import argparse +from scipy.optimize import curve_fit +import h5py + +# Parse user input +parser = argparse.ArgumentParser( + description="Plot multiple density profiles against theoretical prediction" +) +parser.add_argument("files", nargs="+", help="snapshot files to be imaged") +parser.add_argument("--notex", action="store_true", help="Flag to not use LaTeX markup") +parser.add_argument( + "-a", type=float, default=0.1, help="Softening length of theoretical model" +) +parser.add_argument( + "-M", type=float, default=1.0e-5, help="Total Mass of theoretical model" +) +parser.add_argument("-Rmin", type=float, default=0.01, help="Min Radius") +parser.add_argument("-Rmax", type=float, default=2.1, help="Max Radius") +parser.add_argument( + "-nbsamples", type=int, default=64, help="Number of radii to sample (bins)" +) +parser.add_argument( + "-shift", type=float, default=2.0, help="Shift applied to particles in params.yml" +) + +args = parser.parse_args() +fnames = args.files + +# Limit number of snapshots to plot +if len(fnames) > 20: + raise ValueError( + "Too many ({:d}) files provided (cannot plot more than 20).".format(len(fnames)) + ) + +# Set parameters +tex = not args.notex +if tex: + plt.rcParams.update({"text.usetex": tex, "font.size": 14, "font.family": "serif"}) +else: + plt.rcParams.update({"font.size": 12}) +figsize = 7 + +# Model Parameters (Mestel surface density) +G = 4.299581e04 +rsp = np.logspace(np.log10(args.Rmin), np.log10(args.Rmax), args.nbsamples) + + +def plummer_analytical(r): + return ( + 3.0 + * args.M + / (4.0 * np.pi * args.a ** 3) + * (1.0 + r ** 2 / args.a ** 2) ** (-2.5) + ) + + +# Plot densities +fig, ax = plt.subplots(figsize=(1.2 * figsize, figsize)) +for fname in fnames: + print(fname) + f = h5py.File(fname, "r") + pos = np.array(f["DMParticles"]["Coordinates"]) - args.shift + time = ( + f["Header"].attrs["Time"][0] + * f["Units"].attrs["Unit time in cgs (U_t)"][0] + / 31557600.0e9 + ) + mass = np.array(f["DMParticles"]["Masses"]) + x = pos[:, 0] + y = pos[:, 1] + r = np.sqrt(np.sum(pos ** 2, 1)) + + # Methods to compute density profile + def mass_ins(R): + return ((r < R) * mass).sum() + + mass_ins_vect = np.vectorize(mass_ins) + + def density(R): + return np.diff(mass_ins_vect(R)) / np.diff(R) / (4.0 * np.pi * R[1:] ** 2) + + dens = density(rsp) + rs = rsp[1:] + + # remove empty bins + c = dens > 0 + dens = np.compress(c, dens) + rs = np.compress(c, rs) + + # Plot + # ax.loglog(rsp[1:], density(rsp), "o", ms=1.7, label=r"$t=$ {:.3f} Gyr".format(time)) + ax.plot(rs, dens, label=r"$t=$ {:.3f} Gyr".format(time)) + +ax.plot(rsp, plummer_analytical(rsp), c="black", label="Analytical Prediction") +ax.set_xlabel("r [kpc]") +ax.legend() +ax.loglog() +ax.set_title( + r"Plummer Density Profile: $a = {:.1e}$ kpc, $M = {:.1e}$ M$_{{\odot}}$".format( + args.a, args.M * 1e10 + ) +) +plt.tight_layout() +ax.set_ylabel(r"$\rho(r)$ [$M_{\odot}$ kpc$^{-3}$]") +plt.savefig("density_profile.png") diff --git a/examples/GravityTests/Plummer_Selfgravitating/plummerIC.py b/examples/GravityTests/Plummer_Selfgravitating/plummerIC.py new file mode 100644 index 0000000000000000000000000000000000000000..173caa7aaff0aa816509d74810e7ef3a1c42e799 --- /dev/null +++ b/examples/GravityTests/Plummer_Selfgravitating/plummerIC.py @@ -0,0 +1,297 @@ +################################################################################ +# Copyright (c) 2022 Patrick Hirling (patrick.hirling@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 numpy as np +import scipy.special as sci +from scipy.optimize import minimize +import time +import argparse +from swiftsimio import Writer +import unyt + +parser = argparse.ArgumentParser( + description="Generate discrete realization of Anisotropic Plummer Model" +) + +# Parse Main Arguments +parser.add_argument("-a", type=float, default=0.05, help="Softening Length (in kpc)") +parser.add_argument( + "-M", type=float, default=1.0e-5, help="Total Mass of Model (in 10^10 solar masses)" +) +parser.add_argument( + "-q", type=float, default=1.0, help="Anisotropy parameter (between -inf and +2)" +) +parser.add_argument( + "-N", type=int, default=1e4, help="Number of particles in the simulation" +) +parser.add_argument( + "-bound", type=float, default=2.0, help="Upper bound of radius sampling" +) +parser.add_argument("-boxsize", type=float, default=4.0, help="Box Size of simulation") +parser.add_argument( + "--noparallel", + action="store_false", + help="Do not use multiprocessing library (default: false)", +) +parser.add_argument( + "-nbthreads", + type=int, + default=6, + help="Number of threads to use for multiprocessing (default: 6)", +) + +args = parser.parse_args() +if args.q > 2.0: + raise ValueError( + "Anisotropy parameter must be smaller than 2.0 (q = {:.2f} given)".format( + args.q + ) + ) + +#### Parameters +# Plummer Model +G = 4.299581e04 # Gravitational constant [kpc / 10^10 M_s * (kms)^2] +a = args.a # Plummer softening length [kpc] +M = args.M # Total Mass [10^10 M_s] +q = args.q # Anisotropy Parameter (-inf,2] + +# IC File +N = int(args.N) # Number of Particles +fname = "plummer.hdf5" # Name of the ic file (dont forget .hdf5) +pickle_ics = 0 # Optional: Pickle ics (pos,vel,mass) for future use +periodic_bdry = 0 + +# Parallelism for velocity sampling +use_parallel = args.noparallel +nb_threads = int(args.nbthreads) # Set to None to use os.cpucount() + +### Print parameters +print( + "\n############################################################################################# \n" +) +print("Generating Plummer ICs with the following parameters::\n") +print( + "Softening length: a = " + "{:.3e} kpc".format(a) +) +print( + "Total Mass: M = " + + "{:.3e} Solar Masses".format(M * 1e10) +) +print("Anisotropy parameter: q = " + str(q)) +print("Output file: " + fname) +print("Number of particles: N = " + "{:.1e}".format(N)) +print( + "\n############################################################################################# \n" +) + +# Estimate good softening length (density at origin, sphere s.t. contains on avg 1 particle) +epsilon0 = a * N ** (-1.0 / 3.0) +print( + "Recommended Softening length (times conventional factor): " + + "{:.4e}".format(epsilon0) + + " kpc" +) + +### Generate Positions +# Inverse transform sampling from analytical inverse of M(<r) +def r_of_m(m, a, M): + return a * ((M / m) ** (2.0 / 3.0) - 1) ** (-1.0 / 2.0) + + +m_rand = M * np.random.uniform(0.0, 1.0, N) +r_rand = r_of_m(m_rand, a, M) +phi_rand = np.random.uniform(0.0, 2 * np.pi, N) +theta_rand = np.arccos(np.random.uniform(-1.0, 1.0, N)) + +x = r_rand * np.sin(theta_rand) * np.cos(phi_rand) +y = r_rand * np.sin(theta_rand) * np.sin(phi_rand) +z = r_rand * np.cos(theta_rand) + +X = np.array([x, y, z]).transpose() + +### Generate Velocities +# Sample the DF of the Plummer model at the radii generated above (give vr,vt) +# Dejonghe (1987) Anisotropic Distribution Functionl +normalization = 3.0 * sci.gamma(6.0 - q) / (2.0 * (2.0 * np.pi) ** (2.5)) +units = G ** (q - 5.0) * M ** (q - 4.0) * a ** (2.0 - q) + + +def H(E, L): + x = L ** 2 / (2.0 * E * a ** 2) + if x <= 1.0: + return 1.0 / (sci.gamma(4.5 - q)) * sci.hyp2f1(0.5 * q, q - 3.5, 1.0, x) + else: + return ( + 1.0 + / (sci.gamma(1.0 - 0.5 * q) * sci.gamma(4.5 - 0.5 * q) * (x ** (0.5 * q))) + * sci.hyp2f1(0.5 * q, 0.5 * q, 4.5 - 0.5 * q, 1.0 / x) + ) + + +def Fq(E, L): + if E < 0.0: + return 0.0 + else: + return normalization * units * E ** (3.5 - q) * H(E, L) + + +# Helper Functions +# Total energy: E = phi - 1/2v^2. relative potential: psi = -phi. Relative energy: -E = psi - 1/2v^2 +def relative_potential(r): # = - phi + return G * M / np.sqrt(r ** 2 + a ** 2) + + +def relative_energy(r, vr, vt): + return relative_potential(r) - 0.5 * (vr ** 2 + vt ** 2) + + +# N.B: Angular momentum: L = r * vt + +# Convenience Function for scipy.minimize negative of Fq*vt, indep. vars passed as array +def Fq_tomin(v, r): + return -Fq(relative_energy(r, v[0], v[1]), r * v[1]) * v[1] + + +# Find max of DF at given radius +def fmax(r, vmax): + # Constraint function (defined locally, dep on r) + def vel_constr2(v): + return vmax ** 2 - v[0] ** 2 - v[1] ** 2 + + # Initial Guess + v0 = [0.1 * vmax, 0.2 * vmax] + + # Constraint Object + cons = {"type": "ineq", "fun": vel_constr2} + + # Args + args = (r,) + + # Minimize through scipy.optimize.minimize + # fm = minimize(Fq_tomin,v0,constraints=cons,method = 'COBYLA',args=args) + fm = minimize( + Fq_tomin, + v0, + constraints=cons, + method="SLSQP", + args=args, + bounds=[(0, vmax), (0, vmax)], + ) + + # Min of negative df == max of df + return -fm.fun + + +# Sample vr,vt from DF at given Radius through rejection sampling +def sample_vel(r): + # Compute max velocity (equiv. condition for E>=0) + vmax = np.sqrt(2.0 * relative_potential(r)) + # Compute max of DF at this radius + fm = 1.1 * fmax(r, vmax) # 1.1 factor to be sure to include max + while True: + # Select candidates for vr,vt based on max full velocity + while True: + vr = np.random.uniform(0.0, vmax) + vt = np.random.uniform(0.0, vmax) + if vr ** 2 + vt ** 2 <= vmax ** 2: + break + # Rejection Sampling on Fq + f = np.random.uniform(0.0, fm) + if Fq(relative_energy(r, vr, vt), r * vt) * vt >= f: + return vr, vt + + +print("Sampling velocities...") +ti = time.time() +if use_parallel: + from multiprocessing import Pool + + with Pool(nb_threads) as p: + vels = np.array(p.map(sample_vel, r_rand)).transpose() +else: + from tqdm import tqdm + + vels = np.empty((2, N)) + for j, r in enumerate(tqdm(r_rand)): + vels[:, j] = sample_vel(r) +tf = time.time() +print("Sampling took " + "{:.2f}".format(tf - ti) + " seconds.") + +# Convert to Cartesian +# First: project vt on e_theta, e_phi with random orientation +alph = np.random.uniform(0, 2 * np.pi, N) +sgn = np.random.choice([-1, 1], size=N) +vphi = vels[1] * np.cos(alph) +vtheta = vels[1] * np.sin(alph) +# project vr on e_r (random sign) +vr = sgn * vels[0] + +# Convert Spherical to cartesian coordinates +v_x = ( + np.sin(theta_rand) * np.cos(phi_rand) * vr + + np.cos(theta_rand) * np.cos(phi_rand) * vtheta + - np.sin(phi_rand) * vphi +) +v_y = ( + np.sin(theta_rand) * np.sin(phi_rand) * vr + + np.cos(theta_rand) * np.sin(phi_rand) * vtheta + + np.cos(phi_rand) * vphi +) +v_z = np.cos(theta_rand) * vr - np.sin(theta_rand) * vtheta + +# Create velocity array +V = np.array([v_x, v_y, v_z]).transpose() + +### Generate masses + +m = M / N * np.ones(N) + +### Exclude extreme outliers +idx = r_rand < args.bound +X = X[idx] +V = V[idx] +m = m[idx] +new_N = len(m) + +### Write to hdf5 +galactic_units = unyt.UnitSystem( + "galactic", + unyt.kpc, + unyt.unyt_quantity(1e10, units=unyt.Solar_Mass), + unyt.unyt_quantity(1.0, units=unyt.s * unyt.Mpc / unyt.km).to(unyt.Gyr), +) +wrt = Writer(galactic_units, args.boxsize * unyt.kpc) +wrt.dark_matter.coordinates = X * unyt.kpc +wrt.dark_matter.velocities = V * (unyt.km / unyt.s) +wrt.dark_matter.masses = m * 1e10 * unyt.msun +wrt.dark_matter.particle_ids = np.arange(new_N) +wrt.write(fname) +print("Writing IC file...") + +### Optional: Pickle ic arrays for future use +if pickle_ics: + import pickle as pkl + + with open("X.pkl", "wb") as f: + pkl.dump(X[:], f) + + with open("V.pkl", "wb") as f: + pkl.dump(V[:], f) + + with open("M.pkl", "wb") as f: + pkl.dump(m[:], f) diff --git a/examples/GravityTests/Plummer_Selfgravitating/run.sh b/examples/GravityTests/Plummer_Selfgravitating/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..eeae68c7a7f1d0a7b97aa62140419d7026b4c076 --- /dev/null +++ b/examples/GravityTests/Plummer_Selfgravitating/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +if [ ! -e plummer.hdf5 ] +then + echo "Generating initial conditions for Plummer example..." + python3 plummerIC.py -a 0.1 -N 65536 +fi + +# create output directory +if [ ! -e snap ] +then + mkdir snap +fi + +../../../swift --self-gravity --threads=8 params.yml 2>&1 | tee output.log + +echo "Plotting results..." +# If params.yml is left at default values, should produce 10 snapshots +python3 plotdensity.py snapshot_*.hdf5 diff --git a/examples/HydroTests/BlobTest_3D/run.sh b/examples/HydroTests/BlobTest_3D/run.sh index 15c87c734e239a120863fc5b12c87a8b7f2d5ecc..87cb2dd2b6a63999d16f521c8dced85cf2342825 100755 --- a/examples/HydroTests/BlobTest_3D/run.sh +++ b/examples/HydroTests/BlobTest_3D/run.sh @@ -8,6 +8,6 @@ then fi # Run SWIFT -../../swift --hydro --threads=2 blob.yml +../../../swift --hydro --threads=2 blob.yml python3 makeMovie.py diff --git a/examples/HydroTests/BlobTest_Braspenning2022/README.md b/examples/HydroTests/BlobTest_Braspenning2022/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f1821c2034d785f3bdeea670ffdeff8b2d567699 --- /dev/null +++ b/examples/HydroTests/BlobTest_Braspenning2022/README.md @@ -0,0 +1,21 @@ +Blob Test +========= + +The test in this folder is identical to the one presented in Braspenning+ 2022 +(https://ui.adsabs.harvard.edu/abs/2022arXiv220313915B/abstract) +and can be used to reproduce those results + +You can use the provided ICs, where the first value indicates the particle number +and the second the density contrast. Switching ICs requires indicating the new ICs +in the parameter file 'blob.yml' +You can run this test with any of the hydro schemes supported by SWIFT. + +Figures similar to those in Braspenning+ 2022 can be made using 'plot_cloud_evolution.py', +this produces: ++ Evolution of the mass of dense gas ++ Evolution of the mass of intermediate-temperature gas +The second and third row show the same evolutions by downsampled to a lower resolution grid. +This is identical to Fig. 7 in Braspenning+ 2022 + +The simulation can be run using this command: +./swift --pin --hydro --limiter --threads=28 blob.yml diff --git a/examples/HydroTests/BlobTest_Braspenning2022/blob.yml b/examples/HydroTests/BlobTest_Braspenning2022/blob.yml new file mode 100644 index 0000000000000000000000000000000000000000..92142402a49afc4d954ab3fa3bd22e5bf68fc3f7 --- /dev/null +++ b/examples/HydroTests/BlobTest_Braspenning2022/blob.yml @@ -0,0 +1,41 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e24 # 10^-9 Msun in grams + UnitLength_in_cgs: 3.08567758e18 # pc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in 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: .1 # 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: blob # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.001 # Time difference between consecutive outputs (in internal units) + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +Scheduler: + tasks_per_cell: 100 + +# 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: 10 + H_mass_fraction: 0.76 + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./blob_16_100.hdf5 # The file to read + periodic: 1 + cleanup_smoothing_lengths: 1 diff --git a/examples/HydroTests/BlobTest_Braspenning2022/getICs.sh b/examples/HydroTests/BlobTest_Braspenning2022/getICs.sh new file mode 100755 index 0000000000000000000000000000000000000000..c59520f00831d17ba9c653312771dbfbf4082b84 --- /dev/null +++ b/examples/HydroTests/BlobTest_Braspenning2022/getICs.sh @@ -0,0 +1,10 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_16_100.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_16_10.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_32_100.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_32_10.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_64_100.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_64_10.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_128_100.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/BlobTest_Braspenning22/blob_128_10.hdf5 + diff --git a/examples/HydroTests/BlobTest_Braspenning2022/plot_cloud_evolution.py b/examples/HydroTests/BlobTest_Braspenning2022/plot_cloud_evolution.py new file mode 100755 index 0000000000000000000000000000000000000000..7293624a73aa4fcf136260b988ffa178a2e02de6 --- /dev/null +++ b/examples/HydroTests/BlobTest_Braspenning2022/plot_cloud_evolution.py @@ -0,0 +1,243 @@ +from swiftsimio import load +from swiftsimio.visualisation.volume_render import render_gas +import numpy as np +import matplotlib +import matplotlib.pyplot as plt +import os +from unyt import s, pc, Myr, kb, mp, cm, K, erg, g, G, km +import h5py +import glob + + +def analyse_snapshot(i, chi, rho_cl0, m_cl0, resolution): + print("analysing snapshot", i) + try: + data = load("./{}/blob_{:0=4d}.hdf5".format(resolution, i)) + except OSError: + print("snapshot {} is not found".format(i)) + exit() + + cloud = data.gas.densities.value > rho_cl0 / 3 + m_cl = np.sum(data.gas.masses.value[cloud]) + dense_mass = m_cl / m_cl0 + + # Resample onto a 16^3 grid + # Naive histogram + x_cl = np.median(data.gas.coordinates[:, 0].value[cloud]) + bins_y = np.linspace(0, 1, 17) + bins_z = np.linspace(0, 1, 17) + bins_x = np.linspace(0, 4, 4 * 16 + 1) + # Shift x_bins such that x_cl is in the centre of a bin + bin_space_x = 1 / 16 + bins_x += x_cl - bin_space_x / 2 + bins_x = np.sort(bins_x % 4) + print(bins_x) + binned_masses, _ = np.histogramdd( + data.gas.coordinates.v, weights=data.gas.masses.v, bins=[bins_x, bins_y, bins_z] + ) + binned_masses = binned_masses * data.gas.masses.units + densities_resampled = binned_masses / (data.metadata.boxsize[1] / 16) ** 3 + densities_resampled = densities_resampled.to(data.gas.densities.units) + + cloud_resampled = densities_resampled.value > rho_cl0 / 3 + m_cl_resampled = np.sum(binned_masses.value[cloud_resampled]) + # For volume rendering we compute the mass from the density and the volume + dense_mass_resampled_16 = m_cl_resampled / m_cl0 + + # Resample onto a 32^3 grid + # Naive histogram + x_cl = np.median(data.gas.coordinates[:, 0].value[cloud]) + bins_y = np.linspace(0, 1, 33) + bins_z = np.linspace(0, 1, 33) + bins_x = np.linspace(0, 4, 4 * 32 + 1) + # Shift x_bins such that x_cl is in the centre of a bin + bin_space_x = 1 / 32 + bins_x += x_cl - bin_space_x / 2 + bins_x = np.sort(bins_x % 4) + print(bins_x) + binned_masses, _ = np.histogramdd( + data.gas.coordinates.v, weights=data.gas.masses.v, bins=[bins_x, bins_y, bins_z] + ) + binned_masses = binned_masses * data.gas.masses.units + densities_resampled = binned_masses / (data.metadata.boxsize[1] / 32) ** 3 + densities_resampled = densities_resampled.to(data.gas.densities.units) + + cloud_resampled = densities_resampled.value > rho_cl0 / 3 + m_cl_resampled = np.sum(binned_masses.value[cloud_resampled]) + # For volume rendering we compute the mass from the density and the volume + dense_mass_resampled_32 = m_cl_resampled / m_cl0 + + t = data.metadata.time.to(s).value + + # Convert internal energies to temperatures + temperatures = (((5 / 3 - 1) * data.gas.internal_energies / kb) * mp).value + data.gas.temperatures = temperatures + + if chi == 100: + m_t_intermediate = ( + np.sum( + data.gas.masses.value[ + (data.gas.temperatures > 10 ** 4.5) + & (data.gas.temperatures < 10 ** 5.5) + ] + ) + / m_cl0 + ) + elif chi == 10: + m_t_intermediate = ( + np.sum( + data.gas.masses.value[ + (data.gas.temperatures > 10 ** 4.25) + & (data.gas.temperatures < 10 ** 4.75) + ] + ) + / m_cl0 + ) + + """ + resample the intermediate temperature gas to 32^3 resolution + """ + x_cl = np.median(data.gas.coordinates[:, 0].value[cloud]) + bins_y = np.linspace(0, 1, 33) + bins_z = np.linspace(0, 1, 33) + bins_x = np.linspace(0, 4, 4 * 32 + 1) + # Shift x_bins such that x_cl is in the centre of a bin + bin_space_x = 1 / 32 + bins_x += x_cl - bin_space_x / 2 + bins_x = np.sort(bins_x % 4) + print(bins_x) + binned_masses, _ = np.histogramdd( + data.gas.coordinates.v, weights=data.gas.masses.v, bins=[bins_x, bins_y, bins_z] + ) + binned_masstemp, _ = np.histogramdd( + data.gas.coordinates.v, + weights=data.gas.masses.v * data.gas.temperatures, + bins=[bins_x, bins_y, bins_z], + ) + binned_temperatures = binned_masstemp / binned_masses + binned_masses = binned_masses * data.gas.masses.units + + if chi == 100: + mask = (binned_temperatures > 10 ** 4.5) & (binned_temperatures < 10 ** 5.5) + m_t_intermediate_resampled_32 = np.sum(binned_masses.v[mask]) / m_cl0 + elif chi == 10: + mask = (binned_temperatures > 10 ** 4.25) & (binned_temperatures < 10 ** 4.75) + m_t_intermediate_resampled_32 = np.sum(binned_masses.v[mask]) / m_cl0 + + """ + resample the intermediate temperature gas to 16^3 resolution + """ + x_cl = np.median(data.gas.coordinates[:, 0].value[cloud]) + bins_y = np.linspace(0, 1, 17) + bins_z = np.linspace(0, 1, 17) + bins_x = np.linspace(0, 4, 4 * 16 + 1) + # Shift x_bins such that x_cl is in the centre of a bin + bin_space_x = 1 / 16 + bins_x += x_cl - bin_space_x / 2 + bins_x = np.sort(bins_x % 4) + print(bins_x) + binned_masses, _ = np.histogramdd( + data.gas.coordinates.v, weights=data.gas.masses.v, bins=[bins_x, bins_y, bins_z] + ) + binned_masstemp, _ = np.histogramdd( + data.gas.coordinates.v, + weights=data.gas.masses.v * data.gas.temperatures, + bins=[bins_x, bins_y, bins_z], + ) + binned_temperatures = binned_masstemp / binned_masses + binned_masses = binned_masses * data.gas.masses.units + + if chi == 100: + mask = (binned_temperatures > 10 ** 4.5) & (binned_temperatures < 10 ** 5.5) + m_t_intermediate_resampled_16 = np.sum(binned_masses.v[mask]) / m_cl0 + elif chi == 10: + mask = (binned_temperatures > 10 ** 4.25) & (binned_temperatures < 10 ** 4.75) + m_t_intermediate_resampled_16 = np.sum(binned_masses.v[mask]) / m_cl0 + + return ( + t, + dense_mass, + m_t_intermediate, + dense_mass_resampled_16, + m_t_intermediate_resampled_16, + dense_mass_resampled_32, + m_t_intermediate_resampled_32, + ) + + +def load_first_snapshot(chi, resolution): + # Load snapshot 0 + try: + data_0 = load("./{}/blob_0000.hdf5".format(resolution)) + except OSError: + print("snapshot 0 is not found") + + v_wind0 = data_0.gas.velocities[ + 0, 0 + ] # All wind particles have the same initial velocity + r_cl0 = data_0.metadata.boxsize[1] * 0.1 # The cloud is 0.1 * boxsize + + rho_cl0 = chi * np.percentile(data_0.gas.densities.value, 50) + cloud_0 = ( + data_0.gas.densities.value > rho_cl0 / 3 + ) # Create a mask for the dense gas + m_cl0 = np.sum(data_0.gas.masses.value[cloud_0]) + + t_cc = (np.sqrt(chi) * r_cl0 / v_wind0).to(s) # Cloud crushing time + + # This will give the number of snapshots + 1 IC file + hdf5counter = len(glob.glob1("./{}".format(resolution), "*.hdf5")) + # Subtract the IC file from the count + steps = hdf5counter - 1 + + return rho_cl0, m_cl0, steps, t_cc + + +# What is the number of (particles)^1/3? +resolution = 128 +# What is the density contrast? +chi = 10 + +rho_cl0, m_cl0, steps, t_cc = load_first_snapshot(chi, resolution) + +time = np.zeros(steps) +dense_mass = np.zeros(steps) +intermediate_temp_mass = np.zeros(steps) + +dense_mass_16 = np.zeros(steps) +intermediate_temp_mass_16 = np.zeros(steps) + +dense_mass_32 = np.zeros(steps) +intermediate_temp_mass_32 = np.zeros(steps) + +for i in range(steps): + time[i], dense_mass[i], intermediate_temp_mass[i], dense_mass_16[ + i + ], intermediate_temp_mass_16[i], dense_mass_32[i], intermediate_temp_mass_32[ + i + ] = analyse_snapshot( + i, chi, rho_cl0, m_cl0, resolution + ) + +fig, ax = plt.subplots(3, 2, figsize=(10, 15)) + +ax[0, 0].plot(time / t_cc, dense_mass) +ax[0, 0].set_title("Dense Mass") + +ax[0, 1].plot(time / t_cc, intermediate_temp_mass) +ax[0, 1].set_title("Intermediate-temperature Mass") + +ax[1, 0].plot(time / t_cc, dense_mass_16) +ax[1, 0].set_title("Dense Mass resampled 16^3") + +ax[1, 1].plot(time / t_cc, intermediate_temp_mass_16) +ax[1, 1].set_title("Intermediate-temperature Mass resampled 16^3") + +ax[2, 0].plot(time / t_cc, dense_mass_32) +ax[2, 0].set_title("Dense Mass resampled 32^3") + +ax[2, 1].plot(time / t_cc, intermediate_temp_mass_32) +ax[2, 1].set_title("Intermediate-temperature Mass resampled 32^3") + +plt.savefig("cloud_evolution") +plt.close() diff --git a/examples/HydroTests/Diffusion_1D/run.sh b/examples/HydroTests/Diffusion_1D/run.sh index fb70805ca01b96df20fff438b2141b03ccf6e7ab..38871c80c1bfd20dcfb2d1047e65a83ae9db619d 100755 --- a/examples/HydroTests/Diffusion_1D/run.sh +++ b/examples/HydroTests/Diffusion_1D/run.sh @@ -4,8 +4,8 @@ if [ ! -e diffusion.hdf5 ] then echo "Generating initial conditions for the Sedov blast example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --limiter --threads=1 diffusion.yml 2>&1 | tee output.log +../../../swift --hydro --limiter --threads=1 diffusion.yml 2>&1 | tee output.log diff --git a/examples/HydroTests/Diffusion_1D/run_set.sh b/examples/HydroTests/Diffusion_1D/run_set.sh index d6b1f233e65b4e7851f15cf3bc8aadceff4710d7..d665a5970c1899891ed75fce6bfe43636358a809 100644 --- a/examples/HydroTests/Diffusion_1D/run_set.sh +++ b/examples/HydroTests/Diffusion_1D/run_set.sh @@ -1,7 +1,7 @@ #!/bin/bash beta=(1.0 0.1 0.01 0.001) -swift_location="../../swift" +swift_location="../../../swift" flags="--hydro --limiter --threads=2" parameter="diffusion.yml" parameter_fixed="diffusion_fixed_alpha.yml" @@ -13,12 +13,12 @@ do mkdir beta_$i cd beta_$i - python ../$make_ic_script + python3 ../$make_ic_script ../$swift_location $flags -P "SPH:diffusion_beta:${i}" ../$parameter - python ../$plot_script + python3 ../$plot_script ../$swift_location $flags -P "SPH:diffusion_beta:${i}" ../$parameter_fixed - python ../$plot_script -s diffusion_fixed_alpha -o diffusion_fixed_alpha.png + python3 ../$plot_script -s diffusion_fixed_alpha -o diffusion_fixed_alpha.png rm *.hdf5 diff --git a/examples/HydroTests/EvrardCollapse_3D/plotSolution.py b/examples/HydroTests/EvrardCollapse_3D/plotSolution.py index 07ffd592e1dcd310f417557de9b9df50cc9ba378..43cb309a6fcf929a4768ed99c0b3593a33723702 100644 --- a/examples/HydroTests/EvrardCollapse_3D/plotSolution.py +++ b/examples/HydroTests/EvrardCollapse_3D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -24,9 +24,11 @@ import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np from scipy import stats import h5py +import sys # Parameters gas_gamma = 5.0 / 3.0 # Polytropic index @@ -37,26 +39,7 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state -# Plot parameters -params = { - "axes.labelsize": 10, - "axes.titlesize": 10, - "font.size": 12, - "legend.fontsize": 12, - "xtick.labelsize": 10, - "ytick.labelsize": 10, - "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, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -71,20 +54,20 @@ eta = sim["/HydroScheme"].attrs["Kernel eta"] git = sim["Code"].attrs["Git Revision"] coords = sim["/PartType0/Coordinates"] -x = sqrt( +x = np.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) +v = np.sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2 + vels[:, 2] ** 2) u = sim["/PartType0/InternalEnergies"][:] S = sim["/PartType0/Entropies"][:] P = sim["/PartType0/Pressures"][:] rho = sim["/PartType0/Densities"][:] # Bin the data -x_bin_edge = logspace(-3.0, log10(2.0), 100) +x_bin_edge = np.logspace(-3.0, np.log10(2.0), 100) x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge) v_bin, _, _ = stats.binned_statistic(x, v, statistic="mean", bins=x_bin_edge) @@ -102,79 +85,117 @@ P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2) S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2) -ref = loadtxt("evrardCollapse3D_exact.txt") +ref = np.loadtxt("evrardCollapse3D_exact.txt") # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=1, + markeredgecolor="none", + alpha=0.2, + zorder=-1, + rasterized=True, + linestyle="none", +) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) # Velocity profile -------------------------------- -subplot(231) -semilogx(x, -v, ".", color="r", ms=0.2) -semilogx(ref[:, 0], ref[:, 2], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, -v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) -xlim(1.0e-3, 2.0) -ylim(-1.7, 0.1) +plt.subplot(231) +plt.semilogx(x, -v, **scatter_props) +plt.semilogx(ref[:, 0], ref[:, 2], "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, -v_bin, yerr=v_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) +plt.xlim(1.0e-3, 2.0) +plt.ylim(-1.7, 0.1) # Density profile -------------------------------- -subplot(232) -loglog(x, rho, ".", color="r", ms=0.2) -loglog(ref[:, 0], ref[:, 1], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho$", labelpad=0) -xlim(1.0e-3, 2.0) -ylim(1.0e-2, 1.0e4) +plt.subplot(232) +plt.loglog(x, rho, **scatter_props) +plt.loglog(ref[:, 0], ref[:, 1], "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.xlim(1.0e-3, 2.0) +plt.ylim(1.0e-2, 1.0e4) # Pressure profile -------------------------------- -subplot(233) -loglog(x, P, ".", color="r", ms=0.2) -loglog(ref[:, 0], ref[:, 3], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Pressure}}~P$", labelpad=0) -xlim(1.0e-3, 2.0) -ylim(1.0e-4, 1.0e3) +plt.subplot(233) +plt.loglog(x, P, **scatter_props) +plt.loglog(ref[:, 0], ref[:, 3], "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.xlim(1.0e-3, 2.0) +plt.ylim(1.0e-4, 1.0e3) # Internal energy profile ------------------------- -subplot(234) -loglog(x, u, ".", color="r", ms=0.2) -loglog(ref[:, 0], ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) -xlim(1.0e-3, 2.0) -ylim(1.0e-2, 2.0) +plt.subplot(234) +plt.loglog(x, u, **scatter_props) +plt.loglog( + ref[:, 0], + ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), + "--", + color=line_color, + alpha=0.8, + lw=1.2, +) +plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.xlim(1.0e-3, 2.0) +plt.ylim(1.0e-2, 2.0) # Entropy profile --------------------------------- -subplot(235) -semilogx(x, S, ".", color="r", ms=0.2) -semilogx(ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=0) -xlim(1.0e-3, 2.0) -ylim(0.0, 0.25) +plt.subplot(235) +plt.semilogx(x, S, **scatter_props) +plt.semilogx( + ref[:, 0], + ref[:, 3] / ref[:, 1] ** gas_gamma, + "--", + color=line_color, + alpha=0.8, + lw=1.2, +) +plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0) +plt.xlim(1.0e-3, 2.0) +plt.ylim(0.0, 0.25) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) -text( - -0.49, +text_fontsize = 5 + +plt.text( + -0.45, 0.9, "Evrard collapse with $\\gamma=%.3f$ in 3D\nat $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, +) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$Swift$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - -tight_layout() -savefig("EvrardCollapse.png", dpi=200) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("EvrardCollapse.png", dpi=200) diff --git a/examples/HydroTests/EvrardCollapse_3D/run.sh b/examples/HydroTests/EvrardCollapse_3D/run.sh index ae02bcc0baed1aff87c3866b77075f6cb0f89a27..0e1d99f89ec65b74f4bca7604cfd0bfa1c4194b9 100755 --- a/examples/HydroTests/EvrardCollapse_3D/run.sh +++ b/examples/HydroTests/EvrardCollapse_3D/run.sh @@ -4,11 +4,11 @@ if [ ! -e evrard.hdf5 ] then echo "Generating initial conditions for the Evrard collapse example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --self-gravity --threads=4 evrard.yml 2>&1 | tee output.log +../../../swift --hydro --self-gravity --threads=4 evrard.yml 2>&1 | tee output.log # Get the high resolution 1D reference result if not present. if [ ! -e evrardCollapse3D_exact.txt ] @@ -18,4 +18,4 @@ then fi # Plot the solution -python plotSolution.py 8 +python3 plotSolution.py 8 diff --git a/examples/HydroTests/Gradients/run.sh b/examples/HydroTests/Gradients/run.sh index 86ee9a68389319b1cb5f7327e8bd689b6212e6c1..e6427788bd08041c31eab6ba1a514009e5ebfefb 100755 --- a/examples/HydroTests/Gradients/run.sh +++ b/examples/HydroTests/Gradients/run.sh @@ -1,13 +1,13 @@ #! /bin/bash -python makeICs.py stretched -../../swift --hydro --threads=2 gradientsStretched.yml -python plot.py gradients_stretched_0001.hdf5 stretched +python3 makeICs.py stretched +../../../swift --hydro --threads=2 gradientsStretched.yml +python3 plot.py gradients_stretched_0001.hdf5 stretched -python makeICs.py cartesian -../../swift --hydro --threads=2 gradientsCartesian.yml -python plot.py gradients_cartesian_0001.hdf5 cartesian +python3 makeICs.py cartesian +../../../swift --hydro --threads=2 gradientsCartesian.yml +python3 plot.py gradients_cartesian_0001.hdf5 cartesian -python makeICs.py random -../../swift --hydro --threads=2 gradientsRandom.yml -python plot.py gradients_random_0001.hdf5 random +python3 makeICs.py random +../../../swift --hydro --threads=2 gradientsRandom.yml +python3 plot.py gradients_random_0001.hdf5 random diff --git a/examples/HydroTests/GreshoVortex_2D/makeIC.py b/examples/HydroTests/GreshoVortex_2D/makeIC.py index 8aeaaa5037c2de48b98d00ea844bb7467e1a04a6..649933bd35d062f4e49d84c9921213f634f1e37b 100644 --- a/examples/HydroTests/GreshoVortex_2D/makeIC.py +++ b/examples/HydroTests/GreshoVortex_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/GreshoVortex_2D/plotSolution.py b/examples/HydroTests/GreshoVortex_2D/plotSolution.py index fd63e22ba5c995b4ec3ab9b50b9b6f69750a08b0..c2cc978b0c5ef738d840a762ecc79ad88690e95c 100644 --- a/examples/HydroTests/GreshoVortex_2D/plotSolution.py +++ b/examples/HydroTests/GreshoVortex_2D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -91,7 +91,7 @@ u = sim["/PartType0/InternalEnergies"][:] S = sim["/PartType0/Entropies"][:] P = sim["/PartType0/Pressures"][:] -# Bin te data +# Bin the data r_bin_edge = np.arange(0.0, 1.0, 0.02) r_bin = 0.5 * (r_bin_edge[1:] + r_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(r, rho, statistic="mean", bins=r_bin_edge) diff --git a/examples/HydroTests/GreshoVortex_2D/run.sh b/examples/HydroTests/GreshoVortex_2D/run.sh index 0e24112a0faafcd38a06216494f87888f3e132e2..fa994d00971b2fff5e421aa1e313aec7ff99b3d8 100755 --- a/examples/HydroTests/GreshoVortex_2D/run.sh +++ b/examples/HydroTests/GreshoVortex_2D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e greshoVortex.hdf5 ] then echo "Generating initial conditions for the Gresho-Chan vortex example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 gresho.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 gresho.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 11 +python3 plotSolution.py 11 diff --git a/examples/HydroTests/GreshoVortex_3D/makeIC.py b/examples/HydroTests/GreshoVortex_3D/makeIC.py index 8204cf65f340517d58f068d4bf9b53fe5ca40c40..c61113271512ca085be84681a82dc13b6b3d0834 100644 --- a/examples/HydroTests/GreshoVortex_3D/makeIC.py +++ b/examples/HydroTests/GreshoVortex_3D/makeIC.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/HydroTests/GreshoVortex_3D/plotSolution.py b/examples/HydroTests/GreshoVortex_3D/plotSolution.py index d3be5a404f7011d3dea24c992ef4ca93b4c4988c..dd9393047b809cab8f090c4a28abf9a7b5042066 100644 --- a/examples/HydroTests/GreshoVortex_3D/plotSolution.py +++ b/examples/HydroTests/GreshoVortex_3D/plotSolution.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -106,7 +106,7 @@ try: except: plot_viscosity = False -# Bin te data +# Bin the data r_bin_edge = np.arange(0.0, 1.0, 0.02) r_bin = 0.5 * (r_bin_edge[1:] + r_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(r, rho, statistic="mean", bins=r_bin_edge) diff --git a/examples/HydroTests/GreshoVortex_3D/run.sh b/examples/HydroTests/GreshoVortex_3D/run.sh index 15b613782e685d86321460b33c52fe9109230840..1d7686e7fe9dfc49e3a2c42b774680624a338efa 100755 --- a/examples/HydroTests/GreshoVortex_3D/run.sh +++ b/examples/HydroTests/GreshoVortex_3D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e greshoVortex.hdf5 ] then echo "Generating initial conditions for the Gresho-Chan vortex example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=4 gresho.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 gresho.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 11 +python3 plotSolution.py 11 diff --git a/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py b/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py index 410bdae84356b93b9a476fb3117722bf1b7cc0c8..862ff3ec03655639037287c48b97c40ddbb1f062 100644 --- a/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py +++ b/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py @@ -17,128 +17,139 @@ # ############################################################################## -import numpy as np import matplotlib matplotlib.use("Agg") -import pylab as pl -import h5py +import matplotlib.pyplot as plt +import numpy as np import sys +import h5py + +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") -# Parameters -gamma = 1.4 # Polytropic index - -# 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.0, - "text.latex.unicode": True, -} -pl.rcParams.update(params) -pl.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - -# Read the snapshot index from the command line argument 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/Densities"] +rho = file["/PartType0/Densities"][:] v = file["/PartType0/Velocities"][:, 0] -u = file["/PartType0/InternalEnergies"] -S = file["/PartType0/Entropies"] -P = file["/PartType0/Pressures"] +u = file["/PartType0/InternalEnergies"][:] +S = file["/PartType0/Entropies"][:] +P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] scheme = file["/HydroScheme"].attrs["Scheme"] kernel = file["/HydroScheme"].attrs["Kernel function"] neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] eta = file["/HydroScheme"].attrs["Kernel eta"][0] +gamma = file["/HydroScheme"].attrs["Adiabatic index"] git = file["Code"].attrs["Git Revision"] +if gamma != 1.4: + print( + "Error: SWIFT was run with the wrong adiabatic index. Should have been 1.4", + gamma, + ) + exit(1) + ref = np.loadtxt("interactingBlastWaves1D_exact.txt") # Plot the interesting quantities -fig, ax = pl.subplots(2, 3) +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=4, + markeredgecolor="none", + alpha=0.2, + zorder=-1, + rasterized=True, + linestyle="none", +) # Velocity profile -ax[0][0].plot(x, v, "r.", markersize=4.0) -ax[0][0].plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, linewidth=1.2) -ax[0][0].set_xlabel("${\\rm{Position}}~x$", labelpad=0) -ax[0][0].set_ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) -ax[0][0].set_xlim(0.0, 1.0) -ax[0][0].set_ylim(-1.0, 15.0) +plt.subplot(231) +plt.plot(x, v, **scatter_props) +plt.plot(ref[:, 0], ref[:, 2], "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) +plt.xlim(0.0, 1.0) +plt.ylim(-1.0, 15.0) # Density profile -ax[0][1].plot(x, rho, "r.", markersize=4.0) -ax[0][1].plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, linewidth=1.2) -ax[0][1].set_xlabel("${\\rm{Position}}~x$", labelpad=0) -ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad=0) -ax[0][1].set_xlim(0.0, 1.0) +plt.subplot(232) +plt.plot(x, rho, **scatter_props) +plt.plot(ref[:, 0], ref[:, 1], "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.xlim(0.0, 1.0) # Pressure profile -ax[0][2].plot(x, P, "r.", markersize=4.0) -ax[0][2].plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, linewidth=1.2) -ax[0][2].set_xlabel("${\\rm{Position}}~x$", labelpad=0) -ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad=0) -ax[0][2].set_xlim(0.0, 1.0) +plt.subplot(233) +plt.plot(x, P, **scatter_props) +plt.plot(ref[:, 0], ref[:, 3], "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.xlim(0.0, 1.0) # Internal energy profile -ax[1][0].plot(x, u, "r.", markersize=4.0) -ax[1][0].plot( - ref[:, 0], ref[:, 3] / ref[:, 1] / (gamma - 1.0), "k--", alpha=0.8, linewidth=1.2 +plt.subplot(234) +plt.plot(x, u, **scatter_props) +plt.plot( + ref[:, 0], + ref[:, 3] / ref[:, 1] / (gamma - 1.0), + "--", + color=line_color, + alpha=0.8, + lw=1.2, ) -ax[1][0].set_xlabel("${\\rm{Position}}~x$", labelpad=0) -ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) -ax[1][0].set_xlim(0.0, 1.0) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.xlim(0.0, 1.0) # Entropy profile -ax[1][1].plot(x, S, "r.", markersize=4.0) -ax[1][1].plot( - ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "k--", alpha=0.8, linewidth=1.2 +plt.subplot(235) +plt.plot(x, S, **scatter_props) +plt.plot( + ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "--", color=line_color, alpha=0.8, lw=1.2 ) -ax[1][1].set_xlabel("${\\rm{Position}}~x$", labelpad=0) -ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0) -ax[1][1].set_xlim(0.0, 1.0) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0) +plt.xlim(0.0, 1.0) # Run information -ax[1][2].set_frame_on(False) -ax[1][2].text( - -0.49, +plt.subplot(236, frameon=False) + +text_fontsize = 5 + +plt.text( + -0.45, 0.9, "Interacting blast waves test\nwith $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format( - gamma, time + gamma[0], time ), - fontsize=10, + fontsize=text_fontsize, ) -ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10) -ax[1][2].text(-0.49, 0.4, scheme, fontsize=10) -ax[1][2].text(-0.49, 0.3, kernel, fontsize=10) -ax[1][2].text( - -0.49, +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, 0.2, - "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), - fontsize=10, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, ) -ax[1][2].set_xlim(-0.5, 0.5) -ax[1][2].set_ylim(0.0, 1.0) -ax[1][2].set_xticks([]) -ax[1][2].set_yticks([]) +plt.xlim(-0.5, 0.5) +plt.ylim(0.0, 1.0) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() -pl.tight_layout() -pl.savefig("InteractingBlastWaves.png", dpi=200) +plt.savefig("InteractingBlastWaves.png", dpi=200) diff --git a/examples/HydroTests/InteractingBlastWaves_1D/run.sh b/examples/HydroTests/InteractingBlastWaves_1D/run.sh index 42034d5e541c4e038a9284e88651cb6a9fa9013f..a3f927491ec07680756072beb378cc7903e374da 100755 --- a/examples/HydroTests/InteractingBlastWaves_1D/run.sh +++ b/examples/HydroTests/InteractingBlastWaves_1D/run.sh @@ -4,11 +4,11 @@ if [ ! -e interactingBlastWaves.hdf5 ] then echo "Generating initial conditions for the Sedov blast example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 interactingBlastWaves.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 --limiter interactingBlastWaves.yml 2>&1 | tee output.log # Get the high resolution reference solution if not present. if [ ! -e interactingBlastWaves1D_exact.txt ] @@ -18,4 +18,4 @@ then fi # Plot the solution -python plotSolution.py 4 +python3 plotSolution.py 4 diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC.py index 4467acd397cb17406c277f1dd660fa797e72d8a6..66c797f76bb66bf397acd400fa11a1a71dfb62f1 100644 --- a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC.py +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py index 4cef8f7f1f1899759b6874f046deb922d1c88273..ebe431cb6e809a897411f9c01d0451da7582fb28 100644 --- a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh index 4e565a2de588c53f54c928b3b9f7dfec483c8220..d134b8be7a52fb7ad6e968d5a98d7be9499461ca 100755 --- a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh @@ -1,15 +1,20 @@ #!/bin/bash - # Generate the initial conditions if they are not present. +# Generate the initial conditions if they are not present. +if [ ! -e glassPlane_128.hdf5 ] +then + ./getGlass.sh +fi + if [ ! -e kelvinHelmholtzGrowthRate.hdf5 ] then echo "Generating initial conditions for the Kelvin-Helmholtz growth rate " \ "example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 100 +python3 plotSolution.py 100 diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC.py index 2d74ad03bba6081bf6d7f8817908c66036842a86..3a0c1f3d39acd30a25e60f62b39a2d6768297e2c 100644 --- a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC.py +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py index b8845d9fa0818b9c99054b421432d2d2777f4c8a..df897020fca1e146784053454e3ea4bad701ae52 100644 --- a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh index 4e565a2de588c53f54c928b3b9f7dfec483c8220..73f81e3da13e5395d74e9b5509a6dfd0ec31ea3e 100755 --- a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh @@ -1,15 +1,20 @@ #!/bin/bash - # Generate the initial conditions if they are not present. +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + ./getGlass.sh +fi + if [ ! -e kelvinHelmholtzGrowthRate.hdf5 ] then echo "Generating initial conditions for the Kelvin-Helmholtz growth rate " \ "example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 100 +python3 plotSolution.py 100 diff --git a/examples/HydroTests/KelvinHelmholtz_2D/makeIC.py b/examples/HydroTests/KelvinHelmholtz_2D/makeIC.py index 8f9c161d99484a82c1d89d48438e42b1d33b7407..18897ff5edf39e3e5d92c819a67b3b1e89198056 100644 --- a/examples/HydroTests/KelvinHelmholtz_2D/makeIC.py +++ b/examples/HydroTests/KelvinHelmholtz_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py b/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py index 7d582fbb609f30bd2c17e40be836feb2325b6bbe..750dd016328dfdd44296bb400f6dc84a7e58fea5 100644 --- a/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py +++ b/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -35,32 +35,12 @@ rho2 = 1 # Outskirts density import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py +import sys -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -78,111 +58,131 @@ pos = sim["/PartType0/Coordinates"][:, :] x = pos[:, 0] - boxSize / 2 y = pos[:, 1] - boxSize / 2 vel = sim["/PartType0/Velocities"][:, :] -v_norm = sqrt(vel[:, 0] ** 2 + vel[:, 1] ** 2) +v_norm = np.sqrt(vel[:, 0] ** 2 + vel[:, 1] ** 2) rho = sim["/PartType0/Densities"][:] u = sim["/PartType0/InternalEnergies"][:] S = sim["/PartType0/Entropies"][:] P = sim["/PartType0/Pressures"][:] # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) # Azimuthal velocity profile ----------------------------- -subplot(231) -scatter( +plt.subplot(231) +plt.scatter( pos[:, 0], pos[:, 1], c=vel[:, 0], cmap="PuBu", edgecolors="face", - s=4, + s=0.25, vmin=-1.0, vmax=1.0, ) -text( +plt.text( 0.97, 0.97, "${\\rm{Velocity~along}}~x$", ha="right", va="top", backgroundcolor="w" ) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=0) -xlim(0, 1) -ylim(0, 1) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Position}}~y$", labelpad=0) +plt.xlim(0, 1) +plt.ylim(0, 1) # Radial density profile -------------------------------- -subplot(232) -scatter( - pos[:, 0], pos[:, 1], c=rho, cmap="PuBu", edgecolors="face", s=4, vmin=0.8, vmax=2.2 +plt.subplot(232) +plt.scatter( + pos[:, 0], + pos[:, 1], + c=rho, + cmap="PuBu", + edgecolors="face", + s=0.25, + vmin=0.8, + vmax=2.2, ) -text(0.97, 0.97, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w") -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=0) -xlim(0, 1) -ylim(0, 1) +plt.text(0.97, 0.97, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w") +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Position}}~y$", labelpad=0) +plt.xlim(0, 1) +plt.ylim(0, 1) # Radial pressure profile -------------------------------- -subplot(233) -scatter(pos[:, 0], pos[:, 1], c=P, cmap="PuBu", edgecolors="face", s=4, vmin=1, vmax=4) -text(0.97, 0.97, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w") -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=0) -xlim(0, 1) -ylim(0, 1) +plt.subplot(233) +plt.scatter( + pos[:, 0], pos[:, 1], c=P, cmap="PuBu", edgecolors="face", s=0.25, vmin=1, vmax=4 +) +plt.text(0.97, 0.97, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w") +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Position}}~y$", labelpad=0) +plt.xlim(0, 1) +plt.ylim(0, 1) # Internal energy profile -------------------------------- -subplot(234) -scatter( - pos[:, 0], pos[:, 1], c=u, cmap="PuBu", edgecolors="face", s=4, vmin=1.5, vmax=5.0 +plt.subplot(234) +plt.scatter( + pos[:, 0], + pos[:, 1], + c=u, + cmap="PuBu", + edgecolors="face", + s=0.25, + vmin=1.5, + vmax=5.0, +) +plt.text( + 0.97, 0.97, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w" ) -text(0.97, 0.97, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w") -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=0) -xlim(0, 1) -ylim(0, 1) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Position}}~y$", labelpad=0) +plt.xlim(0, 1) +plt.ylim(0, 1) # Radial entropy profile -------------------------------- -subplot(235) -scatter( - pos[:, 0], pos[:, 1], c=S, cmap="PuBu", edgecolors="face", s=4, vmin=0.5, vmax=3.0 +plt.subplot(235) +plt.scatter( + pos[:, 0], + pos[:, 1], + c=S, + cmap="PuBu", + edgecolors="face", + s=0.25, + vmin=0.5, + vmax=3.0, ) -text(0.97, 0.97, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w") -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=0) -xlim(0, 1) -ylim(0, 1) - -# Image -------------------------------------------------- -# subplot(234) -# scatter(pos[:,0], pos[:,1], c=v_norm, cmap="PuBu", edgecolors='face', s=4, vmin=0, vmax=1) -# text(0.95, 0.95, "$|v|$", ha="right", va="top") -# xlim(0,1) -# ylim(0,1) -# xlabel("$x$", labelpad=0) -# ylabel("$y$", labelpad=0) +plt.text(0.97, 0.97, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w") +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Position}}~y$", labelpad=0) +plt.xlim(0, 1) +plt.ylim(0, 1) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) -text(-0.49, 0.9, "Kelvin-Helmholtz instability at $t=%.2f$" % (time), fontsize=10) -text( - -0.49, +plt.text(-0.45, 0.9, "Kelvin-Helmholtz instability at $t=%.2f$" % (time), fontsize=10) +plt.text( + -0.45, 0.8, - "Centre:~~~ $(P, \\rho, v) = (%.3f, %.3f, %.3f)$" % (P1, rho1, v1), + "Centre: $(P, \\rho, v) = (%.3f, %.3f, %.3f)$" % (P1, rho1, v1), fontsize=10, ) -text( - -0.49, +plt.text( + -0.45, 0.7, "Outskirts: $(P, \\rho, v) = (%.3f, %.3f, %.3f)$" % (P2, rho2, v2), fontsize=10, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - -savefig("KelvinHelmholtz.png", dpi=200) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=10) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=10) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=10) +plt.text( + -0.45, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10 +) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("KelvinHelmholtz.png", dpi=200) diff --git a/examples/HydroTests/KelvinHelmholtz_2D/run.sh b/examples/HydroTests/KelvinHelmholtz_2D/run.sh index ee3cdc4f542e3be1f28ba0deff279c612b9c49b0..95c3076921de9fd373ed8319de34e7b8c69046f0 100755 --- a/examples/HydroTests/KelvinHelmholtz_2D/run.sh +++ b/examples/HydroTests/KelvinHelmholtz_2D/run.sh @@ -4,12 +4,13 @@ if [ ! -e kelvinHelmholtz.hdf5 ] then echo "Generating initial conditions for the Kelvin-Helmholtz example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=4 kelvinHelmholtz.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 kelvinHelmholtz.yml 2>&1 | tee output.log # Plot the solution +python3 plotSolution.py 100 python3 makeMovieSwiftsimIO.py diff --git a/examples/HydroTests/KeplerianRing/README.md b/examples/HydroTests/KeplerianRing/README.md index 1cb2e2119d0f0bb093abf194ab18da91dd587d32..0ac9317f910ec265b1d9ea001e6c1ee09fc91a7d 100644 --- a/examples/HydroTests/KeplerianRing/README.md +++ b/examples/HydroTests/KeplerianRing/README.md @@ -18,9 +18,6 @@ The test uses: + $c_s = 0.01 \ll v_\phi = 10$, enforced by giving them a mass of 1 unit and an internal energy of 0.015. -Please note that the initial condition generator **requires python3 rather than -python2**, as well as the plotting script. - Code Setup ---------- @@ -69,7 +66,7 @@ Plotting Once you have ran swift (we suggest that you use the following) - ../swift --external-gravity --stars --hydro --threads=16 keplerian_ring.yml 2>&1 | tee output.log + ../../../swift --external-gravity --stars --hydro --threads=16 keplerian_ring.yml 2>&1 | tee output.log there will be around 350 ```.hdf5``` files in your directory. To check out the results of the example use the plotting script: diff --git a/examples/HydroTests/KeplerianRing/plotSolution.py b/examples/HydroTests/KeplerianRing/plotSolution.py index 239bb639c9488f8f17b76c6ae8679338fd6bf7f9..25c3a846d972e900affda4b14aa2498fabad369c 100644 --- a/examples/HydroTests/KeplerianRing/plotSolution.py +++ b/examples/HydroTests/KeplerianRing/plotSolution.py @@ -306,7 +306,7 @@ def plot_extra_info(ax, filename): ax.text(-0.49, 0.8, f"Compiler: {compiler_name} {compiler_version}", fontsize=10) ax.text(-0.49, 0.7, "Rotations are quoted at $r=1$", fontsize=10) ax.plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) - ax.text(-0.49, 0.5, f"$\\textsc{{Swift}}$ {git}", fontsize=10) + ax.text(-0.49, 0.5, f"$SWIFT$ {git}", fontsize=10) ax.text(-0.49, 0.4, scheme, fontsize=10) ax.text(-0.49, 0.3, kernel, fontsize=10) ax.text( diff --git a/examples/HydroTests/KeplerianRing/run.sh b/examples/HydroTests/KeplerianRing/run.sh index b11a0c1e52f2792447ffe39efbdf5c7b2ddda437..daf743309663c6c3e84c90d53daea720917f411f 100755 --- a/examples/HydroTests/KeplerianRing/run.sh +++ b/examples/HydroTests/KeplerianRing/run.sh @@ -9,7 +9,7 @@ then fi rm -rf keplerian_ring_*.hdf5 -../../swift --external-gravity --hydro --threads=1 --verbose=1 keplerian_ring.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --threads=1 --verbose=1 keplerian_ring.yml 2>&1 | tee output.log echo echo diff --git a/examples/HydroTests/Noh_1D/makeIC.py b/examples/HydroTests/Noh_1D/makeIC.py index 3f33e758fb92dbcbbc23a09c3df751d3909b8829..fe1d38b97d701ed67875749014f0d76994aa63b1 100644 --- a/examples/HydroTests/Noh_1D/makeIC.py +++ b/examples/HydroTests/Noh_1D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/Noh_1D/plotSolution.py b/examples/HydroTests/Noh_1D/plotSolution.py index 4bd837e5890e81f3ff80f1c06e2a3276eb9f328a..d224b7fe7bd67ac63fa172065c84dcff8a26405c 100644 --- a/examples/HydroTests/Noh_1D/plotSolution.py +++ b/examples/HydroTests/Noh_1D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -29,36 +29,15 @@ v0 = 1 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py +import sys -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) - # Read the simulation data sim = h5py.File("noh_%04d.hdf5" % snap, "r") boxSize = sim["/Header"].attrs["BoxSize"][0] @@ -111,77 +90,100 @@ u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=4, + markeredgecolor="none", + zorder=-1, + rasterized=True, + linestyle="none", +) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) # Velocity profile -------------------------------- -subplot(231) -plot(x, v, ".", color="r", ms=4.0) -plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Velocity}}~v_x$", labelpad=-4) -xlim(-0.5, 0.5) -ylim(-1.2, 1.2) +plt.subplot(231) +plt.plot(x, v, **scatter_props) +plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_x$", labelpad=-4) +plt.xlim(-0.5, 0.5) +plt.ylim(-1.2, 1.2) # Density profile -------------------------------- -subplot(232) -plot(x, rho, ".", color="r", ms=4.0) -plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho$", labelpad=0) -xlim(-0.5, 0.5) -ylim(0.95, 4.4) +plt.subplot(232) +plt.plot(x, rho, **scatter_props) +plt.plot(x_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.xlim(-0.5, 0.5) +plt.ylim(0.95, 4.4) # Pressure profile -------------------------------- -subplot(233) -plot(x, P, ".", color="r", ms=4.0) -plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Pressure}}~P$", labelpad=0) -xlim(-0.5, 0.5) -ylim(-0.05, 1.8) +plt.subplot(233) +plt.plot(x, P, **scatter_props) +plt.plot(x_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.xlim(-0.5, 0.5) +plt.ylim(-0.05, 1.8) # Internal energy profile ------------------------- -subplot(234) -plot(x, u, ".", color="r", ms=4.0) -plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) -xlim(-0.5, 0.5) -ylim(-0.05, 0.8) +plt.subplot(234) +plt.plot(x, u, **scatter_props) +plt.plot(x_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.xlim(-0.5, 0.5) +plt.ylim(-0.05, 0.8) # Entropy profile --------------------------------- -subplot(235) -plot(x, S, ".", color="r", ms=4.0) -plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=-9) -xlim(-0.5, 0.5) -ylim(-0.05, 0.2) +plt.subplot(235) +plt.plot(x, S, **scatter_props) +plt.plot(x_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("${\\rm{Position}}~x$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=-9) +plt.xlim(-0.5, 0.5) +plt.ylim(-0.05, 0.2) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) -text( - -0.49, +text_fontsize = 5 + +plt.text( + -0.45, 0.9, "Noh problem with $\\gamma=%.3f$ in 1D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) -text( - -0.49, +plt.text( + -0.45, 0.8, - "ICs:~~ $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0), - fontsize=10, + "ICs: $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0), + fontsize=text_fontsize, +) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - - -savefig("Noh.png", dpi=200) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("Noh.png", dpi=200) diff --git a/examples/HydroTests/Noh_1D/run.sh b/examples/HydroTests/Noh_1D/run.sh index 0a7bd0574c19428a7f82141e619aed1f49e677be..fa8793964ad3cf00e1ee6168ddea47ca94042a90 100755 --- a/examples/HydroTests/Noh_1D/run.sh +++ b/examples/HydroTests/Noh_1D/run.sh @@ -4,11 +4,11 @@ if [ ! -e noh.hdf5 ] then echo "Generating initial conditions for the Noh problem..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 noh.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 noh.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 12 +python3 plotSolution.py 12 diff --git a/examples/HydroTests/Noh_2D/makeIC.py b/examples/HydroTests/Noh_2D/makeIC.py index eab3b3387b9e4b1efa3d71da72933e8e73cbd925..eec7d86d170711df58c7d47372ed2f02e7d9924b 100644 --- a/examples/HydroTests/Noh_2D/makeIC.py +++ b/examples/HydroTests/Noh_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/Noh_2D/plotSolution.py b/examples/HydroTests/Noh_2D/plotSolution.py index a0715d958b821825987738f4c13e9c179b69297d..62e5437c1b82c4862137a0cb738a7eeb22726845 100644 --- a/examples/HydroTests/Noh_2D/plotSolution.py +++ b/examples/HydroTests/Noh_2D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -29,33 +29,13 @@ v0 = 1 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt from scipy import stats +import numpy as np import h5py +import sys -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -81,7 +61,7 @@ rho = sim["/PartType0/Densities"][:] r = np.sqrt((x - 1) ** 2 + (y - 1) ** 2) v = -np.sqrt(vx ** 2 + vy ** 2) -# Bin te data +# Bin the data r_bin_edge = np.arange(0.0, 1.0, 0.02) r_bin = 0.5 * (r_bin_edge[1:] + r_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(r, rho, statistic="mean", bins=r_bin_edge) @@ -129,82 +109,106 @@ u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=1, + markeredgecolor="none", + alpha=0.2, + zorder=-1, + rasterized=True, + linestyle="none", +) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) # Velocity profile -------------------------------- -subplot(231) -plot(r, v, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4) -xlim(0, 0.5) -ylim(-1.2, 0.4) +plt.subplot(231) +plt.plot(r, v, **scatter_props) +plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, v_bin, yerr=v_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4) +plt.xlim(0, 0.5) +plt.ylim(-1.2, 0.4) # Density profile -------------------------------- -subplot(232) -plot(r, rho, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho$", labelpad=0) -xlim(0, 0.5) -ylim(0.95, 19) +plt.subplot(232) +plt.plot(r, rho, **scatter_props) +plt.plot(x_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.xlim(0, 0.5) +plt.ylim(0.95, 19) # Pressure profile -------------------------------- -subplot(233) -plot(r, P, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Pressure}}~P$", labelpad=0) -xlim(0, 0.5) -ylim(-0.5, 11) +plt.subplot(233) +plt.plot(r, P, **scatter_props) +plt.plot(x_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, P_bin, yerr=P_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.xlim(0, 0.5) +plt.ylim(-0.5, 11) # Internal energy profile ------------------------- -subplot(234) -plot(r, u, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) -xlim(0, 0.5) -ylim(-0.05, 0.8) +plt.subplot(234) +plt.plot(r, u, **scatter_props) +plt.plot(x_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, u_bin, yerr=u_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.xlim(0, 0.5) +plt.ylim(-0.05, 0.8) # Entropy profile --------------------------------- -subplot(235) -plot(r, S, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=-9) -xlim(0, 0.5) -ylim(-0.05, 0.2) +plt.subplot(235) +plt.plot(r, S, **scatter_props) +plt.plot(x_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, S_bin, yerr=S_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=-9) +plt.xlim(0, 0.5) +plt.ylim(-0.05, 0.2) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) + +text_fontsize = 5 -text( - -0.49, +plt.text( + -0.45, 0.9, "Noh problem with $\\gamma=%.3f$ in 2D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) -text( - -0.49, +plt.text( + -0.45, 0.8, - "ICs:~~ $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0), - fontsize=10, + "ICs: $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0), + fontsize=text_fontsize, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - - -savefig("Noh.png", dpi=200) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("Noh.png", dpi=200) diff --git a/examples/HydroTests/Noh_2D/run.sh b/examples/HydroTests/Noh_2D/run.sh index 36e2d7db554823c60bacbd2d907b9d06789a9fcd..8a4740897cbb5b8e5d878436338e165f990f757a 100755 --- a/examples/HydroTests/Noh_2D/run.sh +++ b/examples/HydroTests/Noh_2D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e noh.hdf5 ] then echo "Generating initial conditions for the Noh problem..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=2 noh.yml 2>&1 | tee output.log +../../../swift --hydro --threads=2 noh.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 12 +python3 plotSolution.py 12 diff --git a/examples/HydroTests/Noh_3D/makeIC.py b/examples/HydroTests/Noh_3D/makeIC.py index 74a22a4cf6e4f77863190d75cf54f397d16775d4..5c0b8eb1cf323bbbc4f644a1f46581c19cd82f3b 100644 --- a/examples/HydroTests/Noh_3D/makeIC.py +++ b/examples/HydroTests/Noh_3D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/Noh_3D/plotSolution.py b/examples/HydroTests/Noh_3D/plotSolution.py index 1e008fc7dd4e43f0f9e78f5aefd476a9766049c7..6b46ed07802b45abb4f4f0debea813f9b96ee4be 100644 --- a/examples/HydroTests/Noh_3D/plotSolution.py +++ b/examples/HydroTests/Noh_3D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -29,37 +29,16 @@ v0 = 1 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt from scipy import stats +import numpy as np import h5py +import sys -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) - # Read the simulation data sim = h5py.File("noh_%04d.hdf5" % snap, "r") boxSize = sim["/Header"].attrs["BoxSize"][0] @@ -84,7 +63,7 @@ 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) -# Bin te data +# Bin the data r_bin_edge = np.arange(0.0, 1.0, 0.02) r_bin = 0.5 * (r_bin_edge[1:] + r_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(r, rho, statistic="mean", bins=r_bin_edge) @@ -135,82 +114,106 @@ u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=1, + markeredgecolor="none", + alpha=0.2, + zorder=-1, + rasterized=True, + linestyle="none", +) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) # Velocity profile -------------------------------- -subplot(231) -plot(r, v, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4) -xlim(0, 0.5) -ylim(-1.2, 0.4) +plt.subplot(231) +plt.plot(r, v, **scatter_props) +plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, v_bin, yerr=v_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4) +plt.xlim(0, 0.5) +plt.ylim(-1.2, 0.4) # Density profile -------------------------------- -subplot(232) -plot(r, rho, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho$", labelpad=0) -xlim(0, 0.5) -ylim(0.95, 71) +plt.subplot(232) +plt.plot(r, rho, **scatter_props) +plt.plot(x_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.xlim(0, 0.5) +plt.ylim(0.95, 71) # Pressure profile -------------------------------- -subplot(233) -plot(r, P, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Pressure}}~P$", labelpad=0) -xlim(0, 0.5) -ylim(-0.5, 25) +plt.subplot(233) +plt.plot(r, P, **scatter_props) +plt.plot(x_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, P_bin, yerr=P_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.xlim(0, 0.5) +plt.ylim(-0.5, 25) # Internal energy profile ------------------------- -subplot(234) -plot(r, u, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) -xlim(0, 0.5) -ylim(-0.05, 0.8) +plt.subplot(234) +plt.plot(r, u, **scatter_props) +plt.plot(x_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, u_bin, yerr=u_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.xlim(0, 0.5) +plt.ylim(-0.05, 0.8) # Entropy profile --------------------------------- -subplot(235) -plot(r, S, ".", color="r", ms=0.5, alpha=0.2) -plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2) -errorbar(r_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=-9) -xlim(0, 0.5) -ylim(-0.05, 0.2) +plt.subplot(235) +plt.plot(r, S, **scatter_props) +plt.plot(x_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(r_bin, S_bin, yerr=S_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=-9) +plt.xlim(0, 0.5) +plt.ylim(-0.05, 0.2) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) -text( - -0.49, +text_fontsize = 5 + +plt.text( + -0.45, 0.9, "Noh problem with $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) -text( - -0.49, +plt.text( + -0.45, 0.8, - "ICs:~~ $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0), - fontsize=10, + "ICs: $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0), + fontsize=text_fontsize, +) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - - -savefig("Noh.png", dpi=200) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("Noh.png", dpi=200) diff --git a/examples/HydroTests/Noh_3D/run.sh b/examples/HydroTests/Noh_3D/run.sh index 7845b5cfb592f0f8ac4c3951b48689623c06b21c..f004b1e1628accb6be22262cd136ed979cbd42ff 100755 --- a/examples/HydroTests/Noh_3D/run.sh +++ b/examples/HydroTests/Noh_3D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e noh.hdf5 ] then echo "Generating initial conditions for the Noh problem..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=2 noh.yml 2>&1 | tee output.log +../../../swift --hydro --threads=2 noh.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 12 +python3 plotSolution.py 12 diff --git a/examples/HydroTests/PerturbedBox_2D/makeIC.py b/examples/HydroTests/PerturbedBox_2D/makeIC.py index 0b14dec93006b1c564761d66fe14c1c03ce6358b..c9dd873acd30ff2fb0c19a1198be487c44ea041e 100644 --- a/examples/HydroTests/PerturbedBox_2D/makeIC.py +++ b/examples/HydroTests/PerturbedBox_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/PerturbedBox_3D/makeIC.py b/examples/HydroTests/PerturbedBox_3D/makeIC.py index 2c2ed6f2cecc8ddbf72542d26a0260f0b39f3492..72f4ba44121d184e4563606bb5bc1dfa3380ea44 100644 --- a/examples/HydroTests/PerturbedBox_3D/makeIC.py +++ b/examples/HydroTests/PerturbedBox_3D/makeIC.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 diff --git a/examples/HydroTests/PerturbedBox_3D/run.sh b/examples/HydroTests/PerturbedBox_3D/run.sh index 463f6fecf16e13c76d713bba3ef4112ffbde509c..2090b49cd264eb41202fd413aa18434ae314ced1 100755 --- a/examples/HydroTests/PerturbedBox_3D/run.sh +++ b/examples/HydroTests/PerturbedBox_3D/run.sh @@ -4,7 +4,7 @@ if [ ! -e perturbedBox.hdf5 ] then echo "Generating initial conditions for the perturbed box example..." - python makeIC.py 50 + python3 makeIC.py 50 fi -../../swift --hydro --threads=16 perturbedBox.yml 2>&1 | tee output.log +../../../swift --hydro --threads=16 perturbedBox.yml 2>&1 | tee output.log diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/run.sh b/examples/HydroTests/Rayleigh-Taylor_2D/run.sh index 0c8d3eaab9a401c4be3a586e3b7b8900406bbdbb..5d5a73fb331f16c8ab818ae6b2625b506d3d5293 100644 --- a/examples/HydroTests/Rayleigh-Taylor_2D/run.sh +++ b/examples/HydroTests/Rayleigh-Taylor_2D/run.sh @@ -6,10 +6,10 @@ set -e if [ ! -e rayleigh_taylor.hdf5 ] then echo "Generating initial conditions for the Rayleigh Taylor example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --external-gravity --threads=8 rayleigh_taylor.yml 2>&1 | tee output.log +../../../swift --hydro --external-gravity --threads=8 rayleigh_taylor.yml 2>&1 | tee output.log -python makeMovie.py -i 0 -f 1001 +python3 makeMovie.py -i 0 -f 1001 diff --git a/examples/HydroTests/SedovBlast_1D/makeIC.py b/examples/HydroTests/SedovBlast_1D/makeIC.py index ca810ceba17276a50b774afbfbe2afb5918405f4..be22d3a35dff4a9de65e88034c6bb70fd1dc6165 100644 --- a/examples/HydroTests/SedovBlast_1D/makeIC.py +++ b/examples/HydroTests/SedovBlast_1D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SedovBlast_1D/plotSolution.py b/examples/HydroTests/SedovBlast_1D/plotSolution.py index 04707529b5a7ad7ae7459803772b83f83d7830cd..93ecd501946b31d17e84376277813f64203abd0c 100644 --- a/examples/HydroTests/SedovBlast_1D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_1D/plotSolution.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 @@ -35,11 +35,13 @@ gas_gamma = 5.0 / 3.0 # Gas polytropic index import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py +import sys # Plot parameters -style.use("../../../tools/stylesheets/mnras.mplstyle") +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -228,13 +230,13 @@ s_s = P_s / rho_s ** gas_gamma # entropic function # Plot the interesting quantities -figure(figsize=(7, 7 / 1.6)) +plt.figure(figsize=(7, 7 / 1.6)) line_color = "C4" scatter_props = dict( marker=".", - ms=1, + ms=4, markeredgecolor="none", alpha=1.0, zorder=-1, @@ -243,88 +245,88 @@ scatter_props = dict( ) # Velocity profile -------------------------------- -subplot(231) -plot(r, v_r, **scatter_props) -plot(r_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2) -xlabel("Radius $r$") -ylabel("Radialvelocity $v_r$") -xlim(0, 1.3 * r_shock) -ylim(-0.2, 3.8) +plt.subplot(231) +plt.plot(r, v_r, **scatter_props) +plt.plot(r_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("Radius $r$") +plt.ylabel("Radialvelocity $v_r$") +plt.xlim(0, 1.3 * r_shock) +plt.ylim(-0.2, 3.8) # Density profile -------------------------------- -subplot(232) -plot(r, rho, **scatter_props) -plot(r_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2) -xlabel("Radius $r$") -ylabel("Density $\\rho$") -xlim(0, 1.3 * r_shock) -ylim(-0.2, 5.2) +plt.subplot(232) +plt.plot(r, rho, **scatter_props) +plt.plot(r_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("Radius $r$") +plt.ylabel("Density $\\rho$") +plt.xlim(0, 1.3 * r_shock) +plt.ylim(-0.2, 5.2) # Pressure profile -------------------------------- -subplot(233) -plot(r, P, **scatter_props) -plot(r_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2) -xlabel("Radius $r$") -ylabel("Pressure $P$") -xlim(0, 1.3 * r_shock) -ylim(-1, 12.5) +plt.subplot(233) +plt.plot(r, P, **scatter_props) +plt.plot(r_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("Radius $r$") +plt.ylabel("Pressure $P$") +plt.xlim(0, 1.3 * r_shock) +plt.ylim(-1, 12.5) # Internal energy profile ------------------------- -subplot(234) -plot(r, u, **scatter_props) -plot(r_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2) -xlabel("Radius $r$") -ylabel("Internal Energy $u$") -xlim(0, 1.3 * r_shock) -ylim(-2, 22) +plt.subplot(234) +plt.plot(r, u, **scatter_props) +plt.plot(r_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2) +plt.xlabel("Radius $r$") +plt.ylabel("Internal Energy $u$") +plt.xlim(0, 1.3 * r_shock) +plt.ylim(-2, 22) # Entropy profile --------------------------------- -subplot(235) -xlabel("Radius $r$") +plt.subplot(235) +plt.xlabel("Radius $r$") if plot_diffusion or plot_viscosity: if plot_diffusion: - plot(r, diffusion, **scatter_props) + plt.plot(r, diffusion, **scatter_props) if plot_viscosity: - plot(r, viscosity, **scatter_props) + plt.plot(r, viscosity, **scatter_props) - ylabel(r"Rate Coefficient $\alpha$", labelpad=0) - legend() + plt.ylabel(r"Rate Coefficient $\alpha$", labelpad=0) + plt.legend() else: - plot(r, S, **scatter_props) - plot(r_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2) - ylabel("Entropy $S$", labelpad=0) - ylim(-5, 50) + plt.plot(r, S, **scatter_props) + plt.plot(r_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2) + plt.ylabel("Entropy $S$", labelpad=0) + plt.ylim(-5, 50) -xlim(0, 1.3 * r_shock) +plt.xlim(0, 1.3 * r_shock) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) text_fontsize = 5 -text( +plt.text( -0.45, 0.9, "Sedov blast with $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time), fontsize=text_fontsize, ) -text(-0.45, 0.8, "Background $\\rho_0=%.2f$" % (rho_0), fontsize=text_fontsize) -text(-0.45, 0.7, "Energy injected $E_0=%.2f$" % (E_0), fontsize=text_fontsize) -plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.45, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize) -text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) -text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) -text( +plt.text(-0.45, 0.8, "Background $\\rho_0=%.2f$" % (rho_0), fontsize=text_fontsize) +plt.text(-0.45, 0.7, "Energy injected $E_0=%.2f$" % (E_0), fontsize=text_fontsize) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( -0.45, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=text_fontsize, ) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) -tight_layout() +plt.tight_layout() -savefig("Sedov.png") +plt.savefig("Sedov.png") diff --git a/examples/HydroTests/SedovBlast_1D/run.sh b/examples/HydroTests/SedovBlast_1D/run.sh index 2888790eb1877541166c04002f9ae9539e9ef6d7..fb82605166cbd9116160ff3e362d72590d2ecd1d 100755 --- a/examples/HydroTests/SedovBlast_1D/run.sh +++ b/examples/HydroTests/SedovBlast_1D/run.sh @@ -4,11 +4,11 @@ if [ ! -e sedov.hdf5 ] then echo "Generating initial conditions for the Sedov blast example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --limiter --threads=1 sedov.yml 2>&1 | tee output.log +../../../swift --hydro --limiter --threads=1 sedov.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 5 +python3 plotSolution.py 5 diff --git a/examples/HydroTests/SedovBlast_2D/makeIC.py b/examples/HydroTests/SedovBlast_2D/makeIC.py index 30591d669644e791dd41ad0995f70661009210f5..af4e0b9c51628dce9826e6a0d58472a50080874f 100644 --- a/examples/HydroTests/SedovBlast_2D/makeIC.py +++ b/examples/HydroTests/SedovBlast_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SedovBlast_2D/plotSolution.py b/examples/HydroTests/SedovBlast_2D/plotSolution.py index d18b9de84dcce969d203fdf34dd1e08406e12cd0..3eaf7dcad3e98c98c3182ab710494241deec6fad 100644 --- a/examples/HydroTests/SedovBlast_2D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_2D/plotSolution.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 @@ -76,7 +76,7 @@ try: except: plot_viscosity = False -# Bin te data +# Bin the data r_bin_edge = np.arange(0.0, 0.5, 0.01) r_bin = 0.5 * (r_bin_edge[1:] + r_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(r, rho, statistic="mean", bins=r_bin_edge) diff --git a/examples/HydroTests/SedovBlast_2D/run.sh b/examples/HydroTests/SedovBlast_2D/run.sh index 0ad75f9d378712bd62cdd47c66240976cc57f04c..04b3a39658b1056ad8dcede360f5f91bb67fb083 100755 --- a/examples/HydroTests/SedovBlast_2D/run.sh +++ b/examples/HydroTests/SedovBlast_2D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e sedov.hdf5 ] then echo "Generating initial conditions for the Sedov blast example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --limiter --threads=1 sedov.yml 2>&1 | tee output.log +../../../swift --hydro --limiter --threads=1 sedov.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 5 +python3 plotSolution.py 5 diff --git a/examples/HydroTests/SedovBlast_3D/makeIC.py b/examples/HydroTests/SedovBlast_3D/makeIC.py index bc22af821c580b488c271b4a8ed1f9ab50f85c1b..920323f5caa4ed7f089d2d9223614221e9603600 100644 --- a/examples/HydroTests/SedovBlast_3D/makeIC.py +++ b/examples/HydroTests/SedovBlast_3D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SedovBlast_3D/plotSolution.py b/examples/HydroTests/SedovBlast_3D/plotSolution.py index 2684d0de53e38550b9a3f95fc6ff67729f35ba90..e6f5381df0397fc78e277faf5a676035e42ec92c 100644 --- a/examples/HydroTests/SedovBlast_3D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_3D/plotSolution.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 @@ -78,7 +78,7 @@ try: except: plot_viscosity = False -# Bin te data +# Bin the data r_bin_edge = np.arange(0.0, 0.5, 0.01) r_bin = 0.5 * (r_bin_edge[1:] + r_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(r, rho, statistic="mean", bins=r_bin_edge) diff --git a/examples/HydroTests/SedovBlast_3D/run.sh b/examples/HydroTests/SedovBlast_3D/run.sh index 62af72bacca62cd18fa77300d887b0bf0dd20789..10fc83f69ef7b1b046ea2b197b60e4807db89d97 100755 --- a/examples/HydroTests/SedovBlast_3D/run.sh +++ b/examples/HydroTests/SedovBlast_3D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e sedov.hdf5 ] then echo "Generating initial conditions for the Sedov blast example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --limiter --threads=4 sedov.yml 2>&1 | tee output.log +../../../swift --hydro --limiter --threads=4 sedov.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 5 +python3 plotSolution.py 5 diff --git a/examples/HydroTests/SineWavePotential_1D/run.sh b/examples/HydroTests/SineWavePotential_1D/run.sh index 920bd413a71a99e043cc7d31be088e0037c6c7c1..ed2e5d220ad0dc28cb9f69deca2d7e7eab446401 100755 --- a/examples/HydroTests/SineWavePotential_1D/run.sh +++ b/examples/HydroTests/SineWavePotential_1D/run.sh @@ -3,12 +3,12 @@ if [ ! -e sineWavePotential.hdf5 ] then echo "Generating initial conditions for the 1D SineWavePotential example..." - python makeIC.py + python3 makeIC.py fi -../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log for f in sineWavePotential_*.hdf5 do - python plotSolution.py $f + python3 plotSolution.py $f done diff --git a/examples/HydroTests/SineWavePotential_2D/run.sh b/examples/HydroTests/SineWavePotential_2D/run.sh index 920bd413a71a99e043cc7d31be088e0037c6c7c1..ed2e5d220ad0dc28cb9f69deca2d7e7eab446401 100755 --- a/examples/HydroTests/SineWavePotential_2D/run.sh +++ b/examples/HydroTests/SineWavePotential_2D/run.sh @@ -3,12 +3,12 @@ if [ ! -e sineWavePotential.hdf5 ] then echo "Generating initial conditions for the 1D SineWavePotential example..." - python makeIC.py + python3 makeIC.py fi -../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log for f in sineWavePotential_*.hdf5 do - python plotSolution.py $f + python3 plotSolution.py $f done diff --git a/examples/HydroTests/SineWavePotential_3D/run.sh b/examples/HydroTests/SineWavePotential_3D/run.sh index 920bd413a71a99e043cc7d31be088e0037c6c7c1..ed2e5d220ad0dc28cb9f69deca2d7e7eab446401 100755 --- a/examples/HydroTests/SineWavePotential_3D/run.sh +++ b/examples/HydroTests/SineWavePotential_3D/run.sh @@ -3,12 +3,12 @@ if [ ! -e sineWavePotential.hdf5 ] then echo "Generating initial conditions for the 1D SineWavePotential example..." - python makeIC.py + python3 makeIC.py fi -../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log for f in sineWavePotential_*.hdf5 do - python plotSolution.py $f + python3 plotSolution.py $f done diff --git a/examples/HydroTests/SodShockSpherical_2D/makeIC.py b/examples/HydroTests/SodShockSpherical_2D/makeIC.py index 4492f0db194f41f27d7ca6c563c1064fbb79c617..1680372a83edef0775191552e5aaaef7fa7fa8e5 100644 --- a/examples/HydroTests/SodShockSpherical_2D/makeIC.py +++ b/examples/HydroTests/SodShockSpherical_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SodShockSpherical_2D/plotSolution.py b/examples/HydroTests/SodShockSpherical_2D/plotSolution.py index 978847c12d463f8dfd4dc746db4ec218b4849b6b..c9b4f19cd7d5c80ab16ea1f160713f9831b88a6d 100644 --- a/examples/HydroTests/SodShockSpherical_2D/plotSolution.py +++ b/examples/HydroTests/SodShockSpherical_2D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -24,9 +24,11 @@ import matplotlib matplotlib.use("Agg") -from pylab import * -from scipy import stats +import matplotlib.pyplot as plt +import numpy as np +import scipy.stats as stats import h5py +import sys # Parameters gas_gamma = 5.0 / 3.0 # Polytropic index @@ -37,28 +39,7 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -73,9 +54,9 @@ eta = sim["/HydroScheme"].attrs["Kernel eta"] git = sim["Code"].attrs["Git Revision"] coords = sim["/PartType0/Coordinates"] -x = sqrt((coords[:, 0] - 0.5) ** 2 + (coords[:, 1] - 0.5) ** 2) +x = np.sqrt((coords[:, 0] - 0.5) ** 2 + (coords[:, 1] - 0.5) ** 2) vels = sim["/PartType0/Velocities"] -v = sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2) +v = np.sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2) u = sim["/PartType0/InternalEnergies"][:] S = sim["/PartType0/Entropies"][:] P = sim["/PartType0/Pressures"][:] @@ -99,81 +80,113 @@ P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2) S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2) -ref = loadtxt("sodShockSpherical2D_exact.txt") +ref = np.loadtxt("sodShockSpherical2D_exact.txt") # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=1, + markeredgecolor="none", + alpha=0.5, + zorder=-1, + rasterized=True, + linestyle="none", +) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) # Velocity profile -------------------------------- -subplot(231) -plot(x, v, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) +plt.subplot(231) +plt.plot(x, v, **scatter_props) +plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) # Density profile -------------------------------- -subplot(232) -plot(x, rho, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.subplot(232) +plt.plot(x, rho, **scatter_props) +plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) # Pressure profile -------------------------------- -subplot(233) -plot(x, P, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.subplot(233) +plt.plot(x, P, **scatter_props) +plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) # Internal energy profile ------------------------- -subplot(234) -plot(x, u, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.subplot(234) +plt.plot(x, u, **scatter_props) +plt.plot( + ref[:, 0], + ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), + color=line_color, + alpha=0.8, + lw=1.2, +) +plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) # Entropy profile --------------------------------- -subplot(235) -plot(x, S, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=0) +plt.subplot(235) +plt.plot(x, S, **scatter_props) +plt.plot( + ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, color=line_color, alpha=0.8, lw=1.2 +) +plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) -text( - -0.49, +text_fontsize = 5 + +plt.text( + -0.45, 0.9, "Sod shock with $\\gamma=%.3f$ in 2D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) -text( - -0.49, +plt.text( + -0.45, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + fontsize=text_fontsize, ) -text( - -0.49, +plt.text( + -0.45, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, +) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - - -savefig("SodShock.png", dpi=200) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("SodShock.png", dpi=200) diff --git a/examples/HydroTests/SodShockSpherical_2D/run.sh b/examples/HydroTests/SodShockSpherical_2D/run.sh index 609f2e0ae065a1fa76ee7bcfa90efa9cb1aa020a..c73a2cc6693df5c1b4d5323879b676cb6f30aafa 100755 --- a/examples/HydroTests/SodShockSpherical_2D/run.sh +++ b/examples/HydroTests/SodShockSpherical_2D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the Sod shock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log # Get the high resolution 1D reference solution if not present. if [ ! -e sodShockSpherical2D_exact.txt ] @@ -21,4 +21,4 @@ then echo "Fetching reference solution for 2D spherical Sod shock example..." ./getReference.sh fi -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/SodShockSpherical_3D/makeIC.py b/examples/HydroTests/SodShockSpherical_3D/makeIC.py index bd0e1932b5c5fd0c06ba26fb101e737c15ee566e..4a1c72012d2fcae38a1d3183eb1cfb08a091abae 100644 --- a/examples/HydroTests/SodShockSpherical_3D/makeIC.py +++ b/examples/HydroTests/SodShockSpherical_3D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SodShockSpherical_3D/plotSolution.py b/examples/HydroTests/SodShockSpherical_3D/plotSolution.py index bd878db4d8501f1a90ea7d634258f51280a29159..a548f8ed7cbacd5c4bae05164fadc18a6c2cf9d1 100644 --- a/examples/HydroTests/SodShockSpherical_3D/plotSolution.py +++ b/examples/HydroTests/SodShockSpherical_3D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify @@ -24,9 +24,11 @@ import matplotlib matplotlib.use("Agg") -from pylab import * -from scipy import stats +import matplotlib.pyplot as plt +import numpy as np +import scipy.stats as stats import h5py +import sys # Parameters gas_gamma = 5.0 / 3.0 # Polytropic index @@ -37,28 +39,7 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -73,11 +54,11 @@ eta = sim["/HydroScheme"].attrs["Kernel eta"] git = sim["Code"].attrs["Git Revision"] coords = sim["/PartType0/Coordinates"] -x = sqrt( +x = np.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) +v = np.sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2 + vels[:, 2] ** 2) u = sim["/PartType0/InternalEnergies"][:] S = sim["/PartType0/Entropies"][:] P = sim["/PartType0/Pressures"][:] @@ -101,81 +82,113 @@ P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2) S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2) -ref = loadtxt("sodShockSpherical3D_exact.txt") +ref = np.loadtxt("sodShockSpherical3D_exact.txt") # Plot the interesting quantities -figure() +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=1, + markeredgecolor="none", + alpha=0.5, + zorder=-1, + rasterized=True, + linestyle="none", +) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) # Velocity profile -------------------------------- -subplot(231) -plot(x, v, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) +plt.subplot(231) +plt.plot(x, v, **scatter_props) +plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) # Density profile -------------------------------- -subplot(232) -plot(x, rho, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.subplot(232) +plt.plot(x, rho, **scatter_props) +plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) # Pressure profile -------------------------------- -subplot(233) -plot(x, P, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.subplot(233) +plt.plot(x, P, **scatter_props) +plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) # Internal energy profile ------------------------- -subplot(234) -plot(x, u, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.subplot(234) +plt.plot(x, u, **scatter_props) +plt.plot( + ref[:, 0], + ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), + color=line_color, + alpha=0.8, + lw=1.2, +) +plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) # Entropy profile --------------------------------- -subplot(235) -plot(x, S, ".", color="r", ms=0.2) -plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, "k--", alpha=0.8, lw=1.2) -errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) -xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=0) +plt.subplot(235) +plt.plot(x, S, **scatter_props) +plt.plot( + ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, color=line_color, alpha=0.8, lw=1.2 +) +plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0) # Information ------------------------------------- -subplot(236, frameon=False) +plt.subplot(236, frameon=False) -text( - -0.49, +text_fontsize = 5 + +plt.text( + -0.45, 0.9, "Sod shock with $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) -text( - -0.49, +plt.text( + -0.45, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + fontsize=text_fontsize, ) -text( - -0.49, +plt.text( + -0.45, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, +) +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, ) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - - -savefig("SodShock.png", dpi=200) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() + +plt.savefig("SodShock.png", dpi=200) diff --git a/examples/HydroTests/SodShockSpherical_3D/run.sh b/examples/HydroTests/SodShockSpherical_3D/run.sh index c511dbcc6b18248bcfd33a9e0216cd22cf26aead..c4738438a702415e375b235cbff46430d32074ea 100755 --- a/examples/HydroTests/SodShockSpherical_3D/run.sh +++ b/examples/HydroTests/SodShockSpherical_3D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the Sod shock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log # Get the high resolution 1D reference solution if not present. if [ ! -e sodShockSpherical3D_exact.txt ] @@ -21,4 +21,4 @@ then echo "Fetching reference solution for 3D spherical Sod shock example..." ./getReference.sh fi -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/SodShock_1D/makeIC.py b/examples/HydroTests/SodShock_1D/makeIC.py index bb1c655dcb18b47304554da1fdac35f069b4362c..30f4d1adb70d2d8401eeaa7658ab9b52a90345f8 100644 --- a/examples/HydroTests/SodShock_1D/makeIC.py +++ b/examples/HydroTests/SodShock_1D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SodShock_1D/plotSolution.py b/examples/HydroTests/SodShock_1D/plotSolution.py index 0e5d8d5d12df6ab053af97ded693e1471b511e48..c6efc3da2a881d9d33352557b20cda77ff8e62cf 100644 --- a/examples/HydroTests/SodShock_1D/plotSolution.py +++ b/examples/HydroTests/SodShock_1D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2019 Josh Borrow (josh.borrow@durham.ac.uk) # # This program is free software: you can redistribute it and/or modify @@ -35,6 +35,10 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state +import sys + +sys.path.append("../") +from riemannSolver import RiemannSolver import matplotlib @@ -42,29 +46,7 @@ matplotlib.use("Agg") from pylab import * import h5py -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -73,11 +55,11 @@ snap = int(sys.argv[1]) sim = h5py.File("sodShock_%04d.hdf5" % snap, "r") boxSize = sim["/Header"].attrs["BoxSize"][0] time = sim["/Header"].attrs["Time"][0] -scheme = str(sim["/HydroScheme"].attrs["Scheme"]) -kernel = str(sim["/HydroScheme"].attrs["Kernel function"]) +scheme = sim["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = sim["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] eta = sim["/HydroScheme"].attrs["Kernel eta"] -git = str(sim["Code"].attrs["Git Revision"]) +git = sim["Code"].attrs["Git Revision"].decode("utf-8") x = sim["/PartType0/Coordinates"][:, 0] v = sim["/PartType0/Velocities"][:, 0] @@ -102,156 +84,23 @@ x_max = 1.0 x += x_min -# --------------------------------------------------------------- -# Don't touch anything after this. -# --------------------------------------------------------------- - -c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave -c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front - -# Helpful variable -Gama = (gas_gamma - 1.0) / (gas_gamma + 1.0) -beta = (gas_gamma - 1.0) / (2.0 * gas_gamma) - -# Characteristic function and its derivative, following Toro (2009) -def compute_f(P_3, P, c): - u = P_3 / P - if u > 1: - term1 = gas_gamma * ((gas_gamma + 1.0) * u + gas_gamma - 1.0) - term2 = sqrt(2.0 / term1) - fp = (u - 1.0) * c * term2 - dfdp = ( - c * term2 / P - + (u - 1.0) - * c - / term2 - * (-1.0 / term1 ** 2) - * gas_gamma - * (gas_gamma + 1.0) - / P - ) - else: - fp = (u ** beta - 1.0) * (2.0 * c / (gas_gamma - 1.0)) - dfdp = 2.0 * c / (gas_gamma - 1.0) * beta * u ** (beta - 1.0) / P - return (fp, dfdp) - - -# Solution of the Riemann problem following Toro (2009) -def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): - P_new = ( - (c_L + c_R + (v_L - v_R) * 0.5 * (gas_gamma - 1.0)) - / (c_L / P_L ** beta + c_R / P_R ** beta) - ) ** (1.0 / beta) - P_3 = 0.5 * (P_R + P_L) - f_L = 1.0 - while fabs(P_3 - P_new) > 1e-6: - P_3 = P_new - (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) - (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) - f = f_L + f_R + (v_R - v_L) - df = dfdp_L + dfdp_R - dp = -f / df - prnew = P_3 + dp - v_3 = v_L - f_L - return (P_new, v_3) - - -# Solve Riemann problem for post-shock region -(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) - -# Check direction of shocks and wave -shock_R = P_3 > P_R -shock_L = P_3 > P_L - -# Velocity of shock front and and rarefaction wave -if shock_R: - v_right = v_R + c_R ** 2 * (P_3 / P_R - 1.0) / (gas_gamma * (v_3 - v_R)) -else: - v_right = c_R + 0.5 * (gas_gamma + 1.0) * v_3 - 0.5 * (gas_gamma - 1.0) * v_R - -if shock_L: - v_left = v_L + c_L ** 2 * (P_3 / p_L - 1.0) / (gas_gamma * (v_3 - v_L)) -else: - v_left = c_L - 0.5 * (gas_gamma + 1.0) * v_3 + 0.5 * (gas_gamma - 1.0) * v_L - -# Compute position of the transitions -x_23 = -fabs(v_left) * time -if shock_L: - x_12 = -fabs(v_left) * time -else: - x_12 = -(c_L - v_L) * time - -x_34 = v_3 * time +# Prepare reference solution +solver = RiemannSolver(gas_gamma) -x_45 = fabs(v_right) * time -if shock_R: - x_56 = fabs(v_right) * time -else: - x_56 = (c_R + v_R) * time - - -# Prepare arrays delta_x = (x_max - x_min) / N -x_s = arange(x_min, x_max, delta_x) -rho_s = zeros(N) -P_s = zeros(N) -v_s = zeros(N) - -# Compute solution in the different regions -for i in range(N): - if x_s[i] <= x_12: - rho_s[i] = rho_L - P_s[i] = P_L - v_s[i] = v_L - if x_s[i] >= x_12 and x_s[i] < x_23: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1.0 + Gama * P_3 / P_L) - P_s[i] = P_3 - v_s[i] = v_3 - else: - rho_s[i] = rho_L * ( - Gama * (0.0 - x_s[i]) / (c_L * time) + Gama * v_L / c_L + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = P_L * (rho_s[i] / rho_L) ** gas_gamma - v_s[i] = (1.0 - Gama) * (c_L - (0.0 - x_s[i]) / time) + Gama * v_L - if x_s[i] >= x_23 and x_s[i] < x_34: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1 + Gama * P_3 / p_L) - else: - rho_s[i] = rho_L * (P_3 / P_L) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_34 and x_s[i] < x_45: - if shock_R: - rho_s[i] = rho_R * (Gama + P_3 / P_R) / (1.0 + Gama * P_3 / P_R) - else: - rho_s[i] = rho_R * (P_3 / P_R) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_45 and x_s[i] < x_56: - if shock_R: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - else: - rho_s[i] = rho_R * ( - Gama * (x_s[i]) / (c_R * time) - Gama * v_R / c_R + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = p_R * (rho_s[i] / rho_R) ** gas_gamma - v_s[i] = (1.0 - Gama) * (-c_R - (-x_s[i]) / time) + Gama * v_R - if x_s[i] >= x_56: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) # Additional arrays u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function +# Shock position (since we want to overplot it in the viscosity/diffusion plot +c_R = sqrt(gas_gamma * P_R / rho_R) +x_shock = (c_R + v_R) * time # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) # Velocity profile -------------------------------- subplot(231) @@ -304,7 +153,7 @@ if plot_alpha: plot(x, alpha, ".", color="r", ms=4.0) ylabel(r"${\rm{Viscosity}}~\alpha$", labelpad=0) # Show location of shock - plot([x_56, x_56], [-100, 100], color="k", alpha=0.5, ls="dashed", lw=1.2) + axvline(x=x_shock, color="k", alpha=0.5, ls="dashed", lw=1.2) ylim(0, 1) else: plot(x, S, ".", color="r", ms=4.0) @@ -318,29 +167,36 @@ xlim(-0.5, 0.5) # Information ------------------------------------- subplot(236, frameon=False) +text_fontsize = 5 + text( -0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 1D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, ) text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, ) plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) +text(-0.49, 0.5, "SWIFT %s" % git, fontsize=text_fontsize) +text(-0.49, 0.4, scheme, fontsize=text_fontsize) +text(-0.49, 0.3, kernel, fontsize=text_fontsize) +text( + -0.49, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) xlim(-0.5, 0.5) ylim(0, 1) xticks([]) diff --git a/examples/HydroTests/SodShock_1D/run.sh b/examples/HydroTests/SodShock_1D/run.sh index 8be97f7b34c9947268ed44b44b2c445ddb8a717f..b8bb071037e4278a90aed3a40131021f86d90db0 100755 --- a/examples/HydroTests/SodShock_1D/run.sh +++ b/examples/HydroTests/SodShock_1D/run.sh @@ -4,11 +4,11 @@ if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the 1D SodShock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log # Plot the result -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/SodShock_2D/makeIC.py b/examples/HydroTests/SodShock_2D/makeIC.py index c9c7ef2400d4727692efb7a112856065cb0fa635..27fe4b09601cf233f90d0dddfa7e895740be6a39 100644 --- a/examples/HydroTests/SodShock_2D/makeIC.py +++ b/examples/HydroTests/SodShock_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SodShock_2D/plotSolution.py b/examples/HydroTests/SodShock_2D/plotSolution.py index 1f00a24258e71c28580f54e6e37119f73adc3810..e9562d84ae420e10d3488f380a164b8ee6c00dcc 100644 --- a/examples/HydroTests/SodShock_2D/plotSolution.py +++ b/examples/HydroTests/SodShock_2D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -34,6 +34,10 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state +import sys + +sys.path.append("../") +from riemannSolver import RiemannSolver import matplotlib @@ -42,29 +46,7 @@ from pylab import * from scipy import stats import h5py -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -73,11 +55,11 @@ snap = int(sys.argv[1]) sim = h5py.File("sodShock_%04d.hdf5" % snap, "r") boxSize = sim["/Header"].attrs["BoxSize"][0] time = sim["/Header"].attrs["Time"][0] -scheme = sim["/HydroScheme"].attrs["Scheme"] -kernel = sim["/HydroScheme"].attrs["Kernel function"] +scheme = sim["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = sim["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] eta = sim["/HydroScheme"].attrs["Kernel eta"] -git = sim["Code"].attrs["Git Revision"] +git = sim["Code"].attrs["Git Revision"].decode("utf-8") x = sim["/PartType0/Coordinates"][:, 0] v = sim["/PartType0/Velocities"][:, 0] @@ -92,7 +74,7 @@ x_max = 1.0 x += x_min -# Bin te data +# Bin the data x_bin_edge = np.arange(-0.6, 0.6, 0.02) x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge) @@ -112,153 +94,19 @@ S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2) -# Analytic solution -c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave -c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front - -# Helpful variable -Gama = (gas_gamma - 1.0) / (gas_gamma + 1.0) -beta = (gas_gamma - 1.0) / (2.0 * gas_gamma) - -# Characteristic function and its derivative, following Toro (2009) -def compute_f(P_3, P, c): - u = P_3 / P - if u > 1: - term1 = gas_gamma * ((gas_gamma + 1.0) * u + gas_gamma - 1.0) - term2 = sqrt(2.0 / term1) - fp = (u - 1.0) * c * term2 - dfdp = ( - c * term2 / P - + (u - 1.0) - * c - / term2 - * (-1.0 / term1 ** 2) - * gas_gamma - * (gas_gamma + 1.0) - / P - ) - else: - fp = (u ** beta - 1.0) * (2.0 * c / (gas_gamma - 1.0)) - dfdp = 2.0 * c / (gas_gamma - 1.0) * beta * u ** (beta - 1.0) / P - return (fp, dfdp) - - -# Solution of the Riemann problem following Toro (2009) -def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): - P_new = ( - (c_L + c_R + (v_L - v_R) * 0.5 * (gas_gamma - 1.0)) - / (c_L / P_L ** beta + c_R / P_R ** beta) - ) ** (1.0 / beta) - P_3 = 0.5 * (P_R + P_L) - f_L = 1.0 - while fabs(P_3 - P_new) > 1e-6: - P_3 = P_new - (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) - (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) - f = f_L + f_R + (v_R - v_L) - df = dfdp_L + dfdp_R - dp = -f / df - prnew = P_3 + dp - v_3 = v_L - f_L - return (P_new, v_3) - - -# Solve Riemann problem for post-shock region -(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) - -# Check direction of shocks and wave -shock_R = P_3 > P_R -shock_L = P_3 > P_L - -# Velocity of shock front and and rarefaction wave -if shock_R: - v_right = v_R + c_R ** 2 * (P_3 / P_R - 1.0) / (gas_gamma * (v_3 - v_R)) -else: - v_right = c_R + 0.5 * (gas_gamma + 1.0) * v_3 - 0.5 * (gas_gamma - 1.0) * v_R - -if shock_L: - v_left = v_L + c_L ** 2 * (P_3 / p_L - 1.0) / (gas_gamma * (v_3 - v_L)) -else: - v_left = c_L - 0.5 * (gas_gamma + 1.0) * v_3 + 0.5 * (gas_gamma - 1.0) * v_L - -# Compute position of the transitions -x_23 = -fabs(v_left) * time -if shock_L: - x_12 = -fabs(v_left) * time -else: - x_12 = -(c_L - v_L) * time +# Prepare reference solution +solver = RiemannSolver(gas_gamma) -x_34 = v_3 * time - -x_45 = fabs(v_right) * time -if shock_R: - x_56 = fabs(v_right) * time -else: - x_56 = (c_R + v_R) * time - - -# Prepare arrays delta_x = (x_max - x_min) / N -x_s = arange(x_min, x_max, delta_x) -rho_s = zeros(N) -P_s = zeros(N) -v_s = zeros(N) - -# Compute solution in the different regions -for i in range(N): - if x_s[i] <= x_12: - rho_s[i] = rho_L - P_s[i] = P_L - v_s[i] = v_L - if x_s[i] >= x_12 and x_s[i] < x_23: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1.0 + Gama * P_3 / P_L) - P_s[i] = P_3 - v_s[i] = v_3 - else: - rho_s[i] = rho_L * ( - Gama * (0.0 - x_s[i]) / (c_L * time) + Gama * v_L / c_L + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = P_L * (rho_s[i] / rho_L) ** gas_gamma - v_s[i] = (1.0 - Gama) * (c_L - (0.0 - x_s[i]) / time) + Gama * v_L - if x_s[i] >= x_23 and x_s[i] < x_34: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1 + Gama * P_3 / p_L) - else: - rho_s[i] = rho_L * (P_3 / P_L) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_34 and x_s[i] < x_45: - if shock_R: - rho_s[i] = rho_R * (Gama + P_3 / P_R) / (1.0 + Gama * P_3 / P_R) - else: - rho_s[i] = rho_R * (P_3 / P_R) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_45 and x_s[i] < x_56: - if shock_R: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - else: - rho_s[i] = rho_R * ( - Gama * (x_s[i]) / (c_R * time) - Gama * v_R / c_R + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = p_R * (rho_s[i] / rho_R) ** gas_gamma - v_s[i] = (1.0 - Gama) * (-c_R - (-x_s[i]) / time) + Gama * v_R - if x_s[i] >= x_56: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) # Additional arrays u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function - # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) # Velocity profile -------------------------------- subplot(231) @@ -313,33 +161,40 @@ ylim(0.8, 3.8) # Information ------------------------------------- subplot(236, frameon=False) +text_fontsize = 5 + text( -0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 2D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, ) text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, ) plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) +text(-0.49, 0.5, "SWIFT %s" % git, fontsize=text_fontsize) +text(-0.49, 0.4, scheme, fontsize=text_fontsize) +text(-0.49, 0.3, kernel, fontsize=text_fontsize) +text( + -0.49, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) xlim(-0.5, 0.5) ylim(0, 1) xticks([]) yticks([]) - +tight_layout() savefig("SodShock.png", dpi=200) diff --git a/examples/HydroTests/SodShock_2D/run.sh b/examples/HydroTests/SodShock_2D/run.sh index a11c6291a48447b2f64aef458c01036e4ed73441..b3cf134fc76f3dd7ce7c1fc8feaaffe681c35750 100755 --- a/examples/HydroTests/SodShock_2D/run.sh +++ b/examples/HydroTests/SodShock_2D/run.sh @@ -9,10 +9,10 @@ fi if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the Sod shock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/SodShock_3D/makeIC.py b/examples/HydroTests/SodShock_3D/makeIC.py index ece334d9e454683ceb227c4a96d6269e241d8eae..d084a39cfda709aff776bffb84bfda84dcb0191e 100644 --- a/examples/HydroTests/SodShock_3D/makeIC.py +++ b/examples/HydroTests/SodShock_3D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SodShock_3D/plotSolution.py b/examples/HydroTests/SodShock_3D/plotSolution.py index b0e2e3721cab170f3dafa8dcb6c881a9dc32233e..75b72aae0d527fa9b552d13734d47252c904265e 100644 --- a/examples/HydroTests/SodShock_3D/plotSolution.py +++ b/examples/HydroTests/SodShock_3D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -34,6 +34,10 @@ v_R = 0.0 # Velocity right state P_L = 1.0 # Pressure left state P_R = 0.1 # Pressure right state +import sys + +sys.path.append("../") +from riemannSolver import RiemannSolver import matplotlib @@ -42,29 +46,7 @@ from pylab import * from scipy import stats import h5py -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) @@ -73,11 +55,11 @@ snap = int(sys.argv[1]) sim = h5py.File("sodShock_%04d.hdf5" % snap, "r") boxSize = sim["/Header"].attrs["BoxSize"][0] time = sim["/Header"].attrs["Time"][0] -scheme = sim["/HydroScheme"].attrs["Scheme"] -kernel = sim["/HydroScheme"].attrs["Kernel function"] +scheme = sim["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = sim["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] eta = sim["/HydroScheme"].attrs["Kernel eta"] -git = sim["Code"].attrs["Git Revision"] +git = sim["Code"].attrs["Git Revision"].decode("utf-8") x = sim["/PartType0/Coordinates"][:, 0] v = sim["/PartType0/Velocities"][:, 0] @@ -103,7 +85,7 @@ x_max = 1.0 x += x_min N = 1000 -# Bin te data +# Bin the data x_bin_edge = np.arange(-0.6, 0.6, 0.02) x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge) @@ -140,153 +122,19 @@ if plot_viscosity: ) alpha_visc_sigma_bin = np.sqrt(alpha2_visc_bin - alpha_visc_bin ** 2) -# Analytic solution -c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave -c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front - -# Helpful variable -Gama = (gas_gamma - 1.0) / (gas_gamma + 1.0) -beta = (gas_gamma - 1.0) / (2.0 * gas_gamma) - -# Characteristic function and its derivative, following Toro (2009) -def compute_f(P_3, P, c): - u = P_3 / P - if u > 1: - term1 = gas_gamma * ((gas_gamma + 1.0) * u + gas_gamma - 1.0) - term2 = sqrt(2.0 / term1) - fp = (u - 1.0) * c * term2 - dfdp = ( - c * term2 / P - + (u - 1.0) - * c - / term2 - * (-1.0 / term1 ** 2) - * gas_gamma - * (gas_gamma + 1.0) - / P - ) - else: - fp = (u ** beta - 1.0) * (2.0 * c / (gas_gamma - 1.0)) - dfdp = 2.0 * c / (gas_gamma - 1.0) * beta * u ** (beta - 1.0) / P - return (fp, dfdp) - - -# Solution of the Riemann problem following Toro (2009) -def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): - P_new = ( - (c_L + c_R + (v_L - v_R) * 0.5 * (gas_gamma - 1.0)) - / (c_L / P_L ** beta + c_R / P_R ** beta) - ) ** (1.0 / beta) - P_3 = 0.5 * (P_R + P_L) - f_L = 1.0 - while fabs(P_3 - P_new) > 1e-6: - P_3 = P_new - (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) - (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) - f = f_L + f_R + (v_R - v_L) - df = dfdp_L + dfdp_R - dp = -f / df - prnew = P_3 + dp - v_3 = v_L - f_L - return (P_new, v_3) - - -# Solve Riemann problem for post-shock region -(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) - -# Check direction of shocks and wave -shock_R = P_3 > P_R -shock_L = P_3 > P_L - -# Velocity of shock front and and rarefaction wave -if shock_R: - v_right = v_R + c_R ** 2 * (P_3 / P_R - 1.0) / (gas_gamma * (v_3 - v_R)) -else: - v_right = c_R + 0.5 * (gas_gamma + 1.0) * v_3 - 0.5 * (gas_gamma - 1.0) * v_R - -if shock_L: - v_left = v_L + c_L ** 2 * (P_3 / p_L - 1.0) / (gas_gamma * (v_3 - v_L)) -else: - v_left = c_L - 0.5 * (gas_gamma + 1.0) * v_3 + 0.5 * (gas_gamma - 1.0) * v_L - -# Compute position of the transitions -x_23 = -fabs(v_left) * time -if shock_L: - x_12 = -fabs(v_left) * time -else: - x_12 = -(c_L - v_L) * time +# Prepare reference solution +solver = RiemannSolver(gas_gamma) -x_34 = v_3 * time - -x_45 = fabs(v_right) * time -if shock_R: - x_56 = fabs(v_right) * time -else: - x_56 = (c_R + v_R) * time - - -# Prepare arrays delta_x = (x_max - x_min) / N -x_s = arange(x_min, x_max, delta_x) -rho_s = zeros(N) -P_s = zeros(N) -v_s = zeros(N) - -# Compute solution in the different regions -for i in range(N): - if x_s[i] <= x_12: - rho_s[i] = rho_L - P_s[i] = P_L - v_s[i] = v_L - if x_s[i] >= x_12 and x_s[i] < x_23: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1.0 + Gama * P_3 / P_L) - P_s[i] = P_3 - v_s[i] = v_3 - else: - rho_s[i] = rho_L * ( - Gama * (0.0 - x_s[i]) / (c_L * time) + Gama * v_L / c_L + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = P_L * (rho_s[i] / rho_L) ** gas_gamma - v_s[i] = (1.0 - Gama) * (c_L - (0.0 - x_s[i]) / time) + Gama * v_L - if x_s[i] >= x_23 and x_s[i] < x_34: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1 + Gama * P_3 / p_L) - else: - rho_s[i] = rho_L * (P_3 / P_L) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_34 and x_s[i] < x_45: - if shock_R: - rho_s[i] = rho_R * (Gama + P_3 / P_R) / (1.0 + Gama * P_3 / P_R) - else: - rho_s[i] = rho_R * (P_3 / P_R) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_45 and x_s[i] < x_56: - if shock_R: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - else: - rho_s[i] = rho_R * ( - Gama * (x_s[i]) / (c_R * time) - Gama * v_R / c_R + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = p_R * (rho_s[i] / rho_R) ** gas_gamma - v_s[i] = (1.0 - Gama) * (-c_R - (-x_s[i]) / time) + Gama * v_R - if x_s[i] >= x_56: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) # Additional arrays u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function - # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) # Velocity profile -------------------------------- subplot(231) @@ -372,33 +220,40 @@ else: # Information ------------------------------------- subplot(236, frameon=False) +text_fontsize = 5 + text( -0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time), - fontsize=10, + fontsize=text_fontsize, ) text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, ) text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), - fontsize=10, + fontsize=text_fontsize, ) plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) +text(-0.49, 0.5, "SWIFT %s" % git, fontsize=text_fontsize) +text(-0.49, 0.4, scheme, fontsize=text_fontsize) +text(-0.49, 0.3, kernel, fontsize=text_fontsize) +text( + -0.49, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) xlim(-0.5, 0.5) ylim(0, 1) xticks([]) yticks([]) - +tight_layout() savefig("SodShock.png", dpi=200) diff --git a/examples/HydroTests/SodShock_3D/run.sh b/examples/HydroTests/SodShock_3D/run.sh index aceeacd331e9d2e467e1cf42079dcb492ad0c631..04440bb7fa0c71cf23b5c8e27bb181467f421627 100755 --- a/examples/HydroTests/SodShock_3D/run.sh +++ b/examples/HydroTests/SodShock_3D/run.sh @@ -9,10 +9,10 @@ fi if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the Sod shock example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/SodShock_BCC_3D/analyticSolution.py b/examples/HydroTests/SodShock_BCC_3D/analyticSolution.py index 2cac6b0fabde207c325a1969434b6de9e90df9ca..547128fb2bdae3d4bde0979eeab9b88c75dae089 100644 --- a/examples/HydroTests/SodShock_BCC_3D/analyticSolution.py +++ b/examples/HydroTests/SodShock_BCC_3D/analyticSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2019 Josh Borrow (joshua.boorrow@durham.ac.uk) # # This program is free software: you can redistribute it and/or modify @@ -18,7 +18,12 @@ # ############################################################################## -from numpy import * +import numpy as np +import sys + +sys.path.append("../") + +from riemannSolver import RiemannSolver def analytic( @@ -34,146 +39,14 @@ def analytic( x_min=-1.0, x_max=1.0, ): - # Analytic solution - c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave - c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front - - # Helpful variable - Gama = (gas_gamma - 1.0) / (gas_gamma + 1.0) - beta = (gas_gamma - 1.0) / (2.0 * gas_gamma) - - # Characteristic function and its derivative, following Toro (2009) - def compute_f(P_3, P, c): - u = P_3 / P - if u > 1: - term1 = gas_gamma * ((gas_gamma + 1.0) * u + gas_gamma - 1.0) - term2 = sqrt(2.0 / term1) - fp = (u - 1.0) * c * term2 - dfdp = ( - c * term2 / P - + (u - 1.0) - * c - / term2 - * (-1.0 / term1 ** 2) - * gas_gamma - * (gas_gamma + 1.0) - / P - ) - else: - fp = (u ** beta - 1.0) * (2.0 * c / (gas_gamma - 1.0)) - dfdp = 2.0 * c / (gas_gamma - 1.0) * beta * u ** (beta - 1.0) / P - return (fp, dfdp) - - # Solution of the Riemann problem following Toro (2009) - def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): - P_new = ( - (c_L + c_R + (v_L - v_R) * 0.5 * (gas_gamma - 1.0)) - / (c_L / P_L ** beta + c_R / P_R ** beta) - ) ** (1.0 / beta) - P_3 = 0.5 * (P_R + P_L) - f_L = 1.0 - while fabs(P_3 - P_new) > 1e-6: - P_3 = P_new - (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) - (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) - f = f_L + f_R + (v_R - v_L) - df = dfdp_L + dfdp_R - dp = -f / df - prnew = P_3 + dp - v_3 = v_L - f_L - return (P_new, v_3) - - # Solve Riemann problem for post-shock region - (P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) - - # Check direction of shocks and wave - shock_R = P_3 > P_R - shock_L = P_3 > P_L - # Velocity of shock front and and rarefaction wave - if shock_R: - v_right = v_R + c_R ** 2 * (P_3 / P_R - 1.0) / (gas_gamma * (v_3 - v_R)) - else: - v_right = c_R + 0.5 * (gas_gamma + 1.0) * v_3 - 0.5 * (gas_gamma - 1.0) * v_R - - if shock_L: - v_left = v_L + c_L ** 2 * (P_3 / p_L - 1.0) / (gas_gamma * (v_3 - v_L)) - else: - v_left = c_L - 0.5 * (gas_gamma + 1.0) * v_3 + 0.5 * (gas_gamma - 1.0) * v_L - - # Compute position of the transitions - x_23 = -fabs(v_left) * time - if shock_L: - x_12 = -fabs(v_left) * time - else: - x_12 = -(c_L - v_L) * time - - x_34 = v_3 * time - - x_45 = fabs(v_right) * time - if shock_R: - x_56 = fabs(v_right) * time - else: - x_56 = (c_R + v_R) * time - - # Prepare arrays + solver = RiemannSolver(gas_gamma) delta_x = (x_max - x_min) / N - x_s = arange(x_min, x_max, delta_x) - rho_s = zeros(N) - P_s = zeros(N) - v_s = zeros(N) - - # Compute solution in the different regions - for i in range(N): - if x_s[i] <= x_12: - rho_s[i] = rho_L - P_s[i] = P_L - v_s[i] = v_L - if x_s[i] >= x_12 and x_s[i] < x_23: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1.0 + Gama * P_3 / P_L) - P_s[i] = P_3 - v_s[i] = v_3 - else: - rho_s[i] = rho_L * ( - Gama * (0.0 - x_s[i]) / (c_L * time) - + Gama * v_L / c_L - + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = P_L * (rho_s[i] / rho_L) ** gas_gamma - v_s[i] = (1.0 - Gama) * (c_L - (0.0 - x_s[i]) / time) + Gama * v_L - if x_s[i] >= x_23 and x_s[i] < x_34: - if shock_L: - rho_s[i] = rho_L * (Gama + P_3 / P_L) / (1 + Gama * P_3 / p_L) - else: - rho_s[i] = rho_L * (P_3 / P_L) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_34 and x_s[i] < x_45: - if shock_R: - rho_s[i] = rho_R * (Gama + P_3 / P_R) / (1.0 + Gama * P_3 / P_R) - else: - rho_s[i] = rho_R * (P_3 / P_R) ** (1.0 / gas_gamma) - P_s[i] = P_3 - v_s[i] = v_3 - if x_s[i] >= x_45 and x_s[i] < x_56: - if shock_R: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R - else: - rho_s[i] = rho_R * ( - Gama * (x_s[i]) / (c_R * time) - Gama * v_R / c_R + (1.0 - Gama) - ) ** (2.0 / (gas_gamma - 1.0)) - P_s[i] = p_R * (rho_s[i] / rho_R) ** gas_gamma - v_s[i] = (1.0 - Gama) * (-c_R - (-x_s[i]) / time) + Gama * v_R - if x_s[i] >= x_56: - rho_s[i] = rho_R - P_s[i] = P_R - v_s[i] = v_R + x_s = np.arange(0.5 * x_min, 0.5 * x_max, delta_x) + rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) # Additional arrays u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy s_s = P_s / rho_s ** gas_gamma # entropic function - return dict(x=x_s + 1, v=v_s, rho=rho_s, P=P_s, u=u_s, S=s_s) + return dict(x=x_s + 1.0, v=v_s, rho=rho_s, P=P_s, u=u_s, S=s_s) diff --git a/examples/HydroTests/SodShock_BCC_3D/makeIC.py b/examples/HydroTests/SodShock_BCC_3D/makeIC.py index ece334d9e454683ceb227c4a96d6269e241d8eae..d084a39cfda709aff776bffb84bfda84dcb0191e 100644 --- a/examples/HydroTests/SodShock_BCC_3D/makeIC.py +++ b/examples/HydroTests/SodShock_BCC_3D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SodShock_BCC_3D/plotSolution.py b/examples/HydroTests/SodShock_BCC_3D/plotSolution.py index 9660e12a3d226bd6b0e3c152031c93cedb345933..9af07acf8b1bc88df600cb6e39e0f45199ff0424 100644 --- a/examples/HydroTests/SodShock_BCC_3D/plotSolution.py +++ b/examples/HydroTests/SodShock_BCC_3D/plotSolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of the ANARCHY paper. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2019 Josh Borrow (joshua.boorrow@durham.ac.uk) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/HydroTests/SodShock_BCC_3D/run.sh b/examples/HydroTests/SodShock_BCC_3D/run.sh index 70cb3a97b1adf3944b9ed8965afabecfe280f7af..a845838b180420a59ef0dbaaa7b3a6503540bb22 100755 --- a/examples/HydroTests/SodShock_BCC_3D/run.sh +++ b/examples/HydroTests/SodShock_BCC_3D/run.sh @@ -4,12 +4,12 @@ if [ ! -e sodShock.hdf5 ] then echo "Generating initial conditions for the Sod shock example..." - python makeGlass.py -n 64 -o glassCube_64.hdf5 - python makeGlass.py -n 32 -o glassCube_32.hdf5 - python makeIC.py + python3 makeGlass.py -n 64 -o glassCube_64.hdf5 + python3 makeGlass.py -n 32 -o glassCube_32.hdf5 + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/SquareTest_2D/makeIC.py b/examples/HydroTests/SquareTest_2D/makeIC.py index 1a7403e707d70ac8f5708eb47aaebced534d4474..e0b2265bafb80b3c4767350987fe6e21298489c4 100644 --- a/examples/HydroTests/SquareTest_2D/makeIC.py +++ b/examples/HydroTests/SquareTest_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py b/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py index fa4be86595627c06d3439cb953f4ddff352c546d..a4a12afb9b572a6ec295b144eba82c6072a8a81c 100644 --- a/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py +++ b/examples/HydroTests/SquareTest_2D/makeICDifferentMasses.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) -# 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2016 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 diff --git a/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py b/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py deleted file mode 100644 index 48d22ebd471b5f733e7c5fa47c858f43090e30c2..0000000000000000000000000000000000000000 --- a/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py +++ /dev/null @@ -1,193 +0,0 @@ -############################################################################### -# 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/>. -# -############################################################################## - -# Computes the analytical solution of the square test - -# Parameters -gas_gamma = 5.0 / 3.0 # Gas adiabatic index -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 -vy = 0.0 - -# --------------------------------------------------------------- -# Don't touch anything after this. -# --------------------------------------------------------------- - -import matplotlib - -matplotlib.use("Agg") -from pylab import * -import h5py - -# 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.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - -snap = int(sys.argv[1]) - -# Read the simulation data -sim = h5py.File("square_%04d.hdf5" % snap, "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"] - -# Analytical soltion -centre_x = 0.5 + time * vx -centre_y = 0.5 + time * vy - -while centre_x > 1.0: - centre_x -= 1.0 -while centre_x < 0.0: - centre_x += 1.0 -while centre_y > 1.0: - centre_y -= 1.0 -while centre_y < 0.0: - centre_y += 1.0 - -pos = sim["/PartType0/Coordinates"][:, :] -vel = sim["/PartType0/Velocities"][:, :] -v_norm = sqrt(vel[:, 0] ** 2 + vel[:, 1] ** 2) -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 - -# Box wrapping -x[x > 0.5] -= 1.0 -x[x < -0.5] += 1.0 -y[y > 0.5] -= 1.0 -y[y < -0.5] += 1.0 - -# Azimuthal velocity profile ----------------------------- -subplot(231) -scatter(x, y, c=v_norm, cmap="PuBu", edgecolors="face", s=4, vmin=-1.0, vmax=1.0) -text(0.47, 0.47, "${\\rm{Velocity~norm}}$", ha="right", va="top", backgroundcolor="w") -plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2) -plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=-7) -xlim(-0.5, 0.5) -ylim(-0.5, 0.5) - -# Radial density profile -------------------------------- -subplot(232) -scatter(x, y, c=rho, cmap="PuBu", edgecolors="face", s=4, vmin=0.0, vmax=4.0) -text(0.47, 0.47, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w") -plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2) -plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=-7) -xlim(-0.5, 0.5) -ylim(-0.5, 0.5) - -# Radial pressure profile -------------------------------- -subplot(233) -scatter(x, y, c=P, cmap="PuBu", edgecolors="face", s=4, vmin=2, vmax=4) -text(0.47, 0.47, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w") -plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2) -plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=-7) -xlim(-0.5, 0.5) -ylim(-0.5, 0.5) - -# Internal energy profile -------------------------------- -subplot(234) -scatter(x, y, c=u, cmap="PuBu", edgecolors="face", s=4, vmin=0.5, vmax=4.0) -text(0.47, 0.47, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w") -plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2) -plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=-7) -xlim(-0.5, 0.5) -ylim(-0.5, 0.5) - -# Radial entropy profile -------------------------------- -subplot(235) -scatter(x, y, c=S, cmap="PuBu", edgecolors="face", s=4, vmin=0.0, vmax=3.0) -text(0.47, 0.47, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w") -plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2) -plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2) -xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Position}}~y$", labelpad=-7) -xlim(-0.5, 0.5) -ylim(-0.5, 0.5) - - -# Information ------------------------------------- -subplot(236, frameon=False) - -text( - -0.49, - 0.9, - "Square test with $\\gamma=%.3f$ at $t=%.2f$" % (gas_gamma, time), - fontsize=10, -) -text(-0.49, 0.8, "Centre:~~~ $(P, \\rho) = (%.3f, %.3f)$" % (P0, rho0), fontsize=10) -text(-0.49, 0.7, "Outskirts: $(P, \\rho) = (%.3f, %.3f)$" % (P1, rho1), fontsize=10) -plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10) -text(-0.49, 0.4, scheme, fontsize=10) -text(-0.49, 0.3, kernel, fontsize=10) -text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10) -xlim(-0.5, 0.5) -ylim(0, 1) -xticks([]) -yticks([]) - -savefig("SquareTest.png", dpi=200) diff --git a/examples/HydroTests/SquareTest_2D/run.sh b/examples/HydroTests/SquareTest_2D/run.sh index f5e0160627f7417af4f7bf38e50c03214e2f3c90..e6bd3191c51ab961816d9bbffdf4084a8e48cb7c 100755 --- a/examples/HydroTests/SquareTest_2D/run.sh +++ b/examples/HydroTests/SquareTest_2D/run.sh @@ -4,12 +4,12 @@ if [ ! -e square.hdf5 ] then echo "Generating initial conditions for the square test ..." - python makeICDifferentMasses.py + python3 makeICDifferentMasses.py fi # Run SWIFT -../../swift --hydro --threads=4 square.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 square.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 40 -python makeMovie.py +python3 plotSolution.py 40 +python3 makeMovie.py diff --git a/examples/HydroTests/ToroTest2_1D/makeIC.py b/examples/HydroTests/ToroTest2_1D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..193de73201042eb623c23f9bdb75d51d58e74a35 --- /dev/null +++ b/examples/HydroTests/ToroTest2_1D/makeIC.py @@ -0,0 +1,105 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 * + +# Generates a swift IC file for the 1D Toro (2009) test 2 in a periodic box +# Fun fact: because of the periodic boundaries, we get a second test for free + +# Parameters +gamma = 5.0 / 3.0 # Gas adiabatic index +numPart = 1000 # Number of particles +x_min = -1.0 +x_max = 1.0 +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state +fileName = "toroTest2.hdf5" + + +# --------------------------------------------------- + +boxSize = x_max - x_min +delta = boxSize / numPart + +# Build the arrays +coords = zeros((numPart, 3)) +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +h = zeros(numPart) +u = zeros(numPart) + +# Set the particles on the left +for i in range(numPart // 2): + coords[i, 0] = x_min + (i + 0.5) * delta + u[i] = P_L / (rho_L * (gamma - 1.0)) + h[i] = 1.2348 * delta + m[i] = delta * rho_L + v[i, 0] = v_L + +# Set the particles on the right +for j in range(numPart // 2): + i = numPart // 2 + j + coords[i, 0] = x_min + (i + 0.5) * delta + u[i] = P_R / (rho_R * (gamma - 1.0)) + h[i] = 1.2348 * delta + m[i] = delta * rho_R + v[i, 0] = v_R + +# Shift particles +coords[:, 0] -= x_min + +# File +file = h5py.File(fileName, "w") + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +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["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"] = 1 + +# Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1.0 +grp.attrs["Unit mass in cgs (U_M)"] = 1.0 +grp.attrs["Unit time in cgs (U_t)"] = 1.0 +grp.attrs["Unit current in cgs (U_I)"] = 1.0 +grp.attrs["Unit temperature in cgs (U_T)"] = 1.0 + +# Particle group +grp = file.create_group("/PartType0") +grp.create_dataset("Coordinates", data=coords, 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") + +file.close() diff --git a/examples/HydroTests/ToroTest2_1D/plotSolution.py b/examples/HydroTests/ToroTest2_1D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..066a9c51f0b36008834577c82a400d667d920a95 --- /dev/null +++ b/examples/HydroTests/ToroTest2_1D/plotSolution.py @@ -0,0 +1,184 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 Toro (2009) test 2 and plots the SPH answer + +# Generates the analytical solution for the Toro (2009) test case 2 +# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t. +# This follows the solution given in (Toro, 2009) + +# Parameters +gas_gamma = 5.0 / 3.0 # Polytropic index +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state + +import sys + +sys.path.append("../") +from riemannSolver import RiemannSolver + +import matplotlib + +matplotlib.use("Agg") +from pylab import * +import h5py + +style.use("../../../tools/stylesheets/mnras.mplstyle") + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("toroTest2_%04d.hdf5" % snap, "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"] + +x = sim["/PartType0/Coordinates"][:, 0] +v = sim["/PartType0/Velocities"][:, 0] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] + +N = 1000 # Number of points +x_min = -1.0 +x_max = 1.0 + +x += x_min + +# Prepare reference solution +solver = RiemannSolver(gas_gamma) + +delta_x = (x_max - x_min) / N +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) +rho_s2, v_s2, P_s2, _ = solver.solve(rho_R, v_R, P_R, rho_L, v_L, P_L, x_s / time) +x_s2 = np.array(x_s) +x_s2 += 1.0 +s2neg = x_s2 > 1.0 +s2pos = ~s2neg +x_s2[s2neg] -= 2.0 + +# Additional arrays +u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy +s_s = P_s / rho_s ** gas_gamma # entropic function +u_s2 = P_s2 / (rho_s2 * (gas_gamma - 1.0)) # internal energy +s_s2 = P_s2 / rho_s2 ** gas_gamma # entropic function + + +# Plot the interesting quantities +figure(figsize=(7, 7 / 1.6)) + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, ".", color="r", ms=4.0) +plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], v_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], v_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, ".", color="r", ms=4.0) +plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], rho_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], rho_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, ".", color="r", ms=4.0) +plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], P_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], P_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, ".", color="r", ms=4.0) +plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], u_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], u_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) + +# Entropy profile --------------------------------- +subplot(235) + +plot(x, S, ".", color="r", ms=4.0) +plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], s_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], s_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) + + +# Information ------------------------------------- +subplot(236, frameon=False) + +text_fontsize = 5 + +text( + -0.49, + 0.9, + "Toro (2009) test 2 with $\\gamma=%.3f$ in 1D at $t=%.2f$" % (gas_gamma, time), + fontsize=text_fontsize, +) +text( + -0.49, + 0.8, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, +) +text( + -0.49, + 0.7, + "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), + fontsize=text_fontsize, +) +plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) +text(-0.49, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +text( + -0.49, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() + +savefig("ToroTest2.png", dpi=200) diff --git a/examples/HydroTests/ToroTest2_1D/run.sh b/examples/HydroTests/ToroTest2_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..022c8548389e42b4f27615fd3e7be5d62fb64e40 --- /dev/null +++ b/examples/HydroTests/ToroTest2_1D/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e toroTest2.hdf5 ] +then + echo "Generating initial conditions for the 1D Toro (2009) test 2 example..." + python3 makeIC.py +fi + +# Run SWIFT +../../../swift --hydro --threads=1 toroTest2.yml 2>&1 | tee output.log + +# Plot the result +python3 plotSolution.py 1 diff --git a/examples/HydroTests/ToroTest2_1D/toroTest2.yml b/examples/HydroTests/ToroTest2_1D/toroTest2.yml new file mode 100644 index 0000000000000000000000000000000000000000..be03d2ec82e2dcadc841eccbbaf19a1f56a8e329 --- /dev/null +++ b/examples/HydroTests/ToroTest2_1D/toroTest2.yml @@ -0,0 +1,34 @@ +# 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: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-7 # 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: toroTest2 # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.1 # 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: ./toroTest2.hdf5 # The file to read + periodic: 1 diff --git a/examples/HydroTests/ToroTest2_2D/getGlass.sh b/examples/HydroTests/ToroTest2_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ae3c977064f5e7a408aa249c5fd9089b3c52ecb1 --- /dev/null +++ b/examples/HydroTests/ToroTest2_2D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 diff --git a/examples/HydroTests/ToroTest2_2D/makeIC.py b/examples/HydroTests/ToroTest2_2D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..8799ab41fd135e4cd0d7ece14e94fbceeab18316 --- /dev/null +++ b/examples/HydroTests/ToroTest2_2D/makeIC.py @@ -0,0 +1,117 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 * + +# Generates a swift IC file for the 2D Toro (2009) test 2 in a periodic box + +# Parameters +gamma = 5.0 / 3.0 # Gas adiabatic index +x_min = -1.0 +x_max = 1.0 +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state +fileName = "toroTest2.hdf5" + + +# --------------------------------------------------- +boxSize = x_max - x_min + +glass = h5py.File("glassPlane_128.hdf5", "r") + +pos_L = glass["/PartType0/Coordinates"][:, :] * 0.5 +pos_R = glass["/PartType0/Coordinates"][:, :] * 0.5 +h_L = glass["/PartType0/SmoothingLength"][:] * 0.5 +h_R = glass["/PartType0/SmoothingLength"][:] * 0.5 + +# Merge things +aa = pos_L - array([0.5, 0.0, 0.0]) +pos_LL = append(pos_L, pos_L + array([0.5, 0.0, 0.0]), axis=0) +pos_RR = append(pos_R, pos_R + array([0.5, 0.0, 0.0]), axis=0) +pos = append(pos_LL - array([1.0, 0.0, 0.0]), pos_RR, axis=0) +h_LL = append(h_L, h_L) +h_RR = append(h_R, h_R) +h = append(h_LL, h_RR) + +numPart_L = size(h_LL) +numPart_R = size(h_RR) +numPart = size(h) + +vol_L = 0.5 +vol_R = 0.5 + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) + +for i in range(numPart): + x = pos[i, 0] + + if x < 0: # left + u[i] = P_L / (rho_L * (gamma - 1.0)) + m[i] = rho_L * vol_L / numPart_L + v[i, 0] = v_L + else: # right + u[i] = P_R / (rho_R * (gamma - 1.0)) + m[i] = rho_R * vol_R / numPart_R + v[i, 0] = v_R + +# Shift particles +pos[:, 0] -= x_min + +# File +file = h5py.File(fileName, "w") + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [boxSize, 0.5, 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, 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"] = 2 + +# Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1.0 +grp.attrs["Unit mass in cgs (U_M)"] = 1.0 +grp.attrs["Unit time in cgs (U_t)"] = 1.0 +grp.attrs["Unit current in cgs (U_I)"] = 1.0 +grp.attrs["Unit temperature in cgs (U_T)"] = 1.0 + +# 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") + +file.close() diff --git a/examples/HydroTests/ToroTest2_2D/plotSolution.py b/examples/HydroTests/ToroTest2_2D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..e070f2cfc7dcd293faaa507ca75736749d822671 --- /dev/null +++ b/examples/HydroTests/ToroTest2_2D/plotSolution.py @@ -0,0 +1,205 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 Toro (2009) test 2 and plots the SPH answer + +# Generates the analytical solution for the Toro (2009) test case 2 +# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t. +# This follows the solution given in (Toro, 2009) + +# Parameters +gas_gamma = 5.0 / 3.0 # Polytropic index +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state + +import sys + +sys.path.append("../") +from riemannSolver import RiemannSolver + +import matplotlib + +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +style.use("../../../tools/stylesheets/mnras.mplstyle") + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("toroTest2_%04d.hdf5" % snap, "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"] + +x = sim["/PartType0/Coordinates"][:, 0] +v = sim["/PartType0/Velocities"][:, 0] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] + +N = 1000 # Number of points +x_min = -1.0 +x_max = 1.0 +x += x_min + + +# Bin the data +x_bin_edge = np.arange(x_min, x_max, 0.02) +x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) +rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge) +v_bin, _, _ = stats.binned_statistic(x, v, statistic="mean", bins=x_bin_edge) +P_bin, _, _ = stats.binned_statistic(x, P, statistic="mean", bins=x_bin_edge) +S_bin, _, _ = stats.binned_statistic(x, S, statistic="mean", bins=x_bin_edge) +u_bin, _, _ = stats.binned_statistic(x, u, statistic="mean", bins=x_bin_edge) +rho2_bin, _, _ = stats.binned_statistic(x, rho ** 2, statistic="mean", bins=x_bin_edge) +v2_bin, _, _ = stats.binned_statistic(x, v ** 2, statistic="mean", bins=x_bin_edge) +P2_bin, _, _ = stats.binned_statistic(x, P ** 2, statistic="mean", bins=x_bin_edge) +S2_bin, _, _ = stats.binned_statistic(x, S ** 2, statistic="mean", bins=x_bin_edge) +u2_bin, _, _ = stats.binned_statistic(x, u ** 2, statistic="mean", bins=x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin ** 2) +v_sigma_bin = np.sqrt(v2_bin - v_bin ** 2) +P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2) +S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) +u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2) + +# Prepare reference solution +solver = RiemannSolver(gas_gamma) + +delta_x = (x_max - x_min) / N +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) +rho_s2, v_s2, P_s2, _ = solver.solve(rho_R, v_R, P_R, rho_L, v_L, P_L, x_s / time) +x_s2 = np.array(x_s) +x_s2 += 1.0 +s2neg = x_s2 > 1.0 +s2pos = ~s2neg +x_s2[s2neg] -= 2.0 + +# Additional arrays +u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy +s_s = P_s / rho_s ** gas_gamma # entropic function +u_s2 = P_s2 / (rho_s2 * (gas_gamma - 1.0)) # internal energy +s_s2 = P_s2 / rho_s2 ** gas_gamma # entropic function + +# Plot the interesting quantities +figure(figsize=(7, 7 / 1.6)) + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, ".", color="r", ms=0.2) +plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], v_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], v_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, ".", color="r", ms=0.2) +plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], rho_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], rho_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, ".", color="r", ms=0.2) +plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], P_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], P_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, ".", color="r", ms=0.2) +plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], u_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], u_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) + +# Entropy profile --------------------------------- +subplot(235) +plot(x, S, ".", color="r", ms=0.2) +plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], s_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], s_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) + +# Information ------------------------------------- +subplot(236, frameon=False) + +text_fontsize = 5 + +text( + -0.49, + 0.9, + "Toro (2009) test 2 with $\\gamma=%.3f$ in 2D at $t=%.2f$" % (gas_gamma, time), + fontsize=text_fontsize, +) +text( + -0.49, + 0.8, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, +) +text( + -0.49, + 0.7, + "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), + fontsize=text_fontsize, +) +plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) +text(-0.49, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +text( + -0.49, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() +savefig("ToroTest2.png", dpi=200) diff --git a/examples/HydroTests/ToroTest2_2D/run.sh b/examples/HydroTests/ToroTest2_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..f99b85432ec43e1497c7316dc526c832a7ea2c55 --- /dev/null +++ b/examples/HydroTests/ToroTest2_2D/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassPlane_128.hdf5 ] +then + echo "Fetching initial glass file for the Toro (2009) test 2 example..." + ./getGlass.sh +fi +if [ ! -e toroTest2.hdf5 ] +then + echo "Generating initial conditions for the Toro (2009) test 2 example..." + python3 makeIC.py +fi + +# Run SWIFT +../../../swift --hydro --threads=1 toroTest2.yml 2>&1 | tee output.log + +python3 plotSolution.py 1 diff --git a/examples/HydroTests/ToroTest2_2D/toroTest2.yml b/examples/HydroTests/ToroTest2_2D/toroTest2.yml new file mode 100644 index 0000000000000000000000000000000000000000..be03d2ec82e2dcadc841eccbbaf19a1f56a8e329 --- /dev/null +++ b/examples/HydroTests/ToroTest2_2D/toroTest2.yml @@ -0,0 +1,34 @@ +# 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: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-7 # 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: toroTest2 # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.1 # 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: ./toroTest2.hdf5 # The file to read + periodic: 1 diff --git a/examples/HydroTests/ToroTest2_3D/getGlass.sh b/examples/HydroTests/ToroTest2_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c --- /dev/null +++ b/examples/HydroTests/ToroTest2_3D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/HydroTests/ToroTest2_3D/makeIC.py b/examples/HydroTests/ToroTest2_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..45d0a78df5f176a14e5d9fcf122938d02f679ff4 --- /dev/null +++ b/examples/HydroTests/ToroTest2_3D/makeIC.py @@ -0,0 +1,117 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 * + +# Generates a swift IC file for the 3D Toro (2009) test 2 in a periodic box + +# Parameters +gamma = 5.0 / 3.0 # Gas adiabatic index +x_min = -1.0 +x_max = 1.0 +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state +fileName = "toroTest2.hdf5" + + +# --------------------------------------------------- +boxSize = x_max - x_min + +glass = h5py.File("glassCube_64.hdf5", "r") + +pos_L = glass["/PartType0/Coordinates"][:, :] * 0.5 +pos_R = glass["/PartType0/Coordinates"][:, :] * 0.5 +h_L = glass["/PartType0/SmoothingLength"][:] * 0.5 +h_R = glass["/PartType0/SmoothingLength"][:] * 0.5 + +# Merge things +aa = pos_L - array([0.5, 0.0, 0.0]) +pos_LL = append(pos_L, pos_L + array([0.5, 0.0, 0.0]), axis=0) +pos_RR = append(pos_R, pos_R + array([0.5, 0.0, 0.0]), axis=0) +pos = append(pos_LL - array([1.0, 0.0, 0.0]), pos_RR, axis=0) +h_LL = append(h_L, h_L) +h_RR = append(h_R, h_R) +h = append(h_LL, h_RR) + +numPart_L = size(h_LL) +numPart_R = size(h_RR) +numPart = size(h) + +vol_L = 0.25 +vol_R = 0.25 + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) + +for i in range(numPart): + x = pos[i, 0] + + if x < 0: # left + u[i] = P_L / (rho_L * (gamma - 1.0)) + m[i] = rho_L * vol_L / numPart_L + v[i, 0] = v_L + else: # right + u[i] = P_R / (rho_R * (gamma - 1.0)) + m[i] = rho_R * vol_R / numPart_R + v[i, 0] = v_R + +# Shift particles +pos[:, 0] -= x_min + +# File +file = h5py.File(fileName, "w") + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [boxSize, 0.5, 0.5] +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["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 + +# Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1.0 +grp.attrs["Unit mass in cgs (U_M)"] = 1.0 +grp.attrs["Unit time in cgs (U_t)"] = 1.0 +grp.attrs["Unit current in cgs (U_I)"] = 1.0 +grp.attrs["Unit temperature in cgs (U_T)"] = 1.0 + +# 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") + +file.close() diff --git a/examples/HydroTests/ToroTest2_3D/plotSolution.py b/examples/HydroTests/ToroTest2_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..c638709199d12b87346e0ca7aece4ea7fcb923af --- /dev/null +++ b/examples/HydroTests/ToroTest2_3D/plotSolution.py @@ -0,0 +1,205 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 Toro (2009) test 2 and plots the SPH answer + + +# Generates the analytical solution for the Toro (2009) test case 2 +# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t. +# This follows the solution given in (Toro, 2009) + +# Parameters +gas_gamma = 5.0 / 3.0 # Polytropic index +rho_L = 1.0 # Density left state +rho_R = 1.0 # Density right state +v_L = -2.0 # Velocity left state +v_R = 2.0 # Velocity right state +P_L = 0.4 # Pressure left state +P_R = 0.4 # Pressure right state + +import sys + +sys.path.append("../") +from riemannSolver import RiemannSolver + +import matplotlib + +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +style.use("../../../tools/stylesheets/mnras.mplstyle") + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("toroTest2_%04d.hdf5" % snap, "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"] + +x = sim["/PartType0/Coordinates"][:, 0] +v = sim["/PartType0/Velocities"][:, 0] +u = sim["/PartType0/InternalEnergies"][:] +S = sim["/PartType0/Entropies"][:] +P = sim["/PartType0/Pressures"][:] +rho = sim["/PartType0/Densities"][:] + +x_min = -1.0 +x_max = 1.0 +x += x_min +N = 1000 + +# Bin the data +x_bin_edge = np.arange(x_min, x_max, 0.02) +x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) +rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge) +v_bin, _, _ = stats.binned_statistic(x, v, statistic="mean", bins=x_bin_edge) +P_bin, _, _ = stats.binned_statistic(x, P, statistic="mean", bins=x_bin_edge) +S_bin, _, _ = stats.binned_statistic(x, S, statistic="mean", bins=x_bin_edge) +u_bin, _, _ = stats.binned_statistic(x, u, statistic="mean", bins=x_bin_edge) +rho2_bin, _, _ = stats.binned_statistic(x, rho ** 2, statistic="mean", bins=x_bin_edge) +v2_bin, _, _ = stats.binned_statistic(x, v ** 2, statistic="mean", bins=x_bin_edge) +P2_bin, _, _ = stats.binned_statistic(x, P ** 2, statistic="mean", bins=x_bin_edge) +S2_bin, _, _ = stats.binned_statistic(x, S ** 2, statistic="mean", bins=x_bin_edge) +u2_bin, _, _ = stats.binned_statistic(x, u ** 2, statistic="mean", bins=x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin ** 2) +v_sigma_bin = np.sqrt(v2_bin - v_bin ** 2) +P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2) +S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) +u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2) + +# Prepare reference solution +solver = RiemannSolver(gas_gamma) + +delta_x = (x_max - x_min) / N +x_s = arange(0.5 * x_min, 0.5 * x_max, delta_x) +rho_s, v_s, P_s, _ = solver.solve(rho_L, v_L, P_L, rho_R, v_R, P_R, x_s / time) +rho_s2, v_s2, P_s2, _ = solver.solve(rho_R, v_R, P_R, rho_L, v_L, P_L, x_s / time) +x_s2 = np.array(x_s) +x_s2 += 1.0 +s2neg = x_s2 > 1.0 +s2pos = ~s2neg +x_s2[s2neg] -= 2.0 + +# Additional arrays +u_s = P_s / (rho_s * (gas_gamma - 1.0)) # internal energy +s_s = P_s / rho_s ** gas_gamma # entropic function +u_s2 = P_s2 / (rho_s2 * (gas_gamma - 1.0)) # internal energy +s_s2 = P_s2 / rho_s2 ** gas_gamma # entropic function + +# Plot the interesting quantities +figure(figsize=(7, 7 / 1.6)) + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, ".", color="r", ms=0.5, alpha=0.2) +plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], v_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], v_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, ".", color="r", ms=0.5, alpha=0.2) +plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], rho_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], rho_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, ".", color="r", ms=0.5, alpha=0.2) +plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], P_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], P_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, ".", color="r", ms=0.5, alpha=0.2) +plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], u_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], u_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) + +# Entropy profile --------------------------------- +subplot(235) +plot(x, S, ".", color="r", ms=0.5, alpha=0.2) +plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2pos], s_s2[s2pos], "--", color="k", alpha=0.8, lw=1.2) +plot(x_s2[s2neg], s_s2[s2neg], "--", color="k", alpha=0.8, lw=1.2) +errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) + +# Information ------------------------------------- +subplot(236, frameon=False) + +text_fontsize = 5 + +text( + -0.49, + 0.9, + "Toro (2009) test 2 with $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time), + fontsize=text_fontsize, +) +text( + -0.49, + 0.8, + "Left: $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L), + fontsize=text_fontsize, +) +text( + -0.49, + 0.7, + "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R), + fontsize=text_fontsize, +) +plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) +text(-0.49, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +text(-0.49, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +text( + -0.49, + 0.2, + "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=text_fontsize, +) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() +savefig("ToroTest2.png", dpi=200) diff --git a/examples/HydroTests/ToroTest2_3D/run.sh b/examples/HydroTests/ToroTest2_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..14fb22d2e0f2ad4a93d965fe775c2dbf39647a60 --- /dev/null +++ b/examples/HydroTests/ToroTest2_3D/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for the Toro (2009) test 2 example..." + ./getGlass.sh +fi +if [ ! -e toroTest2.hdf5 ] +then + echo "Generating initial conditions for the Toro (2009) test 2 example..." + python3 makeIC.py +fi + +# Run SWIFT +../../../swift --hydro --threads=4 toroTest2.yml 2>&1 | tee output.log + +python3 plotSolution.py 1 diff --git a/examples/HydroTests/ToroTest2_3D/toroTest2.yml b/examples/HydroTests/ToroTest2_3D/toroTest2.yml new file mode 100644 index 0000000000000000000000000000000000000000..be03d2ec82e2dcadc841eccbbaf19a1f56a8e329 --- /dev/null +++ b/examples/HydroTests/ToroTest2_3D/toroTest2.yml @@ -0,0 +1,34 @@ +# 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: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-7 # 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: toroTest2 # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.1 # 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: ./toroTest2.hdf5 # The file to read + periodic: 1 diff --git a/examples/HydroTests/UniformBox_2D/makeIC.py b/examples/HydroTests/UniformBox_2D/makeIC.py index 85bbcc80c077099dd8166158bf7a982ffd1d7628..7011c638886682f8b7903a94747c35295bf7dc3c 100644 --- a/examples/HydroTests/UniformBox_2D/makeIC.py +++ b/examples/HydroTests/UniformBox_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/UniformBox_2D/run.sh b/examples/HydroTests/UniformBox_2D/run.sh index aad344187dfac468c58baa7b32e45024c8ef49b1..83afecc5e9ec7b017f21b85446d056f911c66b80 100755 --- a/examples/HydroTests/UniformBox_2D/run.sh +++ b/examples/HydroTests/UniformBox_2D/run.sh @@ -4,7 +4,7 @@ if [ ! -e uniformPlane.hdf5 ] then echo "Generating initial conditions for the uniform box example..." - python makeIC.py 100 + python3 makeIC.py 100 fi -../../swift --hydro --threads=16 uniformPlane.yml 2>&1 | tee output.log +../../../swift --hydro --threads=16 uniformPlane.yml 2>&1 | tee output.log diff --git a/examples/HydroTests/UniformBox_3D/makeIC.py b/examples/HydroTests/UniformBox_3D/makeIC.py index e23371c8f108f6cdd1af0a79aec8605c628df229..0b2795120422c1179d6ed6a14cfbb7d015845bee 100644 --- a/examples/HydroTests/UniformBox_3D/makeIC.py +++ b/examples/HydroTests/UniformBox_3D/makeIC.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 diff --git a/examples/HydroTests/UniformBox_3D/makeICbig.py b/examples/HydroTests/UniformBox_3D/makeICbig.py index f13ab9698464c3e947d883c692177585132eb2d3..263323b538179146189a1e137125e93ee831212a 100644 --- a/examples/HydroTests/UniformBox_3D/makeICbig.py +++ b/examples/HydroTests/UniformBox_3D/makeICbig.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/HydroTests/UniformBox_3D/run.sh b/examples/HydroTests/UniformBox_3D/run.sh index 86e7ad3fb4cdce8566baec1c659e2dd1f4dd3a3f..45bb31743aeebbc37271643182e04597488cf6bb 100755 --- a/examples/HydroTests/UniformBox_3D/run.sh +++ b/examples/HydroTests/UniformBox_3D/run.sh @@ -7,4 +7,4 @@ then python3 makeIC.py 100 fi -../../swift --hydro --threads=16 uniformBox.yml 2>&1 | tee output.log +../../../swift --hydro --threads=16 uniformBox.yml 2>&1 | tee output.log diff --git a/examples/HydroTests/VacuumSpherical_2D/plotSolution.py b/examples/HydroTests/VacuumSpherical_2D/plotSolution.py index 3f2678b4266d95ad1bdf97b2d3086113046b15f9..ef6f398ec7105bc946cb375167c93b8cc067c1bc 100644 --- a/examples/HydroTests/VacuumSpherical_2D/plotSolution.py +++ b/examples/HydroTests/VacuumSpherical_2D/plotSolution.py @@ -17,14 +17,14 @@ # ############################################################################## -import numpy as np import matplotlib matplotlib.use("Agg") -import pylab as pl +import matplotlib.pyplot as plt +import numpy as np +from scipy import stats import h5py import sys -import scipy.stats as stats # Parameters gamma = 5.0 / 3.0 # Polytropic index @@ -35,30 +35,8 @@ rhoR = 0.0 # Initial vacuum density vR = 0.0 # Initial vacuum velocity PR = 0.0 # Initial vacuum pressure -# 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.0, - "text.latex.unicode": True, -} -pl.rcParams.update(params) -pl.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - -# Read the snapshot index from the command line argument +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") + snap = int(sys.argv[1]) # Open the file and read the relevant data @@ -106,104 +84,115 @@ x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) ref = np.loadtxt("vacuumSpherical2D_exact.txt") # Plot the interesting quantities -fig, ax = pl.subplots(2, 3) +plt.figure(figsize=(7, 7 / 1.6)) + +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=1, + markeredgecolor="none", + alpha=0.5, + zorder=-1, + rasterized=True, + linestyle="none", +) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) # Velocity profile -ax[0][0].plot(x, v, "r.", markersize=0.2) -ax[0][0].plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, linewidth=1.2) -ax[0][0].errorbar( - x_bin, v_bin, yerr=v_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 -) -ax[0][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[0][0].set_ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) -ax[0][0].set_xlim(0.0, 0.4) -ax[0][0].set_ylim(-0.1, 3.2) +plt.subplot(231) +plt.plot(x, v, **scatter_props) +plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) +plt.xlim(0.0, 0.4) +plt.ylim(-0.1, 3.2) # Density profile -ax[0][1].plot(x, rho, "r.", markersize=0.2) -ax[0][1].plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, linewidth=1.2) -ax[0][1].errorbar( - x_bin, - rho_bin, - yerr=rho_sigma_bin, - fmt=".", - markersize=8.0, - color="b", - linewidth=1.2, -) -ax[0][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad=0) -ax[0][1].set_xlim(0.0, 0.4) +plt.subplot(232) +plt.plot(x, rho, **scatter_props) +plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.xlim(0.0, 0.4) # Pressure profile -ax[0][2].plot(x, P, "r.", markersize=0.2) -ax[0][2].plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, linewidth=1.2) -ax[0][2].errorbar( - x_bin, P_bin, yerr=P_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 -) -ax[0][2].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad=0) -ax[0][2].set_xlim(0.0, 0.4) +plt.subplot(233) +plt.plot(x, P, **scatter_props) +plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.xlim(0.0, 0.4) # Internal energy profile -ax[1][0].plot(x, u, "r.", markersize=0.2) -ax[1][0].plot( - ref[:, 0], ref[:, 3] / ref[:, 1] / (gamma - 1.0), "k--", alpha=0.8, linewidth=1.2 -) -ax[1][0].errorbar( - x_bin, u_bin, yerr=u_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 +plt.subplot(234) +plt.plot(x, u, **scatter_props) +plt.plot( + ref[:, 0], + ref[:, 3] / ref[:, 1] / (gamma - 1.0), + color=line_color, + alpha=0.8, + lw=1.2, ) -ax[1][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) -ax[1][0].set_xlim(0.0, 0.4) +plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.xlim(0.0, 0.4) # Entropy profile -ax[1][1].plot(x, S, "r.", markersize=0.2) -ax[1][1].plot( - ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "k--", alpha=0.8, linewidth=1.2 -) -ax[1][1].errorbar( - x_bin, S_bin, yerr=S_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 -) -ax[1][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0) -ax[1][1].set_xlim(0.0, 0.4) -ax[1][1].set_ylim(0.0, 4.0) +plt.subplot(235) +plt.plot(x, S, **scatter_props) +plt.plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0) +plt.xlim(0.0, 0.4) +plt.ylim(0.0, 4.0) # Run information -ax[1][2].set_frame_on(False) -ax[1][2].text( - -0.49, +plt.subplot(236, frameon=False) + +text_fontsize = 5 + +plt.text( + -0.45, 0.9, - "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(gamma, time), - fontsize=10, + "Vacuum test with $\\gamma={0:.3f}$ in 2D at $t = {1:.2f}$".format(gamma, time), + fontsize=text_fontsize, ) -ax[1][2].text( - -0.49, +plt.text( + -0.45, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), + fontsize=text_fontsize, ) -ax[1][2].text( - -0.49, +plt.text( + -0.45, 0.7, "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PR, rhoR, vR), - fontsize=10, + fontsize=text_fontsize, ) -ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10) -ax[1][2].text(-0.49, 0.4, scheme, fontsize=10) -ax[1][2].text(-0.49, 0.3, kernel, fontsize=10) -ax[1][2].text( - -0.49, +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ {0}".format(git.decode("utf-8")), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, 0.2, "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), - fontsize=10, + fontsize=text_fontsize, ) -ax[1][2].set_xlim(-0.5, 0.5) -ax[1][2].set_ylim(0.0, 1.0) -ax[1][2].set_xticks([]) -ax[1][2].set_yticks([]) +plt.xlim(-0.5, 0.5) +plt.ylim(0.0, 1.0) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() -pl.tight_layout() -pl.savefig("Vacuum.png", dpi=200) +plt.savefig("Vacuum.png", dpi=200) diff --git a/examples/HydroTests/VacuumSpherical_2D/run.sh b/examples/HydroTests/VacuumSpherical_2D/run.sh index 54c5efdf9623cd11e504753514052f05ad1b36eb..981a1d70e767a1784d53044642b7d19fb116c5a4 100755 --- a/examples/HydroTests/VacuumSpherical_2D/run.sh +++ b/examples/HydroTests/VacuumSpherical_2D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e vacuum.hdf5 ] then echo "Generating initial conditions for the 2D vacuum expansion example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=4 vacuum.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 vacuum.yml 2>&1 | tee output.log # Get the 1D high resolution reference result if not present. if [ ! -e vacuumSpherical2D_exact.txt ] @@ -23,4 +23,4 @@ then fi # Plot the result -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/VacuumSpherical_3D/getGlass.sh b/examples/HydroTests/VacuumSpherical_3D/getGlass.sh old mode 100644 new mode 100755 diff --git a/examples/HydroTests/VacuumSpherical_3D/plotSolution.py b/examples/HydroTests/VacuumSpherical_3D/plotSolution.py index db4d2b5384d6564bfad590dd314a43c3202decf5..cb5ab373718cfcd4c3c99717a59d48f8a23501f0 100644 --- a/examples/HydroTests/VacuumSpherical_3D/plotSolution.py +++ b/examples/HydroTests/VacuumSpherical_3D/plotSolution.py @@ -17,14 +17,14 @@ # ############################################################################## -import numpy as np import matplotlib matplotlib.use("Agg") -import pylab as pl +import matplotlib.pyplot as plt +import numpy as np +from scipy import stats import h5py import sys -import scipy.stats as stats # Parameters gamma = 5.0 / 3.0 # Polytropic index @@ -35,30 +35,8 @@ rhoR = 0.0 # Initial vacuum density vR = 0.0 # Initial vacuum velocity PR = 0.0 # Initial vacuum pressure -# 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.0, - "text.latex.unicode": True, -} -pl.rcParams.update(params) -pl.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - -# Read the snapshot index from the command line argument +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") + snap = int(sys.argv[1]) # Open the file and read the relevant data @@ -107,105 +85,115 @@ x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) ref = np.loadtxt("vacuumSpherical3D_exact.txt") -# Plot the interesting quantities -fig, ax = pl.subplots(2, 3) +plt.figure(figsize=(7, 7 / 1.6)) -# Velocity profile -ax[0][0].plot(x, v, "r.", markersize=0.2) -ax[0][0].plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, linewidth=1.2) -ax[0][0].errorbar( - x_bin, v_bin, yerr=v_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 +line_color = "C4" +binned_color = "C2" +binned_marker_size = 4 + +scatter_props = dict( + marker=".", + ms=1, + markeredgecolor="none", + alpha=0.5, + zorder=-1, + rasterized=True, + linestyle="none", ) -ax[0][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[0][0].set_ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) -ax[0][0].set_xlim(0.0, 0.4) -ax[0][0].set_ylim(-0.1, 3.2) + +errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2) + +# Velocity profile +plt.subplot(231) +plt.plot(x, v, **scatter_props) +plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) +plt.xlim(0.0, 0.4) +plt.ylim(-0.1, 3.2) # Density profile -ax[0][1].plot(x, rho, "r.", markersize=0.2) -ax[0][1].plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, linewidth=1.2) -ax[0][1].errorbar( - x_bin, - rho_bin, - yerr=rho_sigma_bin, - fmt=".", - markersize=8.0, - color="b", - linewidth=1.2, -) -ax[0][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad=0) -ax[0][1].set_xlim(0.0, 0.4) +plt.subplot(232) +plt.plot(x, rho, **scatter_props) +plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +plt.xlim(0.0, 0.4) # Pressure profile -ax[0][2].plot(x, P, "r.", markersize=0.2) -ax[0][2].plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, linewidth=1.2) -ax[0][2].errorbar( - x_bin, P_bin, yerr=P_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 -) -ax[0][2].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad=0) -ax[0][2].set_xlim(0.0, 0.4) +plt.subplot(233) +plt.plot(x, P, **scatter_props) +plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0) +plt.xlim(0.0, 0.4) # Internal energy profile -ax[1][0].plot(x, u, "r.", markersize=0.2) -ax[1][0].plot( - ref[:, 0], ref[:, 3] / ref[:, 1] / (gamma - 1.0), "k--", alpha=0.8, linewidth=1.2 +plt.subplot(234) +plt.plot(x, u, **scatter_props) +plt.plot( + ref[:, 0], + ref[:, 3] / ref[:, 1] / (gamma - 1.0), + color=line_color, + alpha=0.8, + lw=1.2, ) -ax[1][0].errorbar( - x_bin, u_bin, yerr=u_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 -) -ax[1][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) -ax[1][0].set_xlim(0.0, 0.4) +plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +plt.xlim(0.0, 0.4) # Entropy profile -ax[1][1].plot(x, S, "r.", markersize=0.2) -ax[1][1].plot( - ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "k--", alpha=0.8, linewidth=1.2 -) -ax[1][1].errorbar( - x_bin, S_bin, yerr=S_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2 -) -ax[1][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0) -ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0) -ax[1][1].set_xlim(0.0, 0.4) -ax[1][1].set_ylim(0.0, 4.0) +plt.subplot(235) +plt.plot(x, S, **scatter_props) +plt.plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, color=line_color, alpha=0.8, lw=1.2) +plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props) +plt.xlabel("${\\rm{Radius}}~r$", labelpad=0) +plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0) +plt.xlim(0.0, 0.4) +plt.ylim(0.0, 4.0) # Run information -ax[1][2].set_frame_on(False) -ax[1][2].text( - -0.49, +plt.subplot(236, frameon=False) + +text_fontsize = 5 + +plt.text( + -0.45, 0.9, - "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(gamma, time), - fontsize=10, + "Vacuum test with $\\gamma={0:.3f}$ in 3D at $t = {1:.2f}$".format(gamma, time), + fontsize=text_fontsize, ) -ax[1][2].text( - -0.49, +plt.text( + -0.45, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), + fontsize=text_fontsize, ) -ax[1][2].text( - -0.49, +plt.text( + -0.45, 0.7, "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PR, rhoR, vR), - fontsize=10, + fontsize=text_fontsize, ) -ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10) -ax[1][2].text(-0.49, 0.4, scheme, fontsize=10) -ax[1][2].text(-0.49, 0.3, kernel, fontsize=10) -ax[1][2].text( - -0.49, +plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1) +plt.text(-0.45, 0.5, "$SWIFT$ {0}".format(git.decode("utf-8")), fontsize=text_fontsize) +plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize) +plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize) +plt.text( + -0.45, 0.2, "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), - fontsize=10, + fontsize=text_fontsize, ) -ax[1][2].set_xlim(-0.5, 0.5) -ax[1][2].set_ylim(0.0, 1.0) -ax[1][2].set_xticks([]) -ax[1][2].set_yticks([]) +plt.xlim(-0.5, 0.5) +plt.ylim(0.0, 1.0) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() -pl.tight_layout() -pl.savefig("Vacuum.png", dpi=200) +plt.savefig("Vacuum.png", dpi=200) diff --git a/examples/HydroTests/VacuumSpherical_3D/run.sh b/examples/HydroTests/VacuumSpherical_3D/run.sh index 32a55a7478cbb70d8042154b19964269f337488c..8db8b47a216523acd8bd21ce7e47bd7f10dc392f 100755 --- a/examples/HydroTests/VacuumSpherical_3D/run.sh +++ b/examples/HydroTests/VacuumSpherical_3D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e vacuum.hdf5 ] then echo "Generating initial conditions for the 3D vacuum expansion example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=16 vacuum.yml 2>&1 | tee output.log +../../../swift --hydro --threads=16 vacuum.yml 2>&1 | tee output.log # Get the reference solution if it is not present. if [ ! -e vacuumSpherical3D_exact.txt ] @@ -23,4 +23,4 @@ then fi # Plot the result -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/Vacuum_1D/plotSolution.py b/examples/HydroTests/Vacuum_1D/plotSolution.py index 0fca7e417b98d85efb48af5f4f81c5fa1174ba44..756298ba34e9801adcc997571092da3f68672020 100644 --- a/examples/HydroTests/Vacuum_1D/plotSolution.py +++ b/examples/HydroTests/Vacuum_1D/plotSolution.py @@ -25,6 +25,9 @@ import pylab as pl import h5py import sys +sys.path.append("../") +from riemannSolver import RiemannSolver + # Parameters gamma = 5.0 / 3.0 # Polytropic index rhoL = 1.0 # Initial density in the non vacuum state @@ -34,28 +37,7 @@ rhoR = 0.0 # Initial vacuum density vR = 0.0 # Initial vacuum velocity PR = 0.0 # Initial vacuum pressure -# 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.0, - "text.latex.unicode": True, -} -pl.rcParams.update(params) -pl.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +matplotlib.style.use("../../../tools/stylesheets/mnras.mplstyle") # Read the snapshot index from the command line argument snap = int(sys.argv[1]) @@ -63,61 +45,28 @@ 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/Densities"] +rho = file["/PartType0/Densities"][:] v = file["/PartType0/Velocities"][:, 0] -u = file["/PartType0/InternalEnergies"] -S = file["/PartType0/Entropies"] -P = file["/PartType0/Pressures"] +u = file["/PartType0/InternalEnergies"][:] +S = file["/PartType0/Entropies"][:] +P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] -scheme = file["/HydroScheme"].attrs["Scheme"] -kernel = file["/HydroScheme"].attrs["Kernel function"] +scheme = file["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = file["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] eta = file["/HydroScheme"].attrs["Kernel eta"][0] -git = file["Code"].attrs["Git Revision"] - -# Get the analytic solution, which is just the solution of the corresponding -# vacuum Riemann problem evaluated at the correct time - -# left state sound speed (and rarefaction wave speed) -aL = np.sqrt(gamma * PL / rhoL) - -# vacuum front speed -SL = vL + 2.0 / (gamma - 1.0) * aL +git = file["Code"].attrs["Git Revision"].decode("utf-8") -# we evaluate the solution centred on 0., and shift to the correct position -# afterwards +solver = RiemannSolver(gamma) xa = np.arange(-0.25, 0.25, 0.001) -rhoa = np.zeros(len(xa)) -va = np.zeros(len(xa)) -Pa = np.zeros(len(xa)) - -for i in range(len(xa)): - dxdt = xa[i] / time - if dxdt > vL - aL: - if dxdt < SL: - # rarefaction regime - # factor that appears in both the density and pressure expression - fac = 2.0 / (gamma + 1.0) + (gamma - 1.0) / (gamma + 1.0) * (vL - dxdt) / aL - rhoa[i] = rhoL * fac ** (2.0 / (gamma - 1.0)) - va[i] = 2.0 / (gamma + 1.0) * (aL + 0.5 * (gamma - 1.0) * vL + dxdt) - Pa[i] = PL * fac ** (2.0 * gamma / (gamma - 1.0)) - else: - # vacuum regime - rhoa[i] = 0.0 - va[i] = 0.0 - Pa[i] = 0.0 - else: - # left state regime - rhoa[i] = rhoL - va[i] = vL - Pa[i] = PL +rhoa, va, Pa, _ = solver.solve(rhoL, vL, PL, rhoR, vR, PR, xa / time) ua = Pa / (gamma - 1.0) / rhoa Sa = Pa / rhoa ** gamma # Plot the interesting quantities -fig, ax = pl.subplots(2, 3) +fig, ax = pl.subplots(2, 3, figsize=(7, 7 / 1.6)) # Velocity profile ax[0][0].plot(x, v, "r.", markersize=4.0) @@ -156,33 +105,34 @@ ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0) # Run information ax[1][2].set_frame_on(False) +text_fontsize = 5 ax[1][2].text( -0.49, 0.9, "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(gamma, time), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), + fontsize=text_fontsize, ) ax[1][2].text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PR, rhoR, vR), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10) -ax[1][2].text(-0.49, 0.4, scheme, fontsize=10) -ax[1][2].text(-0.49, 0.3, kernel, fontsize=10) +ax[1][2].text(-0.49, 0.5, "SWIFT {0}".format(git), fontsize=text_fontsize) +ax[1][2].text(-0.49, 0.4, scheme, fontsize=text_fontsize) +ax[1][2].text(-0.49, 0.3, kernel, fontsize=text_fontsize) ax[1][2].text( -0.49, 0.2, "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].set_xlim(-0.5, 0.5) ax[1][2].set_ylim(0.0, 1.0) diff --git a/examples/HydroTests/Vacuum_1D/run.sh b/examples/HydroTests/Vacuum_1D/run.sh index c5f7e0344e7a6517dae51390e2eae4acd80a5f5f..224ba4afb964d40502fb806f1c322e87f9eff017 100755 --- a/examples/HydroTests/Vacuum_1D/run.sh +++ b/examples/HydroTests/Vacuum_1D/run.sh @@ -4,11 +4,11 @@ if [ ! -e vacuum.hdf5 ] then echo "Generating initial conditions for the 1D vacuum expansion example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=1 vacuum.yml 2>&1 | tee output.log +../../../swift --hydro --threads=1 vacuum.yml 2>&1 | tee output.log # Plot the result -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/Vacuum_2D/plotSolution.py b/examples/HydroTests/Vacuum_2D/plotSolution.py index f154b5cb9bab6a0c302cde8306364ba8b8a99afa..c4e0c8b89601c2c2dc0e9e31755bb05fded42108 100644 --- a/examples/HydroTests/Vacuum_2D/plotSolution.py +++ b/examples/HydroTests/Vacuum_2D/plotSolution.py @@ -26,6 +26,9 @@ import h5py import sys import scipy.stats as stats +sys.path.append("../") +from riemannSolver import RiemannSolver + # Parameters gamma = 5.0 / 3.0 # Polytropic index rhoL = 1.0 # Initial density in the non vacuum state @@ -35,28 +38,7 @@ rhoR = 0.0 # Initial vacuum density vR = 0.0 # Initial vacuum velocity PR = 0.0 # Initial vacuum pressure -# 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.0, - "text.latex.unicode": True, -} -pl.rcParams.update(params) -pl.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +matplotlib.style.use("../../../tools/stylesheets/mnras.mplstyle") # Read the snapshot index from the command line argument snap = int(sys.argv[1]) @@ -71,48 +53,15 @@ S = file["/PartType0/Entropies"][:] P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] -scheme = file["/HydroScheme"].attrs["Scheme"] -kernel = file["/HydroScheme"].attrs["Kernel function"] +scheme = file["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = file["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] eta = file["/HydroScheme"].attrs["Kernel eta"][0] -git = file["Code"].attrs["Git Revision"] - -# Get the analytic solution, which is just the solution of the corresponding -# vacuum Riemann problem evaluated at the correct time - -# left state sound speed (and rarefaction wave speed) -aL = np.sqrt(gamma * PL / rhoL) - -# vacuum front speed -SL = vL + 2.0 / (gamma - 1.0) * aL +git = file["Code"].attrs["Git Revision"].decode("utf-8") -# we evaluate the solution centred on 0., and shift to the correct position -# afterwards +solver = RiemannSolver(gamma) xa = np.arange(-0.25, 0.25, 0.001) -rhoa = np.zeros(len(xa)) -va = np.zeros(len(xa)) -Pa = np.zeros(len(xa)) - -for i in range(len(xa)): - dxdt = xa[i] / time - if dxdt > vL - aL: - if dxdt < SL: - # rarefaction regime - # factor that appears in both the density and pressure expression - fac = 2.0 / (gamma + 1.0) + (gamma - 1.0) / (gamma + 1.0) * (vL - dxdt) / aL - rhoa[i] = rhoL * fac ** (2.0 / (gamma - 1.0)) - va[i] = 2.0 / (gamma + 1.0) * (aL + 0.5 * (gamma - 1.0) * vL + dxdt) - Pa[i] = PL * fac ** (2.0 * gamma / (gamma - 1.0)) - else: - # vacuum regime - rhoa[i] = 0.0 - va[i] = 0.0 - Pa[i] = 0.0 - else: - # left state regime - rhoa[i] = rhoL - va[i] = vL - Pa[i] = PL +rhoa, va, Pa, _ = solver.solve(rhoL, vL, PL, rhoR, vR, PR, xa / time) ua = Pa / (gamma - 1.0) / rhoa Sa = Pa / rhoa ** gamma @@ -142,7 +91,7 @@ S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) # Plot the interesting quantities -fig, ax = pl.subplots(2, 3) +fig, ax = pl.subplots(2, 3, figsize=(7, 7 / 1.6)) # Velocity profile ax[0][0].plot(x, v, "r.", markersize=0.2) @@ -202,33 +151,34 @@ ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0) # Run information ax[1][2].set_frame_on(False) +text_fontsize = 5 ax[1][2].text( -0.49, 0.9, "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(gamma, time), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), + fontsize=text_fontsize, ) ax[1][2].text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PR, rhoR, vR), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10) -ax[1][2].text(-0.49, 0.4, scheme, fontsize=10) -ax[1][2].text(-0.49, 0.3, kernel, fontsize=10) +ax[1][2].text(-0.49, 0.5, "SWIFT {0}".format(git), fontsize=text_fontsize) +ax[1][2].text(-0.49, 0.4, scheme, fontsize=text_fontsize) +ax[1][2].text(-0.49, 0.3, kernel, fontsize=text_fontsize) ax[1][2].text( -0.49, 0.2, "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].set_xlim(-0.5, 0.5) ax[1][2].set_ylim(0.0, 1.0) diff --git a/examples/HydroTests/Vacuum_2D/run.sh b/examples/HydroTests/Vacuum_2D/run.sh index 08f3ba19d69865112db6ac68f0264e19b6b4363b..90c3c57bb562b17c0156a90784918735a26b7f28 100755 --- a/examples/HydroTests/Vacuum_2D/run.sh +++ b/examples/HydroTests/Vacuum_2D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e vacuum.hdf5 ] then echo "Generating initial conditions for the 2D vacuum expansion example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=4 vacuum.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 vacuum.yml 2>&1 | tee output.log # Plot the result -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/Vacuum_3D/plotSolution.py b/examples/HydroTests/Vacuum_3D/plotSolution.py index f154b5cb9bab6a0c302cde8306364ba8b8a99afa..c4e0c8b89601c2c2dc0e9e31755bb05fded42108 100644 --- a/examples/HydroTests/Vacuum_3D/plotSolution.py +++ b/examples/HydroTests/Vacuum_3D/plotSolution.py @@ -26,6 +26,9 @@ import h5py import sys import scipy.stats as stats +sys.path.append("../") +from riemannSolver import RiemannSolver + # Parameters gamma = 5.0 / 3.0 # Polytropic index rhoL = 1.0 # Initial density in the non vacuum state @@ -35,28 +38,7 @@ rhoR = 0.0 # Initial vacuum density vR = 0.0 # Initial vacuum velocity PR = 0.0 # Initial vacuum pressure -# 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.0, - "text.latex.unicode": True, -} -pl.rcParams.update(params) -pl.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +matplotlib.style.use("../../../tools/stylesheets/mnras.mplstyle") # Read the snapshot index from the command line argument snap = int(sys.argv[1]) @@ -71,48 +53,15 @@ S = file["/PartType0/Entropies"][:] P = file["/PartType0/Pressures"][:] time = file["/Header"].attrs["Time"][0] -scheme = file["/HydroScheme"].attrs["Scheme"] -kernel = file["/HydroScheme"].attrs["Kernel function"] +scheme = file["/HydroScheme"].attrs["Scheme"].decode("utf-8") +kernel = file["/HydroScheme"].attrs["Kernel function"].decode("utf-8") neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] eta = file["/HydroScheme"].attrs["Kernel eta"][0] -git = file["Code"].attrs["Git Revision"] - -# Get the analytic solution, which is just the solution of the corresponding -# vacuum Riemann problem evaluated at the correct time - -# left state sound speed (and rarefaction wave speed) -aL = np.sqrt(gamma * PL / rhoL) - -# vacuum front speed -SL = vL + 2.0 / (gamma - 1.0) * aL +git = file["Code"].attrs["Git Revision"].decode("utf-8") -# we evaluate the solution centred on 0., and shift to the correct position -# afterwards +solver = RiemannSolver(gamma) xa = np.arange(-0.25, 0.25, 0.001) -rhoa = np.zeros(len(xa)) -va = np.zeros(len(xa)) -Pa = np.zeros(len(xa)) - -for i in range(len(xa)): - dxdt = xa[i] / time - if dxdt > vL - aL: - if dxdt < SL: - # rarefaction regime - # factor that appears in both the density and pressure expression - fac = 2.0 / (gamma + 1.0) + (gamma - 1.0) / (gamma + 1.0) * (vL - dxdt) / aL - rhoa[i] = rhoL * fac ** (2.0 / (gamma - 1.0)) - va[i] = 2.0 / (gamma + 1.0) * (aL + 0.5 * (gamma - 1.0) * vL + dxdt) - Pa[i] = PL * fac ** (2.0 * gamma / (gamma - 1.0)) - else: - # vacuum regime - rhoa[i] = 0.0 - va[i] = 0.0 - Pa[i] = 0.0 - else: - # left state regime - rhoa[i] = rhoL - va[i] = vL - Pa[i] = PL +rhoa, va, Pa, _ = solver.solve(rhoL, vL, PL, rhoR, vR, PR, xa / time) ua = Pa / (gamma - 1.0) / rhoa Sa = Pa / rhoa ** gamma @@ -142,7 +91,7 @@ S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2) x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) # Plot the interesting quantities -fig, ax = pl.subplots(2, 3) +fig, ax = pl.subplots(2, 3, figsize=(7, 7 / 1.6)) # Velocity profile ax[0][0].plot(x, v, "r.", markersize=0.2) @@ -202,33 +151,34 @@ ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0) # Run information ax[1][2].set_frame_on(False) +text_fontsize = 5 ax[1][2].text( -0.49, 0.9, "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(gamma, time), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].text( -0.49, 0.8, - "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), - fontsize=10, + "Left: $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL), + fontsize=text_fontsize, ) ax[1][2].text( -0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PR, rhoR, vR), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1) -ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10) -ax[1][2].text(-0.49, 0.4, scheme, fontsize=10) -ax[1][2].text(-0.49, 0.3, kernel, fontsize=10) +ax[1][2].text(-0.49, 0.5, "SWIFT {0}".format(git), fontsize=text_fontsize) +ax[1][2].text(-0.49, 0.4, scheme, fontsize=text_fontsize) +ax[1][2].text(-0.49, 0.3, kernel, fontsize=text_fontsize) ax[1][2].text( -0.49, 0.2, "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), - fontsize=10, + fontsize=text_fontsize, ) ax[1][2].set_xlim(-0.5, 0.5) ax[1][2].set_ylim(0.0, 1.0) diff --git a/examples/HydroTests/Vacuum_3D/run.sh b/examples/HydroTests/Vacuum_3D/run.sh index b75803f97fdd60a78efe9b05267e265d51fb1f1f..855a006327ae2c859af255f4c09eaada32a54de5 100755 --- a/examples/HydroTests/Vacuum_3D/run.sh +++ b/examples/HydroTests/Vacuum_3D/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e vacuum.hdf5 ] then echo "Generating initial conditions for the 3D vacuum expansion example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --hydro --threads=16 vacuum.yml 2>&1 | tee output.log +../../../swift --hydro --threads=16 vacuum.yml 2>&1 | tee output.log # Plot the result -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/HydroTests/riemannSolver.py b/examples/HydroTests/riemannSolver.py new file mode 100755 index 0000000000000000000000000000000000000000..e1e235cbf0ada06d395ec6986d7b71e792235a20 --- /dev/null +++ b/examples/HydroTests/riemannSolver.py @@ -0,0 +1,1139 @@ +#!/usr/bin/env python + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# 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 riemannsolver.py +# +# @brief Standalone Riemann solver library. +# +# @author Bert Vandenbroucke (vandenbroucke@strw.leidenuniv.nl) +################################################################################ + +################################################################################ +# This is a standalone Riemann solver, based on the exact Riemann solver that is +# part of Shadowfax (https://github.com/AstroUGent/shadowfax), CMacIonize +# (https://github.com/bwvdnbro/CMacIonize) and SWIFT. +# +# It is based on the description given in Toro, E., Riemann Solvers and +# Numerical Methods for Fluid Dynamics, 3rd edition (Springer, 2009). +# +# This file should not be run directly (it just runs some unit tests if this is +# done). Instead, include it in another script as follows: +# import riemannsolver +# (make sure the folder containing the file is part of the system PATH). +# +# Once included, you can create a RiemannSolver object for an ideal gas with a +# given polytropic index (e.g. 5/3): +# solver = riemannsolver.RiemannSolver(5./3.) +# This object has a member function solve which can be used to solve the Riemann +# problem with a given left and right state: +# rhosol, usol, psol, flag = solver.solver(rhoL, uL, pL, rhoR, uR, pR) +# rhosol, rhoL, and rhoR are the densities of respectively the solution, the +# left state, and the right state. +# usol, uL, and uR are the fluid velocities +# psol, pL, and pR are the pressures +# flag is an extra return variable that is set to -1, 1, or 0 if respectively +# the left state, the right state, or a vacuum state was sampled. +# +# By default, the Riemann solution is sampled for a reference velocity dxdt = 0. +# Alternatively, you can sample the solution for another reference velocity +# dxdt = x / t by providing dxdt as an extra argument to solve(). +################################################################################ + +# Import the Python numerical libraries, which we need for sqrt +import numpy as np + +## +# @brief Exact Riemann solver. +## +class RiemannSolver: + + ############################################################################## + # @brief Constructor. + # + # @param gamma Adiabatic index \f$\gamma{}\f$. + ############################################################################## + def __init__(self, gamma): + + if gamma <= 1.0: + print("The adiabatic index needs to be larger than 1!") + exit() + + self._gamma = gamma + + ## related quantities: + # gamma plus 1 divided by 2 gamma + self._gp1d2g = 0.5 * (gamma + 1.0) / gamma + # gamma minus 1 divided by 2 gamma + self._gm1d2g = 0.5 * (gamma - 1.0) / gamma + # gamma minus 1 divided by gamma plus 1 + self._gm1dgp1 = (gamma - 1.0) / (gamma + 1.0) + # two divided by gamma plus 1 + self._tdgp1 = 2.0 / (gamma + 1.0) + # two divided by gamma minus 1 + self._tdgm1 = 2.0 / (gamma - 1.0) + # gamma minus 1 divided by 2 + self._gm1d2 = 0.5 * (gamma - 1.0) + # two times gamma divided by gamma minus 1 + self._tgdgm1 = 2.0 * gamma / (gamma - 1.0) + # gamma inverse + self._ginv = 1.0 / gamma + + ############################################################################## + # @brief Get the soundspeed corresponding to the given density and pressure. + # + # @param rho Density value. + # @param P Pressure value. + # @return Soundspeed. + ############################################################################## + def get_soundspeed(self, rho, P): + if rho > 0.0: + return np.sqrt(self._gamma * P / rho) + else: + return 0.0 + + ############################################################################## + # @brief Riemann fL or fR function. + # + # @param rho Density of the left or right state. + # @param P Pressure of the left or right state. + # @param a Soundspeed of the left or right state. + # @param Pstar (Temporary) pressure of the middle state. + # @return Value of the fL or fR function. + ############################################################################## + def fb(self, rho, P, a, Pstar): + if Pstar > P: + A = self._tdgp1 / rho + B = self._gm1dgp1 * P + fval = (Pstar - P) * np.sqrt(A / (Pstar + B)) + else: + fval = self._tdgm1 * a * ((Pstar / P) ** (self._gm1d2g) - 1.0) + + return fval + + ############################################################################## + # @brief Riemann f function. + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param Pstar (Temporary) pressure of the middle state. + # @return Value of the Riemann f function. + ############################################################################## + def f(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar): + return self.fb(rhoL, PL, aL, Pstar) + self.fb(rhoR, PR, aR, Pstar) + (uR - uL) + + ############################################################################## + # @brief Derivative of the Riemann fL or fR function. + # + # @param rho Density of the left or right state. + # @param P Pressure of the left or right state. + # @param a Soundspeed of the left or right state. + # @param Pstar (Temporary) pressure of the middle state. + # @return Value of the derivative of the Riemann fL or fR function. + ############################################################################## + def fprimeb(self, rho, P, a, Pstar): + if Pstar > P: + A = self._tdgp1 / rho + B = self._gm1dgp1 * P + fval = (1.0 - 0.5 * (Pstar - P) / (B + Pstar)) * np.sqrt(A / (Pstar + B)) + else: + fval = 1.0 / (rho * a) * (Pstar / P) ** (-self._gp1d2g) + + return fval + + ############################################################################## + # @brief Derivative of the Riemann f function. + # + # @param rhoL Density of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param rhoR Density of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param Pstar (Temporary) pressure of the middle state. + # @return Value of the derivative of the Riemann f function. + ############################################################################## + def fprime(self, rhoL, PL, aL, rhoR, PR, aR, Pstar): + return self.fprimeb(rhoL, PL, aL, Pstar) + self.fprimeb(rhoR, PR, aR, Pstar) + + ############################################################################## + # @brief Riemann gL or gR function. + # + # @param rho Density of the left or right state. + # @param P Pressure of the left or right state. + # @param Pstar (Temporary) pressure in the middle state. + # @return Value of the gL or gR function. + ############################################################################## + def gb(self, rho, P, Pstar): + A = self._tdgp1 / rho + B = self._gm1dgp1 * P + return np.sqrt(A / (Pstar + B)) + + ############################################################################## + # @brief Get an initial guess for the pressure in the middle state. + # + # @param rhoL Left state density. + # @param uL Left state velocity. + # @param PL Left state pressure. + # @param aL Left state soundspeed. + # @param rhoR Right state density. + # @param uR Right state velocity. + # @param PR Right state pressure. + # @param aR Right state soundspeed. + # @return Initial guess for the pressure in the middle state. + ############################################################################## + def guess_P(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR): + Pmin = min(PL, PR) + Pmax = max(PL, PR) + qmax = Pmax / Pmin + Ppv = 0.5 * (PL + PR) - 0.125 * (uR - uL) * (PL + PR) * (aL + aR) + Ppv = max(5.0e-9 * (PL + PR), Ppv) + if qmax <= 2 and Pmin <= Ppv and Ppv <= Pmax: + Pguess = Ppv + else: + if Ppv < Pmin: + # two rarefactions + Pguess = ( + (aL + aR - self._gm1d2 * (uR - uL)) + / (aL / (PL ** self._gm1d2g) + aR / (PR ** self._gm1d2g)) + ) ** self._tgdgm1 + else: + # two shocks + gL = self.gb(rhoL, PL, Ppv) + gR = self.gb(rhoR, PR, Ppv) + Pguess = (gL * PL + gR * PR - uR + uL) / (gL + gR) + + # Toro: "Not that approximate solutions may predict, incorrectly, a + # negative value for pressure (...). Thus in order to avoid negative guess + # values we introduce the small positive constant _tolerance" + # (tolerance is 1.e-8 in this case) + Pguess = max(5.0e-9 * (PL + PR), Pguess) + return Pguess + + ############################################################################## + # @brief Find the pressure of the middle state by using Brent's method. + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param Plow Lower bound guess for the pressure of the middle state. + # @param Phigh Higher bound guess for the pressure of the middle state. + # @return Pressure of the middle state, with a 1.e-8 relative error precision. + ############################################################################## + def solve_brent(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, Plow, Phigh): + a = Plow + b = Phigh + c = 0.0 + d = 1.0e230 + + fa = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, a) + fb = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, b) + fc = 0.0 + + s = 0.0 + fs = 0.0 + + if fa * fb > 0.0: + print("Equal sign function values provided to solve_brent!") + exit() + + # if |f(a)| < |f(b)| then swap (a,b) end if + if abs(fa) < abs(fb): + tmp = a + a = b + b = tmp + tmp = fa + fa = fb + fb = tmp + + c = a + fc = fa + mflag = True + + while (not fb == 0.0) and (abs(a - b) > 5.0e-9 * (a + b)): + if (not fa == fc) and (not fb == fc): + # Inverse quadratic interpolation + s = ( + a * fb * fc / (fa - fb) / (fa - fc) + + b * fa * fc / (fb - fa) / (fb - fc) + + c * fa * fb / (fc - fa) / (fc - fb) + ) + else: + # Secant Rule + s = b - fb * (b - a) / (fb - fa) + + tmp2 = 0.25 * (3.0 * a + b) + if ( + (not (((s > tmp2) and (s < b)) or ((s < tmp2) and (s > b)))) + or (mflag and (abs(s - b) >= 0.5 * abs(b - c))) + or ((not mflag) and (abs(s - b) >= 0.5 * abs(c - d))) + or (mflag and (abs(b - c) < 5.0e-9 * (b + c))) + or ((not mflag) and (abs(c - d) < 5.0e-9 * (c + d))) + ): + s = 0.5 * (a + b) + mflag = True + else: + mflag = False + fs = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, s) + d = c + c = b + fc = fb + if fa * fs < 0.0: + b = s + fb = fs + else: + a = s + fa = fs + + # if |f(a)| < |f(b)| then swap (a,b) end if + if abs(fa) < abs(fb): + tmp = a + a = b + b = tmp + tmp = fa + fa = fb + fb = tmp + + return b + + ############################################################################## + # @brief Sample the Riemann problem solution for a position in the right + # shock wave regime. + # + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param ustar Velocity of the middle state. + # @param Pstar Pressure of the middle state. + # @param rhosol Density solution. + # @param usol Velocity solution. + # @param Psol Pressure solution. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution. + ############################################################################## + def sample_right_shock_wave( + self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0]) + ): + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + + # variable used twice below + PdPR = Pstar / PR + # get the shock speed + SR = uR + aR * np.sqrt(self._gp1d2g * PdPR + self._gm1d2g) + middle_state = SR > dxdt + right_state = ~middle_state + ## middle state (shock) regime + rhosol[middle_state] = ( + rhoR * (PdPR + self._gm1dgp1) / (self._gm1dgp1 * PdPR + 1.0) + ) + usol[middle_state] = ustar + Psol[middle_state] = Pstar + ## right state regime + rhosol[right_state] = rhoR + usol[right_state] = uR + Psol[right_state] = PR + + return rhosol, usol, Psol + + ############################################################################## + # @brief Sample the Riemann problem solution for a position in the right + # rarefaction wave regime. + # + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param ustar Velocity of the middle state. + # @param Pstar Pressure of the middle state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution. + ############################################################################## + def sample_right_rarefaction_wave( + self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0]) + ): + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + + # get the velocity of the head of the rarefaction wave + SHR = uR + aR + rarefaction_regime = SHR > dxdt + right_state = ~rarefaction_regime + ## rarefaction wave regime + # variable used twice below + PdPR = Pstar / PR + # get the velocity of the tail of the rarefaction wave + STR = ustar + aR * PdPR ** self._gm1d2g + middle_state = rarefaction_regime & (STR > dxdt) + rarefaction_fan = rarefaction_regime & (~middle_state) + ## middle state regime + rhosol[middle_state] = rhoR * PdPR ** self._ginv + usol[middle_state] = ustar + Psol[middle_state] = Pstar + ## rarefaction fan regime + # variable used twice below + base = self._tdgp1 - self._gm1dgp1 * (uR - dxdt[rarefaction_fan]) / aR + rhosol[rarefaction_fan] = rhoR * base ** self._tdgm1 + usol[rarefaction_fan] = self._tdgp1 * ( + -aR + self._gm1d2 * uR + dxdt[rarefaction_fan] + ) + Psol[rarefaction_fan] = PR * base ** self._tgdgm1 + ## right state regime + rhosol[right_state] = rhoR + usol[right_state] = uR + Psol[right_state] = PR + + return rhosol, usol, Psol + + ############################################################################## + # @brief Sample the Riemann problem solution in the right state regime. + # + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param ustar Velocity of the middle state. + # @param Pstar Pressure of the middle state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution. + ############################################################################## + def sample_right_state(self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0])): + if Pstar > PR: + ## shock wave + rhosol, usol, Psol = self.sample_right_shock_wave( + rhoR, uR, PR, aR, ustar, Pstar, dxdt + ) + else: + ## rarefaction wave + rhosol, usol, Psol = self.sample_right_rarefaction_wave( + rhoR, uR, PR, aR, ustar, Pstar, dxdt + ) + + return rhosol, usol, Psol + + ############################################################################## + # @brief Sample the Riemann problem solution for a position in the left shock + # wave regime. + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param ustar Velocity of the middle state. + # @param Pstar Pressure of the middle state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution. + ############################################################################## + def sample_left_shock_wave( + self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0]) + ): + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + + # variable used twice below + PdPL = Pstar / PL + # get the shock speed + SL = uL - aL * np.sqrt(self._gp1d2g * PdPL + self._gm1d2g) + middle_state = SL < dxdt + left_state = ~middle_state + if np.any(middle_state): + ## middle state (shock) regime + rhosol[middle_state] = ( + rhoL * (PdPL + self._gm1dgp1) / (self._gm1dgp1 * PdPL + 1.0) + ) + usol[middle_state] = ustar + Psol[middle_state] = Pstar + if np.any(left_state): + ## left state regime + rhosol[left_state] = rhoL + usol[left_state] = uL + Psol[left_state] = PL + + return rhosol, usol, Psol + + ############################################################################## + # @brief Sample the Riemann problem solution for a position in the left + # rarefaction wave regime. + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param ustar Velocity of the middle state. + # @param Pstar Pressure of the middle state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution. + ############################################################################## + def sample_left_rarefaction_wave( + self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0]) + ): + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + + # get the velocity of the head of the rarefaction wave + SHL = uL - aL + rarefaction_wave = SHL < dxdt + left_state = ~rarefaction_wave + if np.any(rarefaction_wave): + ## rarefaction wave regime + # variable used twice below + PdPL = Pstar / PL + # get the velocity of the tail of the rarefaction wave + STL = ustar - aL * PdPL ** self._gm1d2g + rarefaction_fan = rarefaction_wave & (STL > dxdt) + middle_state = rarefaction_wave & (~rarefaction_fan) + if np.any(rarefaction_fan): + ## rarefaction fan regime + # variable used twice below + base = self._tdgp1 + self._gm1dgp1 * (uL - dxdt[rarefaction_fan]) / aL + rhosol[rarefaction_fan] = rhoL * base ** self._tdgm1 + usol[rarefaction_fan] = self._tdgp1 * ( + aL + self._gm1d2 * uL + dxdt[rarefaction_fan] + ) + Psol[rarefaction_fan] = PL * base ** self._tgdgm1 + if np.any(middle_state): + ## middle state regime + rhosol[middle_state] = rhoL * PdPL ** self._ginv + usol[middle_state] = ustar + Psol[middle_state] = Pstar + if np.any(left_state): + ## left state regime + rhosol[left_state] = rhoL + usol[left_state] = uL + Psol[left_state] = PL + + return rhosol, usol, Psol + + ############################################################################## + # @brief Sample the Riemann problem solution in the left state regime. + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param ustar Velocity of the middle state. + # @param Pstar Pressure of the middle state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution. + ############################################################################## + def sample_left_state(self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0])): + if Pstar > PL: + ## shock wave + rhosol, usol, Psol = self.sample_left_shock_wave( + rhoL, uL, PL, aL, ustar, Pstar, dxdt + ) + else: + ## rarefaction wave + rhosol, usol, Psol = self.sample_left_rarefaction_wave( + rhoL, uL, PL, aL, ustar, Pstar, dxdt + ) + + return rhosol, usol, Psol + + ############################################################################## + # @brief Sample the vacuum Riemann problem if the right state is a vacuum. + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution, and a flag indicating + # wether the left state (-1), the right state (1), or a vacuum state (0) was + # sampled. + ############################################################################## + def sample_right_vacuum(self, rhoL, uL, PL, aL, dxdt=np.array([0.0])): + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + flagsol = np.zeros(dxdt.shape, dtype=np.int32) + + vacuum_regime = uL - aL < dxdt + left_state = ~vacuum_regime + if np.any(vacuum_regime): + ## vacuum regime + # get the vacuum rarefaction wave speed + SL = uL + self._tdgm1 * aL + rarefaction_wave = vacuum_regime & (SL > dxdt) + if np.any(rarefaction_wave): + ## rarefaction wave regime + # variable used twice below + base = self._tdgp1 + self._gm1dgp1 * (uL - dxdt[rarefaction_wave]) / aL + rhosol[rarefaction_wave] = rhoL * base ** self._tdgm1 + usol[rarefaction_wave] = self._tdgp1 * ( + aL + self._gm1d2 * uL + dxdt[rarefaction_wave] + ) + Psol[rarefaction_wave] = PL * base ** self._tgdgm1 + flagsol[rarefaction_wave] = -1 + ## vacuum + # variables are already 0, as they should be + if np.any(left_state): + ## left state regime + rhosol[left_state] = rhoL + usol[left_state] = uL + Psol[left_state] = PL + flagsol[left_state] = -1 + + return rhosol, usol, Psol, flagsol + + ############################################################################## + # @brief Sample the vacuum Riemann problem if the left state is a vacuum. + # + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution, and a flag indicating + # wether the left state (-1), the right state (1), or a vacuum state (0) was + # sampled. + ############################################################################## + def sample_left_vacuum(self, rhoR, uR, PR, aR, dxdt=np.array([0.0])): + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + flagsol = np.zeros(dxdt.shape, dtype=np.int32) + + vacuum_regime = dxdt < uR + aR + right_state = ~vacuum_regime + if np.any(vacuum_regime): + ## vacuum regime + # get the vacuum rarefaction wave speed + SR = uR - self._tdgm1 * aR + rarefaction_wave = vacuum_regime & (SR < dxdt) + if np.any(rarefaction_wave): + ## rarefaction wave regime + # variable used twice below + base = self._tdgp1 - self._gm1dgp1 * (uR - dxdt[rarefaction_wave]) / aR + rhosol[rarefaction_wave] = rhoR * base ** self._tdgm1 + usol[rarefaction_wave] = self._tdgp1 * ( + -aR + self._tdgm1 * uR + dxdt[rarefaction_wave] + ) + Psol[rarefaction_wave] = PR * base ** self._tgdgm1 + flagsol[rarefaction_wave] = 1 + ## vacuum + # variables are already 0, as they should be + ## right state regime + if np.any(right_state): + rhosol[right_state] = rhoR + usol[right_state] = uR + Psol[right_state] = PR + flagsol[right_state] = 1 + + return rhosol, usol, Psol, flagsol + + ############################################################################## + # @brief Sample the vacuum Riemann problem in the case vacuum is generated in + # between the left and right state. + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution, and a flag indicating + # wether the left state (-1), the right state (1), or a vacuum state (0) was + # sampled. + ############################################################################## + def sample_vacuum_generation(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt): + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + flagsol = np.zeros(dxdt.shape, dtype=np.int32) + + # get the speeds of the left and right rarefaction waves + SR = uR - self._tdgm1 * aR + SL = uL + self._tdgm1 * aL + vacuum = (SR > dxdt) & (SL < dxdt) + rarefaction_regime = ~vacuum + ## vacuum + # variables are already 0, as they should be + if np.any(rarefaction_regime): + ## rarefaction regime + right_rarefaction_regime = rarefaction_regime & (SL < dxdt) + left_rarefaction_regime = rarefaction_regime & (~right_rarefaction_regime) + if np.any(right_rarefaction_regime): + ## right state + right_rarefaction = right_rarefaction_regime & (dxdt < uR + aR) + right_state = right_rarefaction_regime & (~right_rarefaction) + if np.any(right_rarefaction): + ## right rarefaction wave regime + # variable used twice below + base = ( + self._tdgp1 + - self._gm1dgp1 * (uR - dxdt[right_rarefaction]) / aR + ) + rhosol[right_rarefaction] = rhoR * base ** self._tdgm1 + usol[right_rarefaction] = self._tdgp1 * ( + -aR + self._tdgm1 * uR + dxdt[right_rarefaction] + ) + Psol[right_rarefaction] = PR * base ** self._tgdgm1 + if np.any(right_state): + ## right state regime + rhosol[right_state] = rhoR + usol[right_state] = uR + Psol[right_state] = PR + flagsol[right_rarefaction_regime] = 1 + if np.any(left_rarefaction_regime): + ### left state + left_rarefaction = left_rarefaction_regime & (dxdt > uL - aL) + left_state = left_rarefaction_regime & (~left_rarefaction) + if np.any(left_rarefaction): + ## left rarefaction wave regime + # variable used twice below + base = ( + self._tdgp1 + self._gm1dgp1 * (uL - dxdt[left_rarefaction]) / aL + ) + rhosol[left_rarefaction] = rhoL * base ** self._tdgm1 + usol[left_rarefaction] = self._tdgp1 * ( + aL + self._tdgm1 * uL + dxdt[left_rarefaction] + ) + Psol[left_rarefaction] = PL * base ** self._tgdgm1 + if np.any(left_state): + ## left state regime + rhosol[left_state] = rhoL + usol[left_state] = uL + Psol[left_state] = PL + flagsol[left_rarefaction_regime] = -1 + + return rhosol, usol, Psol, flagsol + + ############################################################################## + # @brief Vacuum Riemann solver. + # + # This solver is called when one or both states have a zero density, or when + # the vacuum generation condition is satisfied (meaning vacuum is generated + # in the middle state, although strictly speaking there is no "middle" + # state if vacuum is involved). + # + # @param rhoL Density of the left state. + # @param uL Velocity of the left state. + # @param PL Pressure of the left state. + # @param aL Soundspeed of the left state. + # @param rhoR Density of the right state. + # @param uR Velocity of the right state. + # @param PR Pressure of the right state. + # @param aR Soundspeed of the right state. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution, and a flag indicating + # wether the left state (-1), the right state (1), or a vacuum state (0) was + # sampled. + ############################################################################## + def solve_vacuum(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt=np.array([0.0])): + # if both states are vacuum, the solution is also vacuum + if rhoL == 0.0 and rhoR == 0.0: + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + flagsol = np.zeros(dxdt.shape, dtype=np.int32) + return rhosol, usol, Psol, flagsol + + if rhoR == 0.0: + ## vacuum right state + return self.sample_right_vacuum(rhoL, uL, PL, aL, dxdt) + else: + if rhoL == 0.0: + ## vacuum left state + return self.sample_left_vacuum(rhoR, uR, PR, aR, dxdt) + else: + ## vacuum "middle" state + return self.sample_vacuum_generation( + rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt + ) + + ############################################################################## + # @brief Solve the Riemann problem with the given left and right state. + # + # @param rhoL Left state density. + # @param uL Left state velocity. + # @param PL Left state pressure. + # @param rhoR Right state density. + # @param uR Right state velocity. + # @param PR Right state pressure. + # @param dxdt Point in velocity space where we want to sample the solution. + # @return Density, velocity and pressure solution, and a flag indicating + # wether the left state (-1), the right state (1), or a vacuum state (0) was + # sampled. + ############################################################################## + def solve(self, rhoL, uL, PL, rhoR, uR, PR, dxdt=np.array([0.0])): + + # get the soundspeeds + aL = self.get_soundspeed(rhoL, PL) + aR = self.get_soundspeed(rhoR, PR) + + # handle vacuum + if rhoL == 0.0 or rhoR == 0.0: + return self.solve_vacuum(rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt) + + # handle vacuum generation + if self._tdgm1 * (aL + aR) <= uR - uL: + return self.solve_vacuum(rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt) + + # find the pressure and velocity in the middle state + # since this is an exact Riemann solver, this is an iterative process, + # whereby we basically find the root of a function (the Riemann f function + # defined above) + # we start by using a Newton-Raphson method, since we do not have an + # interval in which the function changes sign + # however, as soon as we have such an interval, we switch to a much more + # robust root finding method (Brent's method). We do this because the + # Newton-Raphson method in some cases can overshoot and return a negative + # pressure, for which the Riemann f function is not defined. Brent's method + # will never stroll outside of the initial interval in which the function + # changes sign. + Pstar = 0.0 + Pguess = self.guess_P(rhoL, uL, PL, aL, rhoR, uR, PR, aR) + # we only store this variable to store the sign of the function for pressure + # zero + # we need to find a larger pressure for which this sign changes to have an + # interval where we can use Brent's method + fPstar = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar) + fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess) + if fPstar * fPguess >= 0.0: + # Newton-Raphson until convergence or until usable interval is found to + # use Brent's method + while abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess < 0.0: + Pstar = Pguess + Pguess = Pguess - fPguess / self.fprime( + rhoL, PL, aL, rhoR, PR, aR, Pguess + ) + fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess) + + # As soon as there is a suitable interval: use Brent's method + if abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess > 0.0: + Pstar = self.solve_brent(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar, Pguess) + else: + Pstar = Pguess + + # the middle state velocity is fixed once the middle state pressure is known + ustar = 0.5 * (uL + uR) + 0.5 * ( + self.fb(rhoR, PR, aR, Pstar) - self.fb(rhoL, PL, aL, Pstar) + ) + + # we now have solved the Riemann problem: we have the left, middle and + # right state, and this completely fixes the solution + # we just need to sample the solution for x/t. + rhosol = np.zeros(dxdt.shape) + usol = np.zeros(dxdt.shape) + Psol = np.zeros(dxdt.shape) + flagsol = np.zeros(dxdt.shape, dtype=np.int32) + + right_state = ustar < dxdt + left_state = ~right_state + + if np.any(right_state): + rhosol[right_state], usol[right_state], Psol[ + right_state + ] = self.sample_right_state( + rhoR, uR, PR, aR, ustar, Pstar, dxdt[right_state] + ) + flagsol[right_state] = 1 + if np.any(left_state): + rhosol[left_state], usol[left_state], Psol[ + left_state + ] = self.sample_left_state(rhoL, uL, PL, aL, ustar, Pstar, dxdt[left_state]) + flagsol[left_state] = -1 + + return rhosol, usol, Psol, flagsol + + ############################################################################## + # @brief Solve the Riemann problem with the given left and right state for + # the velocity and pressure (but do not sample the solution). + # + # @param rhoL Left state density. + # @param uL Left state velocity. + # @param PL Left state pressure. + # @param rhoR Right state density. + # @param uR Right state velocity. + # @param PR Right state pressure. + # @return Velocity and pressure in the middle region. + ############################################################################## + def solve_middle_state(self, rhoL, uL, PL, rhoR, uR, PR): + + # get the soundspeeds + aL = self.get_soundspeed(rhoL, PL) + aR = self.get_soundspeed(rhoR, PR) + + # handle vacuum + if rhoL == 0.0 or rhoR == 0.0: + print("Vacuum not handled yet!") + exit() + + # handle vacuum generation + if self._tdgm1 * (aL + aR) <= uR - uL: + print("Vacuum not handled yet!") + exit() + + # find the pressure and velocity in the middle state + # since this is an exact Riemann solver, this is an iterative process, + # whereby we basically find the root of a function (the Riemann f function + # defined above) + # we start by using a Newton-Raphson method, since we do not have an + # interval in which the function changes sign + # however, as soon as we have such an interval, we switch to a much more + # robust root finding method (Brent's method). We do this because the + # Newton-Raphson method in some cases can overshoot and return a negative + # pressure, for which the Riemann f function is not defined. Brent's method + # will never stroll outside of the initial interval in which the function + # changes sign. + Pstar = 0.0 + Pguess = self.guess_P(rhoL, uL, PL, aL, rhoR, uR, PR, aR) + # we only store this variable to store the sign of the function for pressure + # zero + # we need to find a larger pressure for which this sign changes to have an + # interval where we can use Brent's method + fPstar = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar) + fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess) + if fPstar * fPguess >= 0.0: + # Newton-Raphson until convergence or until usable interval is found to + # use Brent's method + while abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess < 0.0: + Pstar = Pguess + Pguess = Pguess - fPguess / self.fprime( + rhoL, PL, aL, rhoR, PR, aR, Pguess + ) + fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess) + + # As soon as there is a suitable interval: use Brent's method + if abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess > 0.0: + Pstar = self.solve_brent(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar, Pguess) + else: + Pstar = Pguess + + # the middle state velocity is fixed once the middle state pressure is known + ustar = 0.5 * (uL + uR) + 0.5 * ( + self.fb(rhoR, PR, aR, Pstar) - self.fb(rhoL, PL, aL, Pstar) + ) + + return ustar, Pstar + + +################################################################################ +################################################################################ + +################################################################################ +# @brief Check if the relative difference between the two given values is +# smaller than the given tolerance. +# +# @param A First value. +# @param B Second value. +# @param relative_error Tolerance relative difference value. +# @return True if the relative difference between the two values is smaller than +# the given tolerance value. +################################################################################ +def relative_difference_smaller_than(A, B, relative_error): + if A == B: + return True + else: + return abs(A - B) < relative_error * abs(A + B) + + +################################################################################ +# @brief Get the relative difference between the two given values. +# +# @param A First value. +# @param B Second value. +# @return Relative difference between the two values. +################################################################################ +def relative_difference(A, B): + return abs(A - B) / abs(A + B) + + +################################################################################ +# @brief Run a basic Riemann solver test with given left and right state, and +# given reference pressure solution. +# +# @param rhoL Left state density. +# @param uL Left state velocity. +# @param PL Left state pressure. +# @param rhoR Right state density. +# @param uR Right state velocity. +# @param PR Right state pressure. +# @param Pref Reference solution pressure. +################################################################################ +def run_riemannsolver_basic_test(solver, rhoL, uL, PL, rhoR, uR, PR, Pref): + + usol, Psol = solver.solve_middle_state(rhoL, uL, PL, rhoR, uR, PR) + + if not relative_difference_smaller_than(Psol, Pref, 1.0e-4): + print( + "Wrong pressure solution: {Psol}, should be {Pref}".format( + Psol=Psol, Pref=Pref + ) + ) + print( + "(relative difference: {reldiff})!".format( + reldiff=relative_difference(Psol, Pref) + ) + ) + exit() + + +################################################################################ +# @brief Run a Riemann solver test with given left and right state, and given +# reference solution. +# +# @param rhoL Left state density. +# @param uL Left state velocity. +# @param PL Left state pressure. +# @param rhoR Right state density. +# @param uR Right state velocity. +# @param PR Right state pressure. +# @param rhoref Reference solution density. +# @param uref Reference solution velocity. +# @param Pref Reference solution pressure. +# @param flagref Reference solution flag: 1 if the right state is sampled, -1 if +# the left state is sampled, and 0 if a vacuum state is sampled. +################################################################################ +def run_riemannsolver_test( + solver, rhoL, uL, PL, rhoR, uR, PR, rhoref, uref, Pref, flagref +): + rhosol, usol, Psol, flagsol = solver.solve(rhoL, uL, PL, rhoR, uR, PR) + + deviation = False + if not relative_difference_smaller_than(rhosol, rhoref, 1.0e-4): + print( + "Wrong density solution: {rhosol}, should be {rhoref}".format( + rhosol=rhosol, rhoref=rhoref + ) + ) + print( + "(relative difference: {reldiff})!".format( + reldiff=relative_difference(rhosol, rhoref) + ) + ) + deviation = True + + if not relative_difference_smaller_than(usol, uref, 1.0e-4): + print( + "Wrong velocity solution: {usol}, should be {uref}".format( + usol=usol, uref=uref + ) + ) + print( + "(relative difference: {reldiff})!".format( + reldiff=relative_difference(usol, uref) + ) + ) + deviation = True + + if not relative_difference_smaller_than(Psol, Pref, 1.0e-4): + print( + "Wrong pressure solution: {Psol}, should be {Pref}".format( + Psol=Psol, Pref=Pref + ) + ) + print( + "(relative difference: {reldiff})!".format( + reldiff=relative_difference(Psol, Pref) + ) + ) + deviation = True + + if not flagsol == flagref: + print( + "Wrong solution sampled: {flagsol}, should be {flagref}".format( + flagsol=flagsol, flagref=flagref + ) + ) + deviation = True + + if deviation: + raise RuntimeError(f"Wrong solution ({rhosol} {usol} {Psol} {flagsol}!") + + +################################################################################ +# @brief Default action when this file is run directly: run some unit tests. +################################################################################ +if __name__ == "__main__": + print( + "\nThis script should not be run directly. Instead, import it into " + "another script and use its functionality there.\n" + "Now that we're running anyway, we will quickly run some unit tests " + "to make sure everything still works...\n" + ) + + # Toro tests + solver = RiemannSolver(1.4) + run_riemannsolver_basic_test(solver, 1.0, 0.0, 1.0, 0.125, 0.0, 0.1, 0.30313) + run_riemannsolver_basic_test(solver, 1.0, -2.0, 0.4, 1.0, 2.0, 0.4, 0.001894) + run_riemannsolver_basic_test(solver, 1.0, 0.0, 1000.0, 1.0, 0.0, 0.01, 460.894) + run_riemannsolver_basic_test(solver, 1.0, 0.0, 0.01, 1.0, 0.0, 100.0, 46.095) + run_riemannsolver_basic_test( + solver, 5.99924, 19.5975, 460.894, 5.99242, -6.19633, 46.0950, 1691.64 + ) + + # Toro tests with sampling and different adiabatic index + solver = RiemannSolver(5.0 / 3.0) + run_riemannsolver_test( + solver, 1.0, 0.0, 1.0, 0.125, 0.0, 0.1, 0.47969, 0.841194, 0.293945, -1 + ) + run_riemannsolver_test( + solver, 1.0, -2.0, 0.4, 1.0, 2.0, 0.4, 0.00617903, 0.0, 8.32249e-05, -1 + ) + run_riemannsolver_test( + solver, 1.0, 0.0, 1000.0, 1.0, 0.0, 0.01, 0.615719, 18.2812, 445.626, -1 + ) + run_riemannsolver_test( + solver, 1.0, 0.0, 0.01, 1.0, 0.0, 100.0, 0.61577, -5.78011, 44.5687, 1 + ) + run_riemannsolver_test( + solver, + 5.99924, + 19.5975, + 460.894, + 5.99242, + -6.19633, + 46.0950, + 12.743, + 8.56045, + 1841.82, + -1, + ) + # vacuum generation test + run_riemannsolver_test( + solver, 1.0, -1.0, 1.0e-6, 1.0, 1.0, 1.0005e-6, 0.0, 0.0, 0.0, 0 + ) + + print( + "Unit tests successfully finished. Everything still works!\n" + "Have a nice day!\n" + ) diff --git a/examples/IdealisedCluster/IdealisedCluster_M13/getIC.sh b/examples/IdealisedCluster/IdealisedCluster_M13/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..a34436617e539abe0e61876f274c53b6d11e2b4f --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M13/getIC.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Fiducial IC (T_0 = 0 K) +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_fid.hdf5 + +# Different central temperatures T_0 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_Tmin55.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_Tmin575.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_Tmin60.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_Tmin625.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_Tmin65.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_Tmin67.hdf5 + +# Fiducial IC (T_0 = 0 K) at different resolutions +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_lowres8.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_lowres64.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_lowres512.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H13_lowres4096.hdf5 diff --git a/examples/IdealisedCluster/IdealisedCluster_M13/idealised_cluster_M13.yml b/examples/IdealisedCluster/IdealisedCluster_M13/idealised_cluster_M13.yml new file mode 100644 index 0000000000000000000000000000000000000000..27055559430ab33e50efa593d3d9828153051b89 --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M13/idealised_cluster_M13.yml @@ -0,0 +1,236 @@ +# Define some meta-data about the simulation +MetaData: + run_name: Idealised-Cluster-M13-fid + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e21 # kpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 8.184 # The end time of the simulation 8 Gyr (in internal units). + dt_min: 1e-16 # 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: cluster # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.01023 # Time difference between consecutive outputs (in internal units) 0.01023 TU = 10 Myr + compression: 4 # Compress the snapshots + recording_triggers_part: [-1, -1] # Not recording as we have many snapshots + recording_triggers_bpart: [-1, -1] # Not recording as we have many snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.01 + scale_factor_first: 0.01 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: geometric # Use the geometric opening angle condition + theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) + use_tree_below_softening: 0 + max_physical_baryon_softening: 0.3 # Maximal Plummer-equivalent softening length in physical coordinates for baryon particles (in internal units). + +# 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). + h_min_ratio: 0.01 # Minimal smoothing length in units of softening. + h_max: 200. # Maximal smoothing length in co-moving internal units. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + particle_splitting: 1 # Particle splitting is ON + particle_splitting_mass_threshold: 7e-4 # (internal units, i.e. 7e6 Msun ~ 4x initial gas particle mass) + H_mass_fraction: 0.756 + +# 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 + luminosity_filename: ./photometry + birth_time: -9.207 # (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). + overwrite_birth_time: 1 # (Optional) Do we want to overwrite the birth time of the stars read from the ICs? (default: 0). + +Scheduler: + max_top_level_cells: 16 + cell_split_size: 200 + +Restarts: + onexit: 1 + delta_hours: 6.0 + max_run_time: 71.5 # Three days minus fergie time + resubmit_on_exit: 1 + resubmit_command: ./resub.sh + +# Parameters related to the initial conditions +InitialConditions: + file_name: M5_H13_fid.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + stars_smoothing_length: 0.5 + +# NFW potential parameters +NFWPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.0,0.0,0.0] # Location of centre of the NFW potential with respect to centre of the box (internal units) if useabspos=0 otherwise with respect to the 0,0,0, coordinates. + concentration: 7.2 # Concentration of the halo + M_200: 1000.0 # Mass of the halo (M_200 in internal units) + h: 0.704 # Critical density (internal units). + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines fraction of orbital time we need to do an integration step + bulgefraction: 0.0100 # Bulge mass fraction + epsilon: 0.3 # Softening of the NFW potential + + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0.004457 # Inital fraction of particle mass in *all* metals + init_abundance_Hydrogen: 0.749796 # Inital fraction of particle mass in Hydrogen + init_abundance_Helium: 0.245747 # Inital fraction of particle mass in Helium + init_abundance_Carbon: 0.000788 # Inital fraction of particle mass in Carbon + init_abundance_Nitrogen: 0.000231 # Inital fraction of particle mass in Nitrogen + init_abundance_Oxygen: 0.001911 # Inital fraction of particle mass in Oxygen + init_abundance_Neon: 0.000419 # Inital fraction of particle mass in Neon + init_abundance_Magnesium: 0.000236 # Inital fraction of particle mass in Magnesium + init_abundance_Silicon: 0.000222 # Inital fraction of particle mass in Silicon + init_abundance_Iron: 0.000431 # Inital fraction of particle mass in Iron + +# COLIBRE cooling parameters +COLIBRECooling: + dir_name: ./UV_dust1_CR1_G1_shield1.hdf5 # Location of the cooling tables + H_reion_z: 7.5 # Redshift of Hydrogen re-ionization (Planck 2018) + 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 + delta_logTEOS_subgrid_properties: 0.3 # delta log T above the EOS below which the subgrid properties use Teq assumption + rapid_cooling_threshold: 0.333333 # Switch to rapid cooling regime for dt / t_cool above this threshold. + +# EAGLE star formation parameters +EAGLEStarFormation: + SF_threshold: Subgrid # Zdep (Schaye 2004) or Subgrid + SF_model: PressureLaw # PressureLaw (Schaye et al. 2008) or SchmidtLaw + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + min_over_density: 100.0 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e8 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + EOS_entropy_margin_dex: 0.3 # When using Z-based SF threshold, logarithm base 10 of the maximal entropy above the EOS at which stars can form. + threshold_norm_H_p_cm3: 0.1 # When using Z-based SF threshold, normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # When using Z-based SF threshold, reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # When using Z-based SF threshold, slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # When using Z-based SF threshold, maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_temperature1_K: 1000 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming. + threshold_temperature2_K: 31622 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming if also above the density limit. + threshold_number_density_H_p_cm3: 10 # When using subgrid-based SF threshold, subgrid number density above which gas is star-forming if also below the second temperature limit. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 1e-4 # 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: 800 # 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: 10. # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. (NOTE: This is below the min T and hence this floor does nothing) + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# EAGLE feedback model +EAGLEFeedback: + use_SNII_feedback: 1 # Global switch for SNII thermal (stochastic) feedback. + use_SNIa_feedback: 1 # 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: 8.0 # Minimal mass considered for SNII stars in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII stars in solar masses. + SNII_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. + 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). + 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: 1.4588 # 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. + SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? + SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? + SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. + SNIa_DTD_delay_Gyr: 0.04 # Stellar age after which SNIa start in Gyr (40 Myr corresponds to stars ~ 8 Msun). + SNIa_DTD_exp_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_DTD_exp_norm_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. + stellar_evolution_age_cut_Gyr: 0.1 # Stellar age in Gyr above which the enrichment is down-sampled. + stellar_evolution_sampling_rate: 10 # Number of time-steps in-between two enrichment events for a star above the age threshold. + 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. + +# EAGLE AGN model +EAGLEAGN: + subgrid_seed_mass_Msun: 1.0e4 # Black hole subgrid mass at creation time in solar masses. + use_multi_phase_bondi: 0 # Compute Bondi rates per neighbour particle? + use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? + with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? + viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term + with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? + boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. + boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). + with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? + fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed + fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. + coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? + use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). + AGN_delta_T_crit_factor: 1.0 # Multiple of critical dT for numerical efficiency (Dalla Vecchia & Schaye 2012) to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. + AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). + max_reposition_mass: 1e20 # Maximal BH mass considered for BH repositioning in solar masses (large number implies we always reposition). + max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. + with_reposition_velocity_threshold: 0 # Should we only reposition to particles that move slowly w.r.t. the black hole? + max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. + min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. + set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. + threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' + threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). + merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. + minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. + + diff --git a/examples/IdealisedCluster/IdealisedCluster_M13/run.sh b/examples/IdealisedCluster/IdealisedCluster_M13/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..6a777d4e3e8f08d41b3108f2e5668f902ec4e672 --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M13/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ ! -e M5_H13_fid.hdf5 ] +then + echo "Fetching initial conditions for the idealised cluster example..." + ./getIC.sh +fi + +if [ ! -e UV_dust1_CR1_G1_shield1.hdf5 ] +then + echo "Fetching PS20 cooling tables for the isolated galaxy example..." + ../getPS20CoolingTables.sh +fi + +if [ ! -e yieldtables ] +then + echo "Fetching EAGLE stellar yield tables for the isolated galaxy example..." + ../getYieldTable.sh +fi + +if [ ! -e photometry ] +then + echo "Fetching EAGLE photometry tables..." + ../getEaglePhotometryTable.sh +fi + +../../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --temperature --hydro --limiter --sync --black-holes idealised_cluster_M13.yml 2>&1 | tee output.log diff --git a/examples/IdealisedCluster/IdealisedCluster_M135/getIC.sh b/examples/IdealisedCluster/IdealisedCluster_M135/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..2d667b60be168b932fa6ad2c442bc1424925944b --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M135/getIC.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# The fiducial IC (T_0 = 10^6.5 K) +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H135_Tmin65.hdf5 + +# Different central temperatures T_0 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H135_Tmin60.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H135_Tmin675.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H135_Tmin70.hdf5 diff --git a/examples/IdealisedCluster/IdealisedCluster_M135/idealised_cluster_M135.yml b/examples/IdealisedCluster/IdealisedCluster_M135/idealised_cluster_M135.yml new file mode 100644 index 0000000000000000000000000000000000000000..40697364daa7cef8bf7cdc8efeb5c76b551bc488 --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M135/idealised_cluster_M135.yml @@ -0,0 +1,236 @@ +# Define some meta-data about the simulation +MetaData: + run_name: Idealised-Cluster-M13.5-fid + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e21 # kpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 8.184 # The end time of the simulation 8 Gyr (in internal units). + dt_min: 1e-16 # 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: cluster # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.01023 # Time difference between consecutive outputs (in internal units) 0.01023 TU = 10 Myr + compression: 4 # Compress the snapshots + recording_triggers_part: [-1, -1] # Not recording as we have many snapshots + recording_triggers_bpart: [-1, -1] # Not recording as we have many snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.01 + scale_factor_first: 0.01 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: geometric # Use the geometric opening angle condition + theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) + use_tree_below_softening: 0 + max_physical_baryon_softening: 0.3 # Maximal Plummer-equivalent softening length in physical coordinates for baryon particles (in internal units). + +# 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). + h_min_ratio: 0.01 # Minimal smoothing length in units of softening. + h_max: 200. # Maximal smoothing length in co-moving internal units. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + particle_splitting: 1 # Particle splitting is ON + particle_splitting_mass_threshold: 7e-4 # (internal units, i.e. 7e6 Msun ~ 4x initial gas particle mass) + H_mass_fraction: 0.756 + +# 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 + luminosity_filename: ./photometry + birth_time: -9.207 # (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). + overwrite_birth_time: 1 # (Optional) Do we want to overwrite the birth time of the stars read from the ICs? (default: 0). + +Scheduler: + max_top_level_cells: 16 + cell_split_size: 200 + +Restarts: + onexit: 1 + delta_hours: 6.0 + max_run_time: 71.5 # Three days minus fergie time + resubmit_on_exit: 1 + resubmit_command: ./resub.sh + +# Parameters related to the initial conditions +InitialConditions: + file_name: M5_H135_Tmin65.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + stars_smoothing_length: 0.5 + +# NFW potential parameters +NFWPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.0,0.0,0.0] # Location of centre of the NFW potential with respect to centre of the box (internal units) if useabspos=0 otherwise with respect to the 0,0,0, coordinates. + concentration: 6.4 # Concentration of the halo + M_200: 3000.0 # Mass of the halo (M_200 in internal units) + h: 0.704 # Critical density (internal units). + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines fraction of orbital time we need to do an integration step + bulgefraction: 0.0050 # Bulge mass fraction + epsilon: 0.3 # Softening of the NFW potential + + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0.004457 # Inital fraction of particle mass in *all* metals + init_abundance_Hydrogen: 0.749796 # Inital fraction of particle mass in Hydrogen + init_abundance_Helium: 0.245747 # Inital fraction of particle mass in Helium + init_abundance_Carbon: 0.000788 # Inital fraction of particle mass in Carbon + init_abundance_Nitrogen: 0.000231 # Inital fraction of particle mass in Nitrogen + init_abundance_Oxygen: 0.001911 # Inital fraction of particle mass in Oxygen + init_abundance_Neon: 0.000419 # Inital fraction of particle mass in Neon + init_abundance_Magnesium: 0.000236 # Inital fraction of particle mass in Magnesium + init_abundance_Silicon: 0.000222 # Inital fraction of particle mass in Silicon + init_abundance_Iron: 0.000431 # Inital fraction of particle mass in Iron + +# COLIBRE cooling parameters +COLIBRECooling: + dir_name: ./UV_dust1_CR1_G1_shield1.hdf5 # Location of the cooling tables + H_reion_z: 7.5 # Redshift of Hydrogen re-ionization (Planck 2018) + 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 + delta_logTEOS_subgrid_properties: 0.3 # delta log T above the EOS below which the subgrid properties use Teq assumption + rapid_cooling_threshold: 0.333333 # Switch to rapid cooling regime for dt / t_cool above this threshold. + +# EAGLE star formation parameters +EAGLEStarFormation: + SF_threshold: Subgrid # Zdep (Schaye 2004) or Subgrid + SF_model: PressureLaw # PressureLaw (Schaye et al. 2008) or SchmidtLaw + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + min_over_density: 100.0 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e8 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + EOS_entropy_margin_dex: 0.3 # When using Z-based SF threshold, logarithm base 10 of the maximal entropy above the EOS at which stars can form. + threshold_norm_H_p_cm3: 0.1 # When using Z-based SF threshold, normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # When using Z-based SF threshold, reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # When using Z-based SF threshold, slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # When using Z-based SF threshold, maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_temperature1_K: 1000 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming. + threshold_temperature2_K: 31622 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming if also above the density limit. + threshold_number_density_H_p_cm3: 10 # When using subgrid-based SF threshold, subgrid number density above which gas is star-forming if also below the second temperature limit. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 1e-4 # 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: 800 # 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: 10. # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. (NOTE: This is below the min T and hence this floor does nothing) + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# EAGLE feedback model +EAGLEFeedback: + use_SNII_feedback: 1 # Global switch for SNII thermal (stochastic) feedback. + use_SNIa_feedback: 1 # 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: 8.0 # Minimal mass considered for SNII stars in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII stars in solar masses. + SNII_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. + 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). + 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: 1.4588 # 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. + SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? + SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? + SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. + SNIa_DTD_delay_Gyr: 0.04 # Stellar age after which SNIa start in Gyr (40 Myr corresponds to stars ~ 8 Msun). + SNIa_DTD_exp_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_DTD_exp_norm_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. + stellar_evolution_age_cut_Gyr: 0.1 # Stellar age in Gyr above which the enrichment is down-sampled. + stellar_evolution_sampling_rate: 10 # Number of time-steps in-between two enrichment events for a star above the age threshold. + 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. + +# EAGLE AGN model +EAGLEAGN: + subgrid_seed_mass_Msun: 1.0e4 # Black hole subgrid mass at creation time in solar masses. + use_multi_phase_bondi: 0 # Compute Bondi rates per neighbour particle? + use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? + with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? + viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term + with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? + boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. + boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). + with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? + fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed + fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. + coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? + use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). + AGN_delta_T_crit_factor: 1.0 # Multiple of critical dT for numerical efficiency (Dalla Vecchia & Schaye 2012) to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. + AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). + max_reposition_mass: 1e20 # Maximal BH mass considered for BH repositioning in solar masses (large number implies we always reposition). + max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. + with_reposition_velocity_threshold: 0 # Should we only reposition to particles that move slowly w.r.t. the black hole? + max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. + min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. + set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. + threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' + threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). + merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. + minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. + + diff --git a/examples/IdealisedCluster/IdealisedCluster_M135/run.sh b/examples/IdealisedCluster/IdealisedCluster_M135/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..23108949920137da29db249128d10a35431c704c --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M135/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ ! -e M5_H135_Tmin65.hdf5 ] +then + echo "Fetching initial conditions for the idealised cluster example..." + ./getIC.sh +fi + +if [ ! -e UV_dust1_CR1_G1_shield1.hdf5 ] +then + echo "Fetching PS20 cooling tables for the isolated galaxy example..." + ../getPS20CoolingTables.sh +fi + +if [ ! -e yieldtables ] +then + echo "Fetching EAGLE stellar yield tables for the isolated galaxy example..." + ../getYieldTable.sh +fi + +if [ ! -e photometry ] +then + echo "Fetching EAGLE photometry tables..." + ../getEaglePhotometryTable.sh +fi + +../../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --temperature --hydro --limiter --sync --black-holes idealised_cluster_M13.yml 2>&1 | tee output.log diff --git a/examples/IdealisedCluster/IdealisedCluster_M14/getIC.sh b/examples/IdealisedCluster/IdealisedCluster_M14/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..b395732b6edd6fd40f08266de99aa62b67ec118d --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M14/getIC.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# fiducial IC (T_0 = 10^7 K) +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H14_fid.hdf5 + +# Different central temperatures (T_0) +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H14_Tmin65.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H14_Tmin675.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H14_Tmin725.hdf5 + +# Fiducial IC at different resolutions +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_lowres8_Tmin7.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_lowres64_Tmin7.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_lowres512_Tmin7.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_lowres4096_Tmin7.hdf5 diff --git a/examples/IdealisedCluster/IdealisedCluster_M14/idealised_cluster_M14.yml b/examples/IdealisedCluster/IdealisedCluster_M14/idealised_cluster_M14.yml new file mode 100644 index 0000000000000000000000000000000000000000..a6f4be54da81a3a289fd3f83f2aa604a0bc34cc3 --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M14/idealised_cluster_M14.yml @@ -0,0 +1,236 @@ +# Define some meta-data about the simulation +MetaData: + run_name: Idealised-Cluster-M14-fid + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e21 # kpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 8.184 # The end time of the simulation 8 Gyr (in internal units). + dt_min: 1e-16 # 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: cluster # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.01023 # Time difference between consecutive outputs (in internal units) 0.01023 TU = 10 Myr + compression: 4 # Compress the snapshots + recording_triggers_part: [-1, -1] # Not recording as we have many snapshots + recording_triggers_bpart: [-1, -1] # Not recording as we have many snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.01 + scale_factor_first: 0.01 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: geometric # Use the geometric opening angle condition + theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) + use_tree_below_softening: 0 + max_physical_baryon_softening: 0.3 # Maximal Plummer-equivalent softening length in physical coordinates for baryon particles (in internal units). + +# 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). + h_min_ratio: 0.01 # Minimal smoothing length in units of softening. + h_max: 200. # Maximal smoothing length in co-moving internal units. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + particle_splitting: 1 # Particle splitting is ON + particle_splitting_mass_threshold: 7e-4 # (internal units, i.e. 7e6 Msun ~ 4x initial gas particle mass) + H_mass_fraction: 0.756 + +# 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 + luminosity_filename: ./photometry + birth_time: -9.207 # (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). + overwrite_birth_time: 1 # (Optional) Do we want to overwrite the birth time of the stars read from the ICs? (default: 0). + +Scheduler: + max_top_level_cells: 16 + cell_split_size: 200 + +Restarts: + onexit: 1 + delta_hours: 6.0 + max_run_time: 71.5 # Three days minus fergie time + resubmit_on_exit: 1 + resubmit_command: ./resub.sh + +# Parameters related to the initial conditions +InitialConditions: + file_name: M5_H14_fid.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + stars_smoothing_length: 0.5 + +# NFW potential parameters +NFWPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.0,0.0,0.0] # Location of centre of the NFW potential with respect to centre of the box (internal units) if useabspos=0 otherwise with respect to the 0,0,0, coordinates. + concentration: 5.6 # Concentration of the halo + M_200: 10000.0 # Mass of the halo (M_200 in internal units) + h: 0.704 # Critical density (internal units). + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines fraction of orbital time we need to do an integration step + bulgefraction: 0.0025 # Bulge mass fraction + epsilon: 0.3 # Softening of the NFW potential + + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0.004457 # Inital fraction of particle mass in *all* metals + init_abundance_Hydrogen: 0.749796 # Inital fraction of particle mass in Hydrogen + init_abundance_Helium: 0.245747 # Inital fraction of particle mass in Helium + init_abundance_Carbon: 0.000788 # Inital fraction of particle mass in Carbon + init_abundance_Nitrogen: 0.000231 # Inital fraction of particle mass in Nitrogen + init_abundance_Oxygen: 0.001911 # Inital fraction of particle mass in Oxygen + init_abundance_Neon: 0.000419 # Inital fraction of particle mass in Neon + init_abundance_Magnesium: 0.000236 # Inital fraction of particle mass in Magnesium + init_abundance_Silicon: 0.000222 # Inital fraction of particle mass in Silicon + init_abundance_Iron: 0.000431 # Inital fraction of particle mass in Iron + +# COLIBRE cooling parameters +COLIBRECooling: + dir_name: ./UV_dust1_CR1_G1_shield1.hdf5 # Location of the cooling tables + H_reion_z: 7.5 # Redshift of Hydrogen re-ionization (Planck 2018) + 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 + delta_logTEOS_subgrid_properties: 0.3 # delta log T above the EOS below which the subgrid properties use Teq assumption + rapid_cooling_threshold: 0.333333 # Switch to rapid cooling regime for dt / t_cool above this threshold. + +# EAGLE star formation parameters +EAGLEStarFormation: + SF_threshold: Subgrid # Zdep (Schaye 2004) or Subgrid + SF_model: PressureLaw # PressureLaw (Schaye et al. 2008) or SchmidtLaw + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + min_over_density: 100.0 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e8 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + EOS_entropy_margin_dex: 0.3 # When using Z-based SF threshold, logarithm base 10 of the maximal entropy above the EOS at which stars can form. + threshold_norm_H_p_cm3: 0.1 # When using Z-based SF threshold, normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # When using Z-based SF threshold, reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # When using Z-based SF threshold, slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # When using Z-based SF threshold, maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_temperature1_K: 1000 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming. + threshold_temperature2_K: 31622 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming if also above the density limit. + threshold_number_density_H_p_cm3: 10 # When using subgrid-based SF threshold, subgrid number density above which gas is star-forming if also below the second temperature limit. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 1e-4 # 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: 800 # 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: 10. # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. (NOTE: This is below the min T and hence this floor does nothing) + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# EAGLE feedback model +EAGLEFeedback: + use_SNII_feedback: 1 # Global switch for SNII thermal (stochastic) feedback. + use_SNIa_feedback: 1 # 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: 8.0 # Minimal mass considered for SNII stars in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII stars in solar masses. + SNII_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. + 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). + 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: 1.4588 # 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. + SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? + SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? + SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. + SNIa_DTD_delay_Gyr: 0.04 # Stellar age after which SNIa start in Gyr (40 Myr corresponds to stars ~ 8 Msun). + SNIa_DTD_exp_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_DTD_exp_norm_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. + stellar_evolution_age_cut_Gyr: 0.1 # Stellar age in Gyr above which the enrichment is down-sampled. + stellar_evolution_sampling_rate: 10 # Number of time-steps in-between two enrichment events for a star above the age threshold. + 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. + +# EAGLE AGN model +EAGLEAGN: + subgrid_seed_mass_Msun: 1.0e4 # Black hole subgrid mass at creation time in solar masses. + use_multi_phase_bondi: 0 # Compute Bondi rates per neighbour particle? + use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? + with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? + viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term + with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? + boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. + boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). + with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? + fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed + fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. + coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? + use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). + AGN_delta_T_crit_factor: 1.0 # Multiple of critical dT for numerical efficiency (Dalla Vecchia & Schaye 2012) to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. + AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). + max_reposition_mass: 1e20 # Maximal BH mass considered for BH repositioning in solar masses (large number implies we always reposition). + max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. + with_reposition_velocity_threshold: 0 # Should we only reposition to particles that move slowly w.r.t. the black hole? + max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. + min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. + set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. + threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' + threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). + merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. + minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. + + diff --git a/examples/IdealisedCluster/IdealisedCluster_M14/run.sh b/examples/IdealisedCluster/IdealisedCluster_M14/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..38500cd97aaed093402cc2c45eef867d73762109 --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M14/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ ! -e M5_H14_fid.hdf5 ] +then + echo "Fetching initial conditions for the idealised cluster example..." + ./getIC.sh +fi + +if [ ! -e UV_dust1_CR1_G1_shield1.hdf5 ] +then + echo "Fetching PS20 cooling tables for the isolated galaxy example..." + ../getPS20CoolingTables.sh +fi + +if [ ! -e yieldtables ] +then + echo "Fetching EAGLE stellar yield tables for the isolated galaxy example..." + ../getYieldTable.sh +fi + +if [ ! -e photometry ] +then + echo "Fetching EAGLE photometry tables..." + ../getEaglePhotometryTable.sh +fi + +../../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --temperature --hydro --limiter --sync --black-holes idealised_cluster_M13.yml 2>&1 | tee output.log diff --git a/examples/IdealisedCluster/IdealisedCluster_M15/getIC.sh b/examples/IdealisedCluster/IdealisedCluster_M15/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..ec17bb5aa7f770f17cf56a5b1686048b43cc5a19 --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M15/getIC.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# fiducial IC (T_0 = 10^7 K) +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_fid.hdf5 + +# Different central temperatures (T_0) +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_Tmin725.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_Tmin75.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_Tmin80.hdf5 + +# Fiducial IC at different resolutions +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_lowres8.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_lowres512.hdf5 diff --git a/examples/IdealisedCluster/IdealisedCluster_M15/idealised_cluster_M15.yml b/examples/IdealisedCluster/IdealisedCluster_M15/idealised_cluster_M15.yml new file mode 100644 index 0000000000000000000000000000000000000000..40730bbac3ce24ebc3decd30babc5d877f4bac5e --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M15/idealised_cluster_M15.yml @@ -0,0 +1,234 @@ +# Define some meta-data about the simulation +MetaData: + run_name: Idealised-Cluster-M15-fid + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e21 # kpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 8.184 # The end time of the simulation 8 Gyr (in internal units). + dt_min: 1e-16 # 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: cluster # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.01023 # Time difference between consecutive outputs (in internal units) 0.01023 TU = 10 Myr + compression: 4 # Compress the snapshots + recording_triggers_part: [-1, -1] # Not recording as we have many snapshots + recording_triggers_bpart: [-1, -1] # Not recording as we have many snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.01 + scale_factor_first: 0.01 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + MAC: geometric # Use the geometric opening angle condition + theta_cr: 0.7 # Opening angle (Multipole acceptance criterion) + use_tree_below_softening: 0 + max_physical_baryon_softening: 1.2 # Maximal Plummer-equivalent softening length in physical coordinates for baryon particles (in internal units). + +# 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). + h_min_ratio: 0.01 # Minimal smoothing length in units of softening. + h_max: 800. # Maximal smoothing length in co-moving internal units. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + particle_splitting: 1 # Particle splitting is ON + particle_splitting_mass_threshold: 3e-3 # (internal units, i.e. 7e6 Msun ~ 4x initial gas particle mass) + H_mass_fraction: 0.756 + +# 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 + luminosity_filename: ./photometry + birth_time: -9.207 # (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). + overwrite_birth_time: 1 # (Optional) Do we want to overwrite the birth time of the stars read from the ICs? (default: 0). + +Scheduler: + max_top_level_cells: 16 + cell_split_size: 200 + +Restarts: + onexit: 1 + delta_hours: 6.0 + max_run_time: 71.5 # Three days minus fergie time + resubmit_on_exit: 1 + resubmit_command: ./resub.sh + +# Parameters related to the initial conditions +InitialConditions: + file_name: M15_fiducial.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + stars_smoothing_length: 0.5 + +# NFW potential parameters +NFWPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.0,0.0,0.0] # Location of centre of the NFW potential with respect to centre of the box (internal units) if useabspos=0 otherwise with respect to the 0,0,0, coordinates. + concentration: 5.6 # Concentration of the halo + M_200: 10000.0 # Mass of the halo (M_200 in internal units) + h: 0.704 # Critical density (internal units). + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines fraction of orbital time we need to do an integration step + bulgefraction: 0.0025 # Bulge mass fraction + epsilon: 1.2 # Softening of the NFW potential + + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0.004457 # Inital fraction of particle mass in *all* metals + init_abundance_Hydrogen: 0.749796 # Inital fraction of particle mass in Hydrogen + init_abundance_Helium: 0.245747 # Inital fraction of particle mass in Helium + init_abundance_Carbon: 0.000788 # Inital fraction of particle mass in Carbon + init_abundance_Nitrogen: 0.000231 # Inital fraction of particle mass in Nitrogen + init_abundance_Oxygen: 0.001911 # Inital fraction of particle mass in Oxygen + init_abundance_Neon: 0.000419 # Inital fraction of particle mass in Neon + init_abundance_Magnesium: 0.000236 # Inital fraction of particle mass in Magnesium + init_abundance_Silicon: 0.000222 # Inital fraction of particle mass in Silicon + init_abundance_Iron: 0.000431 # Inital fraction of particle mass in Iron + +# COLIBRE cooling parameters +COLIBRECooling: + dir_name: ./UV_dust1_CR1_G1_shield1.hdf5 # Location of the cooling tables + H_reion_z: 7.5 # Redshift of Hydrogen re-ionization (Planck 2018) + 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 + delta_logTEOS_subgrid_properties: 0.3 # delta log T above the EOS below which the subgrid properties use Teq assumption + rapid_cooling_threshold: 0.333333 # Switch to rapid cooling regime for dt / t_cool above this threshold. + +# EAGLE star formation parameters +EAGLEStarFormation: + SF_threshold: Subgrid # Zdep (Schaye 2004) or Subgrid + SF_model: PressureLaw # PressureLaw (Schaye et al. 2008) or SchmidtLaw + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + min_over_density: 100.0 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e8 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + EOS_entropy_margin_dex: 0.3 # When using Z-based SF threshold, logarithm base 10 of the maximal entropy above the EOS at which stars can form. + threshold_norm_H_p_cm3: 0.1 # When using Z-based SF threshold, normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # When using Z-based SF threshold, reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # When using Z-based SF threshold, slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # When using Z-based SF threshold, maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_temperature1_K: 1000 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming. + threshold_temperature2_K: 31622 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming if also above the density limit. + threshold_number_density_H_p_cm3: 10 # When using subgrid-based SF threshold, subgrid number density above which gas is star-forming if also below the second temperature limit. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 1e-4 # 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: 800 # 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: 10. # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. (NOTE: This is below the min T and hence this floor does nothing) + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# EAGLE feedback model +EAGLEFeedback: + use_SNII_feedback: 1 # Global switch for SNII thermal (stochastic) feedback. + use_SNIa_feedback: 1 # 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: 8.0 # Minimal mass considered for SNII stars in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII stars in solar masses. + SNII_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. + 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). + 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: 1.4588 # 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. + SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? + SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? + SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. + SNIa_DTD_delay_Gyr: 0.04 # Stellar age after which SNIa start in Gyr (40 Myr corresponds to stars ~ 8 Msun). + SNIa_DTD_exp_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_DTD_exp_norm_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. + stellar_evolution_age_cut_Gyr: 0.1 # Stellar age in Gyr above which the enrichment is down-sampled. + stellar_evolution_sampling_rate: 10 # Number of time-steps in-between two enrichment events for a star above the age threshold. + 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. + +# EAGLE AGN model +EAGLEAGN: + subgrid_seed_mass_Msun: 1.0e4 # Black hole subgrid mass at creation time in solar masses. + use_multi_phase_bondi: 0 # Compute Bondi rates per neighbour particle? + use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? + with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? + viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term + with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? + boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. + boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). + with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? + fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed + fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? + min_gas_mass_for_nibbling_Msun: 4e6 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. + coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? + use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). + AGN_delta_T_crit_factor: 1.0 # Multiple of critical dT for numerical efficiency (Dalla Vecchia & Schaye 2012) to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. + AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). + max_reposition_mass: 1e20 # Maximal BH mass considered for BH repositioning in solar masses (large number implies we always reposition). + max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. + with_reposition_velocity_threshold: 0 # Should we only reposition to particles that move slowly w.r.t. the black hole? + max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. + min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. + set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. + threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' + threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). + merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. + minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. diff --git a/examples/IdealisedCluster/IdealisedCluster_M15/run.sh b/examples/IdealisedCluster/IdealisedCluster_M15/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..c791af80b01585233d154582099ac2eaebdd56a2 --- /dev/null +++ b/examples/IdealisedCluster/IdealisedCluster_M15/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ ! -e M15_fiducial.hdf5 ] +then + echo "Fetching initial conditions for the idealised cluster example..." + ./getIC.sh +fi + +if [ ! -e UV_dust1_CR1_G1_shield1.hdf5 ] +then + echo "Fetching PS20 cooling tables for the isolated galaxy example..." + ../getPS20CoolingTables.sh +fi + +if [ ! -e yieldtables ] +then + echo "Fetching EAGLE stellar yield tables for the isolated galaxy example..." + ../getYieldTable.sh +fi + +if [ ! -e photometry ] +then + echo "Fetching EAGLE photometry tables..." + ../getEaglePhotometryTable.sh +fi + +../../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --temperature --hydro --limiter --sync --black-holes idealised_cluster_M15.yml 2>&1 | tee output.log diff --git a/examples/IdealisedCluster/README b/examples/IdealisedCluster/README new file mode 100644 index 0000000000000000000000000000000000000000..18689194da63f0ccba8bd627bae226b41d82a39c --- /dev/null +++ b/examples/IdealisedCluster/README @@ -0,0 +1,25 @@ +This directory contains a series of examples using an idealised cluster +that contains a intracluster medium (ICM), a NFW profile for the dark +matter halo and a Hernquist (1990) profile for the central galaxy. + +The ICs are the same as presented in Nobels et al. (2022), see +https://arxiv.org/abs/2204.02205. There are 3 example directories +for the three different halo masse used in Nobels et al. (2022). +The fiducial ICs for the 10^13 Msun and 10^14 Msun idealised cluster +contain resolution variations for 5 different resolutions. For each +halo mass the central temperature variations discussed in the paper +are provided. +The 10^15 Msun halo of the same series introduced by Husko et +al. (2022) https://arxiv.org/abs/2206.06402 is also available at +different resolution and different central temperatures. + +The code should be configured using a NFW profile and the desired subgrid +models: +./configure --with-ext-potential=nfw --enable-fixed-boundary-particles=2 + +The first argument sets the external potential to an NFW profile and the +second argument pins the supermassive BH of the simulation to the centre +of the halo. + +These ICs are ideal for testing galaxy formation subgrid model +implementation. diff --git a/examples/IdealisedCluster/getEaglePhotometryTable.sh b/examples/IdealisedCluster/getEaglePhotometryTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..ee9c3b422f19518612416da0913b162fd4a120ff --- /dev/null +++ b/examples/IdealisedCluster/getEaglePhotometryTable.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/YieldTables/EAGLE/photometry.tar.gz +tar -xf photometry.tar.gz diff --git a/examples/IdealisedCluster/getPS20CoolingTables.sh b/examples/IdealisedCluster/getPS20CoolingTables.sh new file mode 100755 index 0000000000000000000000000000000000000000..0945dc565a4c8b1723b21e685b03369e782cd3be --- /dev/null +++ b/examples/IdealisedCluster/getPS20CoolingTables.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/COLIBRE/UV_dust1_CR1_G1_shield1.hdf5 diff --git a/examples/IdealisedCluster/getYieldTable.sh b/examples/IdealisedCluster/getYieldTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..26eef020cab82acee2c80e88089df1790b281eab --- /dev/null +++ b/examples/IdealisedCluster/getYieldTable.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/IsolatedGalaxy/IsolatedGalaxy_NFW_MN/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_NFW_MN/run.sh index 492eddc7b7fcf2e0a192a7408bdf888f1ef5a1a6..a11b784b49dc19ae936acaab9b09cd72291c6b5c 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_NFW_MN/run.sh +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_NFW_MN/run.sh @@ -6,4 +6,4 @@ then ./getIC.sh fi -../../swift -g -G -s --threads=16 isolated_galaxy.yml +../../../swift -g -G -s --threads=16 isolated_galaxy.yml diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/run.sh index 9669ce5716d5152c0bd7c90f45bbb43b37f51eb1..e0077289f675dad6e21564f7dd26b65fb6034e08 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/run.sh +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/run.sh @@ -6,19 +6,19 @@ then ./getIC.sh fi -../../swift --external-gravity --self-gravity --stars --threads=4 isolated_galaxy.yml 2>&1 | tee output.log +../../../swift --external-gravity --self-gravity --stars --threads=4 isolated_galaxy.yml 2>&1 | tee output.log echo "Make plots of conservation of total angular momentum" if command -v python3 &>/dev/null; then python3 angularmomentum.py else - python angularmomentum.py + python3 angularmomentum.py fi echo "Make plots of change of vertical and radial profile" if command -v python3 &>/dev/null; then python3 profilefit.py else - python profilefit.py + python3 profilefit.py fi diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml index 3c024595a00b84c5a896579b8724486d6571c346..c14d46ed5d5e434a6d18aa4023673a8d203c0077 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml @@ -30,9 +30,11 @@ TimeIntegration: Snapshots: basename: output # Common part of the name of output files time_first: 0. # Time of the first output if non-cosmological time-integration (in internal units) - delta_time: 0.001 # Time difference between consecutive outputs (in internal units) + delta_time: 0.001023 # Time difference between consecutive outputs (in internal units) 0.001023 TU = 1 Myr compression: 4 # Compress the snapshots - + recording_triggers_part: [-1, -1] # Not recording as we have many snapshots + recording_triggers_bpart: [-1, -1] # Not recording as we have many snapshots + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py index 94f27c87ff46c48ffc6b0df8c3e02c7abb6df875..05827463e9b6ab43d14e1f820316c41adbeb9d49 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 @@ -55,10 +55,8 @@ params = { "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 diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/run.sh index 4c0315a26606c31e01fc7272f1ebe8e08b30d3c9..702e6a2c3f355e4ea037179e2e945f90062c70d1 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/run.sh +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/run.sh @@ -30,7 +30,7 @@ then ./getEaglePhotometryTable.sh fi -../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --hydro --limiter --sync isolated_galaxy.yml 2>&1 | tee output.log +../../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --hydro --limiter --sync isolated_galaxy.yml 2>&1 | tee output.log # Kennicutt-Schmidt law plot python3 plotSolution.py 100 diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/run.sh index 0222e05c3383cc258549842d6bb3bb8d88e15453..0356ba18155283c8bad8c558b406e841b78948db 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/run.sh +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/run.sh @@ -6,19 +6,19 @@ then ./getIC.sh fi -../../swift --external-gravity --self-gravity --stars --threads=16 isolated_galaxy.yml 2>&1 | tee output.log +../../../swift --external-gravity --self-gravity --stars --threads=16 isolated_galaxy.yml 2>&1 | tee output.log echo "Make plots of conservation of total angular momentum" if command -v python3 &>/dev/null; then python3 angularmomentum.py else - python angularmomentum.py + python3 angularmomentum.py fi echo "Make plots of change of vertical and radial profile" if command -v python3 &>/dev/null; then python3 profilefit.py else - python profilefit.py + python3 profilefit.py fi diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_sink/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_sink/run.sh index dd61a52d4b8a7273629d549270dffbbaa5faf08a..d23ced0a634a913b111df4b22444726a8b3f7564 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_sink/run.sh +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_sink/run.sh @@ -14,4 +14,4 @@ then ./getIC.sh $filename fi -../../swift --hydro --sinks --stars --external-gravity --self-gravity --threads=8 isolated_galaxy.yml 2>&1 | tee output.log +../../../swift --hydro --sinks --stars --external-gravity --self-gravity --threads=8 isolated_galaxy.yml 2>&1 | tee output.log diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py index d47478760af9b6873c4d89fe1d8b0322f6a5548d..6739b94dc1d08ccf141b2a8007f021c0026b4106 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py @@ -57,10 +57,8 @@ params = { "figure.subplot.bottom": 0.08, "figure.subplot.top": 0.98, "lines.linewidth": 2.0, - "text.latex.unicode": True, } plt.rcParams.update(params) -plt.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) def getSFH(filename, points): diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml index e1e522c3f749c522ae90f4a41e7c7941be9fa1f1..fb8936126da7993f5e46a2f6cf84efe5bbf0a313 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml @@ -26,7 +26,10 @@ TimeIntegration: Snapshots: basename: output # Common part of the name of output files time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) - delta_time: 0.001 # Time difference between consecutive outputs (in internal units) + delta_time: 0.001023 # Time difference between consecutive outputs (in internal units) 0.001023 TU = 1 Myr + compression: 4 # Compress the snapshots + recording_triggers_part: [-1, -1] # Not recording as we have many snapshots + recording_triggers_bpart: [-1, -1] # Not recording as we have many snapshots # Parameters governing the conserved quantities statistics Statistics: diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh index 65f2f9ba0af75423260f6d8921e66e83baba967b..842caee52f224d98959d3a946e9400e7dcc759ba 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh @@ -30,7 +30,7 @@ then ../getEaglePhotometryTable.sh fi -../../swift --threads=16 --external-gravity --self-gravity --stars --star-formation --cooling --hydro isolated_galaxy.yml 2>&1 | tee output.log +../../../swift --threads=16 --external-gravity --self-gravity --stars --star-formation --cooling --hydro isolated_galaxy.yml 2>&1 | tee output.log # Kennicutt-Schmidt law plot python3 plotSolution.py diff --git a/examples/Makefile.am b/examples/Makefile.am index f707edcadb6be63f4cf116bd721d73659bbe653a..3d865c8f99e757b50d98a02afa1614bd25adc189 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,6 +1,6 @@ # This file is part of SWIFT. # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), -# Matthieu Schaller (matthieu.schaller@durham.ac.uk). +# 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 General Public License as published by @@ -15,73 +15,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Common flags -MYFLAGS = - -# 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)/argparse $(HDF5_CPPFLAGS) \ - $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS) $(OPENMP_CFLAGS) - -AM_LDFLAGS = $(HDF5_LDFLAGS) - -# Extra libraries. -EXTRA_LIBS = $(GSL_LIBS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) \ - $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) - -# MPI libraries. -MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS) $(FFTW_MPI_LIBS) -MPI_FLAGS = -DWITH_MPI $(PARMETIS_INCS) $(METIS_INCS) $(FFTW_MPI_INCS) - -# Programs. -bin_PROGRAMS = swift - -# Also build the FOF tool? -if HAVESTANDALONEFOF -bin_PROGRAMS += fof -endif - -# Do we have the CSDS? -if HAVECSDS -LD_CSDS = ../csds/src/.libs/libcsds_writer.a -else -LD_CSDS = -endif - -# Build MPI versions as well? -if HAVEMPI -bin_PROGRAMS += swift_mpi -if HAVESTANDALONEFOF -bin_PROGRAMS += fof_mpi -endif -endif - -# engine_policy_setaffinity is available? -if HAVESETAFFINITY -ENGINE_POLICY_SETAFFINITY=| engine_policy_setaffinity -else -ENGINE_POLICY_SETAFFINITY= -endif - -# Sources for swift -swift_SOURCES = main.c -swift_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -swift_LDADD = ../src/.libs/libswiftsim.a ../argparse/.libs/libargparse.a $(VELOCIRAPTOR_LIBS) $(EXTRA_LIBS) $(LD_CSDS) - -# Sources for swift_mpi, do we need an affinity policy for MPI? -swift_mpi_SOURCES = main.c -swift_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -swift_mpi_LDADD = ../src/.libs/libswiftsim_mpi.a ../argparse/.libs/libargparse.a $(MPI_LIBS) $(VELOCIRAPTOR_MPI_LIBS) $(EXTRA_LIBS) $(LD_CSDS) - -# Sources for fof -fof_SOURCES = main_fof.c -fof_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -fof_LDADD = ../src/.libs/libswiftsim.a ../argparse/.libs/libargparse.a $(VELOCIRAPTOR_LIBS) $(EXTRA_LIBS) $(LD_CSDS) - -# Sources for fof_mpi, do we need an affinity policy for MPI? -fof_mpi_SOURCES = main_fof.c -fof_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -fof_mpi_LDADD = ../src/.libs/libswiftsim_mpi.a ../argparse/.libs/libargparse.a $(MPI_LIBS) $(VELOCIRAPTOR_MPI_LIBS) $(EXTRA_LIBS) $(LD_CSDS) - # Scripts to generate ICs EXTRA_DIST = Cooling/CoolingBox/coolingBox.yml Cooling/CoolingBox/plotEnergy.py Cooling/CoolingBox/makeIC.py Cooling/CoolingBox/run.sh Cooling/CoolingBox/getGlass.sh \ Cosmology/ConstantCosmoVolume/run.sh Cosmology/ConstantCosmoVolume/makeIC.py Cosmology/ConstantCosmoVolume/plotSolution.py Cosmology/ConstantCosmoVolume/constant_volume.yml \ @@ -123,6 +56,9 @@ EXTRA_DIST = Cooling/CoolingBox/coolingBox.yml Cooling/CoolingBox/plotEnergy.py HydroTests/SodShock_2D/getGlass.sh HydroTests/SodShock_2D/makeIC.py HydroTests/SodShock_2D/plotSolution.py HydroTests/SodShock_2D/run.sh HydroTests/SodShock_2D/sodShock.yml \ HydroTests/SodShock_3D/getGlass.sh HydroTests/SodShock_3D/makeIC.py HydroTests/SodShock_3D/plotSolution.py HydroTests/SodShock_3D/run.sh HydroTests/SodShock_3D/sodShock.yml \ HydroTests/SquareTest_2D/makeIC.py HydroTests/SquareTest_2D/plotSolution.py HydroTests/SquareTest_2D/run.sh HydroTests/SquareTest_2D/square.yml \ + HydroTests/ToroTest2_1D/makeIC.py HydroTests/ToroTest2_1D/run.sh HydroTests/ToroTest2_1D/plotSolution.py HydroTests/ToroTest2_1D/toroTest2.yml \ + HydroTests/ToroTest2_2D/makeIC.py HydroTests/ToroTest2_2D/run.sh HydroTests/ToroTest2_2D/plotSolution.py HydroTests/ToroTest2_2D/toroTest2.yml HydroTests/ToroTest2_2D/getGlass.sh \ + HydroTests/ToroTest2_3D/makeIC.py HydroTests/ToroTest2_3D/run.sh HydroTests/ToroTest2_3D/plotSolution.py HydroTests/ToroTest2_3D/toroTest2.yml HydroTests/ToroTest2_3D/getGlass.sh \ HydroTests/UniformBox_2D/makeIC.py HydroTests/UniformBox_2D/run.sh HydroTests/UniformBox_2D/uniformPlane.yml \ HydroTests/UniformBox_3D/makeICbig.py HydroTests/UniformBox_3D/makeIC.py HydroTests/UniformBox_3D/run.sh HydroTests/UniformBox_3D/uniformBox.yml \ SmallCosmoVolume/SmallCosmoVolume_hydro/README SmallCosmoVolume/SmallCosmoVolume_hydro/getIC.sh SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py \ diff --git a/examples/Planetary/EarthImpact/earth_impact.yml b/examples/Planetary/EarthImpact/earth_impact.yml index 4833f9ffbf90b9478a65463122ea7d0dadcfb369..39f561770efce4fbd41183a7462371f79bd8b95e 100644 --- a/examples/Planetary/EarthImpact/earth_impact.yml +++ b/examples/Planetary/EarthImpact/earth_impact.yml @@ -15,7 +15,7 @@ InitialConditions: TimeIntegration: time_begin: 0 # The starting time of the simulation (in internal units). time_end: 36000 # The end time of the simulation (in internal units). - dt_min: 0.0001 # The minimal time-step size of the simulation (in internal units). + dt_min: 0.000001 # The minimal time-step size of the simulation (in internal units). dt_max: 1000 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots diff --git a/examples/Planetary/EarthImpact/run.sh b/examples/Planetary/EarthImpact/run.sh index 40ddcbb63a74751fc9a623e17a0d9bbe94360cb2..b73dea6e0c938ace0de0fe4fa82d5a6c1102fc2a 100755 --- a/examples/Planetary/EarthImpact/run.sh +++ b/examples/Planetary/EarthImpact/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../../swift -s -G -t 8 earth_impact.yml 2>&1 | tee output.log +../../../swift -s -G -t 8 earth_impact.yml 2>&1 | tee output.log # Plot the snapshots python3 plot_solution.py diff --git a/examples/Planetary/EvrardCollapse_3D/run.sh b/examples/Planetary/EvrardCollapse_3D/run.sh index e96abc6d590312700d41fd4f594bf57cb2da0d99..98e868bd1037d38251c0587d981f4364acc5d61b 100755 --- a/examples/Planetary/EvrardCollapse_3D/run.sh +++ b/examples/Planetary/EvrardCollapse_3D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../../swift --hydro --self-gravity --threads=4 evrard.yml 2>&1 | tee output.log +../../../swift --hydro --self-gravity --threads=4 evrard.yml 2>&1 | tee output.log # Get the high resolution 1D reference result if not present. if [ ! -e evrardCollapse3D_exact.txt ] diff --git a/examples/Planetary/GreshoVortex_3D/makeIC.py b/examples/Planetary/GreshoVortex_3D/makeIC.py index 011ab91e00425de3fec4b4758f7c7714629d6227..0259bc9303a920799612cae0347adaab4d7c0de8 100644 --- a/examples/Planetary/GreshoVortex_3D/makeIC.py +++ b/examples/Planetary/GreshoVortex_3D/makeIC.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/Planetary/GreshoVortex_3D/run.sh b/examples/Planetary/GreshoVortex_3D/run.sh index f834a3904670bc9beaa80d7f764c547aa0b25b09..06ca28c934ddbd64fe251ebced0e4f5d1f231c54 100755 --- a/examples/Planetary/GreshoVortex_3D/run.sh +++ b/examples/Planetary/GreshoVortex_3D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../../swift --hydro --threads=4 gresho.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 gresho.yml 2>&1 | tee output.log # Plot the solution python3 ../../HydroTests/GreshoVortex_3D/plotSolution.py 11 diff --git a/examples/Planetary/KelvinHelmholtz_2D/makeIC.py b/examples/Planetary/KelvinHelmholtz_2D/makeIC.py index d8b2ef480277d56c1a336c0ec419983bd2360c41..57f82ea9c4b750e804b1693bb18e56957fe8aac4 100644 --- a/examples/Planetary/KelvinHelmholtz_2D/makeIC.py +++ b/examples/Planetary/KelvinHelmholtz_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -42,9 +42,9 @@ numPart2 = L2 * L2 L1 = int(sqrt(numPart2 / rho2 * rho1)) numPart1 = L1 * L1 -# print "N2 =", numPart2, "N1 =", numPart1 -# print "L2 =", L2, "L1 = ", L1 -# print "rho2 =", rho2, "rho1 =", (float(L1*L1)) / (float(L2*L2)) +print("N2 =", numPart2, "N1 =", numPart1) +print("L2 =", L2, "L1 = ", L1) +print("rho2 =", rho2, "rho1 =", (float(L1 * L1)) / (float(L2 * L2))) coords1 = zeros((numPart1, 3)) coords2 = zeros((numPart2, 3)) @@ -110,8 +110,8 @@ vel[:, 1] = ( omega0 * sin(4 * pi * coords[:, 0]) * ( - exp(-(coords[:, 1] - 0.25) ** 2 / (2 * sigma ** 2)) - + exp(-(coords[:, 1] - 0.75) ** 2 / (2 * sigma ** 2)) + exp(-((coords[:, 1] - 0.25) ** 2) / (2 * sigma ** 2)) + + exp(-((coords[:, 1] - 0.75) ** 2) / (2 * sigma ** 2)) ) ) @@ -155,5 +155,4 @@ ds[()] = ids.reshape((numPart, 1)) ds = grp.create_dataset("MaterialIDs", (numPart, 1), "i") ds[()] = mat.reshape((numPart, 1)) - fileOutput.close() diff --git a/examples/Planetary/KelvinHelmholtz_2D/run.sh b/examples/Planetary/KelvinHelmholtz_2D/run.sh index 385dbb8b0a5039b95830727811a2fe823c3d49f6..da6121423688415d08dd1fdcd26bb457a5c8a9eb 100755 --- a/examples/Planetary/KelvinHelmholtz_2D/run.sh +++ b/examples/Planetary/KelvinHelmholtz_2D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../../swift --hydro --threads=4 kelvinHelmholtz.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 kelvinHelmholtz.yml 2>&1 | tee output.log # Plot the solution diff --git a/examples/Planetary/SodShock_3D/makeIC.py b/examples/Planetary/SodShock_3D/makeIC.py index 5ec1768643007d1df7791ef9b0e79719d2c06698..1953230c9c11727e377c454e2726f4e56cadb4a5 100644 --- a/examples/Planetary/SodShock_3D/makeIC.py +++ b/examples/Planetary/SodShock_3D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/Planetary/SodShock_3D/run.sh b/examples/Planetary/SodShock_3D/run.sh index 4b9bd2e2771e57036c59ed606ff69700916b9bb2..652d760b3023354fa1179a8bc4985664e2c132cc 100755 --- a/examples/Planetary/SodShock_3D/run.sh +++ b/examples/Planetary/SodShock_3D/run.sh @@ -13,6 +13,6 @@ then fi # Run SWIFT -../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log python3 ../../HydroTests/SodShock_3D/plotSolution.py 1 diff --git a/examples/Planetary/SquareTest_2D/makeIC.py b/examples/Planetary/SquareTest_2D/makeIC.py index 460ed629161f7520a94d56d4010102097e6ed7fb..6798050a225dd839cb50d11d6ea714c4baf9529a 100644 --- a/examples/Planetary/SquareTest_2D/makeIC.py +++ b/examples/Planetary/SquareTest_2D/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/Planetary/SquareTest_2D/makeICDifferentMasses.py b/examples/Planetary/SquareTest_2D/makeICDifferentMasses.py index 3545bd42ca0c8e11b9d963b95bcdf70f3b58c9e2..63dd2bf91438fa1d3de59e7ed8e915cfa2ac16ce 100644 --- a/examples/Planetary/SquareTest_2D/makeICDifferentMasses.py +++ b/examples/Planetary/SquareTest_2D/makeICDifferentMasses.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) -# 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2016 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 diff --git a/examples/Planetary/SquareTest_2D/run.sh b/examples/Planetary/SquareTest_2D/run.sh index 4095bbc909131f8f46fff413acb776319cd9f472..c62022fef2ae34ab8331b31a192538bf8a48f0ac 100755 --- a/examples/Planetary/SquareTest_2D/run.sh +++ b/examples/Planetary/SquareTest_2D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../../swift --hydro --threads=4 square.yml 2>&1 | tee output.log +../../../swift --hydro --threads=4 square.yml 2>&1 | tee output.log # Plot the solution python3 ../../HydroTests/SquareTest_2D/plotSolution.py 40 diff --git a/examples/QuickLymanAlpha/L050N0752/run.sh b/examples/QuickLymanAlpha/L050N0752/run.sh index d3a457632a9af7e34af4c06120394600cf09741e..6ba2a475d8f37c4e6aa0f00ae858691436887a5f 100755 --- a/examples/QuickLymanAlpha/L050N0752/run.sh +++ b/examples/QuickLymanAlpha/L050N0752/run.sh @@ -18,7 +18,7 @@ fi # Threading options - run with threads and pinning (latter not required but improves performance) # The corresponding parameter file for this run -../../swift \ +../../../swift \ --cosmology --quick-lyman-alpha \ --threads=16 --pin \ qla_50.yml diff --git a/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/README b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/README new file mode 100644 index 0000000000000000000000000000000000000000..686f50d9e943a9188f5bdf116338eb37f2573b07 --- /dev/null +++ b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/README @@ -0,0 +1,45 @@ +1D advection test for radiative transfer. +This test is identical to the examples/RadiativeTransferTests/Advection_1D +with the difference that particles aren't equally spaced on a line, but +separated into three regions with decreasing intervals between particles +in each of the region. + +More concretely, for a box of size 1, the separation is given as + +dx if x < 0.33 +dx/2 if 0.33 < x < 0.66 +dx/4 if x >= 0.66 + +This leads to the three regions having different time step sizes for RT. + + +Test that your method is TVD and the propagation speed of the photons is +correct. The ICs set up three photon groups: +- The first is a top hat function initial distribution where outside values + are zero. +- The second is a top hat function initial distribution where outside values + are nonzero. This distinction is important to test because photon energies + can't be negative, so these cases need to be tested individually. +- the third is a smooth Gaussian. + +This way, you can test multiple initial condition scenarios simultaneously. +There are no stars to act as sources. Also make sure that you choose your +photon frequencies in a way that doesn't interact with gas! + +The ICs are created to be compatible with GEAR_RT and SPHM1RT. Recommended configuration: +GEAR_RT: + --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro-dimension=1 --with-hydro=gizmo-mfv \ + --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + +SPHM1RT: + --with-rt=SPHM1RT_4 --with-hydro-dimension=1 --with-stars=basic --with-sundials=$SUNDIALS_ROOT + +IMPORTANT: Need SUNDIALS version = 5 . +SUNDIALS_ROOT is the root directory that contains the lib and include directories, e.g. on cosma: +SUNDIALS_ROOT=/cosma/local/sundials/5.1.0/ + +Note that in SPHM1RT any (SPH) hydro scheme is compatible. + +Note that if you want to use a reduced speed of light for this test, you also +need to adapt the fluxes in the initial conditions! They are generated assuming +that the speed of light is not reduced. diff --git a/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/makeIC.py b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/makeIC.py new file mode 100755 index 0000000000000000000000000000000000000000..8d61caffb0f9df90fa55c6addab1a4c078162b41 --- /dev/null +++ b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/makeIC.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## + + +# ----------------------------------------------------------- +# Add initial conditions for photon energies and fluxes +# for 1D advection of photons. +# First photon group: Top hat function with zero as the +# baseline. +# Second photon group: Top hat function with nonzero value +# as the baseline. +# Third photon group: Gaussian. +# ----------------------------------------------------------- + +import h5py +import numpy as np +import unyt +from swiftsimio import Writer + +# define unit system to use +unitsystem = unyt.unit_systems.cgs_unit_system + +# Box is 1 Mpc +boxsize = 1e10 * unitsystem["length"] + +# number of photon groups +nPhotonGroups = 3 + +# number of particles in each dimension +dx_init = boxsize / 1000 + +# filename of ICs to be generated +outputfilename = "advection_1D.hdf5" + + +def initial_condition(x): + """ + The initial conditions that will be advected + + x: particle position. 3D unyt array + + returns: + E: photon energy density for each photon group. List of scalars with size of nPhotonGroups + F: photon flux for each photon group. List with size of nPhotonGroups of numpy arrays of shape (3,) + """ + + # you can make the photon quantities unitless, the units will + # already have been written down in the writer. + + E_list = [] + F_list = [] + + # Group 1 Photons: + # ------------------- + + if x[0] < 0.33 * boxsize: + E = 0.0 + elif x[0] < 0.66 * boxsize: + E = 1.0 + else: + E = 0.0 + + # Assuming all photons flow in only one direction + # (optically thin regime, "free streaming limit"), + # we have that |F| = c * E + F = np.zeros(3, dtype=np.float64) + F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + + E_list.append(E) + F_list.append(F) + + # Group 2 Photons: + # ------------------- + + if x[0] < 0.33 * boxsize: + E = 1.0 + elif x[0] < 0.66 * boxsize: + E = 3.0 + else: + E = 1.0 + + F = np.zeros(3, dtype=np.float64) + F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + + E_list.append(E) + F_list.append(F) + + # Group 3 Photons: + # ------------------- + sigma = 0.1 * boxsize + mean = 0.5 * boxsize + amplitude = 2.0 + + E = amplitude * np.exp(-(x[0] - mean) ** 2 / (2 * sigma ** 2)) + F = np.zeros(3, dtype=np.float64) + F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + + E_list.append(E) + F_list.append(F) + + return E_list, F_list + + +if __name__ == "__main__": + + xp_list = [] + volumes_list = [] + + x = 0.5 * dx_init + while x < boxsize: + if x < 0.33 * boxsize: + dx = dx_init + elif 0.33 * boxsize <= x <= 0.66 * boxsize: + dx = dx_init / 2 + else: + dx = dx_init / 4 + x = x + dx + xp_list.append(x) + volumes_list.append(dx) + + n_p = len(xp_list) + xp = unyt.unyt_array(np.zeros((n_p, 3), dtype=np.float64), boxsize.units) + volumes = unyt.unyt_array(np.zeros((n_p), dtype=np.float64), boxsize.units ** 3) + for i in range(n_p): + xp[i, 0] = xp_list[i] + volumes[i] = volumes_list[i] + + w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, dimension=1) + + w.gas.coordinates = xp + w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) + w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 1000 * unyt.g + w.gas.internal_energy = ( + np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g + ) + + # Generate initial guess for smoothing lengths based on MIPS + w.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=1) + + # If IDs are not present, this automatically generates + w.write(outputfilename) + + # Now open file back up again and add photon groups + # you can make them unitless, the units have already been + # written down in the writer. In this case, it's in cgs. + + F = h5py.File(outputfilename, "r+") + header = F["Header"] + nparts = header.attrs["NumPart_ThisFile"][0] + parts = F["/PartType0"] + + for grp in range(nPhotonGroups): + dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1) + energydata = np.zeros((nparts), dtype=np.float32) + parts.create_dataset(dsetname, data=energydata) + + dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1) + # if dsetname not in parts.keys(): + fluxdata = np.zeros((nparts, 3), dtype=np.float32) + parts.create_dataset(dsetname, data=fluxdata) + + for p in range(nparts): + E, Flux = initial_condition(xp[p]) + for g in range(nPhotonGroups): + # Esetname = "PhotonEnergiesGroup{0:d}".format(g + 1) + # parts[Esetname][p] = E[g] / volumes[p] + # Fsetname = "PhotonFluxesGroup{0:d}".format(g + 1) + # parts[Fsetname][p] = Flux[g] / volumes[p] + # Esetname = "PhotonEnergiesGroup{0:d}".format(g + 1) + # parts[Esetname][p] = E[g] + # Fsetname = "PhotonFluxesGroup{0:d}".format(g + 1) + # parts[Fsetname][p] = Flux[g] + Esetname = "PhotonEnergiesGroup{0:d}".format(g + 1) + parts[Esetname][p] = E[g] * volumes[p] + Fsetname = "PhotonFluxesGroup{0:d}".format(g + 1) + parts[Fsetname][p] = Flux[g] * volumes[p] + + # from matplotlib import pyplot as plt + # plt.figure() + # for g in range(nPhotonGroups): + # # Esetname = "PhotonEnergiesGroup{0:d}".format(g+1) + # # plt.plot(xp[:,0], parts[Esetname], label="E "+str(g+1)) + # Fsetname = "PhotonFluxesGroup{0:d}".format(g+1) + # plt.plot(xp[:,0], parts[Fsetname][:,0], label="F "+str(g+1)) + # plt.legend() + # plt.show() + + F.close() diff --git a/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/plotSolution.py b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/plotSolution.py new file mode 100755 index 0000000000000000000000000000000000000000..fb6a962976e3e94f1fa37fb9dbe88d889f6eabd8 --- /dev/null +++ b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/plotSolution.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------- +# plot photon data assuming a 1D problem +# give snapshot number as cmdline arg to plot +# single snapshot, otherwise this script plots +# all snapshots available in the workdir +# ---------------------------------------------- + +import os +import sys + +import matplotlib as mpl +import numpy as np +import swiftsimio +from matplotlib import pyplot as plt + +# Parameters users should/may tweak +snapshot_base = "output" # snapshot basename +fancy = True # fancy up the plots a bit +plot_analytical_solutions = True # overplot analytical solution + +# properties for all scatterplots +scatterplot_kwargs = { + "facecolor": "red", + "s": 4, + "alpha": 0.6, + "linewidth": 0.0, + "marker": ".", +} + +# properties for all analytical solution plots +analytical_solution_kwargs = {"linewidth": 1.0, "ls": "--", "c": "k", "alpha": 0.5} + +# ----------------------------------------------------------------------- + +if plot_analytical_solutions: + from makeIC import initial_condition + +mpl.rcParams["text.usetex"] = True + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +plot_all = False # plot all snapshots +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + if plot_all: + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + else: + fname = snapshot_basename + "_" + str(snapnr).zfill(4) + ".hdf5" + if not os.path.exists(fname): + print("Didn't find file", fname) + quit(1) + snaplist.append(fname) + + return snaplist + + +def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): + """ + Create the actual plot. + + filename: file to work with + energy_boundaries: list of [E_min, E_max] for each photon group. + If none, limits are set automatically. + flux_boundaries: list of [F_min, F_max] for each photon group. + If none, limits are set automatically. + """ + + print("working on", filename) + + # Read in data firt + data = swiftsimio.load(filename) + meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + boxsize = meta.boxsize[0] + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + + for g in range(ngroups): + # workaround to access named columns data with swiftsimio visualisaiton + new_attribute_str = "radiation_energy" + str(g + 1) + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + setattr(data.gas, new_attribute_str, en) + + # prepare also the fluxes + for direction in ["X"]: + new_attribute_str = "radiation_flux" + str(g + 1) + direction + f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) + setattr(data.gas, new_attribute_str, f) + + part_positions = data.gas.coordinates[:, 0].copy() + + # get analytical solutions + if plot_analytical_solutions: + + time = meta.time + speed = meta.reduced_lightspeed + + advected_positions = data.gas.coordinates[:].copy() + advected_positions[:, 0] -= speed * time + nparts = advected_positions.shape[0] + # add periodicity corrections + negatives = advected_positions < 0.0 + if negatives.any(): + while advected_positions.min() < 0.0: + advected_positions[negatives] += boxsize + overshooters = advected_positions > boxsize + if overshooters.any(): + while advected_positions.max() > boxsize: + advected_positions[overshooters] -= boxsize + + analytical_solutions = np.zeros((nparts, ngroups), dtype=np.float64) + for p in range(part_positions.shape[0]): + E, F = initial_condition(advected_positions[p]) + for g in range(ngroups): + analytical_solutions[p, g] = E[g] + + fig = plt.figure(figsize=(5.05 * ngroups, 5.4), dpi=200) + figname = filename[:-5] + ".png" + + for g in range(ngroups): + + # plot energy density + new_attribute_str = "radiation_energy" + str(g + 1) + photon_energy = getattr(data.gas, new_attribute_str) + + volumes = data.gas.masses / data.gas.densities + photon_energy_density = photon_energy / volumes + + ax = fig.add_subplot(2, ngroups, g + 1) + s = np.argsort(part_positions) + if plot_analytical_solutions: + ax.plot( + part_positions[s], + analytical_solutions[s, g], + **analytical_solution_kwargs, + label="analytical solution", + ) + ax.scatter( + part_positions, + photon_energy_density, + **scatterplot_kwargs, + label="simulation", + ) + ax.legend() + + ax.set_title("Group {0:2d}".format(g + 1)) + if g == 0: + ax.set_ylabel( + "Energy Density [$" + + photon_energy_density.units.latex_representation() + + "$]" + ) + ax.set_xlabel("x [$" + part_positions.units.latex_representation() + "$]") + + # plot flux X + new_attribute_str = "radiation_flux" + str(g + 1) + "X" + photon_flux = getattr(data.gas, new_attribute_str) + + if scheme.startswith("SPH M1closure"): + photon_flux = photon_flux / volumes + + photon_flux = photon_flux.to("erg/cm**2/s") + + ax2 = fig.add_subplot(2, ngroups, g + 1 + ngroups) + ax2.scatter(part_positions, photon_flux, **scatterplot_kwargs) + + if g == 0: + ax2.set_ylabel( + "Flux X [$" + photon_flux.units.latex_representation() + "$]" + ) + ax2.set_xlabel("x [$" + part_positions.units.latex_representation() + "$]") + + # add title + title = filename.replace("_", r"\_") # exception handle underscore for latex + if meta.cosmology is not None: + title += ", $z$ = {0:.2e}".format(meta.z) + title += ", $t$ = {0:.2e}".format(meta.time) + fig.suptitle(title) + + plt.tight_layout() + plt.savefig(figname) + plt.close() + + return + + +if __name__ == "__main__": + + snaplist = get_snapshot_list(snapshot_base) + + for f in snaplist: + plot_photons(f) diff --git a/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/rt_advection1D.yml b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/rt_advection1D.yml new file mode 100644 index 0000000000000000000000000000000000000000..d34aabc03d321e595a3e882c852ebaf7e4539deb --- /dev/null +++ b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/rt_advection1D.yml @@ -0,0 +1,81 @@ +MetaData: + run_name: RT_advection-1D + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1. + UnitLength_in_cgs: 1. + UnitVelocity_in_cgs: 1. + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 4.e-1 # The end time of the simulation (in internal units). + dt_min: 1.e-8 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-02 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 4.e-2 + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 4.e-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.6 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./advection_1D.hdf5 # The file to read + periodic: 1 # periodic ICs + +Restarts: + delta_hours: 72 # (Optional) decimal hours between dumps of restart files. + +GEARRT: + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + f_limit_cooling_time: 0.0 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + CFL_condition: 0.4 # CFL condition for RT, independent of hydro + photon_groups_Hz: [0., 1., 2.] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [0., 0., 0.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + skip_thermochemistry: 1 # ignore thermochemistry. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + +SPHM1RT: + cred: 2.99792458e10 # reduce the speed of light in the code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + photon_groups_Hz: [1., 2.] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + skip_thermochemistry: 1 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 0.752 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.248 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + useabundances: 0 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 0.0 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 1.0 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 0.0 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.08244680851 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 0.0 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 0.0 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) diff --git a/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/run.sh b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..13e8331da60a1f432ff5c966c4f2da997bfff19a --- /dev/null +++ b/examples/RadiativeTransferTests/AdvectionDifferentTimeStepSizes_1D/run.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -f advection_1D.hdf5 ]; then + echo "Generating ICs" + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro \ + --threads=4 \ + --verbose=0 \ + --radiation \ + --stars \ + --feedback \ + --external-gravity \ + ./rt_advection1D.yml 2>&1 | tee output.log + +python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/Advection_1D/README b/examples/RadiativeTransferTests/Advection_1D/README index 3e1b85233cb9be64c53e09d421c6a426a52add1f..13dd7ce4caeeb00bd17b4998dffdbd92eacaee9c 100644 --- a/examples/RadiativeTransferTests/Advection_1D/README +++ b/examples/RadiativeTransferTests/Advection_1D/README @@ -13,8 +13,19 @@ This way, you can test multiple initial condition scenarios simultaneously. There are no stars to act as sources. Also make sure that you choose your photon frequencies in a way that doesn't interact with gas! -The ICs are created to be compatible with GEAR_RT. Recommended configuration: -`--with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro-dimension=1 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none` +The ICs are created to be compatible with GEAR_RT and SPHM1RT. Recommended configuration: +GEAR_RT: + --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro-dimension=1 --with-hydro=gizmo-mfv \ + --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + +SPHM1RT: + --with-rt=SPHM1RT_4 --with-hydro-dimension=1 --with-stars=basic --with-sundials=$SUNDIALS_ROOT + +IMPORTANT: Need SUNDIALS version = 5 . +SUNDIALS_ROOT is the root directory that contains the lib and include directories, e.g. on cosma: +SUNDIALS_ROOT=/cosma/local/sundials/5.1.0/ + +Note that in SPHM1RT any (SPH) hydro scheme is compatible. Note that if you want to use a reduced speed of light for this test, you also need to adapt the fluxes in the initial conditions! They are generated assuming diff --git a/examples/RadiativeTransferTests/Advection_1D/makeIC.py b/examples/RadiativeTransferTests/Advection_1D/makeIC.py index 508b96647d8360a76368090d14186e6e0c07d808..c3396255f456ec8ad124188c8f82172b545be5e3 100755 --- a/examples/RadiativeTransferTests/Advection_1D/makeIC.py +++ b/examples/RadiativeTransferTests/Advection_1D/makeIC.py @@ -3,6 +3,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) # # 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 @@ -30,12 +31,10 @@ # Third photon group: Gaussian. # ----------------------------------------------------------- -from swiftsimio import Writer - -import unyt -import numpy as np import h5py -from matplotlib import pyplot as plt +import numpy as np +import unyt +from swiftsimio import Writer # define unit system to use unitsystem = unyt.unit_systems.cgs_unit_system @@ -60,7 +59,7 @@ def initial_condition(x): x: particle position. 3D unyt array returns: - E: photon energy for each photon group. List of scalars with size of nPhotonGroups + E: photon energy density for each photon group. List of scalars with size of nPhotonGroups F: photon flux for each photon group. List with size of nPhotonGroups of numpy arrays of shape (3,) """ @@ -83,7 +82,7 @@ def initial_condition(x): # Assuming all photons flow in only one direction # (optically thin regime, "free streaming limit"), # we have that |F| = c * E - F = np.zeros(3, dtype=np.float32) + F = np.zeros(3, dtype=np.float64) F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E E_list.append(E) @@ -99,7 +98,7 @@ def initial_condition(x): else: E = 1.0 - F = np.zeros(3, dtype=np.float32) + F = np.zeros(3, dtype=np.float64) F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E E_list.append(E) @@ -112,7 +111,7 @@ def initial_condition(x): amplitude = 2.0 E = amplitude * np.exp(-(x[0] - mean) ** 2 / (2 * sigma ** 2)) - F = np.zeros(3, dtype=np.float32) + F = np.zeros(3, dtype=np.float64) F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E E_list.append(E) @@ -123,7 +122,7 @@ def initial_condition(x): if __name__ == "__main__": - xp = unyt.unyt_array(np.zeros((n_p, 3), dtype=np.float32), boxsize.units) + xp = unyt.unyt_array(np.zeros((n_p, 3), dtype=np.float64), boxsize.units) dx = boxsize / n_p @@ -134,9 +133,9 @@ if __name__ == "__main__": w.gas.coordinates = xp w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) - w.gas.masses = np.ones(xp.shape[0], dtype=np.float32) * 1000 * unyt.g + w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 1000 * unyt.g w.gas.internal_energy = ( - np.ones(xp.shape[0], dtype=np.float32) * (300.0 * unyt.kb * unyt.K) / (unyt.g) + np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g ) # Generate initial guess for smoothing lengths based on MIPS @@ -172,6 +171,7 @@ if __name__ == "__main__": Fsetname = "PhotonFluxesGroup{0:d}".format(g + 1) parts[Fsetname][p] = Flux[g] + # from matplotlib import pyplot as plt # plt.figure() # for g in range(nPhotonGroups): # # Esetname = "PhotonEnergiesGroup{0:d}".format(g+1) diff --git a/examples/RadiativeTransferTests/Advection_1D/plotSolution.py b/examples/RadiativeTransferTests/Advection_1D/plotSolution.py index 00becd2d8e2ac96df2b9299c90fb9ce0b3ba6d82..4feadac2a7e7c5371d89738739315ad1bcbe43b0 100755 --- a/examples/RadiativeTransferTests/Advection_1D/plotSolution.py +++ b/examples/RadiativeTransferTests/Advection_1D/plotSolution.py @@ -27,12 +27,13 @@ # all snapshots available in the workdir # ---------------------------------------------- -import sys import os -import swiftsimio +import sys + +import matplotlib as mpl import numpy as np +import swiftsimio from matplotlib import pyplot as plt -import matplotlib as mpl # Parameters users should/may tweak plot_all_data = True # plot all groups and all photon quantities @@ -109,10 +110,16 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): # Read in data firt data = swiftsimio.load(filename) meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) boxsize = meta.boxsize[0] ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + # Currently, SPHM1RT only works for frequency group = 4 in the code + # However, we only plot 3 frequency groups here, so + # we set ngroups = 3: + if scheme.startswith("SPH M1closure"): + ngroups = 3 for g in range(ngroups): # workaround to access named columns data with swiftsimio visualisaiton @@ -137,6 +144,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): advected_positions = data.gas.coordinates[:].copy() advected_positions[:, 0] -= speed * time + nparts = advected_positions.shape[0] # add periodicity corrections negatives = advected_positions < 0.0 if negatives.any(): @@ -147,9 +155,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): while advected_positions.max() > boxsize: advected_positions[overshooters] -= boxsize - analytical_solutions = np.zeros( - (part_positions.shape[0], ngroups), dtype=np.float - ) + analytical_solutions = np.zeros((nparts, ngroups), dtype=np.float64) for p in range(part_positions.shape[0]): E, F = initial_condition(advected_positions[p]) for g in range(ngroups): @@ -204,6 +210,13 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): # plot flux X new_attribute_str = "radiation_flux" + str(g + 1) + "X" photon_flux = getattr(data.gas, new_attribute_str) + if scheme.startswith("GEAR M1closure"): + photon_flux = photon_flux.to("erg/cm**2/s") + elif scheme.startswith("SPH M1closure"): + photon_flux = photon_flux.to("erg*cm/s") + else: + print("Error: Unknown RT scheme " + scheme) + exit() ax = fig.add_subplot(2, ngroups, g + 1 + ngroups) ax.scatter(part_positions, photon_flux, **scatterplot_kwargs) @@ -270,7 +283,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): ax.set_ylim(fixed_min, fixed_max) # add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: title += ", $z$ = {0:.2e}".format(meta.z) title += ", $t$ = {0:.2e}".format(meta.time) @@ -285,7 +298,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): def get_minmax_vals(snaplist): """ - Find minimal and maximal values for energy and flux + Find minimal and maximal values for energy and flux, so you can fix axes limits over all snapshots snaplist: list of snapshot filenames @@ -319,7 +332,6 @@ def get_minmax_vals(snaplist): for direction in ["X"]: # for direction in ["X", "Y", "Z"]: - new_attribute_str = "radiation_flux" + str(g + 1) + direction f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) fluxmin_group.append(f.min()) fluxmax_group.append(f.max()) diff --git a/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml b/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml index 2a0581e8926528327cdc28603ac3be7ba233e78a..a528dbeb1624e60679f23aecbde2de7a8376c296 100644 --- a/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml +++ b/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml @@ -1,5 +1,5 @@ MetaData: - run_name: "RT_advection-1D" + run_name: RT_advection-1D # Define the system of units to use internally. InternalUnitSystem: @@ -11,6 +11,7 @@ InternalUnitSystem: # Parameters governing the time integration TimeIntegration: + max_nr_rt_subcycles: 1 time_begin: 0. # The starting time of the simulation (in internal units). time_end: 4.e-1 # The end time of the simulation (in internal units). dt_min: 1.e-8 # The minimal time-step size of the simulation (in internal units). @@ -36,28 +37,45 @@ SPH: # Parameters related to the initial conditions InitialConditions: file_name: ./advection_1D.hdf5 # The file to read - periodic: 1 # peridoc ICs - -Scheduler: - max_top_level_cells: 32 + periodic: 1 # periodic ICs Restarts: delta_hours: 72 # (Optional) decimal hours between dumps of restart files. GEARRT: - f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.99 # CFL condition for RT, independent of hydro - photon_groups_Hz: [1., 2.] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter - star_emission_rates_LSol: [1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. - hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas - stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. - stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. - skip_thermochemistry: 1 # ignore thermochemistry. - set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) - mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value - mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value - mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value - mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value - mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + f_limit_cooling_time: 0.0 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + CFL_condition: 0.99 # CFL condition for RT, independent of hydro + photon_groups_Hz: [0., 1., 2.] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [0., 0., 0.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + skip_thermochemistry: 1 # ignore thermochemistry. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value +SPHM1RT: + cred: 2.99792458e10 # reduce the speed of light in the code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + photon_groups_Hz: [0., 1., 2.] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32,1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + skip_thermochemistry: 1 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 0.752 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.248 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + useabundances: 0 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 0.0 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 1.0 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 0.0 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.08244680851 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 0.0 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 0.0 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) diff --git a/examples/RadiativeTransferTests/Advection_1D/run.sh b/examples/RadiativeTransferTests/Advection_1D/run.sh index 07e531811c983aca11127eac6b08bf81c060535d..0b64a9b773f87b40c1237b91960bd09311da6c38 100755 --- a/examples/RadiativeTransferTests/Advection_1D/run.sh +++ b/examples/RadiativeTransferTests/Advection_1D/run.sh @@ -10,7 +10,7 @@ if [ ! -f advection_1D.hdf5 ]; then fi # Run SWIFT with RT -../../swift \ +../../../swift \ --hydro \ --threads=4 \ --verbose=0 \ @@ -18,7 +18,7 @@ fi --stars \ --feedback \ --external-gravity \ - -e \ + --fpe \ ./rt_advection1D.yml 2>&1 | tee output.log python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/Advection_2D/README b/examples/RadiativeTransferTests/Advection_2D/README index 7bc78038813de6c3b58d7fd97f75ad698c04ea90..5914f149225aa447a5bf3d651cf88a408de30722 100644 --- a/examples/RadiativeTransferTests/Advection_2D/README +++ b/examples/RadiativeTransferTests/Advection_2D/README @@ -16,7 +16,18 @@ There are no stars to act as sources. Also make sure that you choose your photon frequencies in a way that doesn't interact with gas! The ICs are created to be compatible with GEAR_RT. Recommended configuration: -`--with-rt=GEAR_4 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none` + --with-rt=GEAR_4 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=gizmo-mfv \ + --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + +SPHM1RT: + --with-rt=SPHM1RT_4 --with-hydro-dimension=2 --with-stars=basic --with-sundials=$SUNDIALS_ROOT + + +IMPORTANT: Need SUNDIALS version = 5 . +SUNDIALS_ROOT is the root directory that contains the lib and include directories, e.g. on cosma: +SUNDIALS_ROOT=/cosma/local/sundials/5.1.0/ + +Note that in SPHM1RT any (SPH) hydro scheme is compatible. Note that if you want to use a reduced speed of light for this test, you also need to adapt the fluxes in the initial conditions! They are generated assuming diff --git a/examples/RadiativeTransferTests/Advection_2D/makeIC.py b/examples/RadiativeTransferTests/Advection_2D/makeIC.py index 5e2d3507ac39d3176cdae469c4de7ad50b734e6a..e9742cbd6279129c696a6e5eb9230c29868870f4 100755 --- a/examples/RadiativeTransferTests/Advection_2D/makeIC.py +++ b/examples/RadiativeTransferTests/Advection_2D/makeIC.py @@ -3,6 +3,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) # # 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 @@ -31,17 +32,15 @@ # Fourth photon group: Circle moving radially from the center # ------------------------------------------------------------- -from swiftsimio import Writer -import unyt -import numpy as np import h5py -from matplotlib import pyplot as plt - +import numpy as np +import unyt +from swiftsimio import Writer # define unit system to use unitsystem = unyt.unit_systems.cgs_unit_system -# Box is 1 Mpc +# define box size boxsize = 1e10 * unitsystem["length"] # number of photon groups @@ -72,8 +71,8 @@ def initial_condition(x): # Group 1 Photons: # ------------------- - in_x = x[0] > 0.33 * boxsize and x[0] < 0.66 * boxsize - in_y = x[1] > 0.33 * boxsize and x[1] < 0.66 * boxsize + in_x = 0.33 * boxsize < x[0] < 0.66 * boxsize + in_y = 0.33 * boxsize < x[1] < 0.66 * boxsize if in_x and in_y: E = 1.0 else: @@ -82,7 +81,7 @@ def initial_condition(x): # Assuming all photons flow in only one direction # (optically thin regime, "free streaming limit"), # we have that |F| = c * E - F = np.zeros(3, dtype=np.float32) + F = np.zeros(3, dtype=np.float64) F[0] = c * E E_list.append(E) @@ -91,14 +90,14 @@ def initial_condition(x): # Group 2 Photons: # ------------------- - in_x = x[0] > 0.33 * boxsize and x[0] < 0.66 * boxsize - in_y = x[1] > 0.33 * boxsize and x[1] < 0.66 * boxsize + in_x = 0.33 * boxsize < x[0] < 0.66 * boxsize + in_y = 0.33 * boxsize < x[1] < 0.66 * boxsize if in_x and in_y: E = 2.0 else: E = 1.0 - F = np.zeros(3, dtype=np.float32) + F = np.zeros(3, dtype=np.float64) F[1] = c * E E_list.append(E) @@ -116,9 +115,9 @@ def initial_condition(x): * np.exp(-((x[0] - mean) ** 2 + (x[1] - mean) ** 2) / (2 * sigma ** 2)) + baseline ) - F = np.zeros(3, dtype=np.float32) - F[0] = c * E * 1.414213562 # sqrt(2) - F[1] = c * E * 1.414213562 # sqrt(2) + F = np.zeros(3, dtype=np.float64) + F[0] = c * E / 1.414213562 # sqrt(2) + F[1] = c * E / 1.414213562 # sqrt(2) E_list.append(E) F_list.append(F) @@ -135,13 +134,13 @@ def initial_condition(x): unit_vector = (dx / r, dy / r) E = 1.0 - F = np.zeros(3, dtype=np.float32) + F = np.zeros(3, dtype=np.float64) F[0] = unit_vector[0] * c * E F[1] = unit_vector[1] * c * E else: E = 0.0 - F = np.zeros(3, dtype=np.float32) + F = np.zeros(3, dtype=np.float64) E_list.append(E) F_list.append(F) @@ -166,9 +165,9 @@ if __name__ == "__main__": w.gas.coordinates = pos w.gas.velocities = np.zeros((numPart, 3)) * (unyt.cm / unyt.s) - w.gas.masses = np.ones(numPart, dtype=np.float32) * 1000 * unyt.g + w.gas.masses = np.ones(numPart, dtype=np.float64) * 1000 * unyt.g w.gas.internal_energy = ( - np.ones(numPart, dtype=np.float32) * (300.0 * unyt.kb * unyt.K) / (unyt.g) + np.ones(numPart, dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g ) # Generate initial guess for smoothing lengths based on MIPS @@ -188,7 +187,7 @@ if __name__ == "__main__": for grp in range(nPhotonGroups): dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1) - energydata = np.zeros((nparts), dtype=np.float32) + energydata = np.zeros(nparts, dtype=np.float32) parts.create_dataset(dsetname, data=energydata) dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1) diff --git a/examples/RadiativeTransferTests/Advection_2D/plotSolution.py b/examples/RadiativeTransferTests/Advection_2D/plotSolution.py index 433aa76160716a77a6e7e21c653df1e8fad4d62c..d0ebe4bf62817d989a72471246965cd461245d2e 100755 --- a/examples/RadiativeTransferTests/Advection_2D/plotSolution.py +++ b/examples/RadiativeTransferTests/Advection_2D/plotSolution.py @@ -26,13 +26,13 @@ # all snapshots available in the workdir # ---------------------------------------------------- -import sys +import gc import os +import sys + +import matplotlib as mpl import swiftsimio -import numpy as np -import gc from matplotlib import pyplot as plt -import matplotlib as mpl from mpl_toolkits.axes_grid1 import make_axes_locatable # Parameters users should/may tweak @@ -133,7 +133,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): f *= data.gas.masses setattr(data.gas, new_attribute_str, f) - # get mass surface density projection that we'll use to remove density dependence in impage + # get mass surface density projection that we'll use to remove density dependence in image mass_map = swiftsimio.visualisation.projection.project_gas( data, project="masses", **projection_kwargs ) @@ -220,7 +220,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): ax.set_ylabel("Energies") # Add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: title += ", $z$ = {0:.2e}".format(meta.z) title += ", $t$ = {0:.2e}".format(meta.time) @@ -236,7 +236,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): def get_minmax_vals(snaplist): """ - Find minimal and maximal values for energy and flux + Find minimal and maximal values for energy and flux, so you can fix axes limits over all snapshots snaplist: list of snapshot filenames @@ -271,7 +271,6 @@ def get_minmax_vals(snaplist): dirmin = [] dirmax = [] for direction in ["X", "Y"]: - new_attribute_str = "radiation_flux" + str(g + 1) + direction f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) dirmin.append(f.min()) dirmax.append(f.max()) diff --git a/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py b/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py index 3e8491ec5d709f11c271d7a9da6d0eb87f3270f9..3a4c37b3027720eb270283ef834f5296d9777c84 100755 --- a/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py +++ b/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py @@ -26,13 +26,13 @@ # all snapshots available in the workdir # ---------------------------------------------------- -import sys +import gc import os +import sys + +import matplotlib as mpl import swiftsimio -import numpy as np -import gc from matplotlib import pyplot as plt -import matplotlib as mpl # Parameters users should/may tweak plot_all_data = True # plot all groups and all photon quantities @@ -87,22 +87,11 @@ def get_snapshot_list(snapshot_basename="output"): return snaplist -def set_colorbar(ax, im): - divider = make_axes_locatable(ax) - cax = divider.append_axes("right", size="5%", pad=0.05) - plt.colorbar(im, cax=cax) - return - - -def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): +def plot_photons(filename): """ Create the actual plot. filename: file to work with - energy_boundaries: list of [E_min, E_max] for each photon group. - If none, limits are set automatically. - flux_boundaries: list of [F_min, F_max] for each photon group. - If none, limits are set automatically. """ print("working on", filename) @@ -124,7 +113,6 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): if plot_all_data: # prepare also the fluxes - # for direction in ["X", "Y", "Z"]: for direction in ["X", "Y"]: new_attribute_str = "radiation_flux" + str(g + 1) + direction f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) @@ -190,10 +178,10 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): ax.set_ylabel("Energies [$" + energy_units_str + "$]") # Add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: - title += ", $z$ = {0:.2e}".format(meta.z) - title += ", $t$ = {0:.2e}".format(meta.time) + title += r", $z$ = {0:.2e}".format(meta.z) + title += r", $t$ = {0:.2e}".format(meta.time) fig.suptitle(title) plt.tight_layout() @@ -204,78 +192,9 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): return -def get_minmax_vals(snaplist): - """ - Find minimal and maximal values for energy and flux - so you can fix axes limits over all snapshots - - snaplist: list of snapshot filenames - - returns: - - energy_boundaries: list of [E_min, E_max] for each photon group - flux_boundaries: list of [Fx_min, Fy_max] for each photon group - """ - - emins = [] - emaxs = [] - fmins = [] - fmaxs = [] - - for filename in snaplist: - - data = swiftsimio.load(filename) - meta = data.metadata - - ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) - emin_group = [] - emax_group = [] - fluxmin_group = [] - fluxmax_group = [] - - for g in range(ngroups): - en = getattr(data.gas.photon_energies, "group" + str(g + 1)) - emin_group.append(en.min()) - emax_group.append(en.max()) - - dirmin = [] - dirmax = [] - for direction in ["X", "Y"]: - new_attribute_str = "radiation_flux" + str(g + 1) + direction - f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) - dirmin.append(f.min()) - dirmax.append(f.max()) - fluxmin_group.append(min(dirmin)) - fluxmax_group.append(max(dirmax)) - - emins.append(emin_group) - emaxs.append(emax_group) - fmins.append(fluxmin_group) - fmaxs.append(fluxmax_group) - - energy_boundaries = [] - flux_boundaries = [] - for g in range(ngroups): - emin = min([emins[f][g] for f in range(len(snaplist))]) - emax = max([emaxs[f][g] for f in range(len(snaplist))]) - energy_boundaries.append([emin, emax]) - fmin = min([fmins[f][g] for f in range(len(snaplist))]) - fmax = max([fmaxs[f][g] for f in range(len(snaplist))]) - flux_boundaries.append([fmin, fmax]) - - return energy_boundaries, flux_boundaries - - if __name__ == "__main__": snaplist = get_snapshot_list(snapshot_base) - if fancy: - energy_boundaries, flux_boundaries = get_minmax_vals(snaplist) - else: - energy_boundaries = None - flux_boundaries = None for f in snaplist: - plot_photons( - f, energy_boundaries=energy_boundaries, flux_boundaries=flux_boundaries - ) + plot_photons(f) diff --git a/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml b/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml index 8c1b9d88f980109b1446960d0741f13015562e82..b3d7030f4ceb9afa7d0b0079a5fe8dc93a0f6596 100644 --- a/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml +++ b/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml @@ -1,5 +1,5 @@ MetaData: - run_name: "RT_advection-2D" + run_name: RT_advection-2D # Define the system of units to use internally. InternalUnitSystem: @@ -11,6 +11,7 @@ InternalUnitSystem: # Parameters governing the time integration TimeIntegration: + max_nr_rt_subcycles: 1 time_begin: 0. # The starting time of the simulation (in internal units). time_end: 3.4e-1 # The end time of the simulation (in internal units). dt_min: 1.e-08 # The minimal time-step size of the simulation (in internal units). @@ -29,40 +30,41 @@ Statistics: # Parameters for the hydrodynamics scheme SPH: - resolution_eta: 3.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + 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.6 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 10. # Kelvin # Parameters related to the initial conditions InitialConditions: file_name: ./advection_2D.hdf5 # The file to read - periodic: 1 # peridoc ICs + periodic: 1 # periodic ICs -Scheduler: - max_top_level_cells: 128 +GEARRT: + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + f_limit_cooling_time: 0.0 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + CFL_condition: 0.99 # CFL condition for RT, independent of hydro + photon_groups_Hz: [1., 2., 3., 4.] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1e-32, 1e-32, 1e-32, 1e-32] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + skip_thermochemistry: 1 # Skip thermochemsitry. + +SPHM1RT: + cred: 2.99792458e10 # reduce the speed of light in the code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + photon_groups_Hz: [1., 2., 3] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32, 1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + skip_thermochemistry: 1 # ignore thermochemistry. -# Parameters for the self-gravity scheme -Gravity: - mesh_side_length: 12 # Number of cells along each axis for the periodic gravity mesh. - eta: 0.025 # Constant dimensionless multiplier for time integration. - MAC: adaptive # Choice of mulitpole acceptance criterion: 'adaptive' OR 'geometric'. - epsilon_fmm: 0.001 # Tolerance parameter for the adaptive multipole acceptance criterion. - theta_cr: 0.7 # Opening angle for the purely gemoetric criterion. - max_physical_baryon_softening: 0.0007 # Maximal Plummer-equivalent softening length in physical coordinates for baryon particles (in internal units). -GEARRT: - f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.99 # CFL condition for RT, independent of hydro - photon_groups_Hz: [1., 2., 3] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter - star_emission_rates_LSol: [1e-32, 1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. - hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas - stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. - stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. - set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) - mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value - mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value - mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value - mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value - mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value - skip_thermochemistry: 1 # Skip thermochemsitry. diff --git a/examples/RadiativeTransferTests/Advection_2D/run.sh b/examples/RadiativeTransferTests/Advection_2D/run.sh index ec8b784fc2411ca9077009bd7dedf39ec7932668..56851dab7bec3104c985b77dde565557dc6ec995 100755 --- a/examples/RadiativeTransferTests/Advection_2D/run.sh +++ b/examples/RadiativeTransferTests/Advection_2D/run.sh @@ -13,11 +13,11 @@ fi if [ ! -e advection_2D.hdf5 ] then echo "Generating initial conditions for the 2D RT advection example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT with RT -../../swift \ +../../../swift \ --hydro \ --threads=4 \ --verbose=0 \ @@ -25,7 +25,7 @@ fi --stars \ --feedback \ --external-gravity \ - -e \ + --fpe \ ./rt_advection2D.yml 2>&1 | tee output.log python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/README b/examples/RadiativeTransferTests/CollidingBeams_1D/README new file mode 100644 index 0000000000000000000000000000000000000000..0aaee5bb32546bc83f47a0bbb1c9c3d9178fabea --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/README @@ -0,0 +1,13 @@ +Collide two photon packets with each other, and see what happens. +Two photon groups contain packets using a top hat functions. One of +them has the lower value at zero, the other is at nonzero. The third +photon group packets are Gaussians. +Ideally, they should just pass through each other. But that is not +what moment based methods do with radiation. + + +To run with GEAR-RT, compile SWIFT with + + --with-rt=GEAR_2 --with-rt-riemann-solver=GLF --with-hydro-dimension=1 --with-hydro=gizmo-mfv \ + --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/makeIC.py b/examples/RadiativeTransferTests/CollidingBeams_1D/makeIC.py new file mode 100755 index 0000000000000000000000000000000000000000..ecfd7a24df2557f895a6115adffe9a6721f37c51 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/makeIC.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## + + +# ----------------------------------------------------------- +# Add initial conditions for photon energies and fluxes +# for 1D advection of photons. +# First photon group: Top hat function with zero as the +# baseline. +# Second photon group: Top hat function with nonzero value +# as the baseline. +# Third photon group: Gaussian. +# ----------------------------------------------------------- + +import h5py +import numpy as np +import unyt +from swiftsimio import Writer + +# define unit system to use +unitsystem = unyt.unit_systems.cgs_unit_system + +# Box is 1 Mpc +boxsize = 1e10 * unitsystem["length"] + +# number of photon groups +nPhotonGroups = 3 + +# number of particles in each dimension +n_p = 1000 + +# filename of ICs to be generated +outputfilename = "collision_1D.hdf5" + + +def initial_condition(x): + """ + The initial conditions that will be advected + + x: particle position. 3D unyt array + + returns: + E: photon energy density for each photon group. List of scalars with size of nPhotonGroups + F: photon flux for each photon group. List with size of nPhotonGroups of numpy arrays of shape (3,) + """ + + # you can make the photon quantities unitless, the units will + # already have been written down in the writer. + + E_list = [] + F_list = [] + + # Group 1 Photons: + # ------------------- + + F = np.zeros(3, dtype=np.float64) + + if x[0] < 0.33 * boxsize: + E = 1.0 + F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + elif x[0] < 0.66 * boxsize: + E = 0.0 + F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + else: + E = 1.0 + F[0] = -unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + + E_list.append(E) + F_list.append(F) + + # Group 2 Photons: + # ------------------- + + F = np.zeros(3, dtype=np.float64) + if x[0] < 0.33 * boxsize: + E = 3.0 + F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + elif x[0] < 0.66 * boxsize: + E = 1.0 + if x[0] < 0.5 * boxsize: + F[0] = unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + else: + F[0] = -unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + else: + E = 3.0 + F[0] = -unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + + E_list.append(E) + F_list.append(F) + + # Group 3 Photons: + # ------------------- + sigma = 0.05 * boxsize + if x[0] < 0.5 * boxsize: + mean = 0.33 / 2 * boxsize + sign = 1 + else: + mean = (5.0 / 6.0) * boxsize + sign = -1 + amplitude = 2.0 + + E = amplitude * np.exp(-(x[0] - mean) ** 2 / (2 * sigma ** 2)) + F = np.zeros(3, dtype=np.float64) + F[0] = sign * unyt.c.to(unitsystem["length"] / unitsystem["time"]) * E + + E_list.append(E) + F_list.append(F) + + return E_list, F_list + + +if __name__ == "__main__": + + xp = unyt.unyt_array(np.zeros((n_p, 3), dtype=np.float64), boxsize.units) + + dx = boxsize / n_p + + for i in range(n_p): + xp[i, 0] = (i + 0.5) * dx + + w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, dimension=1) + + w.gas.coordinates = xp + w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) + w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 1000 * unyt.g + w.gas.internal_energy = ( + np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g + ) + + # Generate initial guess for smoothing lengths based on MIPS + w.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=1) + + # If IDs are not present, this automatically generates + w.write(outputfilename) + + # Now open file back up again and add photon groups + # you can make them unitless, the units have already been + # written down in the writer. In this case, it's in cgs. + + F = h5py.File(outputfilename, "r+") + header = F["Header"] + nparts = header.attrs["NumPart_ThisFile"][0] + parts = F["/PartType0"] + + for grp in range(nPhotonGroups): + dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1) + energydata = np.zeros((nparts), dtype=np.float32) + parts.create_dataset(dsetname, data=energydata) + + dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1) + # if dsetname not in parts.keys(): + fluxdata = np.zeros((nparts, 3), dtype=np.float32) + parts.create_dataset(dsetname, data=fluxdata) + + for p in range(nparts): + E, Flux = initial_condition(xp[p]) + for g in range(nPhotonGroups): + Esetname = "PhotonEnergiesGroup{0:d}".format(g + 1) + parts[Esetname][p] = E[g] + Fsetname = "PhotonFluxesGroup{0:d}".format(g + 1) + parts[Fsetname][p] = Flux[g] + + # from matplotlib import pyplot as plt + # plt.figure() + # for g in range(nPhotonGroups): + # Esetname = "PhotonEnergiesGroup{0:d}".format(g+1) + # plt.plot(xp[:,0], parts[Esetname], label="E "+str(g+1)) + # # Fsetname = "PhotonFluxesGroup{0:d}".format(g+1) + # # plt.plot(xp[:,0], parts[Fsetname][:,0], label="F "+str(g+1)) + # plt.legend() + # plt.show() + + F.close() diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/plotEnergies.py b/examples/RadiativeTransferTests/CollidingBeams_1D/plotEnergies.py new file mode 100755 index 0000000000000000000000000000000000000000..4d5285467455e775e225a02a05378060070599c0 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/plotEnergies.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------- +# Plot the total photon energies in each photon +# group over the course of several snapshots +# ---------------------------------------------- + +import os +import sys + +import matplotlib as mpl +import numpy as np +import swiftsimio +from matplotlib import pyplot as plt + +# Parameters users should/may tweak +snapshot_base = "output" # snapshot basename + + +# ----------------------------------------------------------------------- + +mpl.rcParams["text.usetex"] = True + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +plot_all = False # plot all snapshots +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list """ + + snaplist = [] + + if plot_all: + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + else: + fname = snapshot_basename + "_" + str(snapnr).zfill(4) + ".hdf5" + if not os.path.exists(fname): + print("Didn't find file", fname) + quit(1) + snaplist.append(fname) + + return snaplist + + +def plot_energies(snap_nrs, energy_sums): + """ + Create the actual plot. + """ + + # Plot plot plot! + fig = plt.figure(figsize=(5.0, 5.4), dpi=200) + figname = "energy_budget.png" + + ax1 = fig.add_subplot(1, 1, 1) + + ngroups = energy_sums.shape[1] + + for g in range(ngroups): + ax1.plot(snap_nrs, energy_sums[:, g], label=None) + ax1.scatter(snap_nrs, energy_sums[:, g], label="group {0:d}".format(g + 1)) + + ax1.set_xlabel("Snapshot") + ax1.set_ylabel( + r"Total energy [$" + energy_sums.units.latex_representation() + "$]", + usetex=True, + ) + + # add title + title = "Energy Budget" + ax1.set_title(title) + ax1.legend() + + plt.tight_layout() + plt.savefig(figname) + plt.close() + + return + + +def get_photon_energies(snaplist): + """ + Get total photon energy in each photon group for a list of + snapshots specified by `snaplist` + + snaplist: list of snapshot filenames + + returns: + + snap_nrs : list of integers of snapshot numbers + energy_sums: np.array(shape=(len snaplist, ngroups)) of + total photon energies per group per snapshot + """ + + data = swiftsimio.load(snaplist[0]) + meta = data.metadata + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + + energy_sums = np.zeros((len(snaplist), ngroups)) + snap_nrs = np.zeros(len(snaplist), dtype=int) + + for f, filename in enumerate(snaplist): + + data = swiftsimio.load(filename) + + for g in range(ngroups): + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + energy_sums[f, g] = en.sum() + + nrstring = filename[len(snapshot_base) + 1 : -len(".hdf5")] + nr = int(nrstring) + snap_nrs[f] = nr + + energy_sums = energy_sums * en.units + + sortind = np.argsort(snap_nrs) + snap_nrs = snap_nrs[sortind] + energy_sums = energy_sums[sortind] + + return snap_nrs, energy_sums + + +if __name__ == "__main__": + + snaplist = get_snapshot_list(snapshot_base) + snap_nrs, energy_sums = get_photon_energies(snaplist) + + plot_energies(snap_nrs, energy_sums) diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/plotSolution.py b/examples/RadiativeTransferTests/CollidingBeams_1D/plotSolution.py new file mode 100755 index 0000000000000000000000000000000000000000..5e372d67976336086dac3579a524790242d238e8 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/plotSolution.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3 + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------- +# plot photon data assuming a 1D problem +# give snapshot number as cmdline arg to plot +# single snapshot, otherwise this script plots +# all snapshots available in the workdir +# ---------------------------------------------- + +import os +import sys + +import matplotlib as mpl +import numpy as np +import swiftsimio +from matplotlib import pyplot as plt + +# Parameters users should/may tweak +snapshot_base = "output" # snapshot basename + +# properties for all scatterplots +scatterplot_kwargs = { + "facecolor": "red", + "s": 4, + "alpha": 0.6, + "linewidth": 0.0, + "marker": ".", +} + +# ----------------------------------------------------------------------- + +mpl.rcParams["text.usetex"] = True + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +plot_all = False # plot all snapshots +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + if plot_all: + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + else: + fname = snapshot_basename + "_" + str(snapnr).zfill(4) + ".hdf5" + if not os.path.exists(fname): + print("Didn't find file", fname) + quit(1) + snaplist.append(fname) + + return snaplist + + +def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): + """ + Create the actual plot. + + filename: file to work with + energy_boundaries: list of [E_min, E_max] for each photon group. + If none, limits are set automatically. + flux_boundaries: list of [F_min, F_max] for each photon group. + If none, limits are set automatically. + """ + + print("working on", filename) + + # Read in data firt + data = swiftsimio.load(filename) + meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + + for g in range(ngroups): + # workaround to access named columns data with swiftsimio visualisaiton + new_attribute_str = "radiation_energy" + str(g + 1) + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + setattr(data.gas, new_attribute_str, en) + + # prepare also the fluxes + for direction in ["X"]: + new_attribute_str = "radiation_flux" + str(g + 1) + direction + f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) + setattr(data.gas, new_attribute_str, f) + + part_positions = data.gas.coordinates[:, 0].copy() + + # Plot plot plot! + fig = plt.figure(figsize=(5.0 * ngroups, 5.4), dpi=200) + figname = filename[:-5] + "-all-quantities.png" + + for g in range(ngroups): + + # plot energy + new_attribute_str = "radiation_energy" + str(g + 1) + photon_energy = getattr(data.gas, new_attribute_str) + + ax = fig.add_subplot(2, ngroups, g + 1) + + ax.scatter( + part_positions, photon_energy, **scatterplot_kwargs, label="simulation" + ) + ax.legend() + + ax.set_title("Group {0:2d}".format(g + 1)) + if g == 0: + ax.set_ylabel( + "Energies [$" + photon_energy.units.latex_representation() + "$]" + ) + ax.set_xlabel("x [$" + part_positions.units.latex_representation() + "$]") + + if energy_boundaries is not None: + + if abs(energy_boundaries[g][1]) > abs(energy_boundaries[g][0]): + fixed_min = energy_boundaries[g][0] - 0.1 * abs(energy_boundaries[g][1]) + fixed_max = energy_boundaries[g][1] * 1.1 + else: + fixed_min = energy_boundaries[g][0] * 1.1 + fixed_max = energy_boundaries[g][1] + 0.1 * abs(energy_boundaries[g][0]) + ax.set_ylim(fixed_min, fixed_max) + + # plot flux X + new_attribute_str = "radiation_flux" + str(g + 1) + "X" + photon_flux = getattr(data.gas, new_attribute_str) + if scheme.startswith("GEAR M1closure"): + photon_flux = photon_flux.to("erg/cm**2/s") + elif scheme.startswith("SPH M1closure"): + photon_flux = photon_flux.to("erg*cm/s") + else: + print("Error: Unknown RT scheme " + scheme) + exit() + + ax = fig.add_subplot(2, ngroups, g + 1 + ngroups) + ax.scatter(part_positions, photon_flux, **scatterplot_kwargs) + + if g == 0: + ax.set_ylabel("Flux X [$" + photon_flux.units.latex_representation() + "$]") + ax.set_xlabel("x [$" + part_positions.units.latex_representation() + "$]") + + if flux_boundaries is not None: + + if abs(flux_boundaries[g][1]) > abs(flux_boundaries[g][0]): + fixed_min = flux_boundaries[g][0] - 0.1 * abs(flux_boundaries[g][1]) + fixed_max = flux_boundaries[g][1] * 1.1 + else: + fixed_min = flux_boundaries[g][0] * 1.1 + fixed_max = flux_boundaries[g][1] + 0.1 * abs(flux_boundaries[g][0]) + + ax.set_ylim(fixed_min, fixed_max) + + # add title + title = filename.replace("_", r"\_") # exception handle underscore for latex + if meta.cosmology is not None: + title += ", $z$ = {0:.2e}".format(meta.z) + title += ", $t$ = {0:.2e}".format(meta.time) + fig.suptitle(title) + + plt.tight_layout() + plt.savefig(figname) + plt.close() + + return + + +def get_minmax_vals(snaplist): + """ + Find minimal and maximal values for energy and flux, + so you can fix axes limits over all snapshots + + snaplist: list of snapshot filenames + + returns: + + energy_boundaries: list of [E_min, E_max] for each photon group + flux_boundaries: list of [Fx_min, Fy_max] for each photon group + """ + + emins = [] + emaxs = [] + fmins = [] + fmaxs = [] + + for filename in snaplist: + + data = swiftsimio.load(filename) + meta = data.metadata + + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + emin_group = [] + emax_group = [] + fluxmin_group = [] + fluxmax_group = [] + + for g in range(ngroups): + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + emin_group.append(en.min()) + emax_group.append(en.max()) + + for direction in ["X"]: + f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) + fluxmin_group.append(f.min()) + fluxmax_group.append(f.max()) + + emins.append(emin_group) + emaxs.append(emax_group) + fmins.append(fluxmin_group) + fmaxs.append(fluxmax_group) + + energy_boundaries = [] + flux_boundaries = [] + for g in range(ngroups): + emin = min([emins[f][g] for f in range(len(snaplist))]) + emax = max([emaxs[f][g] for f in range(len(snaplist))]) + energy_boundaries.append([emin, emax]) + fmin = min([fmins[f][g] for f in range(len(snaplist))]) + fmax = max([fmaxs[f][g] for f in range(len(snaplist))]) + flux_boundaries.append([fmin, fmax]) + + return energy_boundaries, flux_boundaries + + +if __name__ == "__main__": + + snaplist = get_snapshot_list(snapshot_base) + energy_boundaries, flux_boundaries = get_minmax_vals(snaplist) + + for f in snaplist: + plot_photons( + f, energy_boundaries=energy_boundaries, flux_boundaries=flux_boundaries + ) diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/rt_collision1D.yml b/examples/RadiativeTransferTests/CollidingBeams_1D/rt_collision1D.yml new file mode 100644 index 0000000000000000000000000000000000000000..25f24418dada5071893d5b8bc1679dfd77c4b614 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/rt_collision1D.yml @@ -0,0 +1,62 @@ +MetaData: + run_name: RT_collision-1D + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1. + UnitLength_in_cgs: 1. + UnitVelocity_in_cgs: 1. + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 4.e-1 # The end time of the simulation (in internal units). + dt_min: 1.e-8 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-02 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 2.e-2 + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 4.e-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.6 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./collision_1D.hdf5 # The file to read + periodic: 1 # periodic ICs + +Restarts: + delta_hours: 72 # (Optional) decimal hours between dumps of restart files. + +GEARRT: + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + f_limit_cooling_time: 0.0 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + CFL_condition: 0.8 # CFL condition for RT, independent of hydro + photon_groups_Hz: [0., 1., 2.] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [0., 0., 0.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + skip_thermochemistry: 1 # ignore thermochemistry. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + diff --git a/examples/RadiativeTransferTests/CollidingBeams_1D/run.sh b/examples/RadiativeTransferTests/CollidingBeams_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..1d121bcc7eeefbb6e257057f78cae61e1981cf58 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_1D/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -f collision_1D.hdf5 ]; then + echo "Generating ICs" + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro \ + --threads=1 \ + --verbose=0 \ + --radiation \ + --stars \ + --feedback \ + --external-gravity \ + ./rt_collision1D.yml 2>&1 | tee output.log + +python3 ./plotEnergies.py +python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/README b/examples/RadiativeTransferTests/CollidingBeams_2D/README new file mode 100644 index 0000000000000000000000000000000000000000..7bc95ce3b54b0281536b7c1fea3bbbfce267e4e9 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/README @@ -0,0 +1,10 @@ +Collide packets of radiation against each other, and see how the M1 +closure treats them. + +Collide them horizontally, vertically, diagonally, and from the side. + +To run with GEAR-RT, configure SWIFT using + + --with-rt=GEAR_4 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=gizmo-mfv \ + --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/getGlass.sh b/examples/RadiativeTransferTests/CollidingBeams_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ae3c977064f5e7a408aa249c5fd9089b3c52ecb1 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/makeIC.py b/examples/RadiativeTransferTests/CollidingBeams_2D/makeIC.py new file mode 100755 index 0000000000000000000000000000000000000000..be2d50a7bb2a6503195a0d6c700f3ee6615091de --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/makeIC.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ------------------------------------------------------------- +# Add initial conditions for photon energies and fluxes +# for 2D advection of photons. +# First photon group: Top hat function with zero as the +# baseline, advects in x direction +# Second photon group: Top hat function with nonzero value +# as the baseline, advcts in y direction. +# Third photon group: Gaussian advecting diagonally +# Fourth photon group: Circle moving radially from the center +# ------------------------------------------------------------- + +import h5py +import numpy as np +import unyt +from swiftsimio import Writer + +# define unit system to use +unitsystem = unyt.unit_systems.cgs_unit_system + +# define box size +boxsize = 1e10 * unitsystem["length"] + +# number of photon groups +nPhotonGroups = 4 + +# filename of ICs to be generated +outputfilename = "collision_2D.hdf5" + + +def get_radiation_IC(pos, numPart, ngroups): + + Elist = [] + Flist = [] + + x = pos[:, 0] + y = pos[:, 1] + c = unyt.c.to(unitsystem["length"] / unitsystem["time"]) + + for g in range(ngroups): + + E = np.zeros(numPart) + F = np.zeros((numPart, 3)) + + # prepare particle array masks + flux_sign = np.ones(numPart, dtype=float) + + if g == 0: + # horizontal beams + # ----------------------- + + left = np.logical_and(x > 0.0, x < 1.0 / 3) + right = np.logical_and(x > 2.0 / 3, x < 1.0) + xcriterion = np.logical_or(left, right) + ycriterion = np.logical_and(y > 0.4, y < 0.6) + is_max = np.logical_and(xcriterion, ycriterion) + + flux_sign[right] = -1.0 + + Emax = 1.0 + E[is_max] = Emax + F[is_max, 0] = Emax * c * flux_sign[is_max] + + if g == 1: + # vertical beams, nonzero energy everywhere + # ------------------------------------------- + + top = np.logical_and(y > 2.0 / 3.0, y < 1.0) + bottom = np.logical_and(y > 0.0, y < 1.0 / 3.0) + + xcriterion = np.logical_and(x > 0.4, x < 0.6) + ycriterion = np.logical_or(top, bottom) + is_max = np.logical_and(xcriterion, ycriterion) + + flux_sign[y > 0.5] = -1.0 + + Emax = 2.0 + Emin = 1.0 + E[:] = Emin + E[is_max] = Emax + F[:, 1] = E * c * flux_sign + + if g == 2: + # diagonal beams + # ------------------------------------------- + + width = 0.1 + l = np.sqrt(2) / 3.0 + sin = np.sin(np.pi / 4) + xprime = l * sin + x0 = xprime - 0.5 * width * sin + x1 = xprime + 0.5 * width * sin + x2 = 1 - xprime - 0.5 * width * sin + x3 = 1 - xprime + 0.5 * width * sin + + def upper_line(x): + return x + 0.5 * width + + def lower_line(x): + return x - 0.5 * width + + def descending_left(x, xprime): + return -(x - xprime) + xprime + + def descending_right(x, xprime): + return -(x - 1.0 + xprime) + 1.0 - xprime + + first = x < x0 + lower_first = np.logical_and(y < upper_line(x), y > lower_line(x)) + firstcond = np.logical_and(first, lower_first) + + second = np.logical_and(x > x0, x < x1) + lower_second = np.logical_and( + y > lower_line(x), y < descending_left(x, xprime) + ) + secondcond = np.logical_and(second, lower_second) + + third = np.logical_and(x > x2, x < x3) + upper_third = np.logical_and( + y < upper_line(x), y > descending_right(x, xprime) + ) + thirdcond = np.logical_and(third, upper_third) + + fourth = np.logical_and(x > x3, x < 1.0) + upper_fourth = np.logical_and(y < upper_line(x), y > lower_line(x)) + fourthcond = np.logical_and(fourth, upper_fourth) + + Emax = 1.0 + E[firstcond] = Emax + E[secondcond] = Emax + E[thirdcond] = Emax + E[fourthcond] = Emax + + flux_sign[thirdcond] = -1.0 + flux_sign[fourthcond] = -1.0 + + F[:, 0] = E * c * flux_sign / np.sqrt(2) + F[:, 1] = E * c * flux_sign / np.sqrt(2) + + if g == 3: + # diagonal beams that meed in the middle + # ------------------------------------------- + + width = 0.1 + l = np.sqrt(2) / 3.0 + sin = np.sin(np.pi / 4) + xprime = l * sin + x0 = xprime - 0.5 * width * sin + x1 = xprime + 0.5 * width * sin + x2 = 1 - xprime - 0.5 * width * sin + x3 = 1 - xprime + 0.5 * width * sin + + def upper_line(x): + return x + 0.5 * width + + def lower_line(x): + return x - 0.5 * width + + def descending_left(x, xprime): + return -(x - xprime) + xprime + + def descending_lower(x, xprime): + return -(x - 1 + xprime) + xprime - 0.5 * width + + def descending_upper(x, xprime): + return -(x - 1.0 + xprime) + xprime + 0.5 * width + + def ascending_right(x, xprime): + return (x - 1 + xprime) + xprime + + first = x < x0 + lower_first = np.logical_and(y < upper_line(x), y > lower_line(x)) + firstcond = np.logical_and(first, lower_first) + + second = np.logical_and(x > x0, x < x1) + lower_second = np.logical_and( + y > lower_line(x), y < descending_left(x, xprime) + ) + secondcond = np.logical_and(second, lower_second) + + third = np.logical_and(x > x2, x < x3) + upper_third = np.logical_and( + y < ascending_right(x, xprime), y > descending_lower(x, xprime) + ) + thirdcond = np.logical_and(third, upper_third) + + fourth = np.logical_and(x > x3, x < 1.0) + upper_fourth = np.logical_and( + y < descending_upper(x, xprime), y > descending_lower(x, xprime) + ) + fourthcond = np.logical_and(fourth, upper_fourth) + + Emax = 1.0 + E[firstcond] = Emax + E[secondcond] = Emax + E[thirdcond] = Emax + E[fourthcond] = Emax + + flux_sign[thirdcond] = -1.0 + flux_sign[fourthcond] = -1.0 + + F[:, 0] = E * c * flux_sign / np.sqrt(2) + F[:, 1] = E * c / np.sqrt(2) + + # histE, _, _ = np.histogram2d(pos[:,0], pos[:,1], weights=E, bins=50) + # histFx, _, _ = np.histogram2d(pos[:,0], pos[:,1], weights=F[:,0], bins=50) + # histFy, _, _ = np.histogram2d(pos[:,0], pos[:,1], weights=F[:,1], bins=50) + # from matplotlib import pyplot as plt + # + # fig = plt.figure() + # ax1 = fig.add_subplot(1, 3, 1) + # ax1.imshow(histE.T, origin="lower") + # ax2 = fig.add_subplot(1, 3, 2) + # ax2.imshow(histFx.T, origin="lower") + # ax3 = fig.add_subplot(1, 3, 3) + # ax3.imshow(histFy.T, origin="lower") + # plt.show() + + Elist.append(E) + Flist.append(F) + + return Elist, Flist + + +if __name__ == "__main__": + glass = h5py.File("glassPlane_128.hdf5", "r") + + # Read particle positions and h from the glass + pos = glass["/PartType0/Coordinates"][:, :] + h = glass["/PartType0/SmoothingLength"][:] + glass.close() + + numPart = np.size(h) + + # get radiation IC while particle coordinates are still [0, 1) + Elist, Flist = get_radiation_IC(pos, numPart, nPhotonGroups) + + pos *= boxsize + h *= boxsize + + w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, dimension=2) + + w.gas.coordinates = pos + w.gas.velocities = np.zeros((numPart, 3)) * (unyt.cm / unyt.s) + w.gas.masses = np.ones(numPart, dtype=np.float64) * 1000 * unyt.g + w.gas.internal_energy = ( + np.ones(numPart, dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g + ) + + # Generate initial guess for smoothing lengths based on MIPS + w.gas.smoothing_length = h + + # If IDs are not present, this automatically generates + w.write(outputfilename) + + # Now open file back up again and add photon groups + # you can make them unitless, the units have already been + # written down in the writer. In this case, it's in cgs. + + F = h5py.File(outputfilename, "r+") + header = F["Header"] + nparts = header.attrs["NumPart_ThisFile"][0] + parts = F["/PartType0"] + + for grp in range(nPhotonGroups): + dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1) + energydata = np.zeros(nparts, dtype=np.float32) + parts.create_dataset(dsetname, data=energydata) + + dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1) + # if dsetname not in parts.keys(): + fluxdata = np.zeros((nparts, 3), dtype=np.float32) + parts.create_dataset(dsetname, data=fluxdata) + + for g in range(nPhotonGroups): + Esetname = "PhotonEnergiesGroup{0:d}".format(g + 1) + parts[Esetname][:] = Elist[g][:] + Fsetname = "PhotonFluxesGroup{0:d}".format(g + 1) + parts[Fsetname][:, :] = Flist[g][:, :] + + F.close() diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/plotEnergies.py b/examples/RadiativeTransferTests/CollidingBeams_2D/plotEnergies.py new file mode 100755 index 0000000000000000000000000000000000000000..4d5285467455e775e225a02a05378060070599c0 --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/plotEnergies.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------- +# Plot the total photon energies in each photon +# group over the course of several snapshots +# ---------------------------------------------- + +import os +import sys + +import matplotlib as mpl +import numpy as np +import swiftsimio +from matplotlib import pyplot as plt + +# Parameters users should/may tweak +snapshot_base = "output" # snapshot basename + + +# ----------------------------------------------------------------------- + +mpl.rcParams["text.usetex"] = True + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +plot_all = False # plot all snapshots +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list """ + + snaplist = [] + + if plot_all: + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + else: + fname = snapshot_basename + "_" + str(snapnr).zfill(4) + ".hdf5" + if not os.path.exists(fname): + print("Didn't find file", fname) + quit(1) + snaplist.append(fname) + + return snaplist + + +def plot_energies(snap_nrs, energy_sums): + """ + Create the actual plot. + """ + + # Plot plot plot! + fig = plt.figure(figsize=(5.0, 5.4), dpi=200) + figname = "energy_budget.png" + + ax1 = fig.add_subplot(1, 1, 1) + + ngroups = energy_sums.shape[1] + + for g in range(ngroups): + ax1.plot(snap_nrs, energy_sums[:, g], label=None) + ax1.scatter(snap_nrs, energy_sums[:, g], label="group {0:d}".format(g + 1)) + + ax1.set_xlabel("Snapshot") + ax1.set_ylabel( + r"Total energy [$" + energy_sums.units.latex_representation() + "$]", + usetex=True, + ) + + # add title + title = "Energy Budget" + ax1.set_title(title) + ax1.legend() + + plt.tight_layout() + plt.savefig(figname) + plt.close() + + return + + +def get_photon_energies(snaplist): + """ + Get total photon energy in each photon group for a list of + snapshots specified by `snaplist` + + snaplist: list of snapshot filenames + + returns: + + snap_nrs : list of integers of snapshot numbers + energy_sums: np.array(shape=(len snaplist, ngroups)) of + total photon energies per group per snapshot + """ + + data = swiftsimio.load(snaplist[0]) + meta = data.metadata + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + + energy_sums = np.zeros((len(snaplist), ngroups)) + snap_nrs = np.zeros(len(snaplist), dtype=int) + + for f, filename in enumerate(snaplist): + + data = swiftsimio.load(filename) + + for g in range(ngroups): + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + energy_sums[f, g] = en.sum() + + nrstring = filename[len(snapshot_base) + 1 : -len(".hdf5")] + nr = int(nrstring) + snap_nrs[f] = nr + + energy_sums = energy_sums * en.units + + sortind = np.argsort(snap_nrs) + snap_nrs = snap_nrs[sortind] + energy_sums = energy_sums[sortind] + + return snap_nrs, energy_sums + + +if __name__ == "__main__": + + snaplist = get_snapshot_list(snapshot_base) + snap_nrs, energy_sums = get_photon_energies(snaplist) + + plot_energies(snap_nrs, energy_sums) diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/plotSolution.py b/examples/RadiativeTransferTests/CollidingBeams_2D/plotSolution.py new file mode 100755 index 0000000000000000000000000000000000000000..8f100e5fae99c15fb18d63b3f572db6f045d802f --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/plotSolution.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------------- +# plot photon data for 2D problems +# give snapshot number as cmdline arg to plot +# single snapshot, otherwise this script plots +# all snapshots available in the workdir +# ---------------------------------------------------- + +import gc +import os +import sys + +import matplotlib as mpl +import swiftsimio +from matplotlib import pyplot as plt +from mpl_toolkits.axes_grid1 import make_axes_locatable + +# Parameters users should/may tweak +plot_all_data = False # plot all groups and all photon quantities +snapshot_base = "output" # snapshot basename +fancy = True # fancy up the plots a bit? + +# parameters for imshow plots +imshow_kwargs = {"origin": "lower", "cmap": "viridis"} + + +projection_kwargs = {"resolution": 1024, "parallel": True} +# ----------------------------------------------------------------------- + + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +plot_all = False +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + +mpl.rcParams["text.usetex"] = True + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + if plot_all: + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + else: + fname = snapshot_basename + "_" + str(snapnr).zfill(4) + ".hdf5" + if not os.path.exists(fname): + print("Didn't find file", fname) + quit(1) + snaplist.append(fname) + + return snaplist + + +def set_colorbar(ax, im): + divider = make_axes_locatable(ax) + cax = divider.append_axes("right", size="5%", pad=0.05) + plt.colorbar(im, cax=cax) + return + + +def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): + """ + Create the actual plot. + + filename: file to work with + energy_boundaries: list of [E_min, E_max] for each photon group. + If none, limits are set automatically. + flux_boundaries: list of [F_min, F_max] for each photon group. + If none, limits are set automatically. + """ + + print("working on", filename) + + # Read in data first + data = swiftsimio.load(filename) + meta = data.metadata + + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + xlabel_units_str = meta.boxsize.units.latex_representation() + + global imshow_kwargs + imshow_kwargs["extent"] = [0, meta.boxsize[0].v, 0, meta.boxsize[1].v] + + for g in range(ngroups): + # workaround to access named columns data with swiftsimio visualisaiton + # add mass weights to remove surface density dependence in images + new_attribute_str = "mass_weighted_radiation_energy" + str(g + 1) + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + en = en * data.gas.masses + setattr(data.gas, new_attribute_str, en) + + if plot_all_data: + # prepare also the fluxes + # for direction in ["X", "Y", "Z"]: + for direction in ["X", "Y"]: + new_attribute_str = ( + "mass_weighted_radiation_flux" + str(g + 1) + direction + ) + f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) + f *= data.gas.masses + setattr(data.gas, new_attribute_str, f) + + # get mass surface density projection that we'll use to remove density dependence in image + mass_map = swiftsimio.visualisation.projection.project_gas( + data, project="masses", **projection_kwargs + ) + + if plot_all_data: + fig = plt.figure(figsize=(5 * 3, 5.05 * ngroups), dpi=200) + figname = filename[:-5] + "-all-quantities.png" + + for g in range(ngroups): + + # get energy projection + new_attribute_str = "mass_weighted_radiation_energy" + str(g + 1) + photon_map = swiftsimio.visualisation.projection.project_gas( + data, project=new_attribute_str, **projection_kwargs + ) + photon_map = photon_map / mass_map + + ax = fig.add_subplot(ngroups, 3, g * 3 + 1) + if energy_boundaries is not None: + imshow_kwargs["vmin"] = energy_boundaries[g][0] + imshow_kwargs["vmax"] = energy_boundaries[g][1] + im = ax.imshow(photon_map.T, **imshow_kwargs) + set_colorbar(ax, im) + ax.set_ylabel("Group {0:2d}".format(g + 1)) + ax.set_xlabel("x [$" + xlabel_units_str + "$]") + if g == 0: + ax.set_title("Energies") + + # get flux X projection + new_attribute_str = "mass_weighted_radiation_flux" + str(g + 1) + "X" + photon_map = swiftsimio.visualisation.projection.project_gas( + data, project=new_attribute_str, **projection_kwargs + ) + photon_map = photon_map / mass_map + + ax = fig.add_subplot(ngroups, 3, g * 3 + 2) + if flux_boundaries is not None: + imshow_kwargs["vmin"] = flux_boundaries[g][0] + imshow_kwargs["vmax"] = flux_boundaries[g][1] + im = ax.imshow(photon_map.T, **imshow_kwargs) + set_colorbar(ax, im) + ax.set_xlabel("x [$" + xlabel_units_str + "$]") + ax.set_ylabel("y [$" + xlabel_units_str + "$]") + if g == 0: + ax.set_title("Flux X") + + # get flux Y projection + new_attribute_str = "mass_weighted_radiation_flux" + str(g + 1) + "Y" + photon_map = swiftsimio.visualisation.projection.project_gas( + data, project=new_attribute_str, **projection_kwargs + ) + photon_map = photon_map / mass_map + + ax = fig.add_subplot(ngroups, 3, g * 3 + 3) + im = ax.imshow(photon_map.T, **imshow_kwargs) + set_colorbar(ax, im) + ax.set_xlabel("x [$" + xlabel_units_str + "$]") + ax.set_ylabel("y [$" + xlabel_units_str + "$]") + if g == 0: + ax.set_title("Flux Y") + + else: # plot just energies + + fig = plt.figure(figsize=(5 * ngroups, 5), dpi=200) + figname = filename[:-5] + ".png" + + for g in range(ngroups): + + # get projection + new_attribute_str = "mass_weighted_radiation_energy" + str(g + 1) + photon_map = swiftsimio.visualisation.projection.project_gas( + data, project=new_attribute_str, **projection_kwargs + ) + photon_map = photon_map / mass_map + + ax = fig.add_subplot(1, ngroups, g + 1) + if energy_boundaries is not None: + imshow_kwargs["vmin"] = energy_boundaries[g][0] + imshow_kwargs["vmax"] = energy_boundaries[g][1] + im = ax.imshow(photon_map.T, **imshow_kwargs) + set_colorbar(ax, im) + ax.set_title("Group {0:2d}".format(g + 1)) + if g == 0: + ax.set_ylabel("Energies") + + # Add title + title = filename.replace("_", r"\_") # exception handle underscore for latex + if meta.cosmology is not None: + title += ", $z$ = {0:.2e}".format(meta.z) + title += ", $t$ = {0:.2e}".format(meta.time) + fig.suptitle(title) + + plt.tight_layout() + plt.savefig(figname) + plt.close() + gc.collect() + + return + + +def get_minmax_vals(snaplist): + """ + Find minimal and maximal values for energy and flux, + so you can fix axes limits over all snapshots + + snaplist: list of snapshot filenames + + returns: + + energy_boundaries: list of [E_min, E_max] for each photon group + flux_boundaries: list of [Fx_min, Fy_max] for each photon group + """ + + emins = [] + emaxs = [] + fmins = [] + fmaxs = [] + + for filename in snaplist: + + data = swiftsimio.load(filename) + meta = data.metadata + + ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) + emin_group = [] + emax_group = [] + fluxmin_group = [] + fluxmax_group = [] + + for g in range(ngroups): + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + emin_group.append(en.min()) + emax_group.append(en.max()) + + dirmin = [] + dirmax = [] + for direction in ["X", "Y"]: + f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) + dirmin.append(f.min()) + dirmax.append(f.max()) + fluxmin_group.append(min(dirmin)) + fluxmax_group.append(max(dirmax)) + + emins.append(emin_group) + emaxs.append(emax_group) + fmins.append(fluxmin_group) + fmaxs.append(fluxmax_group) + + energy_boundaries = [] + flux_boundaries = [] + for g in range(ngroups): + emin = min([emins[f][g] for f in range(len(snaplist))]) + emax = max([emaxs[f][g] for f in range(len(snaplist))]) + energy_boundaries.append([emin, emax]) + fmin = min([fmins[f][g] for f in range(len(snaplist))]) + fmax = max([fmaxs[f][g] for f in range(len(snaplist))]) + flux_boundaries.append([fmin, fmax]) + + return energy_boundaries, flux_boundaries + + +if __name__ == "__main__": + + snaplist = get_snapshot_list(snapshot_base) + if fancy: + energy_boundaries, flux_boundaries = get_minmax_vals(snaplist) + else: + energy_boundaries = None + flux_boundaries = None + + for f in snaplist: + plot_photons( + f, energy_boundaries=energy_boundaries, flux_boundaries=flux_boundaries + ) diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/rt_collision2D.yml b/examples/RadiativeTransferTests/CollidingBeams_2D/rt_collision2D.yml new file mode 100644 index 0000000000000000000000000000000000000000..44daff0259e0bd8165b8d18beb13afe459bb040c --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/rt_collision2D.yml @@ -0,0 +1,60 @@ +MetaData: + run_name: RT_collision-2D + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1. + UnitLength_in_cgs: 1. + UnitVelocity_in_cgs: 1. + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 3.4e-1 # The end time of the simulation (in internal units). + dt_min: 1.e-08 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-02 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.017 + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 0.034 # 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.6 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./collision_2D.hdf5 # The file to read + periodic: 1 # periodic ICs + +GEARRT: + f_reduce_c: 1. + # reduce the speed of light for the RT solver by multiplying c with this factor + f_limit_cooling_time: 0.0 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + CFL_condition: 0.8 # CFL condition for RT, independent of hydro + photon_groups_Hz: [1., 2., 3., 4.] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [0., 0., 0., 0.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + skip_thermochemistry: 1 # Skip thermochemsitry. + diff --git a/examples/RadiativeTransferTests/CollidingBeams_2D/run.sh b/examples/RadiativeTransferTests/CollidingBeams_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..e7af6a0ec80635dddb58397754590ca8854a732c --- /dev/null +++ b/examples/RadiativeTransferTests/CollidingBeams_2D/run.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# exit if anything fails +set -e +set -o pipefail + + # Generate the initial conditions if they are not present. +if [ ! -e glassPlane_128.hdf5 ] +then + echo "Fetching initial glass file for the 2D RT advection example..." + ./getGlass.sh +fi +if [ ! -e collision_2D.hdf5 ] +then + echo "Generating initial conditions for the 2D RT advection example..." + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro \ + --threads=4 \ + --verbose=0 \ + --radiation \ + --stars \ + --feedback \ + --external-gravity \ + ./rt_collision2D.yml 2>&1 | tee output.log + +python3 ./plotEnergies.py +python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/CoolingTest/README b/examples/RadiativeTransferTests/CoolingTest/README new file mode 100644 index 0000000000000000000000000000000000000000..eb50535a856ff2a0a09f1440272ee9f22601d7e5 --- /dev/null +++ b/examples/RadiativeTransferTests/CoolingTest/README @@ -0,0 +1,7 @@ +Check on a simple case whether the cooling works correctly without any +radiation being present: Use a uniform 3D box of hot gas and let it cool down. + +The reference solution assumes case A recombination. + +To run with GEAR-RT, compile swift with: + --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT diff --git a/examples/RadiativeTransferTests/CoolingTest/getReference.sh b/examples/RadiativeTransferTests/CoolingTest/getReference.sh new file mode 100755 index 0000000000000000000000000000000000000000..c12e1e5bc95dd5d9ba72a5a01f0e34c15a12b295 --- /dev/null +++ b/examples/RadiativeTransferTests/CoolingTest/getReference.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/RTCoolingTestReference.txt + diff --git a/examples/RadiativeTransferTests/CoolingTest/makeIC.py b/examples/RadiativeTransferTests/CoolingTest/makeIC.py new file mode 100755 index 0000000000000000000000000000000000000000..a050adcf2463f4e95e2daaa7f6c283a267f80885 --- /dev/null +++ b/examples/RadiativeTransferTests/CoolingTest/makeIC.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + +# ----------------------------------------------------------- +# Use 20 particles in to generate a uniform box with +# high temperatures. +# The goal is to reproduce Figure 3 in Smith e al. 2017 +# (ui.adsabs.harvard.edu/abs/2017MNRAS.466.2217S) +# ----------------------------------------------------------- + +import h5py +import numpy as np +import unyt +from swiftsimio import Writer +from swiftsimio.units import cosmo_units + +# number of particles in each dimension +n_p = 20 +nparts = n_p ** 3 +# filename of ICs to be generated +outputfilename = "cooling_test.hdf5" +# adiabatic index +gamma = 5.0 / 3.0 +# total hydrogen mass fraction +XH = 0.76 +# total helium mass fraction +XHe = 0.24 +# boxsize +boxsize = 1 * unyt.kpc +# initial gas temperature +initial_temperature = 1e6 * unyt.K +# particle mass +# take 0.1 amu/cm^3 +pmass = (0.1 * unyt.atomic_mass_unit / unyt.cm ** 3) * (boxsize ** 3 / nparts) + +# ----------------------------------------------- + + +def internal_energy(T, mu): + """ + Compute the internal energy of the gas for a given + temperature and mean molecular weight + """ + # Using u = 1 / (gamma - 1) * p / rho + # and p = N/V * kT = rho / (mu * m_u) * kT + + u = unyt.boltzmann_constant * T / (gamma - 1) / (mu * unyt.atomic_mass_unit) + return u + + +def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): + """ + Determines the mean molecular weight for given + mass fractions of + hydrogen: XH0 + H+: XHp + He: XHe0 + He+: XHep + He++: XHepp + + returns: + mu: mean molecular weight [in atomic mass units] + NOTE: to get the actual mean mass, you still need + to multiply it by m_u, as is tradition in the formulae + """ + + # 1/mu = sum_j X_j / A_j * (1 + E_j) + # A_H = 1, E_H = 0 + # A_Hp = 1, E_Hp = 1 + # A_He = 4, E_He = 0 + # A_Hep = 4, E_Hep = 1 + # A_Hepp = 4, E_Hepp = 2 + one_over_mu = XH0 + 2 * XHp + 0.25 * XHe0 + 0.5 * XHep + 0.75 * XHepp + + return 1.0 / one_over_mu + + +# assume everything is ionized initially +mu = mean_molecular_weight(0.0, XH, 0.0, 0.0, XHe) +u_part = internal_energy(initial_temperature, mu) +pmass = pmass.to("Msun") + + +xp = unyt.unyt_array(np.zeros((nparts, 3), dtype=np.float32), boxsize.units) +dx = boxsize / n_p +ind = 0 +for i in range(n_p): + x = (i + 0.5) * dx + for j in range(n_p): + y = (j + 0.5) * dx + for k in range(n_p): + z = (k + 0.5) * dx + + xp[ind] = (x, y, z) + ind += 1 + +w = Writer(cosmo_units, boxsize, dimension=3) + +w.gas.coordinates = xp +w.gas.velocities = np.zeros(xp.shape, dtype=np.float32) * (unyt.km / unyt.s) +w.gas.masses = np.ones(nparts, dtype=np.float32) * pmass +w.gas.internal_energy = np.ones(nparts, dtype=np.float32) * u_part + +# Generate initial guess for smoothing lengths based on MIPS +w.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=3) + +# If IDs are not present, this automatically generates +w.write(outputfilename) + + +# Now open file back up again and add RT data. +F = h5py.File(outputfilename, "r+") +header = F["Header"] +parts = F["/PartType0"] + +# Create initial ionization species mass fractions. +# Assume everything is ionized initially +# NOTE: grackle doesn't really like exact zeroes, so +# use something very small instead. +HIdata = np.ones(nparts, dtype=np.float32) * 1e-12 +HIIdata = np.ones(nparts, dtype=np.float32) * XH +HeIdata = np.ones(nparts, dtype=np.float32) * 1e-12 +HeIIdata = np.ones(nparts, dtype=np.float32) * 1e-12 +HeIIIdata = np.ones(nparts, dtype=np.float32) * XHe + +parts.create_dataset("MassFractionHI", data=HIdata) +parts.create_dataset("MassFractionHII", data=HIIdata) +parts.create_dataset("MassFractionHeI", data=HeIdata) +parts.create_dataset("MassFractionHeII", data=HeIIdata) +parts.create_dataset("MassFractionHeIII", data=HeIIIdata) + +# close up, and we're done! +F.close() diff --git a/examples/RadiativeTransferTests/CoolingTest/plotSolution.py b/examples/RadiativeTransferTests/CoolingTest/plotSolution.py new file mode 100755 index 0000000000000000000000000000000000000000..b8622ff805835589115dac07804ea31095709c7e --- /dev/null +++ b/examples/RadiativeTransferTests/CoolingTest/plotSolution.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + +# ------------------------------------------- +# Plot the gas temperature, mean molecular +# weight, and mass fractions +# ------------------------------------------- + +import copy +import os + +import numpy as np +import swiftsimio +import unyt +from matplotlib import pyplot as plt + +# arguments for plots of results +plotkwargs = {"alpha": 0.5} +# arguments for plot of references +referenceplotkwargs = {"color": "grey", "lw": 4, "alpha": 0.6} +# arguments for legends +legendprops = {"size": 8} +# snapshot basenames +snapshot_base = "output" + +# ----------------------------------------------------------------------- + +energy_units = unyt.Msun * unyt.kpc ** 2 / unyt.kyr ** 2 +mass_units = unyt.Msun + + +def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): + """ + Determines the mean molecular weight for given + mass fractions of + hydrogen: XH0 + H+: XHp + He: XHe0 + He+: XHep + He++: XHepp + + returns: + mu: mean molecular weight [in atomic mass units] + NOTE: to get the actual mean mass, you still need + to multiply it by m_u, as is tradition in the formulae + """ + + # 1/mu = sum_j X_j / A_j * (1 + E_j) + # A_H = 1, E_H = 0 + # A_Hp = 1, E_Hp = 1 + # A_He = 4, E_He = 0 + # A_Hep = 4, E_Hep = 1 + # A_Hepp = 4, E_Hepp = 2 + one_over_mu = XH0 + 2 * XHp + 0.25 * XHe0 + 0.5 * XHep + 0.75 * XHepp + + return 1.0 / one_over_mu + + +def gas_temperature(u, mu, gamma): + """ + Compute the gas temperature given the specific internal + energy u and the mean molecular weight mu + """ + + # Using u = 1 / (gamma - 1) * p / rho + # and p = N/V * kT = rho / (mu * m_u) * kT + + T = u * (gamma - 1) * mu * unyt.atomic_mass_unit / unyt.boltzmann_constant + + return T + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + if len(snaplist) == 0: + raise FileNotFoundError( + "Didn't find any snapshots with basename '" + snapshot_basename + "'" + ) + + snaplist = sorted(snaplist) + + return snaplist + + +def get_ion_mass_fractions(swiftsimio_loaded_data): + """ + Returns the ion mass fractions according to + the used scheme. + + swiftsimio_loaded_data: the swiftsimio.load() object + """ + + data = swiftsimio_loaded_data + meta = data.metadata + gas = data.gas + with_rt = True + scheme = None + try: + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + except KeyError: + # allow to read in solutions with only cooling, without RT + with_rt = False + + if with_rt: + if scheme.startswith("GEAR M1closure"): + imf = data.gas.ion_mass_fractions + elif scheme.startswith("SPH M1closure"): + # atomic mass + mamu = { + "e": 0.0, + "HI": 1.0, + "HII": 1.0, + "HeI": 4.0, + "HeII": 4.0, + "HeIII": 4.0, + } + mass_function_hydrogen = data.gas.rt_element_mass_fractions.hydrogen + imf = copy.deepcopy(data.gas.rt_species_abundances) + named_columns = data.gas.rt_species_abundances.named_columns + for column in named_columns: + # abundance is in n_X/n_H unit. We convert it to mass fraction by multipling mass fraction of H + mass_function = ( + getattr(data.gas.rt_species_abundances, column) + * mass_function_hydrogen + * mamu[column] + ) + setattr(imf, column, mass_function) + else: + raise ValueError("Unknown scheme", scheme) + else: + # try to find solutions for cooling only runs + imf = { + "HI": gas.hi[:], + "HII": gas.hii[:], + "HeI": gas.he_i[:], + "HeII": gas.he_ii[:], + "HeIII": gas.he_iii[:], + } + + return imf + + +def get_snapshot_data(snaplist): + """ + Extract the relevant data from the list of snapshots. + + Returns: + numpy arrays of: + time + temperatures + mean molecular weights + mass fractions + """ + + nsnaps = len(snaplist) + firstdata = swiftsimio.load(snaplist[0]) + with_rt = True + try: + ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"]) + except KeyError: + # allow to read in solutions with only cooling, without RT + with_rt = False + + times = np.zeros(nsnaps) * mass_units + temperatures = np.zeros(nsnaps) * unyt.K + mean_molecular_weights = np.zeros(nsnaps) + mass_fractions = np.zeros((nsnaps, 5)) + internal_energies = np.zeros(nsnaps) * energy_units + if with_rt: + photon_energies = np.zeros((ngroups, nsnaps)) * energy_units + else: + photon_energies = None + + for i, snap in enumerate(snaplist): + + data = swiftsimio.load(snap) + gamma = data.gas.metadata.gas_gamma[0] + time = data.metadata.time + gas = data.gas + + u = gas.internal_energies[:].to(energy_units / mass_units) + masses = gas.masses[:].to(mass_units) + imf = get_ion_mass_fractions(data) + mu = mean_molecular_weight(imf.HI, imf.HII, imf.HeI, imf.HeII, imf.HeIII) + T = gas_temperature(u, mu, gamma).to("K") + um = u.to(energy_units / mass_units) * masses + um.to(energy_units) + + times[i] = time.to("Myr") + temperatures[i] = np.mean(T) + mean_molecular_weights[i] = np.mean(mu) + internal_energies[i] = np.mean(um) + + mass_fractions[i, 0] = np.mean(imf.HI) + mass_fractions[i, 1] = np.mean(imf.HII) + mass_fractions[i, 2] = np.mean(imf.HeI) + mass_fractions[i, 3] = np.mean(imf.HeII) + mass_fractions[i, 4] = np.mean(imf.HeIII) + + if with_rt: + for g in range(ngroups): + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + en = en[:].to(energy_units) + photon_energies[g, i] = en.sum() / en.shape[0] + + return ( + times, + temperatures, + mean_molecular_weights, + mass_fractions, + internal_energies, + photon_energies, + ) + + +if __name__ == "__main__": + + # ------------------ + # Plot figures + # ------------------ + + snaplist = get_snapshot_list(snapshot_base) + t, T, mu, mass_fraction, u, photon_energies = get_snapshot_data(snaplist) + with_rt = photon_energies is not None + if with_rt: + ngroups = photon_energies.shape[0] + + refdata = np.loadtxt("RTCoolingTestReference.txt", dtype=np.float64) + t_ref = refdata[:, 0] + dt_ref = refdata[:, 1] + T_ref = refdata[:, 2] + mu_ref = refdata[:, 3] + rho_ref = refdata[:, 4] + rhoHI_ref = refdata[:, 5] + rhoHII_ref = refdata[:, 6] + rhoHeI_ref = refdata[:, 7] + rhoHeII_ref = refdata[:, 8] + rhoHeIII_ref = refdata[:, 9] + rhoe_ref = refdata[:, 10] + + t_ref *= 1e-6 # turn to Myrs + mass_fraction_ref = np.empty((t_ref.shape[0], 5)) + mass_fraction_ref[:, 0] = rhoHI_ref / rho_ref + mass_fraction_ref[:, 1] = rhoHII_ref / rho_ref + mass_fraction_ref[:, 2] = rhoHeI_ref / rho_ref + mass_fraction_ref[:, 3] = rhoHeII_ref / rho_ref + mass_fraction_ref[:, 4] = rhoHeIII_ref / rho_ref + + fig = plt.figure(figsize=(8, 8), dpi=300) + ax1 = fig.add_subplot(2, 2, 1) + ax2 = fig.add_subplot(2, 2, 2) + ax3 = fig.add_subplot(2, 2, 3) + ax4 = fig.add_subplot(2, 2, 4) + + ax1.semilogy(t_ref[1:], T_ref[1:], label="reference", **referenceplotkwargs) + ax1.semilogy(t, T, label="obtained results") + ax1.set_xlabel("time [Myr]") + ax1.set_ylabel("gas temperature [K]") + ax1.legend(prop=legendprops) + ax1.grid() + + ax2.plot(t_ref, mu_ref, label="reference", **referenceplotkwargs) + ax2.plot(t, mu, label="obtained results") + ax2.set_xlabel("time [Myr]") + ax2.set_ylabel("mean molecular weight") + ax2.legend(prop=legendprops) + ax2.grid() + + total_mass_fraction = np.sum(mass_fraction, axis=1) + ax3.plot(t, total_mass_fraction, "k", label="total", ls="-") + + ax3.plot( + t_ref[1:], + mass_fraction_ref[1:, 0], + label="reference", + **referenceplotkwargs, + zorder=0, + ) + ax3.plot(t_ref[1:], mass_fraction_ref[1:, 1], **referenceplotkwargs, zorder=0) + ax3.plot(t_ref[1:], mass_fraction_ref[1:, 2], **referenceplotkwargs, zorder=0) + ax3.plot(t_ref[1:], mass_fraction_ref[1:, 3], **referenceplotkwargs, zorder=0) + ax3.plot(t_ref[1:], mass_fraction_ref[1:, 4], **referenceplotkwargs, zorder=0) + + ax3.plot(t, mass_fraction[:, 0], label="HI", ls=":", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 1], label="HII", ls="-.", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 2], label="HeI", ls=":", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 3], label="HeII", ls="-.", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 4], label="HeIII", ls="--", **plotkwargs, zorder=1) + ax3.legend(loc="upper right", prop=legendprops) + ax3.set_xlabel("time [Myr]") + ax3.set_ylabel("gas mass fractions [1]") + ax3.grid() + + if with_rt: + u.convert_to_units(energy_units) + photon_energies.convert_to_units(energy_units) + tot_energy = u + np.sum(photon_energies, axis=0) + ax4.plot( + t, + tot_energy, + label=f"total energy budget", + color="k", + ls="--", + **plotkwargs, + ) + for g in range(ngroups): + ax4.plot( + t, + photon_energies[g, :], + label=f"photon energies group {g + 1}", + **plotkwargs, + ) + ax4.plot(t, u, label="gas internal energy", **plotkwargs) + ax4.set_xlabel("time [Myr]") + ax4.set_ylabel( + r"energy budget [$" + u.units.latex_representation() + "$]", usetex=True + ) + ax4.legend(prop=legendprops) + ax4.grid() + + plt.tight_layout() + # plt.show() + plt.savefig("cooling_test.png") diff --git a/examples/RadiativeTransferTests/CoolingTest/rt_cooling_test.yml b/examples/RadiativeTransferTests/CoolingTest/rt_cooling_test.yml new file mode 100644 index 0000000000000000000000000000000000000000..e24893955b6b0c21095c252457ea178540cc6a96 --- /dev/null +++ b/examples/RadiativeTransferTests/CoolingTest/rt_cooling_test.yml @@ -0,0 +1,70 @@ +MetaData: + run_name: RT Cooling Test + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e23 # 10^-10 M_sun in grams + UnitLength_in_cgs: 3.08567758e21 # 1 kpc in cm + UnitVelocity_in_cgs: 1e5 # 1 km/s in cm/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.100 # The end time of the simulation (in internal units). + dt_min: 1.e-8 # The minimal time-step size of the simulation (in internal units). + dt_max: 4.882814e-05 # The maximal time-step size of the simulation (in internal units). + + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.001 + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 0.01 # 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.6 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./cooling_test.hdf5 # The file to read + periodic: 1 # periodic ICs + +GEARRT: + f_reduce_c: 1.e-9 # This test is without actual radiation, so we don't care about this + CFL_condition: 0.9 # CFL condition for RT, independent of hydro + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [0., 0., 0.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen mass fraction + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + case_B_recombination: 0 # reference solution assumes case A recombination + + +GrackleCooling: + cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + with_UV_background: 0 # Enable or not the UV background + redshift: 0 # Redshift to use (-1 means time based redshift) + with_metal_cooling: 0 # Enable or not the metal cooling + provide_volumetric_heating_rates: 0 # (optional) User provide volumetric heating rates + provide_specific_heating_rates: 0 # (optional) User provide specific heating rates + max_steps: 10000 # (optional) Max number of step when computing the initial composition + convergence_limit: 1 # (optional) Convergence threshold (relative) for initial composition + self_shielding_method: 0 # (optional) Grackle (1->3 for Grackle's ones, 0 for none and -1 for GEAR) + primordial_chemistry: 1 + thermal_time_myr: 5 + +Scheduler: + tasks_per_cell: 128 diff --git a/examples/RadiativeTransferTests/CoolingTest/run.sh b/examples/RadiativeTransferTests/CoolingTest/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..1b193840caec2c81b69e10924bb79c5ed66a77a0 --- /dev/null +++ b/examples/RadiativeTransferTests/CoolingTest/run.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -f ./cooling_test.hdf5 ]; then + echo "creating ICs" + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro \ + --threads=4 \ + --verbose=0 \ + --radiation \ + --external-gravity \ + --stars \ + --feedback \ +./rt_cooling_test.yml 2>&1 | tee output.log + +# Wanna run with cooling, but no RT? This should do the trick +# ../../../swift \ +# --hydro \ +# --threads=4 \ +# --verbose=0 \ +# --cooling \ +# ./rt_cooling_test.yml 2>&1 | tee output.log + +if [ ! -f "RTCoolingTestReference.txt" ]; then + ./getReference.sh +fi +python3 plotSolution.py diff --git a/examples/RadiativeTransferTests/HeatingTest/README b/examples/RadiativeTransferTests/HeatingTest/README new file mode 100644 index 0000000000000000000000000000000000000000..18ba3a98b493955657afb9b6cdc309af72fbb4cd --- /dev/null +++ b/examples/RadiativeTransferTests/HeatingTest/README @@ -0,0 +1,6 @@ +Runs a uniform box with radiation initially present. No further radiation +sources are present, and the gas should heat up and ionize, while the +radiation fields should decrease at the same rate. + +To run with GEAR-RT, compile swift with: + --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT diff --git a/examples/RadiativeTransferTests/HeatingTest/generate_output_times.py b/examples/RadiativeTransferTests/HeatingTest/generate_output_times.py new file mode 100755 index 0000000000000000000000000000000000000000..2bf2127b605dc6cce770045612e2c3d0e6fcbc27 --- /dev/null +++ b/examples/RadiativeTransferTests/HeatingTest/generate_output_times.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +# ---------------------------------------------- +# Generate an output times list to your liking +# ---------------------------------------------- + +import yaml + +with open(r"rt_heating_test.yml") as paramfile: + params = yaml.load(paramfile, Loader=yaml.FullLoader) + + unit_l = params["InternalUnitSystem"]["UnitLength_in_cgs"] + unit_l = float(unit_l) + unit_v = params["InternalUnitSystem"]["UnitVelocity_in_cgs"] + unit_v = float(unit_v) + t_begin = params["TimeIntegration"]["time_begin"] + t_begin = float(t_begin) + t_end = params["TimeIntegration"]["time_end"] + t_end = float(t_end) + dt_min = params["TimeIntegration"]["dt_min"] + dt_min = float(dt_min) + dt_max = params["TimeIntegration"]["dt_max"] + dt_max = float(dt_max) + +unit_t = unit_l / unit_v +unit_myr = unit_t / (3600 * 24 * 365 * 1e6) + + +# If you're rebuilding this output times list: +# I read this 'dt' out from a run, then used it to generate the output list +dt_heat = 1.001182e-03 + +current_t = t_begin +outputtimes = [] +# first 16 snapshots +for i in range(16): + current_t += dt_heat + outputtimes.append(current_t) +for i in range(16): + current_t += 4 * dt_heat + outputtimes.append(current_t) +for i in range(16): + current_t += 8 * dt_heat + outputtimes.append(current_t) +for i in range(16): + current_t += 16 * dt_heat + outputtimes.append(current_t) +# fill up until heating stops +while current_t + 32 * dt_heat < t_end: + current_t += 32 * dt_heat + outputtimes.append(current_t) +outputtimes.append(t_end) + +print("preparing", len(outputtimes), "snapshots") + +with open(r"snaplist.txt", "w") as outfile: + # THIS HEADER IS IMPORTANT + outfile.write("# Time\n") + for t in outputtimes: + outfile.write("{0:.6g}\n".format(t)) + +print("written snaplist.txt") diff --git a/examples/RadiativeTransferTests/HeatingTest/makeIC.py b/examples/RadiativeTransferTests/HeatingTest/makeIC.py new file mode 100755 index 0000000000000000000000000000000000000000..01984de0f5dbca60e0e6e81af952042c9c8c40e1 --- /dev/null +++ b/examples/RadiativeTransferTests/HeatingTest/makeIC.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ----------------------------------------------------------- +# Use 20 particles in to generate a uniform box with +# a fixed amount of radiation. +# ----------------------------------------------------------- + +from swiftsimio import Writer +from swiftsimio.units import cosmo_units + +import unyt +import numpy as np +import h5py + + +# number of particles in each dimension +n_p = 10 +nparts = n_p ** 3 + +# filename of ICs to be generated +outputfilename = "heating_test.hdf5" +# adiabatic index +gamma = 5.0 / 3.0 +# total hydrogen mass fraction +XH = 0.76 +# total helium mass fraction +XHe = 0.24 +# boxsize +boxsize = 1 * unyt.kpc +# initial gas temperature +initial_temperature = 1e3 * unyt.K +# particle mass +pmass = (unyt.atomic_mass_unit / unyt.cm ** 3) * (boxsize ** 3 / nparts) +pmass = pmass.to("Msun") + +# ----------------------------------------------- + + +def internal_energy(T, mu): + """ + Compute the internal energy of the gas for a given + temperature and mean molecular weight + """ + # Using u = 1 / (gamma - 1) * p / rho + # and p = N/V * kT = rho / (mu * m_u) * kT + + u = unyt.boltzmann_constant * T / (gamma - 1) / (mu * unyt.atomic_mass_unit) + return u + + +def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): + """ + Determines the mean molecular weight for given + mass fractions of + hydrogen: XH0 + H+: XHp + He: XHe0 + He+: XHep + He++: XHepp + + returns: + mu: mean molecular weight [in atomic mass units] + NOTE: to get the actual mean mass, you still need + to multiply it by m_u, as is tradition in the formulae + """ + + # 1/mu = sum_j X_j / A_j * (1 + E_j) + # A_H = 1, E_H = 0 + # A_Hp = 1, E_Hp = 1 + # A_He = 4, E_He = 0 + # A_Hep = 4, E_Hep = 1 + # A_Hepp = 4, E_Hepp = 2 + one_over_mu = XH0 + 2 * XHp + 0.25 * XHe0 + 0.5 * XHep + 0.75 * XHepp + + return 1.0 / one_over_mu + + +# assume everything is neutral initially +mu = mean_molecular_weight(XH, 0, XHe, 0.0, 0.0) +u_part = internal_energy(initial_temperature, mu) +pmass = pmass.to("Msun") + + +xp = unyt.unyt_array(np.zeros((nparts, 3), dtype=np.float32), boxsize.units) +dx = boxsize / n_p +ind = 0 +for i in range(n_p): + x = (i + 0.5) * dx + for j in range(n_p): + y = (j + 0.5) * dx + for k in range(n_p): + z = (k + 0.5) * dx + + xp[ind] = (x, y, z) + ind += 1 + +w = Writer(cosmo_units, boxsize, dimension=3) + + +w.gas.coordinates = xp +w.gas.velocities = np.zeros(xp.shape, dtype=np.float32) * (unyt.km / unyt.s) +w.gas.masses = np.ones(nparts, dtype=np.float32) * pmass +w.gas.internal_energy = np.ones(nparts, dtype=np.float32) * u_part + +# Generate initial guess for smoothing lengths based on MIPS +w.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=3) + +# If IDs are not present, this automatically generates +w.write(outputfilename) + +# Now open file back up again and add RT data. + +F = h5py.File(outputfilename, "r+") +header = F["Header"] +nparts = header.attrs["NumPart_ThisFile"][0] +parts = F["/PartType0"] + +# Create initial ionization species mass fractions. +# Assume everything neutral initially +# NOTE: grackle doesn't really like exact zeroes, so +# use something very small instead. +HIdata = np.ones(nparts, dtype=np.float32) * XH +HIIdata = np.ones(nparts, dtype=np.float32) * 1e-12 +HeIdata = np.ones(nparts, dtype=np.float32) * XHe +HeIIdata = np.ones(nparts, dtype=np.float32) * 1e-12 +HeIIIdata = np.ones(nparts, dtype=np.float32) * 1e-12 + +parts.create_dataset("MassFractionHI", data=HIdata) +parts.create_dataset("MassFractionHII", data=HIIdata) +parts.create_dataset("MassFractionHeI", data=HeIdata) +parts.create_dataset("MassFractionHeII", data=HeIIdata) +parts.create_dataset("MassFractionHeIII", data=HeIIIdata) + + +# Add photon groups +nPhotonGroups = 3 + +# with this IC, the radiative cooling is negligible. +# photon_energy = u_part * pmass * 5.0 +# photon_energy = np.arange(1, nPhotonGroups+1) * photon_energy + +# Fluxes from the Iliev Test0 part3 +fluxes_iliev = np.array([1.350e1, 2.779e1, 6.152e0]) * unyt.erg / unyt.s / unyt.cm ** 2 +energy_density = fluxes_iliev / unyt.c +photon_energy = energy_density * boxsize ** 3 / nparts +photon_energy = photon_energy * 0.00001 + +photon_energy.convert_to_units(cosmo_units["energy"]) +photon_fluxes = 0.333333 * unyt.c * photon_energy +photon_fluxes.convert_to_units( + cosmo_units["energy"] * cosmo_units["length"] / cosmo_units["time"] +) + + +for grp in range(nPhotonGroups): + dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1) + energydata = np.ones(nparts, dtype=np.float32) * photon_energy[grp] + parts.create_dataset(dsetname, data=energydata) + + dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1) + fluxdata = np.ones((nparts, 3), dtype=np.float32) + fluxdata[:, 0] = photon_fluxes[grp] + parts.create_dataset(dsetname, data=fluxdata) + +# close up, and we're done! +F.close() diff --git a/examples/RadiativeTransferTests/HeatingTest/plotSolution.py b/examples/RadiativeTransferTests/HeatingTest/plotSolution.py new file mode 100755 index 0000000000000000000000000000000000000000..2c6cee439cde01eed6161f026582e74d95f99373 --- /dev/null +++ b/examples/RadiativeTransferTests/HeatingTest/plotSolution.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + +# ------------------------------------------- +# Plot the gas temperature, mean molecular +# weight, and mass fractions +# ------------------------------------------- + +import copy +import os + +import numpy as np +import swiftsimio +import unyt +from matplotlib import pyplot as plt + +# arguments for plots of results +plotkwargs = {"alpha": 0.5} +# arguments for plot of references +referenceplotkwargs = {"color": "grey", "lw": 4, "alpha": 0.6} +# arguments for legends +legendprops = {"size": 8} +# snapshot basenames +snapshot_base = "output" + + +# ----------------------------------------------------------------------- + +energy_units = unyt.Msun * unyt.kpc ** 2 / unyt.kyr ** 2 +mass_units = unyt.Msun + + +def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): + """ + Determines the mean molecular weight for given + mass fractions of + hydrogen: XH0 + H+: XHp + He: XHe0 + He+: XHep + He++: XHepp + + returns: + mu: mean molecular weight [in atomic mass units] + NOTE: to get the actual mean mass, you still need + to multiply it by m_u, as is tradition in the formulae + """ + + # 1/mu = sum_j X_j / A_j * (1 + E_j) + # A_H = 1, E_H = 0 + # A_Hp = 1, E_Hp = 1 + # A_He = 4, E_He = 0 + # A_Hep = 4, E_Hep = 1 + # A_Hepp = 4, E_Hepp = 2 + one_over_mu = XH0 + 2 * XHp + 0.25 * XHe0 + 0.5 * XHep + 0.75 * XHepp + + return 1.0 / one_over_mu + + +def gas_temperature(u, mu, gamma): + """ + Compute the gas temperature given the specific internal + energy u and the mean molecular weight mu + """ + + # Using u = 1 / (gamma - 1) * p / rho + # and p = N/V * kT = rho / (mu * m_u) * kT + + T = u * (gamma - 1) * mu * unyt.atomic_mass_unit / unyt.boltzmann_constant + + return T + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + if len(snaplist) == 0: + raise FileNotFoundError( + "Didn't find any snapshots with basename '" + snapshot_basename + "'" + ) + + snaplist = sorted(snaplist) + + return snaplist + + +def get_ion_mass_fractions(swiftsimio_loaded_data): + """ + Returns the ion mass fractions according to + the used scheme. + + swiftsimio_loaded_data: the swiftsimio.load() object + """ + + data = swiftsimio_loaded_data + meta = data.metadata + try: + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + except KeyError: + raise ValueError("This test needs to be run with RT on.") + + if scheme.startswith("GEAR M1closure"): + imf = data.gas.ion_mass_fractions + elif scheme.startswith("SPH M1closure"): + # atomic mass + mamu = {"e": 0.0, "HI": 1.0, "HII": 1.0, "HeI": 4.0, "HeII": 4.0, "HeIII": 4.0} + mass_function_hydrogen = data.gas.rt_element_mass_fractions.hydrogen + imf = copy.deepcopy(data.gas.rt_species_abundances) + named_columns = data.gas.rt_species_abundances.named_columns + for column in named_columns: + # abundance is in n_X/n_H unit. We convert it to mass fraction by multipling mass fraction of H + mass_function = ( + getattr(data.gas.rt_species_abundances, column) + * mass_function_hydrogen + * mamu[column] + ) + setattr(imf, column, mass_function) + else: + raise ValueError("Unknown scheme", scheme) + + return imf + + +def get_snapshot_data(snaplist): + """ + Extract the relevant data from the list of snapshots. + + Returns: + numpy arrays of: + time + temperatures + mean molecular weights + mass fractions + """ + + nsnaps = len(snaplist) + firstdata = swiftsimio.load(snaplist[0]) + ngroups = int(firstdata.metadata.subgrid_scheme["PhotonGroupNumber"]) + + times = np.zeros(nsnaps) * unyt.Myr + temperatures = np.zeros(nsnaps) * unyt.K + mean_molecular_weights = np.zeros(nsnaps) * unyt.atomic_mass_unit + mass_fractions = np.zeros((nsnaps, 5)) + internal_energies = np.zeros(nsnaps) * energy_units + photon_energies = np.zeros((ngroups, nsnaps)) * energy_units + + for i, snap in enumerate(snaplist): + + data = swiftsimio.load(snap) + gamma = data.gas.metadata.gas_gamma[0] + time = data.metadata.time + gas = data.gas + + u = gas.internal_energies.to(energy_units / mass_units) + masses = gas.masses.to(mass_units) + imf = get_ion_mass_fractions(data) + mu = mean_molecular_weight(imf.HI, imf.HII, imf.HeI, imf.HeII, imf.HeIII) + T = gas_temperature(u, mu, gamma).to("K") + um = u.to(energy_units / mass_units) * masses + + times[i] = time.to("Myr") + temperatures[i] = np.mean(T) + mean_molecular_weights[i] = np.mean(mu) + internal_energies[i] = np.mean(um) + + mass_fractions[i, 0] = np.mean(imf.HI) + mass_fractions[i, 1] = np.mean(imf.HII) + mass_fractions[i, 2] = np.mean(imf.HeI) + mass_fractions[i, 3] = np.mean(imf.HeII) + mass_fractions[i, 4] = np.mean(imf.HeIII) + + for g in range(ngroups): + en = getattr(data.gas.photon_energies, "group" + str(g + 1)) + en = en.to(energy_units) + photon_energies[g, i] = en.sum() / en.shape[0] + + return ( + times, + temperatures, + mean_molecular_weights, + mass_fractions, + internal_energies, + photon_energies, + ) + + +if __name__ == "__main__": + + # ------------------ + # Plot figures + # ------------------ + + snaplist = get_snapshot_list(snapshot_base) + t, T, mu, mass_fraction, u, photon_energies = get_snapshot_data(snaplist) + ngroups = photon_energies.shape[0] + + fig = plt.figure(figsize=(8, 8), dpi=300) + ax1 = fig.add_subplot(2, 2, 1) + ax2 = fig.add_subplot(2, 2, 2) + ax3 = fig.add_subplot(2, 2, 3) + ax4 = fig.add_subplot(2, 2, 4) + + ax1.semilogy(t, T, label="obtained results") + ax1.set_xlabel("time [Myr]") + ax1.set_ylabel("gas temperature [K]") + ax1.legend(prop=legendprops) + ax1.grid() + + ax2.plot(t, mu, label="obtained results") + ax2.set_xlabel("time [Myr]") + ax2.set_ylabel("mean molecular weight") + ax2.legend(prop=legendprops) + ax2.grid() + + total_mass_fraction = np.sum(mass_fraction, axis=1) + ax3.plot(t, total_mass_fraction, "k", label="total", ls="-") + + ax3.plot(t, mass_fraction[:, 0], label="HI", ls=":", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 1], label="HII", ls="-.", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 2], label="HeI", ls=":", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 3], label="HeII", ls="-.", **plotkwargs, zorder=1) + ax3.plot(t, mass_fraction[:, 4], label="HeIII", ls="--", **plotkwargs, zorder=1) + ax3.legend(loc="upper right", prop=legendprops) + ax3.set_xlabel("time [Myr]") + ax3.set_ylabel("gas mass fractions [1]") + ax3.grid() + + tot_energy = u + np.sum(photon_energies, axis=0) + ax4.plot( + t, tot_energy, label=f"total energy budget", color="k", ls="--", **plotkwargs + ) + for g in range(ngroups): + ax4.plot( + t, photon_energies[g, :], label=f"photon energies group {g+1}", **plotkwargs + ) + ax4.plot(t, u, label=r"gas internal energy", **plotkwargs) + + ax4.set_yscale("log") + ax4.set_xlabel("time [Myr]") + ax4.set_ylabel( + r"energy budget [$" + energy_units.units.latex_representation() + "$]", + usetex=True, + ) + ax4.legend(prop=legendprops) + ax4.grid() + + for ax in fig.axes: + ax.set_xscale("log") + + plt.tight_layout() + plt.savefig("heating_test.png") diff --git a/examples/RadiativeTransferTests/HeatingTest/rt_heating_test.yml b/examples/RadiativeTransferTests/HeatingTest/rt_heating_test.yml new file mode 100644 index 0000000000000000000000000000000000000000..eec44d2702f8f77b69a11984b6e5fce64bf05849 --- /dev/null +++ b/examples/RadiativeTransferTests/HeatingTest/rt_heating_test.yml @@ -0,0 +1,57 @@ +MetaData: + run_name: Heating Test + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e33 # 1 M_sun in grams + UnitLength_in_cgs: 3.08567758e18 # 1 pc in cm + UnitVelocity_in_cgs: 1e5 # 1 km/s in cm/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + dt_min: 1.0208453377e-08 # 0.01 yr + dt_max: 0.0010208453 # 1 kyr + time_end: 4.10084 # 4 Myr + + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + output_list_on: 1 # (Optional) Enable the output list + output_list: snaplist.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) + + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 1. + +# 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.6 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./heating_test.hdf5 # The file to read + periodic: 1 # periodic ICs + +GEARRT: + f_reduce_c: 1e-6 # We don't care about the radiation propagation in this test, so let's speed things up + CFL_condition: 0.9 # CFL condition for RT, independent of hydro + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1., 1., 1.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + hydrogen_mass_fraction: 0.76 # total hydrogen mass fraction + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + case_B_recombination: 0 # (Optional) use case B recombination interaction rates. + diff --git a/examples/RadiativeTransferTests/HeatingTest/run.sh b/examples/RadiativeTransferTests/HeatingTest/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..dc11c1fde493956a4e569484f138e0d38c1c365f --- /dev/null +++ b/examples/RadiativeTransferTests/HeatingTest/run.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -f ./heating_test.hdf5 ]; then + echo "creating ICs" + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro \ + --threads=4 \ + --verbose=0 \ + --radiation \ + --external-gravity \ + --stars \ + --feedback \ + ./rt_heating_test.yml 2>&1 | tee output.log + +python3 plotSolution.py diff --git a/examples/RadiativeTransferTests/HeatingTest/snaplist.txt b/examples/RadiativeTransferTests/HeatingTest/snaplist.txt new file mode 100644 index 0000000000000000000000000000000000000000..a461e83c58b68260b8afbd5529814ce7abd1fe11 --- /dev/null +++ b/examples/RadiativeTransferTests/HeatingTest/snaplist.txt @@ -0,0 +1,179 @@ +# Time +0.00100118 +0.00200236 +0.00300355 +0.00400473 +0.00500591 +0.00600709 +0.00700827 +0.00800946 +0.00901064 +0.0100118 +0.011013 +0.0120142 +0.0130154 +0.0140165 +0.0150177 +0.0160189 +0.0200236 +0.0240284 +0.0280331 +0.0320378 +0.0360426 +0.0400473 +0.044052 +0.0480567 +0.0520615 +0.0560662 +0.0600709 +0.0640756 +0.0680804 +0.0720851 +0.0760898 +0.0800946 +0.088104 +0.0961135 +0.104123 +0.112132 +0.120142 +0.128151 +0.136161 +0.14417 +0.15218 +0.160189 +0.168199 +0.176208 +0.184217 +0.192227 +0.200236 +0.208246 +0.224265 +0.240284 +0.256303 +0.272322 +0.28834 +0.304359 +0.320378 +0.336397 +0.352416 +0.368435 +0.384454 +0.400473 +0.416492 +0.432511 +0.44853 +0.464548 +0.496586 +0.528624 +0.560662 +0.5927 +0.624738 +0.656775 +0.688813 +0.720851 +0.752889 +0.784927 +0.816965 +0.849002 +0.88104 +0.913078 +0.945116 +0.977154 +1.00919 +1.04123 +1.07327 +1.1053 +1.13734 +1.16938 +1.20142 +1.23346 +1.26549 +1.29753 +1.32957 +1.36161 +1.39365 +1.42568 +1.45772 +1.48976 +1.5218 +1.55383 +1.58587 +1.61791 +1.64995 +1.68199 +1.71402 +1.74606 +1.7781 +1.81014 +1.84217 +1.87421 +1.90625 +1.93829 +1.97033 +2.00236 +2.0344 +2.06644 +2.09848 +2.13052 +2.16255 +2.19459 +2.22663 +2.25867 +2.2907 +2.32274 +2.35478 +2.38682 +2.41886 +2.45089 +2.48293 +2.51497 +2.54701 +2.57904 +2.61108 +2.64312 +2.67516 +2.7072 +2.73923 +2.77127 +2.80331 +2.83535 +2.86739 +2.89942 +2.93146 +2.9635 +2.99554 +3.02757 +3.05961 +3.09165 +3.12369 +3.15573 +3.18776 +3.2198 +3.25184 +3.28388 +3.31591 +3.34795 +3.37999 +3.41203 +3.44407 +3.4761 +3.50814 +3.54018 +3.57222 +3.60426 +3.63629 +3.66833 +3.70037 +3.73241 +3.76444 +3.79648 +3.82852 +3.86056 +3.8926 +3.92463 +3.95667 +3.98871 +4.02075 +4.05278 +4.08482 +4.10084 diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/README b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/README new file mode 100644 index 0000000000000000000000000000000000000000..0f0df76c4d5ffd7e4b8bc79fe5f2e2d4c8e7ce11 --- /dev/null +++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/README @@ -0,0 +1,9 @@ +Ion Mass Fraction Advection Test + +In some schemes, like the meshless (gizmo) scheme, particles exchange +masses via fluxes. This example tests whether the mass flux exchange +is done correctly by setting up regions of special ionization states +and advecting them through time. + +To use the GEAR RT model, compile with : + --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/advect_ions.yml b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/advect_ions.yml new file mode 100644 index 0000000000000000000000000000000000000000..1bf391f4a5da004dbe6cdf967fa7b0db9fb729ad --- /dev/null +++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/advect_ions.yml @@ -0,0 +1,56 @@ +MetaData: + run_name: advect_ions + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841586e+33 # 1 M_Sol + UnitLength_in_cgs: 3.08567758e21 # kpc in cm + UnitVelocity_in_cgs: 1.e5 # km/s + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. # K + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.01 # end time: radiation reaches edge of box + dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-03 # The maximal time-step size of the simulation (in internal units). + + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.001 # Time between snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 5e-4 # 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.9 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./advect_ions.hdf5 # The file to read + periodic: 1 # periodic ICs. Keep them periodic so we don't loose photon energy. + +GEARRT: + f_reduce_c: 1.e-5 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 + f_limit_cooling_time: 0.0 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [0., 0., 0.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.50 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 0 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant over all frequencies. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + skip_thermochemistry: 1 + diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/getGlass.sh b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..a958311b6de07663c7f7c70cff9e95d86ce3efb2 --- /dev/null +++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_64.hdf5 diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/makeIC.py b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/makeIC.py new file mode 100755 index 0000000000000000000000000000000000000000..0f5621e826d5aa99ada36f38046e7fd70137d2ca --- /dev/null +++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/makeIC.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# --------------------------------------------------------------------- +# Test the diffusion/advection of ions by creating regions with/without +# initial ions. Run without radiation. +# --------------------------------------------------------------------- + +from swiftsimio import Writer +from swiftsimio.units import cosmo_units +import unyt +import numpy as np +import h5py + +gamma = 5.0 / 3.0 +outputfilename = "advect_ions.hdf5" + +if __name__ == "__main__": + + glass = h5py.File("glassPlane_64.hdf5", "r") + parts = glass["PartType0"] + xp = parts["Coordinates"][:] + h = parts["SmoothingLength"][:] + glass.close() + + # Set up metadata + unitL = unyt.Mpc + edgelen = 2 * 15 * 1e-3 * unitL # 30 kpc + edgelen = edgelen.to(unitL) + boxsize = np.array([1.0, 1.0, 0.0]) * edgelen + + xp *= edgelen + h *= edgelen + + w = Writer(unit_system=cosmo_units, box_size=boxsize, dimension=2) + + # write particle positions and smoothing lengths + w.gas.coordinates = xp + w.gas.smoothing_length = h + + # get gas masses + mpart = 1.6e5 * unyt.Msun + masses = np.ones(xp.shape[0], dtype=np.float64) * mpart + # change some gas masses + mask = xp[:, 0] > 0.5 * edgelen + masses[mask] *= 3 + w.gas.masses = masses + + w.gas.internal_energy = ( + np.ones(xp.shape[0], dtype=np.float64) * 1.25e6 * unyt.m ** 2 / unyt.s ** 2 + ) + + # get velocities + vels = np.zeros((xp.shape[0], 3)) + vels[:, 0] = -1.0 + vels[:, 1] = +1.0 + w.gas.velocities = vels * 1000 * cosmo_units["length"] / cosmo_units["time"] + + w.write(outputfilename) + + # Now open file back up again and add RT data. + F = h5py.File(outputfilename, "r+") + header = F["Header"] + nparts = header.attrs["NumPart_ThisFile"][0] + parts = F["/PartType0"] + pos = parts["Coordinates"] + + # Create initial ionization species mass fractions. + HIdata = np.ones(nparts, dtype=np.float32) * 0.76 + HIIdata = np.zeros(nparts, dtype=np.float32) + HeIdata = np.ones(nparts, dtype=np.float32) * 0.24 + HeIIdata = np.zeros(nparts, dtype=np.float32) + HeIIIdata = np.zeros(nparts, dtype=np.float32) + + mask1 = pos[:, 0] > edgelen / 3 + mask1 = np.logical_and(mask1, pos[:, 0] < 2 * edgelen / 3) + HIdata[mask1] = 0.26 + HIIdata[mask1] = 0.5 + + mask2 = pos[:, 1] > edgelen / 4 + mask2 = np.logical_and(mask2, pos[:, 1] < edgelen / 2) + HeIdata[mask2] = 0.1 + HeIIdata[mask2] = 0.14 + + mask3 = pos[:, 1] > edgelen / 2 + mask3 = np.logical_and(mask3, pos[:, 1] < 3 * edgelen / 4) + HeIdata[mask3] = 0.05 + HeIIdata[mask3] = 0.09 + HeIIIdata[mask3] = 0.1 + + parts.create_dataset("MassFractionHI", data=HIdata) + parts.create_dataset("MassFractionHII", data=HIIdata) + parts.create_dataset("MassFractionHeI", data=HeIdata) + parts.create_dataset("MassFractionHeII", data=HeIIdata) + parts.create_dataset("MassFractionHeIII", data=HeIIIdata) + + # close up, and we're done! + F.close() diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/plotIonization.py b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/plotIonization.py new file mode 100755 index 0000000000000000000000000000000000000000..57f79fc99b2e333184ba342c531444fcf690574b --- /dev/null +++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/plotIonization.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------------- +# Plot the ionizing species +# ---------------------------------------------------- + +import os +import sys + +import matplotlib as mpl +import swiftsimio +from matplotlib import pyplot as plt +from matplotlib.colors import SymLogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable + +# Parameters users should/may tweak + +# plot all groups and all photon quantities +plot_all_data = True +# snapshot basename +snapshot_base = "output" +# fancy up the plots a bit? +fancy = True + +# parameters for imshow plots +imshow_kwargs = {"origin": "lower", "cmap": "brg"} + +# parameters for swiftsimio projections +projection_kwargs = {"resolution": 512, "parallel": True} + +# ----------------------------------------------------------------------- + + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +plot_all = False +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + +mpl.rcParams["text.usetex"] = True + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + if plot_all: + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + else: + fname = snapshot_basename + "_" + str(snapnr).zfill(4) + ".hdf5" + if not os.path.exists(fname): + print("Didn't find file", fname) + quit(1) + snaplist.append(fname) + + return snaplist + + +def set_colorbar(ax, im): + """ + Adapt the colorbar a bit for axis object <ax> and + imshow instance <im> + """ + divider = make_axes_locatable(ax) + cax = divider.append_axes("right", size="5%", pad=0.05) + plt.colorbar(im, cax=cax) + return + + +def plot_ionization(filename): + """ + Create and save the plot + """ + print("working on", filename) + + data = swiftsimio.load(filename) + meta = data.metadata + + global imshow_kwargs + imshow_kwargs["extent"] = [ + 0.01 * meta.boxsize[0].v, + 0.99 * meta.boxsize[0].v, + 0.01 * meta.boxsize[1].v, + 0.99 * meta.boxsize[1].v, + ] + + mass_map = swiftsimio.visualisation.projection.project_gas( + data, project="masses", **projection_kwargs + ) + + data.gas.mXHI = data.gas.ion_mass_fractions.HI * data.gas.masses + data.gas.mXHII = data.gas.ion_mass_fractions.HII * data.gas.masses + data.gas.mXHeI = data.gas.ion_mass_fractions.HeI * data.gas.masses + data.gas.mXHeII = data.gas.ion_mass_fractions.HeII * data.gas.masses + data.gas.mXHeIII = data.gas.ion_mass_fractions.HeIII * data.gas.masses + + mass_weighted_XHImap = swiftsimio.visualisation.projection.project_gas( + data, project="mXHI", **projection_kwargs + ) + mass_weighted_XHIImap = swiftsimio.visualisation.projection.project_gas( + data, project="mXHII", **projection_kwargs + ) + mass_weighted_XHeImap = swiftsimio.visualisation.projection.project_gas( + data, project="mXHeI", **projection_kwargs + ) + mass_weighted_XHeIImap = swiftsimio.visualisation.projection.project_gas( + data, project="mXHeII", **projection_kwargs + ) + mass_weighted_XHeIIImap = swiftsimio.visualisation.projection.project_gas( + data, project="mXHeIII", **projection_kwargs + ) + + XHImap = mass_weighted_XHImap / mass_map + XHIImap = mass_weighted_XHIImap / mass_map + XHeImap = mass_weighted_XHeImap / mass_map + XHeIImap = mass_weighted_XHeIImap / mass_map + XHeIIImap = mass_weighted_XHeIIImap / mass_map + + fig = plt.figure(figsize=(20, 8), dpi=200) + figname = filename[:-5] + "-mass-fractions.png" + + ax1 = fig.add_subplot(251) + ax2 = fig.add_subplot(252) + ax3 = fig.add_subplot(253) + ax4 = fig.add_subplot(254) + ax5 = fig.add_subplot(255) + ax6 = fig.add_subplot(256) + ax7 = fig.add_subplot(257) + ax8 = fig.add_subplot(258) + ax9 = fig.add_subplot(259) + ax10 = fig.add_subplot(2, 5, 10) + + # im0 = ax0.imshow(mass_map.T, **imshow_kwargs) + # set_colorbar(ax0, im0) + # ax0.set_title("Mass") + + imshow_kwargs_ions = imshow_kwargs.copy() + imshow_kwargs_ions["norm"] = SymLogNorm(vmin=0.0, vmax=1.0, linthresh=1e-3, base=10) + + im1 = ax1.imshow(XHImap.T, **imshow_kwargs_ions) + set_colorbar(ax1, im1) + ax1.set_title("HI Mass Fraction") + + im2 = ax2.imshow(XHIImap.T, **imshow_kwargs_ions) + set_colorbar(ax2, im2) + ax2.set_title("HII Mass Fraction") + + im3 = ax3.imshow(XHeImap.T, **imshow_kwargs_ions) + set_colorbar(ax3, im3) + ax3.set_title("HeI Mass Fraction") + + im4 = ax4.imshow(XHeIImap.T, **imshow_kwargs_ions) + set_colorbar(ax4, im4) + ax4.set_title("HeII Mass Fraction") + + im5 = ax5.imshow(XHeIIImap.T, **imshow_kwargs_ions) + set_colorbar(ax5, im5) + ax5.set_title("HeIII Mass Fraction") + + im6 = ax6.imshow(mass_weighted_XHImap.T, **imshow_kwargs) + set_colorbar(ax6, im6) + ax6.set_title("HI Mass") + + im7 = ax7.imshow(mass_weighted_XHIImap.T, **imshow_kwargs) + set_colorbar(ax7, im7) + ax7.set_title("HII Mass") + + im8 = ax8.imshow(mass_weighted_XHeImap.T, **imshow_kwargs) + set_colorbar(ax8, im8) + ax8.set_title("HeI Mass") + + im9 = ax9.imshow(mass_weighted_XHeIImap.T, **imshow_kwargs) + set_colorbar(ax9, im9) + ax9.set_title("HeII Mass") + + im10 = ax10.imshow(mass_weighted_XHeIIImap.T, **imshow_kwargs) + set_colorbar(ax10, im10) + ax10.set_title("HeIII Mass") + + for ax in [ax1, ax2, ax3, ax4, ax5, ax6, ax7, ax8, ax9, ax10]: + ax.set_xlabel("[kpc]") + ax.set_ylabel("[kpc]") + + title = filename.replace("_", r"\_") # exception handle underscore for latex + if meta.cosmology is not None: + title += ", $z$ = {0:.2e}".format(meta.z) + title += ", $t$ = {0:.2e}".format(meta.time.to("Myr")) + fig.suptitle(title) + + plt.tight_layout() + plt.savefig(figname) + plt.close() + return + + +if __name__ == "__main__": + + snaplist = get_snapshot_list(snapshot_base) + + for f in snaplist: + plot_ionization(f) diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/run.sh b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..b5cff76a3f2d0985e32b5510416583cb4682b17d --- /dev/null +++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/run.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -e glassPlane_64.hdf5 ] +then + echo "Fetching initial glass file ..." + ./getGlass.sh +fi + +if [ ! -f 'advect_ions.hdf5' ]; then + echo "Generating ICs" + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro --threads=4 --stars --external-gravity \ + --feedback --radiation \ + advect_ions.yml 2>&1 | tee output.log + +python3 plotIonization.py +python3 testIonization.py diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/testIonization.py b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/testIonization.py new file mode 100755 index 0000000000000000000000000000000000000000..3c737462e2ea28f61897c34c6b0d7642626aa463 --- /dev/null +++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/testIonization.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------------- +# Check that the total amount of ionized species +# remains constant +# ---------------------------------------------------- + +import os + +import swiftsimio +from matplotlib import pyplot as plt + +snapshot_base = "output" + + +def get_snapshot_list(snapshot_basename="output"): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + return snaplist + + +def compare_data(snaplist): + """ + Create and save the plot + """ + + HI = [] + HII = [] + HeI = [] + HeII = [] + HeIII = [] + + for filename in snaplist: + data = swiftsimio.load(filename) + + mXHI = data.gas.ion_mass_fractions.HI * data.gas.masses + mXHII = data.gas.ion_mass_fractions.HII * data.gas.masses + mXHeI = data.gas.ion_mass_fractions.HeI * data.gas.masses + mXHeII = data.gas.ion_mass_fractions.HeII * data.gas.masses + mXHeIII = data.gas.ion_mass_fractions.HeIII * data.gas.masses + + HI.append(mXHI.sum()) + HII.append(mXHII.sum()) + HeI.append(mXHeI.sum()) + HeII.append(mXHeII.sum()) + HeIII.append(mXHeIII.sum()) + + plt.figure() + plt.plot(range(len(snaplist)), HI, label="HI total mass") + plt.plot(range(len(snaplist)), HII, label="HII total mass") + plt.plot(range(len(snaplist)), HeI, label="HeI total mass") + plt.plot(range(len(snaplist)), HeII, label="HeII total mass") + plt.plot(range(len(snaplist)), HeIII, label="HeIII total mass") + plt.legend() + + # plt.show() + plt.tight_layout() + plt.savefig("total_abundancies.png", dpi=200) + + return + + +if __name__ == "__main__": + + snaplist = get_snapshot_list(snapshot_base) + compare_data(snaplist) diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/README b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/README index de93f3a8ef576145ed36fb8c6381723853ac0284..bd3cb9387cdbd5d3ea4ff4bb837dfc0142d85f5e 100644 --- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/README +++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/README @@ -2,9 +2,17 @@ Check that the initial condition setup for ionizing equilibrium works correctly. GEAR-RT can set the initial ionization state of the gas to the (collisional) ionization equilibrium at startup. This test makes sure that this setup works as -expected. To do this, we set up 10k particles in 1D with a very high range of +expected. To do this, we set up particles in with a very high range of different internal energies, and only run for 1 step. The correct ion mass fractions should already be set in the zeroth output. Compile swift with: - --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro-dimension=1 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none + --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none + + +Additional notes: + +- careful if running with periodicity on. The high difference in internal + energies across the box edge will induce a mass flux when running with + meshless schemes, and the solution won't be exact at output_0000 any + longer since the particles will be kicked before the output is written. diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml index d090322b2737dafe2c3af525f6883c08ef10533f..f43485abb188ea586eb40797e2acb28e3bf4221e 100644 --- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml +++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml @@ -1,16 +1,17 @@ MetaData: - run_name: "Ionization Equilibrium Test" + run_name: Ionization Equilibrium Test # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1123. - UnitLength_in_cgs: 45233. - UnitVelocity_in_cgs: 523. - UnitCurrent_in_cgs: 523152. - UnitTemp_in_cgs: 15323. + UnitMass_in_cgs: 1.98848e23 # 10^-10 M_sun in grams + UnitLength_in_cgs: 3.08567758e15 # 1 pc in cm + UnitVelocity_in_cgs: 1e5 # 1 km/s in cm/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin # Parameters governing the time integration TimeIntegration: + max_nr_rt_subcycles: 1 time_begin: 0. # The starting time of the simulation (in internal units). time_end: 6.250000e-03 # The end time of the simulation (in internal units). dt_min: 1.e-6 # The minimal time-step size of the simulation (in internal units). @@ -36,24 +37,21 @@ SPH: # Parameters related to the initial conditions InitialConditions: file_name: ./ionization_equilibrium_test.hdf5 # The file to read - periodic: 1 # peridoc ICs - -Scheduler: - max_top_level_cells: 12 + periodic: 0 # periodic ICs GEARRT: - f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.9 # CFL condition for RT, independent of hydro - photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed. - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter - star_emission_rates_LSol: [1., 1., 1., 1.] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity. - set_equilibrium_initial_ionization_mass_fractions: 1 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. - hydrogen_mass_fraction: 0.76 # total hydrogen mass fraction - set_initial_ionization_mass_fractions: 0 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) - mass_fraction_HI : 0.2 - mass_fraction_HII: 0.2 - mass_fraction_HeI: 0.2 - mass_fraction_HeII: 0.2 - mass_fraction_HeIII: 0.2 - stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. - stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 # CFL condition for RT, independent of hydro + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1., 1., 1.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + set_equilibrium_initial_ionization_mass_fractions: 1 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + hydrogen_mass_fraction: 0.76 # total hydrogen mass fraction + set_initial_ionization_mass_fractions: 0 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI : 0.2 + mass_fraction_HII: 0.2 + mass_fraction_HeI: 0.2 + mass_fraction_HeII: 0.2 + mass_fraction_HeIII: 0.2 + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py index a57098b4d22284d9001baa83723af170d395ec1d..33566530ab1d5b80fc63743a7c7f76a700957969 100755 --- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py +++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py @@ -1,60 +1,93 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + # ----------------------------------------------------------- -# Use 10k particles in 1D with a wide range of different +# Use particles in with a wide range of different # internal energies (hence temperatures) to test the # ionization equilibrium IC setup within GEAR-RT. # ----------------------------------------------------------- -from swiftsimio import Writer - -import unyt -import numpy as np import h5py -from matplotlib import pyplot as plt +import numpy as np +import unyt +from swiftsimio import Writer +from swiftsimio.units import cosmo_units # define unit system to use -unitsystem = unyt.unit_systems.cgs_unit_system +# unitsystem = unyt.unit_systems.cgs_unit_system +unitsystem = cosmo_units # set minimal and maximal specific internal energy umin = 1e9 * unyt.erg / unyt.g +umin = umin.to(unitsystem["energy"] / unitsystem["mass"]) umax = 1e20 * unyt.erg / unyt.g +umax = umax.to(unitsystem["energy"] / unitsystem["mass"]) # Box is 1 Mpc -boxsize = 1e15 * unitsystem["length"] - -# number of photon groups -nPhotonGroups = 1 +boxsize = 1 * unyt.kpc +boxsize = boxsize.to(unitsystem["length"]) # number of particles in each dimension -n_p = 10000 +n_p = 15 +nparts = n_p ** 3 # filename of ICs to be generated outputfilename = "ionization_equilibrium_test.hdf5" # particle positions -xp = unyt.unyt_array(np.zeros((n_p, 3), dtype=np.float), boxsize.units) +xp = unyt.unyt_array(np.zeros((nparts, 3), dtype=np.float64), boxsize.units) dx = boxsize / n_p + +pmass = 1.0 * unyt.M_Sun +pmass = pmass.to(unitsystem["mass"]) + +ind = 0 for i in range(n_p): - xp[i, 0] = (i + 0.5) * dx + x = (i + 0.5) * dx + for j in range(n_p): + y = (j + 0.5) * dx + for k in range(n_p): + z = (k + 0.5) * dx + + xp[ind] = (x, y, z) + ind += 1 + -w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, dimension=1) +w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, dimension=3) w.gas.coordinates = xp w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) -w.gas.masses = np.ones(xp.shape[0], dtype=np.float) * 1000 * unyt.g +w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * pmass w.gas.internal_energy = ( - np.logspace(np.log10(umin.v), np.log10(umax.v), n_p) * unyt.erg / unyt.g + np.logspace(np.log10(umin.v), np.log10(umax.v), nparts) * umax.units ) # Generate initial guess for smoothing lengths based on MIPS -w.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=1) +w.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=3) # If IDs are not present, this automatically generates w.write(outputfilename) # Generate initial ionization mass fractions. -# These are deliberately bogus so you can make sure that +# These are deliberately bogus, so you can make sure that # they have been changed by SWIFT. F = h5py.File(outputfilename, "r+") @@ -63,15 +96,15 @@ nparts = header.attrs["NumPart_ThisFile"][0] parts = F["/PartType0"] # this is bogus data for debugging checks. -HIdata = np.ones((nparts), dtype=np.float32) * 0.11 +HIdata = np.ones(nparts, dtype=np.float32) * 0.11 parts.create_dataset("MassFractionHI", data=HIdata) -HIIdata = np.ones((nparts), dtype=np.float32) * 0.22 +HIIdata = np.ones(nparts, dtype=np.float32) * 0.22 parts.create_dataset("MassFractionHII", data=HIIdata) -HeIdata = np.ones((nparts), dtype=np.float32) * 0.123 +HeIdata = np.ones(nparts, dtype=np.float32) * 0.123 parts.create_dataset("MassFractionHeI", data=HeIdata) -HeIIdata = np.ones((nparts), dtype=np.float32) * 0.234 +HeIIdata = np.ones(nparts, dtype=np.float32) * 0.234 parts.create_dataset("MassFractionHeII", data=HeIIdata) -HeIIIdata = np.ones((nparts), dtype=np.float32) * 0.345 +HeIIIdata = np.ones(nparts, dtype=np.float32) * 0.345 parts.create_dataset("MassFractionHeIII", data=HeIIIdata) F.close() diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py index 39400b07008e2aa87bb8e78cc5dbb30897b1137f..cd2f5c25b288304f0f6a8259aca3dbce7356c004 100755 --- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py +++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py @@ -1,4 +1,23 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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 numpy as np from matplotlib import pyplot as plt @@ -52,11 +71,13 @@ def gas_temperature(u, mu, gamma): data = swiftsimio.load("output_0000.hdf5") gas = data.gas u = gas.internal_energies.to("erg/g") -XHI = gas.ion_mass_fractions.HI -XHII = gas.ion_mass_fractions.HII -XHeI = gas.ion_mass_fractions.HeI -XHeII = gas.ion_mass_fractions.HeII -XHeIII = gas.ion_mass_fractions.HeIII +sortind = np.argsort(u) +u = u[sortind] +XHI = gas.ion_mass_fractions.HI[sortind] +XHII = gas.ion_mass_fractions.HII[sortind] +XHeI = gas.ion_mass_fractions.HeI[sortind] +XHeII = gas.ion_mass_fractions.HeII[sortind] +XHeIII = gas.ion_mass_fractions.HeIII[sortind] XH = XHI + XHII XHe = XHeI + XHeII + XHeIII mu = mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII) @@ -70,7 +91,7 @@ Tmax = T.v.max() u_theory, mu_theory, T_theory, XHI_theory, XHII_theory, XHeI_theory, XHeII_theory, XHeIII_theory = np.loadtxt( - "IonizationEquilibriumICSetupTestReference.txt", dtype=float, unpack=True + "IonizationEquilibriumICSetupTestReference.txt", dtype=np.float64, unpack=True ) # ------------------ @@ -108,6 +129,7 @@ ax3.semilogx(u, XHeIII, label="HeIII", ls="--", **plotkwargs) ax3.legend() ax3.set_xlabel("specific internal energy [erg/g]") ax3.set_ylabel("gas mass fractions [1]") +ax3.set_yscale("log") ax3.grid() diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/run.sh b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/run.sh index 9899b7cc7de3b1eae6f8ca3a8a571e94c6686c3e..3933238a5ebddeed6d724adf9627a98e156c8597 100755 --- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/run.sh +++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/run.sh @@ -9,7 +9,7 @@ if [ ! -f ./ionization_equilibrium_test.hdf5 ]; then fi # Run SWIFT with RT -../../swift \ +../../../swift \ --hydro \ --threads=1 \ --verbose=0 \ diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/README b/examples/RadiativeTransferTests/RandomizedBox_3D/README index ef80ebb3314670dbc6692e92fa6c042fb298f69f..1d8fb2855841811b5e7d05ad521e4f5af5e0a485 100644 --- a/examples/RadiativeTransferTests/RandomizedBox_3D/README +++ b/examples/RadiativeTransferTests/RandomizedBox_3D/README @@ -1,6 +1,6 @@ This test case contains only gas and stars in a periodic box. The contents have no physical meaning, and are background particles on a regular grid overlapping -a randomly sampled sine wave particle distribution. The purpose of this test are +a randomly sampled sine wave particle distribution. The purposes of this test are as follows: - Neither the stars nor the gas particles are updated all at the same time. @@ -12,3 +12,15 @@ as follows: You can and should run the same sanity check scripts as in ../UniformBox_3D to check that everything's right. + +Compile swift with: + --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro-dimension=3 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --enable-debugging-checks --with-grackle=$GRACKLE_ROOT + + + +Some specific notes for debugging: + +- when running on 2 MPI ranks (at least in the first few steps) cells 27 and + 151 have proxies on the other rank. However, those are made for the gravity + tasks, not for the RT tasks. So their foreign counterparts will have no RT + tasks at all. diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py b/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py index 452390e38ea28016421a7e43969c13bb180630b7..e50dd0fa48e91b7b9c47d48ab023232323cc2a6b 100755 --- a/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py +++ b/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py @@ -26,14 +26,11 @@ # gas and stars that are sampled from a sine wave # --------------------------------------------------------------------- +import numpy as np +import unyt from swiftsimio import Writer from swiftsimio.units import cosmo_units -import unyt -import numpy as np -from scipy import special as sp -from matplotlib import pyplot as plt - np.random.seed(666) # Box is 1 Mpc @@ -60,9 +57,9 @@ for i in range(n_p): y = (j + 0.501) * dx for k in range(n_p): z = (k + 0.501) * dx - xp.append(np.array([x, y, z], dtype=np.float)) + xp.append(np.array([x, y, z], dtype=np.float64)) xp = np.array(xp) -velp = np.zeros((n_p ** 3, 3), dtype=np.float) +velp = np.zeros((n_p ** 3, 3), dtype=np.float64) for i in range(n_s): x = (i + 0.001) * ds @@ -70,9 +67,9 @@ for i in range(n_s): y = (j + 0.001) * ds for k in range(n_s): z = (k + 0.001) * ds - xs.append(np.array([x, y, z], dtype=np.float)) + xs.append(np.array([x, y, z], dtype=np.float64)) xs = np.array(xs) -vels = np.zeros((n_s ** 3, 3), dtype=np.float) +vels = np.zeros((n_s ** 3, 3), dtype=np.float64) amplitude = 0.5 @@ -81,15 +78,15 @@ vels_max = 200.0 def sine(x, amplitude=1.0): - # raise the sine by 1.01 so you don't get negative probablities + # raise the sine by 1.01, so you don't get negative probablities return amplitude * np.sin(x / boxsize.value * 2.0 * np.pi) + 1.01 def sample(n): samples = 0 - keep = np.zeros((n, 3), dtype=np.float) + keep = np.zeros((n, 3), dtype=np.float64) while samples < n: - sample = np.zeros(3, dtype=np.float) + sample = np.zeros(3, dtype=np.float64) found = False while not found: @@ -150,16 +147,18 @@ vs_tot = np.vstack((vels, vels_sampled)) vs = unyt.unyt_array(vs_tot, unyt.km / unyt.s) -w = Writer(cosmo_units, boxsize) +w = Writer(cosmo_units, boxsize, compress=False) w.gas.coordinates = xp w.stars.coordinates = xs w.gas.velocities = vp w.stars.velocities = vs -w.gas.masses = np.ones(xp.shape[0], dtype=np.float) * 1e6 * unyt.msun +w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 1e6 * unyt.msun w.stars.masses = np.random.uniform(1e8, 1e10, size=xs.shape[0]) * unyt.msun # Generate internal energy corresponding to 10^4 K w.gas.internal_energy = ( - np.ones(xp.shape[0], dtype=float) * (1e4 * unyt.kb * unyt.K) / (1e6 * unyt.msun) + np.ones(xp.shape[0], dtype=np.float64) + * (1e4 * unyt.kb * unyt.K) + / (1e6 * unyt.msun) ) diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py index e12c63ac7b231826dfe63e186c6076e768abb5de..6837842e09302839460955eaa855d8c16ecc50fb 100755 --- a/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py @@ -1,4 +1,23 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## # ---------------------------------------------------- # plots 2D projection of radiation energy and fluxes @@ -8,18 +27,20 @@ # all snapshots available in the workdir. # ---------------------------------------------------- -import sys +import gc import os -import swiftsimio +import sys + +import matplotlib as mpl import numpy as np -import gc +import swiftsimio import unyt from matplotlib import pyplot as plt -import matplotlib as mpl +from matplotlib.colors import SymLogNorm from mpl_toolkits.axes_grid1 import make_axes_locatable -from matplotlib.colors import SymLogNorm, LogNorm # Parameters users should/may tweak +# ----------------------------------- # plot all groups and all photon quantities plot_all_data = True @@ -158,9 +179,6 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): ax = fig.add_subplot(ngroups, 4, g * 4 + 1) if energy_boundaries is not None: - # imshow_kwargs["norm"] = None - # imshow_kwargs["vmin"] = energy_boundaries[g][0] - # imshow_kwargs["vmax"] = energy_boundaries[g][1] imshow_kwargs["norm"] = SymLogNorm( vmin=energy_boundaries[g][0], vmax=energy_boundaries[g][1], @@ -259,12 +277,6 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): if energy_boundaries is not None: imshow_kwargs["vmin"] = energy_boundaries[g][0] imshow_kwargs["vmax"] = energy_boundaries[g][1] - # imshow_kwargs["norm"] = SymLogNorm( - # vmin=energy_boundaries[g][0], - # vmax=energy_boundaries[g][1], - # linthresh = 1e-2, - # base=10 - # ) im = ax.imshow(photon_map.T, **imshow_kwargs) set_colorbar(ax, im) ax.set_title("Group {0:2d}".format(g + 1)) @@ -273,7 +285,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): ax.set_ylabel("Energies [$" + energy_units_str + "$]") # Add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: title += ", $z$ = {0:.2e}".format(meta.z) title += ", $t$ = {0:.2e}".format(meta.time.to(time_units)) @@ -331,7 +343,6 @@ def get_minmax_vals(snaplist): dirmin = [] dirmax = [] for direction in ["X", "Y", "Z"]: - new_attribute_str = "radiation_flux" + str(g + 1) + direction f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) dirmin.append(f.min()) dirmax.append(f.max()) diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml b/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml index 7c67e8b5e9635bd6faf0980cc4a107a862394086..340aac434b3d9fe08fc45c577abd379e41b87bef 100644 --- a/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml +++ b/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml @@ -1,5 +1,5 @@ MetaData: - run_name: "randomized-sine" + run_name: randomized-sine # Define the system of units to use internally. InternalUnitSystem: @@ -11,10 +11,11 @@ InternalUnitSystem: # Parameters governing the time integration TimeIntegration: + max_nr_rt_subcycles: 512 time_begin: 0. # The starting time of the simulation (in internal units). time_end: 0.1 # The end time of the simulation (in internal units). - dt_min: 1.e-8 # The minimal time-step size of the simulation (in internal units). - dt_max: 1.e-3 # The maximal time-step size of the simulation (in internal units). + dt_min: 1.e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-2 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots Snapshots: @@ -36,7 +37,7 @@ SPH: # Parameters related to the initial conditions InitialConditions: file_name: ./randomized-sine.hdf5 # The file to read - periodic: 1 # peridoc ICs + periodic: 1 # periodic ICs # Properties of the stars Stars: @@ -44,17 +45,19 @@ Stars: birth_time: -1 # Give the star in the ICs a decent birth time Scheduler: - max_top_level_cells: 64 + dependency_graph_frequency: 0 # (Optional) Dumping frequency of the dependency graph. By default, writes only at the first step. Restarts: delta_hours: 72 # (Optional) decimal hours between dumps of restart files. + enable: 1 # (Optional) whether to enable dumping restarts at fixed intervals. + stop_steps: 128 # (Optional) how many steps to process before checking if the <subdir>/stop file exists. When present the application will attempt to exit early, dumping restart files first. # Parameters for the self-gravity scheme Gravity: - mesh_side_length: 12 # Number of cells along each axis for the periodic gravity mesh. + mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. eta: 0.025 # Constant dimensionless multiplier for time integration. MAC: adaptive # Choice of mulitpole acceptance criterion: 'adaptive' OR 'geometric'. - epsilon_fmm: 0.01 # Tolerance parameter for the adaptive multipole acceptance criterion. + epsilon_fmm: 0.001 # Tolerance parameter for the adaptive multipole acceptance criterion. theta_cr: 0.7 # Opening angle for the purely gemoetric 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). @@ -63,18 +66,18 @@ Gravity: # softening_ratio_background: 0.04 # Fraction of the mean inter-particle separation to use as Plummer-equivalent softening for the background DM particles. GEARRT: - f_reduce_c: 1e-3 # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.99 # CFL condition for RT, independent of hydro - photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter - star_emission_rates_LSol: [20., 30., 40., 50.] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. - hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas - stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. - stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. - set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) - mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value - mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value - mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value - mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value - mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value - skip_thermochemistry: 1 # skip thermochemistry. + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.6 # CFL condition for RT, independent of hydro + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [20., 30., 40.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + skip_thermochemistry: 1 # skip thermochemistry. diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh b/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh index 7ab69b15f5a35c0aee1fca4cd3ede2bf07bc3745..e2171960cfc21169ffb767ccf265cc9071697806 100755 --- a/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh +++ b/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh @@ -9,11 +9,37 @@ if [ ! -f 'randomized-sine.hdf5' ]; then python3 makeIC.py fi +# use cmdline args as shortcut to run with debugger/MPI +# -g run with gdb +# -m run with MPI +# -mg run with MPI and gdb in individual xterm windows +# -ml run with MPI and write individual output file per MPI rank +cmd=../../../swift +if [ $# -gt 0 ]; then + case "$1" in + -g | g | gdb) + cmd='gdb --args ../../../swift' + ;; + -m | m | mpi) + cmd='mpirun -n 3 ../../../swift_mpi' + ;; + -mg | -gm | gm | mg | gmpi | gdbmpi ) + cmd='mpirun -n 3 xterm -e gdb -ex run --args ../../../swift_mpi' + ;; + -ml | ml | lm | mpilog | logmpi) + cmd='mpirun -n 2 --output-filename individual_rank_output --merge-stderr-to-stdout ../../../swift_mpi' + ;; + *) + echo unknown cmdline param, running without gdb + ;; + esac +fi + # Run SWIFT with RT -../../swift \ +$cmd \ --hydro \ - --threads=9 \ - --verbose=0 \ + --threads=3 \ + --verbose=0 \ --radiation \ --self-gravity \ --stars \ diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/README b/examples/RadiativeTransferTests/StromgrenSphere_2D/README index 0a0f3010ca265be9e4433fe7fcbc3762425c60bf..d291adc607e54c1ab20e561f725fe54a535e0416 100644 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/README +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/README @@ -1,22 +1,32 @@ Strömgen Sphere example in 2D +----------------------------- -For now, there are no gas interactions, and we use this -example to perform tests and checks on photon injection -and their propagation. The example contains 1 star in a -glass hydro distribution in 2D. (State 09/2021) +This directory contains two examples in one: -Intented to compile with : - --with-rt=GEAR_1 --with-hydro-dimension=2 --with-rt-riemann-solver=GLF + - run a Strömgren sphere example, where a single central + star ionizes the surrounding medium in a 2D plane. + To run this example, use the provided `run.sh` script. + This script will then make use of the `makeIC.py` and + `plotSolution.py` script. -Scripts in this directory: + - run a propagation test of photons emitted from a single + central source in an otherwise uniform 2D plane. + To run this example, use the provided `runPropagationTest.sh` script. + This script will then make use of the `makePropagationTestIC.py` and + `plotPhotonPropagationCheck.py` script. - - plotRadiationProjection.py: - plot photon energies and fluxes as a 2D image +Additional scripts: + - `plotRadiationProjection.py`: Plots a projection of the radiation + quantities (energies, fluxes). NOTE: you might need to change the + 'snapshot_base' variable at the top of the script depending on which + solutions you want to plot. - - plotPhotonPropagationCheck.py: - Plots radiation energies and fluxes as function of - radius centered on the injecting star. - NOTE: Make sure that you use a photon group that - doesn't interact with the gas to check the - propagation properly. You'll have to hard-code - the index of the photon group in the script itself. +To use the GEAR RT model, compile with : + --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro-dimension=2 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + +To use the SPHM1 RT model, compile with : + --with-rt=SPHM1RT_4 --with-hydro-dimension=2 --with-stars=basic --with-feedback=none --with-sundials=$SUNDIALS_ROOT + +IMPORTANT: Need SUNDIALS version = 5 . +SUNDIALS_ROOT is the root directory that contains the lib and include directories, e.g. on cosma: +SUNDIALS_ROOT=/cosma/local/sundials/5.1.0/ diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/getGlass.sh b/examples/RadiativeTransferTests/StromgrenSphere_2D/getGlass.sh index ae3c977064f5e7a408aa249c5fd9089b3c52ecb1..2425f48d84640360aa7c7563aa5dacc6287cfbf8 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/getGlass.sh +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/getGlass.sh @@ -1,2 +1,2 @@ #!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 \ No newline at end of file diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py index f57ee898b7fe7427734699d33cfbb67188e60e0c..fbba2ce80b42b3e24bdb86c0d5308630457ab15b 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py @@ -1,14 +1,33 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + # --------------------------------------------------------------------- # Add a single star in the center of a glass distribution # --------------------------------------------------------------------- +import h5py +import numpy as np +import unyt from swiftsimio import Writer from swiftsimio.units import cosmo_units -import unyt -import numpy as np -import h5py gamma = 5.0 / 3.0 @@ -130,15 +149,8 @@ def get_number_densities_array(Temp, XH, XHe): # n_He = X_He * rho_gas / m_He = (1 - X_H) / (4 X_H) * n_H # = X_He / 4(1 - X_He) * nH = y * nH - # if XH == 0: - # nH = 0.0 - # nHe = 1.0 - # else: - # nH = XH - # nHe = XHe / 4 - - nH = np.zeros(XH.shape, dtype=float) - nHe = np.zeros(XH.shape, dtype=float) + nH = np.zeros(XH.shape, dtype=np.float64) + nHe = np.zeros(XH.shape, dtype=np.float64) mask = XH == 0 nH[mask] = 0.0 @@ -148,13 +160,13 @@ def get_number_densities_array(Temp, XH, XHe): nH[inv_mask] = XH[inv_mask] nHe[inv_mask] = 0.25 * XHe[inv_mask] - # NOTE: This is not the ionization threshold! - nH0 = np.zeros(XH.shape, dtype=float) - nHp = np.zeros(XH.shape, dtype=float) - nHe0 = np.zeros(XH.shape, dtype=float) - nHep = np.zeros(XH.shape, dtype=float) - nHepp = np.zeros(XH.shape, dtype=float) + nH0 = np.zeros(XH.shape, dtype=np.float64) + nHp = np.zeros(XH.shape, dtype=np.float64) + nHe0 = np.zeros(XH.shape, dtype=np.float64) + nHep = np.zeros(XH.shape, dtype=np.float64) + nHepp = np.zeros(XH.shape, dtype=np.float64) + # NOTE: This is not the ionization threshold! neutral = Temp < 5000 * unyt.K nH0[neutral] = nH[neutral] @@ -291,8 +303,8 @@ def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): if __name__ == "__main__": - glass = h5py.File("glassPlane_64.hdf5", "r") - # glass = h5py.File("glassPlane_128.hdf5", "r") + # glass = h5py.File("glassPlane_64.hdf5", "r") + glass = h5py.File("glassPlane_128.hdf5", "r") parts = glass["PartType0"] xp = parts["Coordinates"][:] h = parts["SmoothingLength"][:] @@ -326,7 +338,7 @@ if __name__ == "__main__": w = Writer(unit_system=cosmo_units, box_size=boxsize, dimension=2) - # write particle positions ans smoothing lengths + # write particle positions and smoothing lengths w.gas.coordinates = xp w.stars.coordinates = xs w.gas.velocities = np.zeros(xp.shape) * (unitL / unyt.Myr) @@ -336,21 +348,21 @@ if __name__ == "__main__": # get gas masses nH = 1e-3 * unyt.cm ** (-3) - rhoH = nH * unyt.proton_mass - Mtot = rhoH * edgelen ** 3 + rho_gas = nH * unyt.proton_mass + Mtot = rho_gas * edgelen ** 3 mpart = Mtot / xp.shape[0] mpart = mpart.to(cosmo_units["mass"]) - w.gas.masses = np.ones(xp.shape[0], dtype=np.float) * mpart - w.stars.masses = np.ones(xs.shape[0], dtype=np.float) * mpart + w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * mpart + w.stars.masses = np.ones(xs.shape[0], dtype=np.float64) * mpart # get gas internal energy for a given temperature and composition XH = 1.0 # hydrogen mass fraction - XHe = 0.0 # hydrogen mass fraction + XHe = 0.0 # helium mass fraction T = 100 * unyt.K XHI, XHII, XHeI, XHeII, XHeIII = get_mass_fractions(T, XH, XHe) mu = mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII) internal_energy = internal_energy(T, mu) - w.gas.internal_energy = np.ones(xp.shape[0], dtype=np.float) * internal_energy + w.gas.internal_energy = np.ones(xp.shape[0], dtype=np.float64) * internal_energy w.write("stromgrenSphere-2D.hdf5") diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/makePropagationTestIC.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/makePropagationTestIC.py new file mode 100755 index 0000000000000000000000000000000000000000..c79156e5d56bdb26d945d41332109d48d4a6e67b --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/makePropagationTestIC.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# --------------------------------------------------------------------- +# Add a single star in the center of a glass distribution +# Intended for the photon propagation test. +# --------------------------------------------------------------------- + +import h5py +import numpy as np +import unyt +from swiftsimio import Writer + +glass = h5py.File("glassPlane_128.hdf5", "r") +parts = glass["PartType0"] +xp = parts["Coordinates"][:] +h = parts["SmoothingLength"][:] +glass.close() + +# find particles closest to the center +# and select a couple of them to put the star in their middle +r = np.sqrt(np.sum((0.5 - xp) ** 2, axis=1)) +mininds = np.argsort(r) +center_parts = xp[mininds[:4]] +xs = center_parts.sum(axis=0) / center_parts.shape[0] + +# Double-check all particles for boundaries +for i in range(2): + mask = xp[:, i] < 0.0 + xp[mask, i] += 1.0 + mask = xp[:, i] > 1.0 + xp[mask, i] -= 1.0 + + +unitL = unyt.cm +t_end = 1e-3 * unyt.s +edgelen = unyt.c.to("cm/s") * t_end * 2.0 +edgelen = edgelen.to(unitL) +boxsize = unyt.unyt_array([edgelen.v, edgelen.v, 0.0], unitL) + +xs = unyt.unyt_array( + [np.array([xs[0] * edgelen, xs[1] * edgelen, 0.0 * edgelen])], unitL +) +xp *= edgelen +h *= edgelen + + +w = Writer(unit_system=unyt.unit_systems.cgs_unit_system, box_size=boxsize, dimension=2) + +w.gas.coordinates = xp +w.stars.coordinates = xs +w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) +w.stars.velocities = np.zeros(xs.shape) * (unyt.cm / unyt.s) +w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 100 * unyt.g +w.stars.masses = np.ones(xs.shape[0], dtype=np.float64) * 100 * unyt.g +w.gas.internal_energy = ( + np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g +) + +w.gas.smoothing_length = h +w.stars.smoothing_length = w.gas.smoothing_length[:1] + +w.write("propagationTest-2D.hdf5") diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py index c8eb62f5dbe47841e957924b8e9d7333f9f32c52..97420caa8bc84af9b0209c4de24c55b1ce0c075e 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py @@ -1,4 +1,23 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## # ---------------------------------------------------------------------- # plots @@ -16,21 +35,27 @@ # correctly. # ---------------------------------------------------------------------- -import sys, os, gc -import swiftsimio +import gc +import os +import sys + +import matplotlib as mpl import numpy as np +import swiftsimio import unyt from matplotlib import pyplot as plt -import matplotlib as mpl from scipy import stats -from scipy.optimize import curve_fit # Parameters users should/may tweak # snapshot basename -snapshot_base = "output" +snapshot_base = "propagation_test" + +# additional anisotropy estimate plot? +plot_anisotropy_estimate = False # which photon group to use. +# NOTE: array index, not group number (which starts at 1 for GEAR) group_index = 0 scatterplot_kwargs = { @@ -55,7 +80,7 @@ except IndexError: mpl.rcParams["text.usetex"] = True -def analytical_intgrated_energy_solution(L, time, r, rmax): +def analytical_integrated_energy_solution(L, time, r, rmax): """ Compute analytical solution for the sum of the energy in bins for given injection rate <L> at time <time> @@ -100,14 +125,20 @@ def analytical_energy_solution(L, time, r, rmax): return r_center, E -def analytical_flux_magnitude_solution(L, time, r, rmax): +def analytical_flux_magnitude_solution(L, time, r, rmax, scheme): """ For radiation that doesn't interact with the gas, the flux should correspond to the free streaming (optically thin) limit. So compute and return that. """ r, E = analytical_energy_solution(L, time, r, rmax) - F = unyt.c.to(r.units / time.units) * E / r.units ** 3 + if scheme.startswith("GEAR M1closure"): + F = unyt.c.to(r.units / time.units) * E / r.units ** 3 + elif scheme.startswith("SPH M1closure"): + F = unyt.c.to(r.units / time.units) * E + else: + raise ValueError("Unknown RT scheme", scheme) + return r, F @@ -160,6 +191,8 @@ def plot_photons(filename, emin, emax, fmin, fmax): boxsize = meta.boxsize edgelen = min(boxsize[0], boxsize[1]) + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + xstar = data.stars.coordinates xpart = data.gas.coordinates dxp = xpart - xstar @@ -168,25 +201,66 @@ def plot_photons(filename, emin, emax, fmin, fmax): time = meta.time r_expect = meta.time * meta.reduced_lightspeed - use_const_emission_rates = bool(meta.parameters["GEARRT:use_const_emission_rates"]) + use_const_emission_rates = False + if scheme.startswith("GEAR M1closure"): + luminosity_model = meta.parameters["GEARRT:stellar_luminosity_model"] + use_const_emission_rates = luminosity_model.decode("utf-8") == "const" + elif scheme.startswith("SPH M1closure"): + use_const_emission_rates = bool( + meta.parameters["SPHM1RT:use_const_emission_rates"] + ) + else: + print("RT scheme not identified. Exit.") + exit() + L = None + if use_const_emission_rates: # read emission rate parameter as string - emissionstr = meta.parameters["GEARRT:star_emission_rates_LSol"].decode("utf-8") - # clean string up - if emissionstr.startswith("["): - emissionstr = emissionstr[1:] - if emissionstr.endswith("]"): - emissionstr = emissionstr[:-1] - - # transform string values to floats with unyts - emissions = emissionstr.split(",") - emlist = [] - for er in emissions: - emlist.append(float(er)) - const_emission_rates = unyt.unyt_array(emlist, unyt.L_Sun) - L = const_emission_rates[group_index] - - fig = plt.figure(figsize=(5 * 4, 5.5), dpi=200) + if scheme.startswith("GEAR M1closure"): + emissionstr = meta.parameters[ + "GEARRT:const_stellar_luminosities_LSol" + ].decode("utf-8") + # clean string up + if emissionstr.startswith("["): + emissionstr = emissionstr[1:] + if emissionstr.endswith("]"): + emissionstr = emissionstr[:-1] + + # transform string values to floats with unyts + emissions = emissionstr.split(",") + emlist = [] + for er in emissions: + emlist.append(float(er)) + const_emission_rates = unyt.unyt_array(emlist, unyt.L_Sun) + L = const_emission_rates[group_index] + elif scheme.startswith("SPH M1closure"): + unit_l_in_cgs = meta.units.length + unit_v_in_cgs = meta.units.length / meta.units.time + unit_m_in_cgs = meta.units.mass + emissionstr = meta.parameters["SPHM1RT:star_emission_rates"].decode("utf-8") + # clean string up + if emissionstr.startswith("["): + emissionstr = emissionstr[1:] + if emissionstr.endswith("]"): + emissionstr = emissionstr[:-1] + + # transform string values to floats with unyts + emissions = emissionstr.split(",") + emlist = [] + for er in emissions: + emlist.append( + float(er) * unit_m_in_cgs * unit_v_in_cgs ** 3 / unit_l_in_cgs + ) + const_emission_rates = unyt.unyt_array(emlist, "erg/s") + L = const_emission_rates[group_index] + else: + raise ValueError("Unknown RT scheme", scheme) + + if plot_anisotropy_estimate: + ncols = 4 + else: + ncols = 3 + fig = plt.figure(figsize=(5 * ncols, 5.5), dpi=200) nbins = 100 r_bin_edges = np.linspace(0.5 * edgelen * 1e-2, 0.507 * edgelen, nbins + 1) @@ -204,8 +278,6 @@ def plot_photons(filename, emin, emax, fmin, fmax): Fy = getattr(data.gas.photon_fluxes, "Group" + str(group_index + 1) + "Y") fmag = np.sqrt(Fx ** 2 + Fy ** 2) - sum_fmag = fmag.sum() - max_fmag = fmag.max() particle_count, _ = np.histogram( r, bins=r_analytical_bin_edges, @@ -220,7 +292,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): # ------------------------ # Plot photon energies # ------------------------ - ax1 = fig.add_subplot(1, 4, 1) + ax1 = fig.add_subplot(1, ncols, 1) ax1.set_title("Particle Radiation Energies") ax1.set_ylabel("Photon Energy [$" + energy_units_str + "$]") @@ -278,7 +350,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): # ------------------------------ # Plot binned photon energies # ------------------------------ - ax2 = fig.add_subplot(1, 4, 2) + ax2 = fig.add_subplot(1, ncols, 2) ax2.set_title("Total Radiation Energy in radial bins") ax2.set_ylabel("Total Photon Energy [$" + energy_units_str + "$]") @@ -301,7 +373,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): if use_const_emission_rates: # plot entire expected solution # Note: you need to use the same bins as for the actual results - rA, EA = analytical_intgrated_energy_solution(L, time, r_bin_edges, r_expect) + rA, EA = analytical_integrated_energy_solution(L, time, r_bin_edges, r_expect) ax2.plot( rA, @@ -323,7 +395,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): # ------------------------------ # Plot photon fluxes # ------------------------------ - ax3 = fig.add_subplot(1, 4, 3) + ax3 = fig.add_subplot(1, ncols, 3) ax3.set_title("Particle Radiation Flux Magnitudes") ax3.set_ylabel("Photon Flux Magnitude [$" + flux_units_str + "$]") @@ -349,7 +421,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): if use_const_emission_rates: # plot entire expected solution rA, FA = analytical_flux_magnitude_solution( - L, time, r_analytical_bin_edges, r_expect + L, time, r_analytical_bin_edges, r_expect, scheme ) mask = particle_count > 0 @@ -387,55 +459,57 @@ def plot_photons(filename, emin, emax, fmin, fmax): # Plot photon flux sum # ------------------------------ - ax4 = fig.add_subplot(1, 4, 4) - ax4.set_title("Vectorial Sum of Radiation Flux in radial bins") - ax4.set_ylabel("[1]") + if plot_anisotropy_estimate: - fmag_sum_bin, _, _ = stats.binned_statistic( - r, - fmag, - statistic="sum", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - mask_sum = fmag_sum_bin > 0 - fmag_max_bin, _, _ = stats.binned_statistic( - r, - fmag, - statistic="max", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - mask_max = fmag_max_bin > 0 - Fx_sum_bin, _, _ = stats.binned_statistic( - r, - Fx, - statistic="sum", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - Fy_sum_bin, _, _ = stats.binned_statistic( - r, - Fy, - statistic="sum", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - F_sum_bin = np.sqrt(Fx_sum_bin ** 2 + Fy_sum_bin ** 2) + ax4 = fig.add_subplot(1, ncols, 4) + ax4.set_title("Vectorial Sum of Radiation Flux in radial bins") + ax4.set_ylabel("[1]") - ax4.plot( - r_bin_centres[mask_sum], - F_sum_bin[mask_sum] / fmag_sum_bin[mask_sum], - **lineplot_kwargs, - label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", - ) - ax4.plot( - r_bin_centres[mask_max], - F_sum_bin[mask_max] / fmag_max_bin[mask_max], - **lineplot_kwargs, - linestyle="--", - label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", - ) + fmag_sum_bin, _, _ = stats.binned_statistic( + r, + fmag, + statistic="sum", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + mask_sum = fmag_sum_bin > 0 + fmag_max_bin, _, _ = stats.binned_statistic( + r, + fmag, + statistic="max", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + mask_max = fmag_max_bin > 0 + Fx_sum_bin, _, _ = stats.binned_statistic( + r, + Fx, + statistic="sum", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + Fy_sum_bin, _, _ = stats.binned_statistic( + r, + Fy, + statistic="sum", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + F_sum_bin = np.sqrt(Fx_sum_bin ** 2 + Fy_sum_bin ** 2) + + ax4.plot( + r_bin_centres[mask_sum], + F_sum_bin[mask_sum] / fmag_sum_bin[mask_sum], + **lineplot_kwargs, + label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", + ) + ax4.plot( + r_bin_centres[mask_max], + F_sum_bin[mask_max] / fmag_max_bin[mask_max], + **lineplot_kwargs, + linestyle="--", + label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", + ) # ------------------------------------------- # Cosmetics that all axes have in common @@ -447,7 +521,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): ax.legend(fontsize="x-small") # Add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: title += ", $z$ = {0:.2e}".format(meta.z) title += ", $t$ = {0:.2e}".format(meta.time) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py index 148c5e0c032e3ead59689881a3ec9ed1eca2310d..06a6e95e32900d3fcde3748fefe74d8825093075 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py @@ -1,4 +1,24 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## + # ---------------------------------------------------- # plots 2D projection of radiation energy and fluxes @@ -8,40 +28,42 @@ # all snapshots available in the workdir. # ---------------------------------------------------- -import sys +import gc import os +import sys + +import matplotlib as mpl import swiftsimio -import numpy as np -import gc import unyt from matplotlib import pyplot as plt -import matplotlib as mpl -from mpl_toolkits.axes_grid1 import make_axes_locatable from matplotlib.colors import SymLogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable # Parameters users should/may tweak # plot all groups and all photon quantities plot_all_data = True -# snapshot basename -snapshot_base = "output" + +# plotting propagation tests or Stromgren sphere? +do_stromgren_sphere = True + # fancy up the plots a bit? fancy = True +mpl.rcParams["text.usetex"] = True + # parameters for imshow plots imshow_kwargs = {"origin": "lower", "cmap": "viridis"} # parameters for swiftsimio projections projection_kwargs = {"resolution": 1024, "parallel": True} -# Set Units of your choice -energy_units = unyt.erg -energy_units_str = "\\rm{erg}" -flux_units = 1e10 * energy_units / unyt.cm ** 2 / unyt.s -flux_units_str = "10^{10} \\rm{erg} \\ \\rm{cm}^{-2} \\ \\rm{s}^{-1}" -time_units = unyt.s -# ----------------------------------------------------------------------- +# snapshot basename +snapshot_base = "propagation_test" +if do_stromgren_sphere: + snapshot_base = "output" +# ----------------------------------------------------------------------- # Read in cmdline arg: Are we plotting only one snapshot, or all? plot_all = False @@ -50,7 +72,38 @@ try: except IndexError: plot_all = True -mpl.rcParams["text.usetex"] = True + +# get the unit for rt according to the RT scheme: +def get_units(scheme, unit_system="cgs_units"): + if unit_system == "cgs_units": + time_units = unyt.s + energy_units = unyt.erg + energy_units_str = "\\rm{erg}" + if scheme.startswith("GEAR M1closure"): + flux_units = 1e-2 * energy_units / unyt.cm ** 2 / unyt.s + flux_units_str = "10^{-2} \\rm{erg} \\ \\rm{cm}^{-2} \\ \\rm{s}^{-1}" + elif scheme.startswith("SPH M1closure"): + flux_units = 1e10 * energy_units * unyt.cm / unyt.s + flux_units_str = "10^{10} \\rm{erg} \\ \\rm{cm} \\ \\rm{s}^{-1}" + else: + raise ValueError("Unkown scheme", scheme) + + elif unit_system == "stromgren_units": + time_units = unyt.Myr + energy_units = 1e50 * unyt.erg + energy_units_str = "10^{50} \\rm{erg}" + if scheme.startswith("GEAR M1closure"): + flux_units = 1e50 * unyt.erg / unyt.kpc ** 2 / unyt.Gyr + flux_units_str = "10^{50} \\rm{erg} \\ \\rm{kpc}^{-2} \\ \\rm{Gyr}^{-1}" + elif scheme.startswith("SPH M1closure"): + flux_units = 1e50 * unyt.erg * unyt.kpc / unyt.Gyr + flux_units_str = "10^{50} \\rm{erg} \\ \\rm{kpc} \\ \\rm{Gyr}^{-1}" + else: + raise ValueError("Unkown scheme", scheme) + else: + raise ValueError("Unit system not identified. Exit.") + + return time_units, energy_units, energy_units_str, flux_units, flux_units_str def get_snapshot_list(snapshot_basename="output"): @@ -76,6 +129,10 @@ def get_snapshot_list(snapshot_basename="output"): quit(1) snaplist.append(fname) + if len(snaplist) == 0: + print("Didn't find any snapshots with basename '", snapshot_basename, "'") + quit(1) + return snaplist @@ -106,6 +163,15 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): # Read in data first data = swiftsimio.load(filename) meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + if do_stromgren_sphere: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="stromgren_units" + ) + else: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="cgs_units" + ) ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) xlabel_units_str = meta.boxsize.units.latex_representation() @@ -139,7 +205,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): if plot_all_data: fig = plt.figure(figsize=(5 * 3, 5.05 * ngroups), dpi=200) - figname = filename[:-5] + "-all-quantities.png" + figname = filename[:-5] + "-radiation.png" for g in range(ngroups): @@ -238,7 +304,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): ax.set_ylabel("Energies [$" + energy_units_str + "$]") # Add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: title += ", $z$ = {0:.2e}".format(meta.z) title += ", $t$ = {0:.2e}".format(meta.time.to(time_units)) @@ -254,7 +320,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): def get_minmax_vals(snaplist): """ - Find minimal and maximal values for energy and flux + Find minimal and maximal values for energy and flux, so you can fix axes limits over all snapshots snaplist: list of snapshot filenames @@ -276,6 +342,15 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + if do_stromgren_sphere: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="stromgren_units" + ) + else: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="cgs_units" + ) ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) emin_group = [] @@ -296,7 +371,6 @@ def get_minmax_vals(snaplist): dirmin = [] dirmax = [] for direction in ["X", "Y"]: - new_attribute_str = "radiation_flux" + str(g + 1) + direction f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) dirmin.append(f.min()) dirmax.append(f.max()) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py index 9417b943d80d7f5871d38893647b415494a6a1be..ea136728f1bbe3f4f930ed0bc31a5e4556092e82 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py @@ -1,21 +1,40 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## + # ---------------------------------------------------- # Plot the hydrogen number density, pressure, # temperature, and hydrogen mass fraction. # ---------------------------------------------------- -import sys +import gc import os +import sys + +import matplotlib as mpl import swiftsimio -import numpy as np -import gc import unyt from matplotlib import pyplot as plt -import matplotlib as mpl +from matplotlib.colors import LogNorm from mpl_toolkits.axes_grid1 import make_axes_locatable -from matplotlib.colors import SymLogNorm, LogNorm -from swiftsimio.visualisation.slice import slice_gas # Parameters users should/may tweak @@ -176,7 +195,7 @@ def plot_result(filename): density_map = mass_weighted_density_map / mass_map density_map = density_map[cutoff:-cutoff, cutoff:-cutoff] density_map = density_map.to("kg/cm**3") - density_map = density_map / unyt.proton_mass + number_density_map = density_map / unyt.proton_mass temperature_map = mass_weighted_temperature_map / mass_map temperature_map = temperature_map[cutoff:-cutoff, cutoff:-cutoff] @@ -192,14 +211,14 @@ def plot_result(filename): try: im1 = ax1.imshow( - density_map.T, + number_density_map.T, **imshow_kwargs, norm=LogNorm(vmin=1e-4, vmax=1e-1), cmap="bone", ) set_colorbar(ax1, im1) - ax1.set_title(r"Hydrogen Number Density [cm$^{-3}$]") - except ValueError: + ax1.set_title(r"Gas Number Density [cm$^{-3}$]") + except (ValueError, TypeError): print( filename, "densities wrong? min", @@ -217,8 +236,8 @@ def plot_result(filename): cmap="cividis", ) set_colorbar(ax2, im2) - ax2.set_title("Hydrogen Mass Fraction [1]") - except ValueError: + ax2.set_title("Neutral Hydrogen Mass Fraction [1]") + except (ValueError, TypeError): print( filename, "mass fraction wrong? min", @@ -237,7 +256,7 @@ def plot_result(filename): ) set_colorbar(ax3, im3) ax3.set_title(r"Pressure [g/cm/s$^2$]") - except ValueError: + except (ValueError, TypeError): print( filename, "pressures wrong? min", @@ -256,7 +275,7 @@ def plot_result(filename): ) set_colorbar(ax4, im4) ax4.set_title(r"Temperature [K]") - except ValueError: + except (ValueError, TypeError): print( filename, "temperatures wrong? min", @@ -270,10 +289,10 @@ def plot_result(filename): ax.set_xlabel("[kpc]") ax.set_ylabel("[kpc]") - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: - title += ", $z$ = {0:.2e}".format(meta.z) - title += ", $t$ = {0:.2e}".format(meta.time.to("Myr")) + title += ", $z$ = {0:.2f}".format(meta.z) + title += ", $t$ = {0:.2f}".format(meta.time.to("Myr")) fig.suptitle(title) plt.tight_layout() @@ -290,3 +309,4 @@ if __name__ == "__main__": for f in snaplist: plot_result(f) + gc.collect() diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/propagationTest-2D.yml b/examples/RadiativeTransferTests/StromgrenSphere_2D/propagationTest-2D.yml new file mode 100644 index 0000000000000000000000000000000000000000..72926eae55e25d0051dfd7e3b1db4c5cad4cd6ff --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/propagationTest-2D.yml @@ -0,0 +1,69 @@ +MetaData: + run_name: StromgrenSpherePropagationTest2D + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1. + UnitLength_in_cgs: 1. + UnitVelocity_in_cgs: 1. + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.001 # end time: radiation reaches edge of box + dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). + dt_max: 2.e-04 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: propagation_test # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.0001 # Time between snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 5e-4 # 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.6 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./propagationTest-2D.hdf5 # The file to read + periodic: 1 # peridioc ICs. Keep them periodic so we don't loose photon energy. + +GEARRT: + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.99 + photon_groups_Hz: [0., 1., 2., ] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1.e-28, 1.e-28, 1.e-28] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + skip_thermochemistry: 1 # skip thermochemistry. + stars_max_timestep: 7.812500e-06 # update stars every step! + +SPHM1RT: + cred: 2.99792458e10 # reduce the speed of light in the code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + photon_groups_Hz: [] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [38.26] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + stars_max_timestep: 9.765625e-07 # update stars every step! + skip_thermochemistry: 1 # ignore thermochemistry. diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/run.sh b/examples/RadiativeTransferTests/StromgrenSphere_2D/run.sh index 4db90a0b079f3c92c4f5bdb079b13edb709707c5..455dab730d5f5fa967df7617416f4f518ab9facb 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/run.sh +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/run.sh @@ -1,5 +1,9 @@ #!/bin/bash +#--------------------------------------- +# Runs the Stromgren Sphere example +#--------------------------------------- + # make run.sh fail if a subcommand fails set -e set -o pipefail @@ -16,13 +20,11 @@ if [ ! -f 'stromgrenSphere-2D.hdf5' ]; then fi # Run SWIFT with RT -../../swift \ +../../../swift \ --hydro --threads=4 --stars --external-gravity \ - --feedback --radiation --fpe \ + --feedback --radiation \ stromgrenSphere-2D.yml 2>&1 | tee output.log -# Plot the photon propagation checks. -# Make sure you set the correct photon group to plot -# inside the script -python3 ./plotPhotonPropagationCheck.py +# Plot the Stromgren 2D checks. +python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/runPropagationTest.sh b/examples/RadiativeTransferTests/StromgrenSphere_2D/runPropagationTest.sh new file mode 100755 index 0000000000000000000000000000000000000000..d1140fa1e97542b5d29b1fe479676bde292504cb --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/runPropagationTest.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +#--------------------------------------- +# Runs the Propagation Test example +#--------------------------------------- + + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -e glassPlane_128.hdf5 ] +then + echo "Fetching initial glass file for Strömgen Sphere 2D example ..." + ./getGlass.sh +fi + +if [ ! -f 'propagationTest-2D.hdf5' ]; then + echo "Generating ICs" + python3 makePropagationTestIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro --threads=4 --stars --external-gravity \ + --feedback --radiation \ + ./propagationTest-2D.yml 2>&1 | tee output.log + +# Plot the photon propagation checks. +# Make sure you set the correct photon group to plot +# inside the script +python3 ./plotPhotonPropagationCheck.py diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml b/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml index 8b6cc7688869c60416672708509ff1c91e753f18..01ef80e23be754244016393c5d7e281f090dadd1 100644 --- a/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml +++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml @@ -1,5 +1,5 @@ MetaData: - run_name: "stromgrenSphere-2D" + run_name: stromgrenSphere-2D # Define the system of units to use internally. InternalUnitSystem: @@ -8,33 +8,25 @@ InternalUnitSystem: UnitVelocity_in_cgs: 1.e5 # km/s UnitCurrent_in_cgs: 1. UnitTemp_in_cgs: 1. # K - # UnitMass_in_cgs: 7.98841586e+23 # 1 M_Sol - # UnitLength_in_cgs: 7.08567758e12 # kpc in cm - # UnitVelocity_in_cgs: 113.e7 # km/s - # UnitCurrent_in_cgs: 1. - # UnitTemp_in_cgs: 123. # K # Parameters governing the time integration TimeIntegration: + max_nr_rt_subcycles: 8 time_begin: 0. # The starting time of the simulation (in internal units). time_end: 0.512 - # time_end: 4.882813e-03 - # time_end: 5.960465e-06 dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). dt_max: 1.e-03 # The maximal time-step size of the simulation (in internal units). - # dt_max: 5.960465e-07 - # Parameters governing the snapshots Snapshots: basename: output # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) - delta_time: 0.004 # Time between snapshots + delta_time: 0.001 # Time between snapshots # Parameters governing the conserved quantities statistics Statistics: time_first: 0. - delta_time: 1e-4 # Time between statistics output + delta_time: 1e-3 # Time between statistics output # Parameters for the hydrodynamics scheme SPH: @@ -45,30 +37,25 @@ SPH: # Parameters related to the initial conditions InitialConditions: file_name: ./stromgrenSphere-2D.hdf5 # The file to read - periodic: 0 # periodic ICs. Keep them periodic so we don't loose photon energy. TODO: CHANGE LATER WHEN YOU ACTUALLY DO GAS INTERACTIONS - -Scheduler: - max_top_level_cells: 12 + periodic: 0 # periodic ICs. Keep them periodic so we don't loose photon energy. Stars: resolution_eta: 2.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. GEARRT: - f_reduce_c: 0.01 # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.9 - photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed. - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter - # star_emission_rates_LSol: [1000000., 1000000., 1000000., 1000000.] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity. - # star_emission_rates_LSol: [61996.44159, 61996.44159] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity. - star_emission_rates_LSol: [247985.76636, 247985.76636] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity. - hydrogen_mass_fraction: 1.00 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas - set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. - set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) - mass_fraction_HI: 0.999999 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value - mass_fraction_HII: 1.e-6 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value - mass_fraction_HeI: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value - mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value - mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value - stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. - stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. - stars_max_timestep: 1.e-4 # (Optional) restrict the maximal timestep of stars to this value (in internal units) + f_reduce_c: 0.01 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 + photon_groups_Hz: [3.288e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [6.198024e+04] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 1.00 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.999999 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 1.e-6 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + stars_max_timestep: 1.6e-5 # (Optional) restrict the maximal timestep of stars to this value (in internal units) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/README b/examples/RadiativeTransferTests/StromgrenSphere_3D/README index da3fd209a4945dd051a4e1a9f9d74e7ac84a0ab1..69bbb63c5e2398da5109899db0ca9d717dd5c0ae 100644 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/README +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/README @@ -1,23 +1,76 @@ Strömgen Sphere example in 3D +----------------------------- -For now, there are no gas interactions, and we use this -example to perform tests and checks on photon injection -and their propagation. The example contains 1 star in a -glass hydro distribution in 3D. (State 09/2021) +This directory contains two examples in one: -Intented to compile with : - --with-rt=GEAR_1 --with-hydro-dimension=3 + - run a Strömgren sphere example, where a single central + star ionizes the surrounding medium in a box. + To run this example, use the provided `run.sh` script. + This script will then make use of the `makeIC.py` and + `plotSolution.py` script. + + There are a few variations: + + - `run_singlebin.sh`: single-frequency-bin static fixed + temperature Stromgren Sphere with pure hydrogen. This + script will make use of `makeIC.py` and + `plotStromgren3DsinglebinCheck.py`, plotting the neutral + fraction against an analytic solution. + + - `run_MF.sh`: multifrequency static variable temperature + Stromgren Sphere with pure hydrogen. This script will make + use of `makeIC.py` and `plotStromgren3DMFCheck.py`, comparing + the neutral fraction and temperature with published solutions. + + - `run_MFHHe.sh`: multifrequency static variable temperature + Stromgren Sphere with hydrogen (75%) and helium (25%). This + script will make use of `makeIC_HHe.py` and + `plotStromgren3DMFHHeCheck.py`, comparing the neutral fraction + and temperature with published solutions. + + + - run a propagation test of photons emitted from a single + central source in an otherwise uniform box. + To run this example, use the provided `runPropagationTest.sh` script. + This script will then make use of the `makePropagationTestIC.py` and + `plotPhotonPropagationCheck.py` script. + + +Additional scripts: + - `plotRadiationProjection.py`: Plots a projection of the radiation + quantities (energies, fluxes). NOTE: you might need to change the + 'snapshot_base' variable at the top of the script depending on which + solutions you want to plot. + + +To use the GEAR RT model, compile with : +for the propagation test and the "singlebin" test: + --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + +for the others: + --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT + +Note that the tests which have reference solutions can't be reproduced by the +GEAR RT scheme as-is, because they require the gas density to be fixed throughout +the entire run. This can't be done for Gizmo-MFV hydrodynamics as easily as for +other SPH schemes, where fixing the particle positions suffices to keep the density +constant. If you want to fix the particle densities nonetheless, please consult the +instructions provided in +https://github.com/SWIFTSIM/swiftsim-rt-tools/blob/main/additionalTests/instructions/GEARRT_turn_off_hydrodynamics_evolution.md + + +To use the SPHM1 RT model, compile with : +for propagation test: + --with-rt=SPHM1RT_4 --with-stars=basic --with-feedback=none --with-sundials=$SUNDIALS_ROOT + +for Stromgren3D test: + --with-rt=SPHM1RT_4 --with-stars=basic --with-feedback=none --with-sundials=$SUNDIALS_ROOT + +for Stromgren3D test without dynamics (no acceleration and no velocity): + --with-rt=SPHM1RT_4 --with-stars=basic --with-feedback=none --with-sundials=$SUNDIALS_ROOT --enable-boundary-particles=262145 --enable-fixed-boundary-particles=262145 + +SUNDIALS_ROOT is the root directory that contains the lib and include directories, e.g. on cosma: +SUNDIALS_ROOT=/cosma/local/sundials/5.1.0/ -Scripts in this directory: - - plotRadiationProjection.py: - plot photon energies and fluxes as a 2D projected - image - - plotPhotonPropagationCheck.py: - Plots radiation energies and fluxes as function of - radius centered on the injecting star. - NOTE: Make sure that you use a photon group that - doesn't interact with the gas to check the - propagation properly. You'll have to hard-code - the index of the photon group in the script itself. diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/README b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/README new file mode 100644 index 0000000000000000000000000000000000000000..952dd9e86e48bb0b37c0ee64e9c716b9adc6b063 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/README @@ -0,0 +1,29 @@ +This directory contains data files for comparisons. + +- Multifrequency static variable temperature Stromgren Sphere with pure hydrogen at 100 Myr after the star turns on: +The full multifrequency solution is taken from the TT1D result in Figure 4 in doi:10.1111/j.1365-2966.2010.18032.x + + Left column in all the followings: Radius in unit of Stromgren Radius (rs = 5.4 kpc) + + Right column in TTT1D_Stromgren100Myr.txt: Temperature in log10([K]) + + Right column in xTT1D_Stromgren100Myr.txt: Hydrogen Neutral fraction in log10 + +- Multifrequency static variable temperature Stromgren Sphere with 75% hydrogen and 25% helium (in mass) at 100 Myr after the star turns on: +The full multifrequency solution is taken from the TT1D result in Figure 9 in doi:10.1111/j.1365-2966.2010.18032.x + + Left column in all the followings: Radius in unit of Stromgren Radius (rs = 5.4 kpc) + + Right column in TTT1D_Stromgren100Myr_HHe.txt: Temperature in log10([K]) + + Right column in xHITT1D_Stromgren100Myr_HHe.txt: HI fraction in log10(n_HI/n_H), where n_H is hydrogen number density + + Right column in xHIITT1D_Stromgren100Myr_HHe.txt: HII fraction in log10(n_HII/n_H) + + Right column in xHeITT1D_Stromgren100Myr_HHe.txt: HeI fraction in log10(n_HeI/n_H) + + Right column in xHeIITT1D_Stromgren100Myr_HHe.txt: HeII fraction in log10(n_HeII/n_H) + + Right column in xHeIIITT1D_Stromgren100Myr_HHe.txt: HeIII fraction in log10(n_HeIII/n_H) + + diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/TTT1D_Stromgren100Myr.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/TTT1D_Stromgren100Myr.txt new file mode 100644 index 0000000000000000000000000000000000000000..66bd34778d76e1825b0bc086798e5fda0275b046 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/TTT1D_Stromgren100Myr.txt @@ -0,0 +1,25 @@ +0.00519,4.49149 +0.0289,4.43201 +0.06153,4.36401 +0.09117,4.29602 +0.14169,4.26179 +0.21598,4.20198 +0.27841,4.17615 +0.33784,4.13339 +0.43007,4.14126 +0.5044,4.11536 +0.57875,4.09792 +0.66798,4.08887 +0.74829,4.07987 +0.85537,4.0707 +0.94462,4.07859 +0.99817,4.07824 +1.04577,4.07793 +1.08144,4.06075 +1.11115,4.03513 +1.13484,3.9587 +1.15856,3.9077 +1.19419,3.8651 +1.22383,3.78863 +1.2654,3.73751 +1.29205,3.65259 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/TTT1D_Stromgren100Myr_HHe.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/TTT1D_Stromgren100Myr_HHe.txt new file mode 100644 index 0000000000000000000000000000000000000000..e146100b7f66c184858ca9543bfb364e61f12fba --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/TTT1D_Stromgren100Myr_HHe.txt @@ -0,0 +1,20 @@ +0.03144,4.407 +0.1048,4.3015 +0.16769,4.2261 +0.24367,4.2111 +0.33275,4.1658 +0.40611,4.1508 +0.49258,4.1055 +0.58428,4.1055 +0.67074,4.0754 +0.77031,4.0754 +0.84891,4.0603 +0.90393,4.0603 +0.95895,4.0603 +1.00349,4 +1.05328,3.9095 +1.08996,3.7739 +1.13188,3.6683 +1.18166,3.5176 +1.23144,3.397 +1.26812,3.3065 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHIITT1D_Stromgren100Myr_HHe.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHIITT1D_Stromgren100Myr_HHe.txt new file mode 100644 index 0000000000000000000000000000000000000000..796bc3aed6bf002da86881aa933f005aca5b2072 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHIITT1D_Stromgren100Myr_HHe.txt @@ -0,0 +1,20 @@ +0.03189,-0.041 +0.10271,-0.0159 +0.18012,-0.0266 +0.25753,-0.0254 +0.33823,-0.0241 +0.40905,-0.035 +0.50128,-0.0215 +0.56386,-0.0325 +0.62974,-0.0675 +0.70385,-0.0543 +0.76479,-0.2573 +0.82408,-0.5444 +0.88008,-0.7835 +0.92784,-1.0227 +0.98219,-1.3098 +1.0316,-1.525 +1.08924,-1.7281 +1.14688,-1.9792 +1.20617,-2.1222 +1.25888,-2.2654 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHITT1D_Stromgren100Myr_HHe.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHITT1D_Stromgren100Myr_HHe.txt new file mode 100644 index 0000000000000000000000000000000000000000..d5cd1d10a9d884084a398428c5411e3b74af76b0 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHITT1D_Stromgren100Myr_HHe.txt @@ -0,0 +1,26 @@ +0.02201,-4.7329 +0.03025,-4.3727 +0.04507,-4.0125 +0.05989,-3.6763 +0.08789,-3.2919 +0.12083,-3.0034 +0.16365,-2.7147 +0.20482,-2.4861 +0.24765,-2.2934 +0.30529,-2.0885 +0.35634,-1.9317 +0.40905,-1.8229 +0.47987,-1.6298 +0.55069,-1.4607 +0.60833,-1.3038 +0.66103,-1.1709 +0.71373,-0.8461 +0.74832,-0.5576 +0.79938,-0.3288 +0.86196,-0.1238 +0.93113,-0.0507 +0.99866,-0.0496 +1.07277,-0.0125 +1.13865,0.0006 +1.20947,-0.0223 +1.26546,-0.0094 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeIIITT1D_Stromgren100Myr_HHe.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeIIITT1D_Stromgren100Myr_HHe.txt new file mode 100644 index 0000000000000000000000000000000000000000..619caec4b544a651c54c81a875bc021d6cbaf551 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeIIITT1D_Stromgren100Myr_HHe.txt @@ -0,0 +1,23 @@ +0.04507,-1.1087 +0.12248,-1.1795 +0.17518,-1.2746 +0.2427,-1.3816 +0.30364,-1.5006 +0.35305,-1.5838 +0.4074,-1.7269 +0.46834,-1.8699 +0.52927,-1.989 +0.59845,-2.0959 +0.66432,-2.1908 +0.72526,-2.2498 +0.7862,-2.3449 +0.84384,-2.5119 +0.90807,-2.7629 +0.96901,-3.0859 +1.02007,-3.3371 +1.07277,-3.6002 +1.11394,-3.8156 +1.15512,-4.0069 +1.1897,-4.2104 +1.23417,-4.4016 +1.27864,-4.6169 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeIITT1D_Stromgren100Myr_HHe.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeIITT1D_Stromgren100Myr_HHe.txt new file mode 100644 index 0000000000000000000000000000000000000000..66d92a693f79877350b9b69c75ce1061a229dad7 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeIITT1D_Stromgren100Myr_HHe.txt @@ -0,0 +1,26 @@ +0.01542,-3.785 +0.02695,-3.2809 +0.04013,-2.8967 +0.05825,-2.5604 +0.07471,-2.3082 +0.10601,-2.0197 +0.14883,-1.755 +0.19659,-1.5503 +0.26082,-1.4053 +0.31846,-1.2844 +0.38105,-1.2594 +0.44693,-1.2103 +0.5161,-1.1852 +0.57868,-1.1722 +0.64127,-1.1472 +0.70715,-1.1462 +0.7862,-1.1569 +0.84714,-1.156 +0.92125,-1.3348 +0.98548,-1.5138 +1.03983,-1.6689 +1.09912,-1.8599 +1.15676,-2.039 +1.193,-2.1824 +1.2457,-2.3496 +1.28523,-2.4209 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeITT1D_Stromgren100Myr_HHe.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeITT1D_Stromgren100Myr_HHe.txt new file mode 100644 index 0000000000000000000000000000000000000000..8c20be23bbab154391c516cb6287020da1963607 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xHeITT1D_Stromgren100Myr_HHe.txt @@ -0,0 +1,21 @@ +0.13236,-4.8031 +0.16859,-4.4185 +0.19988,-4.1421 +0.26576,-3.685 +0.32505,-3.4321 +0.38928,-3.1791 +0.45187,-3.0221 +0.51116,-2.8412 +0.57374,-2.7082 +0.64291,-2.5871 +0.7022,-2.4782 +0.75326,-2.4534 +0.78455,-2.4049 +0.82079,-2.1763 +0.86855,-1.8636 +0.9229,-1.5268 +0.98219,-1.2858 +1.07277,-1.1524 +1.14194,-1.1273 +1.21111,-1.1142 +1.27535,-1.1132 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xTT1D_Stromgren100Myr.txt b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xTT1D_Stromgren100Myr.txt new file mode 100644 index 0000000000000000000000000000000000000000..4b9cce4f1b8b82c002b2fa6170e4e80d012094e7 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/data/xTT1D_Stromgren100Myr.txt @@ -0,0 +1,29 @@ +0.02464,-4.6387 +0.0419,-4.2986 +0.0533,-4.0009 +0.07057,-3.6607 +0.09083,-3.3419 +0.12003,-3.0658 +0.14933,-2.8535 +0.19044,-2.6201 +0.22865,-2.4293 +0.27874,-2.2599 +0.33173,-2.048 +0.38479,-1.8786 +0.4379,-1.7518 +0.49102,-1.6251 +0.54413,-1.4983 +0.60027,-1.4141 +0.65928,-1.2661 +0.70634,-1.0541 +0.74733,-0.7356 +0.78252,-0.5022 +0.82073,-0.3113 +0.8798,-0.2059 +0.93604,-0.1855 +1.00112,-0.144 +1.06624,-0.1238 +1.12547,-0.1248 +1.17871,-0.0831 +1.22609,-0.0839 +1.25864,-0.0632 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/getGlass.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/getGlass.sh index d5c5f590ac37c9c9431d626a2ea61b0c12c1513c..50f27f4e9f9981da9449608bd70669c56d9d3985 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/getGlass.sh +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/getGlass.sh @@ -1,2 +1,3 @@ #!/bin/bash wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 +#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_128.hdf5 \ No newline at end of file diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py index 384178ba209617144391170a1a95bd7d0f619402..473e05f97f4d77e2141df664491250c6c75be81d 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py @@ -1,56 +1,112 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## # --------------------------------------------------------------------- # Add a single star in the center of a glass distribution +# The gas is set up with pure hydrogen gas. # --------------------------------------------------------------------- -from swiftsimio import Writer -import unyt -import numpy as np import h5py +import numpy as np +import unyt +from swiftsimio import Writer +from swiftsimio.units import cosmo_units + +import stromgren_plotting_tools as spt + +gamma = 5.0 / 3.0 + +# switch to replace the central gas particle with a star +# else put the star particle among gas particles +replace_gas = False + + +if __name__ == "__main__": + + glass = h5py.File("glassCube_64.hdf5", "r") + parts = glass["PartType0"] + xp = parts["Coordinates"][:] + h = parts["SmoothingLength"][:] + glass.close() + r = np.sqrt(np.sum((0.5 - xp) ** 2, axis=1)) -glass = h5py.File("glassCube_64.hdf5", "r") -parts = glass["PartType0"] -xp = parts["Coordinates"][:] -h = parts["SmoothingLength"][:] -glass.close() + if replace_gas: + # replace a central gas particle with a star particle + rmin = np.argmin(r) + xs = xp[rmin] + xp = np.delete(xp, rmin, axis=0) + h = np.delete(h, rmin) + else: + # find particles closest to the center + # and select a couple of them to put the star in their middle + mininds = np.argsort(r) + center_parts = xp[mininds[:4]] + xs = center_parts.sum(axis=0) / center_parts.shape[0] -# replace the particle closest to the center -# by the star -r = np.sqrt(np.sum((0.5 - xp) ** 2, axis=1)) -rmin = np.argmin(r) -xs = xp[rmin] -xp = np.delete(xp, rmin, axis=0) -h = np.delete(h, rmin) + # Double-check all particles for boundaries + for i in range(3): + mask = xp[:, i] < 0.0 + xp[mask, i] += 1.0 + mask = xp[:, i] > 1.0 + xp[mask, i] -= 1.0 + # Set up metadata + unitL = unyt.Mpc + edgelen = 22 * 1e-3 * unitL # 22 so we can cut off 1kpc on each edge for image + edgelen = edgelen.to(unitL) + boxsize = np.array([1.0, 1.0, 1.0]) * edgelen -unitL = unyt.cm -t_end = 1e-3 * unyt.s -edgelen = unyt.c.to("cm/s") * t_end * 2.0 -edgelen = edgelen.to(unitL) -boxsize = unyt.unyt_array([edgelen.v, edgelen.v, edgelen.v], unitL) + xs = unyt.unyt_array( + [np.array([xs[0] * edgelen, xs[1] * edgelen, xs[2] * edgelen])], unitL + ) + xp *= edgelen + h *= edgelen -xs = unyt.unyt_array( - [np.array([xs[0] * edgelen, xs[1] * edgelen, xs[2] * edgelen])], unitL -) -xp *= edgelen -h *= edgelen + w = Writer(unit_system=cosmo_units, box_size=boxsize, dimension=3) + # write particle positions and smoothing lengths + w.gas.coordinates = xp + w.stars.coordinates = xs + w.gas.velocities = np.zeros(xp.shape) * (unitL / unyt.Myr) + w.stars.velocities = np.zeros(xs.shape) * (unitL / unyt.Myr) + w.gas.smoothing_length = h + w.stars.smoothing_length = w.gas.smoothing_length[:1] -w = Writer(unit_system=unyt.unit_systems.cgs_unit_system, box_size=boxsize, dimension=3) + # get gas masses + XH = 1.0 # hydrogen mass fraction + XHe = 0.0 # helium mass fraction + nH = 1e-3 * unyt.cm ** (-3) + rho_gas = nH * unyt.proton_mass / XH + Mtot = rho_gas * edgelen ** 3 + mpart = Mtot / xp.shape[0] + mpart = mpart.to(cosmo_units["mass"]) + w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * mpart + w.stars.masses = np.ones(xs.shape[0], dtype=np.float64) * mpart -w.gas.coordinates = xp -w.stars.coordinates = xs -w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) -w.stars.velocities = np.zeros(xs.shape) * (unyt.cm / unyt.s) -w.gas.masses = np.ones(xp.shape[0], dtype=np.float) * 100 * unyt.g -w.stars.masses = np.ones(xs.shape[0], dtype=np.float) * 100 * unyt.g -w.gas.internal_energy = ( - np.ones(xp.shape[0], dtype=np.float) * (300.0 * unyt.kb * unyt.K) / (unyt.g) -) + # get gas internal energy for a given temperature and composition + T = 100 * unyt.K + XHI, XHII, XHeI, XHeII, XHeIII = spt.get_mass_fractions(T, XH, XHe) + mu = spt.mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII) + internal_energy = spt.internal_energy(T, mu, gamma) -w.gas.smoothing_length = h -w.stars.smoothing_length = w.gas.smoothing_length[:1] + w.gas.internal_energy = np.ones(xp.shape[0], dtype=np.float64) * internal_energy -w.write("stromgrenSphere-3D.hdf5") + w.write("stromgrenSphere-3D.hdf5") diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC_HHe.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC_HHe.py new file mode 100755 index 0000000000000000000000000000000000000000..ddb755db536be0a93287f8c96f54ecd4174ef5fb --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC_HHe.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +# --------------------------------------------------------------------- +# Add a single star in the center of a glass distribution +# The gas is set up with a mixture of hydrogen and helium +# with 75% hydrogen and 25% helium in mass. +# This initial condition is comparable to Section 5.3.2 of +# Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x. +# --------------------------------------------------------------------- + +import h5py +import numpy as np +import unyt +from swiftsimio import Writer +from swiftsimio.units import cosmo_units + +import stromgren_plotting_tools as spt + +gamma = 5.0 / 3.0 + +# switch to replace the central gas particle with a star +# else put the star particle among gas particles +replace_gas = True + + +if __name__ == "__main__": + + glass = h5py.File("glassCube_64.hdf5", "r") + parts = glass["PartType0"] + xp = parts["Coordinates"][:] + h = parts["SmoothingLength"][:] + glass.close() + + r = np.sqrt(np.sum((0.5 - xp) ** 2, axis=1)) + + if replace_gas: + # replace a central gas particle with a star particle + rmin = np.argmin(r) + xs = xp[rmin] + xp = np.delete(xp, rmin, axis=0) + h = np.delete(h, rmin) + else: + # find particles closest to the center + # and select a couple of them to put the star in their middle + mininds = np.argsort(r) + center_parts = xp[mininds[:4]] + xs = center_parts.sum(axis=0) / center_parts.shape[0] + + # Double-check all particles for boundaries + for i in range(3): + mask = xp[:, i] < 0.0 + xp[mask, i] += 1.0 + mask = xp[:, i] > 1.0 + xp[mask, i] -= 1.0 + + # Set up metadata + unitL = unyt.Mpc + edgelen = 22 * 1e-3 * unitL # 22 so we can cut off 1kpc on each edge for image + edgelen = edgelen.to(unitL) + boxsize = np.array([1.0, 1.0, 1.0]) * edgelen + + xs = unyt.unyt_array( + [np.array([xs[0] * edgelen, xs[1] * edgelen, xs[2] * edgelen])], unitL + ) + xp *= edgelen + h *= edgelen + + w = Writer(unit_system=cosmo_units, box_size=boxsize, dimension=3) + + # write particle positions and smoothing lengths + w.gas.coordinates = xp + w.stars.coordinates = xs + w.gas.velocities = np.zeros(xp.shape) * (unitL / unyt.Myr) + w.stars.velocities = np.zeros(xs.shape) * (unitL / unyt.Myr) + w.gas.smoothing_length = h + w.stars.smoothing_length = w.gas.smoothing_length[:1] + + # get gas masses + XH = 0.75 # hydrogen mass fraction + XHe = 0.25 # helium mass fraction + nH = 1e-3 * unyt.cm ** (-3) + rho_gas = nH * unyt.proton_mass / XH + Mtot = rho_gas * edgelen ** 3 + mpart = Mtot / xp.shape[0] + mpart = mpart.to(cosmo_units["mass"]) + w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * mpart + w.stars.masses = np.ones(xs.shape[0], dtype=np.float64) * mpart + + # get gas internal energy for a given temperature and composition + T = 100.0 * unyt.K + XHI, XHII, XHeI, XHeII, XHeIII = spt.get_mass_fractions(T, XH, XHe) + mu = spt.mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII) + internal_energy = spt.internal_energy(T, mu, gamma) + + w.gas.internal_energy = np.ones(xp.shape[0], dtype=np.float64) * internal_energy + + w.write("stromgrenSphere-3D-HHe.hdf5") diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/makePropagationTestIC.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/makePropagationTestIC.py new file mode 100755 index 0000000000000000000000000000000000000000..0cd176c81b8702c0586d6e90ac028d38cb511957 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/makePropagationTestIC.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## + + +# --------------------------------------------------------------------- +# Add a single star in the center of a glass distribution +# Intended for the photon propagation test. +# --------------------------------------------------------------------- + +import h5py +import numpy as np +import unyt +from swiftsimio import Writer + +glass = h5py.File("glassCube_64.hdf5", "r") +parts = glass["PartType0"] +xp = parts["Coordinates"][:] +h = parts["SmoothingLength"][:] +glass.close() + +# replace the particle closest to the center +# by the star +r = np.sqrt(np.sum((0.5 - xp) ** 2, axis=1)) +rmin = np.argmin(r) +mininds = np.argsort(r) +center_parts = xp[mininds[:4]] +xs = center_parts.sum(axis=0) / center_parts.shape[0] + +# Double-check all particles for boundaries +for i in range(3): + mask = xp[:, i] < 0.0 + xp[mask, i] += 1.0 + mask = xp[:, i] > 1.0 + xp[mask, i] -= 1.0 + +unitL = unyt.cm +t_end = 1e-3 * unyt.s +edgelen = unyt.c.to("cm/s") * t_end * 2.0 +edgelen = edgelen.to(unitL) +boxsize = unyt.unyt_array([edgelen.v, edgelen.v, edgelen.v], unitL) + +xs = unyt.unyt_array( + [np.array([xs[0] * edgelen, xs[1] * edgelen, xs[2] * edgelen])], unitL +) +xp *= edgelen +h *= edgelen + + +w = Writer(unit_system=unyt.unit_systems.cgs_unit_system, box_size=boxsize, dimension=3) + +w.gas.coordinates = xp +w.stars.coordinates = xs +w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) +w.stars.velocities = np.zeros(xs.shape) * (unyt.cm / unyt.s) +w.gas.masses = np.ones(xp.shape[0], dtype=float) * 100 * unyt.g +w.stars.masses = np.ones(xs.shape[0], dtype=float) * 100 * unyt.g +w.gas.internal_energy = ( + np.ones(xp.shape[0], dtype=float) * (300.0 * unyt.kb * unyt.K) / unyt.g +) + +w.gas.smoothing_length = h +w.stars.smoothing_length = w.gas.smoothing_length[:1] + +w.write("propagationTest-3D.hdf5") diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py index 253389a6c8dadc762e3398ea3cde60e903ef8cb0..42f7ce447a28231cb7fbd56413022b1793fde3e6 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py @@ -1,4 +1,23 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## # ---------------------------------------------------------------------- # plots @@ -16,21 +35,30 @@ # correctly. # ---------------------------------------------------------------------- -import sys, os, gc -import swiftsimio +import gc +import os +import sys + +import matplotlib as mpl import numpy as np +import swiftsimio import unyt from matplotlib import pyplot as plt -import matplotlib as mpl from scipy import stats from scipy.optimize import curve_fit +import stromgren_plotting_tools as spt + # Parameters users should/may tweak # snapshot basename -snapshot_base = "output" +snapshot_base = "propagation_test" + +# additional anisotropy estimate plot? +plot_anisotropy_estimate = False # which photon group to use. +# NOTE: array index, not group number (which starts at 1 for GEAR) group_index = 0 scatterplot_kwargs = { @@ -55,7 +83,7 @@ except IndexError: mpl.rcParams["text.usetex"] = True -def analytical_intgrated_energy_solution(L, time, r, rmax): +def analytical_integrated_energy_solution(L, time, r, rmax): """ Compute analytical solution for the sum of the energy in bins for given injection rate <L> at time <time> @@ -100,14 +128,20 @@ def analytical_energy_solution(L, time, r, rmax): return r_center, E -def analytical_flux_magnitude_solution(L, time, r, rmax): +def analytical_flux_magnitude_solution(L, time, r, rmax, scheme): """ For radiation that doesn't interact with the gas, the flux should correspond to the free streaming (optically thin) limit. So compute and return that. """ r, E = analytical_energy_solution(L, time, r, rmax) - F = unyt.c.to(r.units / time.units) * E / r.units ** 3 + if scheme.startswith("GEAR M1closure"): + F = unyt.c.to(r.units / time.units) * E / r.units ** 3 + elif scheme.startswith("SPH M1closure"): + F = unyt.c.to(r.units / time.units) * E + else: + raise ValueError("Unknown scheme", scheme) + return r, F @@ -157,6 +191,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): # Read in data first data = swiftsimio.load(filename) meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) boxsize = meta.boxsize edgelen = min(boxsize[0], boxsize[1]) @@ -168,25 +203,55 @@ def plot_photons(filename, emin, emax, fmin, fmax): time = meta.time r_expect = meta.time * meta.reduced_lightspeed - use_const_emission_rates = bool(meta.parameters["GEARRT:use_const_emission_rates"]) + L = None + + use_const_emission_rates = False + if scheme.startswith("GEAR M1closure"): + luminosity_model = meta.parameters["GEARRT:stellar_luminosity_model"] + use_const_emission_rates = luminosity_model.decode("utf-8") == "const" + elif scheme.startswith("SPH M1closure"): + use_const_emission_rates = bool( + meta.parameters["SPHM1RT:use_const_emission_rates"] + ) + else: + print("Error: Unknown RT scheme " + scheme) + exit() + if use_const_emission_rates: # read emission rate parameter as string - emissionstr = meta.parameters["GEARRT:star_emission_rates_LSol"].decode("utf-8") - # clean string up - if emissionstr.startswith("["): - emissionstr = emissionstr[1:] - if emissionstr.endswith("]"): - emissionstr = emissionstr[:-1] - - # transform string values to floats with unyts - emissions = emissionstr.split(",") - emlist = [] - for er in emissions: - emlist.append(float(er)) - const_emission_rates = unyt.unyt_array(emlist, unyt.L_Sun) - L = const_emission_rates[group_index] - - fig = plt.figure(figsize=(5 * 4, 5.5), dpi=200) + if scheme.startswith("GEAR M1closure"): + const_emission_rates = ( + spt.trim_paramstr( + meta.parameters["GEARRT:const_stellar_luminosities_LSol"].decode( + "utf-8" + ) + ) + * unyt.L_Sun + ) + L = const_emission_rates[group_index] + elif scheme.startswith("SPH M1closure"): + units = data.units + unit_l_in_cgs = units.length.in_cgs() + unit_v_in_cgs = (units.length / units.time).in_cgs() + unit_m_in_cgs = units.mass.in_cgs() + const_emission_rates = ( + spt.trim_paramstr( + meta.parameters["SPHM1RT:star_emission_rates"].decode("utf-8") + ) + * unit_m_in_cgs + * unit_v_in_cgs ** 3 + / unit_l_in_cgs + ) + L = const_emission_rates[group_index] + else: + print("Error: Unknown RT scheme " + scheme) + exit() + + if plot_anisotropy_estimate: + ncols = 4 + else: + ncols = 3 + fig = plt.figure(figsize=(5 * ncols, 5.5), dpi=200) nbins = 100 r_bin_edges = np.linspace(0.5 * edgelen * 1e-3, 0.507 * edgelen, nbins + 1) @@ -205,8 +270,6 @@ def plot_photons(filename, emin, emax, fmin, fmax): Fz = getattr(data.gas.photon_fluxes, "Group" + str(group_index + 1) + "Z") fmag = np.sqrt(Fx ** 2 + Fy ** 2 + Fz ** 2) - sum_fmag = fmag.sum() - max_fmag = fmag.max() particle_count, _ = np.histogram( r, bins=r_analytical_bin_edges, @@ -221,7 +284,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): # ------------------------ # Plot photon energies # ------------------------ - ax1 = fig.add_subplot(1, 4, 1) + ax1 = fig.add_subplot(1, ncols, 1) ax1.set_title("Particle Radiation Energies") ax1.set_ylabel("Photon Energy [$" + energy_units_str + "$]") @@ -278,7 +341,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): # ------------------------------ # Plot binned photon energies # ------------------------------ - ax2 = fig.add_subplot(1, 4, 2) + ax2 = fig.add_subplot(1, ncols, 2) ax2.set_title("Total Radiation Energy in radial bins") ax2.set_ylabel("Total Photon Energy [$" + energy_units_str + "$]") @@ -301,7 +364,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): if use_const_emission_rates: # plot entire expected solution # Note: you need to use the same bins as for the actual results - rA, EA = analytical_intgrated_energy_solution(L, time, r_bin_edges, r_expect) + rA, EA = analytical_integrated_energy_solution(L, time, r_bin_edges, r_expect) ax2.plot( rA, @@ -323,7 +386,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): # ------------------------------ # Plot photon fluxes # ------------------------------ - ax3 = fig.add_subplot(1, 4, 3) + ax3 = fig.add_subplot(1, ncols, 3) ax3.set_title("Particle Radiation Flux Magnitudes") ax3.set_ylabel("Photon Flux Magnitude [$" + flux_units_str + "$]") @@ -349,7 +412,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): if use_const_emission_rates: # plot entire expected solution rA, FA = analytical_flux_magnitude_solution( - L, time, r_analytical_bin_edges, r_expect + L, time, r_analytical_bin_edges, r_expect, scheme ) mask = particle_count > 0 @@ -387,55 +450,58 @@ def plot_photons(filename, emin, emax, fmin, fmax): # Plot photon flux sum # ------------------------------ - ax4 = fig.add_subplot(1, 4, 4) - ax4.set_title("Vectorial Sum of Radiation Flux in radial bins") - ax4.set_ylabel("[1]") - - fmag_sum_bin, _, _ = stats.binned_statistic( - r, - fmag, - statistic="sum", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - mask_sum = fmag_sum_bin > 0 - fmag_max_bin, _, _ = stats.binned_statistic( - r, - fmag, - statistic="max", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - mask_max = fmag_max_bin > 0 - Fx_sum_bin, _, _ = stats.binned_statistic( - r, - Fx, - statistic="sum", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - Fy_sum_bin, _, _ = stats.binned_statistic( - r, - Fy, - statistic="sum", - bins=r_bin_edges, - range=(r_bin_edges[0], r_bin_edges[-1]), - ) - F_sum_bin = np.sqrt(Fx_sum_bin ** 2 + Fy_sum_bin ** 2) + if plot_anisotropy_estimate: + ax4 = fig.add_subplot(1, ncols, 4) + ax4.set_title("Vectorial Sum of Radiation Flux in radial bins") + ax4.set_ylabel("[1]") + + fmag_sum_bin, _, _ = stats.binned_statistic( + r, + fmag, + statistic="sum", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + mask_sum = fmag_sum_bin > 0 + fmag_max_bin, _, _ = stats.binned_statistic( + r, + fmag, + statistic="max", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + mask_max = fmag_max_bin > 0 + Fx_sum_bin, _, _ = stats.binned_statistic( + r, + Fx, + statistic="sum", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + Fy_sum_bin, _, _ = stats.binned_statistic( + r, + Fy, + statistic="sum", + bins=r_bin_edges, + range=(r_bin_edges[0], r_bin_edges[-1]), + ) + F_sum_bin = np.sqrt(Fx_sum_bin ** 2 + Fy_sum_bin ** 2) - ax4.plot( - r_bin_centres[mask_sum], - F_sum_bin[mask_sum] / fmag_sum_bin[mask_sum], - **lineplot_kwargs, - label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", - ) - ax4.plot( - r_bin_centres[mask_max], - F_sum_bin[mask_max] / fmag_max_bin[mask_max], - **lineplot_kwargs, - linestyle="--", - label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", - ) + ax4.plot( + r_bin_centres[mask_sum], + F_sum_bin[mask_sum] / fmag_sum_bin[mask_sum], + **lineplot_kwargs, + label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ " + + r"/ $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", + ) + ax4.plot( + r_bin_centres[mask_max], + F_sum_bin[mask_max] / fmag_max_bin[mask_max], + **lineplot_kwargs, + linestyle="--", + label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ " + + r" / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $", + ) # ------------------------------------------- # Cosmetics that all axes have in common @@ -447,7 +513,7 @@ def plot_photons(filename, emin, emax, fmin, fmax): ax.legend(fontsize="x-small") # Add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: title += ", $z$ = {0:.2e}".format(meta.z) title += ", $t$ = {0:.2e}".format(meta.time) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py index 05cfa4a6a82dda75934ebeb2914c95a2129d0a6c..09e78f504acf75ef86e923e7d04c7eb11d6b1268 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py @@ -1,4 +1,24 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# 2022 Tsang Keung Chan (chantsangkeung@gmail.com) +# +# 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/>. +# +############################################################################## + # ---------------------------------------------------- # plots 2D projection of radiation energy and fluxes @@ -8,23 +28,26 @@ # all snapshots available in the workdir. # ---------------------------------------------------- -import sys +import gc import os -import swiftsimio +import sys + +import matplotlib as mpl import numpy as np -import gc +import swiftsimio import unyt from matplotlib import pyplot as plt -import matplotlib as mpl -from mpl_toolkits.axes_grid1 import make_axes_locatable from matplotlib.colors import SymLogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable # Parameters users should/may tweak # plot all groups and all photon quantities plot_all_data = True -# snapshot basename -snapshot_base = "output" + +# plotting propagation tests or Stromgren sphere? +do_stromgren_sphere = True + # fancy up the plots a bit? fancy = True @@ -34,14 +57,10 @@ imshow_kwargs = {"origin": "lower", "cmap": "viridis"} # parameters for swiftsimio projections projection_kwargs = {"resolution": 1024, "parallel": True} -# Set Units of your choice -energy_units = unyt.erg -energy_units_str = "\\rm{erg}" -flux_units = 1e10 * energy_units / unyt.cm ** 2 / unyt.s -flux_units_str = "10^{10} \\rm{erg} \\ \\rm{cm}^{-2} \\ \\rm{s}^{-1}" -time_units = unyt.s -# ----------------------------------------------------------------------- - +# snapshot basename +snapshot_base = "propagation_test" +if do_stromgren_sphere: + snapshot_base = "output" # Read in cmdline arg: Are we plotting only one snapshot, or all? plot_all = False @@ -52,6 +71,40 @@ except IndexError: mpl.rcParams["text.usetex"] = True +# ----------------------------------------------------------------------- + + +# get the unit for rt according to the RT scheme: +def get_units(scheme, unit_system="cgs_units"): + if unit_system == "cgs_units": + time_units = unyt.s + energy_units = unyt.erg + energy_units_str = "\\rm{erg}" + if scheme.startswith("GEAR M1closure"): + flux_units = 1e-10 * energy_units / unyt.cm ** 2 / unyt.s + flux_units_str = "10^{-10} \\rm{erg} \\ \\rm{cm}^{-2} \\ \\rm{s}^{-1}" + elif scheme.startswith("SPH M1closure"): + flux_units = 1e10 * energy_units * unyt.cm / unyt.s + flux_units_str = "10^{10} \\rm{erg} \\ \\rm{cm} \\ \\rm{s}^{-1}" + else: + raise ValueError("RT scheme not identified. Exit.") + elif unit_system == "stromgren_units": + time_units = unyt.Myr + energy_units = 1e50 * unyt.erg + energy_units_str = "10^{50} \\rm{erg}" + if scheme.startswith("GEAR M1closure"): + flux_units = 1e50 * unyt.erg / unyt.kpc ** 2 / unyt.Gyr + flux_units_str = "10^{60} \\rm{erg} \\ \\rm{kpc}^{-2} \\ \\rm{Gyr}^{-1}" + elif scheme.startswith("SPH M1closure"): + flux_units = 1e50 * unyt.erg * unyt.kpc / unyt.Gyr + flux_units_str = "10^{60} \\rm{erg} \\ \\rm{kpc} \\ \\rm{Gyr}^{-1}" + else: + raise ValueError("RT scheme not identified. Exit.") + else: + raise ValueError("Unit system not identified. Exit.") + + return time_units, energy_units, energy_units_str, flux_units, flux_units_str + def get_snapshot_list(snapshot_basename="output"): """ @@ -76,6 +129,10 @@ def get_snapshot_list(snapshot_basename="output"): quit(1) snaplist.append(fname) + if len(snaplist) == 0: + print("Didn't find any snapshots with basename '", snapshot_basename, "'") + quit(1) + return snaplist @@ -106,6 +163,16 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): # Read in data first data = swiftsimio.load(filename) meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + if do_stromgren_sphere: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="stromgren_units" + ) + else: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="cgs_units" + ) ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) xlabel_units_str = meta.boxsize.units.latex_representation() @@ -142,7 +209,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): if plot_all_data: fig = plt.figure(figsize=(5 * 4, 5.05 * ngroups), dpi=200) - figname = filename[:-5] + "-all-quantities.png" + figname = filename[:-5] + "-radiation-projection.png" for g in range(ngroups): @@ -300,6 +367,16 @@ def get_minmax_vals(snaplist): data = swiftsimio.load(filename) meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + if do_stromgren_sphere: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="stromgren_units" + ) + else: + time_units, energy_units, energy_units_str, flux_units, flux_units_str = get_units( + scheme, unit_system="cgs_units" + ) ngroups = int(meta.subgrid_scheme["PhotonGroupNumber"]) emin_group = [] @@ -320,7 +397,6 @@ def get_minmax_vals(snaplist): dirmin = [] dirmax = [] for direction in ["X", "Y", "Z"]: - new_attribute_str = "radiation_flux" + str(g + 1) + direction f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) dirmin.append(f.min()) dirmax.append(f.max()) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotSolution.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotSolution.py new file mode 100755 index 0000000000000000000000000000000000000000..bb68a573b932dab72b570aa1446783fd80e76a38 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotSolution.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## + + +# ---------------------------------------------------- +# Plot slices of the neutral hydrogen number density, +# pressure, temperature, and neutral hydrogen mass fraction. +# ---------------------------------------------------- + +import gc +import sys + +import matplotlib as mpl +import swiftsimio +import unyt +from matplotlib import pyplot as plt +from matplotlib.colors import LogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable +from swiftsimio.visualisation.slice import slice_gas + +import stromgren_plotting_tools as spt + +# Parameters users should/may tweak + +# snapshot basename +snapshot_base = "output" + +# parameters for imshow plots +imshow_kwargs = {"origin": "lower"} + +# parameters for swiftsimio slices +slice_kwargs = {"resolution": 1024, "parallel": True} + +# ----------------------------------------------------------------------- + + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +plot_all = False +snapnr = -1 +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + +mpl.rcParams["text.usetex"] = True + + +def set_colorbar(ax, im): + """ + Adapt the colorbar a bit for axis object <ax> and + imshow instance <im> + """ + divider = make_axes_locatable(ax) + cax = divider.append_axes("right", size="5%", pad=0.05) + plt.colorbar(im, cax=cax) + return + + +def plot_result(filename): + """ + Create and save the plot + """ + print("working on", filename) + + data = swiftsimio.load(filename) + meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + global imshow_kwargs + imshow_kwargs["extent"] = [ + 0.0 * meta.boxsize[0].v, + 0.9 * meta.boxsize[0].v, + 0.0 * meta.boxsize[1].v, + 0.9 * meta.boxsize[1].v, + ] + + try: + par = meta.parameters["InitialConditions:periodic"] + periodic = int(par) == 1 + except KeyError: + periodic = False + + # Add a cutoff to the image edges if the run wasn't periodic + cutoff = int(0.05 * slice_kwargs["resolution"]) + + mass_map = slice_gas( + data, project="masses", z_slice=0.5 * meta.boxsize[2], **slice_kwargs + ) + gamma = meta.gas_gamma + + imf = spt.get_imf(scheme, data) + + # use units that are most likely not to produce infinities and NaNs + masses_MSun = data.gas.masses.to("M_Sun") + + data.gas.mXHI = imf.HI * masses_MSun + data.gas.mXHII = imf.HII * masses_MSun + data.gas.mP = data.gas.pressures * masses_MSun + data.gas.mrhoHI = imf.HI * data.gas.densities * masses_MSun + + mu = spt.mean_molecular_weight(imf.HI, imf.HII, imf.HeI, imf.HeII, imf.HeIII) + data.gas.mT = ( + spt.gas_temperature(data.gas.internal_energies, mu, gamma) * masses_MSun + ) + + mass_weighted_HI_map = slice_gas( + data, project="mXHI", z_slice=0.5 * meta.boxsize[2], **slice_kwargs + ) + mass_weighted_pressure_map = slice_gas( + data, project="mP", z_slice=0.5 * meta.boxsize[2], **slice_kwargs + ) + mass_weighted_HI_density_map = slice_gas( + data, project="mrhoHI", z_slice=0.5 * meta.boxsize[2], **slice_kwargs + ) + mass_weighted_temperature_map = slice_gas( + data, project="mT", z_slice=0.5 * meta.boxsize[2], **slice_kwargs + ) + + HI_map = mass_weighted_HI_map / mass_map + if not periodic: + HI_map = HI_map[cutoff:-cutoff, cutoff:-cutoff] + + pressure_map = mass_weighted_pressure_map / mass_map + if not periodic: + pressure_map = pressure_map[cutoff:-cutoff, cutoff:-cutoff] + pressure_map = pressure_map.to("g/cm/s**2") + + HI_density_map = mass_weighted_HI_density_map / mass_map + if not periodic: + HI_density_map = HI_density_map[cutoff:-cutoff, cutoff:-cutoff] + HI_density_map = HI_density_map.to("kg/cm**3") + HI_density_map = HI_density_map / unyt.proton_mass + + temperature_map = mass_weighted_temperature_map / mass_map + if not periodic: + temperature_map = temperature_map[cutoff:-cutoff, cutoff:-cutoff] + temperature_map = temperature_map.to("K") + + fig = plt.figure(figsize=(12, 12), dpi=200) + figname = filename[:-5] + ".png" + + ax1 = fig.add_subplot(221) + ax2 = fig.add_subplot(222) + ax3 = fig.add_subplot(223) + ax4 = fig.add_subplot(224) + + try: + im1 = ax1.imshow( + HI_density_map.T, + **imshow_kwargs, + norm=LogNorm(vmin=1.0e-6, vmax=1e-1), + cmap="bone", + ) + set_colorbar(ax1, im1) + ax1.set_title(r"Neutral Hydrogen Number Density [cm$^{-3}$]") + except (ValueError, TypeError): + print( + filename, + "densities wrong? min", + data.gas.densities.min(), + "max", + data.gas.densities.max(), + ) + return + + try: + im2 = ax2.imshow( + HI_map.T, + **imshow_kwargs, + norm=LogNorm(vmin=1.0e-5, vmax=1.0), + cmap="cividis", + ) + set_colorbar(ax2, im2) + ax2.set_title("Neutral Hydrogen Mass Fraction [1]") + except (ValueError, TypeError): + print( + filename, + "mass fraction wrong? min", + data.gas.ion_mass_fractions.HI.min(), + "max", + data.gas.ion_mass_fractions.HI.max(), + ) + return + + try: + im3 = ax3.imshow( + pressure_map.T, + **imshow_kwargs, + norm=LogNorm(vmin=1e-15, vmax=1e-12), + cmap="viridis", + ) + set_colorbar(ax3, im3) + ax3.set_title(r"Pressure [g/cm/s$^2$]") + except (ValueError, TypeError): + print( + filename, + "pressures wrong? min", + data.gas.pressures.min(), + "max", + data.gas.pressures.max(), + ) + return + + try: + im4 = ax4.imshow( + temperature_map.T, + **imshow_kwargs, + norm=LogNorm(vmin=1e2, vmax=5e4), + cmap="inferno", + ) + set_colorbar(ax4, im4) + ax4.set_title(r"Temperature [K]") + except (ValueError, TypeError): + print( + filename, + "temperatures wrong? min", + temperature_map.min(), + "max", + temperature_map.max(), + ) + return + + for ax in [ax1, ax2, ax3, ax4]: + ax.set_xlabel("[kpc]") + ax.set_ylabel("[kpc]") + + title = filename.replace("_", "\_") # exception handle underscore for latex + if meta.cosmology is not None: + title += ", $z$ = {0:.2e}".format(meta.z) + title += ", $t$ = {0:.1f}".format(meta.time.to("Myr")) + fig.suptitle(title) + + plt.tight_layout() + plt.savefig(figname) + plt.close() + gc.collect() + return + + +if __name__ == "__main__": + + snaplist = spt.get_snapshot_list(snapshot_base, plot_all, snapnr) + + for f in snaplist: + plot_result(f) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFCheck.py new file mode 100755 index 0000000000000000000000000000000000000000..2280d8c93ea0e19f57c259a3f620f8f79a7965fe --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFCheck.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# ---------------------------------------------------- +# Stromgren 3D with multifrequency bins +# The test is identical to the test in Section 5.2.2 of Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x +# The full multifrequency solution is taken from their TT1D result in their Figure 4. +# Plot comparison of simulated neutral fraction and temperature with the solution. +# ---------------------------------------------------- + +import sys + +import matplotlib as mpl +import numpy as np +import swiftsimio +import unyt +from matplotlib import pyplot as plt + +import stromgren_plotting_tools as spt + +# Plot parameters +params = { + "axes.labelsize": 14, + "axes.titlesize": 14, + "font.size": 14, + "legend.fontsize": 14, + "xtick.labelsize": 12, + "ytick.labelsize": 12, + "xtick.direction": "in", + "ytick.direction": "in", + "xtick.top": True, + "ytick.right": True, + "xtick.major.width": 1.5, + "ytick.major.width": 1.5, + "axes.linewidth": 1.5, + "text.usetex": True, + "figure.figsize": (10, 4), + "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": 1, + "lines.linewidth": 2.0, +} +mpl.rcParams.update(params) + +scatterplot_kwargs = { + "alpha": 0.1, + "s": 2, + "marker": ".", + "linewidth": 0.0, + "facecolor": "blue", +} + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +# WARNING: The reference solution is comparable with snapshot_102 only +plot_all = False +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + snapnr = -1 + print( + "WARNING: plotting all snapshots, but you should compare the reference solution with snapshot_102 only" + ) + +snapshot_base = "output_MF" + + +def get_TT1Dsolution(): + """ + Reading the reference solution from the test in Section 5.2.2 + of Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x + Output the radius, neutral fraction, and temperature at t = 100Myr + """ + TT1D_runit = 5.4 * unyt.kpc # kpc + data = np.loadtxt("data/xTT1D_Stromgren100Myr.txt", delimiter=",") + rtt1dlist = data[:, 0] * TT1D_runit + xtt1dlist = 10 ** data[:, 1] + + data = np.loadtxt("data/TTT1D_Stromgren100Myr.txt", delimiter=",") + rTtt1dlist = data[:, 0] * TT1D_runit + Ttt1dlist = 10 ** data[:, 1] * unyt.K + + outdict = { + "rtt1dlist": rtt1dlist, + "xtt1dlist": xtt1dlist, + "rTtt1dlist": rTtt1dlist, + "Ttt1dlist": Ttt1dlist, + } + return outdict + + +def plot_compare(filename): + # Read in data first + print("working on", filename) + data = swiftsimio.load(filename) + meta = data.metadata + boxsize = meta.boxsize + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + gamma = meta.gas_gamma + + xstar = data.stars.coordinates + xpart = data.gas.coordinates + dxp = xpart - xstar + r = np.sqrt(np.sum(dxp ** 2, axis=1)) + + imf = spt.get_imf(scheme, data) + xHI = imf.HI / (imf.HI + imf.HII) + + mu = spt.mean_molecular_weight(imf.HI, imf.HII, imf.HeI, imf.HeII, imf.HeIII) + data.gas.T = spt.gas_temperature(data.gas.internal_energies, mu, gamma) + + outdict = get_TT1Dsolution() + + fig, ax = plt.subplots(1, 2) + + ax[0].scatter(r, xHI, **scatterplot_kwargs) + ax[0].plot( + outdict["rtt1dlist"], outdict["xtt1dlist"], color="k", lw=2.0, label="TT1D" + ) + ax[0].set_ylabel("Neutral Fraction") + xlabel_units_str = meta.boxsize.units.latex_representation() + ax[0].set_xlabel("r [$" + xlabel_units_str + "$]") + ax[0].set_yscale("log") + ax[0].set_xlim([0, boxsize[0] / 2.0]) + + ax[1].scatter(r, data.gas.T, **scatterplot_kwargs) + ax[1].plot( + outdict["rTtt1dlist"], outdict["Ttt1dlist"], color="k", lw=2.0, label="TT1D" + ) + ax[1].set_ylabel("T [K]") + ax[1].set_xlabel("r [$" + xlabel_units_str + "$]") + ax[1].set_yscale("log") + ax[1].set_xlim([0, boxsize[0] / 2.0]) + ax[1].legend(loc="best") + + plt.tight_layout() + figname = filename[:-5] + figname += "-Stromgren3DMF.png" + plt.savefig(figname, dpi=200) + plt.close() + + +if __name__ == "__main__": + snaplist = spt.get_snapshot_list(snapshot_base, plot_all, snapnr) + for f in snaplist: + plot_compare(f) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFHHeCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFHHeCheck.py new file mode 100755 index 0000000000000000000000000000000000000000..b4902fdb4d19eccda36dfa7b442861117637b4d9 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFHHeCheck.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +# ---------------------------------------------------- +# Stromgren 3D with multifrequency bins +# The test is identical to the test in Section 5.3.2 of Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x +# The full multifrequency solution is taken from their TT1D result in their Figure 9. +# Plot comparison of simulated neutral fraction and temperature with the solution. +# ---------------------------------------------------- + +import sys + +import matplotlib as mpl +import matplotlib.lines as mlines +import numpy as np +import swiftsimio +import unyt +from matplotlib import pyplot as plt + +import stromgren_plotting_tools as spt + +# Plot parameters +params = { + "axes.labelsize": 14, + "axes.titlesize": 14, + "font.size": 14, + "legend.fontsize": 14, + "xtick.labelsize": 12, + "ytick.labelsize": 12, + "xtick.direction": "in", + "ytick.direction": "in", + "xtick.top": True, + "ytick.right": True, + "xtick.major.width": 1.5, + "ytick.major.width": 1.5, + "axes.linewidth": 1.5, + "text.usetex": True, + "figure.figsize": (10, 4), + "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": 1, + "lines.linewidth": 2.0, +} +mpl.rcParams.update(params) + +scatterplot_kwargs = {"alpha": 0.1, "s": 2, "marker": ".", "linewidth": 0.0} + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +# WARNING: The reference solution is comparable with snapshot_102 only +plot_all = False +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + snapnr = -1 + print( + "WARNING: plotting all snapshots, but you should compare the reference solution with snapshot_102 only" + ) + +snapshot_base = "output_HHe" + + +def get_TT1Dsolution_HHe(): + """ + Reading the reference solution from the test in Section 5.3.2 + of Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x + Output the radius, neutral fraction, and temperature at t = 100 Myr + """ + TT1D_runit = 5.4 * unyt.kpc # kpc + data = np.loadtxt("data/xHITT1D_Stromgren100Myr_HHe.txt", delimiter=",") + rHItt1dlist = data[:, 0] * TT1D_runit + xHItt1dlist = 10 ** data[:, 1] + + data = np.loadtxt("data/xHIITT1D_Stromgren100Myr_HHe.txt", delimiter=",") + rHIItt1dlist = data[:, 0] * TT1D_runit + xHIItt1dlist = 10 ** data[:, 1] + + data = np.loadtxt("data/xHeITT1D_Stromgren100Myr_HHe.txt", delimiter=",") + rHeItt1dlist = data[:, 0] * TT1D_runit + xHeItt1dlist = 10 ** data[:, 1] + + data = np.loadtxt("data/xHeIITT1D_Stromgren100Myr_HHe.txt", delimiter=",") + rHeIItt1dlist = data[:, 0] * TT1D_runit + xHeIItt1dlist = 10 ** data[:, 1] + + data = np.loadtxt("data/xHeIIITT1D_Stromgren100Myr_HHe.txt", delimiter=",") + rHeIIItt1dlist = data[:, 0] * TT1D_runit + xHeIIItt1dlist = 10 ** data[:, 1] + + data = np.loadtxt("data/TTT1D_Stromgren100Myr_HHe.txt", delimiter=",") + rTtt1dlist = data[:, 0] * TT1D_runit + Ttt1dlist = 10 ** data[:, 1] * unyt.K + + outdict = { + "rHItt1dlist": rHItt1dlist, + "xHItt1dlist": xHItt1dlist, + "rHIItt1dlist": rHIItt1dlist, + "xHIItt1dlist": xHIItt1dlist, + "rHeItt1dlist": rHeItt1dlist, + "xHeItt1dlist": xHeItt1dlist, + "rHeIItt1dlist": rHeIItt1dlist, + "xHeIItt1dlist": xHeIItt1dlist, + "rHeIIItt1dlist": rHeIIItt1dlist, + "xHeIIItt1dlist": xHeIIItt1dlist, + "rTtt1dlist": rTtt1dlist, + "Ttt1dlist": Ttt1dlist, + } + return outdict + + +def plot_compare(filename): + # Read in data first + print("working on", filename) + data = swiftsimio.load(filename) + meta = data.metadata + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + gamma = meta.gas_gamma + + xstar = data.stars.coordinates + xpart = data.gas.coordinates + dxp = xpart - xstar + r = np.sqrt(np.sum(dxp ** 2, axis=1)) + + imf = spt.get_imf(scheme, data) + + mu = spt.mean_molecular_weight(imf.HI, imf.HII, imf.HeI, imf.HeII, imf.HeIII) + data.gas.T = spt.gas_temperature(data.gas.internal_energies, mu, gamma) + + sA = spt.get_abundances(scheme, data) + xHI = sA.HI + xHII = sA.HII + xHeI = sA.HeI + xHeII = sA.HeII + xHeIII = sA.HeIII + + outdict = get_TT1Dsolution_HHe() + + fig, ax = plt.subplots(1, 2) + + ax[0].scatter(r, xHI, **scatterplot_kwargs, facecolor="k") + ax[0].scatter(r, xHII, **scatterplot_kwargs, facecolor="r") + ax[0].scatter(r, xHeI, **scatterplot_kwargs, facecolor="b") + ax[0].scatter(r, xHeII, **scatterplot_kwargs, facecolor="g") + ax[0].scatter(r, xHeIII, **scatterplot_kwargs, facecolor="y") + ax[0].plot( + outdict["rHItt1dlist"], + outdict["xHItt1dlist"], + color="k", + lw=2.0, + ls="dashed", + label="TT1D", + ) + ax[0].plot( + outdict["rHIItt1dlist"], outdict["xHIItt1dlist"], color="r", lw=2.0, ls="dashed" + ) + ax[0].plot( + outdict["rHeItt1dlist"], outdict["xHeItt1dlist"], color="b", lw=2.0, ls="dashed" + ) + ax[0].plot( + outdict["rHeIItt1dlist"], + outdict["xHeIItt1dlist"], + color="g", + lw=2.0, + ls="dashed", + ) + ax[0].plot( + outdict["rHeIIItt1dlist"], + outdict["xHeIIItt1dlist"], + color="y", + lw=2.0, + ls="dashed", + ) + ax[0].set_ylabel("Abundances") + xlabel_units_str = meta.boxsize.units.latex_representation() + ax[0].set_xlabel("r [$" + xlabel_units_str + "$]") + ax[0].set_yscale("log") + ax[0].set_xlim([0, 5.4 * 1.3]) + ax[0].set_ylim([1e-5, 1.1]) + # ax[0].legend(loc="best", fontsize=12) + TT1D_line = mlines.Line2D([], [], lw=2, ls="dashed", label="TT1D", color="k") + first_legend = ax[1].legend(handles=[TT1D_line], loc="best", fontsize=10) + ax[1].add_artist(first_legend) + + ax[1].scatter(r, data.gas.T, **scatterplot_kwargs) + ax[1].plot( + outdict["rTtt1dlist"], + outdict["Ttt1dlist"], + color="k", + lw=2.0, + label="TT1D", + ls="dashed", + ) + ax[1].set_ylabel("T [K]") + ax[1].set_xlabel("r [$" + xlabel_units_str + "$]") + ax[1].set_yscale("log") + ax[1].set_xlim([0, 5.4 * 1.3]) + HI_line = mlines.Line2D([], [], lw=2, ls="dashed", label="HI", color="k") + HII_line = mlines.Line2D([], [], lw=2, ls="dashed", label="HII", color="r") + HeI_line = mlines.Line2D([], [], lw=2, ls="dashed", label="HeI", color="b") + HeII_line = mlines.Line2D([], [], lw=2, ls="dashed", label="HeII", color="g") + HeIII_line = mlines.Line2D([], [], lw=2, ls="dashed", label="HeIII", color="y") + first_legend = ax[0].legend( + handles=[HI_line, HII_line, HeI_line, HeII_line, HeIII_line], + loc="best", + ncol=2, + fontsize=10, + ) + ax[0].add_artist(first_legend) + + plt.tight_layout() + figname = filename[:-5] + figname += "-Stromgren3DMFHHe.png" + plt.savefig(figname, dpi=200) + plt.close() + + +if __name__ == "__main__": + snaplist = spt.get_snapshot_list(snapshot_base, plot_all, snapnr) + for f in snaplist: + plot_compare(f) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DsinglebinCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DsinglebinCheck.py new file mode 100755 index 0000000000000000000000000000000000000000..34a0f9b8914256e789aab5f5811cb0a568fa123e --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DsinglebinCheck.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 + +# ---------------------------------------------------- +# Stromgren 3D with grey approximation (single-frequency bin) and fixed temperature +# The test is identical to Test 1 in Iliev et al. 2006 doi:10.1111/j.1365-2966.2006.10775.x +# Analytic solution is described in Appendix C of SPHM1RT paper (https://arxiv.org/abs/2102.08404) +# Plot comparison of simulated neutral fraction with analytic solution +# ---------------------------------------------------- + +import sys + +import matplotlib as mpl +import numpy as np +import swiftsimio +import unyt +from matplotlib import pyplot as plt +from scipy.integrate import odeint + +import stromgren_plotting_tools as spt + +# Plot parameters +params = { + "axes.labelsize": 14, + "axes.titlesize": 14, + "font.size": 14, + "legend.fontsize": 14, + "xtick.labelsize": 12, + "ytick.labelsize": 12, + "xtick.direction": "in", + "ytick.direction": "in", + "xtick.top": True, + "ytick.right": True, + "xtick.major.width": 1.5, + "ytick.major.width": 1.5, + "axes.linewidth": 1.5, + "text.usetex": True, + "figure.figsize": (5, 4), + "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": 1, + "lines.linewidth": 2.0, +} +mpl.rcParams.update(params) + +scatterplot_kwargs = { + "alpha": 0.1, + "s": 2, + "marker": ".", + "linewidth": 0.0, + "facecolor": "blue", +} + +# Read in cmdline arg: Are we plotting only one snapshot, or all? +# WARNING: The reference solution is comparable with snapshot_500 only +plot_all = False +try: + snapnr = int(sys.argv[1]) +except IndexError: + plot_all = True + snapnr = -1 + print( + "WARNING: plotting all snapshots, but you should compare the reference solution with snapshot_500 only" + ) + +snapshot_base = "output_singlebin" + + +def fn_neutralfraction3d(xn, rn): + """ + ODE for Stromgren sphere 3D: + this is the rhs of the ODE to integrate, i.e. dx/drn=fn(x,r)=x*(1-x)/(1+x)*(2/rn+x) + """ + return xn * (1.0 - xn) / (1.0 + xn) * (2.0 / rn + xn) + + +# analytic solution +def Stromgren3D_neutralfraction3d(rfunc, nH, sigma, alphaB, dNinj, rini): + """ + This function integrates the ODE for Stromgren sphere 3D + Output the neutral fraction xn as a function of radius rfunc + """ + xn0 = nH * alphaB * 4.0 * np.pi / sigma / dNinj * rini * rini + rnounit = rfunc * nH * sigma + xn = odeint(fn_neutralfraction3d, xn0, rnounit) + return xn + + +def get_analytic_neutralfraction_stromgren3D(data, scheme): + """ + This function reads the parameters from snapshot, + and then integrates the ODE for Stromgren sphere 3D + Output the neutral fraction xn and radius r_ana + Work only for SPHM1RT (for now) + """ + meta = data.metadata + rho = data.gas.densities + rini_value = 0.1 + r_ana = np.linspace(rini_value, 10.0, 100) * unyt.kpc + rini = rini_value * unyt.kpc + nH = np.mean(rho.to("g/cm**3") / unyt.proton_mass) + + if scheme.startswith("SPH M1closure"): + sigma_cross = spt.trim_paramstr( + meta.parameters["SPHM1RT:sigma_cross"].decode("utf-8") + ) * unyt.unyt_array(1.0, "cm**2") + sigma = sigma_cross[0] + alphaB = spt.trim_paramstr( + meta.parameters["SPHM1RT:alphaB"].decode("utf-8") + ) * unyt.unyt_array(1.0, "cm**3/s") + else: + raise ValueError( + "Error: Currently get_analytic_neutralfraction_stromgren3D can only work with SPHM1RT" + ) + units = data.units + unit_l_in_cgs = units.length.in_cgs() + unit_v_in_cgs = (units.length / units.time).in_cgs() + unit_m_in_cgs = units.mass.in_cgs() + star_emission_rates = ( + spt.trim_paramstr( + meta.parameters["SPHM1RT:star_emission_rates"].decode("utf-8") + ) + * unit_m_in_cgs + * unit_v_in_cgs ** 3 + / unit_l_in_cgs + ) + ionizing_photon_energy_erg = ( + spt.trim_paramstr( + meta.parameters["SPHM1RT:ionizing_photon_energy_erg"].decode("utf-8") + ) + * unyt.erg + ) + dNinj = star_emission_rates[1] / ionizing_photon_energy_erg[0] + xn = Stromgren3D_neutralfraction3d(r_ana, nH, sigma, alphaB, dNinj, rini) + return r_ana, xn + + +def plot_analytic_compare(filename): + # Read in data first + print("working on", filename) + data = swiftsimio.load(filename) + meta = data.metadata + boxsize = meta.boxsize + scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8")) + + xstar = data.stars.coordinates + xpart = data.gas.coordinates + dxp = xpart - xstar + r = np.sqrt(np.sum(dxp ** 2, axis=1)) + + imf = spt.get_imf(scheme, data) + xHI = imf.HI / (imf.HI + imf.HII) + + r_ana, xn = get_analytic_neutralfraction_stromgren3D(data, scheme) + plt.scatter(r, xHI, **scatterplot_kwargs) + plt.plot(r_ana, xn) + plt.ylabel("Neutral Fraction") + xlabel_units_str = meta.boxsize.units.latex_representation() + plt.xlabel("r [$" + xlabel_units_str + "$]") + plt.yscale("log") + plt.xlim([0, boxsize[0] / 2.0]) + plt.tight_layout() + figname = filename[:-5] + figname += "-Stromgren3Dsinglebin.png" + plt.savefig(figname, dpi=200) + plt.close() + + +if __name__ == "__main__": + snaplist = spt.get_snapshot_list(snapshot_base, plot_all, snapnr) + for f in snaplist: + plot_analytic_compare(f) diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/propagationTest-3D.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/propagationTest-3D.yml new file mode 100644 index 0000000000000000000000000000000000000000..3fa7414add7e5f2dafd8f5c5a0651a953b79e288 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/propagationTest-3D.yml @@ -0,0 +1,71 @@ +MetaData: + run_name: StromgrenSpherePropagationTest3D + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1. + UnitLength_in_cgs: 1. + UnitVelocity_in_cgs: 1. + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 8 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.001 # end time: radiation reaches edge of box + dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). + dt_max: 2.e-04 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: propagation_test # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.0001 # Time between snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 5e-4 # 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.6 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./propagationTest-3D.hdf5 # The file to read + periodic: 1 # peridioc ICs. Keep them periodic so we don't loose photon energy. + +GEARRT: + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.99 + photon_groups_Hz: [0.] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1e-28] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + skip_thermochemistry: 1 # skip thermochemistry. + stars_max_timestep: 1.562500e-05 # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + + +SPHM1RT: + cred: 2.99792458e10 # reduce the speed of light in the code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + photon_groups_Hz: [0.,1.,2.] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter + star_emission_rates: [382600.0, 1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates (1e-28 Lsun) for each photon frequency group to use if use_constant_emission_rates is set. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + stars_max_timestep: 1.953125e-06 # update stars every step! + skip_thermochemistry: 1 # ignore thermochemistry. + diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh index 0afaf94220f0aa102c5db22c05c79e47ec2da6e0..fa28e8d59c40c7ce7ae7a7365d33f5f8d6fe845e 100755 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh @@ -16,13 +16,13 @@ if [ ! -f 'stromgrenSphere-3D.hdf5' ]; then fi # Run SWIFT with RT -../../swift \ +../../../swift \ --hydro --threads=4 --stars --external-gravity \ - --feedback --radiation --fpe \ + --feedback --radiation \ stromgrenSphere-3D.yml 2>&1 | tee output.log -# Plot the photon propagation checks. -# Make sure you set the correct photon group to plot -# inside the script -python3 ./plotPhotonPropagationCheck.py +# option with mpi +# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D.yml 2>&1 | tee output.log +# Plot the Stromgren 3D checks. +python3 ./plotSolution.py diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/runPropagationTest.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/runPropagationTest.sh new file mode 100755 index 0000000000000000000000000000000000000000..3134a61e732a516b529ba5ca6a997b4cbffea8c8 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/runPropagationTest.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for Strömgen Sphere 3D example ..." + ./getGlass.sh +fi + +if [ ! -f 'propagationTest-3D.hdf5' ]; then + echo "Generating ICs" + python3 makePropagationTestIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro --threads=4 --stars --external-gravity \ + --feedback --radiation \ + ./propagationTest-3D.yml 2>&1 | tee output.log + +# Plot the photon propagation checks. +# Make sure you set the correct photon group to plot +# inside the script +python3 ./plotPhotonPropagationCheck.py diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MF.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MF.sh new file mode 100755 index 0000000000000000000000000000000000000000..dfdc64b884744aec9bd08fc2a4b7c9b7751edff1 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MF.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for Strömgen Sphere 3D example ..." + ./getGlass.sh +fi + +if [ ! -f 'stromgrenSphere-3D.hdf5' ]; then + echo "Generating ICs" + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro --threads=4 --stars --external-gravity \ + --feedback --radiation \ + stromgrenSphere-3D-MF.yml 2>&1 | tee output.log + +# option with mpi +# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-MF.yml 2>&1 | tee output.log + +# Plot the Stromgren 3D multifrequency checks. +python3 ./plotStromgren3DMFCheck.py 10 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MFHHe.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MFHHe.sh new file mode 100755 index 0000000000000000000000000000000000000000..f124e0f82c0c39a63804f4c668a8aaf2cc0c2288 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MFHHe.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for Strömgen Sphere 3D example ..." + ./getGlass.sh +fi + +if [ ! -f 'stromgrenSphere-3D-HHe.hdf5' ]; then + echo "Generating ICs" + python3 makeIC_HHe.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro --threads=4 --stars --external-gravity \ + --feedback --radiation \ + stromgrenSphere-3D-MFHHe.yml 2>&1 | tee output.log + +# option with mpi +# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-MFHHe.yml 2>&1 | tee output.log + +# Plot the Stromgren 3D with hydrogen and helium checks. +python3 ./plotStromgren3DMFHHeCheck.py 10 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_singlebin.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_singlebin.sh new file mode 100755 index 0000000000000000000000000000000000000000..b85a7e598480954dca64d4981fb45bea2f3e8ff8 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_singlebin.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# make run.sh fail if a subcommand fails +set -e +set -o pipefail + +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for Strömgen Sphere 3D example ..." + ./getGlass.sh +fi + +if [ ! -f 'stromgrenSphere-3D.hdf5' ]; then + echo "Generating ICs" + python3 makeIC.py +fi + +# Run SWIFT with RT +../../../swift \ + --hydro --threads=4 --stars --external-gravity \ + --feedback --radiation \ + stromgrenSphere-3D-singlebin.yml 2>&1 | tee output.log + +# option with mpi +# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-singlebin.yml 2>&1 | tee output.log + +# Plot the Stromgren 3D single frequency checks. +python3 ./plotStromgren3DsinglebinCheck.py 50 diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MF.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MF.yml new file mode 100644 index 0000000000000000000000000000000000000000..2de98b310d37b85c5636ef73038b557cf34e3066 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MF.yml @@ -0,0 +1,95 @@ +MetaData: + run_name: StromgrenSphere-3D-MF + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841586e+33 # 1 M_Sol + UnitLength_in_cgs: 3.08567758e21 # kpc in cm + UnitVelocity_in_cgs: 1.e5 # km/s + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. # K + + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.112 # end time + dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-03 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output_MF # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.0102 # Time between snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 1e-3 # 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.8 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10 # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./stromgrenSphere-3D.hdf5 # The file to read + periodic: 1 # periodic ICs. + +Stars: + resolution_eta: 2.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. + +GEARRT: + f_reduce_c: 0.01 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1.764e+04, 3.631e+04, 8.037e+03 ] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 1.00 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.999999 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0. # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + stars_max_timestep: 6.250000e-05 # (Optional) restrict the maximal timestep of stars to this value (in internal units) + + +# commented parameters are for isothermal single-frequency bin Stromgren Sphere +SPHM1RT: + cred: 2.99792458e3 # value of reduced speed of light for the RT solver in code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + chi: [0, 0, 0, 0] # (Optional) initial opacity in code unit for all gas particles + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). Outer edges of zero and infinity are assumed. + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32, 1.05e11, 2.16e11, 4.80e10] # (Optional) constant star emission rates (internal unit: energy/time) for each photon frequency group to use if use_constant_emission_rates is set. # = 5e48 photons/sec and BB1e5 + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stars_max_timestep: 1.5625e-05 # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 1.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + coolingon: 1 # (Optional) switch for cooling (and photoheating), but photo-ionization will be ongoing even if coolingon==0 + useabundances: 1 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 1e-5 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 0.99999 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 1e-5 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.0 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 0.0 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 0.0 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) + relativeTolerance: 1e-3 # (Optional) Relative tolerance for SPHM1RT thermo-chemistry intergration + absoluteTolerance: 1e-10 # (Optional) Absolute tolerance for SPHM1RT thermo-chemistry integration + explicitTolerance: 0.1 # (Optional) Tolerance below which we use the explicit solution in SPHM1RT thermo-chemistry + ionizing_photon_energy_erg: [3.0208e-11, 5.61973e-11, 1.05154e-10] # (Optional) ionizing photon energy in erg averaged within frequency bins + reinject: 1 # (Optional) gather energy around injection radius and re-inject the energy + + + diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MFHHe.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MFHHe.yml new file mode 100644 index 0000000000000000000000000000000000000000..ca3a311cfcff596d0d0059671e3a8841e4b743a0 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MFHHe.yml @@ -0,0 +1,93 @@ +MetaData: + run_name: StromgrenSphere-3D-MFHHe + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841586e+33 # 1 M_Sol + UnitLength_in_cgs: 3.08567758e21 # kpc in cm + UnitVelocity_in_cgs: 1.e5 # km/s + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. # K + + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.112 # end time + dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-03 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output_HHe # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.0102 # Time between snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 1e-3 # 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.8 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10 # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./stromgrenSphere-3D-HHe.hdf5 # The file to read + periodic: 1 # periodic ICs. + +Stars: + resolution_eta: 2.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. + +GEARRT: + f_reduce_c: 0.01 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1.764e+04, 3.631e+04, 8.037e+03 ] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.75 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.75 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.25 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + stars_max_timestep: 6.250000e-05 # (Optional) restrict the maximal timestep of stars to this value (in internal units) + + +# commented parameters are for isothermal single-frequency bin Stromgren Sphere +SPHM1RT: + cred: 2.99792458e3 # value of reduced speed of light for the RT solver in code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + chi: [0, 0, 0, 0] # (Optional) initial opacity in code unit for all gas particles + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). Outer edges of zero and infinity are assumed. + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32, 1.05e11, 2.16e11, 4.80e10] # (Optional) constant star emission rates (internal unit: energy/time) for each photon frequency group to use if use_constant_emission_rates is set. # = 5e48 photons/sec and BB1e5 + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stars_max_timestep: 1.5625e-05 # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 0.75 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.25 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + coolingon: 1 # (Optional) switch for cooling (and photoheating), but photo-ionization will be ongoing even if coolingon==0 + useabundances: 1 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 1e-5 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 0.99999 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 1e-5 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.08333 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 0.0 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 0.0 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) + relativeTolerance: 1e-3 # (Optional) Relative tolerance for SPHM1RT thermo-chemistry intergration + absoluteTolerance: 1e-10 # (Optional) Absolute tolerance for SPHM1RT thermo-chemistry integration + explicitTolerance: 0.1 # (Optional) Tolerance below which we use the explicit solution in SPHM1RT thermo-chemistry + ionizing_photon_energy_erg: [3.0208e-11, 5.61973e-11, 1.05154e-10] # (Optional) ionizing photon energy in erg averaged within frequency bins + reinject: 1 # (Optional) gather energy around injection radius and re-inject the energy + diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-singlebin.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-singlebin.yml new file mode 100644 index 0000000000000000000000000000000000000000..203c41ad0c7eb08f05f7a32c999e1789f68db1a1 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-singlebin.yml @@ -0,0 +1,99 @@ +MetaData: + run_name: StromgrenSphere-3D-singlebin + +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841586e+33 # 1 M_Sol + UnitLength_in_cgs: 3.08567758e21 # kpc in cm + UnitVelocity_in_cgs: 1.e5 # km/s + UnitCurrent_in_cgs: 1. + UnitTemp_in_cgs: 1. # K + + +# Parameters governing the time integration +TimeIntegration: + max_nr_rt_subcycles: 1 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.512 # end time + dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-03 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output_singlebin # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.0102 # Time between snapshots + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 1e-3 # 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.8 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10 # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./stromgrenSphere-3D.hdf5 # The file to read + periodic: 1 # periodic ICs. + +Stars: + resolution_eta: 2.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. + +GEARRT: + f_reduce_c: 0.01 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 + photon_groups_Hz: [3.288e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [6.198024e+04] # # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 1.00 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.999999 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 1.e-6 # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0. # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + stars_max_timestep: 6.250000e-05 # (Optional) restrict the maximal timestep of stars to this value (in internal units) + + +# commented parameters are for isothermal single-frequency bin Stromgren Sphere +SPHM1RT: + cred: 2.99792458e3 # value of reduced speed of light for the RT solver in code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + chi: [0, 0, 0, 0] # (Optional) initial opacity in code unit for all gas particles + photon_groups_Hz: [3.288e15, 5.945e19, 5.946e19] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). Outer edges of zero and infinity are assumed. + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32, 2.34e11, 1e-32, 1e-32] # (Optional) constant star emission rates (internal unit: energy/time) for each photon frequency group to use if use_constant_emission_rates is set. # = 5e48 photons/sec + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stars_max_timestep: 7.8125e-6 # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 1.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + coolingon: 0 # (Optional) switch for cooling (and photoheating), but photo-ionization will be ongoing even if coolingon==0 + useabundances: 1 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 1e-5 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 0.99999 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 1e-5 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.0 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 0.0 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 0.0 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) + relativeTolerance: 1e-3 # (Optional) Relative tolerance for SPHM1RT thermo-chemistry intergration + absoluteTolerance: 1e-10 # (Optional) Absolute tolerance for SPHM1RT thermo-chemistry integration + explicitTolerance: 0.1 # (Optional) Tolerance below which we use the explicit solution in SPHM1RT thermo-chemistry + ionizing_photon_energy_erg: [3.0208e-11, 5.61973e-11, 1.05154e-10] # (Optional) ionizing photon energy in erg averaged within frequency bins + useparams: 1 # (Optional) switch to use thermo-chemistry parameters from the parameter file + sigma_cross: [8.13e-18, 1e-32, 1e-32] # (Conditional) (if useparams=1) The cross section of ionizing photons for hydrogen (cm^2) + alphaB: 2.59e-13 # (Conditional) (if useparams=1) The case B recombination coefficient for hydrogen (cgs) + beta: 3.1e-16 # (Conditional) (if useparams=1) The collisional ionization coefficient for hydrogen (cgs) + reinject: 1 # (Optional) gather energy around injection radius and re-inject the energy + + + diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml index 97499f58f4342e14c827b97d062a9129247d1484..41fd6d4f38875ef4c9ab7028eb5b5af2e4ef7d99 100644 --- a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml @@ -1,59 +1,92 @@ MetaData: - run_name: "StromgrenSphere-3D" + run_name: StromgrenSphere-3D # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1. - UnitLength_in_cgs: 1. - UnitVelocity_in_cgs: 1. + UnitMass_in_cgs: 1.98841586e+33 # 1 M_Sol + UnitLength_in_cgs: 3.08567758e21 # kpc in cm + UnitVelocity_in_cgs: 1.e5 # km/s UnitCurrent_in_cgs: 1. - UnitTemp_in_cgs: 1. + UnitTemp_in_cgs: 1. # K + # Parameters governing the time integration TimeIntegration: - time_begin: 0. # The starting time of the simulation (in internal units). - time_end: 0.001 # end time: radiation reaches edge of box - dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). - dt_max: 2.e-04 # The maximal time-step size of the simulation (in internal units). + max_nr_rt_subcycles: 16 + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.512 # end time + dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). + dt_max: 1.e-03 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots Snapshots: basename: output # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) - delta_time: 0.0001 # Time between snapshots + delta_time: 0.001 # Time between snapshots # Parameters governing the conserved quantities statistics Statistics: time_first: 0. - delta_time: 5e-4 # Time between statistics output + delta_time: 1e-3 # 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.6 # Courant-Friedrich-Levy condition for time integration. - minimal_temperature: 10. # Kelvin + CFL_condition: 0.8 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10 # Kelvin # Parameters related to the initial conditions InitialConditions: file_name: ./stromgrenSphere-3D.hdf5 # The file to read - periodic: 1 # peridioc ICs. Keep them periodic so we don't loose photon energy. TODO: CHANGE LATER WHEN YOU ACTUALLY DO GAS INTERACTIONS + periodic: 1 # periodic ICs. -Scheduler: - max_top_level_cells: 64 +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. GEARRT: - f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.6 - photon_groups_Hz: [] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter - star_emission_rates_LSol: [1e-28] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. - hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas - stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. - stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. - set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) - mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value - mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value - mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value - mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value - mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + f_reduce_c: 0.01 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1.764e+04, 3.631e+04, 8.037e+03 ] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 1.00 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 1 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.999999 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 1.e-6 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + stars_max_timestep: 6.26e-5 # (Optional) restrict the maximal timestep of stars to this value (in internal units) + + +SPHM1RT: + cred: 2.99792458e3 # value of reduced speed of light for the RT solver in code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + chi: [0, 0, 0, 0] # (Optional) initial opacity in code unit for all gas particles + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). Outer edges of zero and infinity are assumed. + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32, 1.05e11, 2.16e11, 4.80e10] # (Optional) constant star emission rates (internal unit: energy/time) for each photon frequency group to use if use_constant_emission_rates is set. # = 5e48 photons/sec and BB1e5 + stellar_spectrum_type: 1 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stars_max_timestep: 7.812500e-06 # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + stellar_spectrum_blackbody_temperature_K: 1.e5 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 1.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.0 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + coolingon: 1 # (Optional) switch for cooling (and photoheating), but photo-ionization will be ongoing even if coolingon==0 + useabundances: 1 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 1e-5 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 0.99999 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 1e-5 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.0 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 0.0 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 0.0 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) + relativeTolerance: 1e-3 # (Optional) Relative tolerance for SPHM1RT thermo-chemistry intergration + absoluteTolerance: 1e-10 # (Optional) Absolute tolerance for SPHM1RT thermo-chemistry integration + explicitTolerance: 0.1 # (Optional) Tolerance below which we use the explicit solution in SPHM1RT thermo-chemistry + ionizing_photon_energy_erg: [3.0208e-11, 5.61973e-11, 1.05154e-10] # (Optional) ionizing photon energy in erg averaged within frequency bins + diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgren_plotting_tools.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgren_plotting_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..8141897acdc9b1a93b482789080c1680f30651d7 --- /dev/null +++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgren_plotting_tools.py @@ -0,0 +1,432 @@ +# Contains commonly used functions related to plotting results +# of the Strömgren Sphere Examples and other gas related functions. + +import copy +import os + +import numpy as np +import unyt + +# species masses in atomic mass units +mamu = {"e": 0.0, "HI": 1.0, "HII": 1.0, "HeI": 4.0, "HeII": 4.0, "HeIII": 4.0} + + +def get_number_densities(Temp, XH, XHe): + """ + Compute the number densities of all species at a given + temperature following Katz, Hernquist, and Weinberg 1996 + + Temp: temperature [unyt quantity] + XH: total mass fraction of all hydrogen species (HI + HII) + XHe: total mass fraction of all helium species (HeI + HeII + HeIII) + """ + + # n_H = X_H * rho_gas / m_H + # then m_H = rho_gas * X_H / n_H + # n_He = X_He * rho_gas / m_He = + # = (1 - X_H) * rho_gas / (4 * m_H) + # = (1 - X_H) * rho_gas / (4 * rho_gas * x_H / n_H) + # = (1 - X_H) / (4 * X_H) * n_H + # = X_He / 4(1 - X_He) * n_H = y * n_H + + if XH == 0: + nH = 0.0 + nHe = 1.0 + else: + nH = XH + nHe = XHe / 4 + + # NOTE: This is not the ionization threshold! + if Temp < 5000 * unyt.K: + nH0 = nH + nHp = 0.0 + nHe0 = nHe + nHep = 0.0 + nHepp = 0.0 + + else: + + Temp.convert_to_cgs() + T = Temp.v + # Recombination rate for H+ in units of cm^3 s^-1 + A_Hp = ( + 8.40e-11 + / np.sqrt(T) + * (T * 1e-3) ** (-0.2) + * 1.0 + / (1.0 + (T * 1e-6) ** 0.7) + ) + + # Dielectronic recombination rate for He+ in units of cm^3 s^-1 + A_d = ( + 1.9e-3 + / T ** 1.5 + * np.exp(-470000.0 / T) + * (1.0 + 0.3 * np.exp(-94000.0 / T)) + ) + # Recombination rate for He+ in units of cm^3 s^-1 + A_Hep = 1.5e-10 / T ** 0.6353 + # Recombination rate for He++ in units of cm^3 s^-1 + A_Hepp = ( + 3.36e-10 + / np.sqrt(T) + * (T * 1e-3) ** (-0.2) + * 1.0 + / (1.0 + (T * 1e-6) ** 0.7) + ) + # collisional ionization rate for H0 in units of cm^3 s^-1 + # G_H0 = 1.17e-10 * np.sqrt(T) * np.exp(-157809.1 / T) * 1. / (1. + np.sqrt(T*1e-5)) + G_H0 = ( + 5.85e-11 + * np.sqrt(T) + * np.exp(-157809.1 / T) + * 1.0 + / (1.0 + np.sqrt(T * 1e-5)) + ) + # collisional ionization rate for He0 in units of cm^3 s^-1 + G_He0 = ( + 2.38e-11 + * np.sqrt(T) + * np.exp(-285335.4 / T) + * 1.0 + / (1.0 + np.sqrt(T * 1e-5)) + ) + # collisional ionization rate for He+ in units of cm^3 s^-1 + G_Hep = ( + 5.68e-12 + * np.sqrt(T) + * np.exp(-631515.0 / T) + * 1.0 + / (1.0 + np.sqrt(T * 1e-5)) + ) + + # Katz et al. 1996 eq. 33 - 38 + # Note: We assume all photoionization rates to be zero. + # Also, here we don't care about the actual number density, i.e. + # about the volume, since it'll cancel out later when we compute + # the mass fractions. + + nH0 = nH * A_Hp / (A_Hp + G_H0) + nHp = nH - nH0 + nHep = nHe / (1.0 + (A_Hep + A_d) / G_He0 + G_Hep / A_Hepp) + nHe0 = nHep * (A_Hep + A_d) / G_He0 + nHepp = nHep * G_Hep / A_Hepp + + # electron density + ne = nHp + nHep + 2.0 * nHepp + + return nH0, nHp, nHe0, nHep, nHepp, ne + + +def get_number_densities_array(Temp, XH, XHe): + """ + Compute the number densities of all species at a given + temperature following Katz, Hernquist, and Weinberg 1996 + + Temp: temperature [unyt array] + XH: total mass fraction of all hydrogen species (HI + HII) + XHe: total mass fraction of all helium species (HeI + HeII + HeIII) + """ + + # n_H = X_H * rho_gas / m_H + # then m_H = rho_gas * X_H / n_H + # n_He = X_He * rho_gas / m_He = + # = (1 - X_H) * rho_gas / (4 * m_H) + # = (1 - X_H) * rho_gas / (4 * rho_gas * x_H / n_H) + # = (1 - X_H) / (4 * X_H) * n_H + # = X_He / 4(1 - X_He) * n_H = y * n_H + + nH = np.zeros(XH.shape, dtype=np.float64) + nHe = np.zeros(XH.shape, dtype=np.float64) + + mask = XH == 0 + nH[mask] = 0.0 + nHe[mask] = 1.0 + + inv_mask = np.logical_not(mask) + nH[inv_mask] = XH[inv_mask] + nHe[inv_mask] = 0.25 * XHe[inv_mask] + + nH0 = np.zeros(XH.shape, dtype=np.float64) + nHp = np.zeros(XH.shape, dtype=np.float64) + nHe0 = np.zeros(XH.shape, dtype=np.float64) + nHep = np.zeros(XH.shape, dtype=np.float64) + nHepp = np.zeros(XH.shape, dtype=np.float64) + + # NOTE: This is not the ionization threshold! + neutral = Temp < 5000 * unyt.K + + nH0[neutral] = nH[neutral] + nHp[neutral] = 0.0 + nHe0[neutral] = nHe[neutral] + nHep[neutral] = 0.0 + nHepp[neutral] = 0.0 + + Temp.convert_to_cgs() + T = Temp.v + ionized = np.logical_not(neutral) + + # Recombination rate for H+ in units of cm^3 s^-1 + A_Hp = ( + 8.40e-11 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7) + ) + # Dielectronic recombination rate for He+ in units of cm^3 s^-1 + A_d = 1.9e-3 / T ** 1.5 * np.exp(-470000.0 / T) * (1.0 + 0.3 * np.exp(-94000.0 / T)) + # Recombination rate for He+ in units of cm^3 s^-1 + A_Hep = 1.5e-10 / T ** 0.6353 + # Recombination rate for He++ in units of cm^3 s^-1 + A_Hepp = ( + 3.36e-10 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7) + ) + # collisional ionization rate for H0 in units of cm^3 s^-1 + G_H0 = ( + 5.85e-11 * np.sqrt(T) * np.exp(-157809.1 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5)) + ) + # collisional ionization rate for He0 in units of cm^3 s^-1 + G_He0 = ( + 2.38e-11 * np.sqrt(T) * np.exp(-285335.4 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5)) + ) + # collisional ionization rate for He+ in units of cm^3 s^-1 + G_Hep = ( + 5.68e-12 * np.sqrt(T) * np.exp(-631515.0 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5)) + ) + + # Katz et al. 1996 eq. 33 - 38 + # Note: We assume all photoionization rates to be zero. + # Also, here we don't care about the actual number density, i.e. + # about the volume, since it'll cancel out later when we compute + # the mass fractions. + + nH0[ionized] = nH[ionized] * A_Hp[ionized] / (A_Hp[ionized] + G_H0[ionized]) + nHp[ionized] = nH[ionized] - nH0[ionized] + nHep[ionized] = nHe[ionized] / ( + 1.0 + + (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized] + + G_Hep[ionized] / A_Hepp[ionized] + ) + nHe0[ionized] = nHep[ionized] * (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized] + nHepp[ionized] = nHep[ionized] * G_Hep[ionized] / A_Hepp[ionized] + + # electron density + ne = nHp + nHep + 2.0 * nHepp + + return nH0, nHp, nHe0, nHep, nHepp, ne + + +def get_mass_fractions(T, XH, XHe): + """ + Compute the mass fractions of all species at a + given temperature + + T: temperature + XH: total mass fraction of all hydrogen species (HI + HII) + XHe: total mass fraction of all helium species (HeI + HeII + HeIII) + """ + + # first get number densities + if isinstance(XH, np.ndarray): + nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities_array(T, XH, XHe) + else: + nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities(T, XH, XHe) + + # now get mass denities in units of atomic mass units + mH0 = nH0 + mHp = nHp + mHe0 = 4.0 * nHe0 + mHep = 4.0 * nHep + mHepp = 4.0 * nHepp + # neglect electron mass contributions + + mtot = mH0 + mHp + mHe0 + mHep + mHepp # + me + + XH0 = mH0 / mtot + XHp = mHp / mtot + XHe0 = mHe0 / mtot + XHep = mHep / mtot + XHepp = mHepp / mtot + # Xe = me / mtot + + return XH0, XHp, XHe0, XHep, XHepp + + +def internal_energy(T, mu, gamma): + """ + Compute the internal energy of the gas for a given + temperature and mean molecular weight + """ + # Using u = 1 / (gamma - 1) * p / rho + # and p = N/V * kT = rho / (mu * m_u) * kT + + u = unyt.boltzmann_constant * T / (gamma - 1) / (mu * unyt.atomic_mass_unit) + return u + + +def get_soundspeed_from_internal_energy(data): + """ + Compute the local sound speed for all particles. + data: swiftsimio.load() object. + """ + + u = data.gas.internal_energies + gamma = data.metadata.gas_gamma + + return np.sqrt(u * gamma / (gamma - 1)) + + +def get_soundspeed_from_density_pressure(data): + """ + Compute the local sound speed for all particles. + data: swiftsimio.load() object. + """ + + gamma = data.metadata.gas_gamma + P = data.gas.pressures + rho = data.gas.densities + + return np.sqrt(gamma * P / rho) + + +def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp): + """ + Determines the mean molecular weight for given + mass fractions of + hydrogen: XH0 + H+: XHp + He: XHe0 + He+: XHep + He++: XHepp + + returns: + mu: mean molecular weight [in atomic mass units] + NOTE: to get the actual mean mass, you still need + to multiply it by m_u, as is tradition in the formulae + """ + + # 1/mu = sum_j X_j / A_j * (1 + E_j) + # A_H = 1, E_H = 0 + # A_Hp = 1, E_Hp = 1 + # A_He = 4, E_He = 0 + # A_Hep = 4, E_Hep = 1 + # A_Hepp = 4, E_Hepp = 2 + one_over_mu = XH0 + 2 * XHp + 0.25 * XHe0 + 0.5 * XHep + 0.75 * XHepp + + return 1.0 / one_over_mu + + +def gas_temperature(u, mu, gamma): + """ + Compute the gas temperature given the specific internal + energy u and the mean molecular weight mu + """ + + # Using u = 1 / (gamma - 1) * p / rho + # and p = N/V * kT = rho / (mu * m_u) * kT + + T = u * (gamma - 1) * mu * unyt.atomic_mass_unit / unyt.boltzmann_constant + + return T.to("K") + + +def get_snapshot_list(snapshot_basename="output", plot_all=True, snapnr=0): + """ + Find the snapshot(s) that are to be plotted + and return their names as list + """ + + snaplist = [] + + if plot_all: + dirlist = os.listdir() + for f in dirlist: + if f.startswith(snapshot_basename) and f.endswith("hdf5"): + snaplist.append(f) + + snaplist = sorted(snaplist) + + else: + fname = snapshot_basename + "_" + str(snapnr).zfill(4) + ".hdf5" + if not os.path.exists(fname): + print("Didn't find file", fname) + quit(1) + snaplist.append(fname) + + return snaplist + + +def get_imf(scheme, data): + """ + Get the ion mass fraction (imf) according to the scheme. + return a class with ion mass fraction for species X, + including HI, HII, HeI, HeII, HeIII: + The ion mass function can be accessed through: imf.X + The unit is in m_X/m_tot, where m_X is the mass in species X + and m_tot is the total gas mass. + """ + if scheme.startswith("GEAR M1closure"): + imf = data.gas.ion_mass_fractions + elif scheme.startswith("SPH M1closure"): + # atomic mass + mass_fraction_hydrogen = data.gas.rt_element_mass_fractions.hydrogen + imf = copy.deepcopy(data.gas.rt_species_abundances) + named_columns = data.gas.rt_species_abundances.named_columns + for column in named_columns: + # abundance is in n_X/n_H unit. We convert it to mass fraction by multipling mass fraction of H + mass_fraction = ( + getattr(data.gas.rt_species_abundances, column) + * mass_fraction_hydrogen + * mamu[column] + ) + setattr(imf, column, mass_fraction) + else: + raise ValueError("Unknown scheme", scheme) + return imf + + +def get_abundances(scheme, data): + """ + Get the species abundance according to the scheme + return a class with normalized number densities for abunance X, + including HI, HII, HeI, HeII, HeIII: + The abundances can be accessed through: sA.X + The unit is in n_X/n_H, where n_X is the number density of species X + and n_H is the number density of hydrogen. + """ + if scheme.startswith("GEAR M1closure"): + # atomic mass + sA = copy.deepcopy(data.gas.ion_mass_fractions) + mass_fraction_hydrogen = ( + data.gas.ion_mass_fractions.HI + data.gas.ion_mass_fractions.HII + ) + # abundance is in n_X/n_H unit. We convert mass fraction to abundance by dividing mass fraction of H + sA.HI = data.gas.ion_mass_fractions.HI / mass_fraction_hydrogen / mamu["HI"] + sA.HII = data.gas.ion_mass_fractions.HII / mass_fraction_hydrogen / mamu["HII"] + sA.HeI = data.gas.ion_mass_fractions.HeI / mass_fraction_hydrogen / mamu["HeI"] + sA.HeII = ( + data.gas.ion_mass_fractions.HeII / mass_fraction_hydrogen / mamu["HeII"] + ) + sA.HeIII = ( + data.gas.ion_mass_fractions.HeIII / mass_fraction_hydrogen / mamu["HeIII"] + ) + elif scheme.startswith("SPH M1closure"): + sA = data.gas.rt_species_abundances + else: + raise ValueError("Unknown scheme", scheme) + return sA + + +def trim_paramstr(paramstr): + """ + clean up strings in the form [x,y,z,...] + and return an array: array([x,y,z,...]) + """ + paramstr = paramstr.strip() + if paramstr.startswith("["): + paramstr = paramstr[1:] + if paramstr.endswith("]"): + paramstr = paramstr[:-1] + + params = paramstr.split(",") + paramtrimmed = [] + for er in params: + paramtrimmed.append(float(er)) + return paramtrimmed diff --git a/examples/RadiativeTransferTests/UniformBox_3D/README b/examples/RadiativeTransferTests/UniformBox_3D/README index 6305fd99428c497843e590d218b6b6326632999b..dd27a9062be5d36c8a615a9d29b2ba44e2b07ff7 100644 --- a/examples/RadiativeTransferTests/UniformBox_3D/README +++ b/examples/RadiativeTransferTests/UniformBox_3D/README @@ -29,4 +29,3 @@ Tl;dr: GEAR scheme with debugging checks enabled - `./rt_sanity_checks-GEAR.py` is made to work on any run with the GEAR scheme and debugging checks enabled. - diff --git a/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py b/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py index bcfd67f510f2a89cf15940f8b68cac5fc8aad7b5..b7193b583a3400e85f146ff4c1171d06b3a90230 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py @@ -25,11 +25,10 @@ # there are some hydro particles that have no star neighbours. # --------------------------------------------------------------------- -from swiftsimio import Writer - -import unyt -import numpy as np import h5py +import numpy as np +import unyt +from swiftsimio import Writer # Box is 1 Mpc boxsize = 100 * unyt.m @@ -53,32 +52,32 @@ for i in range(n_p): y = (j + 0.5) * dx for k in range(n_p): z = (k + 0.5) * dx - xp.append(np.array([x, y, z], dtype=np.float)) + xp.append(np.array([x, y, z], dtype=np.float64)) # Generate star coordinates for i in range(n_s): - # factor 0.52 below: shift particles a bit so they don't overlap with hydro + # factor 0.52 below: shift particles a bit, so they don't overlap with hydro x = 0.4 * boxsize + (i + 0.52) * ds for j in range(n_s): y = 0.4 * boxsize + (j + 0.52) * ds for k in range(n_s): z = 0.4 * boxsize + (k + 0.52) * ds - xs.append(np.array([x, y, z], dtype=np.float)) + xs.append(np.array([x, y, z], dtype=np.float64)) xp = unyt.unyt_array(xp, boxsize.units) xs = unyt.unyt_array(xs, boxsize.units) -w = Writer(unyt.unit_systems.cgs_unit_system, boxsize) +w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, compress=False) w.gas.coordinates = xp w.stars.coordinates = xs w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s) w.stars.velocities = np.zeros(xs.shape) * (unyt.cm / unyt.s) -w.gas.masses = np.ones(xp.shape[0], dtype=np.float) * 1000 * unyt.g -w.stars.masses = np.ones(xs.shape[0], dtype=np.float) * 1000 * unyt.g +w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 1000 * unyt.g +w.stars.masses = np.ones(xs.shape[0], dtype=np.float64) * 1000 * unyt.g w.gas.internal_energy = ( - np.ones(xp.shape[0], dtype=np.float) * (300.0 * unyt.kb * unyt.K) / (unyt.g) + np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g ) # Generate initial guess for smoothing lengths based on MIPS @@ -110,12 +109,11 @@ parts = F["/PartType0"] for grp in range(nPhotonGroups): dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1) - energydata = np.ones((nparts), dtype=np.float) * (grp + 1) + energydata = np.ones(nparts, dtype=np.float64) * (grp + 1) parts.create_dataset(dsetname, data=energydata) dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1) - # if dsetname not in parts.keys(): - fluxdata = np.ones((nparts, 3), dtype=np.float) * (grp + 1) + fluxdata = np.ones((nparts, 3), dtype=np.float64) * (grp + 1) fluxdata[:, 1] *= 2.0 fluxdata[:, 2] *= 3.0 parts.create_dataset(dsetname, data=fluxdata) diff --git a/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py index aec820ea3a62b8abd314f95d3b7f7ebc6d493581..746c1e10ff297e7986382f1e1c5bcbf97a053326 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py @@ -1,4 +1,22 @@ #!/usr/bin/env python3 +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) +# +# 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/>. +# +############################################################################## # ---------------------------------------------------- # plots 2D projection of radiation energy and fluxes @@ -8,16 +26,17 @@ # all snapshots available in the workdir. # ---------------------------------------------------- -import sys +import gc import os -import swiftsimio +import sys + +import matplotlib as mpl import numpy as np -import gc +import swiftsimio import unyt from matplotlib import pyplot as plt -import matplotlib as mpl -from mpl_toolkits.axes_grid1 import make_axes_locatable from matplotlib.colors import SymLogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable # Parameters users should/may tweak @@ -262,7 +281,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): ax.set_ylabel("Energies [$" + energy_units_str + "$]") # Add title - title = filename.replace("_", "\_") # exception handle underscore for latex + title = filename.replace("_", r"\_") # exception handle underscore for latex if meta.cosmology is not None: title += ", $z$ = {0:.2e}".format(meta.z) title += ", $t$ = {0:.2e}".format(meta.time.to(time_units)) @@ -278,7 +297,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None): def get_minmax_vals(snaplist): """ - Find minimal and maximal values for energy and flux + Find minimal and maximal values for energy and flux, so you can fix axes limits over all snapshots snaplist: list of snapshot filenames @@ -320,7 +339,6 @@ def get_minmax_vals(snaplist): dirmin = [] dirmax = [] for direction in ["X", "Y", "Z"]: - new_attribute_str = "radiation_flux" + str(g + 1) + direction f = getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + direction) dirmin.append(f.min()) dirmax.append(f.max()) diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py index 62392fe87b478bcda916ff8523e903a5ac300cbe..3992573f8adbf0fb7aff7e790483d6bf90937e11 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py @@ -55,8 +55,8 @@ skip_plots = False # skip showing plots for diagnosis float_comparison_tolerance = 1e-4 # tolerance for a float that was summed up over all particles to vary float_particle_sum_comparison_tolerance = 5e-4 -# tolerance for meshless energy distribution scheme during injeciton comparison -float_psi_comparison_tolerance = 5e-4 +# tolerance for energy conservation and injection during injeciton comparison +energy_conservation_tolerance = 1e-3 # ------------------------------------------------------------------------------- @@ -67,39 +67,50 @@ else: file_prefix = "output" -def check_injection(snapdata, rundata): +def check_essentials(snapdata, rundata): """ - Do checks related to energy injections. + Check whether we have infs or nans. + Also check whether the maximal fluxes F = E * c are respected. + F: photon fluxes [erg/cm**2/s] + E: photon energy density [erg/cm**3] + c: reduced lightspeed that has been used + This might also reveal some unit issues. snapdata: list of swift_rt_GEAR_io.RTSnapData objects - rundata: swift_rt_GEAR_io.Rundata object """ - print("checking injection") + print("checking essentials") + + cred = rundata.reduced_speed_of_light + ngroups = rundata.ngroups # ---------------------------------------------------------------- - # Check 0: Make sure we don't have NaNs or Infs anywhere + # Check 1: Make sure we don't have NaNs or Infs anywhere # ---------------------------------------------------------------- for snap in snapdata: - photon_energies = snap.gas.PhotonEnergies - ok = np.isfinite(photon_energies) - if not ok.all(): - print("In snapshot", snap.snapnr, ":") - print("Found NaNs/infs in photon energies:", np.count_nonzero(ok == 0)) - if break_on_diff: - quit() - - photon_fluxes = snap.gas.PhotonFluxes - ok = np.isfinite(photon_fluxes) - if not ok.any(): - print("In snapshot", snap.snapnr, ":") - print("Found NaNs/infs in photon fluxes:", np.count_nonzero(ok == 0)) - if break_on_diff: - quit() + for g in range(ngroups): + photon_energies = snap.gas.PhotonEnergies[g] + ok = np.isfinite(photon_energies) + if not ok.all(): + print("In snapshot", snap.snapnr, ", group", g + 1, ":") + print( + " Found NaNs/infs in photon energies:", np.count_nonzero(ok == 0) + ) + if break_on_diff: + quit() - if snap.has_stars: + photon_fluxes = snap.gas.PhotonFluxes[g] + ok = np.isfinite(photon_fluxes) + if not ok.any(): + print("In snapshot", snap.snapnr, ", group", g + 1, ":") + print(" Found NaNs/infs in photon fluxes:", np.count_nonzero(ok == 0)) + print(ok) + if break_on_diff: + quit() + + if snap.has_star_debug_data: injected_energies = snap.stars.InjectedPhotonEnergy ok = np.isfinite(injected_energies) if not ok.all(): @@ -111,36 +122,91 @@ def check_injection(snapdata, rundata): if break_on_diff: quit() + # ---------------------------------------------------------------- + # Check 2: Make sure F <= c E + # ---------------------------------------------------------------- + + for snap in snapdata: + + volumes = snap.gas.volumes + mask = volumes > 0.0 + + for g in range(ngroups): + photon_energies = snap.gas.PhotonEnergies[g] + photon_fluxes = snap.gas.PhotonFluxes[g] + photon_energy_densities = photon_energies[mask] / volumes[mask] + max_fluxes = ( + cred * photon_energy_densities * (1.0 + float_comparison_tolerance) + ) + groupfluxes = photon_fluxes[mask] + groupfluxnorm = np.sqrt( + groupfluxes[:, 0] ** 2 + groupfluxes[:, 1] ** 2 + groupfluxes[:, 2] ** 2 + ) + flux_units = groupfluxes.units + + fishy = groupfluxnorm.to(flux_units).v > max_fluxes.to(flux_units).v + + if fishy.any(): + print("In snapshot", snap.snapnr, ", group", g + 1, ":") + print(" Found F > cE, count:", np.count_nonzero(fishy)) + if print_additional_information: + print("--- flux norms", groupfluxnorm[fishy]) + print("--- max fluxes", max_fluxes[fishy]) + print("--- ratio ", groupfluxnorm[fishy] / max_fluxes[fishy]) + print("--- max:", groupfluxnorm.max()) + + if break_on_diff: + quit() + + return + + +def check_injection(snapdata, rundata): + """ + Do checks related to energy injections. + + snapdata: list of swift_rt_GEAR_io.RTSnapData objects + rundata: swift_rt_GEAR_io.Rundata object + """ + + print("checking injection") # ---------------------------------------------------------------- # Check 1: Make sure the right amount of energy has been injected # into the gas # ---------------------------------------------------------------- if not rundata.has_stars: - print("Found no stars in run. Skipping injection tests") + print("Found no stars in run. Skipping injection tests.") + return + + if not rundata.has_star_debug_data: + print( + "Found no debug data in run.", + "Can't do injection tests without it.", + "Compile swift with debugging checks on.", + ) return emission_rates = rundata.const_emission_rates ngroups = rundata.ngroups - initial_energies = np.sum(snapdata[0].gas.PhotonEnergies, axis=0) - initial_time = snapdata[0].time + initial_energies = [ + np.sum(snapdata[0].gas.PhotonEnergies[g]) for g in range(ngroups) + ] # Check 1a) : sum initial energy + sum injected energy = sum current energy # -------------------------------------------------------------------------- # TODO: this assumes no cosmological expansion for snap in snapdata[1:]: - dt = snap.time - initial_time # sum of each group over all particles - photon_energies = np.sum(snap.gas.PhotonEnergies, axis=0) + photon_energies = [np.sum(snap.gas.PhotonEnergies[g]) for g in range(ngroups)] if snap.has_stars: - # in case we only have 1 star, the sum returns a scalar, so add atleast_1d - injected_energies = np.atleast_1d( - np.sum(snap.stars.InjectedPhotonEnergy, axis=0) - ) + injected_energies = [ + np.sum(snap.stars.InjectedPhotonEnergy[:, g]) for g in range(ngroups) + ] else: - injected_energies = np.zeros(ngroups, dtype=np.float) + injected_energies = [0.0] * ngroups for g in range(ngroups): energy_expected = initial_energies[g] + injected_energies[g] @@ -172,100 +238,114 @@ def check_injection(snapdata, rundata): # Remember: The reason we have too little injected energy is because we # don't inject any energy during the zeroth time step. We can't, since the # zeroth time step is the one that determines the time step size of the star. + # Also, the star-feedback loop is skipped. # TODO: this assumes a constant number of stars. You need to deal with SF # TODO: this assumes no cosmological expansion + # upper boundaries (analytically expected values) for the plots upper_boundary_for_plot = [] + # snapshot numbers, used as x-axis in plots snaps_for_1bplot = [] initial_time = snapdata[0].time + if snapdata[0].has_stars: emission_at_initial_time = snapdata[0].stars.InjectedPhotonEnergy.sum(axis=0) else: - emission_at_initial_time = np.zeros(rundata.ngroups, dtype=np.float) * unyt.erg + emission_at_initial_time = ( + np.zeros(rundata.ngroups, dtype=np.float64) * unyt.erg + ) - if rundata.use_const_emission_rate and not rundata.hydro_controlled_injection: + continue_test = True + + if not rundata.use_const_emission_rate: + print("Can't run check 1b without constant emission rates") + continue_test = False + + if continue_test: if len(snapdata) <= 2: # because it's useless to check only snap_0000 print("Check 1b: You need at least 2 snapshots to do this particular test") - else: - diffs_for_plot = [] - energies_for_plot = [] - found_potential_error = False - for snap in snapdata[1:]: # skip snapshot zero - dt = snap.time - initial_time - if snap.has_stars: - injected_energies = np.atleast_1d( - snap.stars.InjectedPhotonEnergy.sum(axis=0) - - emission_at_initial_time + continue_test = False + + if continue_test: + + diffs_for_plot = [] # relative difference between expectation and data + energies_for_plot = [] # the injected energies that were found in data + found_potential_error = False + + for snap in snapdata[1:]: # skip snapshot zero + dt = snap.time - initial_time + if snap.has_stars: + injected_energies = np.atleast_1d( + snap.stars.InjectedPhotonEnergy.sum(axis=0) + - emission_at_initial_time + ) + else: + injected_energies = np.zeros(ngroups) * unyt.erg + # get what energies we expect the stars to have injected + energies_expected = snap.nstars * emission_rates * dt + energies_expected = energies_expected.to(injected_energies.units) + # get relative difference + diff = np.array(injected_energies / energies_expected - 1.0) + + # store data + upper_boundary_for_plot.append(energies_expected) + energies_for_plot.append(injected_energies) + diffs_for_plot.append(diff) + snaps_for_1bplot.append(snap.snapnr) + + # diff should be < 0. Allow for some tolerance here + if (diff > energy_conservation_tolerance).any(): + print( + "Injection Energy Prediction upper boundary is wrong; " + + "snapshot {0:d} tolerance {1:.2e}".format( + snap.snapnr, energy_conservation_tolerance ) - else: - injected_energies = np.zeros(ngroups) * unyt.erg - energies_expected = snap.nstars * emission_rates * dt - energies_expected = energies_expected.to(injected_energies.units) - diff = np.array(injected_energies / energies_expected - 1.0) - - upper_boundary_for_plot.append(energies_expected) - energies_for_plot.append(injected_energies) - diffs_for_plot.append(diff) - snaps_for_1bplot.append(snap.snapnr) - - # diff should be < 0. Allow for some tolerance here - if (diff > float_psi_comparison_tolerance).any(): + ) + for g in range(ngroups): + # if energies_expected[g] > injected_energies[g]: + print("--- group", g + 1) + print("----- injected:", injected_energies[g]) + print("----- expected:", energies_expected[g], "should be smaller") print( - "Injection Energy Prediction upper boundary is wrong; " - + "snapshot {0:d} tolerance {1:.2e}".format( - snap.snapnr, float_psi_comparison_tolerance - ) + "----- ratio :", (injected_energies[g] / energies_expected[g]) ) - for g in range(ngroups): - # if energies_expected[g] > injected_energies[g]: - print("--- group", g + 1) - print("----- injected:", injected_energies[g]) - print( - "----- expected:", energies_expected[g], "should be smaller" - ) - print( - "----- ratio :", - (injected_energies[g] / energies_expected[g]), - ) - print("----- diff :", diff[g], "should be < 0") - found_potential_error = True + print("----- diff :", diff[g], "should be < 0") + found_potential_error = True - if break_on_diff: - quit() + if break_on_diff: + quit() - if not skip_plots and found_potential_error: - # Make this plot if there are possible errors - diffs_for_plot = np.array(diffs_for_plot) - plt.figure() - for g in range(ngroups): - plt.plot( - snaps_for_1bplot, - diffs_for_plot[:, g], - label="group {0:d}".format(g + 1), - ) - plt.plot( - [snaps_for_1bplot[0], snaps_for_1bplot[-1]], - [0, 0], - "k", - label="upper boundary", - ) - plt.legend() - plt.xlabel("snapshot") - plt.ylabel("injected energy / expected energy - 1") - plt.title( - "Difference from expected injected energy - something's fishy" + if not skip_plots and found_potential_error: + # Make this plot if there are possible errors + diffs_for_plot = np.array(diffs_for_plot) + plt.figure() + for g in range(ngroups): + plt.plot( + snaps_for_1bplot, + diffs_for_plot[:, g], + label="group {0:d}".format(g + 1), ) - plt.show() - plt.close() + plt.plot( + [snaps_for_1bplot[0], snaps_for_1bplot[-1]], + [0, 0], + "k", + label="upper boundary", + ) + plt.legend() + plt.xlabel("snapshot") + plt.ylabel("injected energy / expected energy - 1") + plt.title("Difference from expected injected energy - something's fishy") + plt.show() + plt.close() # -------------------------------- # Create additional plots? # -------------------------------- - if rundata.use_const_emission_rate and not rundata.hydro_controlled_injection: + if rundata.use_const_emission_rate and continue_test: if not skip_plots and len(energies_for_plot) > 2: # Show me the plot that the injected energy # is correctly bounded @@ -307,6 +387,7 @@ def main(): """ snapdata, rundata = get_snap_data(prefix=file_prefix) + check_essentials(snapdata, rundata) check_injection(snapdata, rundata) return diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py index 6214eba725a34adc1eefe1956b463c1db16fab73..e5379b38ef2e67fd00daf46eb477d86b1dd8386c 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py @@ -49,8 +49,6 @@ skip_last_snap = False # skip snap_0max.hdf5 print_diffs = True # print differences you find break_on_diff = False # quit when you find a difference -hydro_controlled_injection = False - if len(argv) > 1: file_prefix = argv[1] @@ -58,19 +56,20 @@ else: file_prefix = "output" -def check_hydro_sanity(snapdata): +def check_hydro_sanity(snapdata, rundata): """ Sanity checks for hydro variables. - injection always done? - gradients always done? - thermochemistry always done? - RT transport calls >= RT gradient calls? + - number of subcycles != 0? """ - nsnaps = len(snapdata) npart = snapdata[0].gas.coords.shape[0] print("Checking hydro") + warning_printed = False # ---------------------------------------------- # check absolute values of every snapshot @@ -78,7 +77,7 @@ def check_hydro_sanity(snapdata): for snap in snapdata: gas = snap.gas - stars = snap.stars + # stars = snap.stars # has a particle been called at least once? called = ( @@ -189,8 +188,17 @@ def check_hydro_sanity(snapdata): # at least the number of calls to transport interactions # in RT interactions # -------------------------------------------------------------- - fishy = gas.RTCallsIactTransportInteraction < gas.RTCallsIactGradientInteraction - if fishy.any(): + if rundata.with_mpi: + check = False + if not warning_printed: + print("- MPI run: skipping hydro sanity interaction call count checks") + warning_printed = True + else: + fishy = ( + gas.RTCallsIactTransportInteraction < gas.RTCallsIactGradientInteraction + ) + check = fishy.any() + if check: print("- checking hydro sanity pt2.5; snapshot", snap.snapnr) print( "--- Found RT transport calls iact < gradient calls iact:", @@ -210,10 +218,25 @@ def check_hydro_sanity(snapdata): if break_on_diff: quit() + # ------------------------------------------------------------- + # Check that the subcycle counter isn't zero. + # We expect a particle to be radioactive at least each time it + # is hydro active, so the subcycle counter must never be zero. + # ------------------------------------------------------------- + + fishy = gas.nsubcycles <= 0 + if fishy.any(): + print("- checking hydro sanity pt 2.6; snapshot", snap.snapnr) + print("Found nsubcycles <= 0:", np.count_nonzero(fishy), "/", npart) + if print_diffs: + print("nsubcycles:", gas.nsubcycles[fishy]) + if break_on_diff: + quit() + return -def check_stars_sanity(snapdata): +def check_stars_sanity(snapdata, rundata): """ Sanity checks for stars variables. - total calls keep increasing? @@ -221,8 +244,6 @@ def check_stars_sanity(snapdata): print("Checking stars") - nsnaps = len(snapdata) - # ---------------------------------------------- # check consistency of individual snapshots # ---------------------------------------------- @@ -233,26 +254,30 @@ def check_stars_sanity(snapdata): this = snap.stars nspart = snapdata[0].stars.coords.shape[0] - if hydro_controlled_injection: - if (this.EmissionRateSet != 1).any(): - print("- checking stars sanity pt2", snap.snapnr) - print("--- Emisison Rates not consistent") - count = 0 - for i in range(nspart): - if this.EmissionRateSet[i] != 1: - count += 1 - if print_diffs: - print("-----", this.EmissionRateSet[i], "ID", this.IDs[i]) - - print("--- count", count, "/", this.EmissionRateSet.shape[0]) + fishy = np.logical_and(this.EmissionRateSet == 0, this.RadiationEmittedTot > 0) + if fishy.any(): + count = 0 + print("- checking stars sanity pt1", snap.snapnr) + print("--- Emitted with unset emission rate") + for i in range(nspart): + if this.EmissionRateSet[i] != 1: + count += 1 + if print_diffs: + print("----- IDs", this.IDs[fishy]) + print("----- EmissionRateSet", this.EmissionRateSet[fishy]) + print( + "----- RadiationEmittedTot", this.RadiationEmittedTot[fishy] + ) + + print("--- count", count, "/", this.EmissionRateSet.shape[0]) - if break_on_diff: - quit() + if break_on_diff: + quit() return -def check_stars_hydro_interaction_sanity(snapdata): +def check_stars_hydro_interaction_sanity(snapdata, rundata): """ Sanity checks for hydro vs star interaction call counts. @@ -260,9 +285,6 @@ def check_stars_hydro_interaction_sanity(snapdata): - are total calls each step equal? """ - nsnaps = len(snapdata) - npart = snapdata[0].gas.coords.shape[0] - print("Checking hydro vs stars") # ---------------------------------------------- @@ -280,43 +302,66 @@ def check_stars_hydro_interaction_sanity(snapdata): if snap.has_stars: sum_star_tot_radiation = stars.RadiationEmittedTot.sum() else: + print("Found no stars") sum_star_tot_radiation = 0.0 - if sum_gas_tot_radiation != sum_star_tot_radiation: - print("- checking hydro v star sanity pt1; snapshot", snap.snapnr) - print( - "--- Total emitted and absorbed radiation not equal: Gas", - sum_gas_tot_radiation, - "stars", - sum_star_tot_radiation, - "diff", - sum_star_tot_radiation - sum_gas_tot_radiation, - ) - if break_on_diff: - quit() + if rundata.with_mpi: + # with MPI, stars may have missing integer counts in total + # since stars that have been sent over won't get the updated + # interaction count sent back to their origin. So we allow + # stars to have fewer interaction counts over MPI. + if sum_gas_tot_radiation < sum_star_tot_radiation: + print("- checking hydro v star sanity pt1; snapshot", snap.snapnr) + print( + "--- More star iacts than gas iacts: Gas", + sum_gas_tot_radiation, + "stars", + sum_star_tot_radiation, + "diff", + sum_star_tot_radiation - sum_gas_tot_radiation, + ) + if break_on_diff: + quit() + + else: + if sum_gas_tot_radiation != sum_star_tot_radiation: + print("- checking hydro v star sanity pt1; snapshot", snap.snapnr) + print( + "--- Total emitted and absorbed radiation not equal: Gas", + sum_gas_tot_radiation, + "stars", + sum_star_tot_radiation, + "diff", + sum_star_tot_radiation - sum_gas_tot_radiation, + ) + if break_on_diff: + quit() # -------------------------------------------------------------- # check that we have the correct amount of interactions recorded # for injection prep between stars and gas - # -------------------------------------------------------------- - sum_gas_tot_prep = gas.InjectPrepCountsTot.sum() - if snap.has_stars: - sum_star_tot_prep = stars.InjectPrepCountsTot.sum() - else: - sum_star_tot_prep = 0.0 - - if sum_gas_tot_prep != sum_star_tot_prep: - print("- checking hydro v star sanity pt2; snapshot", snap.snapnr) - print( - "--- Total interactions between gas and stars in prep is wrong:", - sum_gas_tot_prep, - "stars", - sum_star_tot_prep, - "diff", - sum_star_tot_prep - sum_gas_tot_prep, - ) - if break_on_diff: - quit() + # !! Can't do this check any longer since we moved the injection + # !! prep to the star density loop. The repeats and resets in + # !! the ghost mess the counts between parts and sparts up. + # --------------------------------------------------------------- + # sum_gas_tot_prep = gas.InjectPrepCountsTot.sum() + # if snap.has_stars: + # sum_star_tot_prep = stars.InjectPrepCountsTot.sum() + # else: + # sum_star_tot_prep = 0.0 + # + # if sum_gas_tot_prep != sum_star_tot_prep: + # print("- checking hydro v star sanity pt2; snapshot", snap.snapnr) + # print( + # "--- Total interactions between gas and stars in prep is wrong:", + # sum_gas_tot_prep, + # "stars", + # sum_star_tot_prep, + # "diff", + # sum_star_tot_prep - sum_gas_tot_prep, + # ) + # if break_on_diff: + # quit() return @@ -329,12 +374,10 @@ def main(): snapdata, rundata = get_snap_data( prefix=file_prefix, skip_snap_zero=skip_snap_zero, skip_last_snap=skip_last_snap ) - global hydro_controlled_injection - hydro_controlled_injection = rundata.hydro_controlled_injection - check_hydro_sanity(snapdata) - check_stars_sanity(snapdata) - check_stars_hydro_interaction_sanity(snapdata) + check_hydro_sanity(snapdata, rundata) + check_stars_sanity(snapdata, rundata) + check_stars_hydro_interaction_sanity(snapdata, rundata) return diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py index d521a77d79e72ef531055a7914c2cdab89fcef36..22b8987afd1e373bf469cbd7ac9ed6546047dff8 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py @@ -40,11 +40,9 @@ # ----------------------------------------------------------------------- -import numpy as np -import unyt from sys import argv -from swift_rt_GEAR_io import get_snap_data +from swift_rt_GEAR_io import get_snap_data # some behaviour options print_diffs = True # print differences you find @@ -82,31 +80,34 @@ def check_injection(snapdata, rundata): # into the gas # ---------------------------------------------------------------- - initial_energies = snapdata[0].gas.PhotonEnergies.sum(axis=0) - initial_time = snapdata[0].time - emission_rates = rundata.const_emission_rates ngroups = rundata.ngroups + initial_energies = [ + snapdata[0].gas.PhotonEnergies[g].sum(axis=0) for g in range(ngroups) + ] + initial_time = snapdata[0].time + for snap in snapdata: dt = snap.time - initial_time - photon_energies = snap.gas.PhotonEnergies.sum(axis=0) injected = snap.nstars * emission_rates * dt - energies_expected = initial_energies + injected - diff = np.array(1.0 - energies_expected / photon_energies) - - if (np.abs(diff) > float_particle_sum_comparison_tolerance).any(): - print("Snapshot", snap.snapnr, "Injected Energy Prediction is wrong;") - if print_diffs: - for g in range(ngroups): - if abs(diff[g]) > float_particle_sum_comparison_tolerance: - print("--- group ", g + 1) - print("----- diff: ", diff[g]) - print("----- photon energies:", photon_energies[g]) - print("----- expected: ", energies_expected[g]) + for g in range(ngroups): + photon_energy = snap.gas.PhotonEnergies[g].sum(axis=0) + energy_expected = initial_energies[g] + injected[g] + diff = 1.0 - energy_expected / photon_energy + + if abs(diff) > float_particle_sum_comparison_tolerance: + print("Snapshot", snap.snapnr, "Injected Energy Prediction is wrong;") + if print_diffs: + print("--- group ", g + 1) + print("----- diff: ", diff) + print("----- photon energies:", photon_energy) + print("----- expected: ", energy_expected) if break_on_diff: quit() + return + def main(): """ diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py index e47fae3ef394942fcadfb882db72dbb5fc039429..e543243af94134ad6a6fa37064c00fce4b170262 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py @@ -39,10 +39,11 @@ # ----------------------------------------------------------------------- -import numpy as np from sys import argv -from swift_rt_debug_io import get_snap_data +import numpy as np + +from swift_rt_debug_io import get_snap_data # some behaviour options skip_snap_zero = True # skip snap_0000.hdf5 @@ -222,6 +223,25 @@ def check_all_hydro_is_equal(snapdata): if (compare.gas.ThermochemistryDone[nzs] == 0).any(): print("Oh no 3") + # --------------------------------------------------------------- + # Check numbers of subcycles. + # --------------------------------------------------------------- + fishy = ref.gas.nsubcycles != compare.gas.nsubcycles + if fishy.any(): + print("- Comparing hydro", ref.snapnr, "->", compare.snapnr) + print( + "--- Subcycle Calls count differ: {0:8d} / {1:8d}; ".format( + np.count_nonzero(fishy), npart + ) + ) + if not skip_last_snap: + print( + "Note, this might be acceptable behaviour for the final snapshot. You currently aren't skipping it in this check." + ) + + if break_on_diff: + quit() + return @@ -262,18 +282,59 @@ def check_all_stars_is_equal(snapdata): # Smoothing Lengths if not skip_sml: - diff = np.abs((ref.gas.h - compare.gas.h) / ref.gas.h) + diff = np.abs((ref.stars.h - compare.stars.h) / ref.stars.h) if (diff > float_comparison_tolerance).any(): print("- Comparing stars", ref.snapnr, "->", compare.snapnr) print("--- Smoothing Lengths vary") if print_diffs: for i in range(npart): - if ((ref.gas.h[i] - compare.gas.h[i]) / ref.gas.h[i]).any(): - print(ref.gas.h[i], "|", compare.gas.h[i]) + if ( + (ref.stars.h[i] - compare.stars.h[i]) / ref.stars.h[i] + ).any(): + print(ref.stars.h[i], "|", compare.stars.h[i]) if break_on_diff: quit() + # Check all emission rates are set everywhere + fishy = ref.stars.EmissionRateSet != compare.stars.EmissionRateSet + if fishy.any(): + + print("- Comparing stars", ref.snapnr, "->", compare.snapnr) + print("--- EmissionRateSet vary") + if print_diffs: + for i in range(npart): + if ref.stars.EmissionRateSet[i] != compare.stars.EmissionRateSet[i]: + print( + ref.stars.EmissionRateSet[i], + "|", + compare.stars.EmissionRateSet[i], + ) + + if break_on_diff: + quit() + + # Check all emitted radiation is equal + fishy = ref.stars.InjectionInteractions != compare.stars.InjectionInteractions + if fishy.any(): + + print("- Comparing stars", ref.snapnr, "->", compare.snapnr) + print("--- InjectionInteractions vary") + if print_diffs: + for i in range(npart): + if ( + ref.stars.InjectionInteractions[i] + != compare.stars.InjectionInteractions[i] + ): + print( + ref.stars.InjectionInteractions[i], + "|", + compare.stars.InjectionInteractions[i], + ) + + if break_on_diff: + quit() + return diff --git a/examples/RadiativeTransferTests/UniformBox_3D/run.sh b/examples/RadiativeTransferTests/UniformBox_3D/run.sh index 21762615467eb9457e7361ed2ef736177dd0c1b7..214533a4f6366f76ad19ec57c1c1effb80054c8b 100755 --- a/examples/RadiativeTransferTests/UniformBox_3D/run.sh +++ b/examples/RadiativeTransferTests/UniformBox_3D/run.sh @@ -9,11 +9,37 @@ if [ ! -f 'uniformBox-rt.hdf5' ]; then python3 makeIC.py fi +# use cmdline args as shortcut to run with debugger/MPI +# -g run with gdb +# -m run with MPI +# -mg run with MPI and gdb in individual xterm windows +# -ml run with MPI and write individual output file per MPI rank +cmd=../../../swift +if [ $# -gt 0 ]; then + case "$1" in + -g | g | gdb) + cmd='gdb --args ../../../swift' + ;; + -m | m | mpi) + cmd='mpirun -n 2 ../../../swift_mpi' + ;; + -mg | -gm | gm | mg | gmpi | gdbmpi ) + cmd='mpirun -n 2 xterm -e gdb -ex run --args ../../../swift_mpi' + ;; + -ml | ml | lm | mpilog | logmpi) + cmd='mpirun -n 2 --output-filename individual_rank_output --merge-stderr-to-stdout ../../../swift_mpi' + ;; + *) + echo unknown cmdline param, running without gdb + ;; + esac +fi + # Run SWIFT with RT -../../swift \ +$cmd \ --hydro --threads=4 --stars --external-gravity \ - --feedback --radiation \ - uniform_rt_timestep_output_sync.yml 2>&1 | tee output.log + --feedback --radiation --verbose=0 \ + uniform_rt_timestep_output_sync.yml 2>&1 | tee output.log echo "running sanity checks" python3 ./rt_sanity_checks.py | tee sanity_check.log diff --git a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py index f1e6d88836c07756e8e5fed489c5c5b8affcba9a..6fe1efdd00fec032d93a892d81d98619a26cfa58 100644 --- a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py @@ -26,10 +26,10 @@ import os -import h5py + import numpy as np -import unyt import swiftsimio +import unyt class RTGasData(object): @@ -38,10 +38,10 @@ class RTGasData(object): """ def __init__(self): - self.IDs = None self.coords = None self.h = None + self.volumes = None self.PhotonEnergies = None self.PhotonFluxes = None @@ -55,7 +55,6 @@ class RTStarData(object): """ def __init__(self): - self.IDs = None self.coords = None self.h = None @@ -75,7 +74,8 @@ class RTSnapData(object): self.ncells = None self.boxsize = None self.time = None - self.has_stars = True + self.has_stars = False + self.has_star_debug_data = False self.nstars = None self.npart = None @@ -93,12 +93,17 @@ class Rundata(object): def __init__(self): self.units = None - self.hydro_controlled_injection = False self.use_const_emission_rate = False self.has_stars = False # assume we don't have stars, check while reading in + self.has_star_debug_data = ( + False + ) # assume we don't have stars, check while reading in self.ngroups = 0 # photon frequency groups self.const_emission_rates = None + self.reduced_speed_of_light = -1.0 + + self.with_mpi = False return @@ -156,29 +161,28 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): try: scheme = str(firstfile.metadata.subgrid_scheme["RT Scheme"]) except KeyError: - print( + raise ValueError( "These tests only work for the GEAR RT scheme.", "Compile swift --with-rt=GEAR_N", ) - quit() if "GEAR" not in scheme: raise ValueError( "These tests only work for the GEAR RT scheme.", "Compile swift --with-rt=GEAR_N", ) - quit() ngroups = int(firstfile.metadata.subgrid_scheme["PhotonGroupNumber"]) rundata.ngroups = ngroups - rundata.use_const_emission_rate = bool( - firstfile.metadata.parameters["GEARRT:use_const_emission_rates"] - ) + + luminosity_model = firstfile.metadata.parameters["GEARRT:stellar_luminosity_model"] + rundata.use_const_emission_rate = luminosity_model.decode("utf-8") == "const" + rundata.units = firstfile.units if rundata.use_const_emission_rate: # read emission rate parameter as string emissionstr = firstfile.metadata.parameters[ - "GEARRT:star_emission_rates_LSol" + "GEARRT:const_stellar_luminosities_LSol" ].decode("utf-8") # clean string up if emissionstr.startswith("["): @@ -195,17 +199,36 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): if len(rundata.const_emission_rates) != rundata.ngroups: print("Got number of emission rates different from number of groups?") - print(rundata.const_emission_rates, "vs", rundata.ngroups) + print( + "paramfile:", + len(rundata.const_emission_rates), + "vs", + rundata.ngroups, + "groups", + ) if len(rundata.const_emission_rates) > rundata.ngroups: - print("Only using first", rundata.ngroups, "emission rates") rundata.const_emission_rates = rundata.const_emission_rates[ : rundata.ngroups ] + print( + "Only using first", + rundata.ngroups, + "emission rates:", + rundata.const_emission_rates, + ) else: quit() + else: + print( + "Didn't detect use of constant stellar emission rates. Proceeding without." + ) - if "hydro controlled" in scheme: - rundata.hydro_controlled_injection = True + rundata.reduced_speed_of_light = firstfile.metadata.reduced_lightspeed + + with_mpi = False + if firstfile.metadata.code["MPI library"] != b"Non-MPI version of SWIFT": + with_mpi = True + rundata.with_mpi = with_mpi # ------------------- # Read in all files @@ -233,46 +256,66 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): Gas.IDs = data.gas.particle_ids Gas.coords = data.gas.coordinates Gas.h = data.gas.smoothing_lengths - Gas.PhotonEnergies = swiftsimio.cosmo_array( - [ - getattr(data.gas.photon_energies, "group" + str(g)) - for g in range(1, rundata.ngroups + 1) - ] - ).T - - Gas.PhotonFluxes = swiftsimio.cosmo_array( - [ - unyt.uvstack( - [ - getattr(data.gas.photon_fluxes, "Group" + str(g) + d) - for d in ("X", "Y", "Z") - ] - ) - for g in range(1, rundata.ngroups + 1) - ], - data.gas.photon_fluxes.Group1X.units, - ).T + masses = data.gas.masses + densities = data.gas.densities + # skip potential div by zero + mask = densities == 0.0 + masses[mask] = 0.0 + densities[mask] = 1.0 + Gas.volumes = masses / densities + + Gas.PhotonEnergies = [ + getattr(data.gas.photon_energies, "group" + str(g + 1)) + for g in range(rundata.ngroups) + ] + Gas.PhotonFluxes = [ + unyt.uvstack( + [ + getattr(data.gas.photon_fluxes, "Group" + str(g + 1) + d) + for d in ("X", "Y", "Z") + ] + ).T + for g in range(rundata.ngroups) + ] newsnap.gas = Gas newsnap.npart = Gas.IDs.shape[0] # Get star data + Stars = RTStarData() + nstars = 0 + has_stars = False + has_star_debug_data = False try: - Stars = RTStarData() Stars.IDs = data.stars.particle_ids Stars.coords = data.stars.coordinates Stars.h = data.stars.smoothing_lengths - Stars.InjectedPhotonEnergy = data.stars.rtdebug_injected_photon_energy - newsnap.stars = Stars - newsnap.nstars = Stars.IDs.shape[0] + nstars = Stars.IDs.shape[0] + has_stars = True except AttributeError: - Stars = RTStarData() - newsnap.has_stars = False - newsnap.nstars = 0 + pass + + if has_stars: + try: + inj = np.atleast_2d(data.stars.rtdebug_injected_photon_energy) + Stars.InjectedPhotonEnergy = np.reshape( + inj, (Stars.IDs.shape[0], ngroups) + ) + has_star_debug_data = True + except AttributeError: + pass + + newsnap.stars = Stars + newsnap.nstars = nstars + newsnap.has_stars = has_stars + newsnap.has_star_debug_data = has_star_debug_data snapdata.append(newsnap) for snap in snapdata: rundata.has_stars = rundata.has_stars or snap.has_stars + rundata.has_star_debug_data = ( + rundata.has_star_debug_data or snap.has_star_debug_data + ) return snapdata, rundata diff --git a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py index 6f3be1a53a083bfc6fe15234719e406cd977a55b..12e118e85c2cf28466077c2f8c19ea9adda0aaed 100644 --- a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py +++ b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py @@ -50,7 +50,8 @@ class RTGasData(object): self.GradientsDone = None self.RadiationAbsorbedTot = None - self.InjectPrepCountsTot = None + + self.nsubcycles = None return @@ -67,8 +68,8 @@ class RTStarData(object): self.h = None self.EmissionRateSet = None + self.InjectionInteractions = None self.RadiationEmittedTot = None - self.InjectPrepCountsTot = None return @@ -94,8 +95,8 @@ class Rundata(object): """ def __init__(self): - self.hydro_controlled_injection = False self.has_stars = False # assume we don't have stars, check while reading in + self.with_mpi = False return @@ -154,12 +155,10 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): try: scheme = str(F["SubgridScheme"].attrs["RT Scheme"]) except KeyError: - print( + raise ValueError( "These tests only work for the debug RT scheme.", "Compile swift --with-rt=debug", ) - F.close() - quit() if "debug" not in scheme and "GEAR" not in scheme: raise ValueError( @@ -167,8 +166,12 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): "Compile swift --with-rt=debug", ) - if "hydro controlled" in scheme: - rundata.hydro_controlled_injection = True + with_mpi = False + mpistr = F["Code"].attrs["MPI library"] + if mpistr != b"Non-MPI version of SWIFT": + with_mpi = True + rundata.with_mpi = with_mpi + F.close() for f in hdf5files: @@ -206,13 +209,13 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): newsnap.gas.ThermochemistryDone = Gas["RTDebugThermochemistryDone"][:][inds] newsnap.gas.RadiationAbsorbedTot = Gas["RTDebugRadAbsorbedTot"][:][inds] - newsnap.gas.InjectPrepCountsTot = Gas["RTDebugStarsInjectPrepTotCounts"][:][ - inds - ] + newsnap.gas.nsubcycles = Gas["RTDebugSubcycles"][:][inds] try: Stars = F["PartType4"] ids = Stars["ParticleIDs"][:] + has_stars = True + inds = np.argsort(ids) newsnap.stars.IDs = ids[inds] @@ -220,22 +223,21 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False): newsnap.stars.h = Stars["SmoothingLengths"][:][inds] newsnap.stars.EmissionRateSet = Stars["RTDebugEmissionRateSet"][:][inds] - + newsnap.stars.InjectionInteractions = Stars["RTDebugHydroIact"][:][inds] newsnap.stars.RadiationEmittedTot = Stars["RTDebugRadEmittedTot"][:][inds] - newsnap.stars.InjectPrepCountsTot = Stars[ - "RTDebugHydroInjectPrepCountsTot" - ][:][inds] + except KeyError: - newsnap.has_stars = False + has_stars = False + newsnap.has_stars = has_stars snapdata.append(newsnap) for snap in snapdata: rundata.has_stars = rundata.has_stars or snap.has_stars if len(snapdata) == 0: - print( - "Didn't read in snapshot data. Do you only have 2 snapshots in total and skipping the first and the last?" - ) + print("Didn't read in snapshot data.") + print("Do you only have 2 snapshots and are skipping the first and the last?") + quit() return snapdata, rundata diff --git a/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml b/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml index 324de5ca94f7ed5648d6b4669c3604c7c2a17ac9..c2ee0df751c9829e59fc60ff2f351e745a488991 100644 --- a/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml +++ b/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml @@ -1,5 +1,5 @@ MetaData: - run_name: "uniform_rt" + run_name: uniform_rt # Define the system of units to use internally. InternalUnitSystem: @@ -15,6 +15,7 @@ TimeIntegration: time_end: 9.536742e-07 dt_min: 1.e-12 # The minimal time-step size of the simulation (in internal units). dt_max: 5.e-8 # The maximal time-step size of the simulation (in internal units). + max_nr_rt_subcycles: 128 # Parameters governing the snapshots Snapshots: @@ -25,7 +26,7 @@ Snapshots: # Parameters governing the conserved quantities statistics Statistics: time_first: 0. - delta_time: 5e-3 # Time between statistics output + delta_time: 2.980232e-08 # Time between statistics output # Parameters for the hydrodynamics scheme SPH: @@ -39,20 +40,15 @@ InitialConditions: periodic: 1 # periodic ICs? Scheduler: - max_top_level_cells: 24 cell_split_size: 25 # Lower than default to test going deep(er) in the tree dependency_graph_frequency: 0 -# Debugging/Development RT scheme -DebugRT: - all_parts_have_stars: 0 # Set to 1 to do additional tests that only work if all hydro particles have a star particle neighbour - GEARRT: - f_reduce_c: 1e-3 # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.99 # CFL condition for RT, independent of hydro - photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter - star_emission_rates_LSol: [7.839e-28, 1.5678e-27, 2.3517e-27, 3.1356e-27] # 1e6,2e6,3e6,4e6 erg/s in LSol + f_reduce_c: 1. # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.99 # CFL condition for RT, independent of hydro + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [7.839e-28, 1.5678e-27, 2.3517e-27] # 1e6,2e6,3e6 erg/s in LSol hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. diff --git a/examples/SantaBarbara/SantaBarbara-128/run.sh b/examples/SantaBarbara/SantaBarbara-128/run.sh index 72c219acb201b3a3541b6a08d799b21ba4638009..b965ed13559218cfe96ec35285e9129da0816b16 100755 --- a/examples/SantaBarbara/SantaBarbara-128/run.sh +++ b/examples/SantaBarbara/SantaBarbara-128/run.sh @@ -1,4 +1,4 @@ #!/bin/bash # Run SWIFT -../../swift --cosmology --hydro --self-gravity --threads=28 santa_barbara.yml +../../../swift --cosmology --hydro --self-gravity --threads=28 santa_barbara.yml diff --git a/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml b/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml index 55b1ea9b1b408e2e7a95ed2af054bba7e4ca6193..cb1e1f2554437787bd6397fd2e75e44d81410a53 100644 --- a/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml +++ b/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml @@ -42,7 +42,7 @@ Statistics: # Parameters for the self-gravity scheme Gravity: eta: 0.025 - MAC: adpative + MAC: adaptive theta_cr: 0.7 epsilon_fmm: 0.001 use_tree_below_softening: 1 diff --git a/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py index 63e46ccaee7be9ea18090e13ae15bb0a1fae4bef..68b07ca6ec7390b3eab4eb90b5cd991949d03326 100644 --- a/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py +++ b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -30,32 +30,11 @@ snapname = "santabarbara" import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py -import os.path - -# 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.14, - "figure.subplot.right": 0.99, - "figure.subplot.bottom": 0.12, - "figure.subplot.top": 0.99, - "figure.subplot.wspace": 0.15, - "figure.subplot.hspace": 0.12, - "lines.markersize": 6, - "lines.linewidth": 2.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") # Read the simulation data sim = h5py.File("%s_0000.hdf5" % snapname, "r") @@ -65,6 +44,7 @@ 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" @@ -72,6 +52,10 @@ H_transition_temp = sim["/HydroScheme"].attrs[ T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] git = sim["Code"].attrs["Git Revision"] +cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"].decode("utf-8") + +if cooling_model == "Constant Lambda": + Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0] # Cosmological parameters H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] @@ -85,7 +69,7 @@ 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 -# Primoridal ean molecular weight as a function of temperature +# Primoridal mean molecular weight as a function of temperature def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp): if T > T_trans: return 4.0 / (8.0 - 5.0 * (1.0 - H_frac)) @@ -149,28 +133,46 @@ a_evol = np.logspace(-3, 0, 60) T_cmb = (1.0 / a_evol) ** 2 * 2.72 # Plot the interesting quantities -figure() -subplot(111, xscale="log", yscale="log") - -fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1) -plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$") -plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$") -plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5) -fill_between( +plt.figure() +plt.subplot(111, xscale="log", yscale="log") + +plt.fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1) +plt.plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$") +plt.plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$") +plt.plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5) +plt.fill_between( a, 10 ** (T_log_mean - T_log_std), 10 ** (T_log_mean + T_log_std), color="C1", alpha=0.1, ) -plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5) -plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5) - -legend(loc="upper left", frameon=False, handlelength=1.5) +plt.plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5) +plt.plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5) + +plt.legend(loc="upper left", frameon=False, handlelength=1.5) + +# Cooling model +if cooling_model == "Constant Lambda": + plt.text( + 1e-2, + 6e4, + "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$" + % (Lambda / 10.0 ** (int(log10(Lambda))), log10(Lambda)), + fontsize=7, + ) +elif cooling_model == "EAGLE": + plt.text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)") +elif cooling_model == b"Grackle": + plt.text(1e-2, 6e4, "Grackle (Smith et al. 2016)") +else: + plt.text(1e-2, 6e4, "No cooling") # Expected lines -plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7) -text( +plt.plot( + [1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7 +) +plt.text( 2.5e-2, H_transition_temp * 1.07, "$T_{\\rm HII\\rightarrow HI}$", @@ -178,10 +180,10 @@ text( alpha=0.7, fontsize=8, ) -plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7) -text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) -plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7) -text( +plt.plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7) +plt.text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) +plt.plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7) +plt.text( a_evol[20], T_cmb[20] * 0.55, "$(1+z)^2\\times T_{\\rm CMB,0}$", @@ -197,12 +199,14 @@ redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0]) redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"] a_ticks = 1.0 / (redshift_ticks + 1.0) -xticks(a_ticks, redshift_labels) -minorticks_off() +plt.xticks(a_ticks, redshift_labels) +plt.minorticks_off() + +plt.xlabel("${\\rm Redshift}~z$", labelpad=0) +plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) +plt.xlim(9e-3, 1.1) +plt.ylim(20, 2.5e7) -xlabel("${\\rm Redshift}~z$", labelpad=0) -ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) -xlim(9e-3, 1.1) -ylim(20, 2.5e7) +plt.tight_layout() -savefig("Temperature_evolution.png", dpi=200) +plt.savefig("Temperature_evolution.png", dpi=200) diff --git a/examples/SantaBarbara/SantaBarbara-256/run.sh b/examples/SantaBarbara/SantaBarbara-256/run.sh index 72c219acb201b3a3541b6a08d799b21ba4638009..b965ed13559218cfe96ec35285e9129da0816b16 100755 --- a/examples/SantaBarbara/SantaBarbara-256/run.sh +++ b/examples/SantaBarbara/SantaBarbara-256/run.sh @@ -1,4 +1,4 @@ #!/bin/bash # Run SWIFT -../../swift --cosmology --hydro --self-gravity --threads=28 santa_barbara.yml +../../../swift --cosmology --hydro --self-gravity --threads=28 santa_barbara.yml diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/plotProjection.py b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/plotProjection.py new file mode 100644 index 0000000000000000000000000000000000000000..a13666ec4e53ccd7055f6f1c6bb826a30c2c5db6 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/plotProjection.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------ +# Plots a projection of the DM mass. +# usage: +# $ python3 plotProjection.py <snapnr> +# where <snapnr> is number of snapshot to plot +# ------------------------------------------------ + +import sys +import os +import matplotlib + +matplotlib.use("Agg") +from matplotlib import pyplot as plt +from matplotlib.colors import LogNorm + +from swiftsimio import load +from swiftsimio.visualisation.projection import project_pixel_grid +from swiftsimio.visualisation.smoothing_length_generation import ( + generate_smoothing_lengths, +) + + +# Grab snapshot + +snapshot_basename = "snap" + +try: + snapnr = sys.argv[1] +except IndexError: + print("You need to provide the index of the snapshot to plot.") + print("E.g. python3 plotProjection.py 3") + quit() + +try: + snapnr_int = int(snapnr) +except ValueError: + print("<snapnr> must be an integer.") + print("You provided :'" + snapnr + "'") + +file = snapshot_basename + "_" + str(snapnr_int).zfill(4) + ".hdf5" + +if not os.path.isfile(file): + print("Didn't find snapshot", file) + quit() + + +# Load data +data = load(file) +meta = data.metadata +boxsize = meta.boxsize +extent = [0, boxsize[0].v, 0, boxsize[1].v] + +# Generate smoothing lengths for the dark matter +data.dark_matter.smoothing_length = generate_smoothing_lengths( + data.dark_matter.coordinates, + data.metadata.boxsize, + kernel_gamma=1.8, + neighbours=57, + speedup_fac=2, + dimension=3, +) + +# Project the dark matter mass +dm_mass = project_pixel_grid( + # Note here that we pass in the dark matter dataset not the whole + # data object, to specify what particle type we wish to visualise + data=data.dark_matter, + boxsize=data.metadata.boxsize, + resolution=1024, + project="masses", + parallel=True, + region=None, +) + + +# Make figure an plot +fig = plt.figure(figsize=(6, 5), dpi=200) +ax = fig.add_subplot(111) +im = ax.imshow(dm_mass.T, origin="lower", extent=extent, cmap="inferno", norm=LogNorm()) +plt.colorbar(im) + +# Add xlabels +xunits = data.dark_matter.coordinates.units.latex_representation() +ax.set_xlabel("x [" + xunits + "]", usetex=True) +ax.set_ylabel("y [" + xunits + "]", usetex=True) + + +# Add title +title = file.replace("_", r"\_") # exception handle underscore for latex +if meta.cosmology is not None: + title += ", $z$ = {0:.3f}".format(meta.z) +title += ", $t$ = {0:.2e}".format(meta.time.to("Gyr")) +fig.suptitle(title, usetex=True) + +# Save figure +plt.tight_layout() +figname = file[:-5] + ".png" +plt.savefig(figname) +plt.close() + + +print("saved", figname) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run.sh index 5d166e00a630ca93ff92a42f6d26b012b132e097..842dfea7624417e9536b8e21d1b38574b0904e40 100755 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run.sh @@ -8,5 +8,5 @@ then fi # Run SWIFT -../../swift --cosmology --self-gravity --threads=8 small_cosmo_volume_dm.yml 2>&1 | tee output.log +../../../swift --cosmology --self-gravity --power --threads=8 small_cosmo_volume_dm.yml 2>&1 | tee output.log diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml index 2d11c7c5f3f89536fc1b5c296912859da9fca3a1..f469b972e5d6e93f71c51a277e8549e9b2ab9d47 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml @@ -35,6 +35,7 @@ Snapshots: delta_time: 1.0816 # Only every second VELOCIraptor invoke gets a full snapshot dump. scale_factor_first: 0.1 # z = 9 compression: 4 + invoke_ps: 1 # Parameters governing the conserved quantities statistics Statistics: @@ -58,3 +59,11 @@ StructureFinding: basename: ./stf scale_factor_first: 0.1 # z = 9 delta_time: 1.04 + +# Power spectrum calculation options +PowerSpectrum: + grid_side_length: 256 + num_folds: 4 + fold_factor: 2 + window_order: 2 + requested_spectra: ["matter-matter"] diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/plotTempEvolution.py deleted file mode 100644 index 14f016f7c57c6999298da27f859d631d015d772d..0000000000000000000000000000000000000000 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/plotTempEvolution.py +++ /dev/null @@ -1,208 +0,0 @@ -################################################################################ -# 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/>. -# -################################################################################ - -# Computes the temperature evolution of the gas in a cosmological box - -# Physical constants needed for internal energy to temperature conversion -k_in_J_K = 1.38064852e-23 -mH_in_kg = 1.6737236e-27 - -# Number of snapshots generated -n_snapshots = 200 - -import matplotlib - -matplotlib.use("Agg") -from pylab import * -import h5py -import os.path - -# 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.14, - "figure.subplot.right": 0.99, - "figure.subplot.bottom": 0.12, - "figure.subplot.top": 0.99, - "figure.subplot.wspace": 0.15, - "figure.subplot.hspace": 0.12, - "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("snap_0000.hdf5", "r") -boxSize = sim["/Header"].attrs["BoxSize"][0] -time = sim["/Header"].attrs["Time"][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"] - -# Cosmological parameters -H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] -gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] - -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_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 - -# Primoridal ean molecular weight as a function of temperature -def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp): - if T > T_trans: - return 4.0 / (8.0 - 5.0 * (1.0 - H_frac)) - else: - return 4.0 / (1.0 + 3.0 * H_frac) - - -# Temperature of some primoridal gas with a given internal energy -def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp): - T_over_mu = (gas_gamma - 1.0) * u * mH_in_kg / k_in_J_K - ret = np.ones(np.size(u)) * T_trans - - # Enough energy to be ionized? - mask_ionized = T_over_mu > (T_trans + 1) / mu(T_trans + 1, H_frac, T_trans) - if np.sum(mask_ionized) > 0: - ret[mask_ionized] = T_over_mu[mask_ionized] * mu(T_trans * 10, H_frac, T_trans) - - # Neutral gas? - mask_neutral = T_over_mu < (T_trans - 1) / mu((T_trans - 1), H_frac, T_trans) - if np.sum(mask_neutral) > 0: - ret[mask_neutral] = T_over_mu[mask_neutral] * mu(0, H_frac, T_trans) - - return ret - - -z = np.zeros(n_snapshots) -a = np.zeros(n_snapshots) -T_mean = np.zeros(n_snapshots) -T_std = np.zeros(n_snapshots) -T_log_mean = np.zeros(n_snapshots) -T_log_std = np.zeros(n_snapshots) -T_median = np.zeros(n_snapshots) -T_min = np.zeros(n_snapshots) -T_max = np.zeros(n_snapshots) - -# Loop over all the snapshots -for i in range(n_snapshots): - sim = h5py.File("snap_%04d.hdf5" % i, "r") - - z[i] = sim["/Cosmology"].attrs["Redshift"][0] - a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] - - u = sim["/PartType0/InternalEnergies"][:] - - # Compute the temperature - u *= unit_length_in_si ** 2 / unit_time_in_si ** 2 - u /= a[i] ** (3 * (gas_gamma - 1.0)) - Temp = T(u) - - # Gather statistics - T_median[i] = np.median(Temp) - T_mean[i] = Temp.mean() - T_std[i] = Temp.std() - T_log_mean[i] = np.log10(Temp).mean() - T_log_std[i] = np.log10(Temp).std() - T_min[i] = Temp.min() - T_max[i] = Temp.max() - -# CMB evolution -a_evol = np.logspace(-3, 0, 60) -T_cmb = (1.0 / a_evol) ** 2 * 2.72 - -# Plot the interesting quantities -figure() -subplot(111, xscale="log", yscale="log") - -fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1) -plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$") -plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$") -plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5) -fill_between( - a, - 10 ** (T_log_mean - T_log_std), - 10 ** (T_log_mean + T_log_std), - color="C1", - alpha=0.1, -) -plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5) -plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5) - -legend(loc="upper left", frameon=False, handlelength=1.5) - -# Expected lines -plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7) -text( - 2.5e-2, - H_transition_temp * 1.07, - "$T_{\\rm HII\\rightarrow HI}$", - va="bottom", - alpha=0.7, - fontsize=8, -) -plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7) -text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) -plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7) -text( - a_evol[20], - T_cmb[20] * 0.55, - "$(1+z)^2\\times T_{\\rm CMB,0}$", - rotation=-34, - alpha=0.7, - fontsize=8, - va="top", - bbox=dict(facecolor="w", edgecolor="none", pad=1.0, alpha=0.9), -) - - -redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0]) -redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"] -a_ticks = 1.0 / (redshift_ticks + 1.0) - -xticks(a_ticks, redshift_labels) -minorticks_off() - -xlabel("${\\rm Redshift}~z$", labelpad=0) -ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) -xlim(9e-3, 1.1) -ylim(20, 2.5e7) - -savefig("Temperature_evolution.png", dpi=200) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/run.sh index b2585d70b7cd2b717af02f005d690d0e8a9f932e..cf8009dfdd8b3f6883ea709702ef30a0931f5130 100755 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../../swift --cosmology --hydro --self-gravity --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --self-gravity --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log # Plot the temperature evolution -python plotTempEvolution.py +python3 plotTempEvolution.py diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/run.sh index b89662ed7ae621c6fbc29ccfb566fa61367693a9..1dbbda548414824630f51ec3886c3500e4f4c3b1 100755 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/run.sh @@ -8,11 +8,11 @@ then fi # Run SWIFT -../../swift --cosmology --hydro --self-gravity --velociraptor --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --self-gravity --velociraptor --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log echo "Make a plot of the HMF" if command -v python3 &>/dev/null; then python3 haloevol.py else - python haloevol.py + python3 haloevol.py fi diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py index 4f02213ec2a66700d28ad5f8e57e00c30f3019d7..d758fd8a9f9ee81050e4336cb5a6421563532f92 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -26,37 +26,17 @@ mH_in_kg = 1.6737236e-27 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py -import os.path - -# 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.15, - "figure.subplot.right": 0.99, - "figure.subplot.bottom": 0.13, - "figure.subplot.top": 0.99, - "figure.subplot.wspace": 0.15, - "figure.subplot.hspace": 0.12, - "lines.markersize": 6, - "lines.linewidth": 2.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) +import sys + +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") snap = int(sys.argv[1]) # Read the simulation data -sim = h5py.File("snap_%04d.hdf5" % snap, "r") +sim = h5py.File("snapshots/snap_%04d.hdf5" % snap, "r") boxSize = sim["/Header"].attrs["BoxSize"][0] time = sim["/Header"].attrs["Time"][0] z = sim["/Cosmology"].attrs["Redshift"][0] @@ -86,7 +66,7 @@ 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 -# Primoridal ean molecular weight as a function of temperature +# Primoridal mean molecular weight as a function of temperature def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp): if T > T_trans: return 4.0 / (8.0 - 5.0 * (1.0 - H_frac)) @@ -138,26 +118,28 @@ log_T_max = 8 bins_x = np.linspace(log_rho_min, log_rho_max, 54) bins_y = np.linspace(log_T_min, log_T_max, 54) -H, _, _ = histogram2d(log_rho, log_T, bins=[bins_x, bins_y], normed=True) +H, _, _ = np.histogram2d(log_rho, log_T, bins=[bins_x, bins_y], normed=True) # Plot the interesting quantities -figure() +plt.figure() -pcolormesh(bins_x, bins_y, np.log10(H).T) +plt.pcolormesh(bins_x, bins_y, np.log10(H).T) -text(-5, 8.0, "$z=%.2f$" % z) +plt.text(-5, 8.0, "$z=%.2f$" % z) -xticks( +plt.xticks( [-5, -4, -3, -2, -1, 0, 1, 2, 3], ["", "$10^{-4}$", "", "$10^{-2}$", "", "$10^0$", "", "$10^2$", ""], ) -yticks( +plt.yticks( [2, 3, 4, 5, 6, 7, 8], ["$10^{2}$", "", "$10^{4}$", "", "$10^{6}$", "", "$10^8$"] ) -xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) -ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) -xlim(-5.2, 3.2) -ylim(1, 8.5) +plt.xlabel("${\\rm Physical~Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) +plt.xlim(-5.2, 3.2) +plt.ylim(1, 8.5) + +plt.tight_layout() -savefig("rhoT_%04d.png" % snap, dpi=200) +plt.savefig("rhoT_%04d.png" % snap, dpi=200) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py index eb78374e4e060b3cbd38e1108c85c89f8f45385b..f42dd927d45384e65f7ee2fb8b41d93a3df7b1cc 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -29,35 +29,14 @@ n_snapshots = 200 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py -import os.path - -# 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.14, - "figure.subplot.right": 0.99, - "figure.subplot.bottom": 0.12, - "figure.subplot.top": 0.99, - "figure.subplot.wspace": 0.15, - "figure.subplot.hspace": 0.12, - "lines.markersize": 6, - "lines.linewidth": 2.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") # Read the simulation data -sim = h5py.File("snap_0000.hdf5", "r") +sim = h5py.File("snapshots/snap_0000.hdf5", "r") boxSize = sim["/Header"].attrs["BoxSize"][0] time = sim["/Header"].attrs["Time"][0] scheme = sim["/HydroScheme"].attrs["Scheme"][0] @@ -72,7 +51,7 @@ H_transition_temp = sim["/HydroScheme"].attrs[ T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] git = sim["Code"].attrs["Git Revision"] -cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"] +cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"].decode("utf-8") if cooling_model == "Constant Lambda": Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0] @@ -127,7 +106,7 @@ T_max = np.zeros(n_snapshots) # Loop over all the snapshots for i in range(n_snapshots): - sim = h5py.File("snap_%04d.hdf5" % i, "r") + sim = h5py.File("snapshots/snap_%04d.hdf5" % i, "r") z[i] = sim["/Cosmology"].attrs["Redshift"][0] a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] @@ -153,28 +132,28 @@ a_evol = np.logspace(-3, 0, 60) T_cmb = (1.0 / a_evol) ** 2 * 2.72 # Plot the interesting quantities -figure() -subplot(111, xscale="log", yscale="log") - -fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1) -plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$") -plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$") -plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5) -fill_between( +plt.figure() +plt.subplot(111, xscale="log", yscale="log") + +plt.fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1) +plt.plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$") +plt.plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$") +plt.plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5) +plt.fill_between( a, 10 ** (T_log_mean - T_log_std), 10 ** (T_log_mean + T_log_std), color="C1", alpha=0.1, ) -plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5) -plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5) +plt.plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5) +plt.plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5) -legend(loc="upper left", frameon=False, handlelength=1.5) +plt.legend(loc="upper left", frameon=False, handlelength=1.5) # Cooling model if cooling_model == "Constant Lambda": - text( + plt.text( 1e-2, 6e4, "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$" @@ -182,15 +161,17 @@ if cooling_model == "Constant Lambda": fontsize=7, ) elif cooling_model == "EAGLE": - text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)") + plt.text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)") elif cooling_model == b"Grackle": - text(1e-2, 6e4, "Grackle (Smith et al. 2016)") + plt.text(1e-2, 6e4, "Grackle (Smith et al. 2016)") else: - text(1e-2, 6e4, "No cooling") + plt.text(1e-2, 6e4, "No cooling") # Expected lines -plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7) -text( +plt.plot( + [1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7 +) +plt.text( 2.5e-2, H_transition_temp * 1.07, "$T_{\\rm HII\\rightarrow HI}$", @@ -198,10 +179,10 @@ text( alpha=0.7, fontsize=8, ) -plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7) -text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) -plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7) -text( +plt.plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7) +plt.text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) +plt.plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7) +plt.text( a_evol[20], T_cmb[20] * 0.55, "$(1+z)^2\\times T_{\\rm CMB,0}$", @@ -217,12 +198,14 @@ redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0]) redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"] a_ticks = 1.0 / (redshift_ticks + 1.0) -xticks(a_ticks, redshift_labels) -minorticks_off() +plt.xticks(a_ticks, redshift_labels) +plt.minorticks_off() + +plt.xlabel("${\\rm Redshift}~z$", labelpad=0) +plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) +plt.xlim(9e-3, 1.1) +plt.ylim(20, 2.5e7) -xlabel("${\\rm Redshift}~z$", labelpad=0) -ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) -xlim(9e-3, 1.1) -ylim(5, 2.5e7) +plt.tight_layout() -savefig("Temperature_evolution.png", dpi=200) +plt.savefig("Temperature_evolution.png", dpi=200) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh index f65c0ccf8a543a3697566abfbce10137305c15cb..be0b80ed8bad5bc6a12f6a11ae34deb3405efed1 100755 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh @@ -19,7 +19,7 @@ then fi # Run SWIFT -../../swift --cosmology --hydro --self-gravity --cooling --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --self-gravity --cooling --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log # Plot the temperature evolution -python plotTempEvolution.py +python3 plotTempEvolution.py diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml index 0731480156788a424491d7a824ca415eace48576..c308cbd5382db94d36fef8e6d0d9b89594d08099 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml @@ -41,9 +41,11 @@ SPH: # Parameters governing the snapshots Snapshots: + subdir: snapshots basename: snap delta_time: 1.02 scale_factor_first: 0.02 + compression: 4 # Parameters governing the conserved quantities statistics Statistics: diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotProjection.py b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotProjection.py new file mode 100755 index 0000000000000000000000000000000000000000..5f3385eb17add9353e5493ae3dfd2d0a231bcd7b --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotProjection.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------ +# Plots a projection of the DM and baryon mass +# usage: +# $ python3 plotProjection.py <snapnr> +# where <snapnr> is number of snapshot to plot +# ------------------------------------------------ + +import sys +import os +import matplotlib + +matplotlib.use("Agg") +from matplotlib import pyplot as plt +from matplotlib.colors import LogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable + +from swiftsimio import load +from swiftsimio.visualisation.projection import project_pixel_grid, project_gas +from swiftsimio.visualisation.smoothing_length_generation import ( + generate_smoothing_lengths, +) + +from unyt import msun, kpc + + +def set_colorbar(ax, im): + """ + Adapt the colorbar a bit for axis object <ax> and + imshow instance <im> + """ + divider = make_axes_locatable(ax) + cax = divider.append_axes("right", size="5%", pad=0.05) + plt.colorbar(im, cax=cax) + return + + +# Grab snapshot + +snapshot_basename = "snapshots/snap" + +try: + snapnr = sys.argv[1] +except IndexError: + print("You need to provide the index of the snapshot to plot.") + print("E.g. python3 plotProjection.py 3") + quit() + +try: + snapnr_int = int(snapnr) +except ValueError: + print("<snapnr> must be an integer.") + print("You provided :'" + snapnr + "'") + +file = snapshot_basename + "_" + str(snapnr_int).zfill(4) + ".hdf5" + +if not os.path.isfile(file): + print("Didn't find snapshot", file) + quit() + + +# Load data +data = load(file) +meta = data.metadata +boxsize = meta.boxsize +extent = [0, boxsize[0].v, 0, boxsize[1].v] + +# Generate smoothing lengths for the dark matter +data.dark_matter.smoothing_length = generate_smoothing_lengths( + data.dark_matter.coordinates, + data.metadata.boxsize, + kernel_gamma=1.8, + neighbours=57, + speedup_fac=2, + dimension=3, +) + +# Project the dark matter mass +dm_mass = project_pixel_grid( + # Note here that we pass in the dark matter dataset not the whole + # data object, to specify what particle type we wish to visualise + data=data.dark_matter, + boxsize=data.metadata.boxsize, + resolution=1024, + project="masses", + parallel=True, + region=None, +) + + +# Project the gas mass +mass_map = project_gas(data, resolution=1024, project="masses", parallel=True) +mass_map.convert_to_units(msun / kpc ** 2) + + +# Make figure an plot +fig = plt.figure(figsize=(12, 5), dpi=200) + +ax1 = fig.add_subplot(121) +im1 = ax1.imshow( + dm_mass.T, origin="lower", extent=extent, cmap="inferno", norm=LogNorm() +) +ax1.set_title("Dark Matter Mass", usetex=True) +set_colorbar(ax1, im1) + +ax2 = fig.add_subplot(122) +im2 = ax2.imshow( + mass_map.T, origin="lower", extent=extent, cmap="inferno", norm=LogNorm() +) +ax2.set_title("Baryon Mass", usetex=True) +set_colorbar(ax2, im2) + + +# Add xlabels +xunits = data.dark_matter.coordinates.units.latex_representation() +for ax in [ax1, ax2]: + ax.set_xlabel("x [" + xunits + "]", usetex=True) + ax.set_ylabel("y [" + xunits + "]", usetex=True) + + +# Add title +title = file.replace("_", r"\_") # exception handle underscore for latex +if meta.cosmology is not None: + title += ", $z$ = {0:.3f}".format(meta.z) +title += ", $t$ = {0:.2e}".format(meta.time.to("Gyr")) +fig.suptitle(title, usetex=True) + +# Save figure +plt.tight_layout() +figname = file[:-5] + ".png" +plt.savefig(figname) +plt.close() + +print("saved", figname) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py index 14f016f7c57c6999298da27f859d631d015d772d..f42dd927d45384e65f7ee2fb8b41d93a3df7b1cc 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py @@ -1,6 +1,6 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -29,35 +29,14 @@ n_snapshots = 200 import matplotlib matplotlib.use("Agg") -from pylab import * +import matplotlib.pyplot as plt +import numpy as np import h5py -import os.path - -# 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.14, - "figure.subplot.right": 0.99, - "figure.subplot.bottom": 0.12, - "figure.subplot.top": 0.99, - "figure.subplot.wspace": 0.15, - "figure.subplot.hspace": 0.12, - "lines.markersize": 6, - "lines.linewidth": 2.0, - "text.latex.unicode": True, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +plt.style.use("../../../tools/stylesheets/mnras.mplstyle") # Read the simulation data -sim = h5py.File("snap_0000.hdf5", "r") +sim = h5py.File("snapshots/snap_0000.hdf5", "r") boxSize = sim["/Header"].attrs["BoxSize"][0] time = sim["/Header"].attrs["Time"][0] scheme = sim["/HydroScheme"].attrs["Scheme"][0] @@ -72,6 +51,10 @@ H_transition_temp = sim["/HydroScheme"].attrs[ T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] git = sim["Code"].attrs["Git Revision"] +cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"].decode("utf-8") + +if cooling_model == "Constant Lambda": + Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0] # Cosmological parameters H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] @@ -85,7 +68,7 @@ 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 -# Primoridal ean molecular weight as a function of temperature +# Primoridal mean molecular weight as a function of temperature def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp): if T > T_trans: return 4.0 / (8.0 - 5.0 * (1.0 - H_frac)) @@ -123,7 +106,7 @@ T_max = np.zeros(n_snapshots) # Loop over all the snapshots for i in range(n_snapshots): - sim = h5py.File("snap_%04d.hdf5" % i, "r") + sim = h5py.File("snapshots/snap_%04d.hdf5" % i, "r") z[i] = sim["/Cosmology"].attrs["Redshift"][0] a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] @@ -149,28 +132,46 @@ a_evol = np.logspace(-3, 0, 60) T_cmb = (1.0 / a_evol) ** 2 * 2.72 # Plot the interesting quantities -figure() -subplot(111, xscale="log", yscale="log") - -fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1) -plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$") -plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$") -plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5) -fill_between( +plt.figure() +plt.subplot(111, xscale="log", yscale="log") + +plt.fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1) +plt.plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$") +plt.plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$") +plt.plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5) +plt.fill_between( a, 10 ** (T_log_mean - T_log_std), 10 ** (T_log_mean + T_log_std), color="C1", alpha=0.1, ) -plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5) -plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5) - -legend(loc="upper left", frameon=False, handlelength=1.5) +plt.plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5) +plt.plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5) + +plt.legend(loc="upper left", frameon=False, handlelength=1.5) + +# Cooling model +if cooling_model == "Constant Lambda": + plt.text( + 1e-2, + 6e4, + "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$" + % (Lambda / 10.0 ** (int(log10(Lambda))), log10(Lambda)), + fontsize=7, + ) +elif cooling_model == "EAGLE": + plt.text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)") +elif cooling_model == b"Grackle": + plt.text(1e-2, 6e4, "Grackle (Smith et al. 2016)") +else: + plt.text(1e-2, 6e4, "No cooling") # Expected lines -plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7) -text( +plt.plot( + [1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7 +) +plt.text( 2.5e-2, H_transition_temp * 1.07, "$T_{\\rm HII\\rightarrow HI}$", @@ -178,10 +179,10 @@ text( alpha=0.7, fontsize=8, ) -plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7) -text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) -plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7) -text( +plt.plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7) +plt.text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) +plt.plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7) +plt.text( a_evol[20], T_cmb[20] * 0.55, "$(1+z)^2\\times T_{\\rm CMB,0}$", @@ -197,12 +198,14 @@ redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0]) redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"] a_ticks = 1.0 / (redshift_ticks + 1.0) -xticks(a_ticks, redshift_labels) -minorticks_off() +plt.xticks(a_ticks, redshift_labels) +plt.minorticks_off() + +plt.xlabel("${\\rm Redshift}~z$", labelpad=0) +plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) +plt.xlim(9e-3, 1.1) +plt.ylim(20, 2.5e7) -xlabel("${\\rm Redshift}~z$", labelpad=0) -ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) -xlim(9e-3, 1.1) -ylim(20, 2.5e7) +plt.tight_layout() -savefig("Temperature_evolution.png", dpi=200) +plt.savefig("Temperature_evolution.png", dpi=200) diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh index b2585d70b7cd2b717af02f005d690d0e8a9f932e..cf8009dfdd8b3f6883ea709702ef30a0931f5130 100755 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../../swift --cosmology --hydro --self-gravity --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log +../../../swift --cosmology --hydro --self-gravity --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log # Plot the temperature evolution -python plotTempEvolution.py +python3 plotTempEvolution.py diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml index da0a2c7686c9b2957043c77cd9f9cadb6aa7cc09..518800b6150593caa40d580f1dd85d32feee761d 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml @@ -41,9 +41,10 @@ SPH: # Parameters governing the snapshots Snapshots: + subdir: snapshots basename: snap - delta_time: 1.05 - scale_factor_first: 0.05 + delta_time: 1.02 + scale_factor_first: 0.02 compression: 4 # Parameters governing the conserved quantities statistics @@ -61,8 +62,8 @@ InitialConditions: periodic: 1 cleanup_h_factors: 1 cleanup_velocity_factors: 1 - generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs - cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs + cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. # Parameters for the EAGLE "equation of state" EAGLEEntropyFloor: diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/README b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/README new file mode 100644 index 0000000000000000000000000000000000000000..39e3b7dee1c3450ced4a05c23d93e7d68c0cd72d --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/README @@ -0,0 +1,26 @@ +Small LCDM cosmological simulation generated by C. Power. Cosmology +is WMAP9 and the box is 100Mpc/h in size with 64^3 particles. +We use a softening length of 1/25th of the mean inter-particle separation. + +The ICs have been generated to run with Gadget-2 so we need to switch +on the options to cancel the h-factors and a-factors at reading time. +We generate gas from the ICs using SWIFT's internal mechanism and set the +temperature to the expected gas temperature at this redshift. + +This example is intended to be run with the EAGLE-XL model in order to +produce lightcone outputs including gas, stars and black holes. Note +that the resulting output will not be at all realistic due to the extremely +poor mass resolution and the minimum overdensity for star formation has +to be be reduced to allow any star formation. + +To configure the code appropriately, the following flags should be included: + +./configure \ + --with-hydro=sphenix \ + --with-subgrid=EAGLE-XL \ + --with-kernel=wendland-C2 \ + --with-chealpix \ + --enable-lightcone + +MD5 checksum of the ICs: +08736c3101fd738e22f5159f78e6022b small_cosmo_volume.hdf5 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getColibreCoolingTables.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getColibreCoolingTables.sh new file mode 100755 index 0000000000000000000000000000000000000000..20a3ee7257b1a9064c268fe1ac20640ad654e98c --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getColibreCoolingTables.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/COLIBRE/UV_dust1_CR1_G1_shield1.hdf5 + diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getCoolingTables.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getCoolingTables.sh new file mode 100755 index 0000000000000000000000000000000000000000..ecd581fd3dd44a13af1218d7dee6af72a25a324a --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getCoolingTables.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/EAGLE/coolingtables.tar.gz +tar -xvzf coolingtables.tar.gz diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getEaglePhotometryTable.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getEaglePhotometryTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..ee9c3b422f19518612416da0913b162fd4a120ff --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getEaglePhotometryTable.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/YieldTables/EAGLE/photometry.tar.gz +tar -xf photometry.tar.gz diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getEagleYieldTable.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getEagleYieldTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..26eef020cab82acee2c80e88089df1790b281eab --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/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/SmallCosmoVolume/SmallCosmoVolume_lightcone/getIC.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..3b8136cc5aca00a25792655c6c505cfeeb0f2bc9 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getIC.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/small_cosmo_volume.hdf5 + diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getXrayTables.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getXrayTables.sh new file mode 100755 index 0000000000000000000000000000000000000000..84fdc6d8785f0e746f2c7f363d3e17b58a5e40ac --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/getXrayTables.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/COLIBRE/X_Ray_tables.13072021.hdf5 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/map_types.txt b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/map_types.txt new file mode 100644 index 0000000000000000000000000000000000000000..fef8b673f4f1ee1c690193451bb02aee4fb23523 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/map_types.txt @@ -0,0 +1,16 @@ +TotalMass on +DarkMatterMass on +SmoothedGasMass on +UnsmoothedGasMass on +StellarMass on +BlackHoleMass on +StarFormationRate on +XrayErositaLowIntrinsicPhotons on +XrayErositaLowIntrinsicEnergies on +XrayErositaHighIntrinsicPhotons on +XrayErositaHighIntrinsicEnergies on +XrayROSATIntrinsicPhotons on +XrayROSATIntrinsicEnergies on +ComptonY on +DopplerB on +DM on diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plot_healpix_map.py b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plot_healpix_map.py new file mode 100644 index 0000000000000000000000000000000000000000..d29c82be16730fa2075d0bbbfe8e93cf503e8c29 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plot_healpix_map.py @@ -0,0 +1,33 @@ +#!/bin/env python + +import h5py +import numpy as np +import healpy as hp + +import read_lightcone as rl + +plt.subplot(2, 2, 1) +totalmass_map = rl.read_map( + "./lightcones/", "lightcone0", shell_nr=0, map_name="TotalMass" +) +hp.mollview(totalmass_map + 1, norm="log", title="Projected mass", hold=True) + +plt.subplot(2, 2, 2) +gasmass_map = rl.read_map( + "./lightcones/", "lightcone0", shell_nr=0, map_name="SmoothedGasMass" +) +hp.mollview(gasmass_map + 1, norm="log", title="Gas mass", hold=True) + +plt.subplot(2, 2, 3) +stellarmass_map = rl.read_map( + "./lightcones/", "lightcone0", shell_nr=0, map_name="StellarMass" +) +hp.mollview(stellarmass_map + 1, norm="log", title="Stellar mass", hold=True) + +plt.subplot(2, 2, 4) +xray_map = rl.read_map( + "./lightcones/", "lightcone0", shell_nr=0, map_name="XrayROSATIntrinsicPhotons" +) +hp.mollview(xray_map + 1e50, norm="log", title="ROSAT photons", hold=True) + +plt.suptitle("SmallCosmoVolume lightcone") diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/read_lightcone.py b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/read_lightcone.py new file mode 100644 index 0000000000000000000000000000000000000000..c60038e735b8766b5b2e8ec0cd14ac55e621a0db --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/read_lightcone.py @@ -0,0 +1,76 @@ +#!/bin/env python + +import h5py +import numpy as np +import healpy as hp + + +def read_map(basedir, basename, shell_nr, map_name, return_sum=False): + """ + Read the specified healpix map for a lightcone shell + """ + + # Open the index file to determine number of files to read + fname = "%s/%s_index.hdf5" % (basedir, basename) + with h5py.File(fname, "r") as infile: + nr_files_per_shell = infile["Lightcone"].attrs["nr_files_per_shell"][0] + + # Read the pixel data + data = [] + for file_nr in range(nr_files_per_shell): + fname = "%s/%s_shells/shell_%d/%s.shell_%d.%d.hdf5" % ( + basedir, + basename, + shell_nr, + basename, + shell_nr, + file_nr, + ) + with h5py.File(fname, "r") as infile: + data.append(infile[map_name][...]) + if file_nr == 0 and return_sum: + expected_sum = infile[map_name].attrs["expected_sum"] + + data = np.concatenate(data) + + if return_sum: + return data, expected_sum + else: + return data + + +def read_particles(basedir, basename, part_type, properties): + """ + Read particle data from a lightcone + """ + + # Open the index file to determine number of files to read + fname = "%s/%s_index.hdf5" % (basedir, basename) + with h5py.File(fname, "r") as infile: + final_file_on_rank = infile["Lightcone"].attrs["final_particle_file_on_rank"] + nr_mpi_ranks = infile["Lightcone"].attrs["nr_mpi_ranks"][0] + + # Make a dict to store the result + data = {prop_name: [] for prop_name in properties} + + # Loop over MPI ranks + for rank_nr in range(nr_mpi_ranks): + # Loop over files written by this rank + for file_nr in range(final_file_on_rank[rank_nr] + 1): + fname = "%s/%s_particles/%s_%04d.%d.hdf5" % ( + basedir, + basename, + basename, + file_nr, + rank_nr, + ) + with h5py.File(fname, "r") as infile: + for prop_name in properties: + if part_type in infile: + data[prop_name].append(infile[part_type][prop_name][...]) + + # Combine arrays from files + for prop_name in properties: + data[prop_name] = np.concatenate(data[prop_name]) + + return data diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..314d173f7cbdb6e838ad9dbe60e027f78f3390db --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/run.sh @@ -0,0 +1,38 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e small_cosmo_volume.hdf5 ] +then + echo "Fetching initial conditions for the small cosmological volume example..." + ./getIC.sh +fi + +if [ ! -e UV_dust1_CR1_G1_shield1.hdf5 ] +then + echo "Fetching cooling tables for the small cosmological volume example..." + ./getColibreCoolingTables.sh +fi + +if [ ! -e photometry ] +then + echo "Fetching photometry tables for the small cosmological volume example..." + ./getEaglePhotometryTable.sh +fi + +if [ ! -e yieldtables ] +then + echo "Fetching yield tables for the small cosmological volume example..." + ./getEagleYieldTable.sh +fi + +if [ ! -e X_Ray_tables.13072021.hdf5 ] +then + echo "Fetching X ray tables for the small cosmological volume example..." + ./getXrayTables.sh +fi + + +# Run SWIFT +../../../swift --cosmology --eagle --lightcone --pin --threads=8 \ + small_cosmo_volume.yml 2>&1 | tee output.log + diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/shell_redshifts.txt b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/shell_redshifts.txt new file mode 100644 index 0000000000000000000000000000000000000000..2922d99dc924b2a2bf9e2dec1979c8fcfea792a2 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/shell_redshifts.txt @@ -0,0 +1,3 @@ +# Minimum redshift, Maximum redshift +0.0, 0.05 +0.05, 0.25 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/small_cosmo_volume.yml new file mode 100644 index 0000000000000000000000000000000000000000..9bf9901583337abacdba2322ec33ac3c4eb7ef50 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/small_cosmo_volume.yml @@ -0,0 +1,289 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98841e43 # 10^10 M_sun + UnitLength_in_cgs: 3.08567758e24 # 1 Mpc + UnitVelocity_in_cgs: 1e5 # 1 km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +Cosmology: # WMAP9 cosmology + Omega_cdm: 0.2305 + Omega_lambda: 0.724 + Omega_b: 0.0455 + h: 0.703 + a_begin: 0.019607843 # z_ini = 50. + a_end: 1.0 # z_end = 0. + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-8 + dt_max: 1e-2 + +# Parameters governing the snapshots +Snapshots: + subdir: snapshots + basename: snap + delta_time: 1.02 + scale_factor_first: 0.02 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.01 + scale_factor_first: 0.02 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 + MAC: adaptive + theta_cr: 0.7 + epsilon_fmm: 0.001 + 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 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). + h_min_ratio: 0.01 # Minimal smoothing length in units of softening. + h_max: 5.0 # Maximal smoothing length in co-moving internal units. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + initial_temperature: 268.7 # (internal units) + particle_splitting: 1 # Particle splitting is ON + particle_splitting_mass_threshold: 23.07 # (internal units, ~ 4x initial gas particle mass) + +# 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 + luminosity_filename: ./photometry + +# Parameters for the Friends-Of-Friends algorithm +FOF: + basename: fof_output # Filename for the FOF outputs. + min_group_size: 32 # The minimum no. of particles required for a group. + linking_length_ratio: 0.2 # Linking length in units of the main inter-particle separation. + seed_black_holes_enabled: 1 # Enable seeding of black holes in FoF groups + black_hole_seed_halo_mass_Msun: 1.0e10 # Minimal halo mass in which to seed a black hole (in solar masses). + scale_factor_first: 0.05 # Scale-factor of first FoF black hole seeding calls. + delta_time: 1.00751 # Scale-factor ratio between consecutive FoF black hole seeding calls. + +Scheduler: + max_top_level_cells: 8 + cell_split_size: 50 + +Restarts: + onexit: 1 + delta_hours: 6.0 + +# Parameters related to the initial conditions +InitialConditions: + file_name: small_cosmo_volume.hdf5 + periodic: 1 + cleanup_h_factors: 1 + cleanup_velocity_factors: 1 + generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs + cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0. + 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 + +# EAGLE cooling parameters +EAGLECooling: + dir_name: ./coolingtables/ + 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 + He_reion_eV_p_H: 2.0 + +# COLIBRE cooling parameters +COLIBRECooling: + dir_name: ./UV_dust1_CR1_G1_shield1.hdf5 # Location of the cooling tables + H_reion_z: 7.5 # Redshift of Hydrogen re-ionization (Planck 2018) + 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 + delta_logTEOS_subgrid_properties: 0.3 # delta log T above the EOS below which the subgrid properties use Teq assumption + rapid_cooling_threshold: 0.333333 # Switch to rapid cooling regime for dt / t_cool above this threshold. + +# EAGLE star formation parameters +EAGLEStarFormation: + SF_threshold: Subgrid # Zdep (Schaye 2004) or Subgrid + SF_model: PressureLaw # PressureLaw (Schaye et al. 2008) or SchmidtLaw + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + min_over_density: 100.0 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e8 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + EOS_entropy_margin_dex: 0.3 # When using Z-based SF threshold, logarithm base 10 of the maximal entropy above the EOS at which stars can form. + threshold_norm_H_p_cm3: 0.1 # When using Z-based SF threshold, normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # When using Z-based SF threshold, reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # When using Z-based SF threshold, slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # When using Z-based SF threshold, maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_temperature1_K: 1000 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming. + threshold_temperature2_K: 31622 # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming if also above the density limit. + threshold_number_density_H_p_cm3: 10 # When using subgrid-based SF threshold, subgrid number density above which gas is star-forming if also below the second temperature limit. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 1e-4 # 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: 800 # 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: 10. # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. (NOTE: This is below the min T and hence this floor does nothing) + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# EAGLE feedback model +EAGLEFeedback: + use_SNII_feedback: 1 # Global switch for SNII thermal (stochastic) feedback. + use_SNIa_feedback: 1 # 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: 8.0 # Minimal mass considered for SNII stars in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII stars in solar masses. + SNII_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + SNII_sampled_delay: 1 # Sample the SNII lifetimes to do feedback. + 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_function: Independent # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent'). + SNII_energy_fraction_min: 0.5 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 1.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_delta_E_n: 6.0 # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent'). + 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: 1.4588 # 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. + SNII_energy_fraction_use_birth_density: 0 # Are we using the density at birth to compute f_E or at feedback time? + SNII_energy_fraction_use_birth_metallicity: 0 # Are we using the metallicity at birth to compuote f_E or at feedback time? + SNIa_DTD: Exponential # Functional form of the SNIa delay time distribution. + SNIa_DTD_delay_Gyr: 0.04 # Stellar age after which SNIa start in Gyr (40 Myr corresponds to stars ~ 8 Msun). + SNIa_DTD_exp_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_DTD_exp_norm_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. + stellar_evolution_age_cut_Gyr: 0.1 # Stellar age in Gyr above which the enrichment is down-sampled. + stellar_evolution_sampling_rate: 10 # Number of time-steps in-between two enrichment events for a star above the age threshold. + 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. + +# EAGLE AGN model +EAGLEAGN: + subgrid_seed_mass_Msun: 1.0e4 # Black hole subgrid mass at creation time in solar masses. + use_multi_phase_bondi: 0 # Compute Bondi rates per neighbour particle? + use_subgrid_bondi: 0 # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH? + with_angmom_limiter: 0 # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term? + viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term + with_boost_factor: 0 # Are we using the model from Booth & Schaye (2009)? + boost_alpha_only: 0 # If using the boost factor, are we using a constant boost only? + boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model. + boost_beta: 2. # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models. + boost_n_h_star_H_p_cm3: 0.1 # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3). + with_fixed_T_near_EoS: 0 # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term? + fixed_T_above_EoS_dex: 0.3 # Distance above the entropy floor for which we use a fixed sound-speed + fixed_T_near_EoS_K: 8000 # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term + radiative_efficiency: 0.1 # Fraction of the accreted mass that gets radiated. + use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? + min_gas_mass_for_nibbling_Msun: 7.2e6 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. + coupling_efficiency: 0.1 # Fraction of the radiated energy that couples to the gas in feedback events. + AGN_feedback_model: MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity + AGN_use_deterministic_feedback: 1 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? + use_variable_delta_T: 1 # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0]. + AGN_with_locally_adaptive_delta_T: 1 # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_norm: 3e8 # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_reference: 1e8 # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1). + AGN_delta_T_mass_exponent: 0.666667 # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1). + AGN_delta_T_crit_factor: 1.0 # Multiple of critical dT for numerical efficiency (Dalla Vecchia & Schaye 2012) to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_background_factor: 0.0 # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1). + AGN_delta_T_min: 1e7 # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_max: 3e9 # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1). + AGN_delta_T_K: 3.16228e8 # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs). + AGN_use_nheat_with_fixed_dT: 0 # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold. + AGN_use_adaptive_energy_reservoir_threshold: 0 # Switch to calculate an adaptive AGN energy reservoir threshold. + AGN_num_ngb_to_heat: 1. # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0). + max_reposition_mass: 1e20 # Maximal BH mass considered for BH repositioning in solar masses (large number implies we always reposition). + max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. + with_reposition_velocity_threshold: 0 # Should we only reposition to particles that move slowly w.r.t. the black hole? + max_reposition_velocity_ratio: 0.5 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1. + min_reposition_velocity_threshold: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1. + set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' + threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' + merger_threshold_type: DynamicalEscapeVelocity + merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. + minimum_timestep_Myr: 0.1 # Minimum of the accretion-limited time-step length. + with_potential_correction: 1 + +XrayEmissivity: + xray_table_path: ./X_Ray_tables.13072021.hdf5 # Path to the X-ray emissivity tables + +# Parameters common to all lightcones +LightconeCommon: + + subdir: lightcones # All lightcone output is written to this directory + buffer_chunk_size: 10000 # Particles and map updates are buffered in a linked list of chunks of this size + + z_range_for_DM: [0.0, 0.05] # Output redshift range for dark matter + z_range_for_Gas: [0.0, 0.05] # Output redshift range for gas + z_range_for_Stars: [0.0, 0.05] # Output redshift range for stars + z_range_for_BH: [0.0, 0.05] # Output redshift range for black holes + + max_particles_buffered: 100000 # Output particles if buffer size reaches this value + max_updates_buffered: 100000 # Flush map updates if buffer size reaches this value + hdf5_chunk_size: 16384 # Chunk size for HDF5 particle and healpix map datasets + + nside: 512 # Healpix resolution parameter + radius_file: ./shell_redshifts.txt # Redshifts of shells for healpix maps + max_map_update_send_size_mb: 16.0 # Apply map updates over mutliple iterations to limit memory overhead + map_names_file: ./map_types.txt # List of types of healpix maps to make + + distributed_maps: 1 # Split maps over multiple files (1) or use collective I/O to write one file (0) + + particles_lossy_compression: 0 # Apply lossy compression to lightcone particles + particles_gzip_level: 6 # Apply lossless (deflate) compression to lightcone particles + maps_gzip_level: 6 # Apply lossless (deflate) compression to healpix maps + +# Parameters specific to lightcone 0 +Lightcone0: + enabled: 1 # Enable this lightcone + basename: lightcone0 # Base name of this lighcone's output files + observer_position: [35.561875, 35.561875, 35.561875] # Location of the observer in this lightcone + +# Parameters specific to lightcone 1 +Lightcone1: + enabled: 1 + basename: lightcone1 + observer_position: [35.561875, 35.561875, 35.561875] + + gas_filtering_enabled: 1 # Enable filtering out of certain gas particles from particle outputs + min_z_for_gas_filtering: 0.025 # Filter gas particles above this redshift, output all below + min_temp_for_filtered_gas: 1.0e5 # Above min_z_for_gas_filtering only output gas with temperature above this + min_nh_for_filtered_gas: 1.0e-6 # Above min_z_for_gas_filtering only output gas with nh/(1+z)^4 above this diff --git a/examples/SubgridTests/BlackHoleSwallowing/makeIC.py b/examples/SubgridTests/BlackHoleSwallowing/makeIC.py index 731d4cdd5117fb753adeff31f7632f14ce76b050..9f59e3c58d2a0aaee85c5a23512e02d022c1f7bf 100644 --- a/examples/SubgridTests/BlackHoleSwallowing/makeIC.py +++ b/examples/SubgridTests/BlackHoleSwallowing/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py b/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py deleted file mode 100644 index 44d3c7e5ab9d9c5c716fb7e60735337b617c6c32..0000000000000000000000000000000000000000 --- a/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py +++ /dev/null @@ -1,197 +0,0 @@ -# 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.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 - -# Calculate solar mass in internal units -const_solar_mass = 1.98848e33 / unit_mass_in_cgs - -# Define Gyr -Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0 - -# 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.0e-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 index e954f040ca7f6c1d24f2cf203b51fa1bb917a1b5..2e6cced06ab2346efe368f52e4e90a045e855713 100644 --- a/examples/SubgridTests/CosmologicalStellarEvolution/check_stellar_evolution.py +++ b/examples/SubgridTests/CosmologicalStellarEvolution/check_stellar_evolution.py @@ -32,11 +32,9 @@ params = { "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") diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py b/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py deleted file mode 100644 index 27404ac9954c930ea61cfbf07a59cf48f850057e..0000000000000000000000000000000000000000 --- a/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py +++ /dev/null @@ -1,199 +0,0 @@ -# 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.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 - -# Calculate solar mass in internal units -const_solar_mass = 1.98848e33 / unit_mass_in_cgs - -# Define Gyr -Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0 - -# 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/makeIC.py b/examples/SubgridTests/CosmologicalStellarEvolution/makeIC.py index 97a453bb51563f6d0118db7b243f3dffadc456ec..ff7e75b08950d59b248cec93ab7579b9ffa859d6 100644 --- a/examples/SubgridTests/CosmologicalStellarEvolution/makeIC.py +++ b/examples/SubgridTests/CosmologicalStellarEvolution/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/plot_box_evolution.py b/examples/SubgridTests/CosmologicalStellarEvolution/plot_box_evolution.py index 00782078f0ee97bfa5b87c5cfd64f0077677673a..b1e21caa5eac8959406129fa6e17a523278b700e 100644 --- a/examples/SubgridTests/CosmologicalStellarEvolution/plot_box_evolution.py +++ b/examples/SubgridTests/CosmologicalStellarEvolution/plot_box_evolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -51,10 +51,8 @@ params = { "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 diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/plot_particle_evolution.py b/examples/SubgridTests/CosmologicalStellarEvolution/plot_particle_evolution.py index be1588f9b707d448b2905611defd9e760c5f91de..4b7c6e005547073859b065fafb52cafd302cc514 100644 --- a/examples/SubgridTests/CosmologicalStellarEvolution/plot_particle_evolution.py +++ b/examples/SubgridTests/CosmologicalStellarEvolution/plot_particle_evolution.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 @@ -59,10 +59,8 @@ params = { "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 diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/run.sh b/examples/SubgridTests/CosmologicalStellarEvolution/run.sh index 7f55bdcb1f504a9553aa25a056f41e71334446cc..c54d1005cc8c37f9b3e848fc7b457d9d5a19b18d 100755 --- a/examples/SubgridTests/CosmologicalStellarEvolution/run.sh +++ b/examples/SubgridTests/CosmologicalStellarEvolution/run.sh @@ -9,7 +9,7 @@ fi if [ ! -e stellar_evolution.hdf5 ] then echo "Generating initial conditions for the 3D stellar evolution example..." - python makeIC.py + python3 makeIC.py fi # Get the Yield tables @@ -32,22 +32,22 @@ then ./getSolutions.sh fi -../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 -P EAGLEChemistry:init_abundance_Hydrogen:0.71 -P EAGLEChemistry:init_abundance_Helium:0.21 2>&1 | tee output_0p08.log +../../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 -P EAGLEChemistry:init_abundance_Hydrogen:0.71 -P EAGLEChemistry:init_abundance_Helium:0.21 2>&1 | tee output_0p08.log python3 plot_box_evolution.py -../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 -P EAGLEChemistry:init_abundance_Hydrogen:0.74 -P EAGLEChemistry:init_abundance_Helium:0.23 2>&1 | tee output_0p04.log +../../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 -P EAGLEChemistry:init_abundance_Hydrogen:0.74 -P EAGLEChemistry:init_abundance_Helium:0.23 2>&1 | tee output_0p04.log python3 plot_box_evolution.py -../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.01 2>&1 | tee output_0p01.log +../../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.01 2>&1 | tee output_0p01.log python3 plot_box_evolution.py -../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.001 2>&1 | tee output_0p001.log +../../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.001 2>&1 | tee output_0p001.log python3 plot_box_evolution.py -../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.0001 2>&1 | tee output_0p0001.log +../../../swift --temperature --feedback --stars --hydro --cosmology --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.0001 2>&1 | tee output_0p0001.log python3 plot_box_evolution.py diff --git a/examples/SubgridTests/ParticleSplitting/run.sh b/examples/SubgridTests/ParticleSplitting/run.sh index 4dcefb9e715674785972288dc73b4b954bc38fcc..9ce32ccf6e42cbc10cb295a32947739f076aebac 100644 --- a/examples/SubgridTests/ParticleSplitting/run.sh +++ b/examples/SubgridTests/ParticleSplitting/run.sh @@ -8,6 +8,6 @@ fi # A very simple invocation for this one... -../../swift --hydro -t 4 particle_splitting.yml +../../../swift --hydro -t 4 particle_splitting.yml python3 plotSolution.py diff --git a/examples/SubgridTests/PressureFloor/run.sh b/examples/SubgridTests/PressureFloor/run.sh index 6d90f8345bfd25fbe826ad992313fa28bfda22c3..3fecdb9b33a5e6e97956919fccd3459f6419dcee 100644 --- a/examples/SubgridTests/PressureFloor/run.sh +++ b/examples/SubgridTests/PressureFloor/run.sh @@ -10,12 +10,12 @@ fi if [ ! -e coolingBox.hdf5 ] then echo "Generating initial conditions for the cooling box example..." - python makeIC.py + python3 makeIC.py fi rm pressureFloor_* # Run SWIFT -../../swift --self-gravity --hydro --cooling --threads=8 pressureFloor.yml +../../../swift --self-gravity --hydro --cooling --threads=8 pressureFloor.yml # Check if the simulation collapsed -python plotDensity.py 80 +python3 plotDensity.py 80 diff --git a/examples/SubgridTests/SmoothedMetallicity/makeIC.py b/examples/SubgridTests/SmoothedMetallicity/makeIC.py index b4d5caa5cc04354b5ab111e14a5e0d764dcbadcd..d73e7106544d58e513d59d0580d30fbf3f77c30b 100644 --- a/examples/SubgridTests/SmoothedMetallicity/makeIC.py +++ b/examples/SubgridTests/SmoothedMetallicity/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/SubgridTests/SmoothedMetallicity/plotSolution.py b/examples/SubgridTests/SmoothedMetallicity/plotSolution.py index f4cd65524bd8555a6b8b6068594e7780908fbee3..2ad291563f4d39d83b743b0b9262b5955c84731a 100644 --- a/examples/SubgridTests/SmoothedMetallicity/plotSolution.py +++ b/examples/SubgridTests/SmoothedMetallicity/plotSolution.py @@ -2,7 +2,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 @@ -64,11 +64,9 @@ params = { "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"]}) snap = int(sys.argv[1]) @@ -205,7 +203,7 @@ 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.7, "$\\textsc{Swift}$ %s" % git, fontsize=10) +plt.text(-0.49, 0.7, "$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) diff --git a/examples/SubgridTests/SmoothedMetallicity/run.sh b/examples/SubgridTests/SmoothedMetallicity/run.sh index 736a16fc14ece7b09e13b61cd8e04f9735e6cfc6..caa9e32dc8543a769826ddf390cff2d3565f1962 100755 --- a/examples/SubgridTests/SmoothedMetallicity/run.sh +++ b/examples/SubgridTests/SmoothedMetallicity/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e smoothed_metallicity.hdf5 ] then echo "Generating initial conditions for the SmoothedMetallicity example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT -../../swift --steps=1 --hydro --threads=4 smoothed_metallicity.yml 2>&1 | tee output.log +../../../swift --steps=1 --hydro --threads=4 smoothed_metallicity.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 1 +python3 plotSolution.py 1 diff --git a/examples/SubgridTests/StellarEvolution/check_continuous_heating.py b/examples/SubgridTests/StellarEvolution/check_continuous_heating.py deleted file mode 100644 index 44d3c7e5ab9d9c5c716fb7e60735337b617c6c32..0000000000000000000000000000000000000000 --- a/examples/SubgridTests/StellarEvolution/check_continuous_heating.py +++ /dev/null @@ -1,197 +0,0 @@ -# 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.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 - -# Calculate solar mass in internal units -const_solar_mass = 1.98848e33 / unit_mass_in_cgs - -# Define Gyr -Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0 - -# 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.0e-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/StellarEvolution/check_stellar_evolution.py b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py index e954f040ca7f6c1d24f2cf203b51fa1bb917a1b5..a16101a1104191252b5a13312a6f5f43cdf0610f 100644 --- a/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py +++ b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py @@ -1,6 +1,22 @@ -import matplotlib +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## -matplotlib.use("Agg") from pylab import * import h5py import os.path @@ -14,30 +30,6 @@ n_snapshots = ( ) 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] @@ -96,16 +88,16 @@ 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"] + metallicity[:, i] = sim["/PartType0/MetalMassFractions"] 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"] + mass_from_AGB[:, i] = sim["/PartType0/MassesFromAGB"] + metal_mass_frac_from_AGB[:, i] = sim["/PartType0/MetalMassFractionsFromAGB"] + mass_from_SNII[:, i] = sim["/PartType0/MassesFromSNII"] + metal_mass_frac_from_SNII[:, i] = sim["/PartType0/MetalMassFractionsFromSNII"] + mass_from_SNIa[:, i] = sim["/PartType0/MassesFromSNIa"] + metal_mass_frac_from_SNIa[:, i] = sim["/PartType0/MetalMassFractionsFromSNIa"] + iron_mass_frac_from_SNIa[:, i] = sim["/PartType0/IronMassFractionsFromSNIa"] 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_stochastic_heating.py b/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py deleted file mode 100644 index 27404ac9954c930ea61cfbf07a59cf48f850057e..0000000000000000000000000000000000000000 --- a/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py +++ /dev/null @@ -1,199 +0,0 @@ -# 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.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 - -# Calculate solar mass in internal units -const_solar_mass = 1.98848e33 / unit_mass_in_cgs - -# Define Gyr -Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0 - -# 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/StellarEvolution/makeIC.py b/examples/SubgridTests/StellarEvolution/makeIC.py index 97a453bb51563f6d0118db7b243f3dffadc456ec..ff7e75b08950d59b248cec93ab7579b9ffa859d6 100644 --- a/examples/SubgridTests/StellarEvolution/makeIC.py +++ b/examples/SubgridTests/StellarEvolution/makeIC.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/examples/SubgridTests/StellarEvolution/plot_box_evolution.py b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py index 5239c96e0fb3585b73f9171c7077d68f2d333e63..9c7268b87397c783f380dcd604ec2277dea3d489 100644 --- a/examples/SubgridTests/StellarEvolution/plot_box_evolution.py +++ b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -33,28 +33,7 @@ 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.0, -} -rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) - +style.use("../../../tools/stylesheets/mnras.mplstyle") # Number of snapshots and elements newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5")) # , key=os.path.getctime) @@ -194,7 +173,7 @@ eagle_total_metal_mass_SNIa = data[:, 2] * stellar_mass / Msun_in_cgs * unit_mas # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) suptitle("Star metallicity Z = %.4f" % Z_star) @@ -346,4 +325,6 @@ 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)) +tight_layout() + savefig("box_evolution_Z_%.4f.png" % (Z_star), dpi=200) diff --git a/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py index be1588f9b707d448b2905611defd9e760c5f91de..5e72bc477022ec5aa56aa8202d6c36afbba9fffa 100644 --- a/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py +++ b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 @@ -33,6 +33,8 @@ import numpy as np import glob import os.path +style.use("../../../tools/stylesheets/mnras.mplstyle") + # Function to find index in array a for each element in array b def find_indices(a, b): result = np.zeros(len(b)) @@ -41,30 +43,6 @@ def find_indices(a, b): 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 = ( @@ -145,7 +123,7 @@ for i in range(n_snapshots): P = sim["/PartType0/Pressures"][:] rho = sim["/PartType0/Densities"][:] mass = sim["/PartType0/Masses"][:] - metallicity = sim["/PartType0/Metallicities"][:] + metallicity = sim["/PartType0/MetalMassFractions"][:] internal_energy = sim["/PartType0/InternalEnergies"][:] IDs = sim["/PartType0/ParticleIDs"][:] @@ -158,7 +136,7 @@ for i in range(n_snapshots): # Plot the interesting quantities -figure() +figure(figsize=(7, 7 / 1.6)) # Radial velocity -------------------------------- subplot(221) @@ -220,4 +198,6 @@ xlabel("Time (Myr)", labelpad=0) ylabel("Metallicity", labelpad=2) ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) +tight_layout() + savefig("particle_evolution.png", dpi=200) diff --git a/examples/SubgridTests/StellarEvolution/run.sh b/examples/SubgridTests/StellarEvolution/run.sh index a3fa284be151b7f5d5da6e888d9839c00bbf34a5..7d2759e71e1b1d9639ac0ef8338733fde7e3f487 100755 --- a/examples/SubgridTests/StellarEvolution/run.sh +++ b/examples/SubgridTests/StellarEvolution/run.sh @@ -32,22 +32,22 @@ then ./getSolutions.sh fi -../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 -P EAGLEChemistry:init_abundance_Hydrogen:0.71 -P EAGLEChemistry:init_abundance_Helium:0.21 2>&1 | tee output_0p08.log +../../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 -P EAGLEChemistry:init_abundance_Hydrogen:0.71 -P EAGLEChemistry:init_abundance_Helium:0.21 2>&1 | tee output_0p08.log python3 plot_box_evolution.py -../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 -P EAGLEChemistry:init_abundance_Hydrogen:0.74 -P EAGLEChemistry:init_abundance_Helium:0.23 2>&1 | tee output_0p04.log +../../../swift --temperature --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 -P EAGLEChemistry:init_abundance_Hydrogen:0.74 -P EAGLEChemistry:init_abundance_Helium:0.23 2>&1 | tee output_0p04.log python3 plot_box_evolution.py -../../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 +../../../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 python3 plot_box_evolution.py -../../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 +../../../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 python3 plot_box_evolution.py -../../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 +../../../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 python3 plot_box_evolution.py diff --git a/examples/nIFTyCluster/Baryonic/README b/examples/nIFTyCluster/Baryonic/README index 8235ef1f05915450b5473ade5800ff3af85d0058..ea6bd4e4073915b2c7d66ebc53094a34b2c7e3cb 100644 --- a/examples/nIFTyCluster/Baryonic/README +++ b/examples/nIFTyCluster/Baryonic/README @@ -28,3 +28,14 @@ HPC node. Some boilerplate for running on a SLURM batch system is included in the `run.sh` script. + +Running structure finders +------------------------- + +We recommend running velociraptor with the following compilation +options: + +``` +cmake .. -DVR_USE_GAS=ON -DVR_USE_HYDRO=OFF -DVR_ZOOM_SIM=ON + -DCMAKE_CXX_FLAGS="-O3 -march=native" +``` diff --git a/examples/nIFTyCluster/Baryonic/run.sh b/examples/nIFTyCluster/Baryonic/run.sh index 7c9451fa36f55102ffae74dde5f462f6a6d1a15d..351874e30eaeadb649ceff76f7b5036694a018c1 100755 --- a/examples/nIFTyCluster/Baryonic/run.sh +++ b/examples/nIFTyCluster/Baryonic/run.sh @@ -10,4 +10,4 @@ #SBATCH -t 72:00:00 -../../swift --cosmology --hydro --self-gravity -v 1 --pin --threads=56 nifty.yml +../../../swift --cosmology --hydro --self-gravity -v 1 --pin --threads=56 nifty.yml diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 092dd7c35aa6ee8c7dd6d326ca191de1dccb2814..7ae76282ec93bf7a68d81d24d84240cf4e868678 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -12,8 +12,9 @@ InternalUnitSystem: # Values of some physical constants PhysicalConstants: - G: 6.67408e-8 # (Optional) Overwrite the value of Newton's constant used internally by the code. - + G: 6.67408e-8 # (Optional) Overwrite the value of Newton's constant used internally by the code. + mu_0: 1.2566370e1 # (Optional) Overwrite the value of the vacuum permeability used internally by the code. + # Cosmological parameters Cosmology: h: 0.6777 # Reduced Hubble constant @@ -79,6 +80,7 @@ Stars: Gravity: mesh_side_length: 128 # Number of cells along each axis for the periodic gravity mesh (must be even). distributed_mesh: 0 # (Optional) Are we using a distributed mesh when running over MPI (necessary for meshes > 1290^3) + mesh_uses_local_patches: 1 # (Optional) Are we using thread-local patches (1) or direct atomic writes to the global mesh (0) in the non-MPI case? eta: 0.025 # Constant dimensionless multiplier for time integration. MAC: adaptive # Choice of mulitpole acceptance criterion: 'adaptive' OR 'geometric'. epsilon_fmm: 0.001 # Tolerance parameter for the adaptive multipole acceptance criterion. @@ -93,6 +95,7 @@ Gravity: max_physical_nu_softening: 0.0007 # Maximal Plummer-equivalent softening length in physical coordinates for neutrino 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). + rebuild_active_fraction: 1.01 # (Optional) Fraction of active gravity particles needed to trigger a gravity-tree rebuild (not triggered by this if > 1, which 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). @@ -102,6 +105,7 @@ ForceChecks: only_when_all_active: 1 # (Optional) Only compute exact forces during timesteps when all gparts are active (default: 0). only_at_snapshots: 1 # (Optional) Only compute exact forces during timesteps when a snapshot is being dumped (default: 0). +# Parameters related to the Friends-Of-Friends halo finding FOF: basename: fof_output # Filename for the FOF outputs (Unused when FoF is only run to seed BHs). scale_factor_first: 0.91 # Scale-factor of first FoF black hole seeding calls (needed for cosmological runs). @@ -115,6 +119,8 @@ FOF: absolute_linking_length: -1. # (Optional) Absolute linking length (in internal units). When not set to -1, this will overwrite the linking length computed from 'linking_length_ratio'. group_id_default: 2147483647 # (Optional) Sets the group ID of particles in groups below the minimum size. Defaults to 2^31 - 1 if unspecified. Has to be positive. group_id_offset: 1 # (Optional) Sets the offset of group ID labeling. Defaults to 1 if unspecified. + output_list_on: 0 # (Optional) Enable the output list + output_list: ./output_list_fof.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) # Parameters for the task scheduling Scheduler: @@ -138,8 +144,13 @@ Scheduler: engine_max_parts_per_ghost: 1000 # (Optional) Maximum number of parts per ghost. engine_max_sparts_per_ghost: 1000 # (Optional) Maximum number of sparts per ghost. engine_max_parts_per_cooling: 10000 # (Optional) Maximum number of parts per cooling task. + engine_redist_alloc_margin: 1.2 # (Optional) Multiplier factor for the number of local particles to allocate on a given rank. + engine_foreign_alloc_margin: 1.05 # (Optional) Multiplier factor for the number of foreign particles to allocate on a given rank. dependency_graph_frequency: 0 # (Optional) Dumping frequency of the dependency graph. By default, writes only at the first step. + dependency_graph_cell: 0 # (Optional) Write the dependency graph for a single cell with the same frequency as the full dependency graph. Select which cell to write using its cellID specified with this parameter. task_level_output_frequency: 0 # (Optional) Dumping frequency of the task level data. By default, writes only at the first step. + free_foreign_during_restart: 0 # (Optional) Should the code free the foreign data when dumping restart files in order to get breathing space? + free_foreign_during_rebuild: 0 # (Optional) Should the code free the foreign data when calling a rebuld in order to get breathing space? # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) TimeIntegration: @@ -149,6 +160,7 @@ TimeIntegration: dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). max_dt_RMS_factor: 0.25 # (Optional) Dimensionless factor for the maximal displacement allowed based on the RMS velocities. dt_RMS_use_gas_only: 0 # (Optional) When computing the max RMS dt, should only the gas particles be considered in the baryon component calculation? + max_nr_rt_subcycles: 0 # (Optional) Maximal number of radiative transfer sub-cycles per hydro step for any particle. Set = 0 to disable subcycling. Needs to be a power of 2. # Parameters governing the snapshots Snapshots: @@ -159,6 +171,7 @@ Snapshots: delta_time: 0.01 # Time difference between consecutive outputs (in internal units) invoke_stf: 0 # (Optional) Call VELOCIraptor every time a snapshot is written irrespective of the VELOCIraptor output strategy. invoke_fof: 0 # (Optional) Call FOF every time a snapshot is written + invoke_ps: 0 # (Optional) Call a power-spectrum calculation every time a snapshot is written compression: 0 # (Optional) Set the level of GZIP compression of the HDF5 datasets [0-9]. 0 does no compression. The lossless compression is applied to *all* the fields. distributed: 0 # (Optional) When running over MPI, should each rank write a partial snapshot or do we want a single file? 1 implies one file per MPI rank. lustre_OST_count: 0 # (Optional) If > 0, the number of lustre OSTs to distribure the single-striped files over. Has no effect on non-Lustre filesystems. Has an effect only on distributed snapshots. @@ -175,7 +188,9 @@ Snapshots: select_output: selectoutput.yml # (Optional) File containing information to select outputs with (see documentation in the "Output Selection" section) run_on_dump: 0 # (Optional) Run the dump_command each time that a snapshot is dumped? dump_command: ./submit_velociraptor.sh # (Optional) Command to run each time that a snapshot is dumped. - + recording_triggers_part: [1e-3, 1e-2] # (Optional) Time before the snapshots where the trigger for gas particle tracers start (in internal units). + recording_triggers_spart: [1e-3, 1e-2] # (Optional) Time before the snapshots where the trigger for star particle tracers start (in internal units). + recording_triggers_bpart: [1e-3, 1e-2] # (Optional) Time before the snapshots where the trigger for BH particle tracers start (in internal units). # Parameters governing the CSDS snapshot system CSDS: @@ -264,6 +279,7 @@ LineOfSight: time_first: 0.01 # (Optional) Time of the first line-of-sight output (in internal units). delta_time: 1.02 # (Optional) Time difference between consecutive line-of-sight outputs (in internal units) in simulation time intervals. output_list_on: 0 # (Optional) Enable the use of an output list + output_list: ./output_list_los.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) num_along_x: 0 # Number of sight-lines along the x-axis num_along_y: 0 # Number of sight-lines along the y-axis num_along_z: 100 # Number of sight-lines along the z-axis @@ -609,7 +625,7 @@ EAGLEAGN: max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. use_nibbling: 0 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? - min_gas_mass_for_nibbling: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. coupling_efficiency: 0.15 # Fraction of the radiated energy that couples to the gas in feedback events. AGN_feedback_model: Random # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity AGN_use_deterministic_feedback: 0 # Deterministic (reservoir) [1] or stochastic [0] AGN feedback? @@ -640,12 +656,83 @@ EAGLEAGN: reposition_exponent_mass: 2.0 # (Optional) Exponent for scaling of repositioning velocity with BH mass; default = 2.0. Only meaningful if set_reposition_speed is 1. reposition_reference_n_H: 1.0 # Reference gas density around the black holes for scaling of repositioning speed [N_H cm^-3]. Only meaningful if set_reposition_speed is 1. reposition_exponent_n_H: 1.0 # (Optional) Exponent for scaling of repositioning velocity with gas density; default = 1.0. Only meaningful if set_reposition_speed is 1. + with_potential_correction: 1 # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets. threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' - merger_threshold_type: 2 # Type of velocity threshold for BH mergers (0: v_circ at kernel edge, 1: v_esc at actual distance, with softening, 2: v_esc at actual distance, no softening). + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity'). merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. minimum_timestep_Myr: 1.0 # Minimum time-step size for BHs in Mega-years. The time-step size is computed based on the accretion rate and this serves as a minimum. Defaults to FLT_MAX. +# Spin-jet AGN model +SPINJETAGN: + subgrid_seed_mass_Msun: 1e5 # Black hole subgrid mass at creation time in solar masses. + use_subgrid_mass_from_ics: 1 # (Optional) Use subgrid masses specified in ICs [1, default], or initialise them to particle masses [0]? + with_subgrid_mass_check: 1 # (Optional) Verify that initial black hole subgrid masses are positive [1, default]. Only used if use_subgrid_mass_from_ics is 1. + use_multi_phase_bondi: 0 # Compute Bondi rates per neighbour particle [1] or for the smoothed ambient gas around the black hole [0]? + use_subgrid_gas_properties: 1 # Use subgrid density [1] or dynamical density [0] to calculate BH accretion rates? + use_krumholz: 1 # Use Krumholz et al. (2006) [1] or standard Bondi-Hoyle-Lyttleton formula [0] for black hole accretion rates? Only used if multi_phase_bondi is 0. + with_krumholz_vorticity: 0 # Include the vorticity term in Krumholz et al. formula? Only used if use_multi_phase_bondi is 0. + with_angmom_limiter: 0 # Are we applying the Rosas-Guevara (2015) viscous time-scale reduction term? + viscous_alpha: 1e6 # Normalisation constant of the viscous time-scale in the accretion reduction term. Only used if with_angmom_limiter is 1. + with_boost_factor: 0 # Are we using the model from Booth, Schaye (2009)? + boost_alpha: 1. # Lowest value for the accretion effeciency for the Booth, Schaye 2009 accretion model. + boost_beta: 2. # Slope of the power law for the Booth, Schaye 2009 model, set beta to zero for constant alpha models. + boost_n_h_star_cm3: 0.1 # Normalization of the power law for the Booth Schaye 2009 model in cgs (cm^-3). + max_eddington_fraction: 1. # Maximal allowed accretion rate in units of the Eddington rate. + eddington_fraction_for_recording: 0.1 # Record the last time BHs reached an Eddington ratio above this threshold. + use_nibbling: 1 # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? + min_gas_mass_for_nibbling_Msun: 9e5 # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1. + 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. + with_potential_correction: 1 # Subtract BH's own contribution to the potential of neighbours when determining repositioning targets. + max_reposition_mass_Msun: 2e8 # Maximal BH mass considered for BH repositioning in solar masses. + max_reposition_distance_ratio: 3.0 # Maximal distance a BH can be repositioned, in units of the softening length. + with_reposition_velocity_threshold: 0 # Should we only reposition to particles that move slowly w.r.t. the black hole? + max_reposition_velocity_ratio: 0.25 # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1. + min_reposition_velocity_threshold_km_p_s: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1. + set_reposition_speed: 0 # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum? + reposition_coefficient_upsilon: 0.001 # Repositioning speed normalisation [km/s/M_sun]. Only meaningful if set_reposition_speed is 1. + reposition_exponent_xi: 1.0 # (Optional) Scaling of repositioning velocity with BH subgrid mass (default: 1.0, linear). Only meaningful if set_reposition_speed is 1. + threshold_major_merger: 0.333 # Mass ratio threshold to consider a BH merger as 'major' + threshold_minor_merger: 0.1 # Mass ratio threshold to consider a BH merger as 'minor' + merger_threshold_type: DynamicalEscapeVelocity # Type of velocity threshold for BH mergers (CircularVelocity as in EAGLE, EscapeVelocity, or DynamicalEscapeVelocity) + merger_max_distance_ratio: 3.0 # Maximal distance over which two BHs can merge, in units of the softening length. + AGN_use_deterministic_feedback: 1 # Deterministic (1) or stochastic (0) AGN feedback model + AGN_feedback_model: Isotropic # AGN feedback model (Isotropic or MinimumDistance) + minimum_timestep_yr: 1000.0 # Minimum time-step of black-hole particles + include_jets: 1 # Global switch whether to include jet feedback [1] or not [0]. + turn_off_radiative_feedback: 0 # Global switch whether to turn off radiative (thermal) feedback [1] or not [0]. This should only be used if 'include_jets' is set to 1, since we want feedback in some form or another. + alpha_acc: 0.1 # Viscous alpha of the subgrid accretion disks. Likely to be within the 0.1-0.3 range. The main effect is that it sets the transition accretion rate between the thin and thick disk, as dot(m) = 0.2 * alpha^2. + seed_spin: 0.01 # The (randomly-directed) black hole spin assigned to BHs when they are seeded. Should be strictly between 0 and 1. + AGN_jet_velocity_model: BlackHoleMass # How AGN jet velocities are calculated. If 'Constant', a single value is used. If 'BlackHoleMass', then an empirical relation between halo mass and black hole mass is used to calculate jet velocities. 'HaloMass' is currently not supported. + v_jet_km_p_s: 10000. # Jet velocity to use if 'AGN_jet_velocity_model' is 'Constant'. Units are km/s. + v_jet_cs_ratio: 10. # This sets the jet velocity to v_jet_cs_ratio times the sound speed of the hot gas of the parent halo the black hole is in. This is used if 'AGN_jet_velocity_model' is 'BlackHoleMass'. + v_jet_BH_mass_scaling_reference_mass_Msun: 3.4e3 # The reference mass used in the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'. + v_jet_BH_mass_scaling_slope: 0.65 # The slope of the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'. + v_jet_mass_loading: 400. # The constant mass loading to use if 'AGN_jet_velocity_model' is MassLoading. + v_jet_xi: 0.707 # The numerical multiplier by which the jet velocity formula is scaled, if 'AGN_jet_velocity_model' is 'Local' or 'SoundSpeed'. The appropriate values (to exactly obtain the formulas as derived) are 0.63 and 0.707 for the two, respectively. + v_jet_min_km_p_s: 500 # The minimal jet velocity. This is used if 'AGN_jet_velocity_model' is 'BlackHoleMass', 'MassLoading', 'Local' or 'SoundSpeed'. + opening_angle_in_degrees: 7.5 # The half-opening angle of the jet in degrees. Should use values < 15 unless for tests. + N_jet: 2 # Target number of particles to kick as part of a single jet feedback event. Should be a multiple of 2 to ensure approximate momentum conservation (we always kick particles in pairs, one from each 'side' of the BH, relative to the spin vector). + AGN_jet_feedback_model: MinimumDistance # Which particles to kick from the black hole smoothing kernels. Should be 'SpinAxis', 'MinimumDistance', 'MaximumDistance' or 'MinimumDensity' + eps_f_jet: 1. # Coupling efficiency for jet feedback. No reason to expect this to be less than 1. + fix_jet_efficiency: 0 # Global switch whether to fix jet efficiency to a particular value [1], or use a spin-dependant formula [0]. If used, jets will be launched exclusively along the z axis. Should be set to 1 only for tests. + jet_efficiency: 0.1 # The constant jet efficiency used if 'fix_jet_efficiency' is set to 1. + fix_radiative_efficiency: 0 # Global switch whether to fix the radiative efficiency to a particular value [1], or use a spin-dependant formula [0]. + radiative_efficiency: 0.1 # The constant jet efficiency used if 'fix_radiative_efficiency' is set to 1. Otherwise, this value is used to define the Eddington accretion rate. + TD_region: B # How to treat the subgrid accretion disk if it is thin, according to the Shakura & Sunyaev (1973) model. If set to B, region b will be used. If set to C, region c will be used. + include_GRMHD_spindown: 1 # Whether to include high jet spindown rates from GRMHD simulations [1], or use an analytical formula that assumes extraction of energy from the rotational mass/energy of the BH. + include_ADIOS_suppression: 0 # Whether to suppress the accretion rate in the fully thick disc regime [1] (Eddington rate below 0.2alpha^2) by the amount expected to be taken away by isotropic kinetic disk winds. + ADIOS_R_in: 30. # If include_ADIOS_accr_suppression is set to 1, this parameter controls the inner radius within which winds are not important. + ADIOS_s: 0.4 # Slope of the accretion rate - radius relationship if include_ADIOS_accr_suppression is set to 1. + turn_off_secondary_feedback: 1 # If set to 1, there will be only radiative (thermal) feedback in the thin disk mode, and only jets in the thick disk mode. + jet_h_r_slope: 1. # The slope of the dependence of jet efficiency on aspect ratio of the subgrid accretion disk, H/R. Default value is 1, and another reasonable value is 0 (same jet efficiency for all disks). Reality could be anything in between. This parameter is only used if turn_off_secondary_feedback is set to 0. + delta_ADAF: 0.2 # Electron heating parameter, which controls the strength of radiative feedback in thick disks. Should be between 0.1 and 0.5. This parameter is only used if turn_off_secondary_feedback is set to 0. + include_slim_disk: 0 # Global switch whether to include super-Eddington accretion, modeled as the slim disk. If set to 0, disks will be considered thin even at very large accretion rates. + TD_SD_eps_r_threshold: 0.75 # Parameter controlling the transition from thin to slim disk. Accretion disk will be slim if radiative efficiency satisfies eps_slim < TD_SD_eps_r_threshold * eps_thin. This parameter is only used if include_slim_disk is set to 1. + + # Parameters related to the neutrinos -------------------------------------------- Neutrino: use_delta_f: 1 # Use the delta-f method for shot noise reduction @@ -662,39 +749,140 @@ Neutrino: fixed_bg_density: 1 # For linear response neutrinos, whether to use a fixed present-day background density use_model_none: 0 # Option to use no neutrino model +# Parameters related to extra i/o (X-ray emmisivity) ---------------------------- + +XrayEmissivity: + xray_table_path: ./X_Ray_tables.hdf5 # Path to the X-ray emissivity tables + # Parameters related to the sink particles --------------------------------------- # Default sink particles DefaultSink: - cut_off_radius: 1e-3 # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution. + cut_off_radius: 1e-3 # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution. # GEAR sink particles GEARSink: - cut_off_radius: 1e-3 # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution. + cut_off_radius: 1e-3 # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution. # Parameters related to radiative transfer --------------------------------------- -# Debugging/Development RT scheme -DebugRT: - all_parts_have_stars: 0 # Set to 1 to do additional tests that only work if all hydro particles have a star particle neighbour - GEARRT: - f_reduce_c: 1e-3 # reduce the speed of light for the RT solver by multiplying c with this factor - CFL_condition: 0.9 # CFL condition for RT, independent of hydro - photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed. - use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter - star_emission_rates_LSol: [1., 1., 1., 1.] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity. - hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas - set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. - set_initial_ionization_mass_fractions: 0 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) - mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value - mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value - mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value - mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value - mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value - skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. - stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. - stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. - stellar_spectrum_blackbody_temperature_K: 1.e4 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. - stars_max_timestep: -1. # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. - + f_reduce_c: 1e-3 # reduce the speed of light for the RT solver by multiplying c with this factor + CFL_condition: 0.9 # CFL condition for RT, independent of hydro + f_limit_cooling_time: 0.6 # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off. + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N + stellar_luminosity_model: const # Which model to use to determine the stellar luminosities. + const_stellar_luminosities_LSol: [1., 1., 1.] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity. + hydrogen_mass_fraction: 0.76 # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas + set_equilibrium_initial_ionization_mass_fractions: 0 # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium. + set_initial_ionization_mass_fractions: 0 # (Optional) manually overwrite initial mass fraction of each species (using the values you set below) + mass_fraction_HI: 0.76 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value + mass_fraction_HII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value + mass_fraction_HeI: 0.24 # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value + mass_fraction_HeII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value + mass_fraction_HeIII: 0. # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value + skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + stellar_spectrum_blackbody_temperature_K: 1.e4 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + stars_max_timestep: -1. # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + grackle_verbose: 0 # (Optional) set grackle to verbose. (Default: 0) + case_B_recombination: 1 # (Optional) use case B recombination interaction rates. (Default: 1) + max_tchem_recursion: 0 # (Optional) if > 0, sets the maximal recursion depth when re-computing the thermochemistry if |u_new/u_old - 1| > 0.1. + +SPHM1RT: + cred: 2.99792458e10 # value of reduced speed of light for the RT solver in code unit + CFL_condition: 0.1 # CFL condition for RT, independent of hydro + chi: [0, 0, 0, 0] # (Optional) initial opacity in code unit for all gas particles + photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=SPHM1RT_N). Outer edges of zero and infinity are assumed. + use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates parameter + star_emission_rates: [1e-32, 1e-32, 1e-32, 1e-32] # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set. + stellar_spectrum_type: 0 # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum. + stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. + stars_max_timestep: -1. # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off. + stellar_spectrum_blackbody_temperature_K: 1.e4 # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. + skip_thermochemistry: 0 # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT. + init_mass_fraction_metal: 0. # (Optional) Inital mass fraction of particle mass in *all* metals (if it is set, the initial fraction will be over-written.) + init_mass_fraction_Hydrogen: 0.752 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Hydrogen + init_mass_fraction_Helium: 0.248 # (Conditional) (if init_mass_fraction_metal != -1.0f) Inital mass fraction of particle mass in Helium + useabundances: 0 # (Optional) use the species abundances below, instead of reading from initial condition + init_species_abundance_e: 4e-5 # (Conditional) (if useabundances==1) free electron abundances (in unit hydrogen number density:nH) + init_species_abundance_HI: 0.99999 # (Conditional) (if useabundances==1) HI abundances (in unit hydrogen number density:nH) + init_species_abundance_HII: 1e-5 # (Conditional) (if useabundances==1) HII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeI: 0.08242680851 # (Conditional) (if useabundances==1) HeI abundances (in unit hydrogen number density:nH) + init_species_abundance_HeII: 1e-5 # (Conditional) (if useabundances==1) HeII abundances (in unit hydrogen number density:nH) + init_species_abundance_HeIII: 1e-5 # (Conditional) (if useabundances==1) HeIII abundances (in unit hydrogen number density:nH) + relativeTolerance: 1e-3 # (Optional) Relative tolerance for SPHM1RT thermo-chemistry intergration + absoluteTolerance: 1e-10 # (Optional) Absolute tolerance for SPHM1RT thermo-chemistry integration + explicitTolerance: 0.1 # (Optional) Tolerance below which we use the explicit solution in SPHM1RT thermo-chemistry + ionizing_photon_energy_erg: [3.0208e-11, 5.61973e-11, 1.05154e-10] # (Optional) ionizing photon energy in erg averaged within frequency bins #note that we start from frequency bin 1 (instead of 0). + coolingon: 1 # (Optional) switch for cooling (and photoheating), but photo-ionization will be ongoing even if coolingon==0 + useparams: 0 # (Optional) switch to use thermo-chemistry parameters from the parameter file + fixphotondensity: 0 # (Optional) switch for fixing the photo-density + Fgamma_fixed_cgs: [0.0, 0.0, 0.0] # (Conditional) (if fixphotondensity=1) the photo-density values if the photo density is fixed + onthespot: 1 # (Optional) switch for the on the spot approximation + sigma_cross: [2.99e-18, 5.66e-19, 7.84e-20] # (Conditional) (if useparams=1) The cross section of ionizing photons for hydrogen (cm^2) + alphaA: 4.29e-13 # (Conditional) (if useparams=1) The case A recombination coefficient for hydrogen (cgs) + alphaB: 2.59e-13 # (Conditional) (if useparams=1) The case B recombination coefficient for hydrogen (cgs) + beta: 1.245e-15 # (Conditional) (if useparams=1) The collisional ionization coefficient for hydrogen (cgs) + reinject: 1 # (Optional) gather energy around injection radius and re-inject the energy + + +# Parameters related to power spectra -------------------------------------------- + +PowerSpectrum: + grid_side_length: 256 # Size of the grid used in power spectrum calculation. + num_folds: 6 # Number of foldings (1 means no foldings), determines the max k + fold_factor: 4 # (Optional) factor by which to reduce the box along each side each folding (default: 4) + window_order: 3 # (Optional) order of the mass assignment scheme (default: 3, TSC) + output_list_on: 0 # (Optional) Enable the output list + output_list: ./output_list_ps.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) + requested_spectra: ["matter-matter","cdm-cdm","starBH-starBH","gas-matter","pressure-pressure","matter-pressure", "neutrino0-neutrino1"] # Array of strings indicating which components should be correlated for power spectra + + +# Parameters related to lightcones ----------------------------------------------- +# Parameters in the LightconeCommon section apply to all lightcones but can be overridden in the LightconeX sections. +# Up to 8 Lightcone sections named Lightcone0 to Lightcone7 may be present. +LightconeCommon: + + subdir: lightcones # All lightcone output is written to this directory + buffer_chunk_size: 10000 # Particles and map updates are buffered in a linked list of chunks of this size + + z_range_for_DM: [0.0, 0.05] # Output redshift range for dark matter + z_range_for_Gas: [0.0, 0.05] # Output redshift range for gas + z_range_for_Stars: [0.0, 0.05] # Output redshift range for stars + z_range_for_BH: [0.0, 0.05] # Output redshift range for black holes + + max_particles_buffered: 100000 # Output particles if buffer size reaches this value + max_updates_buffered: 100000 # Flush map updates if buffer size reaches this value + hdf5_chunk_size: 16384 # Chunk size for HDF5 particle and healpix map datasets + + nside: 512 # Healpix resolution parameter + radius_file: ./shell_redshifts.txt # Redshifts of shells for healpix maps + max_map_update_send_size_mb: 16.0 # Apply map updates over mutliple iterations to limit memory overhead + map_names_file: ./map_types.txt # List of types of healpix maps to make + + distributed_maps: 1 # Split maps over multiple files (1) or use collective I/O to write one file (0) + + particles_lossy_compression: 0 # Apply lossy compression to lightcone particles + particles_gzip_level: 6 # Apply lossless (deflate) compression to lightcone particles + maps_gzip_level: 6 # Apply lossless (deflate) compression to healpix maps + +# Parameters specific to lightcone 0 - any lightcone parameters not found here are taken from LightconeCommon, above, +# except for 'enabled' and 'basename'. +Lightcone0: + enabled: 1 # Enable this lightcone + basename: lightcone0 # Base name of this lighcone's output files + observer_position: [35.561875, 35.561875, 35.561875] # Location of the observer in this lightcone + +# Parameters specific to lightcone 1 - any lightcone parameters not found here are taken from LightconeCommon, above, +# except for 'enabled' and 'basename'. +Lightcone1: + enabled: 1 + basename: lightcone1 + observer_position: [35.561875, 35.561875, 35.561875] + + gas_filtering_enabled: 1 # Enable filtering out of certain gas particles from particle outputs + min_z_for_gas_filtering: 0.025 # Filter gas particles above this redshift, output all below + min_temp_for_filtered_gas: 1.0e5 # Above min_z_for_gas_filtering only output gas with temperature above this + min_nh_for_filtered_gas: 1.0e-6 # Above min_z_for_gas_filtering only output gas with nh/(1+z)^4 above this diff --git a/format.sh b/format.sh index dd154609e51b801b8bfd3a3d2f5287246779dc86..6d7c710001f5d98de7b136d0aeb5239840b31465 100755 --- a/format.sh +++ b/format.sh @@ -1,13 +1,13 @@ #!/bin/bash # Clang format command, can be overridden using CLANG_FORMAT_CMD. -# We currrently use version 10.0 so any overrides should provide that. -clang=${CLANG_FORMAT_CMD:="clang-format-10"} +# We currrently use version 13.0 so any overrides should provide that. +clang=${CLANG_FORMAT_CMD:="clang-format-13"} # Formatting command cmd="$clang -style=file $(git ls-files | grep '\.[ch]$')" -# Test if `clang-format-10` works +# Test if `clang-format-13` works command -v $clang > /dev/null if [[ $? -ne 0 ]] then diff --git a/format_python.sh b/format_python.sh index 718895dd966c19baa0b7f9ce6a34e9f7495b929f..6447cf3f87099e274014f6285f6c7081f2872015 100755 --- a/format_python.sh +++ b/format_python.sh @@ -14,7 +14,7 @@ if [ ! -d black_formatting_env ] then echo "Formatting environment not found, installing it..." python3 -m venv black_formatting_env - ./black_formatting_env/bin/python3 -m pip install black==19.3b0 + ./black_formatting_env/bin/python3 -m pip install click==8.0.4 black==19.3b0 fi # Now we know exactly which black to use black="./black_formatting_env/bin/python3 -m black" diff --git a/m4/ax_lib_hdf5.m4 b/m4/ax_lib_hdf5.m4 index 918cc76d693dbccd18264c4661bc5ff13b773c72..23e1bca980909af9dfc70e776d633b0d33dd1d9e 100644 --- a/m4/ax_lib_hdf5.m4 +++ b/m4/ax_lib_hdf5.m4 @@ -263,8 +263,18 @@ HDF5 support is being disabled. -I*) echo $HDF5_CPPFLAGS | $GREP -e "$arg" 2>&1 >/dev/null \ || HDF5_CPPFLAGS="$HDF5_CPPFLAGS $arg" ;; - -L*) echo $HDF5_LDFLAGS | $GREP -e "$arg" 2>&1 >/dev/null \ - || HDF5_LDFLAGS="$HDF5_LDFLAGS $arg" + -L*) + case "$arg" in + */usr/lib) + dnl Skip system libraries which shouldn't be present. + ;; + */usr/lib64) + ;; + *) + echo $HDF5_LDFLAGS | $GREP -e "$arg" 2>&1 >/dev/null \ + || HDF5_LDFLAGS="$HDF5_LDFLAGS $arg" + ;; + esac ;; -l*) echo $HDF5_LIBS | $GREP -e "$arg" 2>&1 >/dev/null \ || HDF5_LIBS="$HDF5_LIBS $arg" @@ -292,7 +302,7 @@ HDF5 support is being disabled. AC_MSG_WARN([Unable to compile HDF5 test program]) fi dnl Look for HDF5's high level library - AC_HAVE_LIBRARY([hdf5_hl], [HDF5_LIBS="-lhdf5_hl $HDF5_LIBS"], [], []) + AC_CHECK_LIB([hdf5_hl],[main],[HDF5_LIBS="-lhdf5_hl $HDF5_LIBS"],[],[]) CC=$ax_lib_hdf5_save_CC CPPFLAGS=$ax_lib_hdf5_save_CPPFLAGS diff --git a/m4/ax_mpi.m4 b/m4/ax_mpi.m4 index 5b2322cb3c152c7a898792f2d0afa01ebb385828..7b6fb5e4297b384a9469d78c02e55d667da49e5d 100644 --- a/m4/ax_mpi.m4 +++ b/m4/ax_mpi.m4 @@ -67,7 +67,7 @@ AU_ALIAS([ACX_MPI], [AX_MPI]) AC_DEFUN([AX_MPI], [ -AC_PREREQ(2.50) dnl for AC_LANG_CASE +AC_PREREQ([2.69]) dnl for AC_LANG_CASE AC_LANG_CASE([C], [ AC_REQUIRE([AC_PROG_CC]) @@ -135,16 +135,16 @@ if test x = x"$MPILIBS"; then AC_CHECK_LIB(mpich, MPI_Init, [MPILIBS="-lmpich"]) fi -dnl We have to use AC_TRY_COMPILE and not AC_CHECK_HEADER because the +dnl We have to use AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[],[]) and not AC_CHECK_HEADER because the dnl latter uses $CPP, not $CC (which may be mpicc). AC_LANG_CASE([C], [if test x != x"$MPILIBS"; then AC_MSG_CHECKING([for mpi.h]) - AC_TRY_COMPILE([#include <mpi.h>],[],[AC_MSG_RESULT(yes)], [MPILIBS="" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <mpi.h>]], [[]])],[AC_MSG_RESULT(yes)],[MPILIBS="" AC_MSG_RESULT(no)]) fi], [C++], [if test x != x"$MPILIBS"; then AC_MSG_CHECKING([for mpi.h]) - AC_TRY_COMPILE([#include <mpi.h>],[],[AC_MSG_RESULT(yes)], [MPILIBS="" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <mpi.h>]], [[]])],[AC_MSG_RESULT(yes)],[MPILIBS="" AC_MSG_RESULT(no)]) fi], [Fortran 77], [if test x != x"$MPILIBS"; then diff --git a/m4/ax_openmp.m4 b/m4/ax_openmp.m4 new file mode 100644 index 0000000000000000000000000000000000000000..5d7469380f16fc6ca4b8d5ebf330aa81e12c0701 --- /dev/null +++ b/m4/ax_openmp.m4 @@ -0,0 +1,123 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_openmp.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_OPENMP([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro tries to find out how to compile programs that use OpenMP a +# standard API and set of compiler directives for parallel programming +# (see http://www-unix.mcs/) +# +# On success, it sets the OPENMP_CFLAGS/OPENMP_CXXFLAGS/OPENMP_F77FLAGS +# output variable to the flag (e.g. -omp) used both to compile *and* link +# OpenMP programs in the current language. +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. +# +# If you want to compile everything with OpenMP, you should set: +# +# CFLAGS="$CFLAGS $OPENMP_CFLAGS" +# #OR# CXXFLAGS="$CXXFLAGS $OPENMP_CXXFLAGS" +# #OR# FFLAGS="$FFLAGS $OPENMP_FFLAGS" +# +# (depending on the selected language). +# +# The user can override the default choice by setting the corresponding +# environment variable (e.g. OPENMP_CFLAGS). +# +# ACTION-IF-FOUND is a list of shell commands to run if an OpenMP flag is +# found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it is +# not found. If ACTION-IF-FOUND is not specified, the default action will +# define HAVE_OPENMP. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> +# Copyright (c) 2015 John W. Peterson <jwpeterson@gmail.com> +# Copyright (c) 2016 Nick R. Papior <nickpapior@gmail.com> +# +# 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 <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 +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 13 + +AC_DEFUN([AX_OPENMP], [ +AC_PREREQ([2.69]) dnl for _AC_LANG_PREFIX + +AC_CACHE_CHECK([for OpenMP flag of _AC_LANG compiler], ax_cv_[]_AC_LANG_ABBREV[]_openmp, [save[]_AC_LANG_PREFIX[]FLAGS=$[]_AC_LANG_PREFIX[]FLAGS +ax_cv_[]_AC_LANG_ABBREV[]_openmp=unknown +# Flags to try: -fopenmp (gcc), -mp (SGI & PGI), +# -qopenmp (icc>=15), -openmp (icc), +# -xopenmp (Sun), -omp (Tru64), +# -qsmp=omp (AIX), +# none +ax_openmp_flags="-qopenmp -fopenmp -openmp -mp -xopenmp -omp -qsmp=omp none" +if test "x$OPENMP_[]_AC_LANG_PREFIX[]FLAGS" != x; then + ax_openmp_flags="$OPENMP_[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flags" +fi +for ax_openmp_flag in $ax_openmp_flags; do + case $ax_openmp_flag in + none) []_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[] ;; + *) []_AC_LANG_PREFIX[]FLAGS="$save[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flag" ;; + esac + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +@%:@include <omp.h> + +static void +parallel_fill(int * data, int n) +{ + int i; +@%:@pragma omp parallel for + for (i = 0; i < n; ++i) + data[i] = i; +} + +int +main() +{ + int arr[100000]; + omp_set_num_threads(2); + parallel_fill(arr, 100000); + return 0; +} +]])],[ax_cv_[]_AC_LANG_ABBREV[]_openmp=$ax_openmp_flag; break],[]) +done +[]_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[]FLAGS +]) +if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" = "xunknown"; then + m4_default([$2],:) +else + if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" != "xnone"; then + OPENMP_[]_AC_LANG_PREFIX[]FLAGS=$ax_cv_[]_AC_LANG_ABBREV[]_openmp + fi + m4_default([$1], [AC_DEFINE(HAVE_OPENMP,1,[Define if OpenMP is enabled])]) +fi +])dnl AX_OPENMP diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 index 5fbf9fe0d68616042f87a8365190211cb8ccfbf1..9f35d139149f8d9bda17cddb730cd13bcf775465 100644 --- a/m4/ax_pthread.m4 +++ b/m4/ax_pthread.m4 @@ -14,20 +14,24 @@ # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) +# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is +# needed for multi-threaded programs (defaults to the value of CC +# respectively CXX otherwise). (This is necessary on e.g. AIX to use the +# special cc_r/CC_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" +# CXX="$PTHREAD_CXX" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to @@ -55,6 +59,7 @@ # # Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> # Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG> +# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl> # # 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 @@ -82,7 +87,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 24 +#serial 31 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ @@ -104,6 +109,7 @@ if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) @@ -123,10 +129,12 @@ fi # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. +# Create a list of thread flags to try. Items with a "," contain both +# C compiler flags (before ",") and linker flags (after ","). Other items +# starting with a "-" are C compiler flags, and remaining items are +# library names, except for "none" which indicates that we try without +# any flags at all, and "pthread-config" which is a program returning +# the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" @@ -194,14 +202,47 @@ case $host_os in # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). - ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" ;; esac +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + + # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) +# Note that for GCC and Clang -pthread generally implies -lpthread, +# except when -nostdlib is passed. +# This is problematic using libtool to build C++ shared libraries with pthread: +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 +# To solve this, first try -pthread together with -lpthread for GCC + AS_IF([test "x$GCC" = "xyes"], - [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) + +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first + +AS_IF([test "x$ax_pthread_clang" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread"]) + # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is @@ -224,25 +265,86 @@ AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) -# Are we compiling with Clang? -AC_CACHE_CHECK([whether $CC is Clang], - [ax_cv_PTHREAD_CLANG], - [ax_cv_PTHREAD_CLANG=no - # Note that Autoconf sets GCC=yes for Clang as well as GCC - if test "x$GCC" = "xyes"; then - AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], - [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ -# if defined(__clang__) && defined(__llvm__) - AX_PTHREAD_CC_IS_CLANG -# endif - ], - [ax_cv_PTHREAD_CLANG=yes]) - fi - ]) -ax_pthread_clang="$ax_cv_PTHREAD_CLANG" +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + *,*) + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` + AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h> +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void *some_global = NULL; + static void routine(void *a) + { + /* To avoid any unused-parameter or + unused-but-set-parameter warning. */ + some_global = a; + } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi -ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way @@ -261,11 +363,6 @@ if test "x$ax_pthread_clang" = "xyes"; then # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) - PTHREAD_CFLAGS="-pthread" - PTHREAD_LIBS= - - ax_pthread_ok=yes - # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused @@ -294,7 +391,7 @@ if test "x$ax_pthread_clang" = "xyes"; then # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' - ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do @@ -320,78 +417,7 @@ if test "x$ax_pthread_clang" = "xyes"; then fi # $ax_pthread_clang = yes -if test "x$ax_pthread_ok" = "xno"; then -for ax_pthread_try_flag in $ax_pthread_flags; do - - case $ax_pthread_try_flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -mt,pthread) - AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) - PTHREAD_CFLAGS="-mt" - PTHREAD_LIBS="-lpthread" - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) - PTHREAD_CFLAGS="$ax_pthread_try_flag" - ;; - pthread-config) - AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) - AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) - PTHREAD_LIBS="-l$ax_pthread_try_flag" - ;; - esac - - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h> -# if $ax_pthread_check_cond -# error "$ax_pthread_check_macro must be defined" -# endif - static void routine(void *a) { a = 0; } - static void *start_routine(void *a) { return a; }], - [pthread_t th; pthread_attr_t attr; - pthread_create(&th, 0, start_routine, 0); - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - AC_MSG_RESULT([$ax_pthread_ok]) - AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then @@ -438,7 +464,8 @@ if test "x$ax_pthread_ok" = "xyes"; then AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], - [[int i = PTHREAD_PRIO_INHERIT;]])], + [[int i = PTHREAD_PRIO_INHERIT; + return i;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) @@ -460,18 +487,28 @@ if test "x$ax_pthread_ok" = "xyes"; then [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], - [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], - [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + [ + AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"]) + AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])]) + ], + [ + AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC]) + AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])]) + ] + ) + ]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) +AC_SUBST([PTHREAD_CXX]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then diff --git a/m4/gv_find_library.m4 b/m4/gv_find_library.m4 new file mode 100644 index 0000000000000000000000000000000000000000..7c960f8c7a1cef63bdc124809961cc55be4f3939 --- /dev/null +++ b/m4/gv_find_library.m4 @@ -0,0 +1,120 @@ +# +# SYNOPSIS +# +# GV_FIND_LIBRARY(NAME, VARNAME, PKGNAME, LIBNAME, FUNC) +# +# NAME : Name to use in help strings +# VARNAME : Defines macro HAVE_VARNAME and shell variable USE_VARNAME +# if library found +# PKGNAME : package name used by pkg-config +# LIBNAME : Name used in the library file name +# FUNC : A function in the library +# +# DESCRIPTION +# +# Attempts to set up the specified library using pkg-config +# +# Uses pkg-config to try to find package PKGNAME. If successful defines +# preprocessor macro HAVE_$VARNAME and sets USE_$VARNAME = "yes", otherwise +# sets USE_$VARNAME = "no". +# +# Also sets ${VARNAME}_LIBS and ${VARNAME}_CFLAGS on success. +# +# LAST MODIFICATION +# +# 07/03/09 Version used in Gadgetviewer +# 02/02/21 Use export when temporarily setting PKG_CONFIG_PATH +# 08/02/22 Add pkg-config --libs output to $LIBS rather than $LDFLAGS when running AC_CHECK_LIB +# + +AC_DEFUN([GV_FIND_LIBRARY],[ + +# Allow user to enable/disable library +AC_ARG_WITH([$1], AS_HELP_STRING([--with-$1@<:@=PATH@:>@],[use the $1 library]), + [USE_$2=$withval ; GV_SPEC="yes" ],[USE_$2="check" ; GV_SPEC="no" ]) + +# Figure out if we have been given a path +if test $USE_$2 = "yes" ; then + GV_HAVE_$2_PATH="no" + GV_$2_PATH="" +elif test $USE_$2 = "no" ; then + GV_HAVE_$2_PATH="no" + GV_$2_PATH="" +elif test $USE_$2 = "check" ; then + GV_HAVE_$2_PATH="no" + GV_$2_PATH="" +else + GV_HAVE_$2_PATH="yes" + GV_$2_PATH=$USE_$2 +fi + +GV_FOUND="no" + +# Don't do anything if library has been disabled explicitly +if test $USE_$2 != "no" ; then + + # Add path to PKG_CONFIG_PATH if we have one + TMP_PKG_CONFIG_PATH=$PKG_CONFIG_PATH + if test $GV_HAVE_$2_PATH = "yes" ; then + export PKG_CONFIG_PATH=$GV_$2_PATH/pkgconfig/:$GV_$2_PATH/lib/pkgconfig/:$PKG_CONFIG_PATH + fi + + # Try to set it up with pkg-config. First check we have the macro... + GV_PKG="no" + m4_ifdef([PKG_CHECK_MODULES], + [PKG_CHECK_MODULES([$2], [$3], [GV_PKG="yes"], + [AC_MSG_WARN([Unable to find $1 with pkg-config, will try to link to it anyway...])])], + [AC_MSG_WARN([No PKG_CHECK_MODULES macro, trying without pkg-config support])]) + + # Restore original PKG_CONFIG_PATH + export PKG_CONFIG_PATH=$TMP_PKG_CONFIG_PATH + + # If that didn't work and flags haven't been supplied by hand but we have a path, try sensible defaults + if test ${GV_PKG} = "no" ; then + if test ${GV_HAVE_$2_PATH} = "yes" ; then + # CFLAGS + if test X${$2_CFLAGS} = X ; then + $2_CFLAGS=-I${GV_$2_PATH}/include/ + fi + # LIBS + if test X${$2_LIBS} = X ; then + $2_LIBS="-L${GV_$2_PATH}/lib/" + fi + fi + fi + + # Try to link to the library + TMP_LIBS=$LIBS + LIBS="${$2_LIBS} ${LIBS}" + AC_CHECK_LIB([$4], [$5], [GV_FOUND="yes"], + [AC_MSG_WARN([Unable to link to $1 library. See config.log for details])]) + LIBS=$TMP_LIBS + + if test $GV_FOUND = "no" ; then + # If we can't link the library and it was explicitly asked for, abort + if test $GV_SPEC = "yes" ; then + AC_MSG_ERROR([Unable to link to requested library: $1]) + fi + # If the test failed, don't set flags + $2_LIBS="" + $2_CFLAGS="" + else + # If the test worked make sure -lwhatever is included if we didn't + # use pkg-config + if test $GV_PKG = "no" ; then + $2_LIBS="${$2_LIBS} -l$4" + fi + fi + + AC_SUBST($2_LIBS) + AC_SUBST($2_CFLAGS) + +fi + +# Set shell variable and define macro with test result +USE_$2=$GV_FOUND +if test $GV_FOUND = "yes" ; then + AC_DEFINE([HAVE_$2],[],[Defined if we have $2]) +fi + +]) diff --git a/src/Makefile.am b/src/Makefile.am index 3030502a30abaa325ad65c4efefb6168bac3eae9..80a24fd178407b88df97c52b6fb4580a08a24abf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ # This file is part of SWIFT. # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), -# Matthieu Schaller (matthieu.schaller@durham.ac.uk). +# 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 General Public License as published by @@ -16,7 +16,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Add the non-standard paths to the included library headers -AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS) $(OPENMP_CFLAGS) +AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS) $(SUNDIALS_INCS) $(OPENMP_CFLAGS) $(CHEALPIX_CFLAGS) # Assign a "safe" version number AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) @@ -25,7 +25,7 @@ AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) GIT_CMD = @GIT_CMD@ # Additional dependencies for shared libraries. -EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) +EXTRA_LIBS = $(GSL_LIBS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(SUNDIALS_LIBS) $(CHEALPIX_LIBS) # MPI libraries. MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS) @@ -42,31 +42,46 @@ endif # List required headers include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h -include_HEADERS += cell_hydro.h cell_stars.h cell_grav.h cell_sinks.h cell_black_holes.h +include_HEADERS += cell_hydro.h cell_stars.h cell_grav.h cell_sinks.h cell_black_holes.h cell_rt.h include_HEADERS += engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h include_HEADERS += common_io.h single_io.h distributed_io.h map.h tools.h partition_fixed_costs.h include_HEADERS += partition.h clocks.h parser.h physical_constants.h physical_constants_cgs.h potential.h version.h -include_HEADERS += hydro_properties.h riemann.h threadpool.h cooling_io.h cooling.h cooling_struct.h cooling_properties.h -include_HEADERS += statistics.h memswap.h cache.h runner_doiact_hydro_vec.h profiler.h entropy_floor.h +include_HEADERS += hydro_properties.h riemann.h threadpool.h cooling_io.h cooling.h cooling_struct.h cooling_properties.h cooling_debug.h +include_HEADERS += statistics.h memswap.h cache.h runner_doiact_hydro_vec.h runner_doiact_undef.h profiler.h entropy_floor.h include_HEADERS += csds.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h include_HEADERS += gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h -include_HEADERS += chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h space_getsid.h utilities.h +include_HEADERS += chemistry.h chemistry_io.h chemistry_struct.h chemistry_debug.h cosmology.h restart.h space_getsid.h utilities.h include_HEADERS += cbrt.h exp10.h velociraptor_interface.h swift_velociraptor_part.h output_list.h -include_HEADERS += csds_io.h tracers_io.h tracers.h tracers_struct.h star_formation_io.h +include_HEADERS += csds_io.h +include_HEADERS += tracers_io.h tracers.h tracers_triggers.h tracers_struct.h tracers_debug.h +include_HEADERS += star_formation_io.h star_formation_debug.h extra_io.h include_HEADERS += fof.h fof_struct.h fof_io.h fof_catalogue_io.h include_HEADERS += multipole.h multipole_accept.h multipole_struct.h binomial.h integer_power.h sincos.h include_HEADERS += star_formation_struct.h star_formation.h star_formation_iact.h include_HEADERS += star_formation_logger.h star_formation_logger_struct.h -include_HEADERS += pressure_floor.h pressure_floor_struct.h pressure_floor_iact.h +include_HEADERS += pressure_floor.h pressure_floor_struct.h pressure_floor_iact.h pressure_floor_debug.h include_HEADERS += velociraptor_struct.h velociraptor_io.h random.h memuse.h mpiuse.h memuse_rnodes.h -include_HEADERS += black_holes.h black_holes_io.h black_holes_properties.h black_holes_struct.h -include_HEADERS += feedback.h feedback_struct.h feedback_properties.h +include_HEADERS += black_holes.h black_holes_iact.h black_holes_io.h black_holes_properties.h black_holes_struct.h black_holes_debug.h +include_HEADERS += feedback.h feedback_new_stars.h feedback_struct.h feedback_properties.h feedback_debug.h feedback_iact.h include_HEADERS += space_unique_id.h line_of_sight.h io_compression.h include_HEADERS += rays.h rays_struct.h +include_HEADERS += sink.h sink_struct.h sink_io.h sink_properties.h sink_debug.h include_HEADERS += particle_splitting.h particle_splitting_struct.h include_HEADERS += chemistry_csds.h star_formation_csds.h include_HEADERS += mesh_gravity.h mesh_gravity_mpi.h mesh_gravity_patch.h mesh_gravity_sort.h row_major_id.h -include_HEADERS += hdf5_object_to_blob.h ic_info.h +include_HEADERS += hdf5_object_to_blob.h ic_info.h particle_buffer.h exchange_structs.h +include_HEADERS += lightcone/lightcone.h lightcone/lightcone_particle_io.h lightcone/lightcone_replications.h +include_HEADERS += lightcone/lightcone_crossing.h lightcone/lightcone_array.h lightcone/lightcone_map.h +include_HEADERS += lightcone/lightcone_map_types.h lightcone/projected_kernel.h lightcone/lightcone_shell.h +include_HEADERS += lightcone/healpix_util.h lightcone/pixel_index.h +include_HEADERS += power_spectrum.h +include_HEADERS += ghost_stats.h + +# source files for EAGLE extra I/O +EAGLE_EXTRA_IO_SOURCES= +if HAVEEAGLEEXTRAIO +EAGLE_EXTRA_IO_SOURCES += extra_io/EAGLE/extra_lightcone_map_types.c +endif # source files for QLA (Ploeckinger+20) cooling QLA_COOLING_SOURCES = @@ -114,6 +129,19 @@ GEAR_FEEDBACK_SOURCES += feedback/GEAR/stellar_evolution.c feedback/GEAR/feedbac feedback/GEAR/initial_mass_function.c feedback/GEAR/supernovae_ia.c feedback/GEAR/supernovae_ii.c endif +# source files for GEAR RT +GEAR_RT_SOURCES = +if HAVEGEARRT +GEAR_RT_SOURCES += rt/GEAR/rt_interaction_cross_sections.c +endif + +# source files for SPHM1RT cooling +SPHM1RT_RT_SOURCES = +if HAVESPHM1RTRT +SPHM1RT_RT_SOURCES += rt/SPHM1RT/rt_rate_equations.c +SPHM1RT_RT_SOURCES += rt/SPHM1RT/rt_cooling.c +endif + # Common source files AM_SOURCES = space.c space_rebuild.c space_regrid.c space_unique_id.c AM_SOURCES += space_sort.c space_split.c space_extras.c space_first_init.c space_init.c @@ -122,8 +150,8 @@ AM_SOURCES += runner_main.c runner_doiact_hydro.c runner_doiact_limiter.c AM_SOURCES += runner_doiact_stars.c runner_doiact_black_holes.c runner_ghost.c AM_SOURCES += runner_recv.c runner_pack.c AM_SOURCES += runner_sort.c runner_drift.c runner_black_holes.c runner_time_integration.c -AM_SOURCES += runner_doiact_hydro_vec.c runner_others.c runner_doiact_sinks.c -AM_SOURCES += runner_doiact_rt.c runner_doiact_sinks_merger.c +AM_SOURCES += runner_doiact_hydro_vec.c runner_others.c +AM_SOURCES += runner_sinks.c AM_SOURCES += cell.c cell_convert_part.c cell_drift.c cell_lock.c cell_pack.c cell_split.c AM_SOURCES += cell_unskip.c AM_SOURCES += engine.c engine_maketasks.c engine_split_particles.c engine_strays.c @@ -143,16 +171,23 @@ AM_SOURCES += collectgroup.c hydro_space.c equation_of_state.c io_compression.c AM_SOURCES += chemistry.c cosmology.c velociraptor_interface.c AM_SOURCES += output_list.c velociraptor_dummy.c csds_io.c memuse.c mpiuse.c memuse_rnodes.c AM_SOURCES += fof.c fof_catalogue_io.c -AM_SOURCES += hashmap.c pressure_floor.c +AM_SOURCES += hashmap.c AM_SOURCES += mesh_gravity.c mesh_gravity_mpi.c mesh_gravity_patch.c mesh_gravity_sort.c AM_SOURCES += runner_neutrino.c AM_SOURCES += neutrino/Default/fermi_dirac.c neutrino/Default/neutrino.c neutrino/Default/neutrino_response.c -AM_SOURCES += rt_parameters.c -AM_SOURCES += hdf5_object_to_blob.c ic_info.c +AM_SOURCES += rt_parameters.c hdf5_object_to_blob.c ic_info.c exchange_structs.c particle_buffer.c +AM_SOURCES += lightcone/lightcone.c lightcone/lightcone_particle_io.c lightcone/lightcone_replications.c +AM_SOURCES += lightcone/healpix_util.c lightcone/lightcone_array.c lightcone/lightcone_map.c +AM_SOURCES += lightcone/lightcone_map_types.c lightcone/projected_kernel.c lightcone/lightcone_shell.c +AM_SOURCES += power_spectrum.c +AM_SOURCES += ghost_stats.c +AM_SOURCES += $(EAGLE_EXTRA_IO_SOURCES) AM_SOURCES += $(QLA_COOLING_SOURCES) $(QLA_EAGLE_COOLING_SOURCES) AM_SOURCES += $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES) AM_SOURCES += $(GRACKLE_COOLING_SOURCES) $(GEAR_FEEDBACK_SOURCES) AM_SOURCES += $(COLIBRE_COOLING_SOURCES) +AM_SOURCES += $(SPHM1RT_RT_SOURCES) +AM_SOURCES += $(GEAR_RT_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 @@ -160,8 +195,7 @@ nobase_noinst_HEADERS += gravity_iact.h kernel_long_gravity.h vector.h accumulat nobase_noinst_HEADERS += runner_doiact_nosort.h runner_doiact_hydro.h runner_doiact_stars.h runner_doiact_black_holes.h runner_doiact_grav.h nobase_noinst_HEADERS += runner_doiact_functions_hydro.h runner_doiact_functions_stars.h runner_doiact_functions_black_holes.h nobase_noinst_HEADERS += runner_doiact_functions_limiter.h runner_doiact_limiter.h units.h intrinsics.h minmax.h -nobase_noinst_HEADERS += runner_doiact_rt.h runner_doiact_functions_rt.h runner_doiact_sinks.h runner_doiact_functions_sinks.h -nobase_noinst_HEADERS += runner_doiact_sinks_merger.h runner_doiact_functions_sinks_merger.h +nobase_noinst_HEADERS += runner_doiact_sinks.h nobase_noinst_HEADERS += kick.h timestep.h drift.h adiabatic_index.h io_properties.h dimension.h part_type.h periodic.h memswap.h nobase_noinst_HEADERS += timestep_limiter.h timestep_limiter_iact.h timestep_sync.h timestep_sync_part.h timestep_limiter_struct.h nobase_noinst_HEADERS += csds.h sign.h csds_io.h hashmap.h gravity.h gravity_io.h gravity_csds.h gravity_cache.h output_options.h @@ -171,7 +205,8 @@ nobase_noinst_HEADERS += gravity/MultiSoftening/gravity.h gravity/MultiSoftening nobase_noinst_HEADERS += gravity/MultiSoftening/gravity_debug.h gravity/MultiSoftening/gravity_part.h nobase_noinst_HEADERS += gravity/MultiSoftening/gravity_csds.h nobase_noinst_HEADERS += equation_of_state.h -nobase_noinst_HEADERS += equation_of_state/ideal_gas/equation_of_state.h equation_of_state/isothermal/equation_of_state.h +nobase_noinst_HEADERS += equation_of_state/ideal_gas/equation_of_state.h equation_of_state/isothermal/equation_of_state.h +nobase_noinst_HEADERS += signal_velocity.h nobase_noinst_HEADERS += hydro.h hydro_io.h hydro_csds.h hydro_parameters.h nobase_noinst_HEADERS += hydro/None/hydro.h hydro/None/hydro_iact.h hydro/None/hydro_io.h nobase_noinst_HEADERS += hydro/None/hydro_debug.h hydro/None/hydro_part.h @@ -244,11 +279,12 @@ nobase_noinst_HEADERS += hydro/Shadowswift/voronoi3d_cell.h nobase_noinst_HEADERS += hydro/Shadowswift/voronoi_algorithm.h nobase_noinst_HEADERS += hydro/Shadowswift/voronoi_cell.h nobase_noinst_HEADERS += hydro/Shadowswift/hydro_parameters.h +nobase_noinst_HEADERS += mhd.h mhd_struct.h mhd_io.h +nobase_noinst_HEADERS += mhd/None/mhd.h mhd/None/mhd_iact.h mhd/None/mhd_struct.h mhd/None/mhd_io.h mhd/None/mhd_debug.h mhd/None/mhd_parameters.h nobase_noinst_HEADERS += riemann/riemann_hllc.h riemann/riemann_trrs.h nobase_noinst_HEADERS += riemann/riemann_exact.h riemann/riemann_vacuum.h nobase_noinst_HEADERS += riemann/riemann_checks.h nobase_noinst_HEADERS += rt.h -nobase_noinst_HEADERS += rt_active.h nobase_noinst_HEADERS += rt_additions.h nobase_noinst_HEADERS += rt_io.h nobase_noinst_HEADERS += rt_parameters.h @@ -269,33 +305,48 @@ nobase_noinst_HEADERS += rt/debug/rt_iact.h nobase_noinst_HEADERS += rt/debug/rt_io.h nobase_noinst_HEADERS += rt/debug/rt_parameters.h nobase_noinst_HEADERS += rt/debug/rt_properties.h -nobase_noinst_HEADERS += rt/debug/rt_stellar_emission_rate.h nobase_noinst_HEADERS += rt/debug/rt_struct.h -nobase_noinst_HEADERS += rt/debug/rt_thermochemistry.h -nobase_noinst_HEADERS += rt/GEAR/rt.h nobase_noinst_HEADERS += rt/GEAR/rt_additions.h nobase_noinst_HEADERS += rt/GEAR/rt_blackbody.h nobase_noinst_HEADERS += rt/GEAR/rt_debugging.h nobase_noinst_HEADERS += rt/GEAR/rt_flux.h nobase_noinst_HEADERS += rt/GEAR/rt_getters.h +nobase_noinst_HEADERS += rt/GEAR/rt_grackle_utils.h nobase_noinst_HEADERS += rt/GEAR/rt_gradients.h +nobase_noinst_HEADERS += rt/GEAR/rt.h nobase_noinst_HEADERS += rt/GEAR/rt_iact.h +nobase_noinst_HEADERS += rt/GEAR/rt_interaction_cross_sections.h nobase_noinst_HEADERS += rt/GEAR/rt_interaction_rates.h nobase_noinst_HEADERS += rt/GEAR/rt_io.h nobase_noinst_HEADERS += rt/GEAR/rt_ionization_equilibrium.h nobase_noinst_HEADERS += rt/GEAR/rt_parameters.h nobase_noinst_HEADERS += rt/GEAR/rt_properties.h nobase_noinst_HEADERS += rt/GEAR/rt_riemann_GLF.h -nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL.h nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL_eigenvalues.h -# nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_cell.h +nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL.h +nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_cell.h nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_face.h -nobase_noinst_HEADERS += rt/GEAR/rt_stellar_emission_rate.h +nobase_noinst_HEADERS += rt/GEAR/rt_species.h +nobase_noinst_HEADERS += rt/GEAR/rt_stellar_emission_model.h nobase_noinst_HEADERS += rt/GEAR/rt_struct.h nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry.h nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry_utils.h nobase_noinst_HEADERS += rt/GEAR/rt_unphysical.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_getters.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_setters.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_iact.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_io.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_parameters.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_properties.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_struct.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_gradients.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_additions.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_cooling.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_cooling_rates.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_unphysical.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_species_and_elements.h +nobase_noinst_HEADERS += rt/SPHM1RT/rt_stellar_emission_rate.h nobase_noinst_HEADERS += stars.h stars_io.h stars_csds.h nobase_noinst_HEADERS += stars/None/stars.h stars/None/stars_iact.h stars/None/stars_io.h nobase_noinst_HEADERS += stars/None/stars_debug.h stars/None/stars_part.h @@ -310,76 +361,86 @@ nobase_noinst_HEADERS += potential/isothermal/potential.h potential/disc_patch/p nobase_noinst_HEADERS += potential/sine_wave/potential.h potential/constant/potential.h nobase_noinst_HEADERS += potential/hernquist/potential.h potential/nfw/potential.h nobase_noinst_HEADERS += potential/nfw_mn/potential.h potential/point_mass_softened/potential.h -nobase_noinst_HEADERS += potential/point_mass_ring/potential.h nobase_noinst_HEADERS += star_formation/none/star_formation.h star_formation/none/star_formation_struct.h nobase_noinst_HEADERS += star_formation/none/star_formation_io.h star_formation/none/star_formation_iact.h -nobase_noinst_HEADERS += star_formation/none/star_formation_csds.h +nobase_noinst_HEADERS += star_formation/none/star_formation_csds.h star_formation/none/star_formation_debug.h nobase_noinst_HEADERS += star_formation/QLA/star_formation.h star_formation/QLA/star_formation_struct.h nobase_noinst_HEADERS += star_formation/QLA/star_formation_io.h star_formation/QLA/star_formation_iact.h +nobase_noinst_HEADERS += star_formation/QLA/star_formation_debug.h nobase_noinst_HEADERS += star_formation/EAGLE/star_formation.h star_formation/EAGLE/star_formation_struct.h nobase_noinst_HEADERS += star_formation/EAGLE/star_formation_io.h star_formation/EAGLE/star_formation_iact.h +nobase_noinst_HEADERS += star_formation/EAGLE/star_formation_debug.h nobase_noinst_HEADERS += star_formation/GEAR/star_formation.h star_formation/GEAR/star_formation_struct.h nobase_noinst_HEADERS += star_formation/GEAR/star_formation_io.h star_formation/GEAR/star_formation_iact.h -nobase_noinst_HEADERS += star_formation/GEAR/star_formation_csds.h +nobase_noinst_HEADERS += star_formation/GEAR/star_formation_csds.h star_formation/GEAR/star_formation_debug.h nobase_noinst_HEADERS += star_formation/EAGLE/star_formation_logger.h star_formation/EAGLE/star_formation_logger_struct.h nobase_noinst_HEADERS += star_formation/GEAR/star_formation_logger.h star_formation/GEAR/star_formation_logger_struct.h nobase_noinst_HEADERS += star_formation/none/star_formation_logger.h star_formation/none/star_formation_logger_struct.h nobase_noinst_HEADERS += cooling/none/cooling.h cooling/none/cooling_struct.h -nobase_noinst_HEADERS += cooling/none/cooling_io.h cooling/none/cooling_properties.h +nobase_noinst_HEADERS += cooling/none/cooling_io.h cooling/none/cooling_properties.h cooling/none/cooling_debug.h nobase_noinst_HEADERS += cooling/const_du/cooling.h cooling/const_du/cooling_struct.h -nobase_noinst_HEADERS += cooling/const_du/cooling_io.h cooling/const_du/cooling_properties.h +nobase_noinst_HEADERS += cooling/const_du/cooling_io.h cooling/const_du/cooling_properties.h cooling/const_du/cooling_debug.h nobase_noinst_HEADERS += cooling/const_lambda/cooling.h cooling/const_lambda/cooling_struct.h -nobase_noinst_HEADERS += cooling/const_lambda/cooling_io.h cooling/const_lambda/cooling_properties.h +nobase_noinst_HEADERS += cooling/const_lambda/cooling_io.h cooling/const_lambda/cooling_properties.h cooling/const_lambda/cooling_debug.h nobase_noinst_HEADERS += cooling/grackle/cooling.h cooling/grackle/cooling_struct.h -nobase_noinst_HEADERS += cooling/grackle/cooling_io.h cooling/grackle/cooling_properties.h +nobase_noinst_HEADERS += cooling/grackle/cooling_io.h cooling/grackle/cooling_properties.h cooling/grackle/cooling_debug.h nobase_noinst_HEADERS += cooling/EAGLE/cooling.h cooling/EAGLE/cooling_struct.h cooling/EAGLE/cooling_tables.h nobase_noinst_HEADERS += cooling/EAGLE/cooling_io.h cooling/EAGLE/interpolate.h cooling/EAGLE/cooling_rates.h -nobase_noinst_HEADERS += cooling/EAGLE/cooling_properties.h +nobase_noinst_HEADERS += cooling/EAGLE/cooling_properties.h cooling/EAGLE/cooling_debug.h nobase_noinst_HEADERS += cooling/QLA_EAGLE/cooling.h cooling/QLA_EAGLE/cooling_struct.h cooling/QLA_EAGLE/cooling_tables.h nobase_noinst_HEADERS += cooling/QLA_EAGLE/cooling_io.h cooling/QLA_EAGLE/interpolate.h cooling/QLA_EAGLE/cooling_rates.h -nobase_noinst_HEADERS += cooling/QLA_EAGLE/cooling_properties.h +nobase_noinst_HEADERS += cooling/QLA_EAGLE/cooling_properties.h cooling/QLA_EAGLE/cooling_debug.h nobase_noinst_HEADERS += cooling/QLA/cooling.h cooling/QLA/cooling_struct.h cooling/QLA/cooling_tables.h nobase_noinst_HEADERS += cooling/QLA/cooling_io.h cooling/QLA/interpolate.h cooling/QLA/cooling_rates.h -nobase_noinst_HEADERS += cooling/QLA/cooling_properties.h +nobase_noinst_HEADERS += cooling/QLA/cooling_properties.h cooling/QLA/cooling_debug.h nobase_noinst_HEADERS += cooling/COLIBRE/cooling.h cooling/COLIBRE/cooling_struct.h cooling/COLIBRE/cooling_subgrid.h nobase_noinst_HEADERS += cooling/COLIBRE/cooling_io.h cooling/COLIBRE/interpolate.h cooling/COLIBRE/cooling_rates.h nobase_noinst_HEADERS += cooling/COLIBRE/cooling_tables.h cooling/COLIBRE/cooling_subgrid.h -nobase_noinst_HEADERS += cooling/COLIBRE/cooling_properties.h +nobase_noinst_HEADERS += cooling/COLIBRE/cooling_properties.h cooling/COLIBRE/cooling_debug.h nobase_noinst_HEADERS += chemistry/none/chemistry.h nobase_noinst_HEADERS += chemistry/none/chemistry_io.h nobase_noinst_HEADERS += chemistry/none/chemistry_csds.h nobase_noinst_HEADERS += chemistry/none/chemistry_struct.h nobase_noinst_HEADERS += chemistry/none/chemistry_iact.h +nobase_noinst_HEADERS += chemistry/none/chemistry_debug.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry_io.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry_csds.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry_struct.h nobase_noinst_HEADERS += chemistry/GEAR/chemistry_iact.h +nobase_noinst_HEADERS += chemistry/GEAR/chemistry_debug.h nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry.h nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry_io.h nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry_struct.h nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry_iact.h +nobase_noinst_HEADERS += chemistry/GEAR_DIFFUSION/chemistry_debug.h nobase_noinst_HEADERS += chemistry/EAGLE/chemistry.h nobase_noinst_HEADERS += chemistry/EAGLE/chemistry_io.h nobase_noinst_HEADERS += chemistry/EAGLE/chemistry_struct.h nobase_noinst_HEADERS += chemistry/EAGLE/chemistry_iact.h +nobase_noinst_HEADERS += chemistry/EAGLE/chemistry_debug.h nobase_noinst_HEADERS += chemistry/QLA/chemistry.h nobase_noinst_HEADERS += chemistry/QLA/chemistry_io.h nobase_noinst_HEADERS += chemistry/QLA/chemistry_struct.h nobase_noinst_HEADERS += chemistry/QLA/chemistry_iact.h +nobase_noinst_HEADERS += chemistry/QLA/chemistry_debug.h nobase_noinst_HEADERS += entropy_floor/none/entropy_floor.h nobase_noinst_HEADERS += entropy_floor/EAGLE/entropy_floor.h nobase_noinst_HEADERS += entropy_floor/QLA/entropy_floor.h nobase_noinst_HEADERS += tracers/none/tracers.h tracers/none/tracers_struct.h -nobase_noinst_HEADERS += tracers/none/tracers_io.h +nobase_noinst_HEADERS += tracers/none/tracers_io.h tracers/none/tracers_debug.h nobase_noinst_HEADERS += tracers/EAGLE/tracers.h tracers/EAGLE/tracers_struct.h -nobase_noinst_HEADERS += tracers/EAGLE/tracers_io.h +nobase_noinst_HEADERS += tracers/EAGLE/tracers_io.h tracers/EAGLE/tracers_debug.h +nobase_noinst_HEADERS += extra_io/EAGLE/extra_io.h extra_io/EAGLE/extra.h nobase_noinst_HEADERS += feedback/none/feedback.h feedback/none/feedback_struct.h feedback/none/feedback_iact.h -nobase_noinst_HEADERS += feedback/none/feedback_properties.h +nobase_noinst_HEADERS += feedback/none/feedback_properties.h feedback/none/feedback.h +nobase_noinst_HEADERS += feedback/none/feedback_debug.h nobase_noinst_HEADERS += feedback/EAGLE_kinetic/feedback.h feedback/EAGLE_kinetic/feedback_struct.h nobase_noinst_HEADERS += feedback/EAGLE_kinetic/feedback_properties.h feedback/EAGLE_kinetic/feedback_iact.h +nobase_noinst_HEADERS += feedback/EAGLE_kinetic/feedback_debug.h nobase_noinst_HEADERS += feedback/EAGLE_thermal/feedback.h feedback/EAGLE_thermal/feedback_struct.h nobase_noinst_HEADERS += feedback/EAGLE_thermal/feedback_properties.h feedback/EAGLE_thermal/feedback_iact.h +nobase_noinst_HEADERS += feedback/EAGLE_thermal/feedback_debug.h nobase_noinst_HEADERS += feedback/EAGLE/yield_tables.h feedback/EAGLE/imf.h feedback/EAGLE/interpolate.h nobase_noinst_HEADERS += feedback/EAGLE/enrichment.h nobase_noinst_HEADERS += feedback/GEAR/stellar_evolution_struct.h feedback/GEAR/stellar_evolution.h @@ -387,20 +448,29 @@ nobase_noinst_HEADERS += feedback/GEAR/feedback.h feedback/GEAR/feedback_iact.h nobase_noinst_HEADERS += feedback/GEAR/feedback_properties.h feedback/GEAR/feedback_struct.h nobase_noinst_HEADERS += feedback/GEAR/initial_mass_function.h feedback/GEAR/supernovae_ia.h feedback/GEAR/supernovae_ii.h nobase_noinst_HEADERS += feedback/GEAR/lifetime.h feedback/GEAR/hdf5_functions.h feedback/GEAR/interpolation.h +nobase_noinst_HEADERS += feedback/GEAR/feedback_debug.h nobase_noinst_HEADERS += black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h nobase_noinst_HEADERS += black_holes/Default/black_holes_part.h black_holes/Default/black_holes_iact.h nobase_noinst_HEADERS += black_holes/Default/black_holes_properties.h -nobase_noinst_HEADERS += black_holes/Default/black_holes_struct.h +nobase_noinst_HEADERS += black_holes/Default/black_holes_struct.h +nobase_noinst_HEADERS += black_holes/Default/black_holes_debug.h nobase_noinst_HEADERS += black_holes/EAGLE/black_holes.h black_holes/EAGLE/black_holes_io.h nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_part.h black_holes/EAGLE/black_holes_iact.h nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_properties.h black_holes/EAGLE/black_holes_parameters.h -nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_struct.h +nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_struct.h black_holes/EAGLE/black_holes_debug.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes.h black_holes/SPIN_JET/black_holes_io.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_part.h black_holes/SPIN_JET/black_holes_iact.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_properties.h black_holes/SPIN_JET/black_holes_parameters.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_spin.h black_holes/SPIN_JET/black_holes_struct.h +nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_debug.h nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor.h pressure_floor/none/pressure_floor.h nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_iact.h pressure_floor/none/pressure_floor_iact.h nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_struct.h pressure_floor/none/pressure_floor_struct.h +nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_debug.h pressure_floor/none/pressure_floor_debug.h nobase_noinst_HEADERS += sink/Default/sink.h sink/Default/sink_io.h sink/Default/sink_part.h sink/Default/sink_properties.h -nobase_noinst_HEADERS += sink/Default/sink_iact.h -nobase_noinst_HEADERS += sink.h sink_io.h sink_properties.h +nobase_noinst_HEADERS += sink/Default/sink_iact.h sink/Default/sink_struct.h sink/Default/sink_debug.h +nobase_noinst_HEADERS += sink/GEAR/sink.h sink/GEAR/sink_io.h sink/GEAR/sink_part.h sink/GEAR/sink_properties.h +nobase_noinst_HEADERS += sink/GEAR/sink_iact.h sink/GEAR/sink_struct.h sink/GEAR/sink_debug.h nobase_noinst_HEADERS += neutrino.h neutrino_properties.h neutrino_io.h nobase_noinst_HEADERS += neutrino/Default/neutrino.h neutrino/Default/relativity.h neutrino/Default/fermi_dirac.h nobase_noinst_HEADERS += neutrino/Default/neutrino_properties.h neutrino/Default/neutrino_io.h diff --git a/src/accumulate.h b/src/accumulate.h index 877ac23baa1e9041c45a8d3dcc10a1d660ea6c68..f5a87e86cd29bf2f78261b1192f2c47eb9162938 100644 --- a/src/accumulate.h +++ b/src/accumulate.h @@ -20,7 +20,7 @@ #define SWIFT_ACCUMULATE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "atomic.h" diff --git a/src/active.h b/src/active.h index 9cbc7ade0052194c0c15eebadaf7a7a3dbb4cf04..3150a13a4e426ac6240e1547aeedbecdc5a2a874 100644 --- a/src/active.h +++ b/src/active.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,12 +20,12 @@ #define SWIFT_ACTIVE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "cell.h" #include "engine.h" -#include "feedback.h" +#include "feedback_new_stars.h" #include "part.h" #include "timeline.h" @@ -144,6 +144,26 @@ __attribute__((always_inline)) INLINE static int cell_are_bpart_drifted( return (c->black_holes.ti_old_part == e->ti_current); } +/** + * @brief Check that the #part in a #cell have been drifted to the current time. + * This is just a prototype function to keep the iact functions clean. As we + * don't care about the drifts during the RT sub-cycling, this always just + * returns true. + * + * @param c The #cell. + * @param e The #engine containing information about the current time. + * @return 1 if the #cell has been drifted to the current time, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int +cell_are_part_drifted_rt_sub_cycle(const struct cell *c, + const struct engine *e) { + + /* Note: we can't just use "cell_are_part_drifted" in the hydro_iact + * functions, because an RT sub-cycle may be called during a main + * step for a cell that is hydro inactive and thus may be not drifted. */ + return 1; +} + /* Are cells / particles active for regular tasks ? */ /** @@ -168,6 +188,44 @@ __attribute__((always_inline)) INLINE static int cell_is_active_hydro( return (c->hydro.ti_end_min == e->ti_current); } +/** + * @brief Does a cell contain any particle finishing their RT time-step now ? + * + * @param c The #cell. + * @param e The #engine containing information about the current time. + * @return 1 if the #cell contains at least an active particle, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int cell_is_rt_active( + struct cell *c, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Each cell doing RT needs to have the rt_advance_cell_time task. + * If it doesn't, it's not doing RT. This can happen for foreign + * cells which have been sent to a foreign node for other interactions, + * e.g. gravity. However, foreign cells may have tasks on levels below + * the rt_advance_cell_time, so allow for that exception in this check. */ + + int has_rt_advance_cell_time = 0; + if (c->super != NULL) + has_rt_advance_cell_time = c->super->rt.rt_advance_cell_time != NULL; + + if (has_rt_advance_cell_time && + (c->rt.ti_rt_end_min < e->ti_current_subcycle)) { + error( + "cell %lld in an impossible time-zone! c->ti_rt_end_min=%lld (t=%e) " + "and e->ti_current=%lld (t=%e, a=%e) c->nodeID=%d ACT=%d count=%d", + c->cellID, c->rt.ti_rt_end_min, c->rt.ti_rt_end_min * e->time_base, + e->ti_current_subcycle, e->ti_current_subcycle * e->time_base, + e->cosmology->a, c->nodeID, c->rt.rt_advance_cell_time != NULL, + c->hydro.count); + } +#endif + + /* If there are no sub-cycles, e->ti_current_subcycle = e->ti_current. + * This is also the case if we're currently doing a normal SWIFT step. */ + return (c->rt.ti_rt_end_min == e->ti_current_subcycle); +} + /** * @brief Does a cell contain any g-particle finishing their time-step now ? * @@ -323,6 +381,33 @@ __attribute__((always_inline)) INLINE static int part_is_active_no_debug( return (part_bin <= max_active_bin); } +/** + * @brief Is this particle finishing its RT time-step now ? + * + * @param p The #part. + * @param e The #engine containing information about the current time. + * @return 1 if the #part is active, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int part_is_rt_active( + const struct part *p, const struct engine *e) { + + const timebin_t max_active_bin = e->max_active_bin_subcycle; + const timebin_t part_bin = p->rt_time_data.time_bin; + +#ifdef SWIFT_DEBUG_CHECKS + const integertime_t ti_current_subcycle = e->ti_current_subcycle; + const integertime_t ti_end = + get_integer_time_end(ti_current_subcycle, p->rt_time_data.time_bin); + if (ti_end < ti_current_subcycle) + error( + "particle in an impossible time-zone! p->ti_end_subcycle=%lld " + "e->ti_current_subcycle=%lld", + ti_end, ti_current_subcycle); +#endif + + return (part_bin <= max_active_bin); +} + /** * @brief Is this g-particle finishing its time-step now ? * @@ -448,7 +533,7 @@ __attribute__((always_inline)) INLINE static int part_is_inhibited( * * @param gp The #gpart. * @param e The #engine containing information about the current time. - * @return 1 if the #part is inhibited, 0 otherwise. + * @return 1 if the #gpart is inhibited, 0 otherwise. */ __attribute__((always_inline)) INLINE static int gpart_is_inhibited( const struct gpart *gp, const struct engine *e) { @@ -460,7 +545,7 @@ __attribute__((always_inline)) INLINE static int gpart_is_inhibited( * * @param sp The #spart. * @param e The #engine containing information about the current time. - * @return 1 if the #part is inhibited, 0 otherwise. + * @return 1 if the #spart is inhibited, 0 otherwise. */ __attribute__((always_inline)) INLINE static int spart_is_inhibited( const struct spart *sp, const struct engine *e) { @@ -484,7 +569,7 @@ __attribute__((always_inline)) INLINE static int sink_is_inhibited( * * @param bp The #bpart. * @param e The #engine containing information about the current time. - * @return 1 if the #part is inhibited, 0 otherwise. + * @return 1 if the #bpart is inhibited, 0 otherwise. */ __attribute__((always_inline)) INLINE static int bpart_is_inhibited( const struct bpart *bp, const struct engine *e) { diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h index d64968d736df0f0539a568632e8cf9c50a85145e..a623d40722278b41181642617f223218c1b5f156 100644 --- a/src/adiabatic_index.h +++ b/src/adiabatic_index.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl). * Bert Vandenbroucke (bert.vandenbroucke@gmail.com). * * This program is free software: you can redistribute it and/or modify @@ -27,7 +27,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> diff --git a/src/align.h b/src/align.h index 24ff0828b09855f31c187b655b1d751e78af8769..5389a9170f98f1b48514e75420a81fefa3678923 100644 --- a/src/align.h +++ b/src/align.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 diff --git a/src/approx_math.h b/src/approx_math.h index 70cb3e203cefa7e3c8ce2e4d721d9e23588641ef..bbd2ae56be67389b4b0624405c9e5453eb3a0942 100644 --- a/src/approx_math.h +++ b/src/approx_math.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/atomic.h b/src/atomic.h index d5f2da40595d2896eb43934bca2409df4f73d15f..5d8621985da55d1c2d9b4416b6539956ce9b7794 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -20,7 +20,7 @@ #define SWIFT_ATOMIC_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include "inline.h" diff --git a/src/barrier.h b/src/barrier.h index dbe856b402580f859042023cd423bd955887f790..9adb004cc56ef486deb7589fa5013cd66b0f7d3a 100644 --- a/src/barrier.h +++ b/src/barrier.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2017 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 @@ -38,7 +38,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers */ #include <pthread.h> diff --git a/src/binomial.h b/src/binomial.h index b1caf1258a75b47b0f47c26b2e16ac01d76f6454..21b4828d92fffe65414e5aafb4eb9dac5553ebac 100644 --- a/src/binomial.h +++ b/src/binomial.h @@ -20,7 +20,7 @@ #define SWIFT_BINOMIAL_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "error.h" diff --git a/src/black_holes.h b/src/black_holes.h index c45a8f112e8c449643f6502da259e232dda7ea6d..4051727192b99c512c79f5c0391eac4aa18857d9 100644 --- a/src/black_holes.h +++ b/src/black_holes.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Matthieu Schaller (schaller@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 @@ -20,17 +20,17 @@ #define SWIFT_BLACK_HOLES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct BH model */ #if defined(BLACK_HOLES_NONE) #include "./black_holes/Default/black_holes.h" -#include "./black_holes/Default/black_holes_iact.h" #elif defined(BLACK_HOLES_EAGLE) #include "./black_holes/EAGLE/black_holes.h" -#include "./black_holes/EAGLE/black_holes_iact.h" +#elif defined(BLACK_HOLES_SPIN_JET) +#include "./black_holes/SPIN_JET/black_holes.h" #else #error "Invalid choice of black hole model" #endif -#endif +#endif /* SWIFT_BLACK_HOLES_H */ diff --git a/src/black_holes/Default/black_holes.h b/src/black_holes/Default/black_holes.h index 9ad243cc8c8dfb899d60bea97f3d4ceb419b6372..e12a6a4652d4ecc3124dc8e9623de1b52fc829a5 100644 --- a/src/black_holes/Default/black_holes.h +++ b/src/black_holes/Default/black_holes.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -185,6 +185,27 @@ black_holes_get_subgrid_mass(const struct bpart* bp) { return 0.; } +/** + * @brief Return the current bolometric luminosity of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_bolometric_luminosity(const struct bpart* bp, + const struct phys_const* constants) { + return 0.; +} + +/** + * @brief Return the current kinetic jet power of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double black_holes_get_jet_power( + const struct bpart* bp, const struct phys_const* constants) { + return 0.; +} + /** * @brief Update the properties of a black hole particles by swallowing * a gas particle. @@ -211,11 +232,12 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part( * @param time Time since the start of the simulation (non-cosmo mode). * @param with_cosmology Are we running with cosmology? * @param props The properties of the black hole scheme. + * @param constants The physical constants in internal units. */ __attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo, const double time, const int with_cosmology, - const struct black_holes_props* props) { + const struct black_holes_props* props, const struct phys_const* constants) { /* Nothing to do here: No merging in the default model */ } @@ -314,11 +336,13 @@ black_holes_store_potential_in_part(struct black_holes_part_data* p_data, * @param cosmo The current cosmological model. * @param p The #part that became a black hole. * @param xp The #xpart that became a black hole. + * @param ti_current the current time on the time-line. */ INLINE static void black_holes_create_from_gas( struct bpart* bp, const struct black_holes_props* props, const struct phys_const* constants, const struct cosmology* cosmo, - const struct part* p, const struct xpart* xp) { + const struct part* p, const struct xpart* xp, + const integertime_t ti_current) { /* First initialisation */ black_holes_init_bpart(bp); diff --git a/src/black_holes/Default/black_holes_debug.h b/src/black_holes/Default/black_holes_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..302b9ee22bf259dc3656ce392f54d9681927d7b6 --- /dev/null +++ b/src/black_holes/Default/black_holes_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_DEFAULT_DEBUG_H +#define SWIFT_BLACK_HOLES_DEFAULT_DEBUG_H + +__attribute__((always_inline)) INLINE static void black_holes_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_BLACK_HOLES_DEFAULT_DEBUG_H */ diff --git a/src/black_holes/Default/black_holes_iact.h b/src/black_holes/Default/black_holes_iact.h index f58825ebdbffc6b492a1b1514da8e5dee74f8920..5df3eb3ef4616c4c17939012deda5e0238f869bd 100644 --- a/src/black_holes/Default/black_holes_iact.h +++ b/src/black_holes/Default/black_holes_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -37,7 +37,7 @@ */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_gas_density( - const float r2, const float *dx, const float hi, const float hj, + const float r2, const float dx[3], const float hi, const float hj, struct bpart *bi, const struct part *pj, const struct xpart *xpj, const int with_cosmology, const struct cosmology *cosmo, const struct gravity_props *grav_props, @@ -69,6 +69,35 @@ runner_iact_nonsym_bh_gas_density( #endif } +/** + * @brief Repositioning interaction between two particles (non-symmetric). + * + * Function used to identify the gas particle that this BH may move towards. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_repos( + const float r2, const float dx[3], const float hi, const float hj, + struct bpart *bi, const struct part *pj, const struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) {} + /** * @brief Swallowing interaction between two particles (non-symmetric). * @@ -90,7 +119,7 @@ runner_iact_nonsym_bh_gas_density( */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_gas_swallow( - const float r2, const float *dx, const float hi, const float hj, + const float r2, const float dx[3], const float hi, const float hj, const struct bpart *bi, struct part *pj, struct xpart *xpj, const int with_cosmology, const struct cosmology *cosmo, const struct gravity_props *grav_props, @@ -98,6 +127,31 @@ runner_iact_nonsym_bh_gas_swallow( const struct entropy_floor_properties *floor_props, const integertime_t ti_current, const double time) {} +/** + * @brief Swallowing interaction between two BH particles (non-symmetric). + * + * Function used to identify the BH particle that this BH may move towards. + * + * @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 bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_bh_repos(const float r2, const float dx[3], + const float hi, const float hj, struct bpart *bi, + const struct bpart *bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const integertime_t ti_current) {} + /** * @brief Swallowing interaction between two BH particles (non-symmetric). * @@ -115,7 +169,7 @@ runner_iact_nonsym_bh_gas_swallow( * @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, +runner_iact_nonsym_bh_bh_swallow(const float r2, const float dx[3], const float hi, const float hj, const struct bpart *bi, struct bpart *bj, const struct cosmology *cosmo, @@ -141,8 +195,8 @@ runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_gas_feedback( - const float r2, const float *dx, const float hi, const float hj, - struct bpart *bi, struct part *pj, struct xpart *xpj, + const float r2, const float dx[3], const float hi, const float hj, + const struct bpart *bi, struct part *pj, struct xpart *xpj, const int with_cosmology, const struct cosmology *cosmo, const struct gravity_props *grav_props, const struct black_holes_props *bh_props, diff --git a/src/black_holes/Default/black_holes_io.h b/src/black_holes/Default/black_holes_io.h index 73732e2f77d3186ef0d182400dc1728c06ea35df..43aa06f1fb0f6cb4206d72a2edbd4af47e018547 100644 --- a/src/black_holes/Default/black_holes_io.h +++ b/src/black_holes/Default/black_holes_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Matthieu Schaller (schaller@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 @@ -106,6 +106,15 @@ INLINE static void convert_bpart_vel(const struct engine* e, ret[2] *= cosmo->a_inv; } +INLINE static void convert_bpart_potential(const struct engine* e, + const struct bpart* bp, float* ret) { + + if (bp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(bp->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which b-particle fields to write to a dataset * @@ -120,7 +129,7 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, int with_cosmology) { /* Say how much we want to write */ - *num_fields = 5; + *num_fields = 6; /* List what we want to write */ list[0] = io_make_output_field_convert_bpart( @@ -143,6 +152,10 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, "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_convert_bpart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, bparts, + convert_bpart_potential, "Gravitational potentials of the particles"); + #ifdef DEBUG_INTERACTIONS_BLACK_HOLES list += *num_fields; diff --git a/src/black_holes/Default/black_holes_part.h b/src/black_holes/Default/black_holes_part.h index 777b13f8ccd78a8572e1b8ca066c85a30a6a830b..d8e379dcec878993c41c222ef7bc6de2c9e755ab 100644 --- a/src/black_holes/Default/black_holes_part.h +++ b/src/black_holes/Default/black_holes_part.h @@ -74,6 +74,9 @@ struct bpart { /*! Black holes merger information (e.g. merging ID) */ struct black_holes_bpart_data merger_data; + /*! Tracer structure */ + struct tracers_bpart_data tracers_data; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/black_holes/Default/black_holes_properties.h b/src/black_holes/Default/black_holes_properties.h index e6e59e0f733c4e22bf3b477b635b55656094e9cb..30c6d1d9f9d0425d91b43db6a9c89309aa6043c0 100644 --- a/src/black_holes/Default/black_holes_properties.h +++ b/src/black_holes/Default/black_holes_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/black_holes/EAGLE/black_holes.h b/src/black_holes/EAGLE/black_holes.h index 8fe6222f1c97a28ebbbf9d435f49b1bc41a6cd5d..468a72431d992e0890dbde152c2b4841bc3a94e4 100644 --- a/src/black_holes/EAGLE/black_holes.h +++ b/src/black_holes/EAGLE/black_holes.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -136,12 +136,11 @@ __attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( bp->dt_heat = 0.f; bp->AGN_number_of_AGN_events = 0; bp->AGN_number_of_energy_injections = 0; + bp->AGN_cumulative_energy = 0.f; /* Set the initial targetted heating temperature, used for the * BH time step determination */ bp->AGN_delta_T = props->AGN_delta_T_desired; - - black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); } /** @@ -362,6 +361,27 @@ black_holes_get_subgrid_mass(const struct bpart* bp) { return bp->subgrid_mass; } +/** + * @brief Return the current bolometric luminosity of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_bolometric_luminosity(const struct bpart* bp, + const struct phys_const* constants) { + return 0.; +} + +/** + * @brief Return the current kinetic jet power of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double black_holes_get_jet_power( + const struct bpart* bp, const struct phys_const* constants) { + return 0.; +} + /** * @brief Update the properties of a black hole particles by swallowing * a gas particle. @@ -449,11 +469,12 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part( * @param time Time since the start of the simulation (non-cosmo mode). * @param with_cosmology Are we running with cosmology? * @param props The properties of the black hole scheme. + * @param constants The physical constants in internal units. */ __attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo, const double time, const int with_cosmology, - const struct black_holes_props* props) { + const struct black_holes_props* props, const struct phys_const* constants) { /* Get the current dynamical masses */ const float bpi_dyn_mass = bpi->mass; @@ -938,7 +959,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( * ray directions in the isotropic case or the first N closest particles * in the other modes. */ for (int i = 0; i < bp->num_ngbs; i++) { - const double rand = random_unit_interval_part_ID_and_ray_idx( + const double rand = random_unit_interval_part_ID_and_index( bp->id, i, ti_begin, random_number_BH_feedback); /* Increase the counter if we are lucky */ @@ -1248,11 +1269,13 @@ black_holes_store_potential_in_part(struct black_holes_part_data* p_data, * @param cosmo The current cosmological model. * @param p The #part that became a black hole. * @param xp The #xpart that became a black hole. + * @param ti_current the current time on the time-line. */ INLINE static void black_holes_create_from_gas( struct bpart* bp, const struct black_holes_props* props, const struct phys_const* constants, const struct cosmology* cosmo, - const struct part* p, const struct xpart* xp) { + const struct part* p, const struct xpart* xp, + const integertime_t ti_current) { /* All the non-basic properties of the black hole have been zeroed * in the FOF code. We update them here. diff --git a/src/black_holes/EAGLE/black_holes_debug.h b/src/black_holes/EAGLE/black_holes_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..996bfaab66cd61abbe2adf4a5c13ab6735c472f5 --- /dev/null +++ b/src/black_holes/EAGLE/black_holes_debug.h @@ -0,0 +1,30 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_EAGLE_DEBUG_H +#define SWIFT_BLACK_HOLES_EAGLE_DEBUG_H + +__attribute__((always_inline)) INLINE static void black_holes_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld] black_holes_part_data:", p->id); + warning("[PID%lld] swallow_id = %lld, potential = %.3e", p->id, + p->black_holes_data.swallow_id, p->black_holes_data.potential); +} + +#endif /* SWIFT_BLACK_HOLES_EAGLE_DEBUG_H */ diff --git a/src/black_holes/EAGLE/black_holes_iact.h b/src/black_holes/EAGLE/black_holes_iact.h index 0ce45fc3fd3b8c3a64adbbd2beb65d7a58b098e0..dfd336c88afb0382acb88db3d64002707bb789e1 100644 --- a/src/black_holes/EAGLE/black_holes_iact.h +++ b/src/black_holes/EAGLE/black_holes_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2021 Edo Altamura (edoardo.altamura@manchester.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include "entropy_floor.h" #include "equation_of_state.h" #include "gravity.h" +#include "gravity_iact.h" #include "hydro.h" #include "random.h" #include "rays.h" @@ -51,7 +52,7 @@ */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_gas_density( - const float r2, const float *dx, const float hi, const float hj, + const float r2, const float dx[3], const float hi, const float hj, struct bpart *bi, const struct part *pj, const struct xpart *xpj, const int with_cosmology, const struct cosmology *cosmo, const struct gravity_props *grav_props, @@ -92,11 +93,13 @@ runner_iact_nonsym_bh_gas_density( * re-calculate the sound speed using the fixed internal energy */ const float u_EoS = entropy_floor_temperature(pj, cosmo, floor_props) * bh_props->temp_to_u_factor; - const float u = hydro_get_drifted_comoving_internal_energy(pj); + const float u = hydro_get_drifted_physical_internal_energy(pj, cosmo); + if (u < u_EoS * bh_props->fixed_T_above_EoS_factor && u > bh_props->fixed_u_for_soundspeed) { cj = gas_soundspeed_from_internal_energy( - pj->rho, bh_props->fixed_u_for_soundspeed); + pj->rho, bh_props->fixed_u_for_soundspeed) / + cosmo->a_factor_sound_speed; } } bi->sound_speed_gas += mj * wi * cj; @@ -120,9 +123,9 @@ runner_iact_nonsym_bh_gas_density( /* Contribution to the specific angular momentum of gas, which is later * converted to the circular velocity at the smoothing length */ - bi->circular_velocity_gas[0] += mj * wi * (dx[1] * dv[2] - dx[2] * dv[1]); - bi->circular_velocity_gas[1] += mj * wi * (dx[2] * dv[0] - dx[0] * dv[2]); - bi->circular_velocity_gas[2] += mj * wi * (dx[0] * dv[1] - dx[1] * dv[0]); + bi->circular_velocity_gas[0] -= mj * wi * (dx[1] * dv[2] - dx[2] * dv[1]); + bi->circular_velocity_gas[1] -= mj * wi * (dx[2] * dv[0] - dx[0] * dv[2]); + bi->circular_velocity_gas[2] -= mj * wi * (dx[0] * dv[1] - dx[1] * dv[0]); if (bh_props->use_multi_phase_bondi) { /* Contribution to BH accretion rate @@ -182,12 +185,12 @@ runner_iact_nonsym_bh_gas_density( to randomly select the direction of the ith ray */ /* Random number in [0, 1[ */ - const double rand_theta = random_unit_interval_part_ID_and_ray_idx( + const double rand_theta = random_unit_interval_part_ID_and_index( bi->id, i, ti_current, random_number_isotropic_AGN_feedback_ray_theta); /* Random number in [0, 1[ */ - const double rand_phi = random_unit_interval_part_ID_and_ray_idx( + const double rand_phi = random_unit_interval_part_ID_and_index( bi->id, i, ti_current, random_number_isotropic_AGN_feedback_ray_phi); @@ -251,10 +254,9 @@ runner_iact_nonsym_bh_gas_density( } /** - * @brief Swallowing interaction between two particles (non-symmetric). + * @brief Repositioning interaction between two particles (non-symmetric). * - * Function used to flag the gas particles that will be swallowed - * by the black hole particle. + * Function used to identify the gas particle that this BH may move towards. * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -271,9 +273,9 @@ runner_iact_nonsym_bh_gas_density( * @param time Current physical time in the simulation. */ __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 *bi, struct part *pj, struct xpart *xpj, +runner_iact_nonsym_bh_gas_repos( + const float r2, const float dx[3], const float hi, const float hj, + struct bpart *bi, const struct part *pj, const struct xpart *xpj, const int with_cosmology, const struct cosmology *cosmo, const struct gravity_props *grav_props, const struct black_holes_props *bh_props, @@ -286,12 +288,9 @@ runner_iact_nonsym_bh_gas_swallow( * to r2 / sqrtf(r2) because of 1 / 0 behaviour. */ const float r = sqrtf(r2); const float hi_inv = 1.0f / hi; - const float hi_inv_dim = pow_dimension(hi_inv); const float ui = r * hi_inv; kernel_eval(ui, &wi); - /* Start by checking the repositioning criteria */ - /* (Square of) Max repositioning distance allowed based on the softening */ const float max_dist_repos2 = kernel_gravity_softening_plummer_equivalent_inv * @@ -337,7 +336,30 @@ runner_iact_nonsym_bh_gas_swallow( } if (neighbour_is_slow_enough) { - const float potential = pj->black_holes_data.potential; + float potential = pj->black_holes_data.potential; + + if (bh_props->correct_bh_potential_for_repositioning) { + + /* Let's not include the contribution of the BH + * itself to the potential of the gas particle */ + + /* Note: This assumes the BH and gas have the same + * softening, which is currently true */ + const float eps = gravity_get_softening(bi->gpart, grav_props); + const float eps2 = eps * eps; + const float eps_inv = 1.f / eps; + const float eps_inv3 = eps_inv * eps_inv * eps_inv; + const float BH_mass = bi->mass; + + /* Compute the Newtonian or truncated potential the BH + * exherts onto the gas particle */ + float dummy, pot_ij; + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy, + &pot_ij); + + /* Deduct the BH contribution */ + potential -= pot_ij * grav_props->G_Newton; + } /* Is the potential lower? */ if (potential < bi->reposition.min_potential) { @@ -350,7 +372,49 @@ runner_iact_nonsym_bh_gas_swallow( } } } +} + +/** + * @brief Swallowing interaction between two particles (non-symmetric). + * + * Function used to flag the gas 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 pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_swallow( + const float r2, const float dx[3], const float hi, const float hj, + struct bpart *bi, struct part *pj, struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + float wi; + + /* Compute the kernel function; note that r cannot be optimised + * to r2 / sqrtf(r2) because of 1 / 0 behaviour. */ + const float r = sqrtf(r2); + const float hi_inv = 1.0f / hi; + const float hi_inv_dim = pow_dimension(hi_inv); + const float ui = r * hi_inv; + kernel_eval(ui, &wi); + /* Start by checking the repositioning criteria */ /* Check if the BH needs to be fed. If not, we're done here */ const float bh_mass_deficit = bi->subgrid_mass - bi->mass_at_start_of_step; if (bh_mass_deficit <= 0.f) return; @@ -460,8 +524,7 @@ runner_iact_nonsym_bh_gas_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. + * Function used to identify the BH particle that this BH may move towards. * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -475,13 +538,13 @@ runner_iact_nonsym_bh_gas_swallow( * @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 *bi, struct bpart *bj, - const struct cosmology *cosmo, - const struct gravity_props *grav_props, - const struct black_holes_props *bh_props, - const integertime_t ti_current) { +runner_iact_nonsym_bh_bh_repos(const float r2, const float dx[3], + const float hi, const float hj, struct bpart *bi, + const struct bpart *bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const integertime_t ti_current) { /* Compute relative peculiar velocity between the two BHs * Recall that in SWIFT v is (v_pec * a) */ @@ -529,7 +592,27 @@ runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, } if (neighbour_is_slow_enough) { - const float potential = bj->reposition.potential; + float potential = bj->reposition.potential; + + if (bh_props->correct_bh_potential_for_repositioning) { + + /* Let's not include the contribution of the BH i + * to the potential of the BH j */ + const float eps = gravity_get_softening(bi->gpart, grav_props); + const float eps2 = eps * eps; + const float eps_inv = 1.f / eps; + const float eps_inv3 = eps_inv * eps_inv * eps_inv; + const float BH_mass = bi->mass; + + /* Compute the Newtonian or truncated potential the BH + * exherts onto the gas particle */ + float dummy, pot_ij; + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy, + &pot_ij); + + /* Deduct the BH contribution */ + potential -= pot_ij * grav_props->G_Newton; + } /* Is the potential lower? */ if (potential < bi->reposition.min_potential) { @@ -542,6 +625,42 @@ runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, } } } +} + +/** + * @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 bh_props The properties of the BH scheme + * @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[3], + const float hi, const float hj, + struct bpart *bi, struct bpart *bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_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; /* Find the most massive of the two BHs */ float M = bi->subgrid_mass; @@ -575,7 +694,7 @@ runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, /* Maximum velocity difference between BHs allowed to merge */ float v2_threshold; - if (bh_props->merger_threshold_type == 0) { + if (bh_props->merger_threshold_type == BH_mergers_circular_velocity) { /* 'Old-style' merger threshold using circular velocity at the * edge of the more massive BH's kernel */ @@ -583,22 +702,23 @@ runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, } else { /* Arguably better merger threshold using the escape velocity at - * the distance of the lower-mass BH */ - const float r_12 = sqrt(r2); + * the distance between the BHs */ - if ((bh_props->merger_threshold_type == 1) && - (r_12 < grav_props->epsilon_baryon_cur)) { + if (bh_props->merger_threshold_type == BH_mergers_escape_velocity) { - /* If BHs are within softening range, take this into account */ - const float w_grav = - kernel_grav_pot_eval(r_12 / grav_props->epsilon_baryon_cur); - const float r_mod = w_grav / grav_props->epsilon_baryon_cur; - v2_threshold = 2.f * G_Newton * M / (r_mod); + /* Standard formula (not softening BH interactions) */ + v2_threshold = 2.f * G_Newton * M / sqrt(r2); + } else if (bh_props->merger_threshold_type == + BH_mergers_dynamical_escape_velocity) { + /* General two-body escape velocity based on dynamical masses */ + v2_threshold = 2.f * G_Newton * (bi->mass + bj->mass) / sqrt(r2); } else { - - /* Standard formula if BH interactions are not softened */ - v2_threshold = 2.f * G_Newton * M / (r_12); + /* Cannot happen! */ +#ifdef SWIFT_DEBUG_CHECKS + error("Invalid choice of BH merger threshold type"); +#endif + v2_threshold = 0.f; } } /* Ends sections for different merger thresholds */ @@ -645,7 +765,7 @@ runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx, */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_bh_gas_feedback( - const float r2, const float *dx, const float hi, const float hj, + const float r2, const float dx[3], const float hi, const float hj, const struct bpart *bi, struct part *pj, struct xpart *xpj, const int with_cosmology, const struct cosmology *cosmo, const struct gravity_props *grav_props, diff --git a/src/black_holes/EAGLE/black_holes_io.h b/src/black_holes/EAGLE/black_holes_io.h index 61d95d9bc5a86a6385e04b913d1e70bc5750d2b1..9a588be7eae825f4b9b7894755dfe08574defe31 100644 --- a/src/black_holes/EAGLE/black_holes_io.h +++ b/src/black_holes/EAGLE/black_holes_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Matthieu Schaller (schaller@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 @@ -112,6 +112,15 @@ INLINE static void convert_bpart_vel(const struct engine* e, ret[2] *= cosmo->a_inv; } +INLINE static void convert_bpart_potential(const struct engine* e, + const struct bpart* bp, float* ret) { + + if (bp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(bp->gpart); + else + ret[0] = 0.f; +} + INLINE static void convert_bpart_gas_vel(const struct engine* e, const struct bpart* bp, float* ret) { @@ -146,7 +155,6 @@ INLINE static void convert_bpart_gas_temperatures(const struct engine* e, ret[0] = bp->internal_energy_gas * cosmo->a_factor_internal_energy / props->temp_to_u_factor; } - /** * @brief Specifies which b-particle fields to write to a dataset * @@ -158,10 +166,10 @@ INLINE static void convert_bpart_gas_temperatures(const struct engine* e, INLINE static void black_holes_write_particles(const struct bpart* bparts, struct io_props* list, int* num_fields, - int with_cosmology) { + const int with_cosmology) { /* Say how much we want to write */ - *num_fields = 43; + *num_fields = 44; /* List what we want to write */ list[0] = io_make_output_field_convert_bpart( @@ -221,7 +229,9 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, list[11] = io_make_output_field( "TotalAccretedMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, total_accreted_mass, - "Total mass accreted onto the particles since its birth"); + "Total mass accreted onto the main progenitor of the black holes since " + "birth. This does not include any mass accreted onto any merged black " + "holes."); list[12] = io_make_output_field( "CumulativeNumberOfSeeds", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, @@ -386,13 +396,14 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, "NumberOfHeatingEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, AGN_number_of_energy_injections, "Integer number of (thermal) energy injections the black hole has had " - "so far"); + "so far. This counts each heated gas particle separately, and so can " + "increase by more than one during a single time step."); list[35] = io_make_output_field( "NumberOfAGNEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, AGN_number_of_AGN_events, "Integer number of AGN events the black hole has had so far" - " (the number of times the BH did AGN feedback)"); + " (the number of time steps in which the BH did AGN feedback)."); if (with_cosmology) { list[36] = io_make_output_field( @@ -408,7 +419,9 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, list[37] = io_make_output_field( "AccretionLimitedTimeSteps", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, - dt_heat, "Accretion-limited time-steps of black holes."); + dt_heat, + "Accretion-limited time steps of black holes. The actual time step of " + "the particles may differ due to the minimum allowed value."); list[38] = io_make_output_field( "AGNTotalInjectedEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, @@ -442,6 +455,10 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, "This is based on the unlimited accretion rates, so these fractions " "can be above the limiting fEdd."); + list[43] = io_make_output_field_convert_bpart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, bparts, + convert_bpart_potential, "Gravitational potentials of the particles"); + #ifdef DEBUG_INTERACTIONS_BLACK_HOLES list += *num_fields; diff --git a/src/black_holes/EAGLE/black_holes_parameters.h b/src/black_holes/EAGLE/black_holes_parameters.h index c34ce6eb9e874aa8b330da050399ff9f6d0df9ea..0b6d4d8ba5f035e02aad6910131c934bb9d328e7 100644 --- a/src/black_holes/EAGLE/black_holes_parameters.h +++ b/src/black_holes/EAGLE/black_holes_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Matthieu Schaller (schaller@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 @@ -20,7 +20,7 @@ #define SWIFT_EAGLE_BLACK_HOLES_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /** * @file EAGLE/black_holes_parameters.h diff --git a/src/black_holes/EAGLE/black_holes_part.h b/src/black_holes/EAGLE/black_holes_part.h index ecb5541fdad20b8e3549ccfaf2b0f478849da09a..51bd44ec8b12efc4df8c9ebc614c8aa08a7eed78 100644 --- a/src/black_holes/EAGLE/black_holes_part.h +++ b/src/black_holes/EAGLE/black_holes_part.h @@ -274,6 +274,9 @@ struct bpart { /*! Isotropic AGN feedback information */ struct ray_data rays[eagle_blackhole_number_of_rays]; + /*! Tracer structure */ + struct tracers_bpart_data tracers_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 2845a1a0fec308939ff2ea0a005762be638af2b9..47ea2d2fbd9dffc19b2b2ce0aff0eada913a7282 100644 --- a/src/black_holes/EAGLE/black_holes_properties.h +++ b/src/black_holes/EAGLE/black_holes_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_EAGLE_BLACK_HOLES_PROPERTIES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "chemistry.h" @@ -40,6 +40,12 @@ enum AGN_feedback_models { AGN_minimum_density_model /*< Minimum-density model of AGN feedback */ }; +enum BH_merger_thresholds { + BH_mergers_circular_velocity, /*< v_circ at separation, as in EAGLE */ + BH_mergers_escape_velocity, /*< v_esc at separation */ + BH_mergers_dynamical_escape_velocity /*< combined v_esc_dyn at separation */ +}; + /** * @brief Properties of black holes and AGN feedback in the EAGEL model. */ @@ -228,6 +234,9 @@ struct black_holes_props { /*! Repositioning velocity scaling with gas density */ float reposition_exponent_n_H; + /*! Correct potential of BH? */ + int correct_bh_potential_for_repositioning; + /* ---- Properties of the merger model ---------- */ /*! Mass ratio above which a merger is considered 'minor' */ @@ -236,8 +245,8 @@ struct black_holes_props { /*! Mass ratio above which a merger is considered 'major' */ float major_merger_threshold; - /*! Type of merger threshold (0: standard, 1: improved) */ - int merger_threshold_type; + /*! Type of merger threshold */ + enum BH_merger_thresholds merger_threshold_type; /*! Maximal distance over which BHs merge, in units of softening length */ float max_merging_distance_ratio; @@ -381,9 +390,18 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->use_nibbling = parser_get_param_int(params, "EAGLEAGN:use_nibbling"); if (bp->use_nibbling) { - bp->min_gas_mass_for_nibbling = - parser_get_param_float(params, "EAGLEAGN:min_gas_mass_for_nibbling"); + bp->min_gas_mass_for_nibbling = parser_get_param_float( + params, "EAGLEAGN:min_gas_mass_for_nibbling_Msun"); bp->min_gas_mass_for_nibbling *= phys_const->const_solar_mass; + + if ((bp->min_gas_mass_for_nibbling < 1e-5 * bp->subgrid_seed_mass) || + (bp->min_gas_mass_for_nibbling > 1e5 * bp->subgrid_seed_mass)) { + error( + "The BH seeding mass and minimal gas mass for nibbling differ by " + "more " + "than 10^5. That is probably indicating a typo in the parameter " + "file."); + } } bp->with_fixed_T_near_EoS = @@ -532,6 +550,9 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, params, "EAGLEAGN:reposition_exponent_n_H", 1.0); } + bp->correct_bh_potential_for_repositioning = + parser_get_param_int(params, "EAGLEAGN:with_potential_correction"); + /* Merger parameters ------------------------------------- */ bp->minor_merger_threshold = @@ -540,8 +561,19 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->major_merger_threshold = parser_get_param_float(params, "EAGLEAGN:threshold_major_merger"); - bp->merger_threshold_type = - parser_get_param_int(params, "EAGLEAGN:merger_threshold_type"); + char temp2[40]; + parser_get_param_string(params, "EAGLEAGN:merger_threshold_type", temp2); + if (strcmp(temp2, "CircularVelocity") == 0) + bp->merger_threshold_type = BH_mergers_circular_velocity; + else if (strcmp(temp2, "EscapeVelocity") == 0) + bp->merger_threshold_type = BH_mergers_escape_velocity; + else if (strcmp(temp2, "DynamicalEscapeVelocity") == 0) + bp->merger_threshold_type = BH_mergers_dynamical_escape_velocity; + else + error( + "The BH merger model must be either CircularVelocity, EscapeVelocity, " + "or DynamicalEscapeVelocity, not %s", + temp2); bp->max_merging_distance_ratio = parser_get_param_float(params, "EAGLEAGN:merger_max_distance_ratio"); diff --git a/src/black_holes/SPIN_JET/black_holes.h b/src/black_holes/SPIN_JET/black_holes.h new file mode 100644 index 0000000000000000000000000000000000000000..143958a469fb6409464c922c31a1cc309175c442 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes.h @@ -0,0 +1,1577 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2022 Filip Husko (filip.husko@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_SPIN_JET_BLACK_HOLES_H +#define SWIFT_SPIN_JET_BLACK_HOLES_H + +/* Local includes */ +#include "black_holes_properties.h" +#include "black_holes_spin.h" +#include "black_holes_struct.h" +#include "cooling_properties.h" +#include "cosmology.h" +#include "dimension.h" +#include "gravity.h" +#include "kernel_hydro.h" +#include "minmax.h" +#include "physical_constants.h" +#include "random.h" +#include "rays.h" + +/* Standard includes */ +#include <float.h> +#include <math.h> + +/** + * @brief Computes the time-step of a given black hole particle. + * + * @param bp Pointer to the s-particle data. + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + */ +__attribute__((always_inline)) INLINE static float black_holes_compute_timestep( + const struct bpart* const bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo) { + + /* Do something if and only if the accretion rate is non-zero */ + if (bp->accretion_rate > 0.f) { + + /* Gather some physical constants (all in internal units) */ + const double c = constants->const_speed_light_c; + + /* Compute instantaneous energy supply rate to the BH energy reservoir + * which is proportional to the BH mass accretion rate */ + const double Energy_rate = bp->radiative_efficiency * props->epsilon_f * + bp->accretion_rate * c * c; + + /* Compute instantaneous jet energy supply rate to the BH jet reservoir + * which is proportional to the BH mass accretion rate */ + const double Jet_rate = + bp->jet_efficiency * props->eps_f_jet * bp->accretion_rate * c * c; + + /* Compute average heating energy in AGN feedback, both thermal and jet */ + + /* Average particle mass in BH's kernel */ + const double mean_ngb_mass = bp->ngb_mass / ((double)bp->num_ngbs); + /* Without multiplying by mean_ngb_mass we'd get energy per unit mass */ + const double E_heat = + props->AGN_delta_T_desired * props->temp_to_u_factor * mean_ngb_mass; + const double E_jet = 0.5 * mean_ngb_mass * bp->v_jet * bp->v_jet; + + /* Compute average time between energy injections for the given accretion + * rate. The time is multiplied by the number of Ngbs to heat because + * if more particles are heated at once then the time between different + * AGN feedback events increases proportionally. */ + const double dt_heat = E_heat * props->num_ngbs_to_heat / Energy_rate; + + /* Similar for jets, with N_jet the target number of particles to kick. */ + const double dt_jet = E_jet * props->N_jet / Jet_rate; + + /* Pick the shorter of the two feedback time steps */ + double bh_timestep = min(dt_jet, dt_heat); + + /* See if the spin evolution time step is even smaller */ + bh_timestep = min(bh_timestep, bp->dt_ang_mom); + + /* The final timestep of the BH cannot be smaller than the miminum allowed + * time-step */ + bh_timestep = max(bh_timestep, props->time_step_min); + + return bh_timestep; + } + return FLT_MAX; +} + +/** + * @brief Initialises the b-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param bp The particle to act upon + * @param props The properties of the black holes model. + */ +__attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( + struct bpart* bp, const struct black_holes_props* props) { + + bp->time_bin = 0; + if (props->use_subgrid_mass_from_ics == 0) { + bp->subgrid_mass = bp->mass; + + } else if (props->with_subgrid_mass_check && bp->subgrid_mass <= 0) + error( + "Black hole %lld has a subgrid mass of %f (internal units).\n" + "If this is because the ICs do not contain a 'SubgridMass' data " + "set, you should set the parameter " + "'EAGLEAGN:use_subgrid_mass_from_ics' to 0 to initialize the " + "black hole subgrid masses to the corresponding dynamical masses.\n" + "If the subgrid mass is intentionally set to this value, you can " + "disable this error by setting 'EAGLEAGN:with_subgrid_mass_check' " + "to 0.", + bp->id, bp->subgrid_mass); + bp->total_accreted_mass = 0.f; + bp->accretion_rate = 0.f; + bp->formation_time = -1.f; + bp->energy_reservoir = 0.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; + bp->number_of_gas_swallows = 0; + bp->number_of_direct_gas_swallows = 0; + bp->number_of_repositions = 0; + bp->number_of_reposition_attempts = 0; + bp->number_of_time_steps = 0; + bp->last_high_Eddington_fraction_scale_factor = -1.f; + bp->last_minor_merger_time = -1.; + bp->last_major_merger_time = -1.; + bp->last_AGN_event_time = -1.; + bp->swallowed_angular_momentum[0] = 0.f; + bp->swallowed_angular_momentum[1] = 0.f; + bp->swallowed_angular_momentum[2] = 0.f; + bp->accreted_angular_momentum[0] = 0.f; + bp->accreted_angular_momentum[1] = 0.f; + bp->accreted_angular_momentum[2] = 0.f; + bp->dt_heat = 0.f; + bp->AGN_number_of_AGN_events = 0; + bp->AGN_number_of_energy_injections = 0; + bp->AGN_cumulative_energy = 0.f; + bp->aspect_ratio = 0.01f; + bp->jet_efficiency = 0.1f; + bp->radiative_efficiency = 0.1f; + bp->accretion_disk_angle = 0.01f; + bp->accretion_mode = BH_thin_disc; + bp->eddington_fraction = 0.01f; + bp->jet_reservoir = 0.f; + bp->total_jet_energy = 0.f; + bp->dt_jet = 0.f; + bp->dt_ang_mom = 0.f; + bp->AGN_number_of_AGN_jet_events = 0; + bp->AGN_number_of_jet_injections = 0; + bp->group_mass = 0.f; + for (int i = 0; i < BH_accretion_modes_count; ++i) + bp->accreted_mass_by_mode[i] = 0.f; + for (int i = 0; i < BH_accretion_modes_count; ++i) + bp->thermal_energy_by_mode[i] = 0.f; + for (int i = 0; i < BH_accretion_modes_count; ++i) + bp->jet_energy_by_mode[i] = 0.f; + + black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); +} + +/** + * @brief Prepares a b-particle for its interactions + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_init_bpart( + struct bpart* bp) { + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_density[i] = -1; + bp->num_ngb_density = 0; +#endif + + bp->density.wcount = 0.f; + bp->density.wcount_dh = 0.f; + bp->rho_gas = 0.f; + bp->sound_speed_gas = 0.f; + bp->velocity_gas[0] = 0.f; + bp->velocity_gas[1] = 0.f; + bp->velocity_gas[2] = 0.f; + bp->spec_angular_momentum_gas[0] = 0.f; + bp->spec_angular_momentum_gas[1] = 0.f; + bp->spec_angular_momentum_gas[2] = 0.f; + bp->curl_v_gas[0] = 0.f; + bp->curl_v_gas[1] = 0.f; + bp->curl_v_gas[2] = 0.f; + bp->velocity_dispersion_gas = 0.f; + bp->ngb_mass = 0.f; + bp->num_ngbs = 0; + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + bp->reposition.min_potential = FLT_MAX; + bp->reposition.potential = FLT_MAX; + bp->accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ + bp->f_visc = FLT_MAX; + bp->mass_at_start_of_step = bp->mass; /* bp->mass may grow in nibbling mode */ + + /* Reset the rays carried by this BH */ + ray_init(bp->rays, spinjet_blackhole_number_of_rays); + ray_init(bp->rays_jet, spinjet_blackhole_number_of_rays); + ray_init(bp->rays_jet_pos, spinjet_blackhole_number_of_rays); +} + +/** + * @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) { + + /* Are we doing some repositioning? */ + if (bp->reposition.min_potential != FLT_MAX) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->reposition.delta_x[0] == -FLT_MAX || + bp->reposition.delta_x[1] == -FLT_MAX || + bp->reposition.delta_x[2] == -FLT_MAX) { + error("Something went wrong with the new repositioning position"); + } + + const double dx = bp->reposition.delta_x[0]; + const double dy = bp->reposition.delta_x[1]; + const double dz = bp->reposition.delta_x[2]; + const double d = sqrt(dx * dx + dy * dy + dz * dz); + if (d > 1.01 * kernel_gamma * bp->h) + error("Repositioning BH beyond the kernel support!"); +#endif + + /* Move the black hole */ + bp->x[0] += bp->reposition.delta_x[0]; + bp->x[1] += bp->reposition.delta_x[1]; + bp->x[2] += bp->reposition.delta_x[2]; + + /* Move its gravity properties as well */ + bp->gpart->x[0] += bp->reposition.delta_x[0]; + bp->gpart->x[1] += bp->reposition.delta_x[1]; + bp->gpart->x[2] += bp->reposition.delta_x[2]; + + /* Store the delta position */ + bp->x_diff[0] -= bp->reposition.delta_x[0]; + bp->x_diff[1] -= bp->reposition.delta_x[1]; + bp->x_diff[2] -= bp->reposition.delta_x[2]; + + /* Reset the reposition variables */ + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + bp->reposition.min_potential = FLT_MAX; + + /* Count the jump */ + bp->number_of_repositions++; + } +} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param bp The particle. + */ +__attribute__((always_inline)) INLINE static void +black_holes_reset_predicted_values(struct bpart* bp) {} + +/** + * @brief Kick the additional variables + * + * @param bp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void black_holes_kick_extra( + struct bpart* bp, float dt) {} + +/** + * @brief Finishes the calculation of density on black holes + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_end_density( + struct bpart* bp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* --- Finish the calculation by inserting the missing h factors --- */ + bp->density.wcount *= h_inv_dim; + bp->density.wcount_dh *= h_inv_dim_plus_one; + bp->rho_gas *= h_inv_dim; + const float rho_inv = 1.f / bp->rho_gas; + + /* For the following, we also have to undo the mass smoothing + * (N.B.: bp->velocity_gas is in BH frame, in internal units). */ + bp->sound_speed_gas *= h_inv_dim * rho_inv; + bp->velocity_gas[0] *= h_inv_dim * rho_inv; + bp->velocity_gas[1] *= h_inv_dim * rho_inv; + bp->velocity_gas[2] *= h_inv_dim * rho_inv; + bp->velocity_dispersion_gas *= h_inv_dim * rho_inv; + bp->spec_angular_momentum_gas[0] *= h_inv_dim * rho_inv; + bp->spec_angular_momentum_gas[1] *= h_inv_dim * rho_inv; + bp->spec_angular_momentum_gas[2] *= h_inv_dim * rho_inv; + + /* ... and for the curl, we also need to divide by an extra h factor */ + bp->curl_v_gas[0] *= h_inv_dim_plus_one * rho_inv; + bp->curl_v_gas[1] *= h_inv_dim_plus_one * rho_inv; + bp->curl_v_gas[2] *= h_inv_dim_plus_one * rho_inv; + + /* Calculate circular velocity at the smoothing radius from specific + * angular momentum (extra h_inv) */ + bp->circular_velocity_gas[0] = bp->spec_angular_momentum_gas[0] * h_inv; + bp->circular_velocity_gas[1] = bp->spec_angular_momentum_gas[1] * h_inv; + bp->circular_velocity_gas[2] = bp->spec_angular_momentum_gas[2] * h_inv; + + /* Calculate (actual) gas velocity dispersion. Currently, the variable + * 'velocity_dispersion_gas' holds <v^2> instead. */ + const double speed_gas2 = bp->velocity_gas[0] * bp->velocity_gas[0] + + bp->velocity_gas[1] * bp->velocity_gas[1] + + bp->velocity_gas[2] * bp->velocity_gas[2]; + + bp->velocity_dispersion_gas -= speed_gas2; + bp->velocity_dispersion_gas = sqrt(fabs(bp->velocity_dispersion_gas)); +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +black_holes_bpart_has_no_neighbours(struct bpart* bp, + const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Re-set problematic values */ + bp->density.wcount = kernel_root * h_inv_dim; + bp->density.wcount_dh = 0.f; + + bp->velocity_gas[0] = FLT_MAX; + bp->velocity_gas[1] = FLT_MAX; + bp->velocity_gas[2] = FLT_MAX; + + bp->velocity_dispersion_gas = FLT_MAX; + + bp->curl_v_gas[0] = FLT_MAX; + bp->curl_v_gas[1] = FLT_MAX; + bp->curl_v_gas[2] = FLT_MAX; +} + +/** + * @brief Return the current instantaneous accretion rate of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_accretion_rate(const struct bpart* bp) { + return bp->accretion_rate; +} + +/** + * @brief Return the total accreted gas mass of this BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_accreted_mass(const struct bpart* bp) { + return bp->total_accreted_mass; +} + +/** + * @brief Return the subgrid mass of this BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_subgrid_mass(const struct bpart* bp) { + return bp->subgrid_mass; +} + +/** + * @brief Return the current bolometric luminosity of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_bolometric_luminosity(const struct bpart* bp, + const struct phys_const* constants) { + const double c = constants->const_speed_light_c; + return bp->accretion_rate * bp->radiative_efficiency * c * c; +} + +/** + * @brief Return the current kinetic jet power of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double black_holes_get_jet_power( + const struct bpart* bp, const struct phys_const* constants) { + const double c = constants->const_speed_light_c; + return bp->accretion_rate * bp->jet_efficiency * c * c; +} + +/** + * @brief Update the properties of a black hole particles by swallowing + * a gas particle. + * + * @param bp The #bpart to update. + * @param p The #part that is swallowed. + * @param xp The #xpart that is swallowed. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_swallow_part( + struct bpart* bp, const struct part* p, const struct xpart* xp, + const struct cosmology* cosmo) { + + /* Get the current dynamical masses */ + const float gas_mass = hydro_get_mass(p); + const float BH_mass = bp->mass; + + /* Increase the dynamical mass of the BH. */ + bp->mass += gas_mass; + bp->gpart->mass += gas_mass; + + /* Physical velocity difference between the particles */ + const float dv[3] = {(bp->v[0] - p->v[0]) * cosmo->a_inv, + (bp->v[1] - p->v[1]) * cosmo->a_inv, + (bp->v[2] - p->v[2]) * cosmo->a_inv}; + + /* Physical distance between the particles */ + const float dx[3] = {(bp->x[0] - p->x[0]) * cosmo->a, + (bp->x[1] - p->x[1]) * cosmo->a, + (bp->x[2] - p->x[2]) * cosmo->a}; + + /* Collect the swallowed angular momentum */ + bp->swallowed_angular_momentum[0] += + gas_mass * (dx[1] * dv[2] - dx[2] * dv[1]); + bp->swallowed_angular_momentum[1] += + gas_mass * (dx[2] * dv[0] - dx[0] * dv[2]); + bp->swallowed_angular_momentum[2] += + gas_mass * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Update the BH momentum */ + const float BH_mom[3] = {BH_mass * bp->v[0] + gas_mass * p->v[0], + BH_mass * bp->v[1] + gas_mass * p->v[1], + BH_mass * bp->v[2] + gas_mass * p->v[2]}; + + bp->v[0] = BH_mom[0] / bp->mass; + bp->v[1] = BH_mom[1] / bp->mass; + bp->v[2] = BH_mom[2] / bp->mass; + bp->gpart->v_full[0] = bp->v[0]; + bp->gpart->v_full[1] = bp->v[1]; + bp->gpart->v_full[2] = bp->v[2]; + + const float dr = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]); + message( + "BH %lld swallowing gas particle %lld " + "(Delta_v = [%f, %f, %f] U_V, " + "Delta_x = [%f, %f, %f] U_L, " + "Delta_v_rad = %f)", + bp->id, p->id, -dv[0], -dv[1], -dv[2], -dx[0], -dx[1], -dx[2], + (dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]) / dr); + + /* Update the BH metal masses */ + struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; + const struct chemistry_part_data* p_chem = &p->chemistry_data; + chemistry_add_part_to_bpart(bp_chem, p_chem, gas_mass); + + /* This BH swallowed a gas particle */ + bp->number_of_gas_swallows++; + bp->number_of_direct_gas_swallows++; + + /* This BH lost a neighbour */ + bp->num_ngbs--; + bp->ngb_mass -= gas_mass; + + /* The ray(s) should not point to the no-longer existing particle */ + ray_reset_part_id(bp->rays, spinjet_blackhole_number_of_rays, p->id); + ray_reset_part_id(bp->rays_jet, spinjet_blackhole_number_of_rays, p->id); + ray_reset_part_id(bp->rays_jet_pos, spinjet_blackhole_number_of_rays, p->id); +} + +/** + * @brief Update the properties of a black hole particles by swallowing + * a BH particle. + * + * @param bpi The #bpart to update. + * @param bpj The #bpart that is swallowed. + * @param cosmo The current cosmological model. + * @param time Time since the start of the simulation (non-cosmo mode). + * @param with_cosmology Are we running with cosmology? + * @param props The properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( + struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo, + const double time, const int with_cosmology, + const struct black_holes_props* props, const struct phys_const* constants) { + + /* Get the current dynamical masses */ + const float bpi_dyn_mass = bpi->mass; + const float bpj_dyn_mass = bpj->mass; + + /* Is this merger ratio above the threshold for recording? */ + const double merger_ratio = bpj->subgrid_mass / bpi->subgrid_mass; + if (merger_ratio > props->major_merger_threshold) { + if (with_cosmology) { + bpi->last_major_merger_scale_factor = cosmo->a; + } else { + bpi->last_major_merger_time = time; + } + } else if (merger_ratio > props->minor_merger_threshold) { + if (with_cosmology) { + bpi->last_minor_merger_scale_factor = cosmo->a; + } else { + bpi->last_minor_merger_time = time; + } + } + + /* Evolve the black hole spin. */ + merger_spin_evolve(bpi, bpj, constants); + + /* Increase the masses of the BH. */ + bpi->mass += bpj->mass; + bpi->gpart->mass += bpj->mass; + bpi->subgrid_mass += bpj->subgrid_mass; + + /* We need to see if the new spin is positive or negative, by implementing + King et al. (2005) condition of (counter-)alignment. */ + const float gas_spec_ang_mom_norm = sqrtf( + bpi->spec_angular_momentum_gas[0] * bpi->spec_angular_momentum_gas[0] + + bpi->spec_angular_momentum_gas[1] * bpi->spec_angular_momentum_gas[1] + + bpi->spec_angular_momentum_gas[2] * bpi->spec_angular_momentum_gas[2]); + + float dot_product = -1.; + if (gas_spec_ang_mom_norm > 0.) { + dot_product = 1. / gas_spec_ang_mom_norm * + (bpi->spec_angular_momentum_gas[0] * + bpi->angular_momentum_direction[0] + + bpi->spec_angular_momentum_gas[1] * + bpi->angular_momentum_direction[1] + + bpi->spec_angular_momentum_gas[2] * + bpi->angular_momentum_direction[2]); + } else { + dot_product = 0.; + } + + if (j_BH(bpi, constants) * dot_product < + -0.5 * j_warp(bpi, constants, props)) { + bpi->spin = -1. * fabsf(bpi->spin); + } else { + bpi->spin = fabsf(bpi->spin); + } + + /* Update various quantities with new spin */ + decide_mode(bpi, props); + bpi->aspect_ratio = aspect_ratio(bpi, constants, props); + bpi->accretion_disk_angle = dot_product; + bpi->radiative_efficiency = rad_efficiency(bpi, props); + bpi->jet_efficiency = jet_efficiency(bpi, props); + + /* Collect the swallowed angular momentum */ + bpi->swallowed_angular_momentum[0] += bpj->swallowed_angular_momentum[0]; + bpi->swallowed_angular_momentum[1] += bpj->swallowed_angular_momentum[1]; + bpi->swallowed_angular_momentum[2] += bpj->swallowed_angular_momentum[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]}; + + 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 BH metal masses */ + struct chemistry_bpart_data* bpi_chem = &bpi->chemistry_data; + const struct chemistry_bpart_data* bpj_chem = &bpj->chemistry_data; + chemistry_add_bpart_to_bpart(bpi_chem, bpj_chem); + + /* Update the energy reservoir */ + bpi->energy_reservoir += bpj->energy_reservoir; + + /* Update the jet reservoir */ + bpi->jet_reservoir += bpj->jet_reservoir; + + /* Add up all the BH seeds */ + bpi->cumulative_number_seeds += bpj->cumulative_number_seeds; + + /* Add up all the gas particles we swallowed */ + bpi->number_of_gas_swallows += bpj->number_of_gas_swallows; + + /* Add the subgrid angular momentum that we swallowed */ + bpi->accreted_angular_momentum[0] += bpj->accreted_angular_momentum[0]; + bpi->accreted_angular_momentum[1] += bpj->accreted_angular_momentum[1]; + bpi->accreted_angular_momentum[2] += bpj->accreted_angular_momentum[2]; + + /* We had another merger */ + bpi->number_of_mergers++; +} + +/** + * @brief Compute the accretion rate of the black hole and all the quantites + * required for the feedback loop. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param cosmo The cosmological model. + * @param cooling Properties of the cooling model. + * @param floor_props Properties of the entropy fllor. + * @param time Time since the start of the simulation (non-cosmo mode). + * @param with_cosmology Are we running with cosmology? + * @param dt The time-step size (in physical internal units). + * @param ti_begin Integer time value at the beginning of timestep + */ +__attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo, + const struct cooling_function_data* cooling, + const struct entropy_floor_properties* floor_props, const double time, + const int with_cosmology, const double dt, const integertime_t ti_begin) { + + /* Record that the black hole has another active time step */ + bp->number_of_time_steps++; + + if (dt == 0. || bp->rho_gas == 0.) return; + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->num_ngbs <= 0) { + error( + "The number of BH neighbours is %d, despite the fact that the gas " + " density in the BH kernel is non-zero.", + bp->num_ngbs); + } +#endif + + /* Gather some physical constants (all in internal units) */ + const double G = constants->const_newton_G; + const double c = constants->const_speed_light_c; + const double proton_mass = constants->const_proton_mass; + const double sigma_Thomson = constants->const_thomson_cross_section; + + /* (Subgrid) mass of the BH (internal units) */ + const double BH_mass = bp->subgrid_mass; + + /* Convert the quantities we gathered to physical frame (all internal units). + * Note: for the velocities this means peculiar velocities */ + const double gas_c_phys = bp->sound_speed_gas * cosmo->a_factor_sound_speed; + const double gas_c_phys2 = gas_c_phys * gas_c_phys; + const double gas_v_circular[3] = { + bp->circular_velocity_gas[0] * cosmo->a_inv, + bp->circular_velocity_gas[1] * cosmo->a_inv, + bp->circular_velocity_gas[2] * cosmo->a_inv}; + + /* Norm of the circular velocity of the gas around the BH */ + const double tangential_velocity2 = gas_v_circular[0] * gas_v_circular[0] + + gas_v_circular[1] * gas_v_circular[1] + + gas_v_circular[2] * gas_v_circular[2]; + const double tangential_velocity = sqrt(tangential_velocity2); + + /* We can now compute the accretion rate (internal units) */ + double accr_rate; + + if (props->use_multi_phase_bondi) { + + /* In this case, we are in 'multi-phase-Bondi' mode -- otherwise, + * the accretion_rate is still zero (was initialised to this) */ + const float hi_inv = 1.f / bp->h; + const float hi_inv_dim = pow_dimension(hi_inv); /* 1/h^d */ + accr_rate = bp->accretion_rate * + (4. * M_PI * G * G * BH_mass * BH_mass * hi_inv_dim); + } else { + + /* Standard approach: compute accretion rate for all gas simultaneously. + * + * Convert the quantities we gathered to physical frame (all internal + * units). Note: velocities are already in black hole frame. */ + const double gas_rho_phys = bp->rho_gas * cosmo->a3_inv; + const double gas_v_phys[3] = {bp->velocity_gas[0] * cosmo->a_inv, + bp->velocity_gas[1] * cosmo->a_inv, + bp->velocity_gas[2] * cosmo->a_inv}; + + const double gas_v_norm2 = gas_v_phys[0] * gas_v_phys[0] + + gas_v_phys[1] * gas_v_phys[1] + + gas_v_phys[2] * gas_v_phys[2]; + + /* In the Bondi-Hoyle-Lyttleton formula, the bulk flow of gas is + * added to the sound speed in quadrature. Treated separately (below) + * in the Krumholz et al. (2006) prescription */ + const double denominator2 = + props->use_krumholz ? gas_c_phys2 : gas_v_norm2 + gas_c_phys2; +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure that the denominator is strictly positive */ + if (denominator2 <= 0) + error( + "Invalid denominator for BH particle %lld in Bondi rate " + "calculation.", + bp->id); +#endif + const double denominator_inv = 1. / sqrt(denominator2); + accr_rate = 4. * M_PI * G * G * BH_mass * BH_mass * gas_rho_phys * + denominator_inv * denominator_inv * denominator_inv; + + if (props->use_krumholz) { + + /* Compute the additional correction factors from Krumholz+06, + * accounting for bulk flow and turbulence of ambient gas. */ + const double lambda = 1.1; + const double gas_v_dispersion = + bp->velocity_dispersion_gas * cosmo->a_inv; + const double mach_turb = gas_v_dispersion / gas_c_phys; + const double mach_bulk = sqrt(gas_v_norm2) / gas_c_phys; + const double mach2 = mach_turb * mach_turb + mach_bulk * mach_bulk; + const double m1 = 1. + mach2; + const double mach_factor = + sqrt((lambda * lambda + mach2) / (m1 * m1 * m1 * m1)); + accr_rate *= mach_factor; + } + + if (props->with_krumholz_vorticity) { + + /* Change the accretion rate to equation (3) of Krumholz et al. (2006) + * by adding a vorticity-dependent term in inverse quadrature */ + + /* Convert curl to vorticity in physical units */ + const double gas_curlv_phys[3] = {bp->curl_v_gas[0] * cosmo->a2_inv, + bp->curl_v_gas[1] * cosmo->a2_inv, + bp->curl_v_gas[2] * cosmo->a2_inv}; + const double gas_vorticity = sqrt(gas_curlv_phys[0] * gas_curlv_phys[0] + + gas_curlv_phys[1] * gas_curlv_phys[1] + + gas_curlv_phys[2] * gas_curlv_phys[2]); + + const double Bondi_radius = G * BH_mass / gas_c_phys2; + const double omega_star = gas_vorticity * Bondi_radius / gas_c_phys; + const double f_omega_star = 1.0 / (1.0 + pow(omega_star, 0.9)); + const double mdot_omega = 4. * M_PI * gas_rho_phys * G * G * BH_mass * + BH_mass * denominator_inv * denominator_inv * + denominator_inv * 0.34 * f_omega_star; + + const double accr_rate_inv = 1. / accr_rate; + const double mdot_omega_inv = 1. / mdot_omega; + accr_rate = 1. / sqrt(accr_rate_inv * accr_rate_inv + + mdot_omega_inv * mdot_omega_inv); + } /* ends calculation of vorticity addition to Krumholz prescription */ + + } /* ends section without multi-phase accretion */ + + /* Compute the boost factor from Booth, Schaye (2009) */ + if (props->with_boost_factor) { + const double gas_rho_phys = bp->rho_gas * cosmo->a3_inv; + const double n_H = gas_rho_phys * 0.75 / proton_mass; + const double boost_ratio = n_H / props->boost_n_h_star; + const double boost_factor = + max(pow(boost_ratio, props->boost_beta), props->boost_alpha); + accr_rate *= boost_factor; + } + /* Compute the reduction factor from Rosas-Guevara et al. (2015) */ + if (props->with_angmom_limiter) { + const double Bondi_radius = G * BH_mass / gas_c_phys2; + const double Bondi_time = Bondi_radius / gas_c_phys; + const double r_times_v_tang = Bondi_radius * tangential_velocity; + const double r_times_v_tang_3 = + r_times_v_tang * r_times_v_tang * r_times_v_tang; + const double viscous_time = + 2. * M_PI * r_times_v_tang_3 / + (1e-6 * props->alpha_visc * G * G * BH_mass * BH_mass); + + const double f_visc = min(Bondi_time / viscous_time, 1.); + bp->f_visc = f_visc; + + /* Limit the accretion rate by the Bondi-to-viscous time ratio */ + accr_rate *= f_visc; + } else { + bp->f_visc = 1.0; + } + + /* Compute the Eddington rate (internal units) */ + const double Eddington_rate = + 4. * M_PI * G * BH_mass * proton_mass / + (props->radiative_efficiency * c * sigma_Thomson); + + /* Should we record this time as the most recent high accretion rate? */ + if (accr_rate > props->f_Edd_recording * Eddington_rate) { + if (with_cosmology) { + bp->last_high_Eddington_fraction_scale_factor = cosmo->a; + } else { + bp->last_high_Eddington_fraction_time = time; + } + } + + /* Limit the accretion rate to a fraction of the Eddington rate */ + accr_rate = min(accr_rate, props->f_Edd * Eddington_rate); + bp->accretion_rate = accr_rate; + bp->eddington_fraction = accr_rate / Eddington_rate; + + /* Define feedback-related quantities that we will update and need later on */ + double luminosity = 0.; + double jet_power = 0.; + + /* Check whether we are including ADIOS winds in the thick disk */ + if ((bp->accretion_mode == BH_thick_disc) && + (props->include_ADIOS_suppression)) { + const double Bondi_R = G * BH_mass / gas_c_phys2; + const float ADIOS_suppression = powf( + Bondi_R / (props->ADIOS_R_in * R_gravitational(BH_mass, constants)), + props->ADIOS_s); + accr_rate = accr_rate / ADIOS_suppression; + bp->accretion_rate = accr_rate; + bp->eddington_fraction = accr_rate / Eddington_rate; + } + + /* How much mass will be consumed over this time step? */ + double delta_m_0 = bp->accretion_rate * dt; + + /* Norm of the specific angular momentum, will be needed in a moment. */ + float spec_ang_mom_norm = sqrtf(max( + 0., + bp->spec_angular_momentum_gas[0] * bp->spec_angular_momentum_gas[0] + + bp->spec_angular_momentum_gas[1] * bp->spec_angular_momentum_gas[1] + + bp->spec_angular_momentum_gas[2] * bp->spec_angular_momentum_gas[2])); + + /* Cosine of the angle between the spin vector and the specific angular + momentum vector of gas around the BH. */ + float dot_product = -1.; + if (spec_ang_mom_norm > 0.) { + dot_product = + 1. / spec_ang_mom_norm * + (bp->spec_angular_momentum_gas[0] * bp->angular_momentum_direction[0] + + bp->spec_angular_momentum_gas[1] * bp->angular_momentum_direction[1] + + bp->spec_angular_momentum_gas[2] * bp->angular_momentum_direction[2]); + } else { + dot_product = 0.; + } + + /* Decide if accretion is prograde (spin positive) or retrograde + (spin negative) based on condition from King et al. (2005) */ + if ((j_BH(bp, constants) * dot_product < + -0.5 * j_warp(bp, constants, props)) && + (fabsf(bp->spin) > 0.01)) { + bp->spin = -1. * fabsf(bp->spin); + } else { + bp->spin = fabsf(bp->spin); + } + + /* Calculate how many warp increments the BH will swallow over this time + step */ + double n_i = 0.; + if (bp->accretion_rate > 0.) { + n_i = delta_m_0 / m_warp(bp, constants, props); + } + + /* Update the angular momentum vector of the BH based on how many + increments of warp angular momenta have been consumed. */ + if (spec_ang_mom_norm > 0.) { + + /* If spin is at its floor value of 0.01, we immediately redirect + the spin in the direction of the accreting gas */ + if (fabsf(bp->spin) <= 0.01) { + bp->angular_momentum_direction[0] = + bp->spec_angular_momentum_gas[0] / spec_ang_mom_norm; + bp->angular_momentum_direction[1] = + bp->spec_angular_momentum_gas[1] / spec_ang_mom_norm; + bp->angular_momentum_direction[2] = + bp->spec_angular_momentum_gas[2] / spec_ang_mom_norm; + } else { + const double ang_mom_total[3] = { + bp->angular_momentum_direction[0] * j_BH(bp, constants) + + n_i * bp->spec_angular_momentum_gas[0] / spec_ang_mom_norm * + j_warp(bp, constants, props), + bp->angular_momentum_direction[1] * j_BH(bp, constants) + + n_i * bp->spec_angular_momentum_gas[1] / spec_ang_mom_norm * + j_warp(bp, constants, props), + bp->angular_momentum_direction[2] * j_BH(bp, constants) + + n_i * bp->spec_angular_momentum_gas[2] / spec_ang_mom_norm * + j_warp(bp, constants, props)}; + + /* Modulus of the new J_BH */ + const double modulus = sqrt(ang_mom_total[0] * ang_mom_total[0] + + ang_mom_total[1] * ang_mom_total[1] + + ang_mom_total[2] * ang_mom_total[2]); + + if (modulus > 0.) { + bp->angular_momentum_direction[0] = ang_mom_total[0] / modulus; + bp->angular_momentum_direction[1] = ang_mom_total[1] / modulus; + bp->angular_momentum_direction[2] = ang_mom_total[2] / modulus; + } + } + } + + /* Check if we are fixing the jet along the z-axis. */ + if (props->fix_jet_efficiency) { + bp->angular_momentum_direction[0] = 0.; + bp->angular_momentum_direction[1] = 0.; + bp->angular_momentum_direction[2] = 1; + } + + /* The amount of mass the BH is actually swallowing, including the + effects of efficiencies. */ + const double delta_m_real = + delta_m_0 * (1. - rad_efficiency(bp, props) - jet_efficiency(bp, props)); + + /* Increase the reservoir*/ + bp->jet_reservoir += + delta_m_0 * c * c * props->eps_f_jet * jet_efficiency(bp, props); + bp->energy_reservoir += + delta_m_0 * c * c * props->epsilon_f * rad_efficiency(bp, props); + + float spin_final = -1.; + /* Calculate the change in the BH spin */ + if (bp->subgrid_mass > 0.) { + spin_final = bp->spin + delta_m_0 / bp->subgrid_mass * + da_dln_mbh_0(bp, constants, props); + } else { + error( + "Black hole with id %lld tried to evolve spin with zero " + "(or less) subgrid mass. ", + bp->id); + } + + /* Make sure that the spin does not shoot above 1 or below -1. Physically + this wouldn't happen because the spinup function goes to 0 at a=1, but + numerically it may happen due to finite increments. If this happens, + many spin-related quantities begin to diverge. We also want to avoid + spin equal to zero, or very close to it. The black hole time steps + become shorter and shorter as the BH approaches spin 0, so we simply + 'jump' through 0 instead, choosing a small value of 0.01 (in magnitude) + as a floor for the spin. The spin will always jump to +0.01 since the + spinup function will always make spin go from negative to positive at + these small values. */ + if (spin_final > 0.998) { + spin_final = 0.998; + } else if (spin_final < -0.998) { + spin_final = -0.998; + } else if (fabsf(spin_final) < 0.01) { + spin_final = 0.01; + } + + /* Update the spin and mass. */ + bp->spin = spin_final; + bp->subgrid_mass += delta_m_real; + bp->total_accreted_mass += delta_m_real; + + /* Update the total accreted masses split by accretion mode of the BHs */ + bp->accreted_mass_by_mode[bp->accretion_mode] += delta_m_real; + + if (bp->subgrid_mass < 0.) { + warning( + "Black hole %lld has reached a negative mass (%f) due" + " to jet spindown.", + bp->id, bp->subgrid_mass); + bp->subgrid_mass = props->subgrid_seed_mass; + } + + /* Update other quantities. */ + bp->accretion_disk_angle = dot_product; + bp->aspect_ratio = aspect_ratio(bp, constants, props); + if (props->fix_radiative_efficiency) { + bp->radiative_efficiency = props->radiative_efficiency; + } else { + bp->radiative_efficiency = rad_efficiency(bp, props); + } + if (props->fix_jet_efficiency) { + bp->jet_efficiency = props->jet_efficiency; + } else { + bp->jet_efficiency = jet_efficiency(bp, props); + } + + /* Final jet power at the end of the step */ + jet_power = bp->jet_efficiency * accr_rate * c * c; + + /* Final luminosity at the end of the step */ + luminosity = bp->radiative_efficiency * accr_rate * c * c; + + /* Increase the subgrid angular momentum according to what we accreted + * (already in physical units, a factors from velocity and radius cancel) */ + bp->accreted_angular_momentum[0] += + bp->spec_angular_momentum_gas[0] * delta_m_real; + bp->accreted_angular_momentum[1] += + bp->spec_angular_momentum_gas[1] * delta_m_real; + bp->accreted_angular_momentum[2] += + bp->spec_angular_momentum_gas[2] * delta_m_real; + + if (props->use_nibbling && bp->subgrid_mass < bp->mass) { + /* In this case, the BH is still accreting from its (assumed) subgrid gas + * mass reservoir left over when it was formed. There is some loss in this + * due to radiative and jet losses, so we must decrease the particle mass + * in proprtion to its current accretion rate. We do not account for this + * in the swallowing approach, however. */ + bp->mass -= + (bp->radiative_efficiency + bp->jet_efficiency) * accr_rate * dt; + if (bp->mass < 0) + error( + "Black hole %lld has reached a negative mass (%f). This is " + "not a great situation, so I am stopping.", + bp->id, bp->mass); + } + + /* Below we compute energy required to have a feedback event(s) + * Note that we have subtracted the particles we swallowed from the ngb_mass + * and num_ngbs accumulators. */ + + /* Mean gas particle mass in the BH's kernel */ + const double mean_ngb_mass = bp->ngb_mass / ((double)bp->num_ngbs); + /* Energy per unit mass corresponding to the temperature jump delta_T */ + double delta_u = props->AGN_delta_T_desired * props->temp_to_u_factor; + /* Number of energy injections at this time-step (will be computed below) */ + int number_of_energy_injections; + /* Average total energy needed to heat the target number of Ngbs */ + const double E_feedback_event = + delta_u * mean_ngb_mass * props->num_ngbs_to_heat; + + /* Compute and store BH heating-limited time-step */ + if (luminosity > 0.) { + const float dt_acc = delta_u * mean_ngb_mass * props->num_ngbs_to_heat / + (luminosity * props->epsilon_f); + bp->dt_heat = max(dt_acc, props->time_step_min); + } else { + bp->dt_heat = FLT_MAX; + } + + /* Compute and store BH jet-limited time-step */ + bp->v_jet = black_hole_feedback_dv_jet(bp, props, cosmo, constants); + const float V_jet = bp->v_jet; + if (jet_power > 0.) { + const float dt_acc2 = + 0.5 * mean_ngb_mass * V_jet * V_jet / (jet_power * props->eps_f_jet); + bp->dt_jet = max(dt_acc2, props->time_step_min); + } else { + bp->dt_jet = FLT_MAX; + } + + /* Are we doing some feedback? */ + if (bp->energy_reservoir > E_feedback_event) { + + /* Probability of heating. Relevant only for the stochastic model. */ + double prob = bp->energy_reservoir / (delta_u * bp->ngb_mass); + + /* Compute the number of energy injections based on probability if and + * only if we are using the stochastic (i.e. not deterministic) model + * and the probability prob < 1. */ + if (prob < 1. && !props->AGN_deterministic) { + + /* Initialise counter of energy injections */ + number_of_energy_injections = 0; + + /* How many AGN energy injections will we get? */ + for (int i = 0; i < bp->num_ngbs; i++) { + const double rand = random_unit_interval_part_ID_and_index( + bp->id, i, ti_begin, random_number_BH_feedback); + /* Increase the counter if we are lucky */ + if (rand < prob) number_of_energy_injections++; + } + } + /* Deterministic model or prob >= 1. */ + else { + /* We want to use up all energy available in the reservoir. Therefore, + * number_of_energy_injections is > or = props->num_ngbs_to_heat */ + number_of_energy_injections = + (int)(bp->energy_reservoir / (delta_u * mean_ngb_mass)); + } + + /* Maximum number of energy injections allowed */ + const int N_energy_injections_allowed = + min(spinjet_blackhole_number_of_rays, bp->num_ngbs); + + /* If there are more energy-injection events than min(the number of Ngbs in + * the kernel, maximum number of rays) then lower the number of events & + * proportionally increase the energy per event */ + if (number_of_energy_injections > N_energy_injections_allowed) { + + /* Increase the thermal energy per event */ + const double alpha_thermal = (double)number_of_energy_injections / + (double)N_energy_injections_allowed; + delta_u *= alpha_thermal; + /* Lower the maximum number of events to the max allowed value */ + number_of_energy_injections = N_energy_injections_allowed; + } + + /* Compute how much energy will be deposited onto the gas */ + /* Note that it will in general be different from E_feedback_event if + * gas particles are of different mass. */ + double Energy_deposited = 0.0; + + /* Count the number of unsuccessful energy injections (e.g., if the particle + * that the BH wants to heat has been swallowed and thus no longer exists) + */ + int N_unsuccessful_energy_injections = 0; + + for (int i = 0; i < number_of_energy_injections; i++) { + /* If the gas particle that the BH wants to heat has just been swallowed + * by the same BH, increment the counter of unsuccessful injections. If + * the particle has not been swallowed by the BH, increase the energy that + * will later be subtracted from the BH's energy reservoir. */ + if (bp->rays[i].id_min_length != -1) + Energy_deposited += delta_u * bp->rays[i].mass; + else + N_unsuccessful_energy_injections++; + } + + /* Store all of this in the black hole for delivery onto the gas. */ + bp->to_distribute.AGN_delta_u = delta_u; + bp->to_distribute.AGN_number_of_energy_injections = + number_of_energy_injections; + + /* Subtract the deposited energy from the BH energy reservoir. Note + * that in the stochastic case, the resulting value might be negative. + * This happens when (due to the probabilistic nature of the model) the + * BH injects more energy than it actually has in the reservoir. */ + bp->energy_reservoir -= Energy_deposited; + + /* Total number successful energy injections at this time-step. In each + * energy injection, a certain gas particle from the BH's kernel gets + * heated. (successful = the particle(s) that is going to get heated by + * this BH has not been swallowed by the same BH). */ + const int N_successful_energy_injections = + number_of_energy_injections - N_unsuccessful_energy_injections; + + /* Increase the number of energy injections the black hole has heated so + * far. Note that in the isotropic model, a gas particle may receive AGN + * energy several times at the same time-step. In this case, the number of + * particles heated at this time-step for this BH will be smaller than the + * total number of energy injections for this BH. */ + bp->AGN_number_of_energy_injections += N_successful_energy_injections; + + /* Increase the number of AGN events the black hole has had so far. + * If the BH does feedback, the number of AGN events is incremented by one + */ + bp->AGN_number_of_AGN_events += N_successful_energy_injections > 0; + + /* Update the total (cumulative) energy used for gas heating in AGN feedback + * by this BH */ + bp->AGN_cumulative_energy += Energy_deposited; + + /* Update the total (cumulative) energies of radiative (thermal) feedback + split by accretion mode of the BHs */ + bp->thermal_energy_by_mode[bp->accretion_mode] += Energy_deposited; + + /* Store the time/scale factor when the BH last did AGN feedback */ + if (N_successful_energy_injections) { + if (with_cosmology) { + bp->last_AGN_event_scale_factor = cosmo->a; + } else { + bp->last_AGN_event_time = time; + } + } + } else { + + /* Flag that we don't want to heat anyone */ + bp->to_distribute.AGN_delta_u = 0.f; + bp->to_distribute.AGN_number_of_energy_injections = 0; + } + + /* Calculate energy required for a jet event (with N_jet particles kicked) */ + const double E_jet_event = 0.5 * V_jet * V_jet * mean_ngb_mass * props->N_jet; + + /* Are we doing some feedback? */ + if (bp->jet_reservoir > E_jet_event) { + + int number_of_jet_injections; + double delta_u_jet = 0.5 * V_jet * V_jet; + + number_of_jet_injections = + (int)(bp->jet_reservoir / (0.5 * V_jet * V_jet * mean_ngb_mass)); + + /* Limit the number of injections to 2 x N_jet. The jet feedback time + steps will try to target so that the BH ejects N_jet particles every + time step, but excesses may build up. Allowing for more than N_jet + kicks lets these excesses get released.*/ + if (number_of_jet_injections > 2 * props->N_jet) { + number_of_jet_injections = 2 * props->N_jet; + } else { + number_of_jet_injections = props->N_jet; + } + + /* Compute how much jet energy will be deposited onto the gas */ + /* Note that it will in general be different from E_feedback_event if + * gas particles are of different mass. */ + double Jet_energy_deposited = 0.0; + + /* Count the number of unsuccessful energy injections (e.g., if the particle + * that the BH wants to heat has been swallowed and thus no longer exists) + */ + int N_unsuccessful_jet_injections = 0; + + for (int i = 0; i < number_of_jet_injections; i++) { + + /* If the gas particle that the BH wants to heat has just been swallowed + * by the same BH, increment the counter of unsuccessful injections. If + * the particle has not been swallowed by the BH, increase the energy that + * will later be subtracted from the BH's energy reservoir. + * Loop over jet both rays.*/ + + if (i % 2 == 0) { + if (bp->rays_jet[i].id_min_length != -1) { + Jet_energy_deposited += delta_u_jet * bp->rays_jet[i].mass; + } else { + N_unsuccessful_jet_injections++; + } + } else { + if (bp->rays_jet_pos[i].id_min_length != -1) { + Jet_energy_deposited += delta_u_jet * bp->rays_jet_pos[i].mass; + } else { + N_unsuccessful_jet_injections++; + } + } + } + + /* Store all of this in the black hole for delivery onto the gas. */ + bp->to_distribute.AGN_delta_u_jet = delta_u_jet; + bp->to_distribute.AGN_number_of_jet_injections = number_of_jet_injections; + + /* Subtract the deposited energy from the BH jet energy reservoir. Note + * that in the stochastic case, the resulting value might be negative. + * This happens when (due to the probabilistic nature of the model) the + * BH injects more energy than it actually has in the reservoir. */ + bp->jet_reservoir -= Jet_energy_deposited; + + /* Total number successful jet energy injections at this time-step. In each + * energy injection, a certain gas particle from the BH's kernel gets + * heated. (successful = the particle(s) that is going to get heated by + * this BH has not been swallowed by the same BH). */ + const int N_successful_jet_injections = + number_of_jet_injections - N_unsuccessful_jet_injections; + + /* Increase the number of jet energy injections the black hole has kicked + * so far. */ + bp->AGN_number_of_jet_injections += N_successful_jet_injections; + + /* Increase the number of AGN jet events the black hole has had so far. + * If the BH does feedback, the number of AGN events is incremented by one. + */ + bp->AGN_number_of_AGN_jet_events += N_successful_jet_injections > 0; + + /* Update the total (cumulative) energy used for gas heating in AGN jet + feedback by this BH */ + bp->total_jet_energy += Jet_energy_deposited; + + /* Update the total (cumulative) energies of jet feedback split by + accretion mode of the BHs */ + bp->jet_energy_by_mode[bp->accretion_mode] += Jet_energy_deposited; + + /* Store the time/scale factor when the BH last did AGN jet feedback */ + if (N_successful_jet_injections) { + if (with_cosmology) { + bp->last_AGN_jet_event_scale_factor = cosmo->a; + } else { + bp->last_AGN_jet_event_time = time; + } + } + } else { + + /* Flag that we don't want to kick anyone */ + bp->to_distribute.AGN_number_of_jet_injections = 0; + bp->to_distribute.AGN_delta_u_jet = 0.f; + } + + /* Decide the accretion mode of the BH, based on the new spin and Eddington + * fraction */ + decide_mode(bp, props); + + /* Calculate a BH angular momentum evolution time step. Ths timestep is + chosen so that the BH spin changes by around 10% relative to the current + spin value */ + if ((fabsf(bp->spin) > 0.01) && (bp->accretion_rate > 0.)) { + const float dt_ang_mom = 0.1 * fabsf(bp->spin) / + fabsf(da_dln_mbh_0(bp, constants, props)) * + bp->subgrid_mass / bp->accretion_rate; + bp->dt_ang_mom = dt_ang_mom; + } else { + bp->dt_ang_mom = FLT_MAX; + } +} + +/** + * @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. + * @param dt The black hole particle's time step. + * @param ti_begin The time at the start of the step + */ +__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 double dt, const integertime_t ti_begin) { + + /* First check: did we find any eligible neighbour particle to jump to? */ + if (bp->reposition.min_potential != FLT_MAX) { + + /* Record that we have a (possible) repositioning situation */ + bp->number_of_reposition_attempts++; + + /* Is the potential lower (i.e. the BH is at the bottom already) + * OR is the BH massive enough that we don't reposition? */ + const float potential = gravity_get_comoving_potential(bp->gpart); + 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.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + } else if (props->set_reposition_speed) { + + /* If we are re-positioning, move the BH a fraction of delta_x, so + * that we have a well-defined re-positioning velocity. We have + * checked already that reposition_coefficient_upsilon is positive. */ + const float repos_vel = + props->reposition_coefficient_upsilon * + pow(bp->subgrid_mass / constants->const_solar_mass, + props->reposition_exponent_xi); + + const double dx = bp->reposition.delta_x[0]; + const double dy = bp->reposition.delta_x[1]; + const double dz = bp->reposition.delta_x[2]; + const double d = sqrt(dx * dx + dy * dy + dz * dz); + + /* Convert target reposition velocity to a fractional reposition + * along reposition.delta_x */ + + /* Exclude the pathological case of repositioning by zero distance */ + if (d > 0) { + double repos_frac = repos_vel * dt / d; + + /* We should never get negative repositioning fractions... */ + if (repos_frac < 0) + error("Wanting to reposition by negative fraction (%g)?", repos_frac); + + /* ... but fractions > 1 can occur if the target velocity is high. + * We do not want this, because it could lead to overshooting the + * actual potential minimum. */ + if (repos_frac > 1) repos_frac = 1.; + + bp->reposition.delta_x[0] *= repos_frac; + bp->reposition.delta_x[1] *= repos_frac; + bp->reposition.delta_x[2] *= repos_frac; + } + + /* ends section for fractional repositioning */ + } else { + + /* We _should_ reposition, but not fractionally. Here, we will + * reposition exactly on top of another gas particle - which + * could cause issues, so we add on a small fractional offset + * of magnitude 0.001 h in the reposition delta. */ + + /* Generate three random numbers in the interval [-0.5, 0.5[; id, + * id**2, and id**3 are required to give unique random numbers (as + * random_unit_interval is completely reproducible). */ + const float offset_dx = + random_unit_interval(bp->id, ti_begin, random_number_BH_reposition) - + 0.5f; + const float offset_dy = + random_unit_interval(bp->id * bp->id, ti_begin, + random_number_BH_reposition) - + 0.5f; + const float offset_dz = + random_unit_interval(bp->id * bp->id * bp->id, ti_begin, + random_number_BH_reposition) - + 0.5f; + + const float length_inv = + 1.0f / sqrtf(offset_dx * offset_dx + offset_dy * offset_dy + + offset_dz * offset_dz); + + const float norm = 0.001f * bp->h * length_inv; + + bp->reposition.delta_x[0] += offset_dx * norm; + bp->reposition.delta_x[1] += offset_dy * norm; + bp->reposition.delta_x[2] += offset_dz * norm; + } + } /* ends section if we found eligible repositioning target(s) */ +} + +/** + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on black hole, therefore no need to use + * it. + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_reset_feedback( + struct bpart* restrict bp) { + + bp->to_distribute.AGN_delta_u = 0.f; + bp->to_distribute.AGN_number_of_energy_injections = 0; + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_force[i] = -1; + bp->num_ngb_force = 0; +#endif +} + +/** + * @brief Store the gravitational potential of a black hole by copying it from + * its #gpart friend. + * + * @param bp The black hole particle. + * @param gp The black hole's #gpart. + */ +__attribute__((always_inline)) INLINE static void +black_holes_store_potential_in_bpart(struct bpart* bp, const struct gpart* gp) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->gpart != gp) error("Copying potential to the wrong black hole!"); +#endif + + bp->reposition.potential = gp->potential; +} + +/** + * @brief Store the gravitational potential of a particle by copying it from + * its #gpart friend. + * + * @param p_data The black hole data of a gas particle. + * @param gp The black hole's #gpart. + */ +__attribute__((always_inline)) INLINE static void +black_holes_store_potential_in_part(struct black_holes_part_data* p_data, + const struct gpart* gp) { + p_data->potential = gp->potential; +} + +/** + * @brief Initialise a BH particle that has just been seeded. + * + * @param bp The #bpart to initialise. + * @param props The properties of the black hole scheme. + * @param constants The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param p The #part that became a black hole. + * @param xp The #xpart that became a black hole. + */ +INLINE static void black_holes_create_from_gas( + struct bpart* bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo, + const struct part* p, const struct xpart* xp, + const integertime_t ti_current) { + + /* All the non-basic properties of the black hole have been zeroed + * in the FOF code. We update them here. + * (i.e. position, velocity, mass, time-step have been set) */ + + /* Birth time */ + bp->formation_scale_factor = cosmo->a; + + /* Initial seed mass */ + bp->subgrid_mass = props->subgrid_seed_mass; + + /* Small initial spin in random direction*/ + bp->spin = props->seed_spin; + + const float rand_cos_theta = + 2. * + (0.5 - random_unit_interval(bp->id, ti_current, random_number_BH_spin)); + const float rand_sin_theta = + sqrtf(max(0., (1. - rand_cos_theta) * (1. + rand_cos_theta))); + const float rand_phi = + 2. * M_PI * + random_unit_interval(bp->id * bp->id, ti_current, random_number_BH_spin); + + bp->angular_momentum_direction[0] = rand_sin_theta * cos(rand_phi); + bp->angular_momentum_direction[1] = rand_sin_theta * sin(rand_phi); + bp->angular_momentum_direction[2] = rand_cos_theta; + + bp->aspect_ratio = 0.01f; + bp->jet_efficiency = 0.1f; + bp->radiative_efficiency = 0.1f; + bp->accretion_disk_angle = 0.01f; + bp->accretion_mode = BH_thin_disc; + bp->eddington_fraction = 0.01f; + bp->jet_reservoir = 0.f; + bp->total_jet_energy = 0.f; + bp->dt_jet = 0.f; + bp->dt_ang_mom = 0.f; + bp->v_jet = black_hole_feedback_dv_jet(bp, props, cosmo, constants); + bp->AGN_number_of_AGN_jet_events = 0; + bp->AGN_number_of_jet_injections = 0; + for (int i = 0; i < BH_accretion_modes_count; ++i) + bp->accreted_mass_by_mode[i] = 0.f; + for (int i = 0; i < BH_accretion_modes_count; ++i) + bp->thermal_energy_by_mode[i] = 0.f; + for (int i = 0; i < BH_accretion_modes_count; ++i) + bp->jet_energy_by_mode[i] = 0.f; + + /* We haven't accreted anything yet */ + bp->total_accreted_mass = 0.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; + bp->number_of_gas_swallows = 0; + bp->number_of_direct_gas_swallows = 0; + bp->number_of_time_steps = 0; + + /* We haven't repositioned yet, nor attempted it */ + bp->number_of_repositions = 0; + bp->number_of_reposition_attempts = 0; + + /* Copy over the splitting struct */ + bp->split_data = xp->split_data; + + /* Initial metal masses */ + const float gas_mass = hydro_get_mass(p); + struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; + const struct chemistry_part_data* p_chem = &p->chemistry_data; + chemistry_bpart_from_part(bp_chem, p_chem, gas_mass); + + /* No swallowed angular momentum */ + bp->swallowed_angular_momentum[0] = 0.f; + bp->swallowed_angular_momentum[1] = 0.f; + bp->swallowed_angular_momentum[2] = 0.f; + + /* Last time this BH had a high Eddington fraction */ + bp->last_high_Eddington_fraction_scale_factor = -1.f; + + /* Last time of mergers */ + bp->last_minor_merger_time = -1.; + bp->last_major_merger_time = -1.; + + /* First initialisation */ + black_holes_init_bpart(bp); + + black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); +} + +/** + * @brief Store the halo mass in the fof algorithm for the black + * hole particle. + * + * @param p_data The black hole particle data. + * @param halo_mass The halo mass to update. + */ +__attribute__((always_inline)) INLINE static void black_holes_update_halo_mass( + struct bpart* bp, float halo_mass) { + bp->group_mass = halo_mass; +} + +#endif /* SWIFT_SPIN_JET_BLACK_HOLES_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_debug.h b/src/black_holes/SPIN_JET/black_holes_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..62cf4802dc7121e6a2a73ceba414c71530102771 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_debug.h @@ -0,0 +1,30 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_SPIN_JET_DEBUG_H +#define SWIFT_BLACK_HOLES_SPIN_JET_DEBUG_H + +__attribute__((always_inline)) INLINE static void black_holes_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld] black_holes_part_data:", p->id); + warning("[PID%lld] swallow_id=%lld, potential=%.3e", p->id, + p->black_holes_data.swallow_id, p->black_holes_data.potential); +} + +#endif /* SWIFT_BLACK_HOLES_SPIN_JET_DEBUG_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_iact.h b/src/black_holes/SPIN_JET/black_holes_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..e2ae49843d3893886f7e185fa634b2004d946ca1 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_iact.h @@ -0,0 +1,1090 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2022 Filip Husko (filip.husko@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_SPIN_JET_BH_IACT_H +#define SWIFT_SPIN_JET_BH_IACT_H + +/* Local includes */ +#include "black_holes_parameters.h" +#include "engine.h" +#include "equation_of_state.h" +#include "gravity.h" +#include "gravity_iact.h" +#include "hydro.h" +#include "random.h" +#include "rays.h" +#include "space.h" +#include "timestep_sync_part.h" +#include "tracers.h" + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas, not updated). + * @param xpj The extended data of the second particle (not updated). + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + * @param step The current time-step. + */ +__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 *bi, const struct part *pj, const struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + float wi, wi_dx; + + /* Get r. */ + const float r = sqrtf(r2); + const float r_inv = 1.f / r; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the number of neighbours */ + bi->density.wcount += wi; + bi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + /* Contribution to the number of neighbours */ + bi->num_ngbs++; + + /* Neighbour gas mass */ + const float mj = hydro_get_mass(pj); + + /* Contribution to the BH gas density */ + bi->rho_gas += mj * wi; + + /* Contribution to the total neighbour mass */ + bi->ngb_mass += mj; + + /* Neighbour's sound speed */ + float cj; + if (bh_props->use_subgrid_gas_properties && engine_current_step >= 0) { + const float pressure_j = hydro_get_comoving_pressure(pj); + const float subgrid_dens = cooling_get_subgrid_density(pj, xpj); + cj = gas_soundspeed_from_pressure( + subgrid_dens * cosmo->a * cosmo->a * cosmo->a, pressure_j); + } else { + cj = hydro_get_comoving_soundspeed(pj); + } + + /* Contribution to the smoothed sound speed */ + bi->sound_speed_gas += mj * cj * wi; + + /* Neighbour's (drifted) velocity in the frame of the black hole + * (we do include a Hubble term) */ + const float dv[3] = {pj->v[0] - bi->v[0], pj->v[1] - bi->v[1], + pj->v[2] - bi->v[2]}; + + const float a = cosmo->a; + const float H = cosmo->H; + const float a2H = a * a * H; + + /* Calculate the velocity with the Hubble flow */ + const float v_plus_H_flow[3] = {a2H * dx[0] + dv[0], a2H * dx[1] + dv[1], + a2H * dx[2] + dv[2]}; + + /* Contribution to the smoothed velocity (gas w.r.t. black hole) */ + bi->velocity_gas[0] += mj * dv[0] * wi; + bi->velocity_gas[1] += mj * dv[1] * wi; + bi->velocity_gas[2] += mj * dv[2] * wi; + + /* Contribution to the specific angular momentum of gas, which is later + * converted to the circular velocity at the smoothing length */ + bi->spec_angular_momentum_gas[0] -= mj * wi * (dx[1] * dv[2] - dx[2] * dv[1]); + bi->spec_angular_momentum_gas[1] -= mj * wi * (dx[2] * dv[0] - dx[0] * dv[2]); + bi->spec_angular_momentum_gas[2] -= mj * wi * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Contribution to the smoothed squared relative velocity (for dispersion) + * We will convert this to actual dispersion later. */ + const float norm_v2 = v_plus_H_flow[0] * v_plus_H_flow[0] + + v_plus_H_flow[1] * v_plus_H_flow[1] + + v_plus_H_flow[2] * v_plus_H_flow[2]; + + bi->velocity_dispersion_gas += norm_v2 * wi * mj; + + if (bh_props->use_multi_phase_bondi) { + /* Contribution to BH accretion rate + * + * i) Calculate denominator in Bondi formula */ + const double gas_v_phys[3] = {dv[0] * cosmo->a_inv, dv[1] * cosmo->a_inv, + dv[2] * cosmo->a_inv}; + const double gas_v_norm2 = gas_v_phys[0] * gas_v_phys[0] + + gas_v_phys[1] * gas_v_phys[1] + + gas_v_phys[2] * gas_v_phys[2]; + + const double gas_c_phys = cj * cosmo->a_factor_sound_speed; + const double gas_c_phys2 = gas_c_phys * gas_c_phys; + const double denominator2 = gas_v_norm2 + gas_c_phys2; + +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure that the denominator is strictly positive */ + if (denominator2 <= 0) + error( + "Invalid denominator for BH particle %lld and gas particle " + "%lld in Bondi rate calculation.", + bi->id, pj->id); +#endif + const double denominator_inv = 1. / sqrt(denominator2); + + /* ii) Contribution of gas particle to the BH accretion rate + * (without constant pre-factor) + * N.B.: rhoj is the weighted contribution to BH gas density. */ + const float rhoj = mj * wi * cosmo->a3_inv; + bi->accretion_rate += + rhoj * denominator_inv * denominator_inv * denominator_inv; + } /* End of accretion contribution calculation */ + + /* Need to compute gas vorticity around the black hole, in analogy to + * calculation for Balsara switch in hydro part */ + + /* Factor to make sure we get curl, not angular momentum */ + const float faci = mj * wi_dx * r_inv; + + /* Compute dv cross r */ + const float v_cross_r[3] = {dv[1] * dx[2] - dv[2] * dx[1], + dv[2] * dx[0] - dv[0] * dx[2], + dv[0] * dx[1] - dv[1] * dx[0]}; + + bi->curl_v_gas[0] += faci * v_cross_r[0]; + bi->curl_v_gas[1] += faci * v_cross_r[1]; + bi->curl_v_gas[2] += faci * v_cross_r[2]; + +#ifdef DEBUG_INTERACTIONS_BH + /* Update ngb counters */ + if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_BH) + bi->ids_ngbs_density[si->num_ngb_density] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_density; +#endif + + /* Gas particle id */ + const long long gas_id = pj->id; + + /* Choose AGN feedback model */ + switch (bh_props->feedback_model) { + case AGN_isotropic_model: { + /* Compute arc lengths in AGN isotropic feedback and collect + * relevant data for later use in the feedback_apply loop */ + + /* Loop over rays */ + for (int i = 0; i < spinjet_blackhole_number_of_rays; i++) { + + /* We generate two random numbers that we use + to randomly select the direction of the ith ray */ + + /* Random number in [0, 1[ */ + const double rand_theta = random_unit_interval_part_ID_and_index( + bi->id, i, ti_current, + random_number_isotropic_AGN_feedback_ray_theta); + + /* Random number in [0, 1[ */ + const double rand_phi = random_unit_interval_part_ID_and_index( + bi->id, i, ti_current, + random_number_isotropic_AGN_feedback_ray_phi); + + /* Compute arc length */ + ray_minimise_arclength(dx, r, bi->rays + i, + /*ray_type=*/ray_feedback_thermal, gas_id, + rand_theta, rand_phi, mj, /*ray_ext=*/NULL, + /*v=*/NULL); + } + break; + } + case AGN_minimum_distance_model: { + /* Compute the size of the array that we want to sort. If the current + * function is called for the first time (at this time-step for this BH), + * then bi->num_ngbs = 1 and there is nothing to sort. Note that the + * maximum size of the sorted array cannot be larger then the maximum + * number of rays. */ + const int arr_size = min(bi->num_ngbs, spinjet_blackhole_number_of_rays); + + /* Minimise separation between the gas particles and the BH. The rays + * structs with smaller ids in the ray array will refer to the particles + * with smaller distances to the BH. */ + ray_minimise_distance(r, bi->rays, arr_size, gas_id, mj); + break; + } + } + + const int arr_size_jet = min(bi->num_ngbs, spinjet_blackhole_number_of_rays); + + /* Scalar product of the spin vector and position vector of the gas particle + relative to the BH */ + float cosine_theta = -dx[0] * bi->angular_momentum_direction[0] - + dx[1] * bi->angular_momentum_direction[1] - + dx[2] * bi->angular_momentum_direction[2]; + + /* Norm of the scalar product (ang. mom. dir. is already normalized) */ + const float norm = sqrtf(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]); + + /* Apply the norm to find the cosine of the angle between the two vectors, + if norm is 0 then manually set to small value */ + if (norm > 0.) { + cosine_theta = cosine_theta / norm; + } else { + cosine_theta = 0.001; + } + + /* Define a variable which will be used in ray sorting to make sure that + jet-kicked particles always end up at the end of the rays*/ + float ray_jet_correction = 0.; + + /* Define variables that we will minimize. Two because we have two rays. */ + float quantity_to_minimize = 0.; + float quantity_to_minimize_pos = 0.; + + /* Calculate relative velocity of particle and BH, to be used to see if + this particle was recently kicked */ + const float relative_velocity = + sqrtf((bi->v[0] - pj->v[0]) * (bi->v[0] - pj->v[0]) + + (bi->v[1] - pj->v[1]) * (bi->v[1] - pj->v[1]) + + (bi->v[2] - pj->v[2]) * (bi->v[2] - pj->v[2])) * + cosmo->a_inv; + + /* Choose AGN jet feedback model. Here we calculate the quantities to + minimize, depending on the model. We calculate two numbers, one for each + side of the BH. Particles are prioritized to be kicked from the 'same' + hemisphere as defined by the BH spin vector. However, there may be cases + in which one hemisphere is empty, so particles from the other side are + used. If the particle is on the 'wrong' side of the BH, relative to the + spin vector, we still put it in each ray, but they are pushed to the end + (modulo other particles that were already kicked). We push them to the end + so that they can still be used if the other hemisphere is empty. The way + we push them to the end of the ray is by multiplying whatever quantity is + being minimized by arbitrary numbers (large or small, depending on what is + being minimized). This way, these particles never compete with the ones + that are in the correct hemisphere of the BH. */ + switch (bh_props->jet_feedback_model) { + case AGN_jet_minimum_distance_model: { + + /* Check if the relative velocity is a significant fraction of the jet + launching velocity. If it is, set the ray correction variable to some + arbitrarily large value. */ + if (relative_velocity > 0.3 * bi->v_jet) { + ray_jet_correction = 1e11 * bi->h; + } + + /* In this case we minimize using particle separations from the BH, with + the order closest --> farthest. */ + if (cosine_theta < 0) { + quantity_to_minimize = r + ray_jet_correction; + quantity_to_minimize_pos = 1e8 * r + ray_jet_correction; + } else { + quantity_to_minimize = 1e8 * r + ray_jet_correction; + quantity_to_minimize_pos = r + ray_jet_correction; + } + break; + } + case AGN_jet_maximum_distance_model: { + + /* Check if the relative velocity is a significant fraction of the jet + launching velocity. If it is, set the ray correction variable to some + arbitrarily large value. */ + if (relative_velocity > 0.3 * bi->v_jet) { + ray_jet_correction = 1e13 * 1. / bi->h; + } + + /* In this case we minimize using particle separations from the BH, with + the order farthest --> closest */ + if (cosine_theta < 0) { + quantity_to_minimize = r_inv + ray_jet_correction; + quantity_to_minimize_pos = 1e8 * r_inv + ray_jet_correction; + } else { + quantity_to_minimize = 1e8 * r_inv + ray_jet_correction; + quantity_to_minimize_pos = r_inv + ray_jet_correction; + } + break; + } + case AGN_jet_spin_axis_model: { + + /* Check if the relative velocity is a significant fraction of the jet + launching velocity. If it is, set the ray correction variable to some + arbitrarily large value. */ + if (relative_velocity > 0.3 * bi->v_jet) { + ray_jet_correction = 1e3; + } + + /* In this case we minimize using the angle (cosine) between the position + vector of the particle (relative to the BH) and the spin vector of the + BH. I.e. we launch particles along the spin axis, regardless of the + distances from the BH. */ + quantity_to_minimize = cosine_theta + ray_jet_correction; + quantity_to_minimize_pos = -cosine_theta + ray_jet_correction; + break; + } + case AGN_jet_minimum_density_model: { + + /* Check if the relative velocity is a significant fraction of the jet + launching velocity. If it is, set the ray correction variable to some + arbitrarily large value. */ + if (relative_velocity > 0.3 * bi->v_jet) { + ray_jet_correction = 1e15 * pj->rho; + } + + /* In this case we minimize using particle densities, i.e. we target the + low-density gas. */ + if (cosine_theta < 0) { + quantity_to_minimize = pj->rho + ray_jet_correction; + quantity_to_minimize_pos = 1e8 * pj->rho + ray_jet_correction; + } else { + quantity_to_minimize = 1e8 * pj->rho + ray_jet_correction; + quantity_to_minimize_pos = pj->rho + ray_jet_correction; + } + break; + } + } + + /* Loop over rays and do the actual minimization */ + for (int i = 0; i < spinjet_blackhole_number_of_rays; i++) { + ray_minimise_distance(quantity_to_minimize, bi->rays_jet, arr_size_jet, + gas_id, pj->mass); + ray_minimise_distance(quantity_to_minimize_pos, bi->rays_jet_pos, + arr_size_jet, gas_id, pj->mass); + } +} + +/** + * @brief Swallowing interaction between two particles (non-symmetric). + * + * Function used to identify the gas particle that this BH may move towards. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + * @param step The current time-step. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_repos( + const float r2, const float *dx, const float hi, const float hj, + struct bpart *bi, const struct part *pj, const struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + float wi; + + /* Get r. */ + const float r = sqrtf(r2); + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_eval(ui, &wi); + + /* Start by checking the repositioning criteria */ + + /* (Square of) Max repositioning distance allowed based on the softening */ + const float max_dist_repos2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * + bh_props->max_reposition_distance_ratio * + bh_props->max_reposition_distance_ratio * grav_props->epsilon_baryon_cur * + grav_props->epsilon_baryon_cur; + + /* Is this gas neighbour close enough that we can consider its potential + for repositioning? */ + if (r2 < max_dist_repos2) { + + /* Flag to check whether neighbour is slow enough to be considered + * as repositioning target. Always true if velocity cut is switched off. */ + int neighbour_is_slow_enough = 1; + if (bh_props->with_reposition_velocity_threshold) { + + /* 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; + + /* Compute the maximum allowed velocity */ + float v2_max = bh_props->max_reposition_velocity_ratio * + bh_props->max_reposition_velocity_ratio * + bi->sound_speed_gas * bi->sound_speed_gas; + + /* If desired, limit the value of the threshold (v2_max) to be no + * smaller than a user-defined value */ + if (bh_props->min_reposition_velocity_threshold > 0) { + const float v2_min_thresh = + bh_props->min_reposition_velocity_threshold * + bh_props->min_reposition_velocity_threshold; + v2_max = max(v2_max, v2_min_thresh); + } + + /* Is the neighbour too fast to jump to? */ + if (v2_pec >= v2_max) neighbour_is_slow_enough = 0; + } + + if (neighbour_is_slow_enough) { + + float potential = pj->black_holes_data.potential; + + if (bh_props->correct_bh_potential_for_repositioning) { + /* Let's not include the contribution of the BH + * itself to the potential of the gas particle */ + + /* Note: This assumes the BH and gas have the same + * softening, which is currently true */ + const float eps = gravity_get_softening(bi->gpart, grav_props); + const float eps2 = eps * eps; + const float eps_inv = 1.f / eps; + const float eps_inv3 = eps_inv * eps_inv * eps_inv; + const float BH_mass = bi->mass; + + /* Compute the Newtonian or truncated potential the BH + * exherts onto the gas particle */ + float dummy, pot_ij; + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy, + &pot_ij); + + /* Deduct the BH contribution */ + potential -= pot_ij * grav_props->G_Newton; + } + + /* Is the potential lower? */ + if (potential < bi->reposition.min_potential) { + + /* Store this as our new best */ + bi->reposition.min_potential = potential; + bi->reposition.delta_x[0] = -dx[0]; + bi->reposition.delta_x[1] = -dx[1]; + bi->reposition.delta_x[2] = -dx[2]; + } + } + } +} + +/** + * @brief Swallowing interaction between two particles (non-symmetric). + * + * Function used to flag the gas 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 pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + * @param step The current time-step. + */ +__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 *bi, struct part *pj, struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + float wi; + + /* Get r. */ + const float r = sqrtf(r2); + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float hi_inv_dim = pow_dimension(hi_inv); + const float ui = r * hi_inv; + kernel_eval(ui, &wi); + + /* Check if the BH needs to be fed. If not, we're done here */ + const float bh_mass_deficit = bi->subgrid_mass - bi->mass_at_start_of_step; + if (bh_mass_deficit <= 0) return; + + if (bh_props->use_nibbling) { + + /* If we do nibbling, things are quite straightforward. We transfer + * the mass and all associated quantities right here. */ + + const float bi_mass_orig = bi->mass; + const float pj_mass_orig = hydro_get_mass(pj); + + /* Don't nibble from particles that are too small already */ + if (pj_mass_orig < bh_props->min_gas_mass_for_nibbling) return; + + /* Next line is equivalent to w_ij * m_j / Sum_j (w_ij * m_j) */ + const float particle_weight = hi_inv_dim * wi * pj_mass_orig / bi->rho_gas; + float nibble_mass = bh_mass_deficit * particle_weight; + + /* We radiated away some of the accreted mass, so need to take slightly + * more from the gas than the BH gained */ + const float excess_fraction = 1.0 / (1.0 - bi->radiative_efficiency); + + /* Need to check whether nibbling would push gas mass below minimum + * allowed mass */ + float new_gas_mass = pj_mass_orig - nibble_mass * excess_fraction; + if (new_gas_mass < bh_props->min_gas_mass_for_nibbling) { + new_gas_mass = bh_props->min_gas_mass_for_nibbling; + nibble_mass = (pj_mass_orig - bh_props->min_gas_mass_for_nibbling) / + excess_fraction; + } + + /* Correct for nibbling the particle mass that is stored in rays */ + for (int i = 0; i < spinjet_blackhole_number_of_rays; i++) { + if (bi->rays[i].id_min_length == pj->id) bi->rays[i].mass = new_gas_mass; + if (bi->rays_jet[i].id_min_length == pj->id) { + bi->rays_jet[i].mass = new_gas_mass; + } + if (bi->rays_jet_pos[i].id_min_length == pj->id) { + bi->rays_jet_pos[i].mass = new_gas_mass; + } + } + + /* Transfer (dynamical) mass from the gas particle to the BH */ + bi->mass += nibble_mass; + hydro_set_mass(pj, new_gas_mass); + + /* Add the angular momentum of the accreted gas to the BH total. + * Note no change to gas here. The cosmological conversion factors for + * velocity (a^-1) and distance (a) cancel out, so the angular momentum + * is already in physical units. */ + const float dv[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1], + bi->v[2] - pj->v[2]}; + bi->swallowed_angular_momentum[0] += + nibble_mass * (dx[1] * dv[2] - dx[2] * dv[1]); + bi->swallowed_angular_momentum[1] += + nibble_mass * (dx[2] * dv[0] - dx[0] * dv[2]); + bi->swallowed_angular_momentum[2] += + nibble_mass * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Update the BH momentum and velocity. Again, no change to gas here. */ + const float bi_mom[3] = {bi_mass_orig * bi->v[0] + nibble_mass * pj->v[0], + bi_mass_orig * bi->v[1] + nibble_mass * pj->v[1], + bi_mass_orig * bi->v[2] + nibble_mass * pj->v[2]}; + + bi->v[0] = bi_mom[0] / bi->mass; + bi->v[1] = bi_mom[1] / bi->mass; + bi->v[2] = bi_mom[2] / bi->mass; + + const float nibbled_mass = nibble_mass * excess_fraction; + const float nibbled_fraction = nibbled_mass / pj_mass_orig; + + /* Update the BH and also gas metal masses */ + struct chemistry_bpart_data *bi_chem = &bi->chemistry_data; + struct chemistry_part_data *pj_chem = &pj->chemistry_data; + chemistry_transfer_part_to_bpart(bi_chem, pj_chem, nibbled_mass, + nibbled_fraction); + + } else { /* ends nibbling section, below comes swallowing */ + + /* Probability to swallow this particle + * Recall that in SWIFT the SPH kernel is recovered by computing + * kernel_eval() and muliplying by (1/h^d) */ + const float prob = + (bi->subgrid_mass - bi->mass) * hi_inv_dim * wi / bi->rho_gas; + + /* Draw a random number (Note mixing both IDs) */ + const float rand = random_unit_interval(bi->id + pj->id, ti_current, + random_number_BH_swallow); + + /* Are we lucky? */ + if (rand < prob) { + + /* This particle is swallowed by the BH with the largest ID of all the + * candidates wanting to swallow it */ + if (pj->black_holes_data.swallow_id < bi->id) { + + message("BH %lld wants to swallow gas particle %lld", bi->id, pj->id); + + pj->black_holes_data.swallow_id = bi->id; + + } else { + + message( + "BH %lld wants to swallow gas particle %lld BUT CANNOT (old " + "swallow id=%lld)", + bi->id, pj->id, pj->black_holes_data.swallow_id); + } + } + } /* ends section for swallowing */ +} + +/** + * @brief Swallowing interaction between two BH particles (non-symmetric). + * + * Function used to identify the BH particle that this BH may move towards. + * + * @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 bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_bh_repos(const float r2, const float *dx, const float hi, + const float hj, struct bpart *bi, + struct bpart *bj, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_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; + + /* (Square of) Max repositioning distance allowed based on the softening */ + const float max_dist_repos2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * + bh_props->max_reposition_distance_ratio * + bh_props->max_reposition_distance_ratio * grav_props->epsilon_baryon_cur * + grav_props->epsilon_baryon_cur; + + /* Is this BH neighbour close enough that we can consider its potential + for repositioning? */ + if (r2 < max_dist_repos2) { + + /* Flag to check whether neighbour is slow enough to be considered + * as repositioning target. Always true if velocity cut switched off */ + int neighbour_is_slow_enough = 1; + if (bh_props->with_reposition_velocity_threshold) { + + /* Compute the maximum allowed velocity */ + float v2_max = bh_props->max_reposition_velocity_ratio * + bh_props->max_reposition_velocity_ratio * + bi->sound_speed_gas * bi->sound_speed_gas; + + /* If desired, limit the value of the threshold (v2_max) to be no + * smaller than a user-defined value */ + if (bh_props->min_reposition_velocity_threshold > 0) { + const float v2_min_thresh = + bh_props->min_reposition_velocity_threshold * + bh_props->min_reposition_velocity_threshold; + v2_max = max(v2_max, v2_min_thresh); + } + + /* Is the neighbour too fast to jump to? */ + if (v2_pec >= v2_max) neighbour_is_slow_enough = 0; + } + + if (neighbour_is_slow_enough) { + + float potential = bj->reposition.potential; + + if (bh_props->correct_bh_potential_for_repositioning) { + + /* Let's not include the contribution of the BH i + * to the potential of the BH j */ + + const float eps = gravity_get_softening(bi->gpart, grav_props); + const float eps2 = eps * eps; + const float eps_inv = 1.f / eps; + const float eps_inv3 = eps_inv * eps_inv * eps_inv; + const float BH_mass = bi->mass; + + /* Compute the Newtonian or truncated potential the BH + * exherts onto the gas particle */ + float dummy, pot_ij; + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy, + &pot_ij); + + /* Deduct the BH contribution */ + potential -= pot_ij * grav_props->G_Newton; + } + + /* Is the potential lower? */ + if (potential < bi->reposition.min_potential) { + + /* Store this as our new best */ + bi->reposition.min_potential = potential; + bi->reposition.delta_x[0] = -dx[0]; + bi->reposition.delta_x[1] = -dx[1]; + bi->reposition.delta_x[2] = -dx[2]; + } + } + } +} + +/** + * @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 bh_props The properties of the BH scheme + * @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 *bi, struct bpart *bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_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; + + /* 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; + } + + /* (Square of) max swallowing distance allowed based on the softening */ + const float max_dist_merge2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * + bh_props->max_merging_distance_ratio * + bh_props->max_merging_distance_ratio * 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)) { + + /* Maximum velocity difference between BHs allowed to merge */ + float v2_threshold; + + if (bh_props->merger_threshold_type == BH_mergers_circular_velocity) { + + /* 'Old-style' merger threshold using circular velocity at the + * edge of the more massive BH's kernel (note: we are using the kernel + * support radius here and not just the smoothing length). */ + v2_threshold = G_Newton * M / (kernel_gamma * h); + } else { + + /* Arguably better merger threshold using the escape velocity at + * the distance between the BHs */ + + if (bh_props->merger_threshold_type == BH_mergers_escape_velocity) { + /* Standard formula (not softening BH interactions) */ + v2_threshold = 2.f * G_Newton * M / sqrt(r2); + } else if (bh_props->merger_threshold_type == + BH_mergers_dynamical_escape_velocity) { + /* General two-body escape velocity based on dynamical masses */ + v2_threshold = 2.f * G_Newton * (bi->mass + bj->mass) / sqrt(r2); + } else { + error("Unexpected BH merger threshold type!"); + v2_threshold = 0.f; + } + } /* Ends sections for different merger thresholds */ + + if ((v2_pec < v2_threshold) && (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). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time current physical time in the simulation + * @param step The current time-step. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_feedback( + const float r2, const float *dx, const float hi, const float hj, + const struct bpart *bi, struct part *pj, struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + /* Number of energy injections per BH per time-step */ + const int num_energy_injections_per_BH = + bi->to_distribute.AGN_number_of_energy_injections; + + /* Are we doing some feedback? */ + if (num_energy_injections_per_BH > 0) { + + /* Number of energy injections that have reached this gas particle */ + int num_of_energy_inj_received_by_gas = 0; + + /* Find out how many rays (= energy injections) this gas particle + * has received */ + for (int i = 0; i < num_energy_injections_per_BH; i++) { + if (pj->id == bi->rays[i].id_min_length) + num_of_energy_inj_received_by_gas++; + } + + /* If the number of received rays is non-zero, inject + * AGN energy in thermal form */ + if (num_of_energy_inj_received_by_gas > 0) { + + /* Compute new energy per unit mass of this particle + * The energy the particle receives is proportional to the number of rays + * (num_of_energy_inj_received_by_gas) to which the particle was found to + * be closest. */ + const double u_init = hydro_get_physical_internal_energy(pj, xpj, cosmo); + const float delta_u = bi->to_distribute.AGN_delta_u * + (float)num_of_energy_inj_received_by_gas; + const double u_new = u_init + delta_u; + + hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(pj, cosmo, u_new); + + /* Impose maximal viscosity */ + hydro_diffusive_feedback_reset(pj); + + /* Store the feedback energy */ + const double delta_energy = delta_u * hydro_get_mass(pj); + tracers_after_black_holes_feedback(pj, xpj, with_cosmology, cosmo->a, + time, delta_energy); + + /* message( */ + /* "We did some AGN heating! id %llu BH id %llu probability " */ + /* " %.5e random_num %.5e du %.5e du/ini %.5e", */ + /* pj->id, bi->id, prob, rand, delta_u, delta_u / u_init); */ + + /* Synchronize the particle on the timeline */ + timestep_sync_part(pj); + } + } + + /* Number of jet injections per BH per time-step */ + const int num_jet_injections_per_BH = + bi->to_distribute.AGN_number_of_jet_injections; + + /* Are we doing some jet feedback? */ + if (num_jet_injections_per_BH > 0) { + + /* Number of jet injections that have reached this gas particle */ + int num_of_jet_inj_received_by_gas = 0; + + /* Define a variable to assign a velocity kick direction depending + on which side of the BH smoothing kernel the particle is */ + float direction = 0.; + + /* Find out if this gas particle has received any jet injections (rays). + Loop through num_jet_injections divided by 2 because of two sets of rays */ + for (int i = 0; i < num_jet_injections_per_BH / 2; i++) { + if (pj->id == bi->rays_jet[i].id_min_length) { + + num_of_jet_inj_received_by_gas++; + + /*This particle is in the 'negative' hemisphere (pointing away from the + spin vector of the BH), so it receives a negative kick direction */ + direction = -1.; + } + } + + for (int i = 0; i < num_jet_injections_per_BH / 2; i++) { + if (pj->id == bi->rays_jet_pos[i].id_min_length) { + + num_of_jet_inj_received_by_gas++; + + /* This particle is in the 'positive' hemisphere (pointing in the + direction of the spin vector of the BH), so it receives a positive + kick direction */ + direction = 1.; + } + } + + /* If the number of received rays is non-zero, inject + * AGN jet energy as a kinetic kick */ + if (num_of_jet_inj_received_by_gas > 0) { + + /* Get the kinetic energy per unit mass */ + const float delta_u_jet = bi->to_distribute.AGN_delta_u_jet * + (float)num_of_jet_inj_received_by_gas; + + /* Get the (physical) kick velocity, and convert to code units */ + const float vel_kick = sqrtf(2. * delta_u_jet) * cosmo->a; + + /* Compute velocity kick direction using a function for generating a + * random unit vector within a cone around the spin vector.*/ + float vel_kick_direction[3]; + random_direction_in_cone(bi->id, pj->id, ti_current, + random_number_BH_kick, bh_props->opening_angle, + bi->angular_momentum_direction, + vel_kick_direction); + + /* Include the -1./1. factor (direction) which accounts for kicks in the + * opposite direction of the spin vector */ + vel_kick_direction[0] = direction * vel_kick_direction[0]; + vel_kick_direction[1] = direction * vel_kick_direction[1]; + vel_kick_direction[2] = direction * vel_kick_direction[2]; + + /* Get the initial velocity */ + const float v_init[3] = {xpj->v_full[0], xpj->v_full[1], xpj->v_full[2]}; + + /* We compute this final velocity by requiring that the final energy and + * the inital one differ by the energy received by the particle, i.e. + * + * (pi + delta_pi)^2 / (2m) - pi^2 / (2m) = u, + * + * u here being the energy per unit mass received by the particle. pi is + * the initial momentum, and the momenta terms are expressed in vector + * form. The equation, if expressed in terms of velocities, amounts to + * + * norm(delta_v) + 2 * norm(delta_v) * norm(v_i) * cos_theta_v = v_k^2. + * + * Here, delta_v is the change in velocity which we wish to apply, v_i is + * the initial velocity, cos_theta_v the cosine of the angle between the + * two and v_k^2 is the vel_kick term computed from the energy received + * by the particle. The delta_v applied to the particle will differ in + * norm from the parameter v_j used for jet feedback for two reasons: + * 1) v_k is slightly different from v_j if the particle mass is not + * equal to the mean neighbour mass, and 2) the presence of the initial + * velocity means we need to increase the magnitude of the velocity by + * less than v_j in order to increase its energy by (1/2)mv_j^2. We solve + * the above quadratic equation for the norm(delta_v). We begin by + * calculating norm(v_i) * cos_theta_v, which is the initial velocity + * projected onto the velocity kick direction. */ + const float v_init_proj = v_init[0] * vel_kick_direction[0] + + v_init[1] * vel_kick_direction[1] + + v_init[2] * vel_kick_direction[2]; + const float delta_v = + sqrtf(max(0., v_init_proj * v_init_proj + vel_kick * vel_kick)) - + v_init_proj; + + /* Calculate final velocity by adding delta_v in the direction of the kick + */ + xpj->v_full[0] += delta_v * vel_kick_direction[0]; + xpj->v_full[1] += delta_v * vel_kick_direction[1]; + xpj->v_full[2] += delta_v * vel_kick_direction[2]; + +#ifdef SWIFT_DEBUG_CHECKS + message( + "Black hole with id %lld kicked particle with id %lld , with a final " + "velocity of (%f, %f, %f).", + bi->id, pj->id, xpj->v_full[0], xpj->v_full[1], xpj->v_full[2]); +#endif + + /* Store the jet energy */ + const double delta_energy_jet = delta_u_jet * hydro_get_mass(pj); + tracers_after_jet_feedback(pj, xpj, with_cosmology, cosmo->a, time, + delta_energy_jet); + + /* Impose maximal viscosity */ + hydro_diffusive_feedback_reset(pj); + + /* Update the signal velocity */ + hydro_set_v_sig_based_on_velocity_kick(pj, cosmo, + sqrtf(2. * delta_u_jet)); + + /* Synchronize particle on the time-line */ + timestep_sync_part(pj); + } + } + +#ifdef DEBUG_INTERACTIONS_BH + /* Update ngb counters */ + if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_BH) + bi->ids_ngbs_force[si->num_ngb_force] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_force; +#endif +} + +#endif /* SWIFT_SPIN_JET_BH_IACT_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_io.h b/src/black_holes/SPIN_JET/black_holes_io.h new file mode 100644 index 0000000000000000000000000000000000000000..d3e80a34d0edb213c61d4794bb84208cb60939e2 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_io.h @@ -0,0 +1,526 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2022 Filip Husko (filip.husko@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_SPIN_JET_BLACK_HOLES_IO_H +#define SWIFT_SPIN_JET_BLACK_HOLES_IO_H + +#include "adiabatic_index.h" +#include "black_holes_part.h" +#include "io_properties.h" + +/** + * @brief Specifies which b-particle fields to read from a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void black_holes_read_particles(struct bpart* bparts, + struct io_props* list, + int* num_fields) { + + /* Say how much we want to read */ + *num_fields = 9; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, bparts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, bparts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + bparts, mass); + list[3] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, bparts, id); + list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, + UNIT_CONV_LENGTH, bparts, h); + list[5] = io_make_input_field("EnergyReservoir", FLOAT, 1, OPTIONAL, + UNIT_CONV_ENERGY, bparts, energy_reservoir); + list[6] = io_make_input_field("SubgridMasses", FLOAT, 1, OPTIONAL, + UNIT_CONV_MASS, bparts, subgrid_mass); + list[7] = io_make_input_field("Spins", FLOAT, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, bparts, spin); + list[8] = io_make_input_field("AngularMomentumDirections", FLOAT, 3, + COMPULSORY, UNIT_CONV_NO_UNITS, bparts, + angular_momentum_direction); +} + +INLINE static void convert_bpart_pos(const struct engine* e, + const struct bpart* bp, double* ret) { + + const struct space* s = e->s; + if (s->periodic) { + ret[0] = box_wrap(bp->x[0], 0.0, s->dim[0]); + ret[1] = box_wrap(bp->x[1], 0.0, s->dim[1]); + ret[2] = box_wrap(bp->x[2], 0.0, 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 to physical units */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + +INLINE static void convert_bpart_potential(const struct engine* e, + const struct bpart* bp, float* ret) { + + if (bp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(bp->gpart); + else + ret[0] = 0.f; +} + +INLINE static void convert_bpart_gas_vel(const struct engine* e, + const struct bpart* bp, float* ret) { + + const struct cosmology* cosmo = e->cosmology; + + /* Convert relative velocities to physical units */ + ret[0] = bp->velocity_gas[0] * cosmo->a_inv; + ret[1] = bp->velocity_gas[1] * cosmo->a_inv; + ret[2] = bp->velocity_gas[2] * cosmo->a_inv; +} + +INLINE static void convert_bpart_gas_circular_vel(const struct engine* e, + const struct bpart* bp, + float* ret) { + + const struct cosmology* cosmo = e->cosmology; + + /* Conversion from internal to physical units */ + ret[0] = bp->circular_velocity_gas[0] * cosmo->a_inv; + ret[1] = bp->circular_velocity_gas[1] * cosmo->a_inv; + ret[2] = bp->circular_velocity_gas[2] * cosmo->a_inv; +} + +INLINE static void convert_bpart_gas_velocity_dispersion(const struct engine* e, + const struct bpart* bp, + float* ret) { + + const struct cosmology* cosmo = e->cosmology; + + /* Conversion from internal to physical units */ + ret[0] = bp->velocity_dispersion_gas * cosmo->a_inv; +} + +INLINE static void convert_bpart_gas_velocity_curl(const struct engine* e, + const struct bpart* bp, + float* ret) { + + const struct cosmology* cosmo = e->cosmology; + + /* Conversion from internal to physical units */ + ret[0] = bp->curl_v_gas[0] * cosmo->a2_inv; + ret[1] = bp->curl_v_gas[1] * cosmo->a2_inv; + ret[2] = bp->curl_v_gas[2] * cosmo->a2_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, + const int with_cosmology) { + + /* Say how much we want to write */ + *num_fields = 52; + + /* List what we want to write */ + 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("DynamicalMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, mass, "Dynamical masses of the particles"); + + list[3] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 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, 0.f, bparts, + total_accreted_mass, + "Total mass accreted onto the main progenitor of the black holes since " + "birth. This does not include any mass accreted onto any merged black " + "holes."); + + list[12] = io_make_output_field( + "CumulativeNumberOfSeeds", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + cumulative_number_seeds, + "Total number of BH seeds that have merged into this black hole"); + + list[13] = + io_make_output_field("NumberOfMergers", INT, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, number_of_mergers, + "Number of mergers the black holes went through. " + "This does not include the number of mergers " + "accumulated by any merged black hole."); + + if (with_cosmology) { + list[14] = io_make_output_field( + "LastHighEddingtonFractionScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, + 0.f, bparts, last_high_Eddington_fraction_scale_factor, + "Scale-factors at which the black holes last reached a large Eddington " + "ratio. -1 if never reached."); + } else { + list[14] = io_make_output_field( + "LastHighEddingtonFractionTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + last_high_Eddington_fraction_time, + "Times at which the black holes last reached a large Eddington ratio. " + "-1 if never reached."); + } + + if (with_cosmology) { + list[15] = io_make_output_field( + "LastMinorMergerScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, last_minor_merger_scale_factor, + "Scale-factors at which the black holes last had a minor merger."); + } else { + list[15] = io_make_output_field( + "LastMinorMergerScaleTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + last_minor_merger_time, + "Times at which the black holes last had a minor merger."); + } + + if (with_cosmology) { + list[16] = io_make_output_field( + "LastMajorMergerScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, last_major_merger_scale_factor, + "Scale-factors at which the black holes last had a major merger."); + } else { + list[16] = io_make_output_field( + "LastMajorMergerScaleTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + last_major_merger_time, + "Times at which the black holes last had a major merger."); + } + + list[17] = io_make_output_field( + "SwallowedAngularMomenta", FLOAT, 3, UNIT_CONV_ANGULAR_MOMENTUM, 0.f, + bparts, swallowed_angular_momentum, + "Physical angular momenta that the black holes have accumulated by " + "swallowing gas particles."); + + list[18] = io_make_output_field_convert_bpart( + "GasRelativeVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, + convert_bpart_gas_vel, + "Peculiar relative velocities of the gas particles around the black " + "holes. This is a * dx/dt where x is the co-moving position of the " + "particles."); + + list[19] = io_make_output_field_convert_bpart( + "GasCircularVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, + convert_bpart_gas_circular_vel, + "Circular velocities of the gas around the black hole at the " + "smoothing radius. This is j / h_BH, where j is the smoothed, peculiar " + "specific angular momentum of gas around the black holes, and h_BH is " + "the smoothing length of each black hole."); + + list[20] = + io_make_output_field("TimeBins", CHAR, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + time_bin, "Time-bins of the particles"); + + list[21] = io_make_output_field( + "NumberOfSwallows", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_gas_swallows, + "Number of gas particles the black holes have swallowed. " + "This includes the particles swallowed by any of the black holes that " + "merged into this one."); + + list[22] = io_make_output_field( + "NumberOfDirectSwallows", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_direct_gas_swallows, + "Number of gas particles the black holes have swallowed. " + "This does not include any particles swallowed by any of the black holes " + "that merged into this one."); + + list[23] = io_make_output_field( + "NumberOfRepositions", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_repositions, + "Number of repositioning events the black holes went through. This does " + "not include the number of reposition events accumulated by any merged " + "black holes."); + + list[24] = io_make_output_field( + "NumberOfRepositionAttempts", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_reposition_attempts, + "Number of time steps in which the black holes had an eligible particle " + "to reposition to. They may or may not have ended up moving there, " + "depending on their subgrid mass and on whether these particles were at " + "a lower or higher potential than the black holes themselves. It does " + "not include attempted repositioning events accumulated by any merged " + "black holes."); + + list[25] = io_make_output_field( + "NumberOfTimeSteps", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_time_steps, + "Total number of time steps at which the black holes were active."); + + list[26] = io_make_output_field( + "ViscosityFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, f_visc, + "Multiplicative factors by which the Bondi-Hoyle-Lyttleton accretion " + "rates have been suppressed by the Rosas-Guevara et al. (2015) " + "accretion disc model."); + + list[27] = io_make_output_field_convert_bpart( + "GasVelocityDispersions", FLOAT, 1, UNIT_CONV_SPEED, 0.f, bparts, + convert_bpart_gas_velocity_dispersion, + "Velocity dispersion (3D) of the gas particles around the black " + "holes. This is a * sqrt(<|dx/dt|^2> - <|dx/dt|>^2) where x is the " + "co-moving position of the particles relative to the black holes."); + + list[28] = io_make_output_field_convert_bpart( + "GasCurlVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, + convert_bpart_gas_velocity_curl, + "Velocity curl (3D) of the gas particles around the black holes."); + + list[29] = io_make_output_field( + "AccretedAngularMomenta", FLOAT, 3, UNIT_CONV_ANGULAR_MOMENTUM, 0.f, + bparts, accreted_angular_momentum, + "Physical angular momenta that the black holes have accumulated through " + "subgrid accretion."); + + list[30] = io_make_output_field( + "NumberOfGasNeighbours", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + num_ngbs, + "Integer number of gas neighbour particles within the black hole " + "kernels."); + + list[31] = io_make_output_field( + "NumberOfHeatingEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + AGN_number_of_energy_injections, + "Integer number of (thermal) energy injections the black hole has had " + "so far. This counts each heated gas particle separately, and so can " + "increase by more than one during a single time step."); + + list[32] = io_make_output_field( + "NumberOfAGNEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + AGN_number_of_AGN_events, + "Integer number of AGN events the black hole has had so far" + " (the number of time steps the BH did AGN feedback)"); + + if (with_cosmology) { + list[33] = io_make_output_field( + "LastAGNFeedbackScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, last_AGN_event_scale_factor, + "Scale-factors at which the black holes last had an AGN event."); + } else { + list[33] = io_make_output_field( + "LastAGNFeedbackTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + last_AGN_event_time, + "Times at which the black holes last had an AGN event."); + } + + list[34] = io_make_output_field( + "AccretionLimitedTimeSteps", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + dt_heat, + "Accretion-limited time steps of black holes. The actual time step of " + "the particles may differ due to the minimum allowed value."); + + list[35] = io_make_output_field( + "AGNTotalInjectedEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, + AGN_cumulative_energy, + "Total (cumulative) physical energies injected into gas particles " + "in AGN feedback."); + + list[36] = io_make_output_field_convert_bpart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, bparts, + convert_bpart_potential, "Gravitational potentials of the particles"); + + list[37] = io_make_output_field( + "Spins", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, spin, + "Dimensionless spins of the black holes. " + "Negative values indicate retrograde accretion."); + + list[38] = io_make_output_field( + "AngularMomentumDirections", FLOAT, 3, UNIT_CONV_NO_UNITS, 0.f, bparts, + angular_momentum_direction, + "Direction of the black hole spin vector, normalised to unity."); + + list[39] = io_make_output_field( + "AccretionDiscAspectRatios", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + aspect_ratio, + "The aspect ratio, h/r, of the subgrid accretion disc " + "around the black hole."); + + list[40] = io_make_output_field( + "JetEfficiencies", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + jet_efficiency, "Jet power divided by accretion rate."); + + list[41] = io_make_output_field( + "RadiativeEfficiencies", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + radiative_efficiency, "AGN luminosity divided by accretion rate."); + + list[42] = io_make_output_field("CosAccretionDiskAngle", FLOAT, 1, + UNIT_CONV_NO_UNITS, 0.f, bparts, + accretion_disk_angle, + "Cosine of the angle between the spin vector " + "and the accreting gas angular momentum."); + + list[43] = io_make_output_field( + "AccretionModes", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, accretion_mode, + "Accretion flow regime. 0 - Thick disk, 1 - Thin disk, 2 - Slim disk"); + + list[44] = io_make_output_field( + "JetReservoir", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, jet_reservoir, + "Total jet energy waiting to be released (once it " + "grows large enough to kick a single particle)."); + + list[45] = io_make_output_field( + "InjectedJetEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, + total_jet_energy, "Total jet energy injected into AGN surroundings."); + + list[46] = io_make_output_field( + "JetTimeSteps", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, dt_jet, + "Jet-launching-limited time-steps of black holes."); + + list[47] = io_make_output_field( + "NumberOfJetParticlesLaunched", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + AGN_number_of_jet_injections, + "Integer number of (kinetic) energy injections the black hole has had " + "so far"); + + list[48] = io_make_output_field( + "NumberOfAGNJetEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + AGN_number_of_AGN_jet_events, + "Integer number of AGN jet launching events the black hole has had" + " (the number of times the BH did AGN jet feedback)"); + + if (with_cosmology) { + list[49] = io_make_output_field( + "LastAGNJetScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + last_AGN_jet_event_scale_factor, + "Scale-factors at which the black holes last had an AGN jet event."); + } else { + list[49] = io_make_output_field( + "LastAGNJetTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + last_AGN_jet_event_time, + "Times at which the black holes last had an AGN jet event."); + } + + list[50] = io_make_output_field( + "EddingtonFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + eddington_fraction, + "Accretion rates of black holes in units of their Eddington rates. " + "This is based on the unlimited accretion rates, so these fractions " + "can be above the limiting fEdd."); + + list[51] = io_make_output_field( + "FOFGroupMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, group_mass, + "Parent halo masses of the black holes, as determined from the FOF " + "algorithm."); + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + + list += *num_fields; + *num_fields += 4; + + list[0] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS, + bparts, num_ngb_density); + list[1] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS, + bparts, num_ngb_force); + list[2] = io_make_output_field("Ids_ngb_density", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES, + UNIT_CONV_NO_UNITS, bparts, ids_ngbs_density); + list[3] = io_make_output_field("Ids_ngb_force", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES, + UNIT_CONV_NO_UNITS, bparts, ids_ngbs_force); +#endif +} + +#endif /* SWIFT_SPIN_JET_BLACK_HOLES_IO_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_parameters.h b/src/black_holes/SPIN_JET/black_holes_parameters.h new file mode 100644 index 0000000000000000000000000000000000000000..9259ba8b75f61e042a73a1f39c21298aedce6ee9 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_parameters.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 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_SPIN_JET_BLACK_HOLES_PARAMETERS_H +#define SWIFT_SPIN_JET_BLACK_HOLES_PARAMETERS_H + +/* Configuration file */ +#include <config.h> + +/** + * @file EAGLE/black_holes_parameters.h + * @brief Parameters of the EAGLE black holes + * model that need to be defined at compile time. + * + * @note In this branch, these properties are not used anywhere! + */ + +/*! Maximal distance for merging particles in units of the (spline not Plummer) + * softening length. */ +#define const_max_merging_distance_ratio 3.f + +/*! Maximal distance for repositioning particles in units of the (spline not + * Plummer) softening length. */ +#define const_max_repositioning_distance_ratio 3.f + +#endif /* SWIFT_SPIN_JET_BLACK_HOLES_PARAMETERS_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_part.h b/src/black_holes/SPIN_JET/black_holes_part.h new file mode 100644 index 0000000000000000000000000000000000000000..712c70332230f77ec5747554bacb37e1cffc866e --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_part.h @@ -0,0 +1,374 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2022 Filip Husko (filip.husko@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_SPIN_JET_BLACK_HOLE_PART_H +#define SWIFT_SPIN_JET_BLACK_HOLE_PART_H + +/*! The total number of rays used in AGN feedback */ +#define spinjet_blackhole_number_of_rays 50 + +#include "black_holes_struct.h" +#include "chemistry_struct.h" +#include "particle_splitting_struct.h" +#include "rays_struct.h" +#include "timeline.h" + +/*! The possible accretion modes every black hole can take. */ +enum BH_accretion_modes { + BH_thick_disc = 0, /* At low Eddington ratios */ + BH_thin_disc, /* At moderate Eddington ratios */ + BH_slim_disc, /* Super-Eddington accretion */ + BH_accretion_modes_count /* Number of possible accretion modes */ +}; + +/** + * @brief Particle fields for the black hole particles. + * + * All quantities related to gravity are stored in the associate #gpart. + */ +struct bpart { + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Particle velocity. */ + float v[3]; + + /*! Black hole mass */ + float mass; + + /*! Black hole mass at the start of each step, prior to any nibbling */ + float mass_at_start_of_step; + + /* Particle cutoff radius. */ + float h; + + /*! Particle time bin */ + timebin_t time_bin; + + struct { + + /* Number of neighbours. */ + float wcount; + + /* Number of neighbours spatial derivative. */ + float wcount_dh; + + } density; + + /*! Union for the formation time and formation scale factor */ + union { + + /*! Formation time */ + float formation_time; + + /*! Formation scale factor */ + float formation_scale_factor; + }; + + /*! Subgrid mass of the black hole */ + float subgrid_mass; + + /*! Total accreted mass of the black hole (not including mass merged in + * from other black holes) */ + float total_accreted_mass; + + /*! Energy reservoir for feedback */ + float energy_reservoir; + + /*! Instantaneous accretion rate */ + float accretion_rate; + + /*! Density of the gas surrounding the black hole. */ + float rho_gas; + + /*! Smoothed sound speed of the gas surrounding the black hole. */ + float sound_speed_gas; + + /*! Smoothed velocity of the gas surrounding the black hole, + * in the frame of the black hole (internal units) */ + float velocity_gas[3]; + + /*! Smoothed velocity dispersion (peculiar) of the gas around the + * black hole */ + float velocity_dispersion_gas; + + /*! Circular velocity of the gas around the black hole at the smoothing + * radius (calculated as j_gas / h_BH, where j is specific ang. mom.) */ + float circular_velocity_gas[3]; + + /*! Specific angular momentum of the gas around the black hole */ + float spec_angular_momentum_gas[3]; + + /*! Curl of the gas velocity around the black hole. */ + float curl_v_gas[3]; + + /*! Multiplicative factor for accretion rates, from Rosas-Guevara et al. + * (2015) angular momentum based accretion disc model */ + float f_visc; + + /*! Total mass of the gas neighbours. */ + float ngb_mass; + + /*! 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; + + /*! Total number of gas particles swallowed (including particles swallowed + * by merged-in black holes) */ + int number_of_gas_swallows; + + /*! Total number of gas particles swallowed (excluding particles swallowed + * by merged-in black holes) */ + int number_of_direct_gas_swallows; + + /*! Total number of times the black hole has been repositioned (excluding + * repositionings of merged-in black holes) */ + int number_of_repositions; + + /*! Total number of times a black hole attempted repositioning (including + * cases where it was aborted because the black hole was already at a + * lower potential than all eligible neighbours) */ + int number_of_reposition_attempts; + + /*! Total number of time steps in which the black hole was active. */ + int number_of_time_steps; + + /*! Total (physical) angular momentum accumulated by swallowing particles */ + float swallowed_angular_momentum[3]; + + /*! Total (physical) angular momentum accumulated from subgrid accretion */ + float accreted_angular_momentum[3]; + + /*! Integer (cumulative) number of energy injections in AGN feedback. At a + * given time-step, an AGN-active BH may produce multiple energy injections. + * The number of energy injections is equal to or more than the number of + * particles heated by the BH during this time-step. */ + int AGN_number_of_energy_injections; + + /*! Integer (cumulative) number of AGN events. If a BH does feedback at a + * given time-step, the number of its AGN events is incremented by 1. Each + * AGN event may have multiple energy injections. */ + int AGN_number_of_AGN_events; + + /* Total energy injected into the gas in AGN feedback by this BH */ + float AGN_cumulative_energy; + + /*! Union for the last AGN event time and the last AGN event scale factor */ + union { + + /*! Last AGN event time */ + float last_AGN_event_time; + + /*! Last AGN event scale-factor */ + float last_AGN_event_scale_factor; + }; + + /*! BH accretion-limited time-step */ + float dt_heat; + + /*! Eddington fraction */ + float eddington_fraction; + + /*! BH dimensionless spin */ + float spin; + + /*! The normalized spin/angular momentum vector of the BH */ + float angular_momentum_direction[3]; + + /*! The jet efficiency */ + float jet_efficiency; + + /*! The radiative efficiency */ + float radiative_efficiency; + + /*! Cosine of the angle between the spin vector and the angular momentum + of the gas in the smoothing kernel */ + float accretion_disk_angle; + + /*! Aspect ratio of the subgrid accretion disk */ + float aspect_ratio; + + /*! Which type is the subgrid accretion disk (thick, thin or slim) */ + enum BH_accretion_modes accretion_mode; + + /*! The jet-limited time-step of the BH */ + float dt_jet; + + /*! The angular momentum evolution limited time-step of the BH */ + float dt_ang_mom; + + /*! The current jet kick velocity to be applied */ + float v_jet; + + /*! The energ in the jet reservoir */ + float jet_reservoir; + + /*! Total jet energy launched so far */ + float total_jet_energy; + + /*! Total accreted masses, radiated energies and jet energies launched + by BHs, split by accretion mode */ + float accreted_mass_by_mode[BH_accretion_modes_count]; + float thermal_energy_by_mode[BH_accretion_modes_count]; + float jet_energy_by_mode[BH_accretion_modes_count]; + + /*! Total number of jet kicks */ + int AGN_number_of_jet_injections; + + /*! Total number of jet kicking events */ + int AGN_number_of_AGN_jet_events; + + /*! Halo mass the black hole is assigned to */ + float group_mass; + + union { + + /*! Last time a jet event occurred */ + float last_AGN_jet_event_time; + + /*! Last scale factor a jet event occurred */ + float last_AGN_jet_event_scale_factor; + }; + + /*! Union for the last high Eddington ratio point in time */ + union { + + /*! Last time the BH had a a high Eddington fraction */ + float last_high_Eddington_fraction_time; + + /*! Last scale factor the BH had a a high Eddington fraction */ + float last_high_Eddington_fraction_scale_factor; + }; + + /*! Union for the last minor merger point in time */ + union { + + /*! Last time the BH had a a high Eddington fraction */ + float last_minor_merger_time; + + /*! Last scale factor the BH had a a high Eddington fraction */ + float last_minor_merger_scale_factor; + }; + + /*! Union for the last major merger point in time */ + union { + + /*! Last time the BH had a a high Eddington fraction */ + float last_major_merger_time; + + /*! Last scale factor the BH had a a high Eddington fraction */ + float last_major_merger_scale_factor; + }; + + /*! Properties used in the feedback loop to distribute to gas neighbours. */ + struct { + + /*! Energy per unit mass in a single AGN energy-injection event */ + float AGN_delta_u; + + /*! Number of energy injections per time-step */ + int AGN_number_of_energy_injections; + + /*! Number of energy injections per time-step */ + int AGN_number_of_jet_injections; + + /*! Change in energy from SNII feedback energy injection */ + float AGN_delta_u_jet; + + } to_distribute; + + struct { + + /*! Gravitational potential copied from the #gpart. */ + float potential; + + /*! Value of the minimum potential across all neighbours. */ + float min_potential; + + /*! Delta position to apply after the reposition procedure */ + double delta_x[3]; + + } reposition; + + /*! Splitting structure */ + struct particle_splitting_data split_data; + + /*! 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; + + /*! Isotropic AGN feedback information */ + struct ray_data rays[spinjet_blackhole_number_of_rays]; + + /*! Jet AGN feedback information. This ray is used to kick particles along + the spin axis. */ + struct ray_data rays_jet[spinjet_blackhole_number_of_rays]; + + /*! Jet AGN feedback information. This ray is used to kick particles in the + oppposite direction from the spin axis. */ + struct ray_data rays_jet_pos[spinjet_blackhole_number_of_rays]; + + /*! Tracer structure */ + struct tracers_bpart_data tracers_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_BLACK_HOLES + /*! Number of interactions in the density SELF and PAIR */ + int num_ngb_density; + + /*! List of interacting particles in the density SELF and PAIR */ + long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES]; + + /*! Number of interactions in the force SELF and PAIR */ + int num_ngb_force; + + /*! List of interacting particles in the force SELF and PAIR */ + long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES]; +#endif + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_SPIN_JET_BLACK_HOLE_PART_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_properties.h b/src/black_holes/SPIN_JET/black_holes_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..fd01037c5d92d3ea2d72f984ccfc2b3db67c0b58 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_properties.h @@ -0,0 +1,1001 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2022 Filip Husko (filip.husko@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_SPIN_JET_BLACK_HOLES_PROPERTIES_H +#define SWIFT_SPIN_JET_BLACK_HOLES_PROPERTIES_H + +#include "chemistry.h" +#include "hydro_properties.h" +#include "string.h" + +enum AGN_feedback_models { + AGN_isotropic_model, /*< Isotropic model of AGN feedback */ + AGN_minimum_distance_model /*< Minimum-distance model of AGN feedback */ +}; + +enum AGN_jet_feedback_models { + AGN_jet_minimum_distance_model, /*< Minimum-distance model of AGN feedback */ + AGN_jet_maximum_distance_model, /*< Maximum-distance model of AGN feedback */ + AGN_jet_spin_axis_model, /*< Kicking-along-spin + axis model of AGN feedback */ + AGN_jet_minimum_density_model /*< Minimum-density model of AGN feedback */ +}; + +enum AGN_jet_velocity_models { + AGN_jet_velocity_constant, /*< Use a constant jet velocity */ + AGN_jet_velocity_BH_mass, /*< Scale the jet velocity with BH mass */ + AGN_jet_velocity_mass_loading, /*< Assume constant mass loading */ + AGN_jet_velocity_local, /*< Scale the jet velocity such that particles clear + the kernel exactly when a new one is launched */ + AGN_jet_velocity_sound_speed, /*< Scale the jet velocity such that the + BH kernel is replenished on the same + time-scale as it is evacuated by jets */ + AGN_jet_velocity_halo_mass /*< Scale the jet velocity with halo mass + (NOT WORKING) */ +}; + +enum BH_merger_thresholds { + BH_mergers_circular_velocity, /*< v_circ at separation, as in EAGLE */ + BH_mergers_escape_velocity, /*< v_esc at separation */ + BH_mergers_dynamical_escape_velocity /*< combined v_esc_dyn at separation */ +}; + +enum thin_disc_regions { + TD_region_B, /*< Region B from Shakura & Sunyaev (1973) */ + TD_region_C /*< Region C from Shakura & Sunyaev (1973) */ +}; + +/** + * @brief Properties of black holes and AGN feedback in the EAGEL model. + */ +struct black_holes_props { + + /* ----- Basic neighbour search properties ------ */ + + /*! Resolution parameter */ + float eta_neighbours; + + /*! Target weightd number of neighbours (for info only)*/ + float target_neighbours; + + /*! Smoothing length tolerance */ + float h_tolerance; + + /*! Tolerance on neighbour number (for info only)*/ + float delta_neighbours; + + /*! Maximal number of iterations to converge h */ + int max_smoothing_iterations; + + /*! Maximal change of h over one time-step */ + float log_max_h_change; + + /* ----- Initialisation properties ------ */ + + /*! Mass of a BH seed at creation time */ + float subgrid_seed_mass; + + /*! Should we use the subgrid mass specified in ICs? */ + int use_subgrid_mass_from_ics; + + /*! Should we enforce positive subgrid masses initially? */ + int with_subgrid_mass_check; + + /* ----- Properties of the accretion model ------ */ + + /*! Calculate Bondi accretion rate based on subgrid properties? */ + int use_subgrid_gas_properties; + + /*! Calculate Bondi accretion rate for individual neighbours? */ + int use_multi_phase_bondi; + + /*! Switch between Bondi [0] or Krumholz [1] accretion rates */ + int use_krumholz; + + /*! In Krumholz mode, should we include the vorticity term? */ + int with_krumholz_vorticity; + + /*! Are we applying the angular-momentum-based multiplicative term from + * Rosas-Guevara et al. (2015)? */ + int with_angmom_limiter; + + /*! Normalisation of the viscuous angular momentum accretion reduction */ + float alpha_visc; + + /*! Maximal fraction of the Eddington rate allowed. */ + float f_Edd; + + /*! Eddington fraction threshold for recording */ + float f_Edd_recording; + + /*! Switch for the Booth, Schaye 2009 model */ + int with_boost_factor; + + /*! Lowest value of the boost of the Booth, Schaye 2009 model */ + float boost_alpha; + + /*! Power law slope for the boost of the Booth, Schaye 2009 model */ + float boost_beta; + + /*! Normalisation density (internal units) for the boost of the Booth, Schaye + * 2009 model */ + double boost_n_h_star; + + /*! Switch for nibbling mode */ + int use_nibbling; + + /*! Minimum gas particle mass in nibbling mode */ + float min_gas_mass_for_nibbling; + + /* ---- Properties of the feedback model ------- */ + + /*! AGN feedback model: isotropic or minimum distance */ + enum AGN_feedback_models feedback_model; + + /*! Is the AGN feedback model deterministic or stochastic? */ + int AGN_deterministic; + + /*! Feedback coupling efficiency of the black holes. */ + float epsilon_f; + + /*! Temperature increase induced by AGN feedback (Kelvin) */ + float AGN_delta_T_desired; + + /*! Number of gas neighbours to heat in a feedback event */ + float num_ngbs_to_heat; + + /* ---- Properties of the repositioning model --- */ + + /*! Maximal mass of BH to reposition */ + float max_reposition_mass; + + /*! Maximal distance to reposition, in units of softening length */ + float max_reposition_distance_ratio; + + /*! Switch to enable a relative velocity limit for particles to which the + * black holes can reposition */ + int with_reposition_velocity_threshold; + + /*! Maximal velocity offset of particles to which the black hole can + * reposition, in units of the ambient sound speed of the black hole */ + float max_reposition_velocity_ratio; + + /*! Minimum value of the velocity repositioning threshold */ + float min_reposition_velocity_threshold; + + /*! Switch to enable repositioning at fixed (maximum) speed */ + int set_reposition_speed; + + /*! Normalisation factor for repositioning velocity */ + float reposition_coefficient_upsilon; + + /*! Repositioning velocity scaling with black hole mass */ + float reposition_exponent_xi; + + /*! Correct potential of BH? */ + int correct_bh_potential_for_repositioning; + + /* ---- Properties of the merger model ---------- */ + + /*! Mass ratio above which a merger is considered 'minor' */ + float minor_merger_threshold; + + /*! Mass ratio above which a merger is considered 'major' */ + float major_merger_threshold; + + /*! Type of merger threshold */ + enum BH_merger_thresholds merger_threshold_type; + + /*! Maximal distance over which BHs merge, in units of softening length */ + float max_merging_distance_ratio; + + /* ---- Common conversion factors --------------- */ + + /*! Conversion factor from temperature to internal energy */ + float temp_to_u_factor; + + /* ---- Black hole time-step properties ---------- */ + + /*! -- Minimum allowed time-step of BH in internal units */ + float time_step_min; + + /* ---- Black hole accretion disk parameters ---------- */ + + /*! Viscous alpha of the accretion disk, and various factors and powers + involving it. Here alpha_factor_x refers to alpha raised to the power of + x, expressed as e.g. 0549 if x is 0.549 (these are the decimal + expressions for powers of alpha that appear in accretion disk theory). + alpha_factor_x_inv refers to the inverse of a similar number, or + equivalently alpha raised to the power of -x. alpha_factor_x_inv_10 is + alpha * 10 raised to the power of -x, a combination that also often + appears in the literature. */ + float alpha_acc; + float alpha_acc_2; + float alpha_acc_2_inv; + float alpha_factor_01; + float alpha_factor_02; + float alpha_factor_08; + float alpha_factor_08_inv; + float alpha_factor_08_inv_10; + float alpha_factor_0549; + float alpha_factor_06222; + + /* BH spin magnitude to assign at seeding */ + float seed_spin; + + /*! Transition accretion rate between thick (ADAF) and thin disk. */ + float mdot_crit_ADAF; + + /*! Gas-to-total pressure ratio of the accretion disk */ + float beta_acc; + float beta_acc_inv; + + /*! Critical accretion rate that separates two different states in the + thick disk regime, related to radiation */ + float edd_crit_thick; + + /*! Effective adiabatic index of the accretion disk */ + float gamma_acc; + + /*! Epsilon parameter of the ADAF (thick disk), which appears in + Narayan & Yi (1994, 1995) model */ + float eps_ADAF; + + /*! Numerical coefficient relating sound speed in ADAF (thick disk) to the + Keplerian velocity, which appears in Narayan & Yi (1994, 1995) model */ + float cs_0_ADAF; + + /*! Numerical coefficient relating radial velocity in ADAF (thick disk) to + the Keplerian velocity, which appears in Narayan & Yi (1994, 1995) model */ + float v_0_ADAF; + + /*! Numerical coefficient relating angular velocity in ADAF (thick disk) to + the Keplerian angular velocity, which appears in Narayan & Yi + (1994, 1995) model */ + float omega_0_ADAF; + + /*! Aspect ratio of the ADAF (thick disk), which appears in Narayan & Yi + (1994, 1995) model */ + float h_0_ADAF; + float h_0_ADAF_2; + + /*! Electron heating parameter of the ADAF (thick disk), which appears in + Narayan & Yi (1994, 1995) model */ + float delta_ADAF; + + /*! The gamma parameter of the slim disk, which appears in the Wang & Zhou + (1999) model */ + float gamma_SD; + float gamma_SD_inv; + + /*! The ratio of vertical to horizontal kinematic viscosity of the thin disk. + Note that we use the definition xi = nu_2 / nu_1, whereas xi = + (nu_2 / nu_1) * 2 * alpha^2 is often used, e.g. Fiacconi et al. (2018) */ + float xi_TD; + + /*! Which region of the thin disc (Shakura & Sunyaev) we are assuming the + subgrid accretion disk is represented as: + B - region b; gas pressure dominates over radiation pressure, + electron scattering dominates the opacity + C - region c; gas pressure dominates over radiation pressure, + free-free absorption dominates the opacity */ + enum thin_disc_regions TD_region; + + /*! Parameter controlling when thin disk transitions into slim disk. This + occurs when the radiative efficiency of the slim disk falls below + TD_SD_eps_r_threshold times the radiative efficiency of the thin disk. */ + float TD_SD_eps_r_threshold; + + /* ---- Jet feedback - related parameters ---------- */ + + /*! Global switch for whether to include jets [1] or not [0]. */ + int include_jets; + + /*! Global switch for whether to turn off radiative feedback [1] or not [0]. + */ + int turn_off_radiative_feedback; + + /*! Global switch for whether to turn off radiation in the thick disk and + jets in the thin disk [1] or not [0] */ + int turn_off_secondary_feedback; + + /* Whether we want to include super-Eddington accretion, modeled as the slim + disk */ + int include_slim_disk; + + /* Whether to use GRMHD fits for the spindown rate due to jets */ + int include_GRMHD_spindown; + + /* Whether to include the expected suppression of the accretion rate due to + * ADIOS winds in the thick disk regime */ + int include_ADIOS_suppression; + + /* The inner radius in the accretion rate - R relation, if ADIOS suppression + * is included */ + float ADIOS_R_in; + + /* The slope of the accretion rate - R relation, if ADIOS suppression + * is included */ + float ADIOS_s; + + /*! Whether to fix the radiative efficiency to some value [1] or not [0]. */ + int fix_radiative_efficiency; + + /*! The radiative efficiency to use if fix_radiative_efficiency is 1. If + fix_radiative_efficiency is set to 0, this will still be used to + define the Eddington accretion rate. */ + float radiative_efficiency; + + /*! How many particles to aim to kick as part of jet feedback. */ + int N_jet; + + /*! The type of jet velocity scaling to use. */ + enum AGN_jet_velocity_models AGN_jet_velocity_model; + + /*! Jet velocity if the constant velocity model is used */ + float v_jet; + + /*! Parameters of the scaling between AGN jet velocity and BH mass */ + float v_jet_BH_mass_scaling_reference_mass; + float v_jet_BH_mass_scaling_slope; + + /*! Sets the launching velocity of the jet to v_jet_cs_ratio times the + sound speed of the hot gas in the halo, assuming it is at virial + temperature. This is used if the launching model is BH_mass or + halo_mass. */ + float v_jet_cs_ratio; + + /*! The mass loading to use if the launching velocity model is set to use + a constant mass loading. */ + float v_jet_mass_loading; + + /*! The free numerical parameter to scale the velocity by, if the local or + sound_speed launching models are used. */ + float v_jet_xi; + + /*! The minimal jet velocity to use in the variable-velocity models */ + float v_jet_min; + + /*! The effective (half-)opening angle of the jet. */ + float opening_angle; + + /*! The slope of the dependence of jet efficiency on aspect ratio of the + subgrid accretion disk, H/R. Default value is 1, and another reasonable + value is 0 (same jet efficiency for all disks). */ + float jet_h_r_slope; + + /*! The coupling efficiency for jet feedback. */ + float eps_f_jet; + + /*! Whether to fix the jet efficiency to some value [1] or not [0]. If yes, + the jets will be pointed along the z-axis. */ + int fix_jet_efficiency; + + /*! The jet efficiency to use if fix_jet_efficiency is 1. */ + float jet_efficiency; + + /*! The jet launching scheme to use: minimum distance, + maximum distance, closest to spin axis or minimum density. */ + enum AGN_jet_feedback_models jet_feedback_model; +}; + +/** + * @brief Initialise the black hole properties from the parameter file. + * + * For the basic black holes neighbour finding properties we use the + * defaults from the hydro scheme if the users did not provide specific + * values. + * + * @param bp The #black_holes_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +INLINE static void black_holes_props_init(struct black_holes_props *bp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *hydro_props, + const struct cosmology *cosmo) { + + /* Read in the basic neighbour search properties or default to the hydro + ones if the user did not provide any different values */ + + /* Kernel properties */ + bp->eta_neighbours = parser_get_opt_param_float( + params, "BlackHoles:resolution_eta", hydro_props->eta_neighbours); + + /* Tolerance for the smoothing length Newton-Raphson scheme */ + bp->h_tolerance = parser_get_opt_param_float(params, "BlackHoles:h_tolerance", + hydro_props->h_tolerance); + + /* Get derived properties */ + bp->target_neighbours = pow_dimension(bp->eta_neighbours) * kernel_norm; + const float delta_eta = bp->eta_neighbours * (1.f + bp->h_tolerance); + bp->delta_neighbours = + (pow_dimension(delta_eta) - pow_dimension(bp->eta_neighbours)) * + kernel_norm; + + /* Number of iterations to converge h */ + bp->max_smoothing_iterations = + parser_get_opt_param_int(params, "BlackHoles:max_ghost_iterations", + hydro_props->max_smoothing_iterations); + + /* Time integration properties */ + const float max_volume_change = + parser_get_opt_param_float(params, "BlackHoles:max_volume_change", -1); + if (max_volume_change == -1) + bp->log_max_h_change = hydro_props->log_max_h_change; + else + bp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); + + /* Initialisation properties ---------------------------- */ + + bp->subgrid_seed_mass = + parser_get_param_float(params, "SPINJETAGN:subgrid_seed_mass_Msun"); + + /* Convert to internal units */ + bp->subgrid_seed_mass *= phys_const->const_solar_mass; + + bp->use_subgrid_mass_from_ics = parser_get_opt_param_int( + params, "SPINJETAGN:use_subgrid_mass_from_ics", 1); + if (bp->use_subgrid_mass_from_ics) + bp->with_subgrid_mass_check = parser_get_opt_param_int( + params, "SPINJETAGN:with_subgrid_mass_check", 1); + + /* Accretion parameters ---------------------------------- */ + + bp->use_subgrid_gas_properties = + parser_get_param_int(params, "SPINJETAGN:use_subgrid_gas_properties"); + bp->use_multi_phase_bondi = + parser_get_param_int(params, "SPINJETAGN:use_multi_phase_bondi"); + if (!bp->use_multi_phase_bondi) { + bp->use_krumholz = parser_get_param_int(params, "SPINJETAGN:use_krumholz"); + bp->with_krumholz_vorticity = + parser_get_param_int(params, "SPINJETAGN:with_krumholz_vorticity"); + } + + bp->with_angmom_limiter = + parser_get_param_int(params, "SPINJETAGN:with_angmom_limiter"); + if (bp->with_angmom_limiter) + bp->alpha_visc = parser_get_param_float(params, "SPINJETAGN:viscous_alpha"); + + bp->f_Edd = + parser_get_param_float(params, "SPINJETAGN:max_eddington_fraction"); + bp->f_Edd_recording = parser_get_param_float( + params, "SPINJETAGN:eddington_fraction_for_recording"); + + /* Booth Schaye (2009) Parameters */ + bp->with_boost_factor = + parser_get_param_int(params, "SPINJETAGN:with_boost_factor"); + + if (bp->with_boost_factor) { + bp->boost_alpha = parser_get_param_float(params, "SPINJETAGN:boost_alpha"); + + bp->boost_beta = parser_get_param_float(params, "SPINJETAGN:boost_beta"); + + /* Load the density in cgs and convert to internal units */ + bp->boost_n_h_star = + parser_get_param_float(params, "SPINJETAGN:boost_n_h_star_cm3") / + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + } + + bp->use_nibbling = parser_get_param_int(params, "SPINJETAGN:use_nibbling"); + if (bp->use_nibbling) { + bp->min_gas_mass_for_nibbling = parser_get_param_float( + params, "SPINJETAGN:min_gas_mass_for_nibbling_Msun"); + bp->min_gas_mass_for_nibbling *= phys_const->const_solar_mass; + } + + if ((bp->min_gas_mass_for_nibbling < 1e-5 * bp->subgrid_seed_mass) || + (bp->min_gas_mass_for_nibbling > 1e5 * bp->subgrid_seed_mass)) { + error( + "The BH seeding mass and minimal gas mass for nibbling differ by more " + "than 10^5. That is probably indicating a typo in the parameter file."); + } + + /* Feedback parameters ---------------------------------- */ + + char temp[PARSER_MAX_LINE_SIZE]; + parser_get_param_string(params, "SPINJETAGN:AGN_feedback_model", temp); + if (strcmp(temp, "Isotropic") == 0) + bp->feedback_model = AGN_isotropic_model; + else if (strcmp(temp, "MinimumDistance") == 0) + bp->feedback_model = AGN_minimum_distance_model; + else + error( + "The AGN feedback model must be either MinimumDistance or Isotropic," + " not %s", + temp); + + bp->AGN_deterministic = parser_get_opt_param_int( + params, "SPINJETAGN:AGN_use_deterministic_feedback", 1); + bp->epsilon_f = + parser_get_param_float(params, "SPINJETAGN:coupling_efficiency"); + bp->AGN_delta_T_desired = + parser_get_param_float(params, "SPINJETAGN:AGN_delta_T_K"); + /* Check that it makes sense. */ + if (bp->AGN_delta_T_desired <= 0.f) + error("The AGN heating temperature delta T must be > 0 K, not %.5e K.", + bp->AGN_delta_T_desired); + + bp->AGN_delta_T_desired /= + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + bp->num_ngbs_to_heat = + parser_get_param_float(params, "SPINJETAGN:AGN_num_ngb_to_heat"); + + /* Reposition parameters --------------------------------- */ + + bp->max_reposition_mass = + parser_get_param_float(params, "SPINJETAGN:max_reposition_mass_Msun"); + + /* Convert to internal units */ + bp->max_reposition_mass *= phys_const->const_solar_mass; + + bp->max_reposition_distance_ratio = parser_get_param_float( + params, "SPINJETAGN:max_reposition_distance_ratio"); + + bp->with_reposition_velocity_threshold = parser_get_param_int( + params, "SPINJETAGN:with_reposition_velocity_threshold"); + + if (bp->with_reposition_velocity_threshold) { + bp->max_reposition_velocity_ratio = parser_get_param_float( + params, "SPINJETAGN:max_reposition_velocity_ratio"); + + /* Prevent nonsensical input */ + if (bp->max_reposition_velocity_ratio <= 0) + error("max_reposition_velocity_ratio must be positive, not %f.", + bp->max_reposition_velocity_ratio); + + bp->min_reposition_velocity_threshold = parser_get_param_float( + params, "SPINJETAGN:min_reposition_velocity_threshold_km_p_s"); + /* Convert from km/s to internal units */ + bp->min_reposition_velocity_threshold *= + (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + } + + bp->set_reposition_speed = + parser_get_param_int(params, "SPINJETAGN:set_reposition_speed"); + + if (bp->set_reposition_speed) { + bp->reposition_coefficient_upsilon = parser_get_param_float( + params, "SPINJETAGN:reposition_coefficient_upsilon"); + + /* Prevent the user from making silly wishes */ + if (bp->reposition_coefficient_upsilon <= 0) + error( + "reposition_coefficient_upsilon must be positive, not %f " + "km/s/M_sun.", + bp->reposition_coefficient_upsilon); + + /* Convert from km/s to internal units */ + bp->reposition_coefficient_upsilon *= + (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + + bp->reposition_exponent_xi = parser_get_opt_param_float( + params, "SPINJETAGN:reposition_exponent_xi", 1.0); + } + + bp->correct_bh_potential_for_repositioning = + parser_get_param_int(params, "SPINJETAGN:with_potential_correction"); + + /* Merger parameters ------------------------------------- */ + + bp->minor_merger_threshold = + parser_get_param_float(params, "SPINJETAGN:threshold_minor_merger"); + + bp->major_merger_threshold = + parser_get_param_float(params, "SPINJETAGN:threshold_major_merger"); + + char temp2[PARSER_MAX_LINE_SIZE]; + parser_get_param_string(params, "SPINJETAGN:merger_threshold_type", temp2); + if (strcmp(temp2, "CircularVelocity") == 0) + bp->merger_threshold_type = BH_mergers_circular_velocity; + else if (strcmp(temp2, "EscapeVelocity") == 0) + bp->merger_threshold_type = BH_mergers_escape_velocity; + else if (strcmp(temp2, "DynamicalEscapeVelocity") == 0) + bp->merger_threshold_type = BH_mergers_dynamical_escape_velocity; + else + error( + "The BH merger model must be either CircularVelocity, EscapeVelocity, " + "or DynamicalEscapeVelocity, not %s", + temp2); + + bp->max_merging_distance_ratio = + parser_get_param_float(params, "SPINJETAGN:merger_max_distance_ratio"); + + /* Common conversion factors ----------------------------- */ + + /* Calculate temperature to internal energy conversion factor (all internal + * units) */ + const double k_B = phys_const->const_boltzmann_k; + const double m_p = phys_const->const_proton_mass; + const double mu = hydro_props->mu_ionised; + bp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p); + + /* ---- Black hole time-step properties ------------------ */ + + const double yr_in_cgs = 365.25 * 24. * 3600.; + bp->time_step_min = + parser_get_param_float(params, "SPINJETAGN:minimum_timestep_yr") * + yr_in_cgs / units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + /* ---- Black hole accretion disk physics ------------------ */ + + /* The viscosisty parameter of the subgrid accretion disks.*/ + bp->alpha_acc = parser_get_param_float(params, "SPINJETAGN:alpha_acc"); + + /* Various factors and powers of alpha, the subgrid accretion disc + viscosity, that appear in subgrid accretion disk equations. We + precompute them here. */ + bp->alpha_acc_2 = bp->alpha_acc * bp->alpha_acc; + bp->alpha_acc_2_inv = 1. / bp->alpha_acc_2; + bp->alpha_factor_01 = pow(bp->alpha_acc, 0.1); + bp->alpha_factor_02 = pow(bp->alpha_acc, 0.2); + bp->alpha_factor_08 = pow(bp->alpha_acc, 0.8); + bp->alpha_factor_08_inv = 1. / bp->alpha_factor_08; + bp->alpha_factor_08_inv_10 = pow(bp->alpha_acc * 10., -0.8); + bp->alpha_factor_0549 = pow(bp->alpha_acc, 0.549); + bp->alpha_factor_06222 = pow(bp->alpha_acc * 10., 0.6222); + + if ((bp->alpha_acc <= 0.) || (bp->alpha_acc > 1.)) { + error( + "The alpha viscosity parameter of accretion disks must be between 0. " + " and 1., not %f", + bp->alpha_acc); + } + + /* The BH seed spin*/ + bp->seed_spin = parser_get_param_float(params, "SPINJETAGN:seed_spin"); + if ((bp->seed_spin <= 0.) || (bp->seed_spin > 1.)) { + error( + "The BH seed spin parameter must be strictly between 0 and 1, " + "not %f", + bp->seed_spin); + } + + /* Calculate the critical transition accretion rate between the thick and + thin disk regimes. */ + bp->mdot_crit_ADAF = 0.2 * bp->alpha_acc_2; + + /* Calculate the gas-to-total pressure ratio as based on simulations + (see Yuan & Narayan 2014) */ + bp->beta_acc = 1. / (1. + 2. * bp->alpha_acc); + bp->beta_acc_inv = 1. / bp->beta_acc; + + /* Calculate the critical accretion rate between two thick disk regimes as + in Mahadevan (1997). */ + bp->edd_crit_thick = 2. * bp->delta_ADAF * bp->alpha_acc_2 * + (1. - bp->beta_acc) * bp->beta_acc_inv; + + /* Calculate the adiabatic index based on how strong the magnetic fields are + (see Esin 1997) */ + bp->gamma_acc = (8. - 3. * bp->beta_acc) / (6. - 3. * bp->beta_acc); + + /* Calculate numerical factors of the ADAF (thick disk) as in + Narayan & Yi (1995) */ + bp->eps_ADAF = (1.6667 - bp->gamma_acc) / (bp->gamma_acc - 1.); + bp->cs_0_ADAF = sqrtf(2. / (5. + 2. * bp->eps_ADAF)); + bp->v_0_ADAF = 3. / (5. + 2. * bp->eps_ADAF); + bp->omega_0_ADAF = sqrtf(2. * bp->eps_ADAF / (5. + 2. * bp->eps_ADAF)); + + /* Instead of using the Narayan & Yi value, we set to 0.3 based on numerous + GRMHD simulations (see e.g. Narayan et al. 2021) */ + bp->h_0_ADAF = 0.3; + bp->h_0_ADAF_2 = 0.09; + + bp->delta_ADAF = parser_get_param_float(params, "SPINJETAGN:delta_ADAF"); + + if (bp->delta_ADAF <= 0.) { + error( + "The delta electron heating parameter of thick accretion disks " + " must be > 0. not %f", + bp->delta_ADAF); + } + + bp->gamma_SD = sqrtf(5.); + bp->gamma_SD_inv = 1. / bp->gamma_SD; + + /* Formula taken from Lodato et al. (2010) */ + bp->xi_TD = 2. * (1. + 7. * bp->alpha_acc_2) / (4. + bp->alpha_acc_2) / + bp->alpha_acc_2; + + char temp3[PARSER_MAX_LINE_SIZE]; + parser_get_param_string(params, "SPINJETAGN:TD_region", temp3); + if (strcmp(temp3, "B") == 0) + bp->TD_region = TD_region_B; + else if (strcmp(temp3, "C") == 0) + bp->TD_region = TD_region_C; + else + error("The choice of thin disc region must be B or C, not %s", temp3); + + /* ---- Jet feedback - related parameters ---------- */ + bp->include_jets = parser_get_param_int(params, "SPINJETAGN:include_jets"); + + if ((bp->include_jets != 0) && (bp->include_jets != 1)) { + error("The include_jets parameter must be either 0 or 1, not %d", + bp->include_jets); + } + + bp->turn_off_radiative_feedback = + parser_get_param_int(params, "SPINJETAGN:turn_off_radiative_feedback"); + + if ((bp->turn_off_radiative_feedback != 0) && + (bp->turn_off_radiative_feedback != 1)) { + error( + "The turn_off_radiative_feedback parameter must be either 0 or 1, " + " not %d", + bp->turn_off_radiative_feedback); + } + + if ((bp->turn_off_radiative_feedback) && (bp->include_jets == 0)) { + error( + "The turn_off_radiative_feedback parameter and include_jet parameters" + "cannot at the same time be 1 and 0, respectively. In other words, at" + "least one of the two feedback modes must be turned on."); + } + + bp->turn_off_secondary_feedback = + parser_get_param_int(params, "SPINJETAGN:turn_off_secondary_feedback"); + + if ((bp->turn_off_secondary_feedback != 0) && + (bp->turn_off_secondary_feedback != 1)) { + error( + "The turn_off_secondary_feedback parameter must be either 0 or 1, " + "not %d", + bp->turn_off_secondary_feedback); + } + + bp->include_slim_disk = + parser_get_param_int(params, "SPINJETAGN:include_slim_disk"); + + if ((bp->include_slim_disk != 0) && (bp->include_slim_disk != 1)) { + error( + "The include_slim_disk parameter must be either 0 or 1, " + "not %d", + bp->include_slim_disk); + } + + bp->include_GRMHD_spindown = + parser_get_param_int(params, "SPINJETAGN:include_GRMHD_spindown"); + + if ((bp->include_GRMHD_spindown != 0) && (bp->include_GRMHD_spindown != 1)) { + error( + "The include_GRMHD_spindown parameter must be either 0 or 1, " + "not %d", + bp->include_GRMHD_spindown); + } + + bp->include_ADIOS_suppression = + parser_get_param_int(params, "SPINJETAGN:include_ADIOS_suppression"); + + if ((bp->include_ADIOS_suppression != 0) && + (bp->include_ADIOS_suppression != 1)) { + error( + "The include_ADIOS_suppression parameter must be either 0 or 1, " + "not %d", + bp->include_ADIOS_suppression); + } + + bp->ADIOS_R_in = parser_get_param_float(params, "SPINJETAGN:ADIOS_R_in"); + + if (bp->ADIOS_R_in <= 1.) { + error( + "The ADIOS_R_in parameter must be > 1, " + "not %f", + bp->ADIOS_R_in); + } + + bp->ADIOS_s = parser_get_param_float(params, "SPINJETAGN:ADIOS_s"); + + if ((bp->ADIOS_s < 0.) || (bp->ADIOS_s > 1.)) { + error( + "The ADIOS_s parameter must be between 0 and 1, " + "not %f", + bp->ADIOS_s); + } + + bp->TD_SD_eps_r_threshold = + parser_get_param_float(params, "SPINJETAGN:TD_SD_eps_r_threshold"); + + if ((bp->TD_SD_eps_r_threshold <= 0.) || (bp->TD_SD_eps_r_threshold >= 1.)) { + error( + "The TD_SD_eps_r_threshold parameter governing the transition between" + "thin and slim disk must be between 0. and 1., not %f", + bp->TD_SD_eps_r_threshold); + } + + bp->N_jet = parser_get_param_float(params, "SPINJETAGN:N_jet"); + + if (bp->N_jet % 2 != 0) { + error("The N_jet parameter must be divisible by two, not %d", bp->N_jet); + } + + char temp4[PARSER_MAX_LINE_SIZE]; + parser_get_param_string(params, "SPINJETAGN:AGN_jet_velocity_model", temp4); + if (strcmp(temp4, "Constant") == 0) { + bp->AGN_jet_velocity_model = AGN_jet_velocity_constant; + + bp->v_jet = parser_get_param_float(params, "SPINJETAGN:v_jet_km_p_s"); + + /* Convert to internal units */ + bp->v_jet *= (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + + if (bp->v_jet <= 0.) + error("The v_jet parameter must be > 0., not %f", bp->v_jet); + + } else if (strcmp(temp4, "BlackHoleMass") == 0) { + bp->AGN_jet_velocity_model = AGN_jet_velocity_BH_mass; + + bp->v_jet_BH_mass_scaling_reference_mass = parser_get_param_float( + params, "SPINJETAGN:v_jet_BH_mass_scaling_reference_mass_Msun"); + bp->v_jet_BH_mass_scaling_reference_mass *= phys_const->const_solar_mass; + bp->v_jet_BH_mass_scaling_slope = parser_get_param_float( + params, "SPINJETAGN:v_jet_BH_mass_scaling_slope"); + + bp->v_jet_cs_ratio = + parser_get_param_float(params, "SPINJETAGN:v_jet_cs_ratio"); + + bp->v_jet_min = + parser_get_param_float(params, "SPINJETAGN:v_jet_min_km_p_s"); + bp->v_jet_min *= (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + + } else if (strcmp(temp4, "MassLoading") == 0) { + bp->AGN_jet_velocity_model = AGN_jet_velocity_mass_loading; + + bp->v_jet_mass_loading = + parser_get_param_float(params, "SPINJETAGN:v_jet_mass_loading"); + + bp->v_jet_min = + parser_get_param_float(params, "SPINJETAGN:v_jet_min_km_p_s"); + bp->v_jet_min *= (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + + } else if (strcmp(temp4, "Local") == 0) { + bp->AGN_jet_velocity_model = AGN_jet_velocity_local; + + bp->v_jet_xi = parser_get_param_float(params, "SPINJETAGN:v_jet_xi"); + + bp->v_jet_min = + parser_get_param_float(params, "SPINJETAGN:v_jet_min_km_p_s"); + bp->v_jet_min *= (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + + } else if (strcmp(temp4, "SoundSpeed") == 0) { + bp->AGN_jet_velocity_model = AGN_jet_velocity_sound_speed; + + bp->v_jet_xi = parser_get_param_float(params, "SPINJETAGN:v_jet_xi"); + + bp->v_jet_min = + parser_get_param_float(params, "SPINJETAGN:v_jet_min_km_p_s"); + bp->v_jet_min *= (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + + } else if (strcmp(temp4, "HaloMass") == 0) { + error( + "The scaling of jet velocities with halo mass is currently not " + "supported."); + } else { + error( + "The AGN jet velocity model must be Constant, MassLoading, " + "BlackHoleMass, Local, SoundSpeed or HaloMass, not %s", + temp4); + } + + bp->opening_angle = + parser_get_param_float(params, "SPINJETAGN:opening_angle_in_degrees"); + bp->opening_angle = bp->opening_angle * M_PI / 180.; + + bp->jet_h_r_slope = + parser_get_param_float(params, "SPINJETAGN:jet_h_r_slope"); + + bp->eps_f_jet = parser_get_param_float(params, "SPINJETAGN:eps_f_jet"); + + if ((bp->eps_f_jet <= 0.) || (bp->eps_f_jet > 1.)) { + error( + "The eps_f_jet corresponding to the jet coupling efficiency " + "must be between 0. and 1., not %f", + bp->eps_f_jet); + } + + bp->fix_jet_efficiency = + parser_get_param_int(params, "SPINJETAGN:fix_jet_efficiency"); + + if ((bp->fix_jet_efficiency != 0) && (bp->fix_jet_efficiency != 1)) { + error( + "The fix_jet_efficiency parameter must be either 0 or 1, " + "not %d", + bp->fix_jet_efficiency); + } + + bp->jet_efficiency = + parser_get_param_float(params, "SPINJETAGN:jet_efficiency"); + + if (bp->jet_efficiency <= 0.) { + error( + "The jet_efficiency corresponding to the jet efficiency " + "must be larger than 0., not %f", + bp->jet_efficiency); + } + + bp->fix_radiative_efficiency = + parser_get_param_int(params, "SPINJETAGN:fix_radiative_efficiency"); + + if ((bp->fix_radiative_efficiency != 0) && + (bp->fix_radiative_efficiency != 1)) { + error( + "The fix_radiative_efficiency parameter must be either 0 or 1, " + "not %d", + bp->fix_radiative_efficiency); + } + + bp->radiative_efficiency = + parser_get_param_float(params, "SPINJETAGN:radiative_efficiency"); + + if ((bp->radiative_efficiency <= 0.) || (bp->radiative_efficiency > 1.)) { + error( + "The radiative_efficiency corresponding to the radiative efficiency " + "must be between 0. and 1., not %f", + bp->radiative_efficiency); + } + + char temp5[60]; + parser_get_param_string(params, "SPINJETAGN:AGN_jet_feedback_model", temp5); + if (strcmp(temp5, "MinimumDistance") == 0) + bp->jet_feedback_model = AGN_jet_minimum_distance_model; + else if (strcmp(temp5, "MaximumDistance") == 0) + bp->jet_feedback_model = AGN_jet_maximum_distance_model; + else if (strcmp(temp5, "SpinAxis") == 0) + bp->jet_feedback_model = AGN_jet_spin_axis_model; + else if (strcmp(temp5, "MinimumDensity") == 0) + bp->jet_feedback_model = AGN_jet_minimum_density_model; + else + error( + "The AGN feedback model must be MinimumDistance, MaximumDistance, " + "SpinAxis or MinimumDensity, not %s", + temp5); +} + +/** + * @brief Write a black_holes_props struct to the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_dump( + const struct black_holes_props *props, FILE *stream) { + restart_write_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, "black_holes props", "black holes props"); +} + +/** + * @brief Restore a black_holes_props struct from the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_restore( + const struct black_holes_props *props, FILE *stream) { + restart_read_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, NULL, "black holes props"); +} + +#endif /* SWIFT_SPIN_JET_BLACK_HOLES_PROPERTIES_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_spin.h b/src/black_holes/SPIN_JET/black_holes_spin.h new file mode 100644 index 0000000000000000000000000000000000000000..d7207ef770f31a826946713aed7ae39483fd71e4 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_spin.h @@ -0,0 +1,1215 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Filip Husko (filip.husko@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_SPIN_JET_BLACK_HOLES_SPIN_H +#define SWIFT_SPIN_JET_BLACK_HOLES_SPIN_H + +/* Standard headers */ +#include <float.h> + +/* Local includes */ +#include "black_holes_properties.h" +#include "black_holes_struct.h" +#include "inline.h" +#include "physical_constants.h" + +/** + * @brief Compute the radius of the horizon of a BH particle in gravitational + * units. + * + * @param a Black hole spin, -1 < a < 1. + */ +__attribute__((always_inline)) INLINE static float r_hor(float a) { + return 1. + sqrtf((1. - a) * (1. + a)); +} + +/** + * @brief Compute the radius of the innermost stable circular orbit of a + * BH particle in gravitational units. + * + * The expression is given in Appendix B of Fiacconi et al. (2018) or eqn. 4 in + * Griffin et al. (2019). + * + * @param a Black hole spin, -1 < a < 1. + */ +__attribute__((always_inline)) INLINE static float r_isco(float a) { + const float Z1 = 1. + (cbrtf((1. + fabsf(a)) * (1. - a * a)) + + cbrtf((1. - fabsf(a)) * (1. - a * a))); + const float Z2 = sqrtf(3. * a * a + Z1 * Z1); + + const float R_ISCO = + 3. + Z2 - a / fabsf(a) * sqrtf((3. - Z1) * (3. + Z1 + 2. * Z2)); + +#ifdef SWIFT_DEBUG_CHECKS + if (Z1 > 3.) { + error( + "Something went wrong with calculation of Z1 factor for r_isco of" + " black holes. Z1 is %f instead of Z1 > 3.", + Z1); + } + + if ((3. + Z1 + 2. * Z2) < 0.) { + error( + "Something went wrong with calculation of (3. + Z1 + 2. * Z2 ) " + "factor for r_isco of black holes. (3. + Z1 + 2. * Z2 ) is %f instead " + "of" + " (3. + Z1 + 2. * Z2 ) > 0.", + 3. + Z1 + 2. * Z2); + } + + if (R_ISCO < 1.) { + error( + "Something went wrong with calculation of R_ISCO of black holes. " + "R_ISCO is %f instead >= 1.", + R_ISCO); + } +#endif + + return R_ISCO; +} + +/** + * @brief Compute the magnitude of the angular momentum of the black hole + * given its spin. + * + * @param a Black hole spin magnitude, 0 < a < 1. + * @param constants Physical constants (in internal units). + */ +__attribute__((always_inline)) INLINE static float j_BH( + struct bpart* bp, const struct phys_const* constants) { + + const float J_BH = + fabs(bp->subgrid_mass * bp->subgrid_mass * bp->spin * + constants->const_newton_G / constants->const_speed_light_c); + +#ifdef SWIFT_DEBUG_CHECKS + if (J_BH <= 0.) { + error( + "Something went wrong with calculation of j_BH of black holes. " + " J_BH is %f instead of J_BH > 0.", + J_BH); + } +#endif + + return J_BH; +} + +/** + * @brief Compute the gravitational radius of a black hole. + * + * @param a Black hole mass. + * @param constants Physical constants (in internal units). + */ +__attribute__((always_inline)) INLINE static float R_gravitational( + float mass, const struct phys_const* constants) { + + const float r_G = + mass * constants->const_newton_G / + (constants->const_speed_light_c * constants->const_speed_light_c); + +#ifdef SWIFT_DEBUG_CHECKS + if (r_G <= 0.) { + error( + "Something went wrong with calculation of R_G of black holes. " + " R_G is %f instead of R_G > 0.", + r_G); + } +#endif + + return r_G; +} + +/** + * @brief Compute the warp radius of a black hole particle. + * + * The result depends on bp->accretion_mode (thick disk, thin disk or + * slim disk). For the thick disk and slim disk, the radius is calculated + * from Lubow et al. (2002), eqn. 22 with x=1. The result will be different + * only due to different aspect ratios H/R=h_0. + * + * For the thin disk, the result depends on props->TD_region (B - region b from + * Shakura & Sunyaev 1973, C - region c from Shakura & Sunyaev 1973). The warp + * radii are taken as eqns. 11 from Griffin et al. (2019) and A8 from Fiacconi + * et al. (2018), respectively. + * + * For the thin disk we also have to include the possibility that the self- + * gravity radius is smaller than the warp radius. In this case r_warp=r_sg + * because the disk cannot be larger than the self-gravity radius, and the + * entire disk is warped. The sg radius is taken as eqns. 16 in Griffin et al. + * (2019) and A6 in Fiacconi et al. (2018), respectively. + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float r_warp( + struct bpart* bp, const struct phys_const* constants, + const struct black_holes_props* props) { + + /* Define placeholder variable for the result */ + float Rw = -1.; + + /* Gravitational radius */ + const float R_G = R_gravitational(bp->subgrid_mass, constants); + + /* Start branching depending on which accretion mode the BH is in */ + if (bp->accretion_mode == BH_thick_disc) { + + /* Eqn. 22 from Lubow et al. (2002) with H/R=h_0_ADAF (thick disk) */ + const float base = 15.36 * fabsf(bp->spin) / props->h_0_ADAF_2; + Rw = R_G * powf(base, 0.4); + } else if (bp->accretion_mode == BH_slim_disc) { + + /* Eqn. 22 from Lubow et al. (2002) with H/R=1/gamma_SD (slim disk) */ + const float base = 15.36 * fabsf(bp->spin) * props->gamma_SD; + Rw = R_G * powf(base, 0.4); + } else if (bp->accretion_mode == BH_thin_disc) { + + /* Start branching depending on which region of the thin disk we wish to + base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973, + or TD_region=C: region c) */ + if (props->TD_region == TD_region_B) { + + /* Calculate different factors in eqn. 11 (Griffin et al. 2019) for warp + radius of region b in Shakura & Sunyaev (1973) */ + float mass_factor = + powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), 0.2); + float edd_factor = powf(bp->eddington_fraction, 0.4); + + /* Gather the factors and finalize calculation */ + const float base = mass_factor * fabsf(bp->spin) / + (props->xi_TD * props->alpha_factor_08 * edd_factor); + const float rw = 3410. * 2. * R_G * powf(base, 0.625); + + /* Self-gravity radius in region b: eqn. 16 in Griffin et al. */ + mass_factor = + powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), -0.961); + edd_factor = powf(bp->eddington_fraction, -0.353); + + const float rs = 4790. * 2. * R_G * mass_factor * + props->alpha_factor_0549 * edd_factor; + + /* Take the minimum */ + Rw = min(rs, rw); + } + + if (props->TD_region == TD_region_C) { + + /* Calculate different factors in eqn. A8 (Fiacconi et al. 2018) */ + float mass_factor = + powf(bp->subgrid_mass / (1e6 * constants->const_solar_mass), 0.2); + float edd_factor = powf(bp->eddington_fraction, 0.3); + + /* Gather the factors and finalize calculation */ + const float base = mass_factor * fabsf(bp->spin) / + (props->xi_TD * props->alpha_factor_02 * edd_factor); + const float rw = 1553. * 2. * R_G * powf(base, 0.5714); + + /* Repeat the same for self-gravity radius - eqn. A6 in F2018 */ + mass_factor = + powf(bp->subgrid_mass / (1e6 * constants->const_solar_mass), -1.1556); + edd_factor = powf(bp->eddington_fraction, -0.48889); + + const float rs = 1.2 * 100000. * 2. * R_G * mass_factor * + props->alpha_factor_06222 * edd_factor; + + /* Take the minimum */ + Rw = min(rs, rw); + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (Rw < 0.) { + error( + "Something went wrong with calculation of Rw of black holes. " + " Rw is %f instead of Rw >= 0.", + Rw); + } +#endif + + return Rw; +} + +/** + * @brief Compute the warp mass of a black hole particle. + * + * Calculated as the integral of the surface density of the disk up to R_warp. + * The result again depends on type of accretion mode, both due to different + * R_warp and different surface densities. + * + * The surface densities for the thick and slim disk take the same form + * (eqn. 2.3 in Narayan & Yi 1995 for rho, and then sigma = rho * 2H = + * dot(M_BH) / (2pi * R * abs(v_r))). They differ due to different radial + * radial velocities in the disks: v_r = -alpha * v_0 * v_K (with v_K + * the Keplerian velocity). These differences are encoded in the numerical + * constant v_0, which depends on alpha in Narayan & Yi for the thick disk, + * and is roughly constant for the slim disk (Wang & Zhou 1999). + * + * For the thin disk the surface densities are more complex, and again depend + * on which region of the disk is chosen to be modelled (region b or c from + * Shakura & Sunyaev 1973). Sigma for region b is given by eqn. 7 in Griffin + * et al. (2019) and for region c, it is not given explicitly but can be + * calculated based on Appendix A in Fiacconi et al. (2018). + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float m_warp( + struct bpart* bp, const struct phys_const* constants, + const struct black_holes_props* props) { + + /* Define placeholder variable for the result */ + float Mw = -1.; + + /* Gravitational radius */ + const float R_G = R_gravitational(bp->subgrid_mass, constants); + + /* Start branching depending on which accretion mode the BH is in */ + if ((bp->accretion_mode == BH_thick_disc) || + (bp->accretion_mode == BH_slim_disc)) { + + /* Define v_0, the only factor which differs between thick and slim + disc */ + float v_0; + if (bp->accretion_mode == BH_thick_disc) { + v_0 = props->v_0_ADAF; + } else { + v_0 = props->gamma_SD_inv; + } + + /* Final result based on eqn. 2.3 in Narayan & Yi 1995*/ + Mw = 2. * bp->accretion_rate / + (3. * props->alpha_acc * v_0 * + sqrtf(bp->subgrid_mass * constants->const_newton_G)) * + powf(r_warp(bp, constants, props), 1.5); + } else { + + /* Start branching depending on which region of the thin disk we wish to + base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973, + or TD_region=C: region c) */ + if (props->TD_region == TD_region_B) { + + /* Calculate different factors that appear in result for M_warp */ + const float mass_factor = + powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), 2.2); + const float edd_factor = powf(bp->eddington_fraction, 0.6); + const float R_factor = + powf(r_warp(bp, constants, props) / (2. * R_G), 1.4); + + /* Gather factors and finalize calculation */ + Mw = constants->const_solar_mass * 1.35 * mass_factor * + props->alpha_factor_08_inv * edd_factor * R_factor; + } + if (props->TD_region == TD_region_C) { + + /* Same as above but for region c of disk */ + const float mass_factor = + powf(bp->subgrid_mass / (1e6 * constants->const_solar_mass), 2.2); + const float edd_factor = powf(bp->eddington_fraction, 0.7); + const float R_factor = + powf(r_warp(bp, constants, props) / (2. * R_G), 1.25); + + Mw = constants->const_solar_mass * 0.01 * mass_factor * + props->alpha_factor_08_inv_10 * edd_factor * R_factor; + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (Mw < 0.) { + error( + "Something went wrong with calculation of Mw of black holes. " + " Mw is %f instead of Mw >= 0.", + Mw); + } +#endif + + return Mw; +} + +/** + * @brief Compute the warp angular momentum of a black hole particle. + * + * Calculated as the integral of the surface density times the specific + * angular momentum of the disk up to R_warp. The result depends on type + * of accretion mode, due to different R_warp, surface densities and spec. + * ang. momenta of the disks. + * + * The surface densities are the same as for M_warp. For the thin disk, the + * spec. ang. mom. is L(R) = R * v_K(R), because orbits are perfectly circular. + * For the thick and slim disk, this is replaced by L(R) = Omega_0 * R * v_K(R) + * , with Omega_0 a numerical constant between 0 and 1 which encodes the fact + * that rotation is slower in the two disks. The values for Omega_0 are given + * in Narayan & Yi (1995) and Wang & Zhou (1999) for the thick and slim disk, + * respectively. + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float j_warp( + struct bpart* bp, const struct phys_const* constants, + const struct black_holes_props* props) { + + /* Define placeholder variable for the result */ + float Jw = -1.; + + /* Start branching depending on which accretion mode the BH is in */ + if ((bp->accretion_mode == BH_thick_disc) || + (bp->accretion_mode == BH_slim_disc)) { + + /* Get numerical constants for radial and tangential velocities for the + thick and slim disk, which factor into the surface density and spec. + ang. mom., respectively */ + float v_0 = 0.; + float omega_0 = 0.; + if (bp->accretion_mode == BH_thick_disc) { + v_0 = props->v_0_ADAF; + omega_0 = props->omega_0_ADAF; + } else { + v_0 = props->gamma_SD_inv; + omega_0 = props->gamma_SD_inv; + } + + /* Gather factors for the final result */ + Jw = 2. * bp->accretion_rate * omega_0 / (2. * props->alpha_acc * v_0) * + r_warp(bp, constants, props) * r_warp(bp, constants, props); + } else { + + /* Start branching depending on which region of the thin disk we wish to + base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973, + or TD_region=C: region c). The warp radius can generally be related to + the warp mass and radius, if one assumes Keplerian rotation, with the + following relation: J_warp = (c+2)/(c+5/2) * M_warp * sqrt(M_BH * G * + R_warp), where c is the slope of the surface density profile: sigma~R^c. + For region b, c=-3/5 (see Griffin et al. 2019), and for region c, c=-3/4 + (see Fiacconi et al. 2018). */ + if (props->TD_region == TD_region_B) { + Jw = 0.737 * m_warp(bp, constants, props) * + sqrtf(bp->subgrid_mass * constants->const_newton_G * + r_warp(bp, constants, props)); + } + if (props->TD_region == TD_region_C) { + Jw = 0.714 * m_warp(bp, constants, props) * + sqrtf(bp->subgrid_mass * constants->const_newton_G * + r_warp(bp, constants, props)); + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (Jw < 0.) { + error( + "Something went wrong with calculation of Jw of black holes. " + " Jw is %f instead of Jw >= 0.", + Jw); + } +#endif + + return Jw; +} + +/** + * @brief Compute the spin-dependant radiative efficiency of a BH particle in + * the radiatively efficient (thin disc) regime. + * + * This is eqn. 3 in Griffin et al. (2019), based on Novikov & Thorne (1973). + * + * @param a Black hole spin, -1 < a < 1. + */ +__attribute__((always_inline)) INLINE static float eps_NT(float a) { + +#ifdef SWIFT_DEBUG_CHECKS + if (r_isco(a) <= 0.6667) { + error( + "Something went wrong with calculation of eps_NT of black holes. " + " r_isco is %f instead of r_isco > 1.", + r_isco(a)); + } +#endif + + return 1. - sqrtf(1. - 2. / 3. / r_isco(a)); +} + +/** + * @brief Compute the spin- and accretion rate-dependant radiative efficiency + * of a BH particle in the super-Eddington (slim disk) regime. + * + * This is eqn. 3 in Madau et al. (2014), which is based on numerical GR + * results by Sadowski (2009). + * + * @param a Black hole spin, -1 < a < 1. + * @param m_dot Accretion rate normalized to the Eddington rate. + */ +__attribute__((always_inline)) INLINE static float eps_SD(float a, float mdot) { + const float B = powf(4.627 - 4.445 * a, -0.5524); + const float C = powf(827.3 - 718.1 * a, -0.706); + const float A = powf(0.9663 - 0.9292 * a, -0.5693); + +#ifdef SWIFT_DEBUG_CHECKS + if (mdot <= 0.) { + error( + "The calculation of eps_SD was called even though mdot is %f. " + " This function should not have been called if the accretion rate is " + " not > 0.", + mdot); + } +#endif + + return 0.1 / mdot * (0.985 / (B + 1.6 / mdot) + 0.015 / (C + 1.6 / mdot)) * A; +} + +/** + * @brief Decide which regime (mode) of accretion the BH particle is in. + * + * The possible modes are the thick disk, thin disk and slim disk, in + * order of increasing accretion rate. The transition from thick to thin disk + * is at 0.4*alpha^2, based on current theory (Yuan & Narayan 2014). The + * transition from thin to slim disk occurs when the slim disk efficiency + * becomes sufficiently weak compared to the thin disk one. We parametrize + * this transition as occuring at eps_SD = props->TD_SD_eps_r_threshold * + * eps_TD, with props->TD_SD_eps_r_threshold < 1 of order 0.5 + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static void decide_mode( + struct bpart* bp, const struct black_holes_props* props) { + if (bp->eddington_fraction < props->mdot_crit_ADAF) { + bp->accretion_mode = BH_thick_disc; + } else { + if ((eps_SD(bp->spin, bp->eddington_fraction) < + props->TD_SD_eps_r_threshold * eps_NT(bp->spin)) && + (props->include_slim_disk)) { + bp->accretion_mode = BH_slim_disc; + } else { + bp->accretion_mode = BH_thin_disc; + } + } + + /* If we do not include radiative feedback, then we force the disk to be in + the thick disk mode */ + if (props->turn_off_radiative_feedback) { + bp->accretion_mode = BH_thick_disc; + } + + /* similar for if we do not include jets - we force the disk to be thin */ + if (props->include_jets == 0) { + bp->accretion_mode = BH_thin_disc; + } +} + +/** + * @brief Compute the aspect ratio of the subgrid accretion disk. + * + * The result depends on bp->accretion_mode (thick disk, thin disk or + * slim disk). For the thick disk and slim disk, the aspect ratio is + * a constant, H/R = h_0. + * + * For the thin disk, the result depends on props->TD_region (B - region b from + * Shakura & Sunyaev 1973, C - region c from Shakura & Sunyaev 1973). In region + * b, we take H/R as eqn. 8 in Griffin et al. (2019), and in region c H/r is + * taken directly as eqn. 2.19 from Shakura & Sunyaev (1973). + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float aspect_ratio( + struct bpart* bp, const struct phys_const* constants, + const struct black_holes_props* props) { + + /* Define placeholder variable for the result */ + float h_0 = -1.; + + /* Start branching depending on which accretion mode the BH is in */ + if ((bp->accretion_mode == BH_thick_disc) || + (bp->accretion_mode == BH_slim_disc)) { + if (bp->accretion_mode == BH_thick_disc) { + h_0 = props->h_0_ADAF; + } else { + h_0 = 0.5 * props->gamma_SD_inv; + } + } else { + + /* Start branching depending on which region of the thin disk we wish to + base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973, + or TD_region=C: region c). */ + if (props->TD_region == TD_region_B) { + + /* Compute factors for eqn. 8 in Griffin et al. (2019). */ + const float mass_factor = + powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), -0.1); + const float edd_factor = powf(bp->eddington_fraction, 0.2); + const float R_G = R_gravitational(bp->subgrid_mass, constants); + const float R_factor = + powf(r_warp(bp, constants, props) / (2. * R_G), 0.05); + + /* Gather factors and finalize calculation. */ + h_0 = 1.25 * 0.001 * mass_factor * props->alpha_factor_01 * edd_factor * + R_factor; + } + if (props->TD_region == TD_region_C) { + + /* Compute factors for eqn. 2.19 in Shakura & Sunyaev (1973). */ + const float mass_factor = + powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), -0.1); + const float edd_factor = powf(bp->eddington_fraction, 0.15); + const float R_G = R_gravitational(bp->subgrid_mass, constants); + const float R_factor = + powf(r_warp(bp, constants, props) / (2. * R_G), 0.125); + + /* Gather factors and finalize calculation. */ + h_0 = 1.15 * 0.001 * mass_factor * props->alpha_factor_01 * edd_factor * + R_factor; + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (h_0 <= 0.) { + error( + "Something went wrong with calculation of h_0 of black holes. " + " h_0 is %f instead of h_0 > 0.", + h_0); + } +#endif + + return h_0; +} + +/** + * @brief Compute the jet efficiency of a BH particle. + * + * The result depends on bp->accretion_mode (thick disk, thin disk or + * slim disk), through the varying H/R aspect ratios. + * + * The equation implemented is eqn. 9 from Tchekhovskoy et al. (2010), with the + * dimensionless magnetic flux phi taken as eqn. 9 from Narayan et al. (2021). + * + * The dependence on the aspect ratio comes from results in Tchekhovskoy et al. + * (2014) and the dependence in classical Blandford & Znajek (1979) jet theory. + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float jet_efficiency( + struct bpart* bp, const struct black_holes_props* props) { + + float jet_eff = -1.; + if (props->fix_jet_efficiency) { + jet_eff = props->jet_efficiency; + } else { + const float kappa = 0.05; + const float horizon_ang_vel = + bp->spin / (2. * (1. + sqrtf(1. - bp->spin * bp->spin))); + const float phi = -20.2 * bp->spin * bp->spin * bp->spin - + 14.9 * bp->spin * bp->spin + 34. * bp->spin + 52.6; + jet_eff = kappa * 0.25 * M_1_PI * phi * phi * + powf(bp->aspect_ratio * 3.333, props->jet_h_r_slope) * + horizon_ang_vel * horizon_ang_vel * + (1. + 1.38 * horizon_ang_vel * horizon_ang_vel - + 9.2 * horizon_ang_vel * horizon_ang_vel * horizon_ang_vel * + horizon_ang_vel); + } + + /* Turn off jet feedback if we want to do that */ + if (props->include_jets == 0) { + jet_eff = 0.; + } + + /* Turn off jets in thin disk mode if we want to do that */ + if ((props->turn_off_secondary_feedback) && + (bp->accretion_mode == BH_thin_disc)) { + jet_eff = 0.; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (jet_eff < 0.) { + error( + "Something went wrong with calculation of jet efficiency of black " + "holes. jet_eff is %f instead of jet_eff >= 0.", + jet_eff); + } +#endif + + return jet_eff; +} + +/** + * @brief Compute the radiative efficiency of a BH particle. + * + * The result depends on bp->accretion_mode (thick disk, thin disk or + * slim disk), since all modes have different radiative physics. + * + * For the thin disk, we assume the Novikov-Thorne (1973) radiative efficiency + * based on general relativity. For the slim disk, we take the fit from Madau + * et al. (2014), which is based on numerical GR results by Sadowski (2009). + * For the thick disk, we assume radiative efficiencies from Mahadevan et al. + * (1997). + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float rad_efficiency( + struct bpart* bp, const struct black_holes_props* props) { + + /* Calculate Novikov-Thorne efficiency, which will be needed twice. */ + const float eps_TD = eps_NT(bp->spin); + + /* Define placeholder variable for the result */ + float rad_eff = -1; + + if (props->fix_radiative_efficiency) { + rad_eff = props->radiative_efficiency; + } else { + + /* Start branching depending on which accretion mode the BH is in */ + if (bp->accretion_mode == BH_thin_disc) { + + /* Assign Novikov-Thorne efficiency to the thin disk. */ + rad_eff = eps_TD; + } else if (bp->accretion_mode == BH_slim_disc) { + + /* Assign Madau 2014 efficiency to the slim disk. */ + rad_eff = eps_SD(bp->spin, bp->eddington_fraction); + } else { + +#ifdef SWIFT_DEBUG_CHECKS + if (props->beta_acc > 1.) { + error( + "Something went wrong with calculation of radiative efficiency of " + " black holes. beta_acc is %f instead of beta_acc < 1.", + props->beta_acc); + } +#endif + + /* Assign Mahadevan 1997 efficiency to the thick disk. */ + if (bp->eddington_fraction < props->edd_crit_thick) { + rad_eff = 4.8 * eps_TD / r_isco(bp->spin) * (1. - props->beta_acc) * + props->delta_ADAF; + } else { + rad_eff = 2.4 * eps_TD / r_isco(bp->spin) * props->beta_acc * + bp->eddington_fraction * props->alpha_acc_2_inv; + } + } + } + + /* Turn off radiative feedback if we want to do that */ + if (props->turn_off_radiative_feedback) { + rad_eff = 0.; + } + + /* Turn off radiation in the thick disk mode if we want to do that */ + if ((props->turn_off_secondary_feedback) && + (bp->accretion_mode == BH_thick_disc)) { + rad_eff = 0.; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (rad_eff < 0.) { + error( + "Something went wrong with calculation of radiative efficiency of " + " black holes. rad_eff is %f instead of rad_eff >= 0.", + rad_eff); + } +#endif + + return rad_eff; +} + +/** + * @brief Compute the spec. ang. mom. at the inner radius of a BH particle. + * + * The result depends on bp->accretion_mode (thick disk, thin disk or + * slim disk), since advection-dominated modes (thick and slim disk) + * have more radial orbits. + * + * For the thin disk, we assume that the spec. ang. mom. consumed matches that + * of the innermost stable circular orbit (ISCO). For the other two modes, we + * assume that the accreted ang. mom. at the event horizon is 45 per cent of + * that at the ISCO, based on the fit from Benson & Babul (2009). + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float l_acc( + struct bpart* bp, const struct phys_const* constants, + const struct black_holes_props* props) { + + /* Define placeholder variable for the result */ + float L = -1.; + +#ifdef SWIFT_DEBUG_CHECKS + if (r_isco(bp->spin) <= 0.6667) { + error( + "Something went wrong with calculation of l_acc of black holes. " + " r_isco is %f instead of r_isco > 1.", + r_isco(bp->spin)); + } +#endif + + /* Spec. ang. mom. at ISCO */ + const float L_ISCO = 0.385 * (1. + 2. * sqrtf(3. * r_isco(bp->spin) - 2.)); + + /* Branch depending on which accretion mode the BH is in */ + if ((bp->accretion_mode == BH_thick_disc) || + (bp->accretion_mode == BH_slim_disc)) { + L = 0.45 * L_ISCO; + } else { + L = L_ISCO; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (L <= 0.) { + error( + "Something went wrong with calculation of l_acc of black holes. " + " l_acc is %f instead of l_acc > 0.", + L); + } +#endif + + return L; +} + +/** + * @brief Compute the evolution of the spin of a BH particle. + * + * The result depends on bp->accretion_mode (thick disk, thin disk or + * slim disk), due to differing spec. ang. momenta as well as jet and + * radiative efficiencies. + * + * This equation corresponds to eqn. 2 in Benson & Babul (2009), including + * a jet spindown term. + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float da_dln_mbh_0( + struct bpart* bp, const struct phys_const* constants, + const struct black_holes_props* props) { + const float a = bp->spin; + + if ((a == 0.) || (a < -0.9981) || (a > 0.9981)) { + error( + "The da_dln_mbh_0 function was called and spin is %f. Spin should " + " not be a = 0, a < -0.998 or a > 0.998.", + a); + } + + float spinup_rate = 0.; + + if (props->include_GRMHD_spindown) { + if (bp->accretion_mode == BH_thin_disc) { + spinup_rate = l_acc(bp, constants, props) - + 2. * a * (1. - rad_efficiency(bp, props)); + } else { + spinup_rate = 0.45 - 12.53 * a - 7.8 * a * a + 9.44 * a * a * a + + 5.71 * a * a * a * a - 4.03 * a * a * a * a * a; + } + } else { + spinup_rate = + l_acc(bp, constants, props) - + 2. * a * (1. - rad_efficiency(bp, props)) - + sqrtf(1. - a * a) / a * + (a * a + (1. + sqrtf(1. - a * a)) * (1. + sqrtf(1. - a * a))) * + jet_efficiency(bp, props); + } + + return spinup_rate; +} + +/** + * @brief Compute the jet kick velocity to be used for jet feedback. + * + * @param bp The #bpart doing feedback. + * @param props Properties of the BH scheme. + * @param cosmo The current cosmological model. + * @param constants The physical constants (in internal units). + */ +__attribute__((always_inline)) INLINE static float black_hole_feedback_dv_jet( + const struct bpart* bp, const struct black_holes_props* props, + const struct cosmology* cosmo, const struct phys_const* constants) { + + float v_jet = -1.; + if (props->AGN_jet_velocity_model == AGN_jet_velocity_BH_mass) { + + /* Assign the halo mass according to an empirical relation given in the + parameter file */ + const float halo_mass = + powf(bp->subgrid_mass / props->v_jet_BH_mass_scaling_reference_mass, + props->v_jet_BH_mass_scaling_slope); + + /* Get the critical density and virial overdensity at this redshift */ + const float critical_density = cosmo->critical_density; + const float overdensity = cosmo->overdensity_BN98; + + /* Gather the previous factors and compute the virial radius, virial + velocity and finally the sound speed in the hot gas */ + const float virial_radius = + cbrtf(3. * halo_mass / (4. * M_PI * overdensity * critical_density)); + const float virial_velocity = + sqrtf(halo_mass * constants->const_newton_G / virial_radius); + const float sound_speed = sqrtf(5. / 3. * 0.5) * virial_velocity; + + /* Return the jet velocity as some factor times the sound speed */ + v_jet = fmaxf(props->v_jet_min, props->v_jet_cs_ratio * sound_speed); + + } else if (props->AGN_jet_velocity_model == AGN_jet_velocity_constant) { + v_jet = props->v_jet; + + } else if (props->AGN_jet_velocity_model == AGN_jet_velocity_mass_loading) { + + /* Calculate jet velocity from the efficiency and mass loading, and then + apply a floor value*/ + v_jet = sqrtf(2.f * bp->jet_efficiency / props->v_jet_mass_loading) * + constants->const_speed_light_c; + v_jet = fmaxf(props->v_jet_min, v_jet); + + } else if (props->AGN_jet_velocity_model == AGN_jet_velocity_local) { + + /* Calculate jet power */ + const float jet_power = bp->jet_efficiency * bp->accretion_rate * + constants->const_speed_light_c * + constants->const_speed_light_c; + + /* Calculate jet velocity from the power, smoothing length (proper, not + comoving) and neighbour mass. Then apply floor. */ + v_jet = props->v_jet_xi * cbrt(jet_power * bp->h * cosmo->a / + (bp->ngb_mass / ((double)bp->num_ngbs))); + v_jet = fmaxf(v_jet, props->v_jet_min); + + } else if (props->AGN_jet_velocity_model == AGN_jet_velocity_sound_speed) { + + /* Calculate jet power */ + const float jet_power = bp->jet_efficiency * bp->accretion_rate * + constants->const_speed_light_c * + constants->const_speed_light_c; + + /* Calculate jet velocity from the power, smoothing length (proper, not + comoving), neighbour sound speed and neighbour mass. Apply floor. */ + v_jet = props->v_jet_xi * sqrtf(jet_power * bp->h * cosmo->a / + (bp->ngb_mass * bp->sound_speed_gas)); + v_jet = fmaxf(v_jet, props->v_jet_min); + + } else { + error( + "The scaling of jet velocities with halo mass is currently not " + "supported."); + } + + if (v_jet <= 0.) { + error( + "The black_hole_feedback_dv_jet returned a value less than 0. which " + " is v_jet = %f.", + v_jet); + } + + return v_jet; +} + +/** + * @brief Auxilliary function used for the calculation of final spin of + * a BH merger. + * + * This implements the fitting formula for the variable l from Barausse & + * Rezolla (2009), ApJ, 704, Equation 10. It is used in the merger_spin_evolve() + * function. + * + * @param a1 spin of the first (more massive) black hole + * @param a2 spin of the less massive black hole + * @param q mass ratio of the two black holes, 0 < q < 1 + * @param eta symmetric mass ratio of the two black holes + * @param cos_alpha cosine of the angle between the two spins + * @param cos_beta cosine of the angle between the first spin and the initial + * total angular momentum + * @param cos_gamma cosine of the angle between the second spin and the initial + * total angular momentu + */ +__attribute__((always_inline)) INLINE static float l_variable( + const float a1, const float a2, const float q, const float eta, + const float cos_alpha, const float cos_beta, const float cos_gamma) { + + /* Define the numerical fitting parameters used in Eqn. 10 */ + const float s4 = -0.1229f; + const float s5 = 0.4537f; + const float t0 = -2.8904f; + const float t2 = -3.5171f; + const float t3 = 2.5763f; + + /* Gather the terms of Eqn. 10 */ + const float term1 = 2.f * sqrtf(3.f); + const float term2 = t2 * eta; + const float term3 = t3 * eta * eta; + const float term4 = + s4 * + (a1 * a1 + a2 * a2 * q * q * q * q + 2.f * a1 * a2 * q * q * cos_alpha) / + ((1.f + q * q) * (1.f + q * q)); + const float term5 = (s5 * eta + t0 + 2.f) * + (a1 * cos_beta + a2 * q * q * cos_gamma) / (1.f + q * q); + + /* Return the variable l */ + return term1 + term2 + term3 + term4 + term5; +} + +/** + * @brief Auxilliary function used for the calculation of final spin of + * a BH merger. + * + * This implements the fitting formula for the final spin from Barausse & + * Rezolla (2009), ApJ, 704, Equation 6. It is used in the merger_spin_evolve() + * function. + * + * @param a1 spin of the first (more massive) black hole + * @param a2 spin of the less massive black hole + * @param q mass ratio of the two black holes, 0 < q < 1 + * @param cos_alpha cosine of the angle between the two spins + * @param cos_beta cosine of the angle between the first spin and the initial + * total angular momentum + * @param cos_gamma cosine of the angle between the second spin and the initial + * total angular momentu + */ +__attribute__((always_inline)) INLINE static float final_spin( + const float a1, const float a2, const float q, const float cos_alpha, + const float cos_beta, const float cos_gamma, const float l) { + + /* Gather the terms of Eqn. 6 */ + const float term1 = a1 * a1; + const float term2 = a2 * a2 * q * q * q * q; + const float term3 = 2.f * a1 * a2 * q * q * cos_alpha; + const float term4 = 2.f * (a1 * cos_beta + a2 * q * q * cos_gamma) * l * q; + const float term5 = l * l * q * q; + + /* Calculate the final spin */ + return sqrtf(term1 + term2 + term3 + term4 + term5) / ((1.f + q) * (1.f + q)); +} + +/** + * @brief Auxilliary function used for the calculation of mass lost to GWs. + * + * In this model (SWIFT-EAGLE with spin) we assume 0 losses. + * + * @param a1 spin of the first (more massive) black hole + * @param a2 spin of the less massive black hole + * @param q mass ratio of the two black holes, 0 < q < 1 + * @param eta symmetric mass ratio of the two black holes + * @param cos_beta cosine of the angle between the first spin and the initial + * total angular momentum + * @param cos_gamma cosine of the angle between the second spin and the initial + * total angular momentu + */ +__attribute__((always_inline)) INLINE static float mass_fraction_lost_to_GWs( + const float a1, const float a2, const float q, const float eta, + const float cos_beta, const float cos_gamma) { + return 0.; +} + +/** + * @brief Compute the resultant spin of a black hole merger, as well as the + * mass lost to gravitational waves. + * + * This implements the fitting formula for the final spin from Barausse & + * Rezolla (2009), ApJ, 704, Equations 6 and 7. For the fraction of mass lost, + * we use Eqns 16-18 from Barausse et al. (2012), ApJ, 758. + * + * @param bp Pointer to the b-particle data. + * @param constants Physical constants (in internal units). + * @param props Properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float merger_spin_evolve( + struct bpart* bpi, const struct bpart* bpj, + const struct phys_const* constants) { + + /* Check if something is wrong with the masses. This is important and could + possibly happen as a result of jet spindown and mass loss at any time, + so we want to know about it. */ + if ((bpj->subgrid_mass <= 0.) || (bpi->subgrid_mass <= 0.)) { + error( + "Something went wrong with calculation of spin of a black hole " + " merger remnant. The black hole masses are %f and %f, instead of > " + "0.", + bpj->subgrid_mass, bpi->subgrid_mass); + } + + /* Get the black hole masses before the merger and losses to GWs. */ + const float m1 = bpi->subgrid_mass; + const float m2 = bpj->subgrid_mass; + + /* Define some variables (combinations of mass ratios) used in the + papers described in the header. */ + const float mass_ratio = m2 / m1; + const float sym_mass_ratio = + mass_ratio / ((mass_ratio + 1.f) * (mass_ratio + 1.f)); + const float reduced_mass = m1 * m2 / (m1 + m2); + + /* The absolute values of the spins are also needed */ + const float spin1 = fabsf(bpi->spin); + const float spin2 = fabsf(bpj->spin); + + /* Check if the BHs have been spun down to 0. This is again an important + potential break point, we want to know about it. */ + if ((spin1 == 0.) || (spin2 == 0.)) { + error( + "Something went wrong with calculation of spin of a black hole " + " merger remnant. The black hole spins are %f and %f, instead of > 0.", + spin1, spin2); + } + + /* Define the spin directions. */ + const float spin_vec1[3] = {spin1 * bpi->angular_momentum_direction[0], + spin1 * bpi->angular_momentum_direction[1], + spin1 * bpi->angular_momentum_direction[2]}; + const float spin_vec2[3] = {spin2 * bpj->angular_momentum_direction[0], + spin2 * bpj->angular_momentum_direction[1], + spin2 * bpj->angular_momentum_direction[2]}; + + /* We want to compute the direction of the orbital angular momentum of the + two BHs, which is used in the fits. Start by defining the coordinates in + the frame of one of the BHs (it doesn't matter which one, the total + angular momentum is the same). */ + const float relative_coordinates[3] = { + bpj->x[0] - bpi->x[0], bpj->x[1] - bpi->x[1], bpj->x[2] - bpi->x[2]}; + const float relative_velocities[3] = { + bpj->v[0] - bpi->v[0], bpj->v[1] - bpi->v[1], bpj->v[2] - bpi->v[2]}; + + /* Calculate the orbital angular momentum itself. */ + const float orbital_angular_momentum[3] = { + reduced_mass * (relative_coordinates[1] * relative_velocities[2] - + relative_coordinates[2] * relative_velocities[1]), + reduced_mass * (relative_coordinates[2] * relative_velocities[0] - + relative_coordinates[0] * relative_velocities[2]), + reduced_mass * (relative_coordinates[0] * relative_velocities[1] - + relative_coordinates[1] * relative_velocities[0])}; + + /* Calculate the magnitude of the orbital angular momentum. */ + const float orbital_angular_momentum_magnitude = + sqrtf(orbital_angular_momentum[0] * orbital_angular_momentum[0] + + orbital_angular_momentum[1] * orbital_angular_momentum[1] + + orbital_angular_momentum[2] * orbital_angular_momentum[2]); + + /* Normalize and get the direction of the orbital angular momentum. */ + float orbital_angular_momentum_direction[3] = {0.f, 0.f, 0.f}; + if (orbital_angular_momentum_magnitude > 0.) { + orbital_angular_momentum_direction[0] = + orbital_angular_momentum[0] / orbital_angular_momentum_magnitude; + orbital_angular_momentum_direction[1] = + orbital_angular_momentum[1] / orbital_angular_momentum_magnitude; + orbital_angular_momentum_direction[2] = + orbital_angular_momentum[2] / orbital_angular_momentum_magnitude; + } + + /* We also need to compute the total (initial) angular momentum of the + system, i.e. including the orbital angular momentum and the spins. This + is needed since the final spin is assumed to be along the direction of + this total angular momentum. Hence here we compute the direction. */ + float total_angular_momentum_direction[3] = { + m1 * spin1 * spin_vec1[0] + m2 * spin2 * spin_vec2[0] + + orbital_angular_momentum[0], + m1 * spin1 * spin_vec1[1] + m2 * spin2 * spin_vec2[1] + + orbital_angular_momentum[1], + m1 * spin1 * spin_vec1[2] + m2 * spin2 * spin_vec2[2] + + orbital_angular_momentum[2]}; + + /* The above is actually the total angular momentum, so we need to normalize + to get the directions. */ + const float total_angular_momentum_magnitude = + sqrtf(total_angular_momentum_direction[0] * + total_angular_momentum_direction[0] + + total_angular_momentum_direction[1] * + total_angular_momentum_direction[1] + + total_angular_momentum_direction[2] * + total_angular_momentum_direction[2]); + total_angular_momentum_direction[0] = + total_angular_momentum_direction[0] / total_angular_momentum_magnitude; + total_angular_momentum_direction[1] = + total_angular_momentum_direction[1] / total_angular_momentum_magnitude; + total_angular_momentum_direction[2] = + total_angular_momentum_direction[2] / total_angular_momentum_magnitude; + + /* We now define some extra variables used by the fitting functions. The + below ones are cosines of angles between the two spins and orbital angular + momentum in various combinations (Eqn 9 in Barausse & Rezolla 2009) */ + const float cos_alpha = + (spin_vec1[0] * spin_vec2[0] + spin_vec1[1] * spin_vec2[1] + + spin_vec1[2] * spin_vec2[2]) / + (spin1 * spin2); + const float cos_beta = + (spin_vec1[0] * orbital_angular_momentum_direction[0] + + spin_vec1[1] * orbital_angular_momentum_direction[1] + + spin_vec1[2] * orbital_angular_momentum_direction[2]) / + spin1; + const float cos_gamma = + (spin_vec2[0] * orbital_angular_momentum_direction[0] + + spin_vec2[1] * orbital_angular_momentum_direction[1] + + spin_vec2[2] * orbital_angular_momentum_direction[2]) / + spin2; + + /* Get the variable l used in the fit, see Eqn. 10 in Barausse & Rezolla + (2009). */ + const float l = l_variable(spin1, spin2, mass_ratio, sym_mass_ratio, + cos_alpha, cos_beta, cos_gamma); + +#ifdef SWIFT_DEBUG_CHECKS + if (l < 0.) { + error( + "Something went wrong with calculation of spin of a black hole " + " merger remnant. The l factor is %f, instead of >= 0.", + l); + } +#endif + + /* Calculate the magnitude of final spin from Barausse & Rezolla (2009), + Eqn. 6. */ + const float final_spin_magnitude = + final_spin(spin1, spin2, mass_ratio, cos_alpha, cos_beta, cos_gamma, l); + +#ifdef SWIFT_DEBUG_CHECKS + if (final_spin_magnitude <= 0.) { + error( + "Something went wrong with calculation of spin of a black hole " + " merger remnant. The final spin magnitude is %f, instead of > 0.", + final_spin_magnitude); + } +#endif + + /* Assign the final spin value to the BH, but also make sure we don't go + above 0.998 nor below 0.001. */ + bpi->spin = min(final_spin_magnitude, 0.998); + if (fabsf(bpi->spin) < 0.01) { + bpi->spin = 0.01; + } + + /* Assign the directions of the spin to the BH. */ + bpi->angular_momentum_direction[0] = total_angular_momentum_direction[0]; + bpi->angular_momentum_direction[1] = total_angular_momentum_direction[1]; + bpi->angular_momentum_direction[2] = total_angular_momentum_direction[2]; + + /* Finally we also want to calculate the fraction of total mass-energy + lost during the merger to gravitational waves. We use Eqn. 16 and 18 + from Barausse et al. (2012), ApJ, p758. */ + const float mass_frac_lost_to_GW = mass_fraction_lost_to_GWs( + spin1, spin2, mass_ratio, sym_mass_ratio, cos_beta, cos_gamma); + + return mass_frac_lost_to_GW; +} + +#endif /* SWIFT_SPIN_JET_BLACK_HOLES_SPIN_H */ diff --git a/src/black_holes/SPIN_JET/black_holes_struct.h b/src/black_holes/SPIN_JET/black_holes_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..3ddd08fcc609daf5074ebc93312c1cb2e8859794 --- /dev/null +++ b/src/black_holes/SPIN_JET/black_holes_struct.h @@ -0,0 +1,134 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 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_BLACK_HOLES_STRUCT_SPIN_JET_H +#define SWIFT_BLACK_HOLES_STRUCT_SPIN_JET_H + +/* Standard headers */ +#include <float.h> + +/* Local includes */ +#include "inline.h" + +/** + * @brief Black holes-related fields carried by each *gas* particle. + */ +struct black_holes_part_data { + + /*! ID of the black-hole that will swallow this #part. */ + long long swallow_id; + + /*! Gravitational potential of the particle (for repositioning) */ + float potential; +}; + +/** + * @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 Reset the particle-carried potential at the start of a time-step. + * + * @param p_data The #part's black hole data. + */ +__attribute__((always_inline)) INLINE static void black_holes_init_potential( + struct black_holes_part_data* p_data) { + p_data->potential = FLT_MAX; +} + +/** + * @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_SPIN_JET_H */ diff --git a/src/black_holes_debug.h b/src/black_holes_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..1a00ff2c5da1b4d9cb29b49601cdbbffea71f7bd --- /dev/null +++ b/src/black_holes_debug.h @@ -0,0 +1,36 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_DEBUG_H +#define SWIFT_BLACK_HOLES_DEBUG_H + +/* Config parameters. */ +#include <config.h> + +/* Import the debug routines of the right black holes definition */ +#if defined(BLACK_HOLES_NONE) +#include "./black_holes/Default/black_holes_debug.h" +#elif defined(BLACK_HOLES_EAGLE) +#include "./black_holes/EAGLE/black_holes_debug.h" +#elif defined(BLACK_HOLES_SPIN_JET) +#include "./black_holes/SPIN_JET/black_holes_debug.h" +#else +#error "Invalid choice of BH model" +#endif + +#endif /* SWIFT_BLACK_HOLES_DEBUG_H */ diff --git a/src/black_holes_iact.h b/src/black_holes_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..01fcd73160bb430167ce27c7745d82726e9be3d1 --- /dev/null +++ b/src/black_holes_iact.h @@ -0,0 +1,36 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_BLACK_HOLES_IACT_H +#define SWIFT_BLACK_HOLES_IACT_H + +/* Config parameters. */ +#include <config.h> + +/* Select the correct BH model */ +#if defined(BLACK_HOLES_NONE) +#include "./black_holes/Default/black_holes_iact.h" +#elif defined(BLACK_HOLES_EAGLE) +#include "./black_holes/EAGLE/black_holes_iact.h" +#elif defined(BLACK_HOLES_SPIN_JET) +#include "./black_holes/SPIN_JET/black_holes_iact.h" +#else +#error "Invalid choice of black hole model" +#endif + +#endif /* SWIFT_BLACK_HOLES_IACT_H */ diff --git a/src/black_holes_io.h b/src/black_holes_io.h index e82bcc813984a13b76f036edfea202dbaa5a07ce..0507cef106dd74c18c32146cc3117625dea4ce3c 100644 --- a/src/black_holes_io.h +++ b/src/black_holes_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Matthieu Schaller (schaller@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 @@ -19,7 +19,7 @@ #ifndef SWIFT_BLACK_HOLES_IO_H #define SWIFT_BLACK_HOLES_IO_H -#include "../config.h" +#include <config.h> /* Local includes */ #include "engine.h" @@ -29,6 +29,8 @@ #include "./black_holes/Default/black_holes_io.h" #elif defined(BLACK_HOLES_EAGLE) #include "./black_holes/EAGLE/black_holes_io.h" +#elif defined(BLACK_HOLES_SPIN_JET) +#include "./black_holes/SPIN_JET/black_holes_io.h" #else #error "Invalid choice of BH model" #endif diff --git a/src/black_holes_properties.h b/src/black_holes_properties.h index 3226b5391a979d27ab125c88d50acb3d59e1edf0..f0f7ae660ebbb56cb7949e517e9f334a137e53c3 100644 --- a/src/black_holes_properties.h +++ b/src/black_holes_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,13 +20,15 @@ #define SWIFT_BLACK_HOLES_PROPERTIES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct black_holes model */ #if defined(BLACK_HOLES_NONE) #include "./black_holes/Default/black_holes_properties.h" #elif defined(BLACK_HOLES_EAGLE) #include "./black_holes/EAGLE/black_holes_properties.h" +#elif defined(BLACK_HOLES_SPIN_JET) +#include "./black_holes/SPIN_JET/black_holes_properties.h" #else #error "Invalid choice of black hole model" #endif diff --git a/src/black_holes_struct.h b/src/black_holes_struct.h index 73cc156420699923a71bd23d1c9d239d987bfd95..09027e83f7a3339130a1c6cd6a92070b1715edb2 100644 --- a/src/black_holes_struct.h +++ b/src/black_holes_struct.h @@ -25,7 +25,9 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> + +/* Local includes */ #include "inline.h" /* Import the right black holes definition */ @@ -33,6 +35,8 @@ #include "./black_holes/Default/black_holes_struct.h" #elif defined(BLACK_HOLES_EAGLE) #include "./black_holes/EAGLE/black_holes_struct.h" +#elif defined(BLACK_HOLES_SPIN_JET) +#include "./black_holes/SPIN_JET/black_holes_struct.h" #else #error "Invalid choice of black hole model." #endif diff --git a/src/cache.h b/src/cache.h index 4b32ac92cf01867997b6bca246ecd01d31ae400f..229685a112e074e1b41d0e001f63c3bfced1d79a 100644 --- a/src/cache.h +++ b/src/cache.h @@ -20,7 +20,7 @@ #define SWIFT_CACHE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "align.h" diff --git a/src/cbrt.h b/src/cbrt.h index 54560e41d5e6ef143b839f014ce8f0c4a7624ff6..8ef76a369355cc2b767c861393a2893ca5334fd4 100644 --- a/src/cbrt.h +++ b/src/cbrt.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -21,7 +21,7 @@ #define SWIFT_CBRT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> diff --git a/src/cell.c b/src/cell.c index cf439427d8a70c2b7f29594a3911424902325ca6..c8d5dac7271b6e9f557aa4940c30020d367a3040 100644 --- a/src/cell.c +++ b/src/cell.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -22,7 +22,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -579,18 +579,17 @@ void cell_clean_links(struct cell *c, void *data) { c->hydro.gradient = NULL; c->hydro.force = NULL; c->hydro.limiter = NULL; - c->hydro.rt_inject = NULL; - c->hydro.rt_gradient = NULL; - c->hydro.rt_transport = NULL; + c->rt.rt_gradient = NULL; + c->rt.rt_transport = NULL; c->grav.grav = NULL; c->grav.mm = NULL; c->stars.density = NULL; c->stars.prepare1 = NULL; c->stars.prepare2 = NULL; c->stars.feedback = NULL; - c->sinks.compute_formation = NULL; - c->sinks.merger = NULL; - c->sinks.accretion = NULL; + c->sinks.swallow = NULL; + c->sinks.do_sink_swallow = NULL; + c->sinks.do_gas_swallow = NULL; c->black_holes.density = NULL; c->black_holes.swallow = NULL; c->black_holes.do_gas_swallow = NULL; @@ -1254,7 +1253,7 @@ void cell_check_timesteps(const struct cell *c, const integertime_t ti_current, if (c->hydro.ti_end_min == 0 && c->grav.ti_end_min == 0 && c->stars.ti_end_min == 0 && c->black_holes.ti_end_min == 0 && - c->sinks.ti_end_min == 0 && c->nr_tasks > 0) + c->sinks.ti_end_min == 0 && c->rt.ti_rt_end_min == 0 && c->nr_tasks > 0) error("Cell without assigned time-step"); if (c->split) { diff --git a/src/cell.h b/src/cell.h index 0c223333df58785d33758d7b6287fd733df20e03..7e9de839e6b87cb8a7f73f07e706b5985cf8b527 100644 --- a/src/cell.h +++ b/src/cell.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -24,7 +24,7 @@ #define SWIFT_CELL_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include <stddef.h> @@ -36,8 +36,10 @@ #include "cell_black_holes.h" #include "cell_grav.h" #include "cell_hydro.h" +#include "cell_rt.h" #include "cell_sinks.h" #include "cell_stars.h" +#include "ghost_stats.h" #include "kernel_hydro.h" #include "multipole_struct.h" #include "part.h" @@ -50,6 +52,7 @@ /* Avoid cyclic inclusions */ struct engine; struct scheduler; +struct replication_list; /* Max tag size set to 2^29 to take into account some MPI implementations * that use 2^31 as the upper bound on MPI tags and the fact that @@ -208,6 +211,17 @@ struct pcell { } sinks; + /*! RT variables */ + struct { + + /*! Minimal integer end-of-timestep in this cell for RT tasks */ + integertime_t ti_rt_end_min; + + /*! smallest RT time-step size in this cell */ + integertime_t ti_rt_min_step_size; + + } rt; + /*! Maximal depth in that part of the tree */ int maxdepth; @@ -258,6 +272,16 @@ struct pcell_step { /*! Maximal distance any #part has travelled since last rebuild */ float dx_max_part; } black_holes; + + struct { + + /*! Minimal integer end-of-timestep in this cell (rt) */ + integertime_t ti_rt_end_min; + + /*! smallest RT time-step size in this cell */ + integertime_t ti_rt_min_step_size; + + } rt; }; /** @@ -316,7 +340,10 @@ enum cell_flags { cell_flag_do_hydro_sync = (1UL << 17), cell_flag_do_hydro_sub_sync = (1UL << 18), cell_flag_unskip_self_grav_processed = (1UL << 19), - cell_flag_unskip_pair_grav_processed = (1UL << 20) + cell_flag_unskip_pair_grav_processed = (1UL << 20), + cell_flag_skip_rt_sort = (1UL << 21), /* skip rt_sort after a RT recv? */ + cell_flag_do_rt_sub_sort = (1UL << 22), /* same as hydro_sub_sort for RT */ + cell_flag_rt_requests_sort = (1UL << 23) /* was this sort requested by RT? */ }; /** @@ -368,6 +395,9 @@ struct cell { /*! Sink particles variables */ struct cell_sinks sinks; + /*! Radiative transfer variables */ + struct cell_rt rt; + #ifdef WITH_MPI /*! MPI variables */ struct { @@ -431,7 +461,10 @@ struct cell { float dmin; /*! ID of the previous owner, e.g. runner. */ - int owner; + short int owner; + + /*! ID of a threadpool thread that maybe associated with this cell. */ + short int tpid; /*! ID of the node this cell lives on. */ int nodeID; @@ -462,6 +495,8 @@ struct cell { char subtasks_executed[task_type_count]; #endif + struct ghost_stats ghost_statistics; + } SWIFT_STRUCT_ALIGN; /* Convert cell location to ID. */ @@ -536,14 +571,19 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s, const int with_star_formation, const int with_star_formation_sink); int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s); -int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s); +int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s, + const int sub_cycle); int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s); int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s); -void cell_drift_part(struct cell *c, const struct engine *e, int force); -void cell_drift_gpart(struct cell *c, const struct engine *e, int force); -void cell_drift_spart(struct cell *c, const struct engine *e, int force); +void cell_drift_part(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list_in); +void cell_drift_gpart(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list); +void cell_drift_spart(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list); void cell_drift_sink(struct cell *c, const struct engine *e, int force); -void cell_drift_bpart(struct cell *c, const struct engine *e, int force); +void cell_drift_bpart(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list); 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(const struct cell *c, const integertime_t ti_current, @@ -575,7 +615,8 @@ void cell_activate_subcell_black_holes_tasks(struct cell *ci, struct cell *cj, void cell_activate_subcell_external_grav_tasks(struct cell *ci, struct scheduler *s); void cell_activate_subcell_rt_tasks(struct cell *ci, struct cell *cj, - struct scheduler *s); + struct scheduler *s, const int sub_cycle); +void cell_set_no_rt_sort_flag_up(struct cell *c); void cell_activate_super_spart_drifts(struct cell *c, struct scheduler *s); void cell_activate_super_sink_drifts(struct cell *c, struct scheduler *s); void cell_activate_drift_part(struct cell *c, struct scheduler *s); @@ -584,6 +625,7 @@ void cell_activate_drift_spart(struct cell *c, struct scheduler *s); void cell_activate_drift_sink(struct cell *c, struct scheduler *s); void cell_activate_drift_bpart(struct cell *c, struct scheduler *s); void cell_activate_sync_part(struct cell *c, struct scheduler *s); +void cell_activate_rt_sorts(struct cell *c, int sid, struct scheduler *s); void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s); void cell_activate_stars_sorts(struct cell *c, int sid, struct scheduler *s); void cell_activate_limiter(struct cell *c, struct scheduler *s); @@ -630,6 +672,18 @@ int cell_can_use_pair_mm(const struct cell *ci, const struct cell *cj, const struct engine *e, const struct space *s, const int use_rebuild_data, const int is_tree_walk); +/** + * @brief Does a #cell contain no particle at all. + * + * @param c The #cell. + */ +__attribute__((always_inline)) INLINE static int cell_is_empty( + const struct cell *c) { + + return (c->hydro.count == 0 && c->grav.count == 0 && c->stars.count == 0 && + c->black_holes.count == 0 && c->sinks.count == 0); +} + /** * @brief Compute the square of the minimal distance between any two points in * two cells of the same size @@ -684,9 +738,12 @@ __attribute__((always_inline)) INLINE static double cell_min_dist2_same_size( } else { - const double dx = min(fabs(cix_max - cjx_min), fabs(cix_min - cjx_max)); - const double dy = min(fabs(ciy_max - cjy_min), fabs(ciy_min - cjy_max)); - const double dz = min(fabs(ciz_max - cjz_min), fabs(ciz_min - cjz_max)); + const double dx = min4(fabs(cix_min - cjx_min), fabs(cix_min - cjx_max), + fabs(cix_max - cjx_min), fabs(cix_max - cjx_max)); + const double dy = min4(fabs(ciy_min - cjy_min), fabs(ciy_min - cjy_max), + fabs(ciy_max - cjy_min), fabs(ciy_max - cjy_max)); + const double dz = min4(fabs(ciz_min - cjz_min), fabs(ciz_min - cjz_max), + fabs(ciz_max - cjz_min), fabs(ciz_max - cjz_max)); return dx * dx + dy * dy + dz * dz; } diff --git a/src/cell_black_holes.h b/src/cell_black_holes.h index 6064375c629214f7c7315aee722aed09aa3c5781..fb8a779b4b7c33b8db6d59b44f6480307e2fb36c 100644 --- a/src/cell_black_holes.h +++ b/src/cell_black_holes.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_CELL_BLACK_HOLES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "lock.h" diff --git a/src/cell_convert_part.c b/src/cell_convert_part.c index 41c2c6145e81501bb015cb6f5c4b17d2987b070d..78f430a07c3c91150fc87482edb4235108f46363 100644 --- a/src/cell_convert_part.c +++ b/src/cell_convert_part.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "cell.h" @@ -155,7 +155,7 @@ void cell_recursively_shift_gparts(struct cell *c, struct spart *cell_add_spart(struct engine *e, struct cell *const c) { /* Perform some basic consitency checks */ if (c->nodeID != engine_rank) error("Adding spart on a foreign node"); - if (c->grav.ti_old_part != e->ti_current) error("Undrifted cell!"); + if (c->stars.ti_old_part != e->ti_current) error("Undrifted cell!"); if (c->split) error("Addition of spart performed above the leaf level"); /* Progeny number at each level */ @@ -565,6 +565,9 @@ void cell_remove_part(const struct engine *e, struct cell *c, struct part *p, /* Mark the particle as inhibited */ p->time_bin = time_bin_inhibited; + /* Mark the RT time bin as inhibited as well, + * so part_is_rt_active() checks work as intended */ + p->rt_time_data.time_bin = time_bin_inhibited; /* Mark the gpart as inhibited and stand-alone */ if (p->gpart) { diff --git a/src/cell_drift.c b/src/cell_drift.c index 53997e39676b1597bc34cb7ea6f4518044a91582..dd8a0e47065315882b8d24c250aec0c3b99fc6cd 100644 --- a/src/cell_drift.c +++ b/src/cell_drift.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "cell.h" @@ -30,13 +30,112 @@ #include "drift.h" #include "feedback.h" #include "gravity.h" +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_array.h" #include "multipole.h" #include "neutrino.h" #include "pressure_floor.h" #include "rt.h" +#include "sink.h" #include "star_formation.h" #include "tracers.h" +#ifdef WITH_LIGHTCONE +/** + * @brief Compute refined lightcone replication list for a cell + * + * Returns a pointer to the new list, which must be freed later. + * + * @param e The #engine + * @param c The #cell + * @param replication_list_in The input replication_list struct + */ +static struct replication_list *refine_replications( + const struct engine *e, const struct cell *c, + struct replication_list *replication_list_in) { + struct replication_list *replication_list; + if (e->lightcone_array_properties->nr_lightcones > 0) { + if (replication_list_in) { + /* We're not at the top of the hierarchy, so use the replication lists + * passed in */ + replication_list = replication_list_in; + } else { + /* Current call is top of the recursive hierarchy, so compute refined + * replication lists */ + replication_list = + lightcone_array_refine_replications(e->lightcone_array_properties, c); + } + } else { + replication_list = NULL; + } + return replication_list; +} +#endif + +/** + * @brief Recursively set the hydro's ti_old_part to the current time. + * + * @param c The cell to update. + * @param ti The current integer time. + */ +void cell_set_ti_old_part(struct cell *c, const integertime_t ti) { + + c->hydro.ti_old_part = ti; + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) cell_set_ti_old_part(c->progeny[k], ti); + } + } +} + +/** + * @brief Recursively set the gravity's ti_old_part to the current time. + * + * @param c The cell to update. + * @param ti The current integer time. + */ +void cell_set_ti_old_gpart(struct cell *c, const integertime_t ti) { + + c->grav.ti_old_part = ti; + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) cell_set_ti_old_gpart(c->progeny[k], ti); + } + } +} + +/** + * @brief Recursively set the stars' ti_old_part to the current time. + * + * @param c The cell to update. + * @param ti The current integer time. + */ +void cell_set_ti_old_spart(struct cell *c, const integertime_t ti) { + + c->stars.ti_old_part = ti; + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) cell_set_ti_old_spart(c->progeny[k], ti); + } + } +} + +/** + * @brief Recursively set the black holes' ti_old_part to the current time. + * + * @param c The cell to update. + * @param ti The current integer time. + */ +void cell_set_ti_old_bpart(struct cell *c, const integertime_t ti) { + + c->black_holes.ti_old_part = ti; + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) cell_set_ti_old_bpart(c->progeny[k], ti); + } + } +} + /** * @brief Recursively drifts the #part in a cell hierarchy. * @@ -44,7 +143,8 @@ * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ -void cell_drift_part(struct cell *c, const struct engine *e, int force) { +void cell_drift_part(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); @@ -77,12 +177,20 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { cell_clear_flag(c, cell_flag_do_hydro_drift | cell_flag_do_hydro_sub_drift); /* Update the time of the last drift */ - c->hydro.ti_old_part = ti_current; + cell_set_ti_old_part(c, ti_current); return; } - /* Ok, we have some particles somewhere in the hierarchy to drift */ + /* Ok, we have some particles somewhere in the hierarchy to drift + + IMPORTANT: after this point we must not return without freeing the + replication lists if we allocated them. + */ + struct replication_list *replication_list = NULL; +#ifdef WITH_LIGHTCONE + replication_list = refine_replications(e, c, replication_list_in); +#endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_hydro_sub_drift))) { @@ -93,7 +201,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { struct cell *cp = c->progeny[k]; /* Collect */ - cell_drift_part(cp, e, force); + cell_drift_part(cp, e, force, replication_list); /* Update */ dx_max = max(dx_max, cp->hydro.dx_max_part); @@ -148,8 +256,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { /* Drift... */ drift_part(p, xp, dt_drift, dt_kick_hydro, dt_kick_grav, dt_therm, - ti_old_part, ti_current, e->cosmology, e->hydro_properties, - e->entropy_floor); + ti_old_part, ti_current, e, replication_list, c->loc); /* Update the tracers properties */ tracers_after_drift(p, xp, e->internal_units, e->physical_constants, @@ -225,12 +332,16 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { /* Mark the particle has not being swallowed */ black_holes_mark_part_as_not_swallowed(&p->black_holes_data); + /* Mark the particle has not being swallowed by a sink */ + sink_mark_part_as_not_swallowed(&p->sink_data); + /* Reset the gas particle-carried feedback fields */ feedback_reset_part(p, xp); /* Get ready for a density calculation */ if (part_is_active(p, e)) { hydro_init_part(p, &e->s->hs); + mhd_init_part(p); black_holes_init_potential(&p->black_holes_data); chemistry_init_part(p, e->chemistry); pressure_floor_init_part(p, xp); @@ -238,6 +349,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { tracers_after_init(p, xp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, e->hydro_properties, e->cooling_func, e->time); + sink_init_part(p); rt_init_part(p); /* Update the maximal active smoothing length in the cell */ @@ -264,6 +376,14 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { c->hydro.ti_old_part = ti_current; } +#ifdef WITH_LIGHTCONE + /* If we're at the top of the recursive hierarchy, clean up the refined + * replication lists */ + if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) + lightcone_array_free_replications(e->lightcone_array_properties, + replication_list); +#endif + /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_hydro_drift | cell_flag_do_hydro_sub_drift); } @@ -275,7 +395,8 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ -void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { +void cell_drift_gpart(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); @@ -304,12 +425,21 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { cell_clear_flag(c, cell_flag_do_grav_drift | cell_flag_do_grav_sub_drift); /* Update the time of the last drift */ - c->grav.ti_old_part = ti_current; + cell_set_ti_old_gpart(c, ti_current); return; } - /* Ok, we have some particles somewhere in the hierarchy to drift */ + /* Ok, we have some particles somewhere in the hierarchy to drift. + If making lightcones, get the refined replication list for this cell. + + IMPORTANT: after this point we must not return without freeing the + replication lists if we allocated them. + */ + struct replication_list *replication_list = NULL; +#ifdef WITH_LIGHTCONE + replication_list = refine_replications(e, c, replication_list_in); +#endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_grav_sub_drift))) { @@ -320,7 +450,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { struct cell *cp = c->progeny[k]; /* Recurse */ - cell_drift_gpart(cp, e, force); + cell_drift_gpart(cp, e, force, replication_list); } } @@ -353,7 +483,8 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { } /* Drift... */ - drift_gpart(gp, dt_drift_k, ti_old_gpart, ti_current, grav_props, e); + drift_gpart(gp, dt_drift_k, ti_old_gpart, ti_current, grav_props, e, + replication_list, c->loc); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ @@ -414,6 +545,14 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { c->grav.ti_old_part = ti_current; } +#ifdef WITH_LIGHTCONE + /* If we're at the top of the recursive hierarchy, clean up the refined + * replication lists */ + if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) + lightcone_array_free_replications(e->lightcone_array_properties, + replication_list); +#endif + /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_grav_drift | cell_flag_do_grav_sub_drift); } @@ -425,7 +564,8 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ -void cell_drift_spart(struct cell *c, const struct engine *e, int force) { +void cell_drift_spart(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); @@ -457,12 +597,20 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { cell_clear_flag(c, cell_flag_do_stars_drift | cell_flag_do_stars_sub_drift); /* Update the time of the last drift */ - c->stars.ti_old_part = ti_current; + cell_set_ti_old_spart(c, ti_current); return; } - /* Ok, we have some particles somewhere in the hierarchy to drift */ + /* Ok, we have some particles somewhere in the hierarchy to drift + + IMPORTANT: after this point we must not return without freeing the + replication lists if we allocated them. + */ + struct replication_list *replication_list = NULL; +#ifdef WITH_LIGHTCONE + replication_list = refine_replications(e, c, replication_list_in); +#endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_stars_sub_drift))) { @@ -473,7 +621,7 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { struct cell *cp = c->progeny[k]; /* Recurse */ - cell_drift_spart(cp, e, force); + cell_drift_spart(cp, e, force, replication_list); /* Update */ dx_max = max(dx_max, cp->stars.dx_max_part); @@ -512,7 +660,8 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { if (spart_is_inhibited(sp, e)) continue; /* Drift... */ - drift_spart(sp, dt_drift, ti_old_spart, ti_current); + drift_spart(sp, dt_drift, ti_old_spart, ti_current, e, replication_list, + c->loc); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ @@ -600,6 +749,14 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { c->stars.ti_old_part = ti_current; } +#ifdef WITH_LIGHTCONE + /* If we're at the top of the recursive hierarchy, clean up the refined + * replication lists */ + if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) + lightcone_array_free_replications(e->lightcone_array_properties, + replication_list); +#endif + /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_stars_drift | cell_flag_do_stars_sub_drift); } @@ -611,7 +768,8 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ -void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { +void cell_drift_bpart(struct cell *c, const struct engine *e, int force, + struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; @@ -644,12 +802,20 @@ void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { cell_clear_flag(c, cell_flag_do_bh_drift | cell_flag_do_bh_sub_drift); /* Update the time of the last drift */ - c->black_holes.ti_old_part = ti_current; + cell_set_ti_old_bpart(c, ti_current); return; } - /* Ok, we have some particles somewhere in the hierarchy to drift */ + /* Ok, we have some particles somewhere in the hierarchy to drift + + IMPORTANT: after this point we must not return without freeing the + replication lists if we allocated them. + */ + struct replication_list *replication_list = NULL; +#ifdef WITH_LIGHTCONE + replication_list = refine_replications(e, c, replication_list_in); +#endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_bh_sub_drift))) { @@ -660,7 +826,7 @@ void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { struct cell *cp = c->progeny[k]; /* Recurse */ - cell_drift_bpart(cp, e, force); + cell_drift_bpart(cp, e, force, replication_list); /* Update */ dx_max = max(dx_max, cp->black_holes.dx_max_part); @@ -689,7 +855,7 @@ void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { dt_drift = (ti_current - ti_old_bpart) * e->time_base; } - /* Loop over all the star particles in the cell */ + /* Loop over all the black hole particles in the cell */ const size_t nr_bparts = c->black_holes.count; for (size_t k = 0; k < nr_bparts; k++) { @@ -700,7 +866,8 @@ void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { if (bpart_is_inhibited(bp, e)) continue; /* Drift... */ - drift_bpart(bp, dt_drift, ti_old_bpart, ti_current); + drift_bpart(bp, dt_drift, ti_old_bpart, ti_current, e, replication_list, + c->loc); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ @@ -779,6 +946,14 @@ void cell_drift_bpart(struct cell *c, const struct engine *e, int force) { c->black_holes.ti_old_part = ti_current; } +#ifdef WITH_LIGHTCONE + /* If we're at the top of the recursive hierarchy, clean up the refined + * replication lists */ + if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) + lightcone_array_free_replications(e->lightcone_array_properties, + replication_list); +#endif + /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_bh_drift | cell_flag_do_bh_sub_drift); } @@ -865,7 +1040,7 @@ void cell_drift_sink(struct cell *c, const struct engine *e, int force) { dt_drift = (ti_current - ti_old_sink) * e->time_base; } - /* Loop over all the star particles in the cell */ + /* Loop over all the sink particles in the cell */ const size_t nr_sinks = c->sinks.count; for (size_t k = 0; k < nr_sinks; k++) { @@ -929,6 +1104,9 @@ void cell_drift_sink(struct cell *c, const struct engine *e, int force) { /* Maximal smoothing length */ cell_r_max = max(cell_r_max, sink->r_cut); + /* Mark the particle has not being swallowed */ + sink_mark_sink_as_not_swallowed(&sink->merger_data); + /* Get ready for a density calculation */ if (sink_is_active(sink, e)) { sink_init_sink(sink); diff --git a/src/cell_grav.h b/src/cell_grav.h index b0780c7d570694be0670960b2b3f26b6d62c4bfd..53f7a4178fc78626f034ccc8bf8c5dcb7eb6e07a 100644 --- a/src/cell_grav.h +++ b/src/cell_grav.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_CELL_GRAV_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "lock.h" diff --git a/src/cell_hydro.h b/src/cell_hydro.h index 26c507d28a56ada1c451cf48082a6b96302f0802..39db7bc21934e434583551de2a693da46fc7cacc 100644 --- a/src/cell_hydro.h +++ b/src/cell_hydro.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_CELL_HYDRO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "lock.h" @@ -100,42 +100,9 @@ struct cell_hydro { /*! Task for star formation */ struct task *star_formation; - /*! Task for star formation from sink particles */ - struct task *star_formation_sink; - - /*! Task for sink formation */ - struct task *sink_formation; - /*! Task for sorting the stars again after a SF event */ struct task *stars_resort; - /*! Radiative transfer ghost in task */ - struct task *rt_in; - - /*! Task for self/pair injection step of radiative transfer */ - struct link *rt_inject; - - /*! Radiative transfer ghost1 task (finishes up injection) */ - struct task *rt_ghost1; - - /*! Task for self/pair gradient step of radiative transfer */ - struct link *rt_gradient; - - /*! Radiative transfer ghost2 task */ - struct task *rt_ghost2; - - /*! Task for self/pair transport step of radiative transfer */ - struct link *rt_transport; - - /*! Radiative transfer transport out task */ - struct task *rt_transport_out; - - /*! Radiative transfer thermochemistry task */ - struct task *rt_tchem; - - /*! Radiative transfer ghost out task */ - struct task *rt_out; - /*! Last (integer) time the cell's part were drifted forward in time. */ integertime_t ti_old_part; diff --git a/src/cell_lock.c b/src/cell_lock.c index 29b7ada3f87491c05fd7904994a3eb925a6f5a8c..223c06a5e9408d10335078a809cf59a45fd88cbb 100644 --- a/src/cell_lock.c +++ b/src/cell_lock.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "cell.h" diff --git a/src/cell_pack.c b/src/cell_pack.c index c391b23c106d3f6b1943c8e2deead5d022fa57f5..cb96b58b40c9b4946b112dd8f01046e2472e981e 100644 --- a/src/cell_pack.c +++ b/src/cell_pack.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "cell.h" @@ -51,6 +51,8 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc, pc->stars.ti_end_min = c->stars.ti_end_min; pc->sinks.ti_end_min = c->sinks.ti_end_min; pc->black_holes.ti_end_min = c->black_holes.ti_end_min; + pc->rt.ti_rt_end_min = c->rt.ti_rt_end_min; + pc->rt.ti_rt_min_step_size = c->rt.ti_rt_min_step_size; pc->hydro.ti_old_part = c->hydro.ti_old_part; pc->grav.ti_old_part = c->grav.ti_old_part; @@ -208,6 +210,8 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c, c->stars.ti_end_min = pc->stars.ti_end_min; c->black_holes.ti_end_min = pc->black_holes.ti_end_min; c->sinks.ti_end_min = pc->sinks.ti_end_min; + c->rt.ti_rt_end_min = pc->rt.ti_rt_end_min; + c->rt.ti_rt_min_step_size = pc->rt.ti_rt_min_step_size; c->hydro.ti_old_part = pc->hydro.ti_old_part; c->grav.ti_old_part = pc->grav.ti_old_part; @@ -250,7 +254,7 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c, for (int k = 0; k < 8; k++) if (pc->progeny[k] >= 0) { struct cell *temp; - space_getcells(s, 1, &temp); + space_getcells(s, 1, &temp, 0); temp->hydro.count = 0; temp->grav.count = 0; temp->stars.count = 0; @@ -341,6 +345,8 @@ int cell_pack_end_step(const struct cell *c, struct pcell_step *pcells) { /* Pack this cell's data. */ pcells[0].hydro.ti_end_min = c->hydro.ti_end_min; pcells[0].hydro.dx_max_part = c->hydro.dx_max_part; + pcells[0].rt.ti_rt_end_min = c->rt.ti_rt_end_min; + pcells[0].rt.ti_rt_min_step_size = c->rt.ti_rt_min_step_size; pcells[0].grav.ti_end_min = c->grav.ti_end_min; @@ -383,6 +389,9 @@ int cell_unpack_end_step(struct cell *c, const struct pcell_step *pcells) { c->hydro.ti_end_min = pcells[0].hydro.ti_end_min; c->hydro.dx_max_part = pcells[0].hydro.dx_max_part; + c->rt.ti_rt_end_min = pcells[0].rt.ti_rt_end_min; + c->rt.ti_rt_min_step_size = pcells[0].rt.ti_rt_min_step_size; + c->grav.ti_end_min = pcells[0].grav.ti_end_min; c->stars.ti_end_min = pcells[0].stars.ti_end_min; diff --git a/src/cell_rt.h b/src/cell_rt.h new file mode 100644 index 0000000000000000000000000000000000000000..049c74d5c731442a951ff1da5baffaf53fb0c20a --- /dev/null +++ b/src/cell_rt.h @@ -0,0 +1,98 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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_CELL_RT_H +#define SWIFT_CELL_RT_H + +/* Config parameters. */ +#include <config.h> + +/* Local includes. */ +#include "timeline.h" + +/** + * @brief Radiative transfer related cell variables. + */ +struct cell_rt { + + /* If we are not using RT, compact as much of the unecessary variables + into an anonymous union to save memory in the cell structure. */ +#ifdef RT_NONE + union { +#endif + + /*! Radiative transfer ghost in task */ + struct task *rt_in; + + /*! Radiative transfer ghost1 task (finishes up injection) */ + struct task *rt_ghost1; + + /*! Task for self/pair gradient step of radiative transfer */ + struct link *rt_gradient; + + /*! Radiative transfer ghost2 task */ + struct task *rt_ghost2; + + /*! Task for self/pair transport step of radiative transfer */ + struct link *rt_transport; + + /*! Radiative transfer transport out task */ + struct task *rt_transport_out; + + /*! Radiative transfer thermochemistry task */ + struct task *rt_tchem; + + /*! Radiative transfer cell time advancement task */ + struct task *rt_advance_cell_time; + + /*! Sort a cell after a recv rt gradients */ + struct task *rt_sorts; + + /*! Collect the cell times from the super to the top level */ + struct task *rt_collect_times; + + /*! Radiative transfer ghost out task */ + struct task *rt_out; + + /*! Bit mask of sorts that need to be computed for this cell. + * Needed to be able to skip sorting undrifted cells. */ + uint16_t do_sort; + +#ifdef RT_NONE + }; +#endif + +#ifdef SWIFT_RT_DEBUG_CHECKS + /*! has rt_advance_cell_time run on this cell? */ + int advanced_time; +#endif + + /*! Minimum end of (integer) time step in this cell for RT tasks. */ + integertime_t ti_rt_end_min; + + /*! Maximum beginning of (integer) time step in this cell for RT tasks. */ + integertime_t ti_rt_beg_max; + + /*! Minimum (integer) time step size in this cell for RT tasks. */ + integertime_t ti_rt_min_step_size; + + /*! Number of #part updated for RT in this cell */ + int updated; +}; + +#endif /* SWIFT_CELL_RT_H */ diff --git a/src/cell_sinks.h b/src/cell_sinks.h index 373fddf7327bbdfddfcf539de164447afa08b11d..ac1d9d592030e83df9c8c48b8a0772e2981c201f 100644 --- a/src/cell_sinks.h +++ b/src/cell_sinks.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_CELL_SINKS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "lock.h" @@ -42,14 +42,14 @@ struct cell_sinks { /*! Pointer to the #sink data. */ struct sink *parts; - /*! Linked list of the tasks computing this cell's sink formation checks. */ - struct link *compute_formation; + /*! Linked list of the tasks computing this cell's sink swallow. */ + struct link *swallow; - /*! Linked list of the tasks computing this cell's sink accretion. */ - struct link *accretion; + /*! Linked list of the tasks computing this cell's sink do_gas_swallow. */ + struct link *do_gas_swallow; - /*! Linked list of the tasks computing this cell's sink merger. */ - struct link *merger; + /*! Linked list of the tasks computing this cell's sink do_sink_swallow. */ + struct link *do_sink_swallow; /*! The drift task for sinks */ struct task *drift; @@ -57,12 +57,22 @@ struct cell_sinks { /*! Implicit tasks marking the entry of the sink block of tasks */ struct task *sink_in; - /*! Implicit tasks marking the separation between merger and accretion */ - struct task *ghost; + /*! Implicit tasks marking the end of sink swallow */ + struct task *sink_ghost1; + + /*! Implicit tasks marking the separation between do_gas_swallow and + * do_sink_swallow */ + struct task *sink_ghost2; /*! Implicit tasks marking the exit of the sink block of tasks */ struct task *sink_out; + /*! Task for star formation from sink particles */ + struct task *star_formation_sink; + + /*! Task for sink formation */ + struct task *sink_formation; + /*! Last (integer) time the cell's sink were drifted forward in time. */ integertime_t ti_old_part; diff --git a/src/cell_split.c b/src/cell_split.c index 71bd71b85fa42cf54811c9e2f7422c3ddc9ff7f5..9e292428aa17859b093df8e7e8a40f083c614fe7 100644 --- a/src/cell_split.c +++ b/src/cell_split.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "cell.h" diff --git a/src/cell_stars.h b/src/cell_stars.h index b0ca6218a5fdb5b1b63d2ee13f99896e8e307136..e1030b7ad41c2c18360ea920986d5b27bae6cded 100644 --- a/src/cell_stars.h +++ b/src/cell_stars.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_CELL_STARS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "lock.h" diff --git a/src/cell_unskip.c b/src/cell_unskip.c index 2de766db46bd85df3ceb878f1630f0e66c5f3ba9..c4a52110a0caf6927e86f2efe771879462ca124b 100644 --- a/src/cell_unskip.c +++ b/src/cell_unskip.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "cell.h" @@ -29,7 +29,6 @@ #include "active.h" #include "engine.h" #include "feedback.h" -#include "rt_active.h" #include "space_getsid.h" extern int engine_star_resort_task_depth; @@ -129,10 +128,10 @@ void cell_activate_star_formation_sink_tasks(struct cell *c, #endif /* Have we already unskipped that task? */ - if (c->hydro.star_formation_sink->skip == 0) return; + if (c->sinks.star_formation_sink->skip == 0) return; /* Activate the star formation task */ - scheduler_activate(s, c->hydro.star_formation_sink); + scheduler_activate(s, c->sinks.star_formation_sink); /* Activate the star resort tasks at whatever level they are */ if (with_feedback) { @@ -155,10 +154,10 @@ void cell_activate_sink_formation_tasks(struct cell *c, struct scheduler *s) { #endif /* Have we already unskipped that task? */ - if (c->hydro.sink_formation->skip == 0) return; + if (c->sinks.sink_formation->skip == 0) return; /* Activate the star formation task */ - scheduler_activate(s, c->hydro.sink_formation); + scheduler_activate(s, c->sinks.sink_formation); } /** @@ -258,7 +257,8 @@ void cell_activate_cooling(struct cell *c, struct scheduler *s, */ void cell_activate_super_spart_drifts(struct cell *c, struct scheduler *s) { - /* Early abort? */ + /* Early abort? + * We can stop if there is no gas as none of it will turn into stars */ if (c->hydro.count == 0) return; if (c == c->hydro.super) { @@ -594,6 +594,7 @@ void cell_activate_hydro_sorts_up(struct cell *c, struct scheduler *s) { error("Trying to activate un-existing c->hydro.sorts"); #endif scheduler_activate(s, c->hydro.sorts); + cell_set_flag(c, cell_flag_skip_rt_sort); if (c->nodeID == engine_rank) cell_activate_drift_part(c, s); } else { for (struct cell *parent = c->parent; @@ -606,6 +607,7 @@ void cell_activate_hydro_sorts_up(struct cell *c, struct scheduler *s) { error("Trying to activate un-existing parents->hydro.sorts"); #endif scheduler_activate(s, parent->hydro.sorts); + cell_set_flag(parent, cell_flag_skip_rt_sort); if (parent->nodeID == engine_rank) cell_activate_drift_part(parent, s); break; } @@ -636,6 +638,95 @@ void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s) { } } +/** + * @brief Activate the sorts up a cell hierarchy. Activate drifts + * and hydro sorts on local cells, and rt_sorts on foreign cells. + */ +void cell_activate_rt_sorts_up(struct cell *c, struct scheduler *s) { + + cell_set_flag(c, cell_flag_rt_requests_sort); + + if (c == c->hydro.super) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank && c->hydro.sorts == NULL) + error("Trying to activate non-existing c->hydro.sorts"); + if (c->nodeID != engine_rank && c->rt.rt_sorts == NULL) + error("Trying to activate non-existing c->rt.rt_sorts"); +#endif + if (c->nodeID == engine_rank) { + cell_set_flag(c, cell_flag_skip_rt_sort); + scheduler_activate(s, c->hydro.sorts); + } else { + scheduler_activate(s, c->rt.rt_sorts); + } + } else { + + for (struct cell *parent = c->parent; + parent != NULL && !cell_get_flag(parent, cell_flag_do_rt_sub_sort); + parent = parent->parent) { + + /* Need a separate flag for RT sub sorts because RT sorts don't + * necessarily activate the hydro sorts tasks, yet the do_hydro_sub_sort + * flag is used as an early exit while climbing up the tree. */ + cell_set_flag(parent, cell_flag_do_rt_sub_sort); + cell_set_flag(parent, cell_flag_rt_requests_sort); + + if (parent == c->hydro.super) { + +#ifdef SWIFT_DEBUG_CHECKS + if (parent->nodeID == engine_rank && parent->hydro.sorts == NULL) + error("Trying to activate non-existing parents->hydro.sorts"); + if (parent->nodeID != engine_rank && parent->rt.rt_sorts == NULL) + error("Trying to activate non-existing parents->rt.rt_sorts"); +#endif + + if (parent->nodeID == engine_rank) { + /* Mark the progeny to skip the RT sort as well */ + cell_set_flag(c, cell_flag_skip_rt_sort); + scheduler_activate(s, parent->hydro.sorts); + } else { + scheduler_activate(s, parent->rt.rt_sorts); + } + break; + } + } + } +} + +/** + * @brief Activate the sorts on a given cell, if needed. Activate + * hydro sorts on local cells, and rt_sorts on foreign cells. + */ +void cell_activate_rt_sorts(struct cell *c, int sid, struct scheduler *s) { + /* Do we need to re-sort? */ + if (c->hydro.dx_max_sort > space_maxreldx * c->dmin) { + /* Climb up the tree to active the sorts in that direction */ + for (struct cell *finger = c; finger != NULL; finger = finger->parent) { + if (finger->hydro.requires_sorts) { + atomic_or(&finger->hydro.do_sort, finger->hydro.requires_sorts); + cell_activate_rt_sorts_up(finger, s); + } + finger->hydro.sorted = 0; + } + } + + /* Has this cell been sorted at all for the given sid? */ + if (!(c->hydro.sorted & (1 << sid)) || c->nodeID != engine_rank) { + atomic_or(&c->hydro.do_sort, (1 << sid)); + cell_activate_rt_sorts_up(c, s); + } +} + +/** + * @brief Mark cells up a hierarchy to not run RT sorts. + * */ +void cell_set_skip_rt_sort_flag_up(struct cell *c) { + + for (struct cell *finger = c; finger != NULL; finger = finger->parent) { + cell_set_flag(finger, cell_flag_skip_rt_sort); + } +} + /** * @brief Activate the sorts up a cell hierarchy. */ @@ -990,6 +1081,7 @@ void cell_activate_subcell_black_holes_tasks(struct cell *ci, struct cell *cj, /* We have reached the bottom of the tree: activate drift */ cell_activate_drift_bpart(ci, s); cell_activate_drift_part(ci, s); + if (with_timestep_sync) cell_activate_sync_part(ci, s); } } @@ -1431,121 +1523,89 @@ void cell_activate_subcell_external_grav_tasks(struct cell *ci, } /** - * @brief Traverse a sub-cell task and activate the stars drift tasks that are - * required by a Radiative Transfer (injection) task + * @brief Traverse a sub-cell task and activate the sort tasks that are + * required by a RT task * * @param ci The first #cell we recurse in. * @param cj The second #cell we recurse in. * @param s The task #scheduler. + * @param sub_cycle Are we in a subcycle or not? */ void cell_activate_subcell_rt_tasks(struct cell *ci, struct cell *cj, - struct scheduler *s) { + struct scheduler *s, const int sub_cycle) { + + /* Only do this during real time steps, not during subcycling. */ + if (sub_cycle) return; const struct engine *e = s->space->e; /* Store the current dx_max and h_max values. */ - ci->stars.dx_max_part_old = ci->stars.dx_max_part; - ci->stars.h_max_old = ci->stars.h_max; ci->hydro.dx_max_part_old = ci->hydro.dx_max_part; ci->hydro.h_max_old = ci->hydro.h_max; if (cj != NULL) { - cj->stars.dx_max_part_old = cj->stars.dx_max_part; - cj->stars.h_max_old = cj->stars.h_max; cj->hydro.dx_max_part_old = cj->hydro.dx_max_part; cj->hydro.h_max_old = cj->hydro.h_max; } + const int ci_active = cell_is_rt_active(ci, e); + const int cj_active = ((cj != NULL) && cell_is_rt_active(cj, e)); + /* Self interaction? */ if (cj == NULL) { - const int ci_active = rt_should_iact_cell(ci, e); - /* Do anything? */ - if (!ci_active || ci->hydro.count == 0) return; + if (ci->hydro.count == 0 || !ci_active) return; /* Recurse? */ - if (cell_can_recurse_in_self_stars_task(ci)) { + if (cell_can_recurse_in_self_hydro_task(ci)) { /* Loop over all progenies and pairs of progenies */ for (int j = 0; j < 8; j++) { if (ci->progeny[j] != NULL) { - cell_activate_subcell_rt_tasks(ci->progeny[j], NULL, s); + cell_activate_subcell_rt_tasks(ci->progeny[j], NULL, s, sub_cycle); for (int k = j + 1; k < 8; k++) if (ci->progeny[k] != NULL) - cell_activate_subcell_rt_tasks(ci->progeny[j], ci->progeny[k], s); + cell_activate_subcell_rt_tasks(ci->progeny[j], ci->progeny[k], s, + sub_cycle); } } - } else { - /* We have reached the bottom of the tree: activate drift */ - cell_activate_drift_spart(ci, s); - cell_activate_drift_part(ci, s); } } - /* Otherwise, pair interaction */ + /* Otherwise, pair interation */ else { + /* Should we even bother? */ + if (!ci_active && !cj_active) return; + if (ci->hydro.count == 0 || cj->hydro.count == 0) return; + /* Get the orientation of the pair. */ double shift[3]; const int sid = space_getsid(s->space, &ci, &cj, shift); - const int ci_active = - rt_should_iact_cell_pair(ci, cj, e) && (cj->hydro.count > 0); - const int cj_active = - rt_should_iact_cell_pair(cj, ci, e) && (ci->hydro.count > 0); - - /* Should we even bother? */ - if (!ci_active && !cj_active) return; - /* recurse? */ - if (cell_can_recurse_in_pair_stars_task(ci, cj) && - cell_can_recurse_in_pair_stars_task(cj, ci)) { - + if (cell_can_recurse_in_pair_hydro_task(ci) && + cell_can_recurse_in_pair_hydro_task(cj)) { const struct cell_split_pair *csp = &cell_split_pairs[sid]; for (int k = 0; k < csp->count; k++) { const int pid = csp->pairs[k].pid; const int pjd = csp->pairs[k].pjd; if (ci->progeny[pid] != NULL && cj->progeny[pjd] != NULL) - cell_activate_subcell_rt_tasks(ci->progeny[pid], cj->progeny[pjd], s); + cell_activate_subcell_rt_tasks(ci->progeny[pid], cj->progeny[pjd], s, + sub_cycle); } } /* Otherwise, activate the sorts and drifts. */ - else { - - if (ci_active) { + else if (ci_active || cj_active) { - /* We are going to interact this pair, so store some values. */ - atomic_or(&cj->hydro.requires_sorts, 1 << sid); - atomic_or(&ci->stars.requires_sorts, 1 << sid); - - cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; - ci->stars.dx_max_sort_old = ci->stars.dx_max_sort; - - /* Activate the drifts if the cells are local. */ - if (ci->nodeID == engine_rank) cell_activate_drift_spart(ci, s); - if (cj->nodeID == engine_rank) cell_activate_drift_part(cj, s); - - /* Do we need to sort the cells? */ - cell_activate_hydro_sorts(cj, sid, s); - cell_activate_stars_sorts(ci, sid, s); - } - - if (cj_active) { - - /* We are going to interact this pair, so store some values. */ - atomic_or(&cj->stars.requires_sorts, 1 << sid); - atomic_or(&ci->hydro.requires_sorts, 1 << sid); - - ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; - cj->stars.dx_max_sort_old = cj->stars.dx_max_sort; - - /* Activate the drifts if the cells are local. */ - if (ci->nodeID == engine_rank) cell_activate_drift_part(ci, s); - if (cj->nodeID == engine_rank) cell_activate_drift_spart(cj, s); + /* We are going to interact this pair, so store some values. */ + atomic_or(&ci->hydro.requires_sorts, 1 << sid); + atomic_or(&cj->hydro.requires_sorts, 1 << sid); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; - /* Do we need to sort the cells? */ - cell_activate_hydro_sorts(ci, sid, s); - cell_activate_stars_sorts(cj, sid, s); - } + /* Do we need to sort the cells? */ + cell_activate_rt_sorts(ci, sid, s); + cell_activate_rt_sorts(cj, sid, s); } } } @@ -1565,10 +1625,11 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { const int with_feedback = e->policy & engine_policy_feedback; const int with_timestep_limiter = (e->policy & engine_policy_timestep_limiter); + const int with_sinks = e->policy & engine_policy_sinks; #ifdef WITH_MPI const int with_star_formation = e->policy & engine_policy_star_formation; - if (e->policy & engine_policy_sinks) error("TODO"); + if (with_sinks) error("Cannot use sink tasks and MPI"); #endif int rebuild = 0; @@ -1653,6 +1714,26 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { #endif } } + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force interaction + * on this node as well. */ + else if (ci_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* NOTE: (yuyttenh, 09/2022) Since the particle communications send + * over whole particles currently, just activating the gradient + * send/recieve should be enough for now. The remote active + * particles are only needed for the sorts and the flux exchange on + * the node of the inactive cell, so sending over the xv and + * gradient suffices. If at any point the commutications change, we + * should probably also send over the rho separately. */ + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_xv); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rho); +#else + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_gradient); +#endif +#endif + } /* If the foreign cell is active, we want its particles for the limiter */ @@ -1682,6 +1763,24 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { #endif } } + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force interaction + * on both nodes */ + else if (cj_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* See NOTE on line 1542 */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_xv, ci_nodeID); + /* Drift the cell which will be sent; note that not all sent + particles will be drifted, only those that are needed. */ + cell_activate_drift_part(cj, s); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_send(s, cj->mpi.send, task_subtype_rho, ci_nodeID); +#else + scheduler_activate_send(s, cj->mpi.send, task_subtype_gradient, + ci_nodeID); +#endif +#endif + } /* If the local cell is active, send its particles for the limiting. */ if (cj_active && with_timestep_limiter) { @@ -1714,6 +1813,20 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { #endif } } + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force interaction + * on this node as well. */ + else if (cj_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* See NOTE on line 1542. */ + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_xv); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rho); +#else + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_gradient); +#endif +#endif + } /* If the foreign cell is active, we want its particles for the limiter */ @@ -1744,6 +1857,24 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { #endif } } + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force interaction + * on both nodes */ + else if (ci_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* See NOTE on line 1542. */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_xv, cj_nodeID); + /* Drift the cell which will be sent; note that not all sent + particles will be drifted, only those that are needed. */ + cell_activate_drift_part(ci, s); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_send(s, ci->mpi.send, task_subtype_rho, cj_nodeID); +#else + scheduler_activate_send(s, ci->mpi.send, task_subtype_gradient, + cj_nodeID); +#endif +#endif + } /* If the local cell is active, send its particles for the limiting. */ if (ci_active && with_timestep_limiter) { @@ -1769,7 +1900,8 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { } /* Unskip all the other task types. */ - if (c->nodeID == nodeID && cell_is_active_hydro(c, e)) { + int c_active = cell_is_active_hydro(c, e); + if (c->nodeID == nodeID && c_active) { for (struct link *l = c->hydro.gradient; l != NULL; l = l->next) { scheduler_activate(s, l->t); } @@ -1798,10 +1930,62 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { cell_activate_star_formation_tasks(c->top, s, with_feedback); cell_activate_super_spart_drifts(c->top, s); } - if (c->top->hydro.star_formation_sink != NULL) { + if (with_sinks && c->top->sinks.star_formation_sink != NULL) { cell_activate_star_formation_sink_tasks(c->top, s, with_feedback); } } + /* Additionally unskip force interactions between inactive local cell and + * active remote cell. (The cell unskip will only be called for active cells, + * so, we have to do this now, from the active remote cell). */ + else if (c->nodeID != nodeID && c_active) { +#if defined(MPI_SYMMETRIC_FORCE_INTERACTION) && defined(WITH_MPI) + for (struct link *l = c->hydro.force; l != NULL; l = l->next) { + struct task *t = l->t; + if (t->type != task_type_pair && t->type != task_type_sub_pair) continue; + + struct cell *ci = l->t->ci; + struct cell *cj = l->t->cj; + + const int ci_active = cell_is_active_hydro(ci, e); + const int cj_active = cell_is_active_hydro(cj, e); + const int ci_nodeID = ci->nodeID; + const int cj_nodeID = cj->nodeID; + if ((!ci_active && ci_nodeID == nodeID && cj_active && + cj_nodeID != nodeID) || + (!cj_active && cj_nodeID == nodeID && ci_active && + ci_nodeID != nodeID)) { + scheduler_activate(s, l->t); + + if (t->type == task_type_pair) { + /* Store some values. */ + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; + + /* Activate the drift tasks. */ + if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); + if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); + + /* Activate the limiter tasks. */ + if (ci_nodeID == nodeID && with_timestep_limiter) + cell_activate_limiter(ci, s); + if (cj_nodeID == nodeID && with_timestep_limiter) + cell_activate_limiter(cj, s); + + /* Check the sorts and activate them if needed. */ + cell_activate_hydro_sorts(ci, t->flags, s); + cell_activate_hydro_sorts(cj, t->flags, s); + } + + /* Store current values of dx_max and h_max. */ + else if (t->type == task_type_sub_pair) { + cell_activate_subcell_hydro_tasks(ci, cj, s, with_timestep_limiter); + } + } + } +#endif + } return rebuild; } @@ -2408,6 +2592,7 @@ int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s) { if (t->type == task_type_self) { cell_activate_drift_part(ci, s); cell_activate_drift_bpart(ci, s); + if (with_timestep_sync) cell_activate_sync_part(ci, s); } /* Activate the drifts */ @@ -2672,7 +2857,7 @@ int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s) { } /* Un-skip the star formation tasks involved with this cell. */ - for (struct link *l = c->sinks.compute_formation; l != NULL; l = l->next) { + for (struct link *l = c->sinks.swallow; l != NULL; l = l->next) { struct task *t = l->t; struct cell *ci = t->ci; struct cell *cj = t->cj; @@ -2776,7 +2961,7 @@ int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s) { } } - for (struct link *l = c->sinks.merger; l != NULL; l = l->next) { + for (struct link *l = c->sinks.do_sink_swallow; l != NULL; l = l->next) { struct task *t = l->t; struct cell *ci = t->ci; struct cell *cj = t->cj; @@ -2801,7 +2986,7 @@ int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s) { } } - for (struct link *l = c->sinks.accretion; l != NULL; l = l->next) { + for (struct link *l = c->sinks.do_gas_swallow; l != NULL; l = l->next) { struct task *t = l->t; struct cell *ci = t->ci; struct cell *cj = t->cj; @@ -2840,7 +3025,10 @@ int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s) { (cell_is_active_sinks(c, e) || cell_is_active_hydro(c, e))) { if (c->sinks.sink_in != NULL) scheduler_activate(s, c->sinks.sink_in); - if (c->sinks.ghost != NULL) scheduler_activate(s, c->sinks.ghost); + if (c->sinks.sink_ghost1 != NULL) + scheduler_activate(s, c->sinks.sink_ghost1); + if (c->sinks.sink_ghost2 != NULL) + scheduler_activate(s, c->sinks.sink_ghost2); if (c->sinks.sink_out != NULL) scheduler_activate(s, c->sinks.sink_out); if (c->kick1 != NULL) scheduler_activate(s, c->kick1); if (c->kick2 != NULL) scheduler_activate(s, c->kick2); @@ -2861,26 +3049,26 @@ int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s) { * * @param c the #cell. * @param s the #scheduler. + * @param sub_cycle 1 if this is unskipping during an RT subcycle, 0 if normal + * unskip * * @return 1 If the space needs rebuilding. 0 otherwise. */ -int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) { +int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s, + const int sub_cycle) { - /* Note: we only get this far if engine_policy_rt is flagged. */ + /* Do we have work here? */ + if (c->hydro.count == 0) return 0; struct engine *e = s->space->e; const int nodeID = e->nodeID; int rebuild = 0; /* TODO: implement rebuild conditions? */ - if (c->stars.drift != NULL) { - if (rt_should_iact_cell(c, e)) { - cell_activate_drift_part(c, s); - cell_activate_drift_spart(c, s); - } - } + /* Note: we only get this far if engine_policy_rt is flagged. */ + if (!(e->policy & engine_policy_rt)) error("Unskipping RT tasks without RT"); + + for (struct link *l = c->rt.rt_gradient; l != NULL; l = l->next) { - /* Now unskip all RT specific interaction tasks */ - for (struct link *l = c->hydro.rt_inject; l != NULL; l = l->next) { struct task *t = l->t; struct cell *ci = t->ci; struct cell *cj = t->cj; @@ -2891,207 +3079,254 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) { const int ci_nodeID = nodeID; const int cj_nodeID = nodeID; #endif + const int ci_active = cell_is_rt_active(ci, e); + const int cj_active = (cj != NULL) && cell_is_rt_active(cj, e); - /* Activate the drifts */ - if (t->type == task_type_self) { - if (rt_should_iact_cell(ci, e)) { - cell_activate_drift_part(ci, s); - cell_activate_drift_spart(ci, s); - scheduler_activate(s, t); - } - } - - else if (t->type == task_type_pair) { - - const int ci_active = rt_should_iact_cell_pair(ci, cj, e); - const int cj_active = (cj != NULL) && rt_should_iact_cell_pair(cj, ci, e); - - /* Only activate tasks that involve a local active cell. */ - if ((ci_active || cj_active) && - (ci_nodeID == nodeID || cj_nodeID == nodeID)) { - scheduler_activate(s, t); - - /* Do ci */ - if (ci_active) { - /* stars for ci */ - atomic_or(&ci->stars.requires_sorts, 1 << t->flags); - ci->stars.dx_max_sort_old = ci->stars.dx_max_sort; + /* Only activate tasks that involve a local active cell. */ + if ((ci_active && ci_nodeID == nodeID) || + (cj_active && cj_nodeID == nodeID)) { + scheduler_activate(s, t); - /* hydro for cj */ + if (!sub_cycle) { + /* Activate sorts only during main/normal steps. */ + if (t->type == task_type_pair) { + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; - /* Activate the drift tasks. */ - if (ci_nodeID == nodeID) cell_activate_drift_spart(ci, s); - if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); - /* Check the sorts and activate them if needed. */ - cell_activate_stars_sorts(ci, t->flags, s); - cell_activate_hydro_sorts(cj, t->flags, s); + cell_activate_rt_sorts(ci, t->flags, s); + cell_activate_rt_sorts(cj, t->flags, s); } - /* Do cj */ - if (cj_active) { - /* hydro for ci */ - atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); - ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; - - /* stars for cj */ - atomic_or(&cj->stars.requires_sorts, 1 << t->flags); - cj->stars.dx_max_sort_old = cj->stars.dx_max_sort; - - /* Activate the drift tasks. */ - if (cj_nodeID == nodeID) cell_activate_drift_spart(cj, s); - if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); + /* Store current values of dx_max and h_max. */ + else if (t->type == task_type_sub_self) { + cell_activate_subcell_rt_tasks(ci, NULL, s, sub_cycle); + } - /* Check the sorts and activate them if needed. */ - cell_activate_hydro_sorts(ci, t->flags, s); - cell_activate_stars_sorts(cj, t->flags, s); + /* Store current values of dx_max and h_max. */ + else if (t->type == task_type_sub_pair) { + cell_activate_subcell_rt_tasks(ci, cj, s, sub_cycle); } } } - else if (t->type == task_type_sub_self) { - scheduler_activate(s, t); - cell_activate_subcell_rt_tasks(ci, NULL, s); - } - - else if (t->type == task_type_sub_pair) { - scheduler_activate(s, t); - cell_activate_subcell_rt_tasks(ci, cj, s); - } - /* Only interested in pair interactions as of here. */ if (t->type == task_type_pair || t->type == task_type_sub_pair) { - /* Check whether there was too much particle motion, i.e. the */ - /* cell neighbour conditions were violated. */ - if (cell_need_rebuild_for_stars_pair(ci, cj)) rebuild = 1; - if (cell_need_rebuild_for_stars_pair(cj, ci)) rebuild = 1; - /* Activate rt_in for each cell that is part of - * a pair/sub_pair task as to not miss any dependencies */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_in); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_in); +#ifdef WITH_MPI - /* For the same reason, catch the dependencies with the RT ghost1 */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_ghost1); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_ghost1); - } - } + /* Activate the send/recv tasks. */ + if (ci_nodeID != nodeID) { + /* If the local cell is active, receive data from the foreign cell. */ + if (cj_active) { + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_gradient); + if (sub_cycle) { + /* If we're in a sub-cycle, then there should be no sorts. But since + * hydro sorts won't be active then, the RT sorts would run. Make + * sure the cells are also marked to skip the RT sorts, otherwise + * the 'sorted' flags will be wrongly set after a recv rt_gradient. + * The recv tasks might also run on a higher level than the current + * cell, so walk all the way up. */ + cell_set_skip_rt_sort_flag_up(ci); + } - if (c->nodeID == nodeID) { + /* We only need updates later on if the other cell is active too */ + if (ci_active) { + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_transport); + } + } else if (ci_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force interaction + * on this node as well. + * The gradient recv is only necessary in normal steps in case we need + * to sort, not during sub-cycles. */ + if (!sub_cycle) + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_gradient); + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_transport); + if (sub_cycle) cell_set_skip_rt_sort_flag_up(ci); +#endif + } - for (struct link *l = c->hydro.rt_gradient; l != NULL; l = l->next) { - /* TODO: all the MPI checks */ - /* I assume that all hydro related subcell unskipping/activation necessary - * here is being done in the hydro part of cell_unskip */ + /* Is the foreign cell active and will need stuff from us? */ + if (ci_active) { - struct task *t = l->t; - struct cell *ci = t->ci; - struct cell *cj = t->cj; -#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; + scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_gradient, + ci_nodeID); + + if (cj_active) { + scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_transport, + ci_nodeID); + } + } else if (cj_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force interaction + * on both nodes. + * The gradient send is only necessary in normal steps in case we need + * to sort, not during sub-cycles. */ + if (!sub_cycle) + scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_gradient, + ci_nodeID); + scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_transport, + ci_nodeID); #endif + } - const int ci_active = cell_is_active_hydro(ci, e); - const int cj_active = (cj != NULL) && cell_is_active_hydro(cj, e); + } else if (cj_nodeID != nodeID) { - if (t->type == task_type_self || t->type == task_type_sub_self) { - if (ci_active) scheduler_activate(s, t); - } + /* If the local cell is active, receive data from the foreign cell. */ + if (ci_active) { + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_gradient); + if (sub_cycle) { + /* No RT sorts during sub-cycling */ + cell_set_skip_rt_sort_flag_up(cj); + } + + /* We only need updates later on if the other cell is active too */ + if (cj_active) { + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_transport); + } + } else if (cj_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force interaction + * on this node as well. + * The gradient recv is only necessary in normal steps in case we need + * to sort, not during sub-cycles. */ + if (!sub_cycle) + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_gradient); + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_transport); + if (sub_cycle) cell_set_skip_rt_sort_flag_up(cj); +#endif + } - else if (t->type == task_type_pair || t->type == task_type_sub_pair) { + /* Is the foreign cell active and will need stuff from us? */ + if (cj_active) { - /* Only activate tasks that involve a local active cell. */ - if ((ci_active || cj_active) && - (ci_nodeID == nodeID || cj_nodeID == nodeID)) { - scheduler_activate(s, t); + scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_gradient, + cj_nodeID); - /* Activate rt_ghost1 dependencies for each cell that is part of - * a pair/sub_pair task as to not miss any dependencies */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_ghost1); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_ghost1); + if (ci_active) { + scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_transport, + cj_nodeID); + } + } else if (ci_active) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force interaction + * on both nodes + * The gradient send is only necessary in normal steps in case we need + * to sort, not during sub-cycles. */ + if (!sub_cycle) + scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_gradient, + cj_nodeID); + scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_transport, + cj_nodeID); +#endif } } +#endif } + } - for (struct link *l = c->hydro.rt_transport; l != NULL; l = l->next) { - /* TODO: all the MPI checks */ - /* I assume that all hydro related subcell unskipping/activation necessary - * here is being done in the hydro part of cell_unskip */ + for (struct link *l = c->rt.rt_transport; l != NULL; l = l->next) { - struct task *t = l->t; - struct cell *ci = t->ci; - struct cell *cj = t->cj; + struct task *t = l->t; + struct cell *ci = t->ci; + struct cell *cj = t->cj; #ifdef WITH_MPI - const int ci_nodeID = ci->nodeID; - const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1; + 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; + const int ci_nodeID = nodeID; + const int cj_nodeID = nodeID; #endif - const int ci_active = cell_is_active_hydro(ci, e); - const int cj_active = (cj != NULL) && cell_is_active_hydro(cj, e); - - if (t->type == task_type_self || t->type == task_type_sub_self) { - if (ci_active) scheduler_activate(s, t); - } + const int ci_active = cell_is_rt_active(ci, e); + const int cj_active = ((cj != NULL) && cell_is_rt_active(cj, e)); - else if (t->type == task_type_pair || t->type == task_type_sub_pair) { + if ((ci_active && ci_nodeID == nodeID) || + (cj_active && cj_nodeID == nodeID)) { + scheduler_activate(s, t); - /* Only activate tasks that involve a local active cell. */ - if ((ci_active || cj_active) && - (ci_nodeID == nodeID || cj_nodeID == nodeID)) { - scheduler_activate(s, t); + if (t->type == task_type_pair || t->type == task_type_sub_pair) { - /* Activate transport_out for each cell that is part of - * a pair/sub_pair task as to not miss any dependencies */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_transport_out); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_transport_out); - } + /* Activate transport_out for each cell that is part of + * a pair/sub_pair task as to not miss any dependencies */ + if (ci_nodeID == nodeID) + scheduler_activate(s, ci->hydro.super->rt.rt_transport_out); + if (cj_nodeID == nodeID) + scheduler_activate(s, cj->hydro.super->rt.rt_transport_out); } } + } - /* Unskip all the other task types */ - - if (rt_should_do_unskip_cell(c, e)) { - /* If we don't have any pair/sub_pair tasks for this cell, then we haven't - * unskipped the ghosts yet. Do this now. */ - - /* You need to pay attention to stars as well when unskipping rt_in - * to gather dependencies from the feedback loop. */ - if (c->hydro.rt_in != NULL) scheduler_activate(s, c->hydro.rt_in); + /* Unskip all the other task types */ + if (cell_is_rt_active(c, e)) { + if (c->nodeID == nodeID) { + if (c->rt.rt_in != NULL) scheduler_activate(s, c->rt.rt_in); + if (c->rt.rt_ghost1 != NULL) scheduler_activate(s, c->rt.rt_ghost1); + if (c->rt.rt_ghost2 != NULL) scheduler_activate(s, c->rt.rt_ghost2); + if (c->rt.rt_transport_out != NULL) + scheduler_activate(s, c->rt.rt_transport_out); + if (c->rt.rt_tchem != NULL) scheduler_activate(s, c->rt.rt_tchem); + if (c->rt.rt_out != NULL) scheduler_activate(s, c->rt.rt_out); + } else { +#if defined(MPI_SYMMETRIC_FORCE_INTERACTION) && defined(WITH_MPI) + /* Additionally unskip force interactions between inactive local cell and + * active remote cell. (The cell unskip will only be called for active + * cells, so, we have to do this now, from the active remote cell). */ + for (struct link *l = c->rt.rt_transport; l != NULL; l = l->next) { + struct task *t = l->t; + if (t->type != task_type_pair && t->type != task_type_sub_pair) + continue; + + struct cell *ci = l->t->ci; + struct cell *cj = l->t->cj; + + const int ci_active = cell_is_rt_active(ci, e); + const int cj_active = cell_is_rt_active(cj, e); + const int ci_nodeID = ci->nodeID; + const int cj_nodeID = cj->nodeID; + if ((!ci_active && ci_nodeID == nodeID && cj_active && + cj_nodeID != nodeID) || + (!cj_active && cj_nodeID == nodeID && ci_active && + ci_nodeID != nodeID)) { + scheduler_activate(s, l->t); + + if (!sub_cycle) { + /* Activate sorts only during main/normal steps. */ + if (t->type == task_type_pair) { + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; + + /* Check the sorts and activate them if needed. */ + cell_activate_rt_sorts(ci, t->flags, s); + cell_activate_rt_sorts(cj, t->flags, s); + } - /* Also activate the rt_ghost1 task even if you don't have stars in - * this cell to gather dependencies properly. Otherwise, dependency - * issues arise when the timestep task starts changing what's active - * and what's not active */ - if (c->hydro.rt_ghost1 != NULL) scheduler_activate(s, c->hydro.rt_ghost1); + /* Store current values of dx_max and h_max. */ + else if (t->type == task_type_sub_pair) { + cell_activate_subcell_rt_tasks(ci, cj, s, sub_cycle); + } + } + } + } +#endif } - if (cell_is_active_hydro(c, e)) { - if (c->hydro.rt_ghost2 != NULL) scheduler_activate(s, c->hydro.rt_ghost2); - if (c->hydro.rt_transport_out != NULL) - scheduler_activate(s, c->hydro.rt_transport_out); - if (c->hydro.rt_tchem != NULL) scheduler_activate(s, c->hydro.rt_tchem); - if (c->hydro.rt_out != NULL) scheduler_activate(s, c->hydro.rt_out); - } - } else { - /* RT doesn't run with MPI as of yet */ - error("Unskipping RT tasks for non-local cells?"); + /* The rt_advance_cell_time tasks also run on foreign cells */ + if (c->super != NULL && c->super->rt.rt_advance_cell_time != NULL) + scheduler_activate(s, c->super->rt.rt_advance_cell_time); + /* The rt_collect_times tasks replace the timestep_collect tasks + * during sub-cycles, so we only activate it when sub-cycling. */ + if (c->rt.rt_collect_times != NULL && sub_cycle) + scheduler_activate(s, c->rt.rt_collect_times); } return rebuild; diff --git a/src/chemistry.c b/src/chemistry.c index 4afa199258f56d4fc01d67c9335e87a86ead09bc..fb10f4be7e781d24f1c8aee4b363829e94a156ee 100644 --- a/src/chemistry.c +++ b/src/chemistry.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "chemistry.h" diff --git a/src/chemistry.h b/src/chemistry.h index 45db33d1f746294af017c123d7cbf02ac818b995..ba303b4d4d90f6d6822a68285ac84eeb3bb9f96b 100644 --- a/src/chemistry.h +++ b/src/chemistry.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -25,7 +25,9 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "chemistry_struct.h" /* Import the right chemistry definition */ diff --git a/src/chemistry/EAGLE/chemistry.h b/src/chemistry/EAGLE/chemistry.h index f664d489073c752e7e4905c73405ee5db6d6a358..f8d0815e78018955b601ccb8a6ed796c9318a0ec 100644 --- a/src/chemistry/EAGLE/chemistry.h +++ b/src/chemistry/EAGLE/chemistry.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/chemistry/EAGLE/chemistry_debug.h b/src/chemistry/EAGLE/chemistry_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..255bacbafd7b53d28b1c212f4ee03c23b511458d --- /dev/null +++ b/src/chemistry/EAGLE/chemistry_debug.h @@ -0,0 +1,56 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_CHEMISTRY_EAGLE_DEBUG_H +#define SWIFT_CHEMISTRY_EAGLE_DEBUG_H + +__attribute__((always_inline)) INLINE static void chemistry_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld chemistry_part_data:", p->id); + for (int i = 0; i < chemistry_element_count; i++) { + warning("[PID%lld metal_mass_fraction[%i]: %.3e", p->id, i, + p->chemistry_data.metal_mass_fraction[i]); + } + warning("[PID%lld metal_mass_fraction_total: %.3e", p->id, + p->chemistry_data.metal_mass_fraction_total); + for (int i = 0; i < chemistry_element_count; i++) { + warning("[PID%lld smoothed_metal_mass_fraction[%i]: %.3e", p->id, i, + p->chemistry_data.smoothed_metal_mass_fraction[i]); + } + warning("[PID%lld metal_mass_fraction_total: %.3e", p->id, + p->chemistry_data.smoothed_metal_mass_fraction_total); + warning( + "[PID%lld mass_from_SNIa: %.3e, metal_mass_fraction_from_SNIa: %.3e, " + "mass_from_AGB: %.3e, " + "metal_mass_fraction_from_AGB: %.3e, " + "mass_from_SNII: %.3e, " + "metal_mass_fraction_from_SNII: %.3e, " + "iron_mass_fraction_from_SNIa: %.3e, " + "smoothed_iron_mass_fraction_from_SNIa: %.3e", + p->id, p->chemistry_data.mass_from_SNIa, + p->chemistry_data.metal_mass_fraction_from_SNIa, + p->chemistry_data.mass_from_AGB, + p->chemistry_data.metal_mass_fraction_from_AGB, + p->chemistry_data.mass_from_SNII, + p->chemistry_data.metal_mass_fraction_from_SNII, + p->chemistry_data.iron_mass_fraction_from_SNIa, + p->chemistry_data.smoothed_iron_mass_fraction_from_SNIa); +} + +#endif /* SWIFT_CHEMISTRY_EAGLE_DEBUG_H */ diff --git a/src/chemistry/EAGLE/chemistry_iact.h b/src/chemistry/EAGLE/chemistry_iact.h index 3c91f203d7c7fccdf5b98faf53ca032d91fc37f6..290f399a5e73da9141263476880f77878fba3f61 100644 --- a/src/chemistry/EAGLE/chemistry_iact.h +++ b/src/chemistry/EAGLE/chemistry_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/chemistry/EAGLE/chemistry_io.h b/src/chemistry/EAGLE/chemistry_io.h index 75aae6677e90467b9e5550d7457de78b8269be1d..6f61e41dc80b94525887f859afca809956661b82 100644 --- a/src/chemistry/EAGLE/chemistry_io.h +++ b/src/chemistry/EAGLE/chemistry_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/chemistry/EAGLE/chemistry_struct.h b/src/chemistry/EAGLE/chemistry_struct.h index 491bc444d7f201f9a9a389c6a04d2ba04e927914..eedcfb8f977c693d9001948136c7a5fc81b7ef95 100644 --- a/src/chemistry/EAGLE/chemistry_struct.h +++ b/src/chemistry/EAGLE/chemistry_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -130,4 +130,11 @@ struct chemistry_bpart_data { float smoothed_formation_metallicity; }; +/** + * @brief Chemical abundances traced by the #sink in the EAGLE model. + * + * Nothing here. + */ +struct chemistry_sink_data {}; + #endif /* SWIFT_CHEMISTRY_STRUCT_EAGLE_H */ diff --git a/src/chemistry/GEAR/chemistry.h b/src/chemistry/GEAR/chemistry.h index 588741c4cc3c33593a7fe56787c11efe8b2f2e96..6267ffb514ea68984f9669e4c93a8e44a3bf5f5a 100644 --- a/src/chemistry/GEAR/chemistry.h +++ b/src/chemistry/GEAR/chemistry.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -140,6 +140,93 @@ static INLINE void chemistry_read_solar_abundances( #endif } +/** + * @brief Get the name of the element i. + * + * @param sm The #stellar_model. + * @param i The element indice. + */ +static INLINE const char* chemistry_get_element_name( + const struct chemistry_global_data* data, int i) { + + return data->elements_name + i * GEAR_LABELS_SIZE; +} + +/** + * @brief Get the index of the element . + * + * @param sm The #stellar_model. + * @param element_name The element name. + */ +static INLINE int chemistry_get_element_index( + const struct chemistry_global_data* data, const char* element_name) { + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + if (strcmp(chemistry_get_element_name(data, i), element_name) == 0) + return i; + } + error("Chemical element %s not found !", element_name); + + return -1; +} + +/** + * @brief Read the name of all the elements present in the tables. + * It is nearly a copy/paste of stellar_evolution_read_elements + * + * @param parameter_file The parsed parameter file. + * @param data The properties to initialise. + */ +static INLINE void chemistry_read_elements(struct swift_params* params, + struct chemistry_global_data* data) { + + /* Read the elements from the parameter file. */ + int nval = -1; + char** elements; + parser_get_param_string_array(params, "GEARFeedback:elements", &nval, + &elements); + + /* Check that we have the correct number of elements. */ + if (nval != GEAR_CHEMISTRY_ELEMENT_COUNT - 1) { + error( + "You need to provide %i elements but found %i. " + "If you wish to provide a different number of elements, " + "you need to compile with --with-chemistry=GEAR_N where N " + "is the number of elements + 1.", + GEAR_CHEMISTRY_ELEMENT_COUNT, nval); + } + + /* Copy the elements into the stellar model. */ + for (int i = 0; i < nval; i++) { + if (strlen(elements[i]) >= GEAR_LABELS_SIZE) { + error("Element name '%s' too long", elements[i]); + } + strcpy(data->elements_name + i * GEAR_LABELS_SIZE, elements[i]); + } + + /* Cleanup. */ + parser_free_param_string_array(nval, elements); + + /* Add the metals to the end. */ + strcpy(data->elements_name + + (GEAR_CHEMISTRY_ELEMENT_COUNT - 1) * GEAR_LABELS_SIZE, + "Metals"); + + /* Check the elements */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + for (int j = i + 1; j < GEAR_CHEMISTRY_ELEMENT_COUNT; j++) { + const char* el_i = chemistry_get_element_name(data, i); + const char* el_j = chemistry_get_element_name(data, j); + if (strcmp(el_i, el_j) == 0) { + error("You need to provide each element only once (%s).", el_i); + } + } + } + + /* Check that iron is at index 0 */ + if (chemistry_get_element_index(data, "Fe") != 0) + error("Element Fe must be at index 0 !"); +} + /** * @brief Initialises the chemistry properties. * @@ -178,6 +265,7 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, if (scale_metallicity) { /* Read the solar abundances */ chemistry_read_solar_abundances(parameter_file, data); + chemistry_read_elements(parameter_file, data); /* Scale the solar abundances */ chemistry_scale_initial_metallicities(parameter_file, data); @@ -186,6 +274,7 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, #ifdef FEEDBACK_GEAR else { chemistry_read_solar_abundances(parameter_file, data); + chemistry_read_elements(parameter_file, data); } #endif } @@ -342,34 +431,31 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_spart( } /** - * @brief Initialise the chemistry properties of a black hole with - * the chemistry properties of the gas it is born from. + * @brief Add the chemistry data of a sink particle to a sink. * - * Nothing to do here. - * - * @param bp_data The black hole data to initialise. - * @param p_data The gas data to use. + * @param si_data The black hole data to add to. + * @param sj_data The gas data to use. * @param gas_mass The mass of the gas particle. */ -__attribute__((always_inline)) INLINE static void chemistry_bpart_from_part( - struct chemistry_bpart_data* bp_data, - const struct chemistry_part_data* p_data, const double gas_mass) { - error("Loic: to be implemented"); +__attribute__((always_inline)) INLINE static void chemistry_add_sink_to_sink( + struct chemistry_sink_data* si_data, + const struct chemistry_sink_data* sj_data) { + + // To be implemented. } /** - * @brief Add the chemistry data of a gas particle to a black hole. + * @brief Add the chemistry data of a gas particle to a sink. * - * Nothing to do here. - * - * @param bp_data The black hole data to add to. + * @param sp_data The sink data to add to. * @param p_data The gas data to use. * @param gas_mass The mass of the gas particle. */ -__attribute__((always_inline)) INLINE static void chemistry_add_part_to_bpart( - struct chemistry_bpart_data* bp_data, +__attribute__((always_inline)) INLINE static void chemistry_add_part_to_sink( + struct chemistry_sink_data* sp_data, const struct chemistry_part_data* p_data, const double gas_mass) { - error("Loic: to be implemented"); + + // To be implemented. } /** @@ -464,6 +550,20 @@ chemistry_get_star_total_metal_mass_fraction_for_feedback( .metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT - 1]; } +/** + * @brief Returns the total iron mass fraction of the + * star particle to be used in feedback/enrichment related routines. + * We assume iron to be stored at index 0. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static double +chemistry_get_star_total_iron_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return sp->chemistry_data.metal_mass_fraction[0]; +} + /** * @brief Returns the abundances (metal mass fraction) of the * star particle to be used in feedback/enrichment related routines. diff --git a/src/chemistry/GEAR/chemistry_csds.h b/src/chemistry/GEAR/chemistry_csds.h index f1552fa06142ce78438bc31ee3dc27f3c780aada..5602dced0c33b5fd80d03f668537ffc118efd63d 100644 --- a/src/chemistry/GEAR/chemistry_csds.h +++ b/src/chemistry/GEAR/chemistry_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/chemistry/GEAR/chemistry_debug.h b/src/chemistry/GEAR/chemistry_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..fca7741b492a88d7cef31e6e66404238cb349458 --- /dev/null +++ b/src/chemistry/GEAR/chemistry_debug.h @@ -0,0 +1,36 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_CHEMISTRY_GEAR_DEBUG_H +#define SWIFT_CHEMISTRY_GEAR_DEBUG_H + +__attribute__((always_inline)) INLINE static void chemistry_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld] chemistry_part_data:", p->id); + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + warning("[PID%lld] metal_mass[%i]: %.3e", p->id, i, + p->chemistry_data.metal_mass[i]); + } + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + warning("[PID%lld] smoothed_metal_mass_fraction[%i]: %.3e", p->id, i, + p->chemistry_data.smoothed_metal_mass_fraction[i]); + } +} + +#endif /* SWIFT_CHEMISTRY_GEAR_DEBUG_H */ diff --git a/src/chemistry/GEAR/chemistry_iact.h b/src/chemistry/GEAR/chemistry_iact.h index f92cdba6faeab92258e8a8481088a238e7cc7697..40a31cd125e48f5c99cc7265f9cb5c84c75c8dc8 100644 --- a/src/chemistry/GEAR/chemistry_iact.h +++ b/src/chemistry/GEAR/chemistry_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 diff --git a/src/chemistry/GEAR/chemistry_io.h b/src/chemistry/GEAR/chemistry_io.h index 30dc89f8e2ba6ac7f3b7832bac73e2bdc1e9f12e..0cba098a7387a125ca3ffba16d4e12f44940640b 100644 --- a/src/chemistry/GEAR/chemistry_io.h +++ b/src/chemistry/GEAR/chemistry_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/chemistry/GEAR/chemistry_struct.h b/src/chemistry/GEAR/chemistry_struct.h index 58db88afb9216f3ec592473c4979ef75cec80d22..3b26c112192b29100760aeec5062359bf3dba081 100644 --- a/src/chemistry/GEAR/chemistry_struct.h +++ b/src/chemistry/GEAR/chemistry_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -19,6 +19,8 @@ #ifndef SWIFT_CHEMISTRY_STRUCT_GEAR_H #define SWIFT_CHEMISTRY_STRUCT_GEAR_H +#define GEAR_LABELS_SIZE 10 // redumndant with the one defined in + /** * @brief Global chemical abundance information. */ @@ -29,6 +31,9 @@ struct chemistry_global_data { /* Solar mass abundances read from the chemistry table */ float solar_abundances[GEAR_CHEMISTRY_ELEMENT_COUNT]; + + /*! Name of the different elements */ + char elements_name[GEAR_CHEMISTRY_ELEMENT_COUNT * GEAR_LABELS_SIZE]; }; /** @@ -57,4 +62,9 @@ struct chemistry_spart_data { */ struct chemistry_bpart_data {}; +/** + * @brief Chemical abundances traced by the #sink in the GEAR model. + */ +struct chemistry_sink_data {}; + #endif /* SWIFT_CHEMISTRY_STRUCT_GEAR_H */ diff --git a/src/chemistry/GEAR_DIFFUSION/chemistry_debug.h b/src/chemistry/GEAR_DIFFUSION/chemistry_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..dee9dccbb75eccaab88d0e2f330cf4dbe99989a7 --- /dev/null +++ b/src/chemistry/GEAR_DIFFUSION/chemistry_debug.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_CHEMISTRY_GEAR_DIFFUSION_DEBUG_H +#define SWIFT_CHEMISTRY_GEAR_DIFFUSION_DEBUG_H + +__attribute__((always_inline)) INLINE static void chemistry_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld] chemistry_part_data:", p->id); + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + warning("[PID%lld] metal_mass[%i]: %.3e", p->id, i, + p->chemistry_data.metal_mass[i]); + } + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + warning("[PID%lld] smoothed_metal_mass_fraction[%i]: %.3e", p->id, i, + p->chemistry_data.smoothed_metal_mass_fraction[i]); + } + warning("[PID%lld] diff_coef: %.3e", p->id, p->chemistry_data.diff_coef); + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + warning("[PID%lld] metal_mass_dt[%i]: %.3e", p->id, i, + p->chemistry_data.metal_mass_dt[i]); + } + warning("[PID%lld S: [[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e]]", + p->id, p->chemistry_data.S[0][0], p->chemistry_data.S[0][1], + p->chemistry_data.S[0][2], p->chemistry_data.S[1][0], + p->chemistry_data.S[1][1], p->chemistry_data.S[1][2], + p->chemistry_data.S[2][0], p->chemistry_data.S[2][1], + p->chemistry_data.S[2][2]); +} + +#endif /* SWIFT_CHEMISTRY_GEAR_DIFFUSION_DEBUG_H */ diff --git a/src/chemistry/GEAR_DIFFUSION/chemistry_struct.h b/src/chemistry/GEAR_DIFFUSION/chemistry_struct.h index eb8b1e255e4d4130ef843251dff34bd199e42295..986f71b28439e7596eb41cb606d89009a84905bc 100644 --- a/src/chemistry/GEAR_DIFFUSION/chemistry_struct.h +++ b/src/chemistry/GEAR_DIFFUSION/chemistry_struct.h @@ -67,4 +67,9 @@ struct chemistry_spart_data { */ struct chemistry_bpart_data {}; +/** + * @brief Chemical abundances traced by the #sink in the GEAR model. + */ +struct chemistry_sink_data {}; + #endif /* SWIFT_CHEMISTRY_STRUCT_GEAR_DIFFUSION_H */ diff --git a/src/chemistry/QLA/chemistry_debug.h b/src/chemistry/QLA/chemistry_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..ec4c56d6d0b2211e5a287812670099d67c81582c --- /dev/null +++ b/src/chemistry/QLA/chemistry_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_CHEMISTRY_QLA_DEBUG_H +#define SWIFT_CHEMISTRY_QLA_DEBUG_H + +__attribute__((always_inline)) INLINE static void chemistry_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_CHEMISTRY_QLA_DEBUG_H */ diff --git a/src/chemistry/QLA/chemistry_io.h b/src/chemistry/QLA/chemistry_io.h index 62eda9244b888325c52bb20fb50c0d61937b27ec..d02da1ec8e1f83441eaa18398f8e795eeccf5a48 100644 --- a/src/chemistry/QLA/chemistry_io.h +++ b/src/chemistry/QLA/chemistry_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2020 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 diff --git a/src/chemistry/QLA/chemistry_struct.h b/src/chemistry/QLA/chemistry_struct.h index ac5ac803773790704666bbab5ac38bd16c766575..4f6d5d2bb06bd4e84ddcbc13ffd007cc189143a3 100644 --- a/src/chemistry/QLA/chemistry_struct.h +++ b/src/chemistry/QLA/chemistry_struct.h @@ -52,4 +52,11 @@ struct chemistry_part_data {}; */ struct chemistry_bpart_data {}; +/** + * @brief Chemical abundances traced by the #sink in the QLA model. + * + * Nothing here. + */ +struct chemistry_sink_data {}; + #endif /* SWIFT_CHEMISTRY_STRUCT_QLA_H */ diff --git a/src/chemistry/none/chemistry.h b/src/chemistry/none/chemistry.h index ca108b1b886cf940af2c0698b1a42610c87d44d3..239bc6a0047760962b0024073f4fddb6c1e632bc 100644 --- a/src/chemistry/none/chemistry.h +++ b/src/chemistry/none/chemistry.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -248,6 +248,32 @@ __attribute__((always_inline)) INLINE static void chemistry_add_bpart_to_bpart( struct chemistry_bpart_data* bp_data, const struct chemistry_bpart_data* swallowed_data) {} +/** + * @brief Add the chemistry data of a sink particle to a sink. + * + * Nothing to do here. + * + * @param si_data The black hole data to add to. + * @param sj_data The gas data to use. + * @param gas_mass The mass of the gas particle. + */ +__attribute__((always_inline)) INLINE static void chemistry_add_sink_to_sink( + struct chemistry_sink_data* si_data, + const struct chemistry_sink_data* sj_data) {} + +/** + * @brief Add the chemistry data of a gas particle to a sink. + * + * Nothing to do here. + * + * @param sp_data The sink data to add to. + * @param p_data The gas data to use. + * @param gas_mass The mass of the gas particle. + */ +__attribute__((always_inline)) INLINE static void chemistry_add_part_to_sink( + struct chemistry_sink_data* sp_data, + const struct chemistry_part_data* p_data, const double gas_mass) {} + /** * @brief Split the metal content of a particle into n pieces * diff --git a/src/chemistry/none/chemistry_csds.h b/src/chemistry/none/chemistry_csds.h index 04d009789bae0c3f15d998e707e626a5ce253885..ba416cbceceecd18d3c8beb13b9c3d70c6a5e567 100644 --- a/src/chemistry/none/chemistry_csds.h +++ b/src/chemistry/none/chemistry_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/chemistry/none/chemistry_debug.h b/src/chemistry/none/chemistry_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..500d364ef107d1b2627062491a6d7feb2a11e2f6 --- /dev/null +++ b/src/chemistry/none/chemistry_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_CHEMISTRY_NONE_DEBUG_H +#define SWIFT_CHEMISTRY_NONE_DEBUG_H + +__attribute__((always_inline)) INLINE static void chemistry_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_CHEMISTRY_NONE_DEBUG_H */ diff --git a/src/chemistry/none/chemistry_iact.h b/src/chemistry/none/chemistry_iact.h index 5fb3b12b6927783279ebe087899c4637d88d0a17..f662881c8f7633d4cdf74981328d0fec4d4d1df6 100644 --- a/src/chemistry/none/chemistry_iact.h +++ b/src/chemistry/none/chemistry_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 diff --git a/src/chemistry/none/chemistry_io.h b/src/chemistry/none/chemistry_io.h index 19a3a1b9ffde0d3669c7751a218155be03681a77..062c0339dcf20935934b55c006ad5d30b9d7b467 100644 --- a/src/chemistry/none/chemistry_io.h +++ b/src/chemistry/none/chemistry_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/chemistry/none/chemistry_struct.h b/src/chemistry/none/chemistry_struct.h index 78f9aed11fe879ae480a336393ac8210b5c7cd65..5fbb25fa0cfd1e886b39840a19bef31be56cac36 100644 --- a/src/chemistry/none/chemistry_struct.h +++ b/src/chemistry/none/chemistry_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -57,4 +57,11 @@ struct chemistry_spart_data {}; */ struct chemistry_bpart_data {}; +/** + * @brief Chemical abundances traced by the #sink. + * + * Nothing here. + */ +struct chemistry_sink_data {}; + #endif /* SWIFT_CHEMISTRY_STRUCT_NONE_H */ diff --git a/src/chemistry_csds.h b/src/chemistry_csds.h index 3e313f307c156899142c03e42bae377668055628..5866340191f3eb3c8c96e30dcb8db11e55dc9ce5 100644 --- a/src/chemistry_csds.h +++ b/src/chemistry_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -20,7 +20,7 @@ #define SWIFT_CHEMISTRY_CSDS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "align.h" diff --git a/src/chemistry_debug.h b/src/chemistry_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..0d08c8561fe9344c1a83e49e92bd4167f5cb2aad --- /dev/null +++ b/src/chemistry_debug.h @@ -0,0 +1,40 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_CHEMISTRY_DEBUG_H +#define SWIFT_CHEMISTRY_DEBUG_H + +/* Config parameters. */ +#include <config.h> + +/* Import the debug routines of the right chemistry definition */ +#if defined(CHEMISTRY_NONE) +#include "./chemistry/none/chemistry_debug.h" +#elif defined(CHEMISTRY_GEAR) +#include "./chemistry/GEAR/chemistry_debug.h" +#elif defined(CHEMISTRY_GEAR_DIFFUSION) +#include "./chemistry/GEAR_DIFFUSION/chemistry_debug.h" +#elif defined(CHEMISTRY_QLA) +#include "./chemistry/QLA/chemistry_debug.h" +#elif defined(CHEMISTRY_EAGLE) +#include "./chemistry/EAGLE/chemistry_debug.h" +#else +#error "Invalid choice of chemistry function." +#endif + +#endif /* SWIFT_CHEMISTRY_DEBUG_H */ diff --git a/src/chemistry_io.h b/src/chemistry_io.h index d7386f8c68a42b797a8d201e8a68329f00b4f6fc..cecbd789bcd007d0d2cb1f3ff8bf3d36066ce062 100644 --- a/src/chemistry_io.h +++ b/src/chemistry_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_CHEMISTRY_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right functions */ #if defined(CHEMISTRY_NONE) diff --git a/src/chemistry_struct.h b/src/chemistry_struct.h index 0b1cb59e592d9dbce8e77fb01cf45e1be846a55b..51572af3c7f45818fd3862bbc49e8518c5de951d 100644 --- a/src/chemistry_struct.h +++ b/src/chemistry_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right chemistry definition */ #if defined(CHEMISTRY_NONE) diff --git a/src/clocks.c b/src/clocks.c index 0ac2d97e18658195f9140857f8ee6fef4bd1fe1d..9144830572a833d89521b84ded3a54039daa00e7 100644 --- a/src/clocks.c +++ b/src/clocks.c @@ -26,11 +26,12 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers. */ #include <limits.h> #include <stdio.h> +#include <time.h> #include <unistd.h> /* Local headers. */ @@ -315,3 +316,22 @@ int clocks_random_seed(void) { return (getticks() % INT_MAX); #endif } + +/** + * @brief Get the current time, either in SWIFT format, "%T %F %Z" + * or seconds in the epoch. + * @param swift return SWIFT format. + * @result the formatted time, take a copy if you need to keep it longer than + * the next call. + */ +const char *clocks_now(int swift) { + static char now[64]; + time_t tm = time(NULL); + struct tm *timeinfo = localtime(&tm); + if (swift) { + strftime(now, 64, "%T %F %Z", timeinfo); + } else { + strftime(now, 64, "%s", timeinfo); + } + return now; +} diff --git a/src/clocks.h b/src/clocks.h index c9c23807587e1ad4b519651911e1c37c9c1a1d6b..e39d8e8195439f37dff477a62401f023b755078d 100644 --- a/src/clocks.h +++ b/src/clocks.h @@ -20,7 +20,7 @@ #define SWIFT_CLOCKS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* System includes. */ #include <sys/times.h> @@ -55,4 +55,6 @@ double clocks_get_hours_since_start(void); void clocks_get_cputimes_used(double *usertime, double *systime); int clocks_random_seed(void); +const char *clocks_now(int swift); + #endif /* SWIFT_CLOCKS_H */ diff --git a/src/collectgroup.c b/src/collectgroup.c index ad080b30693f51ed211341a9d2120b788145eb34..f876ccb37f86322617756bba75c638a8909eba27 100644 --- a/src/collectgroup.c +++ b/src/collectgroup.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -40,11 +40,13 @@ struct mpicollectgroup1 { long long updated, g_updated, s_updated, sink_updated, b_updated; long long inhibited, g_inhibited, s_inhibited, sink_inhibited, b_inhibited; integertime_t ti_hydro_end_min; + integertime_t ti_rt_end_min; integertime_t ti_gravity_end_min; integertime_t ti_stars_end_min; integertime_t ti_sinks_end_min; integertime_t ti_black_holes_end_min; integertime_t ti_hydro_beg_max; + integertime_t ti_rt_beg_max; integertime_t ti_gravity_beg_max; integertime_t ti_stars_beg_max; integertime_t ti_sinks_beg_max; @@ -55,6 +57,7 @@ struct mpicollectgroup1 { float tasks_per_cell_max; struct star_formation_history sfh; float runtime; + int flush_lightcone_maps; double deadtime; #ifdef WITH_CSDS float csds_file_size_gb; @@ -98,6 +101,8 @@ void collectgroup1_apply(const struct collectgroup1 *grp1, struct engine *e) { e->ti_hydro_end_min = grp1->ti_hydro_end_min; e->ti_hydro_beg_max = grp1->ti_hydro_beg_max; + e->ti_rt_end_min = grp1->ti_rt_end_min; + e->ti_rt_beg_max = grp1->ti_rt_beg_max; e->ti_gravity_end_min = grp1->ti_gravity_end_min; e->ti_gravity_beg_max = grp1->ti_gravity_beg_max; e->ti_stars_end_min = grp1->ti_stars_end_min; @@ -132,6 +137,7 @@ void collectgroup1_apply(const struct collectgroup1 *grp1, struct engine *e) { star_formation_logger_add_to_accumulator(&e->sfh, &grp1->sfh); e->runtime = grp1->runtime; + e->flush_lightcone_maps = grp1->flush_lightcone_maps; e->global_deadtime = grp1->deadtime; } @@ -193,6 +199,7 @@ void collectgroup1_apply(const struct collectgroup1 *grp1, struct engine *e) { * @param tasks_per_cell the used number of tasks per cell. * @param sfh The star formation history logger * @param runtime The runtime of rank in hours. + * @param flush_lightcone_maps Flag whether lightcone maps should be updated * @param deadtime The deadtime of rank. * @param csds_file_size_gb The current size of the CSDS. */ @@ -201,14 +208,15 @@ void collectgroup1_init( size_t s_updated, size_t sink_updated, size_t b_updated, size_t inhibited, size_t g_inhibited, size_t s_inhibited, size_t sink_inhibited, size_t b_inhibited, integertime_t ti_hydro_end_min, - integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min, + integertime_t ti_hydro_beg_max, integertime_t ti_rt_end_min, + integertime_t ti_rt_beg_max, integertime_t ti_gravity_end_min, integertime_t ti_gravity_beg_max, integertime_t ti_stars_end_min, integertime_t ti_stars_beg_max, integertime_t ti_sinks_end_min, integertime_t ti_sinks_beg_max, integertime_t ti_black_holes_end_min, integertime_t ti_black_holes_beg_max, int forcerebuild, long long total_nr_cells, long long total_nr_tasks, float tasks_per_cell, - const struct star_formation_history sfh, float runtime, double deadtime, - float csds_file_size_gb) { + const struct star_formation_history sfh, float runtime, + int flush_lightcone_maps, double deadtime, float csds_file_size_gb) { grp1->updated = updated; grp1->g_updated = g_updated; @@ -222,6 +230,8 @@ void collectgroup1_init( grp1->sink_inhibited = sink_inhibited; grp1->ti_hydro_end_min = ti_hydro_end_min; grp1->ti_hydro_beg_max = ti_hydro_beg_max; + grp1->ti_rt_end_min = ti_rt_end_min; + grp1->ti_rt_beg_max = ti_rt_beg_max; grp1->ti_gravity_end_min = ti_gravity_end_min; grp1->ti_gravity_beg_max = ti_gravity_beg_max; grp1->ti_stars_end_min = ti_stars_end_min; @@ -236,6 +246,7 @@ void collectgroup1_init( grp1->tasks_per_cell_max = tasks_per_cell; grp1->sfh = sfh; grp1->runtime = runtime; + grp1->flush_lightcone_maps = flush_lightcone_maps; grp1->deadtime = deadtime; #ifdef WITH_CSDS grp1->csds_file_size_gb = csds_file_size_gb; @@ -267,11 +278,13 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { mpigrp11.sink_inhibited = grp1->sink_inhibited; mpigrp11.b_inhibited = grp1->b_inhibited; mpigrp11.ti_hydro_end_min = grp1->ti_hydro_end_min; + mpigrp11.ti_rt_end_min = grp1->ti_rt_end_min; mpigrp11.ti_gravity_end_min = grp1->ti_gravity_end_min; mpigrp11.ti_stars_end_min = grp1->ti_stars_end_min; mpigrp11.ti_sinks_end_min = grp1->ti_sinks_end_min; mpigrp11.ti_black_holes_end_min = grp1->ti_black_holes_end_min; mpigrp11.ti_hydro_beg_max = grp1->ti_hydro_beg_max; + mpigrp11.ti_rt_beg_max = grp1->ti_rt_beg_max; mpigrp11.ti_gravity_beg_max = grp1->ti_gravity_beg_max; mpigrp11.ti_stars_beg_max = grp1->ti_stars_beg_max; mpigrp11.ti_sinks_beg_max = grp1->ti_sinks_beg_max; @@ -282,6 +295,7 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { mpigrp11.tasks_per_cell_max = grp1->tasks_per_cell_max; mpigrp11.sfh = grp1->sfh; mpigrp11.runtime = grp1->runtime; + mpigrp11.flush_lightcone_maps = grp1->flush_lightcone_maps; mpigrp11.deadtime = grp1->deadtime; #ifdef WITH_CSDS mpigrp11.csds_file_size_gb = grp1->csds_file_size_gb; @@ -304,11 +318,13 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { grp1->sink_inhibited = mpigrp12.sink_inhibited; grp1->b_inhibited = mpigrp12.b_inhibited; grp1->ti_hydro_end_min = mpigrp12.ti_hydro_end_min; + grp1->ti_rt_end_min = mpigrp12.ti_rt_end_min; grp1->ti_gravity_end_min = mpigrp12.ti_gravity_end_min; grp1->ti_stars_end_min = mpigrp12.ti_stars_end_min; grp1->ti_sinks_end_min = mpigrp12.ti_sinks_end_min; grp1->ti_black_holes_end_min = mpigrp12.ti_black_holes_end_min; grp1->ti_hydro_beg_max = mpigrp12.ti_hydro_beg_max; + grp1->ti_rt_beg_max = mpigrp12.ti_rt_beg_max; grp1->ti_gravity_beg_max = mpigrp12.ti_gravity_beg_max; grp1->ti_stars_beg_max = mpigrp12.ti_stars_beg_max; grp1->ti_sinks_beg_max = mpigrp12.ti_sinks_beg_max; @@ -319,6 +335,8 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { grp1->tasks_per_cell_max = mpigrp12.tasks_per_cell_max; grp1->sfh = mpigrp12.sfh; grp1->runtime = mpigrp12.runtime; + grp1->flush_lightcone_maps = mpigrp12.flush_lightcone_maps; + grp1->deadtime = mpigrp12.deadtime; #ifdef WITH_CSDS grp1->csds_file_size_gb = mpigrp12.csds_file_size_gb; @@ -355,6 +373,8 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11, /* Minimum end time. */ mpigrp11->ti_hydro_end_min = min(mpigrp11->ti_hydro_end_min, mpigrp12->ti_hydro_end_min); + mpigrp11->ti_rt_end_min = + min(mpigrp11->ti_rt_end_min, mpigrp12->ti_rt_end_min); mpigrp11->ti_gravity_end_min = min(mpigrp11->ti_gravity_end_min, mpigrp12->ti_gravity_end_min); mpigrp11->ti_stars_end_min = @@ -367,6 +387,8 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11, /* Maximum beg time. */ mpigrp11->ti_hydro_beg_max = max(mpigrp11->ti_hydro_beg_max, mpigrp12->ti_hydro_beg_max); + mpigrp11->ti_rt_beg_max = + max(mpigrp11->ti_rt_beg_max, mpigrp12->ti_rt_beg_max); mpigrp11->ti_gravity_beg_max = max(mpigrp11->ti_gravity_beg_max, mpigrp12->ti_gravity_beg_max); mpigrp11->ti_stars_beg_max = @@ -394,6 +416,10 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11, /* Use the maximum runtime as the global runtime. */ mpigrp11->runtime = max(mpigrp11->runtime, mpigrp12->runtime); + /* Lightcone maps are all updated if any need to be updated */ + if (mpigrp11->flush_lightcone_maps || mpigrp12->flush_lightcone_maps) + mpigrp11->flush_lightcone_maps = 1; + /* Sum the deadtime. */ mpigrp11->deadtime += mpigrp12->deadtime; diff --git a/src/collectgroup.h b/src/collectgroup.h index 74a51a0663c52c9abad62035b35525c4a1c08bca..68815dab0f98035f5bfc4b3616e00da96fa124a4 100644 --- a/src/collectgroup.h +++ b/src/collectgroup.h @@ -20,7 +20,7 @@ #define SWIFT_COLLECTGROUP_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers. */ #include <stddef.h> @@ -46,6 +46,7 @@ struct collectgroup1 { /* Times for the time-step */ integertime_t ti_hydro_end_min, ti_hydro_beg_max; + integertime_t ti_rt_end_min, ti_rt_beg_max; integertime_t ti_gravity_end_min, ti_gravity_beg_max; integertime_t ti_stars_end_min, ti_stars_beg_max; integertime_t ti_black_holes_end_min, ti_black_holes_beg_max; @@ -64,6 +65,9 @@ struct collectgroup1 { /* Global runtime of application in hours. */ float runtime; + /* Flag to determine if lightcone maps should be updated this step */ + int flush_lightcone_maps; + /* Accumulated dead time during the step. */ double deadtime; @@ -80,14 +84,15 @@ void collectgroup1_init( size_t s_updated, size_t b_updated, size_t sink_updated, size_t inhibited, size_t g_inhibited, size_t s_inhibited, size_t sink_inhibited, size_t b_inhibited, integertime_t ti_hydro_end_min, - integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min, + integertime_t ti_hydro_beg_max, integertime_t ti_rt_end_min, + integertime_t ti_rt_beg_max, integertime_t ti_gravity_end_min, integertime_t ti_gravity_beg_max, integertime_t ti_stars_end_min, integertime_t ti_stars_beg_max, integertime_t ti_sinks_end_min, integertime_t ti_sinks_beg_max, integertime_t ti_black_holes_end_min, integertime_t ti_black_holes_beg_max, int forcerebuild, long long total_nr_cells, long long total_nr_tasks, float tasks_per_cell, - const struct star_formation_history sfh, float runtime, double deadtime, - float csds_file_size_gb); + const struct star_formation_history sfh, float runtime, + int flush_lightcone_maps, double deadtime, float csds_file_size_gb); void collectgroup1_reduce(struct collectgroup1 *grp1); #ifdef WITH_MPI void mpicollect_free_MPI_type(void); diff --git a/src/common_io.c b/src/common_io.c index 9fc9fccd4f3985fda7c9f0258b30c89dfd4e5edc..145eba9015802b8a5ecc871c16ddfd02bff37559 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "common_io.h" @@ -38,10 +38,12 @@ #include "black_holes_io.h" #include "chemistry_io.h" #include "cooling_io.h" +#include "extra_io.h" #include "feedback.h" #include "fof_io.h" #include "gravity_io.h" #include "hydro_io.h" +#include "mhd_io.h" #include "neutrino_io.h" #include "particle_splitting.h" #include "rt_io.h" @@ -285,9 +287,9 @@ void io_read_array_attribute(hid_t grp, const char* name, /* Check if correct number of element */ if (count != number_element) { error( - "Error found a different number of elements than expected (%lli != " - "%lli) in attribute %s", - count, number_element, name); + "Error found a different number of elements than expected (%llu != " + "%llu) in attribute %s", + (unsigned long long)count, (unsigned long long)number_element, name); } /* Read attribute */ @@ -356,9 +358,9 @@ void io_read_array_dataset(hid_t grp, const char* name, enum IO_DATA_TYPE type, /* Check if correct number of element */ if (count != number_element) { error( - "Error found a different number of elements than expected (%lli != " - "%lli) in dataset %s", - count, number_element, name); + "Error found a different number of elements than expected (%llu != " + "%llu) in dataset %s", + (unsigned long long)count, (unsigned long long)number_element, name); } /* Read dataset */ @@ -528,6 +530,7 @@ void io_write_meta_data(hid_t h_file, const struct engine* e, if (h_grp < 0) error("Error while creating SPH group"); hydro_props_print_snapshot(h_grp, e->hydro_properties); hydro_write_flavour(h_grp); + mhd_write_flavour(h_grp); H5Gclose(h_grp); } @@ -539,6 +542,7 @@ void io_write_meta_data(hid_t h_file, const struct engine* e, H5Gcreate(h_grp, "NamedColumns", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp_columns < 0) error("Error while creating named columns group"); entropy_floor_write_flavour(h_grp); + extra_io_write_flavour(h_grp, h_grp_columns); cooling_write_flavour(h_grp, h_grp_columns, e->cooling_func); chemistry_write_flavour(h_grp, h_grp_columns, e); tracers_write_flavour(h_grp); @@ -592,6 +596,36 @@ void io_write_meta_data(hid_t h_file, const struct engine* e, parser_write_params_to_hdf5(e->parameter_file, h_grp, /*write_used=*/0); H5Gclose(h_grp); + /* Print the recording triggers */ + h_grp = H5Gcreate(h_file, "/RecordingTriggers", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp < 0) error("Error while creating recording triggers group"); + if (num_snapshot_triggers_part) { + io_write_attribute(h_grp, "DesiredRecordingTimesGas", DOUBLE, + e->snapshot_recording_triggers_desired_part, + num_snapshot_triggers_part); + io_write_attribute(h_grp, "ActualRecordingTimesGas", DOUBLE, + e->snapshot_recording_triggers_part, + num_snapshot_triggers_part); + } + if (num_snapshot_triggers_spart) { + io_write_attribute(h_grp, "DesiredRecordingTimesStars", DOUBLE, + e->snapshot_recording_triggers_desired_spart, + num_snapshot_triggers_spart); + io_write_attribute(h_grp, "ActualRecordingTimesStars", DOUBLE, + e->snapshot_recording_triggers_spart, + num_snapshot_triggers_spart); + } + if (num_snapshot_triggers_bpart) { + io_write_attribute(h_grp, "DesiredRecordingTimesBlackHoles", DOUBLE, + e->snapshot_recording_triggers_desired_bpart, + num_snapshot_triggers_bpart); + io_write_attribute(h_grp, "ActualRecordingTimesBlackHoles", DOUBLE, + e->snapshot_recording_triggers_bpart, + num_snapshot_triggers_bpart); + } + H5Gclose(h_grp); + /* Print the system of Units used in the spashot */ io_write_unit_system(h_file, snapshot_units, "Units"); @@ -1671,6 +1705,8 @@ void io_select_hydro_fields(const struct part* const parts, hydro_write_particles(parts, xparts, list, num_fields); + *num_fields += mhd_write_particles(parts, xparts, list + *num_fields); + *num_fields += particle_splitting_write_particles( parts, xparts, list + *num_fields, with_cosmology); *num_fields += chemistry_write_particles(parts, xparts, list + *num_fields, @@ -1691,6 +1727,8 @@ void io_select_hydro_fields(const struct part* const parts, if (with_rt) { *num_fields += rt_write_particles(parts, list + *num_fields); } + *num_fields += extra_io_write_particles(parts, xparts, list + *num_fields, + with_cosmology); } /** @@ -1793,6 +1831,8 @@ void io_select_star_fields(const struct spart* const sparts, if (with_rt) { *num_fields += rt_write_stars(sparts, list + *num_fields); } + *num_fields += + extra_io_write_sparticles(sparts, list + *num_fields, with_cosmology); } /** @@ -1815,10 +1855,14 @@ void io_select_bh_fields(const struct bpart* const bparts, *num_fields += particle_splitting_write_bparticles(bparts, list + *num_fields); *num_fields += chemistry_write_bparticles(bparts, list + *num_fields); + *num_fields += + tracers_write_bparticles(bparts, list + *num_fields, with_cosmology); if (with_fof) { *num_fields += fof_write_bparts(bparts, list + *num_fields); } if (with_stf) { *num_fields += velociraptor_write_bparts(bparts, list + *num_fields); } + *num_fields += + extra_io_write_bparticles(bparts, list + *num_fields, with_cosmology); } diff --git a/src/common_io.h b/src/common_io.h index 73719f35c920203960afc094ea3060e04456e968..d7334a83fddf90aca12940cfe3290955fe65f10b 100644 --- a/src/common_io.h +++ b/src/common_io.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -21,7 +21,7 @@ #define SWIFT_COMMON_IO_H /* Config parameters. */ -#include "config.h" +#include <config.h> /* Local includes. */ #include "part_type.h" @@ -116,6 +116,7 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], const int snap_num, const long long global_counts[swift_type_count], const long long global_offsets[swift_type_count], + const int to_write[swift_type_count], const int num_fields[swift_type_count], const struct unit_system* internal_units, const struct unit_system* snapshot_units); diff --git a/src/common_io_cells.c b/src/common_io_cells.c index 1d76aea1708b529786e3b7b629c5620bf2337004..86b38b4183a0d744332b4b3e0c2ba40e19a88b43 100644 --- a/src/common_io_cells.c +++ b/src/common_io_cells.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "common_io.h" @@ -29,306 +29,119 @@ #include "timeline.h" #include "units.h" -static long long cell_count_non_inhibited_gas(const struct cell* c, - const int subsample, - const float subsample_ratio, - const int snap_num) { - const int total_count = c->hydro.count; - 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)) { - - /* When subsampling, select particles at random */ - if (subsample) { - const float r = random_unit_interval(parts[i].id, snap_num, - random_number_snapshot_sampling); - if (r > subsample_ratio) continue; - } - - ++count; - } - } - return count; -} - -static long long cell_count_non_inhibited_dark_matter( - const struct cell* c, const int subsample, const float subsample_ratio, - const int snap_num) { - - 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)) { - - /* When subsampling, select particles at random */ - if (subsample) { - const float r = - random_unit_interval(gparts[i].id_or_neg_offset, snap_num, - random_number_snapshot_sampling); - if (r > subsample_ratio) continue; - } - ++count; - } - } - return count; -} - -static long long cell_count_non_inhibited_background_dark_matter( - const struct cell* c, const int subsample, const float subsample_ratio, - const int snap_num) { - - 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)) { - - /* When subsampling, select particles at random */ - if (subsample) { - const float r = - random_unit_interval(gparts[i].id_or_neg_offset, snap_num, - random_number_snapshot_sampling); - if (r > subsample_ratio) continue; - } - - ++count; - } - } - return count; -} - -static long long cell_count_non_inhibited_stars(const struct cell* c, - const int subsample, - const float subsample_ratio, - const int snap_num) { - 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)) { - - /* When subsampling, select particles at random */ - if (subsample) { - const float r = random_unit_interval(sparts[i].id, snap_num, - random_number_snapshot_sampling); - if (r > subsample_ratio) continue; - } - - ++count; - } - } - return count; -} - -static long long cell_count_non_inhibited_black_holes( - const struct cell* c, const int subsample, const float subsample_ratio, - const int snap_num) { - const int total_count = c->black_holes.count; - 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)) { - - /* When subsampling, select particles at random */ - if (subsample) { - const float r = random_unit_interval(bparts[i].id, snap_num, - random_number_snapshot_sampling); - if (r > subsample_ratio) continue; - } - - ++count; - } - } - return count; -} - -static long long cell_count_non_inhibited_sinks(const struct cell* c, - const int subsample, - const float subsample_ratio, - const int snap_num) { - const int total_count = c->sinks.count; - struct sink* sinks = c->sinks.parts; - long long count = 0; - for (int i = 0; i < total_count; ++i) { - if ((sinks[i].time_bin != time_bin_inhibited) && - (sinks[i].time_bin != time_bin_not_created)) { - - /* When subsampling, select particles at random */ - if (subsample) { - const float r = random_unit_interval(sinks[i].id, snap_num, - random_number_snapshot_sampling); - if (r > subsample_ratio) continue; - } - - ++count; - } - } - return count; -} - -static long long cell_count_non_inhibited_neutrinos(const struct cell* c, - const int subsample, - const float subsample_ratio, - const int snap_num) { - - 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_neutrino)) { - - /* When subsampling, select particles at random */ - if (subsample) { - const float r = - random_unit_interval(gparts[i].id_or_neg_offset, snap_num, - random_number_snapshot_sampling); - if (r > subsample_ratio) continue; - } - - ++count; - } - } - return count; -} - -/** - * @brief Count the number of local non-inhibited gas particles to write. - * - * Takes into account downsampling. - * - * @param s The #space. - * @param subsample Are we subsampling? - * @param subsample_ratio The fraction of particle to keep when subsampling. - * @param snap_num The snapshot number to use as random seed. - */ -long long io_count_gas_to_write(const struct space* s, const int subsample, - const float subsample_ratio, - const int snap_num) { - - long long count = 0; - for (int i = 0; i < s->nr_local_cells; ++i) { - - const struct cell* c = &s->cells_top[s->local_cells_top[i]]; - count += - cell_count_non_inhibited_gas(c, subsample, subsample_ratio, snap_num); - } - return count; -} +/* Standard includes */ +#include <float.h> /** - * @brief Count the number of local non-inhibited dark matter particles to - * write. - * - * Takes into account downsampling. + * @brief Count the non-inhibted particles in the cell and return the + * min/max positions * - * @param s The #space. - * @param subsample Are we subsampling? - * @param subsample_ratio The fraction of particle to keep when subsampling. - * @param snap_num The snapshot number to use as random seed. + * @param c The #cell. + * @param subsample Are we subsampling the output? + * @param susample_ratio Fraction of particles to write when sub-sampling. + * @param snap_num The snapshot number (used for the sampling random draws). + * @param min_pos (return) The min position of all particles to write. + * @param max_pos (return) The max position of all particles to write. */ -long long io_count_dark_matter_to_write(const struct space* s, - const int subsample, - const float subsample_ratio, - const int snap_num) { - - long long count = 0; - for (int i = 0; i < s->nr_local_cells; ++i) { - - const struct cell* c = &s->cells_top[s->local_cells_top[i]]; - count += cell_count_non_inhibited_dark_matter(c, subsample, subsample_ratio, - snap_num); +#define CELL_COUNT_NON_INHIBITED_PARTICLES(TYPE, CELL_TYPE) \ + cell_count_non_inhibited_##TYPE( \ + const struct cell* c, const int subsample, const float subsample_ratio, \ + const int snap_num, double min_pos[3], double max_pos[3]) { \ + \ + const int total_count = c->CELL_TYPE.count; \ + const struct TYPE* parts = c->CELL_TYPE.parts; \ + long long count = 0; \ + min_pos[0] = min_pos[1] = min_pos[2] = DBL_MAX; \ + max_pos[0] = max_pos[1] = max_pos[2] = -DBL_MAX; \ + \ + for (int i = 0; i < total_count; ++i) { \ + if ((parts[i].time_bin != time_bin_inhibited) && \ + (parts[i].time_bin != time_bin_not_created)) { \ + \ + /* When subsampling, select particles at random */ \ + if (subsample) { \ + const float r = random_unit_interval( \ + parts[i].id, snap_num, random_number_snapshot_sampling); \ + if (r > subsample_ratio) continue; \ + } \ + \ + ++count; \ + \ + min_pos[0] = min(parts[i].x[0], min_pos[0]); \ + min_pos[1] = min(parts[i].x[1], min_pos[1]); \ + min_pos[2] = min(parts[i].x[2], min_pos[2]); \ + \ + max_pos[0] = max(parts[i].x[0], max_pos[0]); \ + max_pos[1] = max(parts[i].x[1], max_pos[1]); \ + max_pos[2] = max(parts[i].x[2], max_pos[2]); \ + } \ + } \ + return count; \ } - return count; -} - -/** - * @brief Count the number of local non-inhibited background dark matter - * particles to write. - * - * Takes into account downsampling. - * - * @param s The #space. - * @param subsample Are we subsampling? - * @param subsample_ratio The fraction of particle to keep when subsampling. - * @param snap_num The snapshot number to use as random seed. - */ -long long io_count_background_dark_matter_to_write(const struct space* s, - const int subsample, - const float subsample_ratio, - const int snap_num) { - long long count = 0; - for (int i = 0; i < s->nr_local_cells; ++i) { - - const struct cell* c = &s->cells_top[s->local_cells_top[i]]; - count += cell_count_non_inhibited_background_dark_matter( - c, subsample, subsample_ratio, snap_num); - } - return count; -} +static long long CELL_COUNT_NON_INHIBITED_PARTICLES(part, hydro); +static long long CELL_COUNT_NON_INHIBITED_PARTICLES(spart, stars); +static long long CELL_COUNT_NON_INHIBITED_PARTICLES(bpart, black_holes); +static long long CELL_COUNT_NON_INHIBITED_PARTICLES(sink, sinks); /** - * @brief Count the number of local non-inhibited stars particles to write. + * @brief Count the non-inhibted g-particles in the cell and return the + * min/max positions * - * Takes into account downsampling. - * - * @param s The #space. - * @param subsample Are we subsampling? - * @param subsample_ratio The fraction of particle to keep when subsampling. - * @param snap_num The snapshot number to use as random seed. + * @param c The #cell. + * @param subsample Are we subsampling the output? + * @param susample_ratio Fraction of particles to write when sub-sampling. + * @param snap_num The snapshot number (used for the sampling random draws). + * @param min_pos (return) The min position of all particles to write. + * @param max_pos (return) The max position of all particles to write. */ -long long io_count_stars_to_write(const struct space* s, const int subsample, - const float subsample_ratio, - const int snap_num) { - - long long count = 0; - for (int i = 0; i < s->nr_local_cells; ++i) { - - const struct cell* c = &s->cells_top[s->local_cells_top[i]]; - count += - cell_count_non_inhibited_stars(c, subsample, subsample_ratio, snap_num); +#define CELL_COUNT_NON_INHIBITED_GPARTICLES(TYPE, PART_TYPE) \ + cell_count_non_inhibited_##TYPE( \ + const struct cell* c, const int subsample, const float subsample_ratio, \ + const int snap_num, double min_pos[3], double max_pos[3]) { \ + \ + const int total_count = c->grav.count; \ + const struct gpart* gparts = c->grav.parts; \ + long long count = 0; \ + min_pos[0] = min_pos[1] = min_pos[2] = DBL_MAX; \ + max_pos[0] = max_pos[1] = max_pos[2] = -DBL_MAX; \ + \ + 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 == PART_TYPE)) { \ + \ + /* When subsampling, select particles at random */ \ + if (subsample) { \ + const float r = \ + random_unit_interval(gparts[i].id_or_neg_offset, snap_num, \ + random_number_snapshot_sampling); \ + if (r > subsample_ratio) continue; \ + } \ + \ + ++count; \ + \ + min_pos[0] = min(gparts[i].x[0], min_pos[0]); \ + min_pos[1] = min(gparts[i].x[1], min_pos[1]); \ + min_pos[2] = min(gparts[i].x[2], min_pos[2]); \ + \ + max_pos[0] = max(gparts[i].x[0], max_pos[0]); \ + max_pos[1] = max(gparts[i].x[1], max_pos[1]); \ + max_pos[2] = max(gparts[i].x[2], max_pos[2]); \ + } \ + } \ + return count; \ } - return count; -} -/** - * @brief Count the number of local non-inhibited sinks particles to write. - * - * Takes into account downsampling. - * - * @param s The #space. - * @param subsample Are we subsampling? - * @param subsample_ratio The fraction of particle to keep when subsampling. - * @param snap_num The snapshot number to use as random seed. - */ -long long io_count_sinks_to_write(const struct space* s, const int subsample, - const float subsample_ratio, - const int snap_num) { - - long long count = 0; - for (int i = 0; i < s->nr_local_cells; ++i) { - - const struct cell* c = &s->cells_top[s->local_cells_top[i]]; - count += - cell_count_non_inhibited_sinks(c, subsample, subsample_ratio, snap_num); - } - return count; -} +static long long CELL_COUNT_NON_INHIBITED_GPARTICLES(dark_matter, + swift_type_dark_matter); +static long long CELL_COUNT_NON_INHIBITED_GPARTICLES( + background_dark_matter, swift_type_dark_matter_background); +static long long CELL_COUNT_NON_INHIBITED_GPARTICLES(neutrinos, + swift_type_neutrino); /** - * @brief Count the number of local non-inhibited black holes particles to - * write. + * @brief Count the number of local non-inhibited particles to write. * * Takes into account downsampling. * @@ -337,45 +150,28 @@ long long io_count_sinks_to_write(const struct space* s, const int subsample, * @param subsample_ratio The fraction of particle to keep when subsampling. * @param snap_num The snapshot number to use as random seed. */ -long long io_count_black_holes_to_write(const struct space* s, - const int subsample, - const float subsample_ratio, - const int snap_num) { - - long long count = 0; - for (int i = 0; i < s->nr_local_cells; ++i) { - - const struct cell* c = &s->cells_top[s->local_cells_top[i]]; - count += cell_count_non_inhibited_black_holes(c, subsample, subsample_ratio, - snap_num); +#define IO_COUNT_PARTICLES_TO_WRITE(NAME, TYPE) \ + io_count_##NAME##_to_write(const struct space* s, const int subsample, \ + const float subsample_ratio, \ + const int snap_num) { \ + long long count = 0; \ + for (int i = 0; i < s->nr_local_cells; ++i) { \ + double dummy1[3], dummy2[3]; \ + const struct cell* c = &s->cells_top[s->local_cells_top[i]]; \ + count += cell_count_non_inhibited_##TYPE(c, subsample, subsample_ratio, \ + snap_num, dummy1, dummy2); \ + } \ + return count; \ } - return count; -} - -/** - * @brief Count the number of local non-inhibited neutrinos particles to write. - * - * Takes into account downsampling. - * - * @param s The #space. - * @param subsample Are we subsampling? - * @param subsample_ratio The fraction of particle to keep when subsampling. - * @param snap_num The snapshot number to use as random seed. - */ -long long io_count_neutrinos_to_write(const struct space* s, - const int subsample, - const float subsample_ratio, - const int snap_num) { - long long count = 0; - for (int i = 0; i < s->nr_local_cells; ++i) { - - const struct cell* c = &s->cells_top[s->local_cells_top[i]]; - count += cell_count_non_inhibited_neutrinos(c, subsample, subsample_ratio, - snap_num); - } - return count; -} +long long IO_COUNT_PARTICLES_TO_WRITE(gas, part); +long long IO_COUNT_PARTICLES_TO_WRITE(dark_matter, dark_matter); +long long IO_COUNT_PARTICLES_TO_WRITE(background_dark_matter, + background_dark_matter); +long long IO_COUNT_PARTICLES_TO_WRITE(stars, spart); +long long IO_COUNT_PARTICLES_TO_WRITE(sinks, sink); +long long IO_COUNT_PARTICLES_TO_WRITE(black_holes, bpart); +long long IO_COUNT_PARTICLES_TO_WRITE(neutrinos, neutrinos); #if defined(HAVE_HDF5) @@ -387,29 +183,30 @@ long long io_count_neutrinos_to_write(const struct space* s, #endif /** - * @brief Write a single 1D array to a hdf5 group. + * @brief Write a single rank 1 or rank 2 array to a hdf5 group. * - * This creates a simple Nx1 array with a chunk size of 1024x1. + * This creates a simple Nxdim array with a chunk size of 1024xdim. * The Fletcher-32 filter is applied to the array. * * @param h_grp The open hdf5 group. * @param n The number of elements in the array. + * @param dim The dimensionality of each element. * @param array The data to write. * @param type The type of the data to write. * @param name The name of the array. * @param array_content The name of the parent group (only used for error * messages). */ -void io_write_array(hid_t h_grp, const int n, const void* array, +void io_write_array(hid_t h_grp, const int n, const int dim, const void* array, const enum IO_DATA_TYPE type, const char* name, const char* array_content) { /* Create memory space */ - const hsize_t shape[2] = {(hsize_t)n, 1}; + const hsize_t shape[2] = {(hsize_t)n, dim}; hid_t h_space = H5Screate(H5S_SIMPLE); if (h_space < 0) error("Error while creating data space for %s %s", name, array_content); - hid_t h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + hid_t h_err = H5Sset_extent_simple(h_space, dim > 1 ? 2 : 1, shape, shape); if (h_err < 0) error("Error while changing shape of %s %s data space.", name, array_content); @@ -417,9 +214,9 @@ void io_write_array(hid_t h_grp, const int n, const void* array, /* Dataset type */ hid_t h_type = H5Tcopy(io_hdf5_type(type)); - const hsize_t chunk[2] = {(1024 > n ? n : 1024), 1}; + const hsize_t chunk[2] = {(1024 > n ? n : 1024), dim}; hid_t h_prop = H5Pcreate(H5P_DATASET_CREATE); - h_err = H5Pset_chunk(h_prop, 1, chunk); + h_err = H5Pset_chunk(h_prop, dim > 1 ? 2 : 1, chunk); if (h_err < 0) error("Error while setting chunk shapes of %s %s data space.", name, array_content); @@ -430,6 +227,16 @@ void io_write_array(hid_t h_grp, const int n, const void* array, error("Error while setting check-sum filter on %s %s data space.", name, array_content); + /* Impose SHUFFLE compression */ + h_err = H5Pset_shuffle(h_prop); + if (h_err < 0) + error("Error while setting shuffling options for field '%s'.", name); + + /* Impose GZIP compression */ + h_err = H5Pset_deflate(h_prop, 4); + if (h_err < 0) + error("Error while setting compression options for field '%s'.", name); + /* Write */ hid_t h_data = H5Dcreate(h_grp, name, h_type, h_space, H5P_DEFAULT, h_prop, H5P_DEFAULT); @@ -459,6 +266,8 @@ void io_write_array(hid_t h_grp, const int n, const void* array, * @param global_counts The total number of particles across all nodes. * @param global_offsets The offsets of this node into the global list of * particles. + * @param to_write Whether a given particle type should be written to the cell + * info. * @param numFields The number of fields to write for each particle type. * @param internal_units The internal unit system. * @param snapshot_units The snapshot unit system. @@ -472,6 +281,7 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], const int snap_num, const long long global_counts[swift_type_count], const long long global_offsets[swift_type_count], + const int to_write[swift_type_count], const int num_fields[swift_type_count], const struct unit_system* internal_units, const struct unit_system* snapshot_units) { @@ -500,6 +310,30 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], int* files = NULL; files = (int*)malloc(nr_cells * sizeof(int)); + /* Temporary memory for the min position of particles in the cells */ + double *min_part_pos = NULL, *min_gpart_pos = NULL, + *min_gpart_background_pos = NULL, *min_spart_pos = NULL, + *min_bpart_pos = NULL, *min_sink_pos = NULL, *min_nupart_pos = NULL; + min_part_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + min_gpart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + min_gpart_background_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + min_spart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + min_bpart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + min_sink_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + min_nupart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + + /* Temporary memory for the max position of particles in the cells */ + double *max_part_pos = NULL, *max_gpart_pos = NULL, + *max_gpart_background_pos = NULL, *max_spart_pos = NULL, + *max_bpart_pos = NULL, *max_sink_pos = NULL, *max_nupart_pos = NULL; + max_part_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + max_gpart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + max_gpart_background_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + max_spart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + max_bpart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + max_sink_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + max_nupart_pos = (double*)calloc(3 * nr_cells, sizeof(double)); + /* Count of particles in each cell */ long long *count_part = NULL, *count_gpart = NULL, *count_background_gpart = NULL, *count_spart = NULL, @@ -563,35 +397,44 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], centres[i * 3 + 1] = box_wrap(centres[i * 3 + 1], 0.0, dim[1]); centres[i * 3 + 2] = box_wrap(centres[i * 3 + 2], 0.0, dim[2]); - /* Count real particles that will be written */ - count_part[i] = cell_count_non_inhibited_gas( + /* Count real particles that will be written and collect the min/max + * positions */ + count_part[i] = cell_count_non_inhibited_part( &cells_top[i], subsample[swift_type_gas], - subsample_fraction[swift_type_gas], snap_num); + subsample_fraction[swift_type_gas], snap_num, &min_part_pos[i * 3], + &max_part_pos[i * 3]); count_gpart[i] = cell_count_non_inhibited_dark_matter( &cells_top[i], subsample[swift_type_dark_matter], - subsample_fraction[swift_type_dark_matter], snap_num); + subsample_fraction[swift_type_dark_matter], snap_num, + &min_gpart_pos[i * 3], &max_gpart_pos[i * 3]); count_background_gpart[i] = cell_count_non_inhibited_background_dark_matter( &cells_top[i], subsample[swift_type_dark_matter_background], - subsample_fraction[swift_type_dark_matter_background], snap_num); + subsample_fraction[swift_type_dark_matter_background], snap_num, + &min_gpart_background_pos[i * 3], + &max_gpart_background_pos[i * 3]); - count_spart[i] = cell_count_non_inhibited_stars( + count_spart[i] = cell_count_non_inhibited_spart( &cells_top[i], subsample[swift_type_stars], - subsample_fraction[swift_type_stars], snap_num); + subsample_fraction[swift_type_stars], snap_num, &min_spart_pos[i * 3], + &max_spart_pos[i * 3]); - count_bpart[i] = cell_count_non_inhibited_black_holes( + count_bpart[i] = cell_count_non_inhibited_bpart( &cells_top[i], subsample[swift_type_black_hole], - subsample_fraction[swift_type_black_hole], snap_num); + subsample_fraction[swift_type_black_hole], snap_num, + &min_bpart_pos[i * 3], &max_bpart_pos[i * 3]); - count_sink[i] = cell_count_non_inhibited_sinks( + count_sink[i] = cell_count_non_inhibited_sink( &cells_top[i], subsample[swift_type_sink], - subsample_fraction[swift_type_sink], snap_num); + subsample_fraction[swift_type_sink], snap_num, &min_sink_pos[i * 3], + &max_sink_pos[i * 3]); count_nupart[i] = cell_count_non_inhibited_neutrinos( &cells_top[i], subsample[swift_type_neutrino], - subsample_fraction[swift_type_neutrino], snap_num); + subsample_fraction[swift_type_neutrino], snap_num, + &min_nupart_pos[i * 3], &max_nupart_pos[i * 3]); /* Offsets including the global offset of all particles on this MPI rank * Note that in the distributed case, the global offsets are 0 such that @@ -680,6 +523,36 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], on floating point numbers */ MPI_Allreduce(MPI_IN_PLACE, centres, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + MPI_Allreduce(MPI_IN_PLACE, min_part_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, min_gpart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, min_gpart_background_pos, 3 * nr_cells, + MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, min_spart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, min_bpart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, min_sink_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, min_nupart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + + MPI_Allreduce(MPI_IN_PLACE, max_part_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, max_gpart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, max_gpart_background_pos, 3 * nr_cells, + MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, max_spart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, max_bpart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, max_sink_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, max_nupart_pos, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); #endif /* When writing a single file, only rank 0 writes the meta-data */ @@ -697,6 +570,26 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], centres[i * 3 + 2] *= factor; } + /* Convert the particle envelopes */ + for (int i = 0; i < nr_cells; ++i) { + for (int k = 0; k < 3; ++k) { + min_part_pos[i * 3 + k] *= factor; + max_part_pos[i * 3 + k] *= factor; + min_gpart_pos[i * 3 + k] *= factor; + max_gpart_pos[i * 3 + k] *= factor; + min_gpart_background_pos[i * 3 + k] *= factor; + max_gpart_background_pos[i * 3 + k] *= factor; + min_sink_pos[i * 3 + k] *= factor; + max_sink_pos[i * 3 + k] *= factor; + min_spart_pos[i * 3 + k] *= factor; + max_spart_pos[i * 3 + k] *= factor; + min_bpart_pos[i * 3 + k] *= factor; + max_bpart_pos[i * 3 + k] *= factor; + min_nupart_pos[i * 3 + k] *= factor; + max_nupart_pos[i * 3 + k] *= factor; + } + } + /* Convert the cell widths */ cell_width[0] *= factor; cell_width[1] *= factor; @@ -713,20 +606,8 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], H5Gclose(h_subgrp); /* Write the centres to the group */ - hsize_t shape[2] = {(hsize_t)nr_cells, 3}; - hid_t h_space = H5Screate(H5S_SIMPLE); - if (h_space < 0) error("Error while creating data space for cell centres"); - hid_t h_err = H5Sset_extent_simple(h_space, 2, shape, shape); - if (h_err < 0) - error("Error while changing shape of gas offsets data space."); - hid_t h_data = H5Dcreate(h_grp, "Centres", io_hdf5_type(DOUBLE), h_space, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - if (h_data < 0) error("Error while creating dataspace for gas offsets."); - h_err = H5Dwrite(h_data, io_hdf5_type(DOUBLE), h_space, H5S_ALL, - H5P_DEFAULT, centres); - if (h_err < 0) error("Error while writing centres."); - H5Dclose(h_data); - H5Sclose(h_space); + io_write_array(h_grp, nr_cells, /*dim=*/3, centres, DOUBLE, "Centres", + "Cell centres"); /* Group containing the offsets and counts for each particle type */ hid_t h_grp_offsets = H5Gcreate(h_grp, "OffsetsInFile", H5P_DEFAULT, @@ -737,72 +618,112 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], if (h_grp_files < 0) error("Error while creating filess sub-group"); hid_t h_grp_counts = H5Gcreate(h_grp, "Counts", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + hid_t h_grp_min_pos = + H5Gcreate(h_grp, "MinPositions", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + hid_t h_grp_max_pos = + H5Gcreate(h_grp, "MaxPositions", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp_counts < 0) error("Error while creating counts sub-group"); - if (global_counts[swift_type_gas] > 0 && num_fields[swift_type_gas] > 0) { - io_write_array(h_grp_files, nr_cells, files, INT, "PartType0", "files"); - io_write_array(h_grp_offsets, nr_cells, offset_part, LONGLONG, + if (to_write[swift_type_gas] > 0 && num_fields[swift_type_gas] > 0) { + io_write_array(h_grp_files, nr_cells, /*dim=*/1, files, INT, "PartType0", + "files"); + io_write_array(h_grp_offsets, nr_cells, /*dim=*/1, offset_part, LONGLONG, "PartType0", "offsets"); - io_write_array(h_grp_counts, nr_cells, count_part, LONGLONG, "PartType0", - "counts"); + io_write_array(h_grp_counts, nr_cells, /*dim=*/1, count_part, LONGLONG, + "PartType0", "counts"); + io_write_array(h_grp_min_pos, nr_cells, /*dim=*/3, min_part_pos, DOUBLE, + "PartType0", "min_pos"); + io_write_array(h_grp_max_pos, nr_cells, /*dim=*/3, max_part_pos, DOUBLE, + "PartType0", "max_pos"); } - if (global_counts[swift_type_dark_matter] > 0 && + if (to_write[swift_type_dark_matter] > 0 && num_fields[swift_type_dark_matter] > 0) { - io_write_array(h_grp_files, nr_cells, files, INT, "PartType1", "files"); - io_write_array(h_grp_offsets, nr_cells, offset_gpart, LONGLONG, + io_write_array(h_grp_files, nr_cells, /*dim=*/1, files, INT, "PartType1", + "files"); + io_write_array(h_grp_offsets, nr_cells, /*dim=*/1, offset_gpart, LONGLONG, "PartType1", "offsets"); - io_write_array(h_grp_counts, nr_cells, count_gpart, LONGLONG, "PartType1", - "counts"); + io_write_array(h_grp_counts, nr_cells, /*dim=*/1, count_gpart, LONGLONG, + "PartType1", "counts"); + io_write_array(h_grp_min_pos, nr_cells, /*dim=*/3, min_gpart_pos, DOUBLE, + "PartType1", "min_pos"); + io_write_array(h_grp_max_pos, nr_cells, /*dim=*/3, max_gpart_pos, DOUBLE, + "PartType1", "max_pos"); } - if (global_counts[swift_type_dark_matter_background] > 0 && + if (to_write[swift_type_dark_matter_background] > 0 && num_fields[swift_type_dark_matter_background] > 0) { - io_write_array(h_grp_files, nr_cells, files, INT, "PartType2", "files"); - io_write_array(h_grp_offsets, nr_cells, offset_background_gpart, LONGLONG, - "PartType2", "offsets"); - io_write_array(h_grp_counts, nr_cells, count_background_gpart, LONGLONG, - "PartType2", "counts"); + io_write_array(h_grp_files, nr_cells, /*dim=*/1, files, INT, "PartType2", + "files"); + io_write_array(h_grp_offsets, nr_cells, /*dim=*/1, + offset_background_gpart, LONGLONG, "PartType2", "offsets"); + io_write_array(h_grp_counts, nr_cells, /*dim=*/1, count_background_gpart, + LONGLONG, "PartType2", "counts"); + io_write_array(h_grp_min_pos, nr_cells, /*dim=*/3, + min_gpart_background_pos, DOUBLE, "PartType2", "min_pos"); + io_write_array(h_grp_max_pos, nr_cells, /*dim=*/3, + max_gpart_background_pos, DOUBLE, "PartType2", "max_pos"); } - if (global_counts[swift_type_sink] > 0 && num_fields[swift_type_sink] > 0) { - io_write_array(h_grp_files, nr_cells, files, INT, "PartType3", "files"); - io_write_array(h_grp_offsets, nr_cells, offset_sink, LONGLONG, + if (to_write[swift_type_sink] > 0 && num_fields[swift_type_sink] > 0) { + io_write_array(h_grp_files, nr_cells, /*dim=*/1, files, INT, "PartType3", + "files"); + io_write_array(h_grp_offsets, nr_cells, /*dim=*/1, offset_sink, LONGLONG, "PartType3", "offsets"); - io_write_array(h_grp_counts, nr_cells, count_sink, LONGLONG, "PartType3", - "counts"); + io_write_array(h_grp_counts, nr_cells, /*dim=*/1, count_sink, LONGLONG, + "PartType3", "counts"); + io_write_array(h_grp_min_pos, nr_cells, /*dim=*/3, min_sink_pos, DOUBLE, + "PartType3", "min_pos"); + io_write_array(h_grp_max_pos, nr_cells, /*dim=*/3, max_sink_pos, DOUBLE, + "PartType3", "max_pos"); } - if (global_counts[swift_type_stars] > 0 && - num_fields[swift_type_stars] > 0) { - io_write_array(h_grp_files, nr_cells, files, INT, "PartType4", "files"); - io_write_array(h_grp_offsets, nr_cells, offset_spart, LONGLONG, + if (to_write[swift_type_stars] > 0 && num_fields[swift_type_stars] > 0) { + io_write_array(h_grp_files, nr_cells, /*dim=*/1, files, INT, "PartType4", + "files"); + io_write_array(h_grp_offsets, nr_cells, /*dim=*/1, offset_spart, LONGLONG, "PartType4", "offsets"); - io_write_array(h_grp_counts, nr_cells, count_spart, LONGLONG, "PartType4", - "counts"); + io_write_array(h_grp_counts, nr_cells, /*dim=*/1, count_spart, LONGLONG, + "PartType4", "counts"); + io_write_array(h_grp_min_pos, nr_cells, /*dim=*/3, min_spart_pos, DOUBLE, + "PartType4", "min_pos"); + io_write_array(h_grp_max_pos, nr_cells, /*dim=*/3, max_spart_pos, DOUBLE, + "PartType4", "max_pos"); } - if (global_counts[swift_type_black_hole] > 0 && + if (to_write[swift_type_black_hole] > 0 && num_fields[swift_type_black_hole] > 0) { - io_write_array(h_grp_files, nr_cells, files, INT, "PartType5", "files"); - io_write_array(h_grp_offsets, nr_cells, offset_bpart, LONGLONG, + io_write_array(h_grp_files, nr_cells, /*dim=*/1, files, INT, "PartType5", + "files"); + io_write_array(h_grp_offsets, nr_cells, /*dim=*/1, offset_bpart, LONGLONG, "PartType5", "offsets"); - io_write_array(h_grp_counts, nr_cells, count_bpart, LONGLONG, "PartType5", - "counts"); + io_write_array(h_grp_counts, nr_cells, /*dim=*/1, count_bpart, LONGLONG, + "PartType5", "counts"); + io_write_array(h_grp_min_pos, nr_cells, /*dim=*/3, min_bpart_pos, DOUBLE, + "PartType5", "min_pos"); + io_write_array(h_grp_max_pos, nr_cells, /*dim=*/3, max_bpart_pos, DOUBLE, + "PartType5", "max_pos"); } - if (global_counts[swift_type_neutrino] > 0 && + if (to_write[swift_type_neutrino] > 0 && num_fields[swift_type_neutrino] > 0) { - io_write_array(h_grp_files, nr_cells, files, INT, "PartType6", "files"); - io_write_array(h_grp_offsets, nr_cells, offset_nupart, LONGLONG, - "PartType6", "offsets"); - io_write_array(h_grp_counts, nr_cells, count_nupart, LONGLONG, + io_write_array(h_grp_files, nr_cells, /*dim=*/1, files, INT, "PartType6", + "files"); + io_write_array(h_grp_offsets, nr_cells, /*dim=*/1, offset_nupart, + LONGLONG, "PartType6", "offsets"); + io_write_array(h_grp_counts, nr_cells, /*dim=*/1, count_nupart, LONGLONG, "PartType6", "counts"); + io_write_array(h_grp_min_pos, nr_cells, /*dim=*/3, min_nupart_pos, DOUBLE, + "PartType6", "min_pos"); + io_write_array(h_grp_max_pos, nr_cells, /*dim=*/3, max_nupart_pos, DOUBLE, + "PartType6", "max_pos"); } H5Gclose(h_grp_offsets); H5Gclose(h_grp_files); H5Gclose(h_grp_counts); + H5Gclose(h_grp_min_pos); + H5Gclose(h_grp_max_pos); } /* Free everything we allocated */ @@ -822,6 +743,20 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3], free(offset_bpart); free(offset_sink); free(offset_nupart); + free(min_part_pos); + free(min_gpart_pos); + free(min_gpart_background_pos); + free(min_spart_pos); + free(min_bpart_pos); + free(min_sink_pos); + free(min_nupart_pos); + free(max_part_pos); + free(max_gpart_pos); + free(max_gpart_background_pos); + free(max_spart_pos); + free(max_bpart_pos); + free(max_sink_pos); + free(max_nupart_pos); } #endif /* HAVE_HDF5 */ diff --git a/src/common_io_copy.c b/src/common_io_copy.c index e7e9f157726591a8e19c15c51df947e5617132ca..2be7cb6e4aacfc6796d6592ae1c8f8abfaf36ac2 100644 --- a/src/common_io_copy.c +++ b/src/common_io_copy.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2017 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "common_io.h" diff --git a/src/common_io_fields.c b/src/common_io_fields.c index fe29638824d6e693bc4ca4e0d3bd1a10b8ce848a..c85bb674759e9742a6fa5937a424088d51717d77 100644 --- a/src/common_io_fields.c +++ b/src/common_io_fields.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2017 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "common_io.h" diff --git a/src/const.h b/src/const.h index b1e7e777e52d13785da8eeb7e4ca13019234ebef..429fe2275a518df5c57402ab1f7276e2df39be1e 100644 --- a/src/const.h +++ b/src/const.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (ptcedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -102,4 +102,8 @@ #define SOURCETERMS_NONE //#define SOURCETERMS_SN_FEEDBACK +/* GRACKLE doesn't really like exact zeroes, so use something + * comparatively small instead. */ +#define RT_GEAR_TINY_MASS_FRACTION 1.e-20 + #endif /* SWIFT_CONST_H */ diff --git a/src/cooling.c b/src/cooling.c index 563485e97e9f3689ba06013f146325443d0e30b9..665f0a8360030b23c1a2f2f6147997399b2c0d58 100644 --- a/src/cooling.c +++ b/src/cooling.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "cooling.h" diff --git a/src/cooling.h b/src/cooling.h index 601f730fb06cca3f4f7f05452d0799b24aa1e964..747de00f6fff0bd7795ee0164331a466726e53d2 100644 --- a/src/cooling.h +++ b/src/cooling.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "parser.h" diff --git a/src/cooling/COLIBRE/cooling.c b/src/cooling/COLIBRE/cooling.c index bfec6a64fbe241a4eabf7c81a684273a039e6d85..5adfe42ef668dc0090429431bbfcea040b119ba8 100644 --- a/src/cooling/COLIBRE/cooling.c +++ b/src/cooling/COLIBRE/cooling.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -22,7 +22,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/cooling/COLIBRE/cooling.h b/src/cooling/COLIBRE/cooling.h index 09ac296aad23f01494323f4ba1fda2d14a56dfa8..1cc12cab7204d0059e662ab149612e933077b3e9 100644 --- a/src/cooling/COLIBRE/cooling.h +++ b/src/cooling/COLIBRE/cooling.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 diff --git a/src/cooling/COLIBRE/cooling_debug.h b/src/cooling/COLIBRE/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..4e4beef77926345da2d7610c93c9d525566ddc18 --- /dev/null +++ b/src/cooling/COLIBRE/cooling_debug.h @@ -0,0 +1,35 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_COLIBRE_DEBUG_H +#define SWIFT_COOLING_COLIBRE_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld] cooling_part_data:", p->id); + warning("[PID%lld] subgrid_temp=%.3e, subgrid_dens=%.3e", p->id, + p->cooling_data.subgrid_temp, p->cooling_data.subgrid_dens); + if (xp != NULL) { + warning("[PID%lld] cooling_xpart_data:", p->id); + warning("[PID%lld] radiated_energy=%.3e", p->id, + xp->cooling_data.radiated_energy); + } +} + +#endif /* SWIFT_COOLING_COLIBRE_DEBUG_H */ diff --git a/src/cooling/COLIBRE/cooling_io.h b/src/cooling/COLIBRE/cooling_io.h index 28206a05ea719c1ee6c24166ff556653f895594a..ddeb56b851f81ed062768134970762746b577e2f 100644 --- a/src/cooling/COLIBRE/cooling_io.h +++ b/src/cooling/COLIBRE/cooling_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -20,7 +20,7 @@ #define SWIFT_COOLING_COLIBRE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" diff --git a/src/cooling/COLIBRE/cooling_properties.h b/src/cooling/COLIBRE/cooling_properties.h index cd67d2d0002bf81760964977e9088f49952db4b6..4dd44a7e925717acf13f0064677c8ed4d939af42 100644 --- a/src/cooling/COLIBRE/cooling_properties.h +++ b/src/cooling/COLIBRE/cooling_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/COLIBRE/cooling_rates.h b/src/cooling/COLIBRE/cooling_rates.h index 16c419dc42488f418ba81620c7d8b5af400abee3..c70b02d9c57f265df289e5263241130c36e2fe66 100644 --- a/src/cooling/COLIBRE/cooling_rates.h +++ b/src/cooling/COLIBRE/cooling_rates.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -20,7 +20,7 @@ #define SWIFT_COLIBRE_COOLING_RATES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "chemistry_struct.h" @@ -111,8 +111,9 @@ __attribute__((always_inline)) INLINE static float abundance_ratio_to_solar( /* Convert mass fractions to abundances (nx/nH) and compute metal mass */ float totmass = 0., metalmass = 0.; - for (enum colibre_cooling_element elem = element_H; elem < element_OA; - elem++) { + for (int elem_nr = element_H; elem_nr < element_OA; elem_nr++) { + + enum colibre_cooling_element elem = (enum colibre_cooling_element)elem_nr; /* Normal elements: Get the abundance from the particle carried arrays */ if ((elem != element_S) && (elem != element_Ca)) { diff --git a/src/cooling/COLIBRE/cooling_struct.h b/src/cooling/COLIBRE/cooling_struct.h index 36e6604a3cbbd7b64ed20e0b8c56a22006f96fb6..a151ea505c5abaadf69b1272f920ae78183fed79 100644 --- a/src/cooling/COLIBRE/cooling_struct.h +++ b/src/cooling/COLIBRE/cooling_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/COLIBRE/cooling_subgrid.h b/src/cooling/COLIBRE/cooling_subgrid.h index 86868a272e5ec07a4076b02ca21fd51efe24b296..ed50efebd5a3daf36a1a5bd4e338cdede231efd7 100644 --- a/src/cooling/COLIBRE/cooling_subgrid.h +++ b/src/cooling/COLIBRE/cooling_subgrid.h @@ -20,7 +20,7 @@ #define SWIFT_COLIBRE_COOLING_SUBGRID_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "cooling.h" diff --git a/src/cooling/COLIBRE/cooling_tables.c b/src/cooling/COLIBRE/cooling_tables.c index a61ae4a7de22a73a55e804cbb050b8323d790632..bce5946f99fa70b18f56e5ab4fffa97821bb1fb3 100644 --- a/src/cooling/COLIBRE/cooling_tables.c +++ b/src/cooling/COLIBRE/cooling_tables.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -23,7 +23,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This file's header */ #include "cooling_tables.h" diff --git a/src/cooling/COLIBRE/cooling_tables.h b/src/cooling/COLIBRE/cooling_tables.h index 5d024e65fa66e450100f9b23a9c7ecafe2d6c710..3eb70ee999645d2844a151c21aab7ac6a2909180 100644 --- a/src/cooling/COLIBRE/cooling_tables.h +++ b/src/cooling/COLIBRE/cooling_tables.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "config.h" +#include <config.h> /*! Number of different bins along the temperature axis of the tables */ #define colibre_cooling_N_temperature 86 diff --git a/src/cooling/COLIBRE/interpolate.h b/src/cooling/COLIBRE/interpolate.h index 039b394ff5653c90567a094236b9dee2e58423fc..4f0c8c7b887261f14ec5ffd43aa1f1ea3a3b00cf 100644 --- a/src/cooling/COLIBRE/interpolate.h +++ b/src/cooling/COLIBRE/interpolate.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "align.h" diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c index ed1ca14627c639074f5327fc9c364cb167d4d648..aaa88c629871acf49da9799d5a4ab0e81d549cf7 100644 --- a/src/cooling/EAGLE/cooling.c +++ b/src/cooling/EAGLE/cooling.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -22,7 +22,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -667,7 +667,7 @@ float cooling_get_temperature( * @brief Compute the electron pressure of a #part based on the cooling * function. * - * There are no subgrid properties in this model, we return 0. + * Does not exist in this model. We return 0. * * @param phys_const #phys_const data structure. * @param hydro_props The properties of the hydro scheme. diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h index 3006076d27c45dc1c576a2cdcd206c3c05083b5a..40b1fe7a5163d01e3b237121eed3985f2c1e2f4a 100644 --- a/src/cooling/EAGLE/cooling.h +++ b/src/cooling/EAGLE/cooling.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 diff --git a/src/cooling/EAGLE/cooling_debug.h b/src/cooling/EAGLE/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..e36b50089ef83dc0802fd1320764cd601b463731 --- /dev/null +++ b/src/cooling/EAGLE/cooling_debug.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_EAGLE_DEBUG_H +#define SWIFT_COOLING_EAGLE_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] cooling_xpart_data:", p->id); + warning("[PID%lld] radiated_energy = %.3e", p->id, + xp->cooling_data.radiated_energy); + } +} + +#endif /* SWIFT_COOLING_EAGLE_DEBUG_H */ diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h index 2d3bcbfdd7c84da2d0127b6ab8fd68ad4b8d0785..46a2d3ef14996520888784824ef7cdbaa32ed48c 100644 --- a/src/cooling/EAGLE/cooling_io.h +++ b/src/cooling/EAGLE/cooling_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -20,7 +20,7 @@ #define SWIFT_COOLING_EAGLE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" diff --git a/src/cooling/EAGLE/cooling_properties.h b/src/cooling/EAGLE/cooling_properties.h index 0c9e4039dfa2f9729c70ccb422648be6cd5161ee..5d8bb496724667a504b94ea35dc0078768dba09e 100644 --- a/src/cooling/EAGLE/cooling_properties.h +++ b/src/cooling/EAGLE/cooling_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/EAGLE/cooling_rates.h b/src/cooling/EAGLE/cooling_rates.h index 39978c9bfa8cf2fbea25e8856c2939a8d6ee69e4..132badb8a25696f72b8211052294605978e12ed9 100644 --- a/src/cooling/EAGLE/cooling_rates.h +++ b/src/cooling/EAGLE/cooling_rates.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -21,7 +21,7 @@ #ifndef SWIFT_EAGLE_COOLING_RATES_H #define SWIFT_EAGLE_COOLING_RATES_H -#include "../config.h" +#include <config.h> /* Local includes. */ #include "cooling_tables.h" diff --git a/src/cooling/EAGLE/cooling_struct.h b/src/cooling/EAGLE/cooling_struct.h index 2770eadefcdd80ba1a1f1c009bbb250a10f601c4..6c747eeca8957eece4c5ee22673e60aefd881acd 100644 --- a/src/cooling/EAGLE/cooling_struct.h +++ b/src/cooling/EAGLE/cooling_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/EAGLE/cooling_tables.c b/src/cooling/EAGLE/cooling_tables.c index 8890870709278492f05c23af2257f5a7f580900a..0b32ce20d9b557f0b9873fbe52850a45dd3f200f 100644 --- a/src/cooling/EAGLE/cooling_tables.c +++ b/src/cooling/EAGLE/cooling_tables.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -23,8 +23,9 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ #include <hdf5.h> #include <math.h> #include <stdlib.h> diff --git a/src/cooling/EAGLE/cooling_tables.h b/src/cooling/EAGLE/cooling_tables.h index 3890a91a8fc78f4088d4774469f22d8df02783b6..c492fa962d4401c06a5be19496559323e3a68691 100644 --- a/src/cooling/EAGLE/cooling_tables.h +++ b/src/cooling/EAGLE/cooling_tables.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -25,7 +25,9 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "cooling_struct.h" /*! Number of different bins along the redhsift axis of the tables */ diff --git a/src/cooling/EAGLE/interpolate.h b/src/cooling/EAGLE/interpolate.h index 78955e7fd7409d3501d16ae50d4a2248cb1cff0b..080c881525e5584a5c76964f7adce0c27b9f3ae8 100644 --- a/src/cooling/EAGLE/interpolate.h +++ b/src/cooling/EAGLE/interpolate.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "align.h" diff --git a/src/cooling/QLA/cooling.c b/src/cooling/QLA/cooling.c index ca9eb6770c55f4e641c5fa835c040b56e813a8d3..60e50ce2f26f3caf1b24d5cb2412d06aa58ab357 100644 --- a/src/cooling/QLA/cooling.c +++ b/src/cooling/QLA/cooling.c @@ -22,7 +22,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/cooling/QLA/cooling_debug.h b/src/cooling/QLA/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..743382956783357b5fb62e2a238e6eb53f7bc38a --- /dev/null +++ b/src/cooling/QLA/cooling_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_QLA_DEBUG_H +#define SWIFT_COOLING_QLA_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_COOLING_QLA_DEBUG_H */ diff --git a/src/cooling/QLA/cooling_io.h b/src/cooling/QLA/cooling_io.h index 7c6ea88539977fd720b412f085eb57ca9b5db701..5f4352f5c6a75d800763fabfeecb13beb10ed612 100644 --- a/src/cooling/QLA/cooling_io.h +++ b/src/cooling/QLA/cooling_io.h @@ -20,7 +20,7 @@ #define SWIFT_COOLING_QLA_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" diff --git a/src/cooling/QLA/cooling_rates.h b/src/cooling/QLA/cooling_rates.h index b668694be66b7e551e8c4c0e5fb0016574021b94..0a2d544ae69a47bf8ae94d12801b18bd9e24c63d 100644 --- a/src/cooling/QLA/cooling_rates.h +++ b/src/cooling/QLA/cooling_rates.h @@ -20,7 +20,7 @@ #define SWIFT_QLA_COOLING_RATES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "chemistry_struct.h" diff --git a/src/cooling/QLA/cooling_tables.c b/src/cooling/QLA/cooling_tables.c index ff84e9e1926311bf9e069655adf050c480522813..728df70232a5d1f44b45b0c969463b1307986d95 100644 --- a/src/cooling/QLA/cooling_tables.c +++ b/src/cooling/QLA/cooling_tables.c @@ -23,7 +23,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This file's header */ #include "cooling_tables.h" diff --git a/src/cooling/QLA/cooling_tables.h b/src/cooling/QLA/cooling_tables.h index 06610abcefbf8ee15621a142bbaec39af59e57f1..6f8121ce1b9d03e07e5e3f7243f3ef0987734a31 100644 --- a/src/cooling/QLA/cooling_tables.h +++ b/src/cooling/QLA/cooling_tables.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "config.h" +#include <config.h> /*! Number of different bins along the temperature axis of the tables */ #define qla_cooling_N_temperature 86 diff --git a/src/cooling/QLA/interpolate.h b/src/cooling/QLA/interpolate.h index b1cacae696393cdfc84e72a7a811362ab4fcc9e4..2444e31fb4becf824c1f4ec3caef0469414f3a7e 100644 --- a/src/cooling/QLA/interpolate.h +++ b/src/cooling/QLA/interpolate.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "align.h" diff --git a/src/cooling/QLA_EAGLE/cooling.c b/src/cooling/QLA_EAGLE/cooling.c index bab618ebf4471454d16f4302bc7b2e2b1128e336..eacc7d02c5af41de9d84ea45acaf0d52e60c2d75 100644 --- a/src/cooling/QLA_EAGLE/cooling.c +++ b/src/cooling/QLA_EAGLE/cooling.c @@ -22,7 +22,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/cooling/QLA_EAGLE/cooling_debug.h b/src/cooling/QLA_EAGLE/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..19089c70f632ebad60e109f74d0369531b7157ed --- /dev/null +++ b/src/cooling/QLA_EAGLE/cooling_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_QLA_EAGLE_DEBUG_H +#define SWIFT_COOLING_QLA_EAGLE_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_COOLING_QLA_EAGLE_DEBUG_H */ diff --git a/src/cooling/QLA_EAGLE/cooling_io.h b/src/cooling/QLA_EAGLE/cooling_io.h index 171d0e742a9d5d2c57a44b39506a450d62b3fefc..fdaae31ede74213ef5feae81de5a8db3cef14e5a 100644 --- a/src/cooling/QLA_EAGLE/cooling_io.h +++ b/src/cooling/QLA_EAGLE/cooling_io.h @@ -20,7 +20,7 @@ #define SWIFT_COOLING_QLA_EAGLE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" diff --git a/src/cooling/QLA_EAGLE/cooling_rates.h b/src/cooling/QLA_EAGLE/cooling_rates.h index f89e14eaa70c5936d8bdd2f3290ea0f4ab4e9868..c71fa4b31a5941a42fa0eafd00fbe15be00f3d48 100644 --- a/src/cooling/QLA_EAGLE/cooling_rates.h +++ b/src/cooling/QLA_EAGLE/cooling_rates.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -21,7 +21,7 @@ #ifndef SWIFT_QLA_EAGLE_COOLING_RATES_H #define SWIFT_QLA_EAGLE_COOLING_RATES_H -#include "../config.h" +#include <config.h> /* Local includes. */ #include "cooling_tables.h" diff --git a/src/cooling/QLA_EAGLE/cooling_tables.c b/src/cooling/QLA_EAGLE/cooling_tables.c index 1e97822d0bcdd9ffe08e350623eb2027a830c57e..5ee735a0f2d4d7dd6177ce6fab0b41875a8386bc 100644 --- a/src/cooling/QLA_EAGLE/cooling_tables.c +++ b/src/cooling/QLA_EAGLE/cooling_tables.c @@ -23,8 +23,9 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ #include <hdf5.h> #include <math.h> #include <stdlib.h> diff --git a/src/cooling/QLA_EAGLE/cooling_tables.h b/src/cooling/QLA_EAGLE/cooling_tables.h index 2f406d610c0d00980ba39e02cf08ef75556d287f..3759088c74055fde8fbf1e2ce9a0458da1968386 100644 --- a/src/cooling/QLA_EAGLE/cooling_tables.h +++ b/src/cooling/QLA_EAGLE/cooling_tables.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "config.h" +#include <config.h> /*! Number of different bins along the redhsift axis of the tables */ #define qla_eagle_cooling_N_redshifts 49 diff --git a/src/cooling/QLA_EAGLE/interpolate.h b/src/cooling/QLA_EAGLE/interpolate.h index 98b49405f830e8531e1fa4c048391a1bc90adf72..d8811beed11ed402be764d95bfebf29a26d640e7 100644 --- a/src/cooling/QLA_EAGLE/interpolate.h +++ b/src/cooling/QLA_EAGLE/interpolate.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "align.h" diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h index c5b4f4ee28cd12c1267b4298c670e4ae0ad8bef6..4076ac07ce6cbc434510da3b47469a05668f2c9e 100644 --- a/src/cooling/const_du/cooling.h +++ b/src/cooling/const_du/cooling.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Richard Bower (r.g.bower@durham.ac.uk) * Stefan Arridge (stefan.arridge@durham.ac.uk) * @@ -34,7 +34,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -148,6 +148,53 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( return cooling->cooling_tstep_mult * internal_energy / fabsf(cooling_rate); } +/** + * @brief Compute the electron pressure of a #part based on the cooling + * function. + * + * Does not exist in this model. We return 0. + * + * @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. + */ +__attribute__((always_inline)) INLINE static double +cooling_get_electron_pressure(const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + const struct unit_system* us, + const struct cosmology* cosmo, + const struct cooling_function_data* cooling, + const struct part* p, const struct xpart* xp) { + return 0; +} + +/** + * @brief Compute the y-Compton contribution of a #part based on the cooling + * function. + * + * Does not exist in this model. We return 0. + * + * @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. + */ +__attribute__((always_inline)) INLINE static double cooling_get_ycompton( + const struct phys_const* phys_const, const struct hydro_props* hydro_props, + const struct unit_system* us, const struct cosmology* cosmo, + const struct cooling_function_data* cooling, const struct part* p, + const struct xpart* xp) { + error("This cooling model does not compute Compton Y!"); + return 0.; +} + /** * @brief Sets the cooling properties of the (x-)particles to a valid start * state. diff --git a/src/cooling/const_du/cooling_debug.h b/src/cooling/const_du/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..29375d87cbf5a3148f1d2751afa48637a85d9e3d --- /dev/null +++ b/src/cooling/const_du/cooling_debug.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_CONST_DU_DEBUG_H +#define SWIFT_COOLING_CONST_DU_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] cooling_xpart_data:", p->id); + warning("[PID%lld] radiated_energy = %.3e", p->id, + xp->cooling_data.radiated_energy); + } +} + +#endif /* SWIFT_COOLING_CONST_DU_DEBUG_H */ diff --git a/src/cooling/const_du/cooling_io.h b/src/cooling/const_du/cooling_io.h index 9763a52339e689e38df7c6a9e80982d260aeaac6..2bc09ec7c16aa073c7b7ba03a04f290467f03307 100644 --- a/src/cooling/const_du/cooling_io.h +++ b/src/cooling/const_du/cooling_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Richard Bower (r.g.bower@durham.ac.uk) * Stefan Arridge (stefan.arridge@durham.ac.uk) * @@ -31,7 +31,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" diff --git a/src/cooling/const_du/cooling_properties.h b/src/cooling/const_du/cooling_properties.h index b022da36a2e3ef1b97f10f6a8048fdff37c5ff32..80860f82413e74dc385292f95a166fdd532e6f47 100644 --- a/src/cooling/const_du/cooling_properties.h +++ b/src/cooling/const_du/cooling_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Richard Bower (r.g.bower@durham.ac.uk) * Stefan Arridge (stefan.arridge@durham.ac.uk) * diff --git a/src/cooling/const_du/cooling_struct.h b/src/cooling/const_du/cooling_struct.h index cb5f0da8cc7450cc725c0ec85724679714944e5c..ac1a43b036d9225624a698d3d7bf6c05133dfcad 100644 --- a/src/cooling/const_du/cooling_struct.h +++ b/src/cooling/const_du/cooling_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Richard Bower (r.g.bower@durham.ac.uk) * Stefan Arridge (stefan.arridge@durham.ac.uk) * diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h index 7659177ef797bc2ff3294c8c3af338e647daaea8..64a2d35040e25032961d906c38d02e0f9325ef2f 100644 --- a/src/cooling/const_lambda/cooling.h +++ b/src/cooling/const_lambda/cooling.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Stefan Arridge (stefan.arridge@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -29,7 +29,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -178,12 +178,24 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( total_du_dt = -u_old_com / ((2.5f + 0.0001f) * dt_therm); } - /* Update the internal energy time derivative */ - hydro_set_comoving_internal_energy_dt(p, total_du_dt); + if (cooling->rapid_cooling) { + const float u_new_com = u_old_com + total_du_dt * dt_therm; + const float u_new_phys = u_new_com * cosmo->a_factor_internal_energy; + hydro_set_physical_internal_energy(p, xp, cosmo, u_new_phys); + hydro_set_drifted_physical_internal_energy(p, cosmo, u_new_phys); + hydro_set_physical_internal_energy_dt(p, cosmo, 0.); + } else { + /* Update the internal energy time derivative */ + hydro_set_comoving_internal_energy_dt(p, total_du_dt); + } + const float actual_cooling_du_dt = total_du_dt - hydro_du_dt_com; + const float actual_cooling_du_dt_physical = actual_cooling_du_dt / cosmo->a / + cosmo->a * + cosmo->a_factor_internal_energy; /* Store the radiated energy (assuming dt will not change) */ xp->cooling_data.radiated_energy += - -hydro_get_mass(p) * cooling_du_dt_physical * dt; + -hydro_get_mass(p) * actual_cooling_du_dt_physical * dt; } /** @@ -228,6 +240,53 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( return cooling->cooling_tstep_mult * u / fabsf(cooling_du_dt); } +/** + * @brief Compute the electron pressure of a #part based on the cooling + * function. + * + * Does not exist in this model. We return 0. + * + * @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. + */ +__attribute__((always_inline)) INLINE static double +cooling_get_electron_pressure(const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + const struct unit_system* us, + const struct cosmology* cosmo, + const struct cooling_function_data* cooling, + const struct part* p, const struct xpart* xp) { + return 0; +} + +/** + * @brief Compute the y-Compton contribution of a #part based on the cooling + * function. + * + * Does not exist in this model. We return 0. + * + * @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. + */ +__attribute__((always_inline)) INLINE static double cooling_get_ycompton( + const struct phys_const* phys_const, const struct hydro_props* hydro_props, + const struct unit_system* us, const struct cosmology* cosmo, + const struct cooling_function_data* cooling, const struct part* p, + const struct xpart* xp) { + error("This cooling model does not compute Compton Y!"); + return 0.; +} + /** * @brief Sets the cooling properties of the (x-)particles to a valid start * state. @@ -368,6 +427,8 @@ static INLINE void cooling_init_backend(struct swift_params* parameter_file, parser_get_param_double(parameter_file, "LambdaCooling:lambda_nH2_cgs"); cooling->cooling_tstep_mult = parser_get_opt_param_float( parameter_file, "LambdaCooling:cooling_tstep_mult", FLT_MAX); + cooling->rapid_cooling = parser_get_opt_param_int( + parameter_file, "LambdaCooling:rapid_cooling", 0); /* Some useful conversion values */ cooling->conv_factor_density_to_cgs = @@ -416,6 +477,12 @@ static INLINE void cooling_print_backend( else message("Cooling function time-step size limited to %f of u/(du/dt)", cooling->cooling_tstep_mult); + + if (cooling->rapid_cooling) { + message("Using rapid cooling"); + } else { + message("Using normal cooling"); + } } /** diff --git a/src/cooling/const_lambda/cooling_debug.h b/src/cooling/const_lambda/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..c0ff24b6a2ecf9855f058c342919038c65b7284a --- /dev/null +++ b/src/cooling/const_lambda/cooling_debug.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_CONST_LAMBDA_DEBUG_H +#define SWIFT_COOLING_CONST_LAMBDA_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] cooling_xpart_data:", p->id); + warning("[PID%lld] radiated_energy = %.3e", p->id, + xp->cooling_data.radiated_energy); + } +} + +#endif /* SWIFT_COOLING_CONST_LAMBDA_DEBUG_H */ diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h index c80afd7a42e76aeee5348a4452e0668434cd051d..9494731d047b4f1b7b03d03e85d128c286351ae9 100644 --- a/src/cooling/const_lambda/cooling_io.h +++ b/src/cooling/const_lambda/cooling_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Stefan Arridge (stefan.arridge@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -29,7 +29,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" @@ -51,6 +51,7 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( io_write_attribute_s(h_grp, "Cooling Model", "Constant Lambda"); io_write_attribute_d(h_grp, "Lambda/n_H^2 [cgs]", cooling->lambda_nH2_cgs); + io_write_attribute_i(h_grp, "Rapid cooling", cooling->rapid_cooling); } #endif @@ -76,7 +77,7 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( struct io_props* list) { list[0] = io_make_output_field_convert_part( - "Temperature", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, + "Temperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, convert_part_T, "Temperatures of the gas particles"); list[1] = io_make_output_field( diff --git a/src/cooling/const_lambda/cooling_properties.h b/src/cooling/const_lambda/cooling_properties.h index d7241109625a12dd8a48510a21e44dce949f41ed..ecb5c9c8e25d41a1ada04727ad403d6db5bf3ce4 100644 --- a/src/cooling/const_lambda/cooling_properties.h +++ b/src/cooling/const_lambda/cooling_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Stefan Arridge (stefan.arridge@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -48,6 +48,9 @@ struct cooling_function_data { /*! Constant multiplication factor for time-step criterion */ float cooling_tstep_mult; + + /*! Use rapid cooling? */ + int rapid_cooling; }; #endif /* SWIFT_COOLING_PROPERTIES_CONST_LAMBDA_H */ diff --git a/src/cooling/const_lambda/cooling_struct.h b/src/cooling/const_lambda/cooling_struct.h index d32039f35fcfee8a4a49bbfc9669a060c9e32243..e3de964ad9c205f0071a7c5a1a1456ba96d5e8a5 100644 --- a/src/cooling/const_lambda/cooling_struct.h +++ b/src/cooling/const_lambda/cooling_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Stefan Arridge (stefan.arridge@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify diff --git a/src/cooling/grackle/cooling.c b/src/cooling/grackle/cooling.c index a0bc0e3c91040697df062496f3b085c5e14722e4..48e2072a8f23d7e95cddccd8fa9cfc455479eaea 100644 --- a/src/cooling/grackle/cooling.c +++ b/src/cooling/grackle/cooling.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -21,7 +21,7 @@ * @brief Cooling using the GRACKLE 3.1.1 library. */ -#include "../config.h" +#include <config.h> /* Include header */ #include "cooling.h" @@ -159,6 +159,8 @@ void cooling_compute_equilibrium( const struct cooling_function_data* restrict cooling, const struct part* restrict p, struct xpart* restrict xp) { + /* TODO: this can fail spectacularly and needs to be replaced. */ + /* get temporary data */ struct part p_tmp = *p; struct cooling_function_data cooling_tmp = *cooling; @@ -227,9 +229,9 @@ void cooling_first_init_part(const struct phys_const* restrict phys_const, /* primordial chemistry >= 1 */ xp->cooling_data.HI_frac = zero; xp->cooling_data.HII_frac = grackle_data->HydrogenFractionByMass; - xp->cooling_data.HeI_frac = 1. - grackle_data->HydrogenFractionByMass; + xp->cooling_data.HeI_frac = zero; xp->cooling_data.HeII_frac = zero; - xp->cooling_data.HeIII_frac = zero; + xp->cooling_data.HeIII_frac = 1. - grackle_data->HydrogenFractionByMass; xp->cooling_data.e_frac = xp->cooling_data.HII_frac + 0.25 * xp->cooling_data.HeII_frac + 0.5 * xp->cooling_data.HeIII_frac; @@ -251,6 +253,7 @@ void cooling_first_init_part(const struct phys_const* restrict phys_const, #endif // MODE >= 3 #if COOLING_GRACKLE_MODE > 0 + /* TODO: this can fail spectacularly and needs to be replaced. */ cooling_compute_equilibrium(phys_const, us, hydro_props, cosmo, cooling, p, xp); #endif @@ -308,33 +311,35 @@ void cooling_print_backend(const struct cooling_function_data* cooling) { */ #if COOLING_GRACKLE_MODE > 0 void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { + struct xpart* xp, gr_float rho, + gr_float species_densities[12]) { /* HI */ - xp->cooling_data.HI_frac *= rho; - data->HI_density = &xp->cooling_data.HI_frac; + species_densities[0] = xp->cooling_data.HI_frac * rho; + data->HI_density = &species_densities[0]; /* HII */ - xp->cooling_data.HII_frac *= rho; - data->HII_density = &xp->cooling_data.HII_frac; + species_densities[1] = xp->cooling_data.HII_frac * rho; + data->HII_density = &species_densities[1]; /* HeI */ - xp->cooling_data.HeI_frac *= rho; - data->HeI_density = &xp->cooling_data.HeI_frac; + species_densities[2] = xp->cooling_data.HeI_frac * rho; + data->HeI_density = &species_densities[2]; /* HeII */ - xp->cooling_data.HeII_frac *= rho; - data->HeII_density = &xp->cooling_data.HeII_frac; + species_densities[3] = xp->cooling_data.HeII_frac * rho; + data->HeII_density = &species_densities[3]; /* HeIII */ - xp->cooling_data.HeIII_frac *= rho; - data->HeIII_density = &xp->cooling_data.HeIII_frac; + species_densities[4] = xp->cooling_data.HeIII_frac * rho; + data->HeIII_density = &species_densities[4]; /* HeII */ - xp->cooling_data.e_frac *= rho; - data->e_density = &xp->cooling_data.e_frac; + species_densities[5] = xp->cooling_data.e_frac * rho; + data->e_density = &species_densities[5]; } #else void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { + struct xpart* xp, gr_float rho, + gr_float species_densities[12]) { data->HI_density = NULL; data->HII_density = NULL; data->HeI_density = NULL; @@ -354,22 +359,24 @@ void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, */ #if COOLING_GRACKLE_MODE > 1 void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { + struct xpart* xp, gr_float rho, + gr_float species_densities[12]) { /* HM */ - xp->cooling_data.HM_frac *= rho; - data->HM_density = &xp->cooling_data.HM_frac; + species_densities[6] = xp->cooling_data.HM_frac * rho; + data->HM_density = &species_densities[6]; /* H2I */ - xp->cooling_data.H2I_frac *= rho; - data->H2I_density = &xp->cooling_data.H2I_frac; + species_densities[7] = xp->cooling_data.H2I_frac * rho; + data->H2I_density = &species_densities[7]; /* H2II */ - xp->cooling_data.H2II_frac *= rho; - data->H2II_density = &xp->cooling_data.H2II_frac; + species_densities[8] = xp->cooling_data.H2II_frac * rho; + data->H2II_density = &species_densities[8]; } #else void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { + struct xpart* xp, gr_float rho, + gr_float species_densities[12]) { data->HM_density = NULL; data->H2I_density = NULL; data->H2II_density = NULL; @@ -386,22 +393,24 @@ void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, */ #if COOLING_GRACKLE_MODE > 2 void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { + struct xpart* xp, gr_float rho, + gr_float species_densities[12]) { /* DI */ - xp->cooling_data.DI_frac *= rho; - data->DI_density = &xp->cooling_data.DI_frac; + species_densities[9] = xp->cooling_data.DI_frac * rho; + data->DI_density = &species_densities[9]; /* DII */ - xp->cooling_data.DII_frac *= rho; - data->DII_density = &xp->cooling_data.DII_frac; + species_densities[10] = xp->cooling_data.DII_frac * rho; + data->DII_density = &species_densities[10]; /* HDI */ - xp->cooling_data.HDI_frac *= rho; - data->HDI_density = &xp->cooling_data.HDI_frac; + species_densities[11] = xp->cooling_data.HDI_frac * rho; + data->HDI_density = &species_densities[11]; } #else void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { + struct xpart* xp, gr_float rho, + gr_float species_densities[12]) { data->DI_density = NULL; data->DII_density = NULL; data->HDI_density = NULL; @@ -504,11 +513,12 @@ void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, * @param rho The particle density. */ void cooling_copy_to_grackle(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { + struct xpart* xp, gr_float rho, + gr_float species_densities[12]) { - cooling_copy_to_grackle1(data, p, xp, rho); - cooling_copy_to_grackle2(data, p, xp, rho); - cooling_copy_to_grackle3(data, p, xp, rho); + cooling_copy_to_grackle1(data, p, xp, rho, species_densities); + cooling_copy_to_grackle2(data, p, xp, rho, species_densities); + cooling_copy_to_grackle3(data, p, xp, rho, species_densities); data->volumetric_heating_rate = NULL; data->specific_heating_rate = NULL; @@ -620,6 +630,9 @@ gr_float cooling_new_energy( gr_float energy = hydro_get_physical_internal_energy(p, xp, cosmo) + dt_therm * hydro_get_physical_internal_energy_dt(p, cosmo); energy = max(energy, hydro_props->minimal_internal_energy); + /* keep this array here so you can point to and from it in + * copy to/from grackle */ + gr_float species_densities[12]; /* initialize density */ data.density = &density; @@ -633,7 +646,7 @@ gr_float cooling_new_energy( data.z_velocity = NULL; /* copy to grackle structure */ - cooling_copy_to_grackle(&data, p, xp, density); + cooling_copy_to_grackle(&data, p, xp, density, species_densities); /* Apply the self shielding if requested */ cooling_apply_self_shielding(cooling, &chemistry_grackle, p, cosmo); @@ -705,8 +718,9 @@ gr_float cooling_time(const struct phys_const* restrict phys_const, data.y_velocity = NULL; data.z_velocity = NULL; + gr_float species_densities[12]; /* copy data from particle to grackle data */ - cooling_copy_to_grackle(&data, p, xp, density); + cooling_copy_to_grackle(&data, p, xp, density, species_densities); /* Apply the self shielding if requested */ cooling_apply_self_shielding(cooling, &chemistry_grackle, p, cosmo); diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h index f37180c4358a0ba22d9dda84eca17d7a8b529f0d..5173e9adb4c96bac2c01b557e772f68b46873c04 100644 --- a/src/cooling/grackle/cooling.h +++ b/src/cooling/grackle/cooling.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -100,11 +100,14 @@ float cooling_get_radiated_energy(const struct xpart* restrict xp); void cooling_print_backend(const struct cooling_function_data* cooling); void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho); + struct xpart* xp, gr_float rho, + gr_float species_densities[12]); void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho); + struct xpart* xp, gr_float rho, + gr_float species_densities[12]); void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho); + struct xpart* xp, gr_float rho, + gr_float species_densities[12]); void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, struct xpart* xp, gr_float rho); void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p, @@ -112,7 +115,8 @@ void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p, void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, struct xpart* xp, gr_float rho); void cooling_copy_to_grackle(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho); + struct xpart* xp, gr_float rho, + gr_float species_densities[12]); void cooling_copy_from_grackle(grackle_field_data* data, const struct part* p, struct xpart* xp, gr_float rho); void cooling_apply_self_shielding( diff --git a/src/cooling/grackle/cooling_debug.h b/src/cooling/grackle/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..0322cae6bf069bcb04387df87c3df8f85d9ed17e --- /dev/null +++ b/src/cooling/grackle/cooling_debug.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_GRACKLE_DEBUG_H +#define SWIFT_COOLING_GRACKLE_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] cooling_xpart_data:", p->id); + warning("[PID%lld] radiated_energy = %.3e, time_last_event = %.3e", p->id, + xp->cooling_data.radiated_energy, xp->cooling_data.time_last_event); + +#if COOLING_GRACKLE_MODE >= 1 + warning( + "[PID%lld] HI_frac = %.3e, HII_frac = %.3e, HeI_frac = %.3e, HeII_frac " + "= " + "%.3e, " + "HeIII_frac = %.3e, e_frac = %.3e", + p->id, xp->cooling_data.HI_frac, xp->cooling_data.HII_frac, + xp->cooling_data.HeI_frac, xp->cooling_data.HeII_frac, + xp->cooling_data.HeIII_frac, xp->cooling_data.e_frac); +#if COOLING_GRACKLE_MODE >= 2 + warning("[PID%lld] HM_frac = %.3e, H2I_frac = %.3e, H2II_frac = %.3e", + p->id, xp->cooling_data.HM_frac, xp->cooling_data.H2I_frac, + xp->cooling_data.H2II_frac); +#if COOLING_GRACKLE_MODE >= 3 + warning("[PID%lld] DI_frac = %.3e, DII_frac = %.3e, HDI_frac = %.3e", p->id, + xp->cooling_data.DI_frac, xp->cooling_data.DII_frac, + xp->cooling_data.HDI_frac); +#endif +#endif +#endif + warning("[PID%lld] metal_frac = %.3e", p->id, xp->cooling_data.metal_frac); + } +} + +#endif /* SWIFT_COOLING_GRACKLE_DEBUG_H */ diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h index 4c2f8af9a898586b84b1bf355ac92a4e3c2f6a0f..290b9d894b212b2a70c6f87bc7b3347b18d77087 100644 --- a/src/cooling/grackle/cooling_io.h +++ b/src/cooling/grackle/cooling_io.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/grackle/cooling_properties.h b/src/cooling/grackle/cooling_properties.h index 95d5596c67a76b7b34bd90c968a8e2fb0b33b80f..385ecdf59fec48d94130426389acd732c922083c 100644 --- a/src/cooling/grackle/cooling_properties.h +++ b/src/cooling/grackle/cooling_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/grackle/cooling_struct.h b/src/cooling/grackle/cooling_struct.h index 53f042490ad30e2c51bd9d90d623166b4cb63a76..460b6e64dcaaf44605e9733ef7962fcd23edbc25 100644 --- a/src/cooling/grackle/cooling_struct.h +++ b/src/cooling/grackle/cooling_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h index ecb926287cb1460109a0ff92df1872a471d630fa..69128aa3577c072b1c2633cabef820acdb21d486 100644 --- a/src/cooling/none/cooling.h +++ b/src/cooling/none/cooling.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -23,7 +23,7 @@ * @file src/cooling/none/cooling.h * @brief Empty infrastructure for the cases without cooling function */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/cooling/none/cooling_debug.h b/src/cooling/none/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..ccb481c2d48506afe87d25849431023f0b7c9078 --- /dev/null +++ b/src/cooling/none/cooling_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_NONE_DEBUG_H +#define SWIFT_COOLING_NONE_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_COOLING_NONE_DEBUG_H */ diff --git a/src/cooling/none/cooling_io.h b/src/cooling/none/cooling_io.h index c4ec341b8c51aa2d0952140753e6aa6cfff401f8..a0962787f88b4e1ec15fd009dd487d89b131904a 100644 --- a/src/cooling/none/cooling_io.h +++ b/src/cooling/none/cooling_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_COOLING_NONE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" diff --git a/src/cooling/none/cooling_properties.h b/src/cooling/none/cooling_properties.h index 9782c7bcc71d37c336dd334d5332a3f174b7a167..350b9525b8ee9fca5f63a1146518e28da5565ace 100644 --- a/src/cooling/none/cooling_properties.h +++ b/src/cooling/none/cooling_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling/none/cooling_struct.h b/src/cooling/none/cooling_struct.h index db3302d94dffec8fbe32a59d9f1000d7361aba5d..730a2899babb2f872707776f6d47fde52fce61b4 100644 --- a/src/cooling/none/cooling_struct.h +++ b/src/cooling/none/cooling_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/cooling_debug.h b/src/cooling_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..50d213eaf97393d77abe3fd5aabdafb53694a9e5 --- /dev/null +++ b/src/cooling_debug.h @@ -0,0 +1,48 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_COOLING_DEBUG_H +#define SWIFT_COOLING_DEBUG_H + +/* Config parameters. */ +#include <config.h> + +/* Import the debug routines of the right cooling definition */ +#if defined(COOLING_NONE) +#include "./cooling/none/cooling_debug.h" +#elif defined(COOLING_CONST_DU) +#include "./cooling/const_du/cooling_debug.h" +#elif defined(COOLING_CONST_LAMBDA) +#include "./cooling/const_lambda/cooling_debug.h" +#elif defined(COOLING_COMPTON) +#include "./cooling/Compton/cooling_debug.h" +#elif defined(COOLING_GRACKLE) +#include "./cooling/grackle/cooling_debug.h" +#elif defined(COOLING_QLA) +#include "./cooling/QLA/cooling_debug.h" +#elif defined(COOLING_QLA_EAGLE) +#include "./cooling/QLA_EAGLE/cooling_debug.h" +#elif defined(COOLING_EAGLE) +#include "./cooling/EAGLE/cooling_debug.h" +#elif defined(COOLING_COLIBRE) +#include "./cooling/COLIBRE/cooling_debug.h" +#else +#error "Invalid choice of cooling function." +#endif + +#endif /* SWIFT_COOLING_DEBUG_H */ diff --git a/src/cooling_io.h b/src/cooling_io.h index 78ca59587a738e01c79681f0bca181a36f00c2dd..ed08fe3371b562b1722b8f24d9f6e7e7dbde82d3 100644 --- a/src/cooling_io.h +++ b/src/cooling_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_COOLING_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the i/o routines of the right cooling definition */ #if defined(COOLING_NONE) diff --git a/src/cooling_properties.h b/src/cooling_properties.h index 1dad53bbee1bc6c6b950eda96a93f1f2b5032294..ba399d1e9c0e3ab7aa149736b9bc878b661d078b 100644 --- a/src/cooling_properties.h +++ b/src/cooling_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right cooling definition */ #if defined(COOLING_NONE) diff --git a/src/cooling_struct.h b/src/cooling_struct.h index c0cfe83addec94386e794ebd520cfa999b69cb99..c7962624cc862abe2594d273d2faf57127d86a18 100644 --- a/src/cooling_struct.h +++ b/src/cooling_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right cooling definition */ #if defined(COOLING_NONE) diff --git a/src/cosmology.c b/src/cosmology.c index e544610321d1914b15b9e559fccad04fe658911c..23915f925378b2f5dd76e7b23cd858ef85c40d44 100644 --- a/src/cosmology.c +++ b/src/cosmology.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -55,11 +55,11 @@ const size_t GSL_workspace_size = 100000; * * Uses linear interpolation. * - * @brief table The table of value to interpolate from (should be of length + * @param table The table of value to interpolate from (should be of length * cosmology_table_length). - * @brief x The value to interpolate at. - * @brief x_min The mininum of the range of x. - * @brief x_max The maximum of the range of x. + * @param x The value to interpolate at. + * @param x_min The mininum of the range of x. + * @param x_max The maximum of the range of x. */ static INLINE double interp_table(const double *table, const double x, const double x_min, const double x_max) { @@ -79,6 +79,65 @@ static INLINE double interp_table(const double *table, const double x, return table[ii - 1] + (table[ii] - table[ii - 1]) * (xx - ii); } +/** + * @brief Invert a function y(a) which is tabulated at intervals in log(a) + * + * The function to invert must be monotonically increasing and is + * assumed to be zero at a=a_begin. + * + * @param y_table Input array with cosmology_table_length elements. Element i + * contains the value of y at log(a)=log(a_begin)+delta_log_a*(i+1). + * @param log_a_begin Log of expansion factor at the start of the interval + * @param delta_y Interval in y at which to tabulate a-a_begin in a_table + * @param delta_log_a Interval in log(a) at which the function is tabulated in + * y_table + * @param a_table Output array with cosmology_table_length elements. Element i + * contains the value of a-a_begin at which y=delta_y*(i+1). + * + */ +#ifdef HAVE_LIBGSL +static void invert_table(const double *y_table, const double log_a_begin, + const double delta_y, const double delta_log_a, + double *a_table) { + + int i_a = 0; + for (int i_y = 0; i_y < cosmology_table_length; i_y++) { + + double y_interp = delta_y * (i_y + 1); + + /* Find next y in tabulated y(a) */ + while (i_a < cosmology_table_length && y_table[i_a] <= y_interp) { + i_a++; + } + + /* Find y values we're interpolating between */ + double scale = 0.0; + if (i_a == 0) { + /* We're interpolating between y=0 and the first tabulated point */ + double y1 = 0.0; + double y2 = y_table[i_a]; + scale = (y_interp - y1) / (y2 - y1) + i_a; + } else if ((i_a > 0) && (i_a < cosmology_table_length)) { + /* We're interpolating between two tabulated points in the array */ + double y1 = y_table[i_a - 1]; + double y2 = y_table[i_a]; + scale = (y_interp - y1) / (y2 - y1) + i_a; + } else if (i_a == cosmology_table_length) { + /* This happens when y_interp equals the final tabulated point */ + scale = i_a; + } else { + error("Interpolating function to invert outside tabulated range!"); + } + + /* Compute log(a) at this point */ + const double log_a = log_a_begin + scale * delta_log_a; + + /* Store value of a-a_begin corresponding to y=y_interp */ + a_table[i_y] = exp(log_a) - exp(log_a_begin); + } +} +#endif /* HAVE_LIBGSL */ + /** * @brief Computes the dark-energy equation of state at a given scale-factor a. * @@ -220,9 +279,18 @@ void cosmology_update(struct cosmology *c, const struct phys_const *phys_const, /* Mean density */ c->mean_density = c->critical_density_0 * c->a3_inv; + /* Mean matter density */ + c->mean_density_Omega_m = c->mean_density * Omega_m; + /* Mean baryonic density */ c->mean_density_Omega_b = c->mean_density * c->Omega_b; + /* Over-density threshold for virialization + * Fitting function from Bryan & Norman, 1998, ApJ, 495, 1, 80-99 + * Equation 6. */ + const double x = Omega_m * c->a3_inv / (E_z * E_z) - 1.; + c->overdensity_BN98 = 18. * M_PI * M_PI + 82. * x - 39 * x * x; + /* Time-step conversion factor */ c->time_step_factor = c->H; @@ -281,6 +349,31 @@ double gravity_kick_integrand(double a, void *param) { return (1. / H) * a_inv * a_inv; } +/** + * @brief Computes \f$ c dt / a \f$ for the current cosmology. + * + * @param a The scale-factor of interest. + * @param param The current #cosmology. + */ +double comoving_distance_integrand(double a, void *param) { + + const struct cosmology *c = (const struct cosmology *)param; + const double Omega_nu = cosmology_get_neutrino_density(c, a); + const double Omega_r = c->Omega_r + Omega_nu; + const double Omega_m = c->Omega_cdm + c->Omega_b; + const double Omega_k = c->Omega_k; + const double Omega_l = c->Omega_lambda; + const double w_0 = c->w_0; + const double w_a = c->w_a; + const double H0 = c->H0; + const double const_speed_light_c = c->const_speed_light_c; + const double a_inv = 1. / a; + const double E_z = E(Omega_r, Omega_m, Omega_k, Omega_l, w_0, w_a, a); + const double H = H0 * E_z; + + return (const_speed_light_c / H) * a_inv * a_inv; +} + /** * @brief Computes \f$ dt / a^{3(\gamma - 1) + 1} \f$ for the current cosmology. * @@ -573,6 +666,14 @@ void cosmology_init_tables(struct cosmology *c) { SWIFT_STRUCT_ALIGNMENT, cosmology_table_length * sizeof(double)) != 0) error("Failed to allocate cosmology interpolation table"); + if (swift_memalign("cosmo.table", (void **)&c->comoving_distance_interp_table, + SWIFT_STRUCT_ALIGNMENT, + cosmology_table_length * sizeof(double)) != 0) + error("Failed to allocate cosmology interpolation table"); + if (swift_memalign( + "cosmo.table", (void **)&c->comoving_distance_inverse_interp_table, + SWIFT_STRUCT_ALIGNMENT, cosmology_table_length * sizeof(double)) != 0) + error("Failed to allocate cosmology interpolation table"); /* Prepare a table of scale factors for the integral bounds */ const double delta_a = @@ -653,48 +754,89 @@ void cosmology_init_tables(struct cosmology *c) { GSL_INTEG_GAUSS61, space, &result, &abserr); c->universe_age_at_present_day = result; + /* Integrate the comoving distance \int_{a_begin}^{a_table[i]} c dt/a */ + F.function = &comoving_distance_integrand; + for (int i = 0; i < cosmology_table_length; i++) { + gsl_integration_qag(&F, a_begin, a_table[i], 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + + /* Store result */ + c->comoving_distance_interp_table[i] = result; + } + + /* Integrate the comoving distance \int_{a_begin}^{1.0} c dt/a */ + F.function = &comoving_distance_integrand; + gsl_integration_qag(&F, a_begin, 1.0, 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + c->comoving_distance_interp_table_offset = result; + + /* Integrate the comoving distance \int_{a_begin}^{a_end} c dt/a */ + F.function = &comoving_distance_integrand; + gsl_integration_qag(&F, a_begin, a_end, 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + c->comoving_distance_start_to_end = result; + /* Update the times */ c->time_begin = cosmology_get_time_since_big_bang(c, c->a_begin); c->time_end = cosmology_get_time_since_big_bang(c, c->a_end); - /* - * Inverse t(a) - */ + /* Interval in log(a) at which the time and comoving distance functions are + * tabulated */ + const double delta_log_a = + (c->log_a_end - c->log_a_begin) / cosmology_table_length; + /* Tabulate inverted t(a) function */ const double delta_t = (c->time_end - c->time_begin) / cosmology_table_length; + invert_table(c->time_interp_table, c->log_a_begin, delta_t, delta_log_a, + c->scale_factor_interp_table); - /* index in the time_interp_table */ - int i_a = 0; + /* Tabulate inverted comoving distance function */ + const double r_begin = cosmology_get_comoving_distance(c, a_begin); + const double r_end = cosmology_get_comoving_distance(c, a_end); + const double delta_r = (r_begin - r_end) / cosmology_table_length; + invert_table(c->comoving_distance_interp_table, c->log_a_begin, delta_r, + delta_log_a, c->comoving_distance_inverse_interp_table); - for (int i_time = 0; i_time < cosmology_table_length; i_time++) { - /* Current time - * time_interp_table = \int_a_begin^a => no need of time_begin */ - double time_interp = delta_t * (i_time + 1); + /* Free the workspace and temp array */ + gsl_integration_workspace_free(space); + swift_free("cosmo.table", a_table); - /* Find next time in time_interp_table */ - while (i_a < cosmology_table_length && - c->time_interp_table[i_a] <= time_interp) { - i_a++; - } +#ifdef SWIFT_DEBUG_CHECKS - /* Find linear interpolation scaling */ - double scale = 0; - if (i_a != cosmology_table_length) { - scale = time_interp - c->time_interp_table[i_a - 1]; - scale /= c->time_interp_table[i_a] - c->time_interp_table[i_a - 1]; - } + const int n = 1000 * cosmology_table_length; + double max_error_time = 0; + double max_error_distance = 0; + + for (int i = 0; i < n; i += 1) { + + double a_check, frac_error; + + /* Choose value of expansion factor for check */ + const double dloga = (c->log_a_end - c->log_a_begin) / (n - 1); + const double a = exp(c->log_a_begin + dloga * i); - scale += i_a; + /* Verify that converting expansion factor to time and back recovers the + * original value */ + const double t = cosmology_get_time_since_big_bang(c, a); + a_check = cosmology_get_scale_factor(c, t); + frac_error = fabs(a_check / a - 1.0); + if (frac_error > max_error_time) max_error_time = frac_error; - /* Compute interpolated scale factor */ - double log_a = c->log_a_begin + scale * (c->log_a_end - c->log_a_begin) / - cosmology_table_length; - c->scale_factor_interp_table[i_time] = exp(log_a) - c->a_begin; + /* Verify that converting expansion factor to comoving distance and back + * recovers the original value */ + const double r = cosmology_get_comoving_distance(c, a); + a_check = cosmology_scale_factor_at_comoving_distance(c, r); + frac_error = fabs(a_check / a - 1.0); + if (frac_error > max_error_distance) max_error_distance = frac_error; } - /* Free the workspace and temp array */ - gsl_integration_workspace_free(space); - swift_free("cosmo.table", a_table); + message("Max fractional error in a to age of universe round trip = %16.8e\n", + max_error_time); + message( + "Max fractional error in a to comoving distance round trip = %16.8e\n", + max_error_distance); + +#endif /* SWIFT_DEBUG_CHECKS */ #else @@ -798,6 +940,9 @@ void cosmology_init(struct swift_params *params, const struct unit_system *us, const double rho_c3_on_4sigma = c->critical_density_0 * cc * cc * cc / (4. * phys_const->const_stefan_boltzmann); + /* Store speed of light in internal units */ + c->const_speed_light_c = phys_const->const_speed_light_c; + /* Handle neutrinos only if present */ if (c->N_ur == 0. && c->N_nu == 0) { /* Infer T_CMB_0 from Omega_r */ @@ -957,7 +1102,9 @@ void cosmology_init_no_cosmo(struct cosmology *c) { c->critical_density = 0.; c->critical_density_0 = 0.; c->mean_density = 0.; + c->mean_density_Omega_m = 0; c->mean_density_Omega_b = 0; + c->overdensity_BN98 = 0.; c->T_CMB_0 = 0.; c->T_CMB_0_K = 0.; @@ -979,6 +1126,8 @@ void cosmology_init_no_cosmo(struct cosmology *c) { c->neutrino_density_late_table = NULL; c->time_interp_table_offset = 0.; c->scale_factor_interp_table = NULL; + c->comoving_distance_interp_table = NULL; + c->comoving_distance_inverse_interp_table = NULL; c->time_begin = 0.; c->time_end = 0.; @@ -1157,6 +1306,49 @@ double cosmology_get_delta_time(const struct cosmology *c, return t2 - t1; } +/** + * @brief Compute the comoving distance to the specified scale factor + * + * @param c The current #cosmology. + * @param a The scale factor + */ +double cosmology_get_comoving_distance(const struct cosmology *c, + const double a) { + +#ifdef SWIFT_DEBUG_CHECKS + if (a < c->a_begin) error("a must be >= a_begin"); + if (a > c->a_end) error("a must be <= a_end"); +#endif + + const double log_a = log(a); + + /* Comoving distance from a_begin to a */ + const double dist = interp_table(c->comoving_distance_interp_table, log_a, + c->log_a_begin, c->log_a_end); + + /* Subtract dist from comoving distance from a_begin to a=1 */ + return c->comoving_distance_interp_table_offset - dist; +} + +/** + * @brief Compute scale factor from a comoving distance (in internal units). + * + * @param c The current #cosmology. + * @param r The comoving distance + * @return The scale factor. + */ +double cosmology_scale_factor_at_comoving_distance(const struct cosmology *c, + double r) { + + /* Get comoving distance from a_begin to a corresponding to input r */ + const double r_interp = c->comoving_distance_interp_table_offset - r; + + const double a = + interp_table(c->comoving_distance_inverse_interp_table, r_interp, 0.0, + c->comoving_distance_start_to_end); + return a + c->a_begin; +} + /** * @brief Compute neutrino density parameter Omega_nu at the given scale-factor * This is the effective present day value, i.e. must be multiplied by (1+z)^4 @@ -1322,6 +1514,8 @@ void cosmology_clean(struct cosmology *c) { swift_free("cosmo.table", c->hydro_kick_corr_interp_table); swift_free("cosmo.table", c->time_interp_table); swift_free("cosmo.table", c->scale_factor_interp_table); + swift_free("cosmo.table", c->comoving_distance_interp_table); + swift_free("cosmo.table", c->comoving_distance_inverse_interp_table); if (c->N_nu > 0) { swift_free("cosmo.table", c->neutrino_density_early_table); swift_free("cosmo.table", c->neutrino_density_late_table); @@ -1358,7 +1552,7 @@ void cosmology_write_model(hid_t h_grp, const struct cosmology *c) { io_write_attribute_d(h_grp, "T_nu_0 [eV]", c->T_nu_0_eV); io_write_attribute_d(h_grp, "N_eff", c->N_eff); io_write_attribute_d(h_grp, "N_ur", c->N_ur); - io_write_attribute_d(h_grp, "N_nu", c->N_nu); + io_write_attribute_i(h_grp, "N_nu", c->N_nu); if (c->N_nu > 0) { io_write_attribute(h_grp, "M_nu_eV", DOUBLE, c->M_nu_eV, c->N_nu); io_write_attribute(h_grp, "deg_nu", DOUBLE, c->deg_nu, c->N_nu); @@ -1373,6 +1567,14 @@ void cosmology_write_model(hid_t h_grp, const struct cosmology *c) { io_write_attribute_d(h_grp, "Scale-factor", c->a); io_write_attribute_d(h_grp, "Critical density [internal units]", c->critical_density); + io_write_attribute_d(h_grp, + "Critical density at redshift zero [internal units]", + c->critical_density_0); + io_write_attribute_d(h_grp, "Mean matter density [internal units]", + c->mean_density_Omega_m); + io_write_attribute_d(h_grp, "Mean baryonic density [internal units]", + c->mean_density_Omega_b); + io_write_attribute_d(h_grp, "Virial overdensity (BN98)", c->overdensity_BN98); } #endif diff --git a/src/cosmology.h b/src/cosmology.h index 3db7a6da141f953bf565fd20c849a7c89e78870a..a5fa2f32e41dfd5c4c9d1c141f83ecad489302d7 100644 --- a/src/cosmology.h +++ b/src/cosmology.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -20,7 +20,9 @@ #define SWIFT_COSMOLOGY_H /* Config parameters. */ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "parser.h" #include "physical_constants.h" #include "timeline.h" @@ -81,10 +83,18 @@ struct cosmology { /*! The mean density at the current redshift (in internal physical units) */ double mean_density; + /*! The mean matter density at the current redshift (in internal physical + * units) */ + double mean_density_Omega_m; + /*! The mean baryonic density at the current redshift (in internal physical * units) */ double mean_density_Omega_b; + /*! Over-density for virialised haloes at the current redshift + * from the Bryan & Norman 1998 fit */ + double overdensity_BN98; + /*! Conversion factor from internal time-step size to cosmological step */ double time_step_factor; @@ -213,6 +223,9 @@ struct cosmology { /*! Log of final expansion factor */ double log_a_end; + /*! Speed of light (internal units) */ + double const_speed_light_c; + /*! Drift factor interpolation table */ double *drift_fac_interp_table; @@ -231,6 +244,18 @@ struct cosmology { /*! Scale factor interpolation table */ double *scale_factor_interp_table; + /*! Comoving distance interpolation table */ + double *comoving_distance_interp_table; + + /*! Comoving distance from present day (a=1) to a_end */ + double comoving_distance_interp_table_offset; + + /*! Comoving distance from a_start to a_end */ + double comoving_distance_start_to_end; + + /*! Comoving distance inverse interpolation table */ + double *comoving_distance_inverse_interp_table; + /*! Massive neutrino density interpolation table at early times */ double *neutrino_density_early_table; @@ -279,6 +304,12 @@ double cosmology_get_timebase(struct cosmology *c, double cosmology_get_scale_factor(const struct cosmology *cosmo, double t); +double cosmology_get_comoving_distance(const struct cosmology *c, + const double a); + +double cosmology_scale_factor_at_comoving_distance(const struct cosmology *c, + double r); + double cosmology_get_time_since_big_bang(const struct cosmology *c, double a); void cosmology_init(struct swift_params *params, const struct unit_system *us, const struct phys_const *phys_const, struct cosmology *c); diff --git a/src/csds.c b/src/csds.c index cec056b299587eb95ca95e71b06abf0025042895..0dbd8e4221d8edc8b561436f460cd273ff7d34c7 100644 --- a/src/csds.c +++ b/src/csds.c @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef HAVE_POSIX_FALLOCATE /* Are we on a sensible platform? */ #ifdef WITH_CSDS diff --git a/src/csds.h b/src/csds.h index ac929dc9a62e9b0a5ecbf2d05f50f4b1ea15be74..57682faf3d2f81e87d615116f15264a27a015111 100644 --- a/src/csds.h +++ b/src/csds.h @@ -20,7 +20,7 @@ #ifndef SWIFT_CSDS_H #define SWIFT_CSDS_H -#include "../config.h" +#include <config.h> #ifdef WITH_CSDS diff --git a/src/csds_io.c b/src/csds_io.c index 0400e783eb8b28e0828900419af66a57baf525f7..08308ff0154006ffce57afc03f12d4cc9d6c3d39 100644 --- a/src/csds_io.c +++ b/src/csds_io.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(WITH_CSDS) diff --git a/src/csds_io.h b/src/csds_io.h index 4f86e1dadd0f63ce03d2f6c714f402d0bc812e88..3f7de3ef12d8284e02b911124752af91e58989ce 100644 --- a/src/csds_io.h +++ b/src/csds_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 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 @@ -20,7 +20,7 @@ #define SWIFT_CSDS_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef WITH_CSDS diff --git a/src/cycle.h b/src/cycle.h index 0ba1277893fe7be7d8b62cdcb9a8873d5709eb60..73b2e3aa485283103dbfa670a7efdb39701cf4f5 100644 --- a/src/cycle.h +++ b/src/cycle.h @@ -42,7 +42,7 @@ (In order to use some of the OS-dependent timer routines like Solaris' gethrtime, you need to paste the autoconf snippet below - into your configure.ac file and #include "config.h" before cycle.h, + into your configure.ac file and #include <config.h> before cycle.h, or define the relevant macros manually if you are not using autoconf.) */ @@ -81,16 +81,8 @@ intrinsic.])], [rtc_ok=no]) /***************************************************************************/ #include <stdint.h> -#if TIME_WITH_SYS_TIME #include <sys/time.h> #include <time.h> -#else -#if HAVE_SYS_TIME_H -#include <sys/time.h> -#else -#include <time.h> -#endif -#endif #define INLINE_ELAPSED(INL) \ static INL double elapsed(ticks t1, ticks t0) { \ diff --git a/src/debug.c b/src/debug.c index ef4189ef52ac519e1c9754d377198f62149c98e6..978da76415b20caf0724f7c4328a8d431bf16aa8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013- 2015: - * Matthieu Schaller (matthieu.schaller@durham.ac.uk), + * Matthieu Schaller (schaller@strw.leidenuniv.nl), * Pedro Gonnet (pedro.gonnet@durham.ac.uk), * Peter W. Draper (p.w.draper@durham.ac.uk). * @@ -30,14 +30,28 @@ #include <stdio.h> #include <unistd.h> +#ifdef HAVE_BACKTRACE +#include <execinfo.h> +#endif + /* Local includes. */ #include "active.h" +#include "black_holes_debug.h" #include "cell.h" +#include "chemistry_debug.h" +#include "cooling_debug.h" #include "engine.h" +#include "feedback_debug.h" #include "hydro.h" #include "inline.h" +#include "mhd.h" #include "part.h" +#include "particle_splitting.h" +#include "pressure_floor_debug.h" +#include "sink_debug.h" #include "space.h" +#include "star_formation_debug.h" +#include "tracers_debug.h" /* Import the right hydro definition */ #if defined(NONE_SPH) @@ -70,6 +84,13 @@ #error "Invalid choice of SPH variant" #endif +/* Import the right MHD definition */ +#if defined(NONE_MHD) +#include "./mhd/None/mhd_debug.h" +#else +#error "Invalid choice of MHD variant" +#endif + /* Import the right gravity definition */ #if defined(DEFAULT_GRAVITY) #include "./gravity/Default/gravity_debug.h" @@ -100,8 +121,19 @@ void printParticle(const struct part *parts, const struct xpart *xparts, /* Look for the particle. */ for (size_t i = 0; i < N; i++) if (parts[i].id == id) { - printf("## Particle[%zu]:\n id=%lld ", i, parts[i].id); + warning("[PID%lld] ## Particle[%zu]:\n id=%lld ", parts[i].id, i, + parts[i].id); hydro_debug_particle(&parts[i], &xparts[i]); + mhd_debug_particle(&parts[i], &xparts[i]); + chemistry_debug_particle(&parts[i], &xparts[i]); + cooling_debug_particle(&parts[i], &xparts[i]); + particle_splitting_debug_particle(&parts[i], &xparts[i]); + tracers_debug_particle(&parts[i], &xparts[i]); + star_formation_debug_particle(&parts[i], &xparts[i]); + feedback_debug_particle(&parts[i], &xparts[i]); + black_holes_debug_particle(&parts[i], &xparts[i]); + sink_debug_particle(&parts[i], &xparts[i]); + pressure_floor_debug_particle(&parts[i], &xparts[i]); found = 1; break; } @@ -152,9 +184,21 @@ void printgParticle(const struct gpart *gparts, const struct part *parts, */ void printParticle_single(const struct part *p, const struct xpart *xp) { - printf("## Particle: id=%lld ", p->id); + warning("[PID%lld] ## Particle: id=%lld ", p->id, p->id); hydro_debug_particle(p, xp); - printf("\n"); + mhd_debug_particle(p, xp); + chemistry_debug_particle(p, xp); + cooling_debug_particle(p, xp); + particle_splitting_debug_particle(p, xp); + tracers_debug_particle(p, xp); + star_formation_debug_particle(p, xp); + feedback_debug_particle(p, xp); + black_holes_debug_particle(p, xp); + sink_debug_particle(p, xp); + pressure_floor_debug_particle(p, xp); + if (xp == NULL) { + warning("[PID%lld] No xpart data available.", p->id); + } } /** @@ -779,3 +823,27 @@ void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells) { } #endif /* HAVE_MPI */ + +/** + * @brief Output a backtrace of the current calling stack. + * + * Requires the glibc extension backtrace(). + * + * @param description some string to output along with the stack. + */ +void print_backtrace(const char *description) { +#ifdef HAVE_BACKTRACE + + message("%s", description); + + /* Boiler plate from the man page. */ + void *buffer[100]; + int nptrs = backtrace(buffer, 100); + char **strings = backtrace_symbols(buffer, nptrs); + if (strings == NULL) { + perror("backtrace_symbols"); + } else { + for (int j = 0; j < nptrs; j++) message("%s", strings[j]); + } +#endif +} diff --git a/src/debug.h b/src/debug.h index 3cafd17b835a1a816e049f3a714bedcaf34d183a..4ec9dc8127997fb58fb69598cf0aa3c90fa9f45d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 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 @@ -20,7 +20,7 @@ #define SWIFT_DEBUG_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include "cell.h" @@ -49,4 +49,6 @@ void dumpMETISGraph(const char *prefix, idx_t nvtxs, idx_t ncon, idx_t *xadj, void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells); #endif +void print_backtrace(const char *description); + #endif /* SWIFT_DEBUG_H */ diff --git a/src/dimension.h b/src/dimension.h index 2ae3a6d8ee9cac0b8bd3241d5e7b45f8f62dc92a..f44c95731b6b9fc2b87749a3d1a8934334681274 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 @@ -26,7 +26,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" @@ -178,73 +178,120 @@ __attribute__((always_inline)) INLINE static float pow_dimension_minus_one( * @brief Inverts the given dimension by dimension matrix (in place) * * @param A A 3x3 matrix of which we want to invert the top left dxd part + * @return Exit code: 0 for success, 1 if a singular matrix was detected. */ -__attribute__((always_inline)) INLINE static void +__attribute__((always_inline)) INLINE static int invert_dimension_by_dimension_matrix(float A[3][3]) { #if defined(HYDRO_DIMENSION_3D) - float detA, Ainv[3][3]; - - detA = A[0][0] * A[1][1] * A[2][2] + A[0][1] * A[1][2] * A[2][0] + - A[0][2] * A[1][0] * A[2][1] - A[0][2] * A[1][1] * A[2][0] - - A[0][1] * A[1][0] * A[2][2] - A[0][0] * A[1][2] * A[2][1]; - - if (detA && !isnan(detA)) { - Ainv[0][0] = (A[1][1] * A[2][2] - A[1][2] * A[2][1]) / detA; - Ainv[0][1] = (A[0][2] * A[2][1] - A[0][1] * A[2][2]) / detA; - Ainv[0][2] = (A[0][1] * A[1][2] - A[0][2] * A[1][1]) / detA; - Ainv[1][0] = (A[1][2] * A[2][0] - A[1][0] * A[2][2]) / detA; - Ainv[1][1] = (A[0][0] * A[2][2] - A[0][2] * A[2][0]) / detA; - Ainv[1][2] = (A[0][2] * A[1][0] - A[0][0] * A[1][2]) / detA; - Ainv[2][0] = (A[1][0] * A[2][1] - A[1][1] * A[2][0]) / detA; - Ainv[2][1] = (A[0][1] * A[2][0] - A[0][0] * A[2][1]) / detA; - Ainv[2][2] = (A[0][0] * A[1][1] - A[0][1] * A[1][0]) / detA; - } else { - Ainv[0][0] = 0.0f; - Ainv[0][1] = 0.0f; - Ainv[0][2] = 0.0f; - Ainv[1][0] = 0.0f; - Ainv[1][1] = 0.0f; - Ainv[1][2] = 0.0f; - Ainv[2][0] = 0.0f; - Ainv[2][1] = 0.0f; - Ainv[2][2] = 0.0f; + int pivot[3]; + for (int i = 0; i < 3; i++) { + int imax = i; + float Smax = fabsf(A[imax][i]); + for (int j = i + 1; j < 3; j++) { + const float this_Smax = fabsf(A[j][i]); + if (this_Smax > Smax) { + Smax = this_Smax; + imax = j; + } + } + + if (Smax < 1.e-8f) { + /* singular matrix. Early abort */ + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + A[j][k] = 0.0f; + } + } + return 1; + } + + pivot[i] = imax; + if (i != imax) { + for (int j = 0; j < 3; j++) { + const float temp = A[i][j]; + A[i][j] = A[imax][j]; + A[imax][j] = temp; + } + } + + const float Aii_inv = 1.0f / A[i][i]; + for (int j = i + 1; j < 3; j++) { + A[j][i] *= Aii_inv; + } + + for (int j = i + 1; j < 3; j++) { + for (int k = i + 1; k < 3; k++) { + A[j][k] -= A[j][i] * A[i][k]; + } + } } - A[0][0] = Ainv[0][0]; - A[0][1] = Ainv[0][1]; - A[0][2] = Ainv[0][2]; - A[1][0] = Ainv[1][0]; - A[1][1] = Ainv[1][1]; - A[1][2] = Ainv[1][2]; - A[2][0] = Ainv[2][0]; - A[2][1] = Ainv[2][1]; - A[2][2] = Ainv[2][2]; + for (int i = 0; i < 3; i++) { + A[i][i] = 1.0f / A[i][i]; + for (int j = i + 1; j < 3; j++) { + float Aij = 0.0f; + for (int k = i; k < j; k++) { + Aij -= A[i][k] * A[k][j]; + } + A[i][j] = Aij / A[j][j]; + } + } + + float work[3]; + for (int jp1 = 3; jp1 > 0; jp1--) { + const int j = jp1 - 1; + for (int i = 0; i < jp1; i++) { + work[i] = A[i][j]; + } + for (int i = jp1; i < 3; i++) { + work[i] = 0.0f; + } + for (int k = jp1; k < 3; k++) { + for (int i = 0; i < 3; i++) { + work[i] -= A[i][k] * A[k][j]; + } + } + for (int i = 0; i < 3; i++) { + A[i][j] = work[i]; + } + } + + for (int jp1 = 3; jp1 > 0; jp1--) { + const int j = jp1 - 1; + const int jp = pivot[j]; + if (jp != j) { + for (int i = 0; i < 3; i++) { + const float temp = A[i][j]; + A[i][j] = A[i][jp]; + A[i][jp] = temp; + } + } + } + + return 0; #elif defined(HYDRO_DIMENSION_2D) - float detA, Ainv[2][2]; + float Ainv[2][2]; - detA = A[0][0] * A[1][1] - A[0][1] * A[1][0]; + const float detA = A[0][0] * A[1][1] - A[0][1] * A[1][0]; - if (detA && !isnan(detA)) { - Ainv[0][0] = A[1][1] / detA; - Ainv[0][1] = -A[0][1] / detA; - Ainv[1][0] = -A[1][0] / detA; - Ainv[1][1] = A[0][0] / detA; - } else { - Ainv[0][0] = 0.0f; - Ainv[0][1] = 0.0f; - Ainv[1][0] = 0.0f; - Ainv[1][1] = 0.0f; - } + const float detAinv = (detA != 0.0f) ? 1.0f / detA : 0.0f; + + Ainv[0][0] = A[1][1] * detAinv; + Ainv[0][1] = -A[0][1] * detAinv; + Ainv[1][0] = -A[1][0] * detAinv; + Ainv[1][1] = A[0][0] * detAinv; A[0][0] = Ainv[0][0]; A[0][1] = Ainv[0][1]; A[1][0] = Ainv[1][0]; A[1][1] = Ainv[1][1]; + return 0; + #elif defined(HYDRO_DIMENSION_1D) if (A[0][0] && !isnan(A[0][0])) { @@ -253,6 +300,8 @@ invert_dimension_by_dimension_matrix(float A[3][3]) { A[0][0] = 0.0f; } + return 0; + #else error("The dimension is not defined !"); diff --git a/src/distributed_io.c b/src/distributed_io.c index 2510eb156d10a48feac0880680c34f833fcac108..d2466030eae7635eddb2d4ec5830070e3557cd2c 100644 --- a/src/distributed_io.c +++ b/src/distributed_io.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && defined(WITH_MPI) @@ -169,7 +169,8 @@ void write_distributed_array( h_err = H5Pset_chunk(h_prop, rank, chunk_shape); if (h_err < 0) error("Error while setting chunk size (%llu, %llu) for field '%s'.", - chunk_shape[0], chunk_shape[1], props.name); + (unsigned long long)chunk_shape[0], + (unsigned long long)chunk_shape[1], props.name); /* Are we imposing some form of lossy compression filter? */ if (lossy_compression != compression_write_lossless) @@ -458,6 +459,7 @@ void write_virtual_file(struct engine* e, const char* fileName_base, const char* xmfFileName, const long long N_total[swift_type_count], const long long* N_counts, const int num_ranks, + const int to_write[swift_type_count], const int numFields[swift_type_count], char current_selection_name[FIELD_BUFFER_SIZE], const struct unit_system* internal_units, @@ -570,6 +572,8 @@ void write_virtual_file(struct engine* e, const char* fileName_base, swift_type_count); io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, swift_type_count); + io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total, + swift_type_count); double MassTable[swift_type_count] = {0}; io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count); io_write_attribute(h_grp, "InitialMassTable", DOUBLE, @@ -582,6 +586,7 @@ void write_virtual_file(struct engine* e, const char* fileName_base, io_write_attribute_i(h_grp, "ThisFile", 0); io_write_attribute_s(h_grp, "SelectOutput", current_selection_name); io_write_attribute_i(h_grp, "Virtual", 1); + io_write_attribute(h_grp, "CanHaveTypes", INT, to_write, swift_type_count); if (subsample_any) { io_write_attribute_s(h_grp, "OutputType", "SubSampled"); @@ -603,9 +608,10 @@ void write_virtual_file(struct engine* e, const char* fileName_base, /* Loop over all particle types */ for (int ptype = 0; ptype < swift_type_count; ptype++) { - /* Don't do anything if there are (a) no particles of this kind, or (b) - * if we have disabled every field of this particle type. */ - if (N_total[ptype] == 0 || numFields[ptype] == 0) continue; + /* Don't do anything if there are + * (a) no particles of this kind in this run, or + * (b) if we have disabled every field of this particle type. */ + if (!to_write[ptype] || numFields[ptype] == 0) continue; /* Add the global information for that particle type to * the XMF meta-file */ @@ -630,7 +636,8 @@ void write_virtual_file(struct engine* e, const char* fileName_base, if (h_err < 0) error("Error while creating alias for particle group.\n"); /* Write the number of particles as an attribute */ - io_write_attribute_l(h_grp, "NumberOfParticles", N_total[ptype]); + io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]); + io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]); int num_fields = 0; struct io_props list[100]; @@ -761,8 +768,13 @@ void write_output_distributed(struct engine* e, 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; + const int with_DM = e->s->with_DM; const int with_neutrinos = e->s->with_neutrinos; const int with_rt = e->policy & engine_policy_rt; + const int with_hydro = (e->policy & engine_policy_hydro) ? 1 : 0; + const int with_stars = (e->policy & engine_policy_stars) ? 1 : 0; + const int with_black_hole = (e->policy & engine_policy_black_holes) ? 1 : 0; + const int with_sink = (e->policy & engine_policy_sinks) ? 1 : 0; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -941,6 +953,15 @@ void write_output_distributed(struct engine* e, MPI_Gather(N, swift_type_count, MPI_LONG_LONG_INT, N_counts, swift_type_count, MPI_LONG_LONG_INT, 0, comm); + /* List what fields to write. + * Note that we want to want to write a 0-size dataset for some species + * in case future snapshots will contain them (e.g. star formation) */ + const int to_write[swift_type_count] = { + with_hydro, with_DM, with_DM_background, with_sink, + with_stars, with_black_hole, with_neutrinos + + }; + /* Use a single Lustre stripe with a rank-based OST offset? */ if (e->snapshot_lustre_OST_count != 0) { char string[1200]; @@ -1036,6 +1057,8 @@ void write_output_distributed(struct engine* e, swift_type_count); io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, swift_type_count); + io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total, + swift_type_count); double MassTable[swift_type_count] = {0}; io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count); io_write_attribute(h_grp, "InitialMassTable", DOUBLE, @@ -1048,6 +1071,7 @@ void write_output_distributed(struct engine* e, io_write_attribute_i(h_grp, "ThisFile", mpi_rank); io_write_attribute_s(h_grp, "SelectOutput", current_selection_name); io_write_attribute_i(h_grp, "Virtual", 0); + io_write_attribute(h_grp, "CanHaveTypes", INT, to_write, swift_type_count); if (subsample_any) { io_write_attribute_s(h_grp, "OutputType", "SubSampled"); @@ -1079,15 +1103,16 @@ void write_output_distributed(struct engine* e, e->s->nr_cells, e->s->width, mpi_rank, /*distributed=*/1, subsample, subsample_fraction, e->snapshot_output_count, N_total, global_offsets, - numFields, internal_units, snapshot_units); + to_write, numFields, internal_units, snapshot_units); H5Gclose(h_grp); /* Loop over all particle types */ for (int ptype = 0; ptype < swift_type_count; ptype++) { - /* Don't do anything if there are (a) no particles of this kind, or (b) - * if we have disabled every field of this particle type. */ - if (N[ptype] == 0 || numFields[ptype] == 0) continue; + /* Don't do anything if there are + * (a) no particles of this kind in this run, or + * (b) if we have disabled every field of this particle type. */ + if (!to_write[ptype] || numFields[ptype] == 0) continue; /* Open the particle group in the file */ char partTypeGroupName[PARTICLE_GROUP_BUFFER_SIZE]; @@ -1106,7 +1131,8 @@ void write_output_distributed(struct engine* e, if (h_err < 0) error("Error while creating alias for particle group.\n"); /* Write the number of particles as an attribute */ - io_write_attribute_l(h_grp, "NumberOfParticles", N[ptype]); + io_write_attribute_ll(h_grp, "NumberOfParticles", N[ptype]); + io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]); int num_fields = 0; struct io_props list[100]; @@ -1431,7 +1457,7 @@ void write_output_distributed(struct engine* e, /* Write the virtual meta-file */ if (mpi_rank == 0) write_virtual_file(e, fileName_base, xmfFileName, N_total, N_counts, - mpi_size, numFields, current_selection_name, + mpi_size, to_write, numFields, current_selection_name, internal_units, snapshot_units, subsample_any, subsample_fraction); @@ -1470,7 +1496,7 @@ void write_output_distributed(struct engine* e, e->s->nr_cells, e->s->width, mpi_rank, /*distributed=*/0, subsample, subsample_fraction, e->snapshot_output_count, N_total, global_offsets, - numFields, internal_units, snapshot_units); + to_write, numFields, internal_units, snapshot_units); /* Close everything */ if (mpi_rank == 0) { diff --git a/src/distributed_io.h b/src/distributed_io.h index 41b2740ecede0b919a738c55350e2c62c731a604..0a73129ab7ab6c5a4502b4dcfeab9b72b47558f8 100644 --- a/src/distributed_io.h +++ b/src/distributed_io.h @@ -20,7 +20,7 @@ #define SWIFT_DISTRIBUTED_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && defined(WITH_MPI) diff --git a/src/drift.h b/src/drift.h index e490b06bed7f9ea77bbdab82d5ab81655bfa46ee..4f7d6768b78a7c2547df9598abec9338f1d7017e 100644 --- a/src/drift.h +++ b/src/drift.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_DRIFT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "black_holes.h" @@ -31,7 +31,11 @@ #include "entropy_floor.h" #include "hydro.h" #include "hydro_properties.h" +#include "lightcone/lightcone_crossing.h" +#include "lightcone/lightcone_replications.h" +#include "mhd.h" #include "part.h" +#include "rt.h" #include "sink.h" #include "stars.h" @@ -48,7 +52,8 @@ __attribute__((always_inline)) INLINE static void drift_gpart( struct gpart *restrict gp, double dt_drift, integertime_t ti_old, integertime_t ti_current, const struct gravity_props *grav_props, - const struct engine *e) { + const struct engine *e, struct replication_list *replication_list, + const double cell_loc[3]) { #ifdef SWIFT_DEBUG_CHECKS if (gp->time_bin == time_bin_not_created) { @@ -88,12 +93,36 @@ __attribute__((always_inline)) INLINE static void drift_gpart( } #endif +#ifdef WITH_LIGHTCONE + /* Store initial position and velocity for lightcone check after the drift */ + const double x[3] = {gp->x[0], gp->x[1], gp->x[2]}; + const float v_full[3] = {gp->v_full[0], gp->v_full[1], gp->v_full[2]}; +#endif + /* Drift... */ gp->x[0] += gp->v_full[0] * dt_drift; gp->x[1] += gp->v_full[1] * dt_drift; gp->x[2] += gp->v_full[2] * dt_drift; gravity_predict_extra(gp, grav_props); + +#ifdef WITH_LIGHTCONE + /* Check for lightcone crossing */ + switch (gp->type) { + case swift_type_dark_matter: + case swift_type_dark_matter_background: + case swift_type_neutrino: + /* This particle has no *part counterpart, so check for lightcone crossing + * here */ + lightcone_check_particle_crosses(e, replication_list, x, v_full, gp, + dt_drift, ti_old, ti_current, cell_loc); + break; + default: + /* Particle has a counterpart or is of a type not supported in lightcones + */ + break; + } +#endif } /** @@ -114,9 +143,12 @@ __attribute__((always_inline)) INLINE static void drift_gpart( __attribute__((always_inline)) INLINE static void drift_part( struct part *restrict p, struct xpart *restrict xp, double dt_drift, double dt_kick_hydro, double dt_kick_grav, double dt_therm, - integertime_t ti_old, integertime_t ti_current, - const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const struct entropy_floor_properties *floor) { + integertime_t ti_old, integertime_t ti_current, const struct engine *e, + struct replication_list *replication_list, const double cell_loc[3]) { + + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *floor = e->entropy_floor; #ifdef SWIFT_DEBUG_CHECKS if (p->ti_drift != ti_old) @@ -143,6 +175,12 @@ __attribute__((always_inline)) INLINE static void drift_part( } #endif +#ifdef WITH_LIGHTCONE + /* Store initial position and velocity for lightcone check after the drift */ + const double x[3] = {p->x[0], p->x[1], p->x[2]}; + const float v_full[3] = {xp->v_full[0], xp->v_full[1], xp->v_full[2]}; +#endif + /* Drift... */ p->x[0] += xp->v_full[0] * dt_drift; p->x[1] += xp->v_full[1] * dt_drift; @@ -161,7 +199,10 @@ __attribute__((always_inline)) INLINE static void drift_part( } /* Predict the values of the extra fields */ - hydro_predict_extra(p, xp, dt_drift, dt_therm, cosmo, hydro_props, floor); + hydro_predict_extra(p, xp, dt_drift, dt_therm, dt_kick_grav, cosmo, + hydro_props, floor); + mhd_predict_extra(p, xp, dt_drift, dt_therm, cosmo, hydro_props, floor); + rt_predict_extra(p, xp, dt_drift); /* Compute offsets since last cell construction */ for (int k = 0; k < 3; k++) { @@ -179,6 +220,13 @@ __attribute__((always_inline)) INLINE static void drift_part( p->v[2] = 0.f; } #endif + +#ifdef WITH_LIGHTCONE + /* Check if the particle crossed the lightcone */ + if (p->gpart) + lightcone_check_particle_crosses(e, replication_list, x, v_full, p->gpart, + dt_drift, ti_old, ti_current, cell_loc); +#endif } /** @@ -191,7 +239,8 @@ __attribute__((always_inline)) INLINE static void drift_part( */ __attribute__((always_inline)) INLINE static void drift_spart( struct spart *restrict sp, double dt_drift, integertime_t ti_old, - integertime_t ti_current) { + integertime_t ti_current, const struct engine *e, + struct replication_list *replication_list, const double cell_loc[3]) { #ifdef SWIFT_DEBUG_CHECKS if (sp->ti_drift != ti_old) @@ -219,6 +268,12 @@ __attribute__((always_inline)) INLINE static void drift_spart( } #endif +#ifdef WITH_LIGHTCONE + /* Store initial position and velocity for lightcone check after the drift */ + const double x[3] = {sp->x[0], sp->x[1], sp->x[2]}; + const float v_full[3] = {sp->v[0], sp->v[1], sp->v[2]}; +#endif + /* Drift... */ sp->x[0] += sp->v[0] * dt_drift; sp->x[1] += sp->v[1] * dt_drift; @@ -233,6 +288,13 @@ __attribute__((always_inline)) INLINE static void drift_spart( sp->x_diff[k] -= dx; sp->x_diff_sort[k] -= dx; } + +#ifdef WITH_LIGHTCONE + /* Check for lightcone crossing */ + if (sp->gpart) + lightcone_check_particle_crosses(e, replication_list, x, v_full, sp->gpart, + dt_drift, ti_old, ti_current, cell_loc); +#endif } /** @@ -245,7 +307,8 @@ __attribute__((always_inline)) INLINE static void drift_spart( */ __attribute__((always_inline)) INLINE static void drift_bpart( struct bpart *restrict bp, double dt_drift, integertime_t ti_old, - integertime_t ti_current) { + integertime_t ti_current, const struct engine *e, + struct replication_list *replication_list, const double cell_loc[3]) { #ifdef SWIFT_DEBUG_CHECKS if (bp->ti_drift != ti_old) @@ -273,6 +336,12 @@ __attribute__((always_inline)) INLINE static void drift_bpart( } #endif +#ifdef WITH_LIGHTCONE + /* Store initial position and velocity for lightcone check after the drift */ + const double x[3] = {bp->x[0], bp->x[1], bp->x[2]}; + const float v_full[3] = {bp->v[0], bp->v[1], bp->v[2]}; +#endif + /* Drift... */ bp->x[0] += bp->v[0] * dt_drift; bp->x[1] += bp->v[1] * dt_drift; @@ -286,6 +355,13 @@ __attribute__((always_inline)) INLINE static void drift_bpart( const float dx = bp->v[k] * dt_drift; bp->x_diff[k] -= dx; } + +#ifdef WITH_LIGHTCONE + /* Check for lightcone crossing */ + if (bp->gpart) + lightcone_check_particle_crosses(e, replication_list, x, v_full, bp->gpart, + dt_drift, ti_old, ti_current, cell_loc); +#endif } /** diff --git a/src/engine.c b/src/engine.c index 246a69986346165b5bfb56d834ea2739442046a9..9bae8120d17c28b5ee146426e5e18516cf25c597 100644 --- a/src/engine.c +++ b/src/engine.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * Angus Lepper (angus.lepper@ed.ac.uk) * 2016 John A. Regan (john.a.regan@durham.ac.uk) @@ -23,7 +23,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -67,11 +67,14 @@ #include "debug.h" #include "equation_of_state.h" #include "error.h" +#include "extra_io.h" #include "feedback.h" #include "fof.h" #include "gravity.h" #include "gravity_cache.h" #include "hydro.h" +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_array.h" #include "line_of_sight.h" #include "map.h" #include "memuse.h" @@ -83,6 +86,9 @@ #include "output_list.h" #include "output_options.h" #include "partition.h" +#include "potential.h" +#include "power_spectrum.h" +#include "pressure_floor.h" #include "profiler.h" #include "proxy.h" #include "restart.h" @@ -126,7 +132,8 @@ const char *engine_policy_names[] = {"none", "csds", "line of sight", "sink", - "rt"}; + "rt", + "power spectra"}; const int engine_default_snapshot_subsample[swift_type_count] = {0}; @@ -432,10 +439,11 @@ void engine_repartition_trigger(struct engine *e) { fprintf(timelog, "# %d mean times: %f %f %f\n", e->step, umean, smean, tmean); if (abs_trigger > 1.f) abs_trigger = 0.f; /* Not relevant. */ + double systime = smean > 0. ? (smaxtime - smintime) / smean : 0.; + double ttime = tmean > 0. ? (tmaxtime - tmintime) / tmean : 0.; fprintf(timelog, "# %d balance: %f, expected: %f (sys: %f, total: %f)\n", - e->step, balance, abs_trigger, (smaxtime - smintime) / smean, - (tmaxtime - tmintime) / tmean); + e->step, balance, abs_trigger, systime, ttime); fclose(timelog); @@ -1050,9 +1058,9 @@ int engine_estimate_nr_tasks(const struct engine *e) { if (e->policy & engine_policy_hydro) { /* 2 self (density, force), 1 sort, 26/2 density pairs 26/2 force pairs, 1 drift, 3 ghosts, 2 kicks, 1 time-step, - 1 end_force, 2 extra space + 1 end_force, 1 collect, 2 extra space */ - n1 += 37; + n1 += 38; n2 += 2; #ifdef WITH_MPI n1 += 6; @@ -1118,12 +1126,16 @@ int engine_estimate_nr_tasks(const struct engine *e) { } #endif if (e->policy & engine_policy_rt) { - /* inject: 1 self + (3^3-1)/2 = 26/2 = 13 pairs | 14 - * gradient: 1 self + 13 pairs | + 14 + /* gradient: 1 self + 13 pairs | 14 * transport: 1 self + 13 pairs | + 14 * implicits: in + out, transport_out | + 3 - * others: ghost1, ghost2, thermochemistry | + 3 */ - n1 += 48; + * others: ghost1, ghost2, tchem, cell advance | + 4 + * sort, collect_times | + 2 + * 2 extra space | + 2 */ + n1 += 39; +#ifdef WITH_MPI + n1 += 2; +#endif } #ifdef WITH_MPI @@ -1199,6 +1211,12 @@ void engine_rebuild(struct engine *e, const int repartitioned, /* Give some breathing space */ scheduler_free_tasks(&e->sched); + /* Free the foreign particles to get more breathing space. */ +#ifdef WITH_MPI + if (e->free_foreign_when_rebuilding) + space_free_foreign_parts(e->s, /*clear_cell_pointers=*/1); +#endif + /* Re-build the space. */ space_rebuild(e->s, repartitioned, e->verbose); @@ -1263,6 +1281,16 @@ void engine_rebuild(struct engine *e, const int repartitioned, e->total_nr_sinks = num_particles[3]; e->total_nr_bparts = num_particles[4]; +#ifdef WITH_MPI + MPI_Allreduce(MPI_IN_PLACE, &e->s->min_a_grav, 1, MPI_FLOAT, MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &e->s->max_softening, 1, MPI_FLOAT, MPI_MAX, + MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &e->s->max_mpole_power, + SELF_GRAVITY_MULTIPOLE_ORDER + 1, MPI_FLOAT, MPI_MAX, + MPI_COMM_WORLD); +#endif + /* Flag that there are no inhibited particles */ e->nr_inhibited_parts = 0; e->nr_inhibited_gparts = 0; @@ -1312,6 +1340,12 @@ void engine_rebuild(struct engine *e, const int repartitioned, /* Re-build the tasks. */ engine_maketasks(e); + /* Reallocate freed memory */ +#ifdef WITH_MPI + if (e->free_foreign_when_rebuilding) + engine_allocate_foreign_particles(e, /*fof=*/0); +#endif + /* Make the list of top-level cells that have tasks */ space_list_useful_top_level_cells(e->s); @@ -1580,8 +1614,8 @@ void engine_skip_force_and_kick(struct engine *e) { t->type == task_type_stars_ghost || t->type == task_type_stars_ghost_in || t->type == task_type_stars_ghost_out || t->type == task_type_sink_in || - t->type == task_type_sink_ghost || t->type == task_type_sink_out || - t->type == task_type_sink_formation || + t->type == task_type_sink_ghost1 || t->type == task_type_sink_ghost2 || + t->type == task_type_sink_formation || t->type == task_type_sink_out || t->type == task_type_stars_prep_ghost1 || t->type == task_type_hydro_prep_ghost1 || t->type == task_type_stars_prep_ghost2 || @@ -1590,6 +1624,7 @@ void engine_skip_force_and_kick(struct engine *e) { t->type == task_type_bh_swallow_ghost3 || t->type == task_type_bh_in || t->type == task_type_bh_out || t->type == task_type_rt_ghost1 || t->type == task_type_rt_ghost2 || t->type == task_type_rt_tchem || + t->type == task_type_rt_advance_cell_time || t->type == task_type_neutrino_weight || t->type == task_type_csds || t->subtype == task_subtype_force || t->subtype == task_subtype_limiter || @@ -1606,15 +1641,14 @@ void engine_skip_force_and_kick(struct engine *e) { t->subtype == task_subtype_bpart_merger || t->subtype == task_subtype_bpart_swallow || t->subtype == task_subtype_bpart_feedback || - t->subtype == task_subtype_sink_compute_formation || - t->subtype == task_subtype_sink_merger || - t->subtype == task_subtype_sink_accretion || + t->subtype == task_subtype_sink_swallow || + t->subtype == task_subtype_sink_do_sink_swallow || + t->subtype == task_subtype_sink_do_gas_swallow || t->subtype == task_subtype_tend || t->subtype == task_subtype_rho || t->subtype == task_subtype_spart_density || t->subtype == task_subtype_part_prep1 || t->subtype == task_subtype_spart_prep2 || t->subtype == task_subtype_sf_counts || - t->subtype == task_subtype_rt_inject || t->subtype == task_subtype_rt_gradient || t->subtype == task_subtype_rt_transport) t->skip = 1; @@ -1739,6 +1773,115 @@ void engine_get_max_ids(struct engine *e) { #endif } +/** + * @brief Run the radiative transfer sub-cycles outside the + * regular time-steps. + * + * @param e The #engine + **/ +void engine_run_rt_sub_cycles(struct engine *e) { + + /* Do we have work to do? */ + if (!(e->policy & engine_policy_rt)) return; + if (e->max_nr_rt_subcycles <= 1) return; + + /* Get the subcycling step */ + const integertime_t rt_step_size = e->ti_rt_end_min - e->ti_current; + if (rt_step_size == 0) { + /* When we arrive at the final step, the rt_step_size can be == 0 */ + if (!engine_is_done(e)) error("Got rt_step_size = 0"); + return; + } + + /* At this point, the non-RT ti_end_min is up-to-date. Use that and + * the time of the previous regular step to get how many subcycles + * we need. */ + const int nr_rt_cycles = (e->ti_end_min - e->ti_current) / rt_step_size; + /* You can't check here that the number of cycles is exactly the number + * you fixed it to be. E.g. stars or gravity may reduce the time step + * sizes for some main steps such that they coincide with the RT bins, + * yielding effectively no subcycles. (At least for low numbers.) */ + + if (nr_rt_cycles < 0) { + error( + "Got negative nr of sub-cycles??? ti_rt_end_min = %lld ti_current = " + "%lld rt_step_size = %lld", + e->ti_rt_end_min, e->ti_current, rt_step_size); + } else if (nr_rt_cycles == 0) { + /* This can happen if in the previous main step no RT/hydro updates + * happened, but something else (e.g. stars, gravity) only. In this + * case, exit early. */ + return; + } + + /* Get some time variables for printouts. Don't update the ones in the + * engine like in the regular step, or the outputs in the regular steps + * will be wrong. */ + /* think cosmology one day: needs adapting here */ + if (e->policy & engine_policy_cosmology) + error("Can't run RT subcycling with cosmology yet"); + const double dt_subcycle = rt_step_size * e->time_base; + double time = e->ti_current_subcycle * e->time_base + e->time_begin; + + /* Collect and print info before it's gone */ + engine_collect_end_of_sub_cycle(e); + if (e->nodeID == 0) { + printf( + " %6d cycle 0 (during regular tasks) dt=%14e " + "min/max active bin=%2d/%2d rt_updates=%18lld\n", + e->step, dt_subcycle, e->min_active_bin_subcycle, + e->max_active_bin_subcycle, e->rt_updates); + } + + /* Take note of the (integer) time until which the radiative transfer + * has been integrated so far. At the start of the sub-cycling, this + * should be e->ti_current_subcycle + dt_rt_min, since the first (i.e. + * zeroth) RT cycle has been completed during the regular step. + * This is used for a consistency/debugging check. */ + integertime_t rt_integration_end = e->ti_current_subcycle + rt_step_size; + + for (int sub_cycle = 1; sub_cycle < nr_rt_cycles; ++sub_cycle) { + + e->rt_updates = 0ll; + integertime_t ti_subcycle_old = e->ti_current_subcycle; + e->ti_current_subcycle = e->ti_current + sub_cycle * rt_step_size; + e->max_active_bin_subcycle = get_max_active_bin(e->ti_current_subcycle); + e->min_active_bin_subcycle = + get_min_active_bin(e->ti_current_subcycle, ti_subcycle_old); + /* think cosmology one day: needs adapting here */ + if (e->policy & engine_policy_cosmology) + error("Can't run RT subcycling with cosmology yet"); + time = e->ti_current_subcycle * e->time_base + e->time_begin; + + /* Do the actual work now. */ + engine_unskip_rt_sub_cycle(e); + engine_launch(e, "cycles"); + + /* Collect number of updates and print */ + engine_collect_end_of_sub_cycle(e); + + rt_integration_end += rt_step_size; + + if (e->nodeID == 0) { + printf( + " %6d cycle %3d time=%13.6e dt=%14e " + "min/max active bin=%2d/%2d rt_updates=%18lld\n", + e->step, sub_cycle, time, dt_subcycle, e->min_active_bin_subcycle, + e->max_active_bin_subcycle, e->rt_updates); + } + } + + if (rt_integration_end != e->ti_end_min) + error( + "End of sub-cycling doesn't add up: got %lld should have %lld. Started " + "at ti_current = %lld dt_rt = %lld cycles = %d", + rt_integration_end, e->ti_end_min, e->ti_current, rt_step_size, + nr_rt_cycles); + + /* Once we're done, clean up after ourselves */ + e->rt_updates = 0ll; +} + /** * @brief Initialises the particles and set them in a state ready to move *forward in time. @@ -1856,6 +1999,10 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, } } + /* Apply some RT conversions (e.g. energy -> energy density) */ + if (e->policy & engine_policy_rt) + space_convert_rt_quantities(e->s, e->verbose); + /* Collect initial mean mass of each particle type */ space_collect_mean_masses(e->s, e->verbose); @@ -1904,6 +2051,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, #endif scheduler_write_dependencies(&e->sched, e->verbose, e->step); + scheduler_write_cell_dependencies(&e->sched, e->verbose, e->step); if (e->nodeID == 0) scheduler_write_task_level(&e->sched, e->step); /* Run the 0th time-step */ @@ -1911,10 +2059,6 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, engine_launch(e, "tasks"); TIMER_TOC2(timer_runners); - /* Initialise additional RT data now that time bins are set */ - if (e->policy & engine_policy_rt) - space_convert_rt_quantities_after_zeroth_step(e->s, e->verbose); - #ifdef SWIFT_HYDRO_DENSITY_CHECKS /* Run the brute-force hydro calculation for some parts */ if (e->policy & engine_policy_hydro) @@ -2056,6 +2200,9 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, } } + /* Run the RT sub-cycles now. */ + engine_run_rt_sub_cycles(e); + clocks_gettime(&time2); #ifdef SWIFT_DEBUG_CHECKS @@ -2078,6 +2225,14 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, e->force_checks_snapshot_flag = 0; #endif +#ifdef SWIFT_RT_DEBUG_CHECKS + /* if we're running the debug RT scheme, do some checks after every step, + * and reset debugging flags now. */ + if (e->policy & engine_policy_rt) { + rt_debugging_checks_end_of_step(e, e->verbose); + } +#endif + if (e->verbose) message("took %.3f %s.", e->wallclock_time, clocks_getunit()); } @@ -2085,8 +2240,9 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, * @brief Let the #engine loose to compute the forces. * * @param e The #engine. + * @return Should the run stop after this step? */ -void engine_step(struct engine *e) { +int engine_step(struct engine *e) { TIMER_TIC2; @@ -2113,7 +2269,7 @@ void engine_step(struct engine *e) { /* Print some information to the screen */ printf( " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld " - "%12lld %12lld %21.3f %6d %21.3f\n", + "%12lld %12lld %21.3f %6d %17.3f\n", e->step, e->time, e->cosmology->a, e->cosmology->z, e->time_step, e->min_active_bin, e->max_active_bin, e->updates, e->g_updates, e->s_updates, e->sink_updates, e->b_updates, e->wallclock_time, @@ -2140,7 +2296,7 @@ void engine_step(struct engine *e) { fprintf( e->file_timesteps, " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %12lld " - "%12lld %21.3f %6d %21.3f\n", + "%12lld %21.3f %6d %17.3f\n", e->step, e->time, e->cosmology->a, e->cosmology->z, e->time_step, e->min_active_bin, e->max_active_bin, e->updates, e->g_updates, e->s_updates, e->sink_updates, e->b_updates, e->wallclock_time, @@ -2154,8 +2310,19 @@ void engine_step(struct engine *e) { clocks_from_ticks(getticks() - tic_files), clocks_getunit()); } - /* We need some cells to exist but not the whole task stuff. */ + /* When restarting, we may have had some i/o to do on the step + * where we decided to stop. We have to do this now. + * We need some cells to exist but not the whole task stuff. */ if (e->restarting) space_rebuild(e->s, 0, e->verbose); + if (e->restarting) engine_io(e); + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* If we're restarting, clean up some flags and counters first. If would + * usually be done at the end of the step, but the restart dump + * interrupts it. */ + if (e->restarting && (e->policy & engine_policy_rt)) + rt_debugging_checks_end_of_step(e, e->verbose); +#endif /* Move forward in time */ e->ti_old = e->ti_current; @@ -2166,6 +2333,12 @@ void engine_step(struct engine *e) { engine_current_step = e->step; e->step_props = engine_step_prop_none; + /* RT sub-cycling related time updates */ + e->max_active_bin_subcycle = get_max_active_bin(e->ti_end_min); + e->min_active_bin_subcycle = + get_min_active_bin(e->ti_end_min, e->ti_current_subcycle); + e->ti_current_subcycle = e->ti_end_min; + /* When restarting, move everyone to the current time. */ if (e->restarting) engine_drift_all(e, /*drift_mpole=*/1); @@ -2181,6 +2354,13 @@ void engine_step(struct engine *e) { e->time_step = (e->ti_current - e->ti_old) * e->time_base; } +#ifdef WITH_LIGHTCONE + /* Determine which periodic replications could contribute to the lightcone + during this time step */ + lightcone_array_prepare_for_step(e->lightcone_array_properties, e->cosmology, + e->ti_earliest_undrifted, e->ti_current); +#endif + /*****************************************************/ /* OK, we now know what the next end of time-step is */ /*****************************************************/ @@ -2201,6 +2381,9 @@ void engine_step(struct engine *e) { hydro_props_update(e->hydro_properties, e->gravity_properties, e->cosmology); + /* Check for any snapshot triggers */ + engine_io_check_snapshot_triggers(e); + if (e->verbose) message("Updating general quantities took %.3f %s", clocks_from_ticks(getticks() - tic_updates), clocks_getunit()); @@ -2212,13 +2395,14 @@ void engine_step(struct engine *e) { e->forcerebuild = 1; /* Trigger a FOF black hole seeding? */ - if (e->policy & engine_policy_fof) { + if (e->policy & engine_policy_fof && !e->restarting) { if (e->ti_end_min > e->ti_next_fof && e->ti_next_fof > 0) { e->run_fof = 1; e->forcerebuild = 1; } } + /* Trigger a rebuild if we reached a gravity mesh step? */ if ((e->policy & engine_policy_self_gravity) && e->s->periodic && e->mesh->ti_end_mesh_next == e->ti_current) e->forcerebuild = 1; @@ -2231,6 +2415,37 @@ void engine_step(struct engine *e) { } } + /* Trigger a tree-rebuild if the fraction of active gparts is large enough */ + if ((e->policy & engine_policy_self_gravity) && !e->forcerebuild && + e->gravity_properties->rebuild_active_fraction <= 1.0f) { + + ticks tic = getticks(); + + /* Count the number of active particles */ + size_t nr_gparts = e->s->nr_gparts; + size_t nr_active_gparts = 0; + for (size_t i = 0; i < nr_gparts; ++i) { + struct gpart *gp = &e->s->gparts[i]; + if (gpart_is_active(gp, e)) nr_active_gparts++; + } + + long long total_nr_active_gparts = nr_active_gparts; +#ifdef WITH_MPI + MPI_Allreduce(MPI_IN_PLACE, &total_nr_active_gparts, 1, MPI_LONG_LONG_INT, + MPI_SUM, MPI_COMM_WORLD); +#endif + + if (e->verbose) + message("Counting active gparts took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + /* Trigger the tree-rebuild? */ + if (((double)total_nr_active_gparts > + ((double)e->total_nr_gparts) * + e->gravity_properties->rebuild_active_fraction)) + e->forcerebuild = 1; + } + #ifdef WITH_CSDS if (e->policy & engine_policy_csds) { /* Mark the current time step in the particle csds file. */ @@ -2268,15 +2483,13 @@ void engine_step(struct engine *e) { /* Prepare the tasks to be launched, rebuild or repartition if needed. */ const int drifted_all = engine_prepare(e); + /* Dump local cells and active particle counts. */ + // dumpCells("cells", 1, 0, 0, 0, e->s, e->nodeID, e->step); + #ifdef SWIFT_DEBUG_CHECKS /* Print the number of active tasks */ if (e->verbose) engine_print_task_counts(e); -#endif - - /* Dump local cells and active particle counts. */ - // dumpCells("cells", 1, 0, 0, 0, e->s, e->nodeID, e->step); -#ifdef SWIFT_DEBUG_CHECKS /* Check that we have the correct total mass in the top-level multipoles */ long long num_gpart_mpole = 0; if (e->policy & engine_policy_self_gravity) { @@ -2325,7 +2538,7 @@ void engine_step(struct engine *e) { } #endif - /* Re-compute the mesh forces */ + /* Re-compute the mesh forces? */ if ((e->policy & engine_policy_self_gravity) && e->s->periodic && e->mesh->ti_end_mesh_next == e->ti_current) { @@ -2350,14 +2563,21 @@ void engine_step(struct engine *e) { /* Write the dependencies */ if (e->sched.frequency_dependency != 0 && - e->step % e->sched.frequency_dependency == 0) + e->step % e->sched.frequency_dependency == 0) { scheduler_write_dependencies(&e->sched, e->verbose, e->step); + scheduler_write_cell_dependencies(&e->sched, e->verbose, e->step); + } /* Write the task levels */ if (e->sched.frequency_task_levels != 0 && e->step % e->sched.frequency_task_levels == 0) scheduler_write_task_level(&e->sched, e->step); + /* we have to reset the ghost histograms here and not in engine_launch, + because engine_launch is re-used for the limiter and sync (and we don't + want to lose the data from the tasks) */ + space_reset_ghost_histograms(e->s); + /* Start all the tasks. */ TIMER_TIC; engine_launch(e, "tasks"); @@ -2422,12 +2642,6 @@ void engine_step(struct engine *e) { space_check_unskip_flags(e->s); #endif -#if defined(SWIFT_RT_DEBUG_CHECKS) - /* if we're running the debug RT scheme, do some checks after every step */ - if (e->policy & engine_policy_rt) - rt_debugging_checks_end_of_step(e, e->verbose); -#endif - /* Compute the local accumulated deadtime. */ const ticks deadticks = (e->nr_threads * e->sched.deadtime.waiting_ticks) - e->sched.deadtime.active_ticks; @@ -2442,6 +2656,14 @@ void engine_step(struct engine *e) { e->sink_updates_since_rebuild += e->collect_group1.sink_updated; e->b_updates_since_rebuild += e->collect_group1.b_updated; + /* Check if we updated all of the particles on this step */ + if ((e->collect_group1.updated == e->total_nr_parts) && + (e->collect_group1.g_updated == e->total_nr_gparts) && + (e->collect_group1.s_updated == e->total_nr_sparts) && + (e->collect_group1.sink_updated == e->total_nr_sinks) && + (e->collect_group1.b_updated == e->total_nr_bparts)) + e->ti_earliest_undrifted = e->ti_current; + #ifdef SWIFT_DEBUG_CHECKS /* Verify that all cells have correct time-step information */ space_check_timesteps(e->s); @@ -2450,20 +2672,44 @@ void engine_step(struct engine *e) { error("Obtained a time-step of size 0"); #endif + /* Run the RT sub-cycling now. */ + engine_run_rt_sub_cycles(e); + #ifdef WITH_CSDS if (e->policy & engine_policy_csds && e->verbose) message("The CSDS currently uses %f GB of storage", e->collect_group1.csds_file_size_gb); #endif - /********************************************************/ - /* OK, we are done with the regular stuff. Time for i/o */ - /********************************************************/ + /********************************************************/ + /* OK, we are done with the regular stuff. Time for i/o */ + /********************************************************/ + +#ifdef WITH_LIGHTCONE + /* Flush lightcone buffers if necessary */ + const int flush = e->flush_lightcone_maps; + lightcone_array_flush(e->lightcone_array_properties, &(e->threadpool), + e->cosmology, e->internal_units, e->snapshot_units, + /*flush_map_updates=*/flush, /*flush_particles=*/0, + /*end_file=*/0, /*dump_all_shells=*/0); +#endif /* Create a restart file if needed. */ - engine_dump_restarts(e, 0, e->restart_onexit && engine_is_done(e)); + const int force_stop = + engine_dump_restarts(e, 0, e->restart_onexit && engine_is_done(e)); + + /* Is there any form of i/o this step? + * + * Note that if the run was forced to stop, we do not dump, + * we will do so when the run is restarted*/ + if (!force_stop) engine_io(e); - engine_check_for_dumps(e); +#ifdef SWIFT_RT_DEBUG_CHECKS + /* if we're running the debug RT scheme, do some checks after every step. + * Do this after the output so we can safely reset debugging checks now. */ + if (e->policy & engine_policy_rt) + rt_debugging_checks_end_of_step(e, e->verbose); +#endif TIMER_TOC2(timer_step); @@ -2472,6 +2718,8 @@ void engine_step(struct engine *e) { /* Time in ticks at the end of this step. */ e->toc_step = getticks(); + + return force_stop; } /** @@ -2665,6 +2913,11 @@ void engine_pin(void) { #ifdef HAVE_SETAFFINITY cpu_set_t *entry_affinity = engine_entry_affinity(); + + /* Share this affinity with the threadpool, it will use this even when the + * main thread is otherwise pinned. */ + threadpool_set_affinity_mask(entry_affinity); + int pin; for (pin = 0; pin < CPU_SETSIZE && !CPU_ISSET(pin, entry_affinity); ++pin) ; @@ -2765,7 +3018,6 @@ void engine_numa_policies(int rank, int verbose) { * @param Nnuparts Total number of neutrino 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 ? * @param internal_units The system of units used internally. * @param physical_constants The #phys_const used for this run. * @param cosmo The #cosmology used for this run. @@ -2778,12 +3030,15 @@ void engine_numa_policies(int rank, int verbose) { * @param neutrinos The #neutrino_props used for this run. * @param feedback The #feedback_props used for this run. * @param mesh The #pm_mesh used for the long-range periodic forces. + * @param pow_data The properties and pointers for power spectrum calculation. * @param potential The properties of the external potential. * @param cooling_func The properties of the cooling function. * @param starform The #star_formation model of this run. * @param chemistry The chemistry information. + * @param io_extra_props The properties needed for the extra i/o fields. * @param fof_properties The #fof_props of this run. * @param los_properties the #los_props of this run. + * @param lightcone_array_properties the #lightcone_array_props of this run. * @param ics_metadata metadata read from the simulation ICs */ void engine_init( @@ -2791,7 +3046,7 @@ void engine_init( struct output_options *output_options, long long Ngas, long long Ngparts, long long Nsinks, long long Nstars, long long Nblackholes, long long Nbackground_gparts, long long Nnuparts, int policy, int verbose, - struct repartition *reparttype, const struct unit_system *internal_units, + const struct unit_system *internal_units, const struct phys_const *physical_constants, struct cosmology *cosmo, struct hydro_props *hydro, const struct entropy_floor_properties *entropy_floor, @@ -2799,12 +3054,16 @@ void engine_init( const struct black_holes_props *black_holes, const struct sink_props *sinks, const struct neutrino_props *neutrinos, struct neutrino_response *neutrino_response, - struct feedback_props *feedback, struct rt_props *rt, struct pm_mesh *mesh, + struct feedback_props *feedback, + struct pressure_floor_props *pressure_floor, struct rt_props *rt, + struct pm_mesh *mesh, struct power_spectrum_data *pow_data, const struct external_potential *potential, struct cooling_function_data *cooling_func, const struct star_formation *starform, const struct chemistry_global_data *chemistry, + struct extra_io_properties *io_extra_props, struct fof_props *fof_properties, struct los_props *los_properties, + struct lightcone_array_props *lightcone_array_properties, struct ic_info *ics_metadata) { struct clocks_time tic, toc; @@ -2826,9 +3085,9 @@ void engine_init( e->total_nr_neutrino_gparts = Nnuparts; e->proxy_ind = NULL; e->nr_proxies = 0; - e->reparttype = reparttype; e->ti_old = 0; e->ti_current = 0; + e->ti_earliest_undrifted = 0; e->time_step = 0.; e->time_base = 0.; e->time_base_inv = 0.; @@ -2836,8 +3095,23 @@ void engine_init( e->time_end = 0.; e->max_active_bin = num_time_bins; e->min_active_bin = 1; + e->ti_current_subcycle = 0; + e->max_active_bin_subcycle = num_time_bins; + e->min_active_bin_subcycle = 1; e->internal_units = internal_units; e->output_list_snapshots = NULL; + if (num_snapshot_triggers_part) + parser_get_param_double_array(params, "Snapshots:recording_triggers_part", + num_snapshot_triggers_part, + e->snapshot_recording_triggers_desired_part); + if (num_snapshot_triggers_spart) + parser_get_param_double_array(params, "Snapshots:recording_triggers_spart", + num_snapshot_triggers_spart, + e->snapshot_recording_triggers_desired_spart); + if (num_snapshot_triggers_bpart) + parser_get_param_double_array(params, "Snapshots:recording_triggers_bpart", + num_snapshot_triggers_bpart, + e->snapshot_recording_triggers_desired_bpart); e->a_first_snapshot = parser_get_opt_param_double(params, "Snapshots:scale_factor_first", 0.1); e->time_first_snapshot = @@ -2869,6 +3143,8 @@ void engine_init( parser_get_opt_param_int(params, "Snapshots:invoke_stf", 0); e->snapshot_invoke_fof = parser_get_opt_param_int(params, "Snapshots:invoke_fof", 0); + e->snapshot_invoke_ps = + parser_get_opt_param_int(params, "Snapshots:invoke_ps", 0); e->snapshot_use_delta_from_edge = parser_get_opt_param_int(params, "Snapshots:use_delta_from_edge", 0); if (e->snapshot_use_delta_from_edge) { @@ -2879,11 +3155,18 @@ void engine_init( parser_get_opt_param_int(params, "FOF:dump_catalogue_when_seeding", 0); e->snapshot_units = (struct unit_system *)malloc(sizeof(struct unit_system)); units_init_default(e->snapshot_units, params, "Snapshots", internal_units); + e->free_foreign_when_dumping_restart = parser_get_opt_param_int( + params, "Scheduler:free_foreign_during_restart", 0); + e->free_foreign_when_rebuilding = parser_get_opt_param_int( + params, "Scheduler:free_foreign_during_rebuild", 0); e->snapshot_output_count = 0; e->stf_output_count = 0; e->los_output_count = 0; + e->ps_output_count = 0; e->dt_min = parser_get_param_double(params, "TimeIntegration:dt_min"); e->dt_max = parser_get_param_double(params, "TimeIntegration:dt_max"); + e->max_nr_rt_subcycles = parser_get_opt_param_int( + params, "TimeIntegration:max_nr_rt_subcycles", /*default=*/0); e->dt_max_RMS_displacement = FLT_MAX; e->max_RMS_displacement_factor = parser_get_opt_param_double( params, "TimeIntegration:max_dt_RMS_factor", 0.25); @@ -2899,6 +3182,7 @@ void engine_init( e->ti_next_stats = 0; e->ti_next_stf = 0; e->ti_next_fof = 0; + e->ti_next_ps = 0; e->verbose = verbose; e->wallclock_time = 0.f; e->physical_constants = physical_constants; @@ -2912,17 +3196,21 @@ void engine_init( e->neutrino_properties = neutrinos; e->neutrino_response = neutrino_response; e->mesh = mesh; + e->power_data = pow_data; e->external_potential = potential; e->cooling_func = cooling_func; e->star_formation = starform; e->feedback_props = feedback; + e->pressure_floor_props = pressure_floor; e->rt_props = rt; e->chemistry = chemistry; + e->io_extra_props = io_extra_props; e->fof_properties = fof_properties; e->parameter_file = params; e->output_options = output_options; e->stf_this_timestep = 0; e->los_properties = los_properties; + e->lightcone_array_properties = lightcone_array_properties; e->ics_metadata = ics_metadata; #ifdef WITH_MPI e->usertime_last_step = 0.0; @@ -3002,6 +3290,16 @@ void engine_init( parser_get_opt_param_double(params, "LineOfSight:delta_time", -1.); } + /* Initialise power spectrum output. */ + if (e->policy & engine_policy_power_spectra) { + e->time_first_ps_output = + parser_get_opt_param_double(params, "PowerSpectrum:time_first", 0.); + e->a_first_ps_output = parser_get_opt_param_double( + params, "PowerSpectrum:scale_factor_first", 0.1); + e->delta_time_ps = + parser_get_opt_param_double(params, "PowerSpectrum:delta_time", -1.); + } + /* Initialise FoF calls frequency. */ if (e->policy & engine_policy_fof) { @@ -3291,6 +3589,7 @@ void engine_clean(struct engine *e, const int fof, const int restart) { output_list_clean(&e->output_list_stats); output_list_clean(&e->output_list_stf); output_list_clean(&e->output_list_los); + output_list_clean(&e->output_list_ps); output_options_clean(e->output_options); @@ -3339,6 +3638,7 @@ void engine_clean(struct engine *e, const int fof, const int restart) { free((void *)e->output_options); free((void *)e->external_potential); free((void *)e->black_holes_properties); + free((void *)e->pressure_floor_props); free((void *)e->rt_props); free((void *)e->sink_properties); free((void *)e->stars_properties); @@ -3349,15 +3649,18 @@ void engine_clean(struct engine *e, const int fof, const int restart) { free((void *)e->internal_units); free((void *)e->cosmology); free((void *)e->mesh); + free((void *)e->power_data); free((void *)e->chemistry); free((void *)e->entropy_floor); free((void *)e->cooling_func); free((void *)e->star_formation); free((void *)e->feedback_props); + free((void *)e->io_extra_props); #ifdef WITH_FOF free((void *)e->fof_properties); #endif free((void *)e->los_properties); + free((void *)e->lightcone_array_properties); free((void *)e->ics_metadata); #ifdef WITH_MPI free((void *)e->reparttype); @@ -3366,6 +3669,7 @@ void engine_clean(struct engine *e, const int fof, const int restart) { if (e->output_list_stats) free((void *)e->output_list_stats); if (e->output_list_stf) free((void *)e->output_list_stf); if (e->output_list_los) free((void *)e->output_list_los); + if (e->output_list_ps) free((void *)e->output_list_ps); #ifdef WITH_CSDS if (e->policy & engine_policy_csds) free((void *)e->csds); #endif @@ -3405,20 +3709,24 @@ void engine_struct_dump(struct engine *e, FILE *stream) { gravity_props_struct_dump(e->gravity_properties, stream); stars_props_struct_dump(e->stars_properties, stream); pm_mesh_struct_dump(e->mesh, stream); + power_spectrum_struct_dump(e->power_data, stream); potential_struct_dump(e->external_potential, stream); cooling_struct_dump(e->cooling_func, stream); starformation_struct_dump(e->star_formation, stream); feedback_struct_dump(e->feedback_props, stream); + pressure_floor_struct_dump(e->pressure_floor_props, stream); rt_struct_dump(e->rt_props, stream); black_holes_struct_dump(e->black_holes_properties, stream); sink_struct_dump(e->sink_properties, stream); neutrino_struct_dump(e->neutrino_properties, stream); neutrino_response_struct_dump(e->neutrino_response, stream); chemistry_struct_dump(e->chemistry, stream); + extra_io_struct_dump(e->io_extra_props, stream); #ifdef WITH_FOF fof_struct_dump(e->fof_properties, stream); #endif los_struct_dump(e->los_properties, stream); + lightcone_array_struct_dump(e->lightcone_array_properties, stream); ic_info_struct_dump(e->ics_metadata, stream); parser_struct_dump(e->parameter_file, stream); output_options_struct_dump(e->output_options, stream); @@ -3508,6 +3816,11 @@ void engine_struct_restore(struct engine *e, FILE *stream) { pm_mesh_struct_restore(mesh, stream); e->mesh = mesh; + struct power_spectrum_data *pow_data = + (struct power_spectrum_data *)malloc(sizeof(struct power_spectrum_data)); + power_spectrum_struct_restore(pow_data, stream); + e->power_data = pow_data; + struct external_potential *external_potential = (struct external_potential *)malloc(sizeof(struct external_potential)); potential_struct_restore(external_potential, stream); @@ -3529,9 +3842,16 @@ void engine_struct_restore(struct engine *e, FILE *stream) { feedback_struct_restore(feedback_properties, stream); e->feedback_props = feedback_properties; + struct pressure_floor_props *pressure_floor_properties = + (struct pressure_floor_props *)malloc( + sizeof(struct pressure_floor_props)); + pressure_floor_struct_restore(pressure_floor_properties, stream); + e->pressure_floor_props = pressure_floor_properties; + struct rt_props *rt_properties = (struct rt_props *)malloc(sizeof(struct rt_props)); - rt_struct_restore(rt_properties, stream); + rt_struct_restore(rt_properties, stream, e->physical_constants, + e->internal_units); e->rt_props = rt_properties; struct black_holes_props *black_holes_properties = @@ -3560,6 +3880,11 @@ void engine_struct_restore(struct engine *e, FILE *stream) { chemistry_struct_restore(chemistry, stream); e->chemistry = chemistry; + struct extra_io_properties *extra_io_props = + (struct extra_io_properties *)malloc(sizeof(struct extra_io_properties)); + extra_io_struct_restore(extra_io_props, stream); + e->io_extra_props = extra_io_props; + #ifdef WITH_FOF struct fof_props *fof_props = (struct fof_props *)malloc(sizeof(struct fof_props)); @@ -3572,6 +3897,12 @@ void engine_struct_restore(struct engine *e, FILE *stream) { los_struct_restore(los_properties, stream); e->los_properties = los_properties; + struct lightcone_array_props *lightcone_array_properties = + (struct lightcone_array_props *)malloc( + sizeof(struct lightcone_array_props)); + lightcone_array_struct_restore(lightcone_array_properties, stream); + e->lightcone_array_properties = lightcone_array_properties; + struct ic_info *ics_metadata = (struct ic_info *)malloc(sizeof(struct ic_info)); ic_info_struct_restore(ics_metadata, stream); diff --git a/src/engine.h b/src/engine.h index 3e1e4b42811a9ba82fcf656ff96acb2d0b7eaf6e..1aa0c304829908b2a70c9c53c7970da3931ff721 100644 --- a/src/engine.h +++ b/src/engine.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * Angus Lepper (angus.lepper@ed.ac.uk) * 2016 John A. Regan (john.a.regan@durham.ac.uk) @@ -25,7 +25,7 @@ #define SWIFT_ENGINE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -37,19 +37,23 @@ #include "clocks.h" #include "collectgroup.h" #include "ic_info.h" +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_array.h" #include "mesh_gravity.h" #include "output_options.h" #include "parser.h" #include "partition.h" -#include "potential.h" #include "runner.h" #include "scheduler.h" #include "space.h" #include "task.h" +#include "tracers_triggers.h" #include "units.h" #include "velociraptor_interface.h" struct black_holes_properties; +struct extra_io_properties; +struct external_potential; /** * @brief The different policies the #engine can follow. @@ -83,8 +87,9 @@ enum engine_policy { engine_policy_line_of_sight = (1 << 24), engine_policy_sinks = (1 << 25), engine_policy_rt = (1 << 26), + engine_policy_power_spectra = (1 << 27), }; -#define engine_maxpolicy 27 +#define engine_maxpolicy 28 extern const char *engine_policy_names[engine_maxpolicy + 1]; /** @@ -101,16 +106,17 @@ enum engine_step_properties { engine_step_prop_stf = (1 << 6), engine_step_prop_fof = (1 << 7), engine_step_prop_mesh = (1 << 8), - engine_step_prop_done = (1 << 9), + engine_step_prop_power_spectra = (1 << 9), + engine_step_prop_done = (1 << 10), }; /* Some constants */ #define engine_maxproxies 64 #define engine_tasksreweight 1 #define engine_parts_size_grow 1.05 -#define engine_redistribute_alloc_margin 1.2 +#define engine_redistribute_alloc_margin_default 1.2 #define engine_rebuild_link_alloc_margin 1.2 -#define engine_foreign_alloc_margin 1.05 +#define engine_foreign_alloc_margin_default 1.05 #define engine_default_energy_file_name "statistics" #define engine_default_timesteps_file_name "timesteps" #define engine_max_parts_per_ghost_default 1000 @@ -182,12 +188,23 @@ struct engine { double time; integertime_t ti_current; + /* The earliest time any particle may still need to be drifted from */ + integertime_t ti_earliest_undrifted; + /* The highest active bin at this time */ timebin_t max_active_bin; /* The lowest active bin at this time */ timebin_t min_active_bin; + /* RT sub-cycling counterparts for timestepping vars */ + integertime_t ti_current_subcycle; + timebin_t max_active_bin_subcycle; + timebin_t min_active_bin_subcycle; + + /* Maximal number of radiative transfer sub-cycles per hydro step */ + int max_nr_rt_subcycles; + /* Time step */ double time_step; @@ -204,6 +221,12 @@ struct engine { /* Maximal hydro ti_beg for the next time-step */ integertime_t ti_hydro_beg_max; + /* Minimal rt ti_end for the next time-step */ + integertime_t ti_rt_end_min; + + /* Maximal rt ti_beg for the next time-step */ + integertime_t ti_rt_beg_max; + /* Minimal gravity ti_end for the next time-step */ integertime_t ti_gravity_end_min; @@ -247,7 +270,7 @@ struct engine { integertime_t ti_beg_max; /* Number of particles updated in the previous step */ - long long updates, g_updates, s_updates, b_updates, sink_updates; + long long updates, g_updates, s_updates, b_updates, sink_updates, rt_updates; /* Number of updates since the last rebuild */ long long updates_since_rebuild; @@ -292,8 +315,8 @@ struct engine { long long count_inhibited_bparts; #endif - /* Maximal ID of the parts (used for the generation of new IDs when splitting) - */ + /* Maximal ID of the parts, *excluding* background particles + * (used for the generation of new IDs when splitting) */ long long max_parts_id; /* Total mass in the simulation */ @@ -327,11 +350,25 @@ struct engine { int snapshot_compression; int snapshot_invoke_stf; int snapshot_invoke_fof; + int snapshot_invoke_ps; struct unit_system *snapshot_units; int snapshot_use_delta_from_edge; double snapshot_delta_from_edge; int snapshot_output_count; + /* Snapshot recording trigger mechanism counters */ + double snapshot_recording_triggers_part[num_snapshot_triggers_part]; + double snapshot_recording_triggers_desired_part[num_snapshot_triggers_part]; + int snapshot_recording_triggers_started_part[num_snapshot_triggers_part]; + + double snapshot_recording_triggers_spart[num_snapshot_triggers_spart]; + double snapshot_recording_triggers_desired_spart[num_snapshot_triggers_spart]; + int snapshot_recording_triggers_started_spart[num_snapshot_triggers_spart]; + + double snapshot_recording_triggers_bpart[num_snapshot_triggers_bpart]; + double snapshot_recording_triggers_desired_bpart[num_snapshot_triggers_bpart]; + int snapshot_recording_triggers_started_bpart[num_snapshot_triggers_bpart]; + /* Metadata from the ICs */ struct ic_info *ics_metadata; @@ -363,6 +400,18 @@ struct engine { int run_fof; int dump_catalogue_when_seeding; + /* power spectrum information */ + double a_first_ps_output; + double time_first_ps_output; + double delta_time_ps; + int ps_output_count; + + /* Output_List for the power spectrum */ + struct output_list *output_list_ps; + + /* Integer time of the next ps output */ + integertime_t ti_next_ps; + /* Statistics information */ double a_first_statistics; double time_first_statistics; @@ -481,6 +530,9 @@ struct engine { /* The mesh used for long-range gravity forces */ struct pm_mesh *mesh; + /* Properties and pointers for the power spectrum */ + struct power_spectrum_data *power_data; + /* Properties of external gravitational potential */ const struct external_potential *external_potential; @@ -493,12 +545,18 @@ struct engine { /* Properties of the sellar feedback model */ struct feedback_props *feedback_props; + /* Properties of the pressure floor scheme */ + struct pressure_floor_props *pressure_floor_props; + /* Properties of the radiative transfer model */ struct rt_props *rt_props; /* Properties of the chemistry model */ const struct chemistry_global_data *chemistry; + /* Properties used to compute the extra i/o fields */ + struct extra_io_properties *io_extra_props; + /*! The FOF properties data. */ struct fof_props *fof_properties; @@ -524,9 +582,35 @@ struct engine { /* Number of Lustre OSTs on the system to use as rank-based striping offset */ int restart_lustre_OST_count; + /* Do we free the foreign data before writing restart files? */ + int free_foreign_when_dumping_restart; + + /* Do we free the foreign data before rebuilding the tree? */ + int free_foreign_when_rebuilding; + + /* Name of the restart file directory. */ + const char *restart_dir; + /* Name of the restart file. */ const char *restart_file; + /* Flag whether we should resubmit on this step? */ + int resubmit; + + /* Do we want to run the resubmission command once a run reaches the time + * limit? */ + int resubmit_after_max_hours; + + /* What command should we run to resubmit at the end? */ + char resubmit_command[PARSER_MAX_LINE_SIZE]; + + /* How often to check for the stop file and dump restarts and exit the + * application. */ + int restart_stop_steps; + + /* Get the maximal wall-clock time of this run */ + float restart_max_hours_runtime; + /* Ticks between restart dumps. */ ticks restart_dt; @@ -558,6 +642,9 @@ struct engine { /* Line of sight properties. */ struct los_props *los_properties; + /* Line of sight properties. */ + struct lightcone_array_props *lightcone_array_properties; + /* Line of sight outputs information. */ struct output_list *output_list_los; double a_first_los; @@ -566,6 +653,9 @@ struct engine { integertime_t ti_next_los; int los_output_count; + /* Lightcone information */ + int flush_lightcone_maps; + #ifdef SWIFT_GRAVITY_FORCE_CHECKS /* Run brute force checks only on steps when all gparts active? */ int force_checks_only_all_active; @@ -589,15 +679,19 @@ void engine_compute_next_stf_time(struct engine *e); void engine_compute_next_fof_time(struct engine *e); void engine_compute_next_statistics_time(struct engine *e); void engine_compute_next_los_time(struct engine *e); +void engine_compute_next_ps_time(struct engine *e); void engine_recompute_displacement_constraint(struct engine *e); void engine_unskip(struct engine *e); +void engine_unskip_rt_sub_cycle(struct engine *e); void engine_drift_all(struct engine *e, const int drift_mpoles); void engine_drift_top_multipoles(struct engine *e); void engine_reconstruct_multipoles(struct engine *e); void engine_allocate_foreign_particles(struct engine *e, const int fof); void engine_print_stats(struct engine *e); -void engine_check_for_dumps(struct engine *e); +void engine_io(struct engine *e); +void engine_io_check_snapshot_triggers(struct engine *e); void engine_collect_end_of_step(struct engine *e, int apply); +void engine_collect_end_of_sub_cycle(struct engine *e); void engine_dump_snapshot(struct engine *e); void engine_run_on_dump(struct engine *e); void engine_init_output_lists(struct engine *e, struct swift_params *params, @@ -607,7 +701,7 @@ void engine_init( struct output_options *output_options, long long Ngas, long long Ngparts, long long Nsinks, long long Nstars, long long Nblackholes, long long Nbackground_gparts, long long Nnuparts, int policy, int verbose, - struct repartition *reparttype, const struct unit_system *internal_units, + const struct unit_system *internal_units, const struct phys_const *physical_constants, struct cosmology *cosmo, struct hydro_props *hydro, const struct entropy_floor_properties *entropy_floor, @@ -615,22 +709,28 @@ void engine_init( const struct black_holes_props *black_holes, const struct sink_props *sinks, const struct neutrino_props *neutrinos, struct neutrino_response *neutrino_response, - struct feedback_props *feedback, struct rt_props *rt, struct pm_mesh *mesh, + struct feedback_props *feedback, + struct pressure_floor_props *pressure_floor, struct rt_props *rt, + struct pm_mesh *mesh, struct power_spectrum_data *pow_data, const struct external_potential *potential, struct cooling_function_data *cooling_func, const struct star_formation *starform, const struct chemistry_global_data *chemistry, + struct extra_io_properties *io_extra_props, struct fof_props *fof_properties, struct los_props *los_properties, + struct lightcone_array_props *lightcone_array_properties, struct ic_info *ics_metadata); void engine_config(int restart, int fof, struct engine *e, struct swift_params *params, int nr_nodes, int nodeID, int nr_task_threads, int nr_pool_threads, int with_aff, - int verbose, const char *restart_file); + int verbose, const char *restart_dir, + const char *restart_file, struct repartition *reparttype); void engine_launch(struct engine *e, const char *call); int engine_prepare(struct engine *e); +void engine_run_rt_sub_cycles(struct engine *e); void engine_init_particles(struct engine *e, int flag_entropy_ICs, int clean_h_values); -void engine_step(struct engine *e); +int engine_step(struct engine *e); void engine_split(struct engine *e, struct partition *initial_partition); void engine_exchange_strays(struct engine *e, const size_t offset_parts, const int *ind_part, size_t *Npart, @@ -676,6 +776,6 @@ void engine_numa_policies(int rank, int verbose); /* Struct dump/restore support. */ void engine_struct_dump(struct engine *e, FILE *stream); void engine_struct_restore(struct engine *e, FILE *stream); -void engine_dump_restarts(struct engine *e, int drifted_all, int final_step); +int engine_dump_restarts(struct engine *e, int drifted_all, int force); #endif /* SWIFT_ENGINE_H */ diff --git a/src/engine_collect_end_of_step.c b/src/engine_collect_end_of_step.c index 9f96556463b0d1895d4cd643435a591a6a7dd673..edca57333c939646e7e04fd22bf2ff2798e544b3 100644 --- a/src/engine_collect_end_of_step.c +++ b/src/engine_collect_end_of_step.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,13 +20,14 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "engine.h" /* Local headers. */ #include "active.h" +#include "lightcone/lightcone_array.h" #include "star_formation_logger.h" #include "timeline.h" @@ -38,6 +39,7 @@ struct end_of_step_data { size_t updated, g_updated, s_updated, sink_updated, b_updated; size_t inhibited, g_inhibited, s_inhibited, sink_inhibited, b_inhibited; integertime_t ti_hydro_end_min, ti_hydro_beg_max; + integertime_t ti_rt_end_min, ti_rt_beg_max; integertime_t ti_gravity_end_min, ti_gravity_beg_max; integertime_t ti_stars_end_min, ti_stars_beg_max; integertime_t ti_sinks_end_min, ti_sinks_beg_max; @@ -45,6 +47,7 @@ struct end_of_step_data { struct engine *e; struct star_formation_history sfh; float runtime; + int flush_lightcone_maps; double deadtime; float csds_file_size_gb; }; @@ -73,6 +76,7 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, size_t updated = 0, g_updated = 0, s_updated = 0, sink_updated = 0, b_updated = 0; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0; + integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_beg_max = 0; integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_beg_max = 0; integertime_t ti_sinks_end_min = max_nr_timesteps, ti_sinks_beg_max = 0; @@ -96,6 +100,10 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, ti_hydro_end_min = min(ti_hydro_end_min, c->hydro.ti_end_min); ti_hydro_beg_max = max(ti_hydro_beg_max, c->hydro.ti_beg_max); + if (c->rt.ti_rt_end_min > e->ti_current) + ti_rt_end_min = min(c->rt.ti_rt_end_min, ti_rt_end_min); + ti_rt_beg_max = max(c->rt.ti_rt_beg_max, ti_rt_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_beg_max = max(ti_gravity_beg_max, c->grav.ti_beg_max); @@ -154,6 +162,10 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, data->ti_hydro_end_min = min(ti_hydro_end_min, data->ti_hydro_end_min); data->ti_hydro_beg_max = max(ti_hydro_beg_max, data->ti_hydro_beg_max); + if (ti_rt_end_min > e->ti_current) + data->ti_rt_end_min = min(ti_rt_end_min, data->ti_rt_end_min); + data->ti_rt_beg_max = max(ti_rt_beg_max, data->ti_rt_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); @@ -203,6 +215,7 @@ void engine_collect_end_of_step(struct engine *e, int apply) { data.updated = 0, data.g_updated = 0, data.s_updated = 0, data.b_updated = 0; data.sink_updated = 0; data.ti_hydro_end_min = max_nr_timesteps, data.ti_hydro_beg_max = 0; + data.ti_rt_end_min = max_nr_timesteps, data.ti_rt_beg_max = 0; data.ti_gravity_end_min = max_nr_timesteps, data.ti_gravity_beg_max = 0; data.ti_stars_end_min = max_nr_timesteps, data.ti_stars_beg_max = 0; data.ti_sinks_end_min = max_nr_timesteps, data.ti_sinks_beg_max = 0; @@ -219,6 +232,11 @@ void engine_collect_end_of_step(struct engine *e, int apply) { /* Need to use a consistent check of the hours since we started. */ data.runtime = clocks_get_hours_since_start(); + /* Get flag to determine if lightcone maps buffers should be flushed on this + * step */ + data.flush_lightcone_maps = + lightcone_array_trigger_map_update(e->lightcone_array_properties); + data.deadtime = e->local_deadtime; /* Initialize the total SFH of the simulation to zero */ @@ -242,12 +260,14 @@ void engine_collect_end_of_step(struct engine *e, int apply) { &e->collect_group1, data.updated, data.g_updated, data.s_updated, data.sink_updated, data.b_updated, data.inhibited, data.g_inhibited, data.s_inhibited, data.sink_inhibited, data.b_inhibited, - data.ti_hydro_end_min, data.ti_hydro_beg_max, data.ti_gravity_end_min, - data.ti_gravity_beg_max, data.ti_stars_end_min, data.ti_stars_beg_max, - data.ti_sinks_end_min, data.ti_sinks_beg_max, data.ti_black_holes_end_min, + data.ti_hydro_end_min, data.ti_hydro_beg_max, data.ti_rt_end_min, + data.ti_rt_beg_max, data.ti_gravity_end_min, data.ti_gravity_beg_max, + data.ti_stars_end_min, data.ti_stars_beg_max, data.ti_sinks_end_min, + data.ti_sinks_beg_max, data.ti_black_holes_end_min, 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, data.runtime, data.deadtime, data.csds_file_size_gb); + data.sfh, data.runtime, data.flush_lightcone_maps, data.deadtime, + data.csds_file_size_gb); /* Aggregate collective data from the different nodes for this step. */ #ifdef WITH_MPI @@ -332,3 +352,70 @@ void engine_collect_end_of_step(struct engine *e, int apply) { message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); } + +/** + * @brief Mapping function to collect the data from the end of the sub-cycle + * + * @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_sub_cycle_mapper(void *map_data, int num_elements, + void *extra_data) { + + struct engine *e = (struct engine *)extra_data; + struct space *s = e->s; + int *local_cells = (int *)map_data; + + /* Local collectible */ + long long rt_updated = 0LL; + + for (int ind = 0; ind < num_elements; ind++) { + struct cell *c = &s->cells_top[local_cells[ind]]; + + if (c->hydro.count > 0) { + + /* Aggregate data */ + rt_updated += c->rt.updated; + + /* Collected, so clear for next time. */ + c->rt.updated = 0; + } + } + + /* write back to the global data. */ + atomic_add(&e->rt_updates, rt_updated); +} + +/** + * @brief Collects additional data at the end of a subcycle. + * This function does not collect any data relevant to the + * time-steps or time integration. + * + * @param e The #engine. + */ +void engine_collect_end_of_sub_cycle(struct engine *e) { + + const ticks tic = getticks(); + struct space *s = e->s; + + /* Collect information from the local top-level cells */ + threadpool_map(&e->threadpool, engine_collect_end_of_sub_cycle_mapper, + s->local_cells_top, s->nr_local_cells, sizeof(int), + threadpool_auto_chunk_size, e); + + /* Aggregate collective data from the different nodes for this step. */ +#ifdef WITH_MPI + long long rt_updates_tot = 0ll; + int test = MPI_Reduce(&e->rt_updates, &rt_updates_tot, 1, MPI_LONG_LONG, + MPI_SUM, 0, MPI_COMM_WORLD); + if (test != MPI_SUCCESS) error("MPI reduce failed"); + /* Overwrite only on rank 0. */ + if (e->nodeID == 0) e->rt_updates = rt_updates_tot; +#endif + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} diff --git a/src/engine_config.c b/src/engine_config.c index bb6f74c378d6b9d52485f1e3b95b4e801a00ccd4..e6b028fdee2ab2bbbea1a29b77d268a4def3095c 100644 --- a/src/engine_config.c +++ b/src/engine_config.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,8 +20,10 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ +#include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> @@ -37,7 +39,9 @@ #include "fof.h" #include "mpiuse.h" #include "part.h" +#include "pressure_floor.h" #include "proxy.h" +#include "rt.h" #include "star_formation.h" #include "star_formation_logger.h" #include "stars_io.h" @@ -156,11 +160,13 @@ static void engine_dumper_init(struct engine *e) { * @param with_aff use processor affinity, if supported. * @param verbose Is this #engine talkative ? * @param restart_file The name of our restart file. + * @param reparttype What type of repartition algorithm are we using. */ void engine_config(int restart, int fof, struct engine *e, struct swift_params *params, int nr_nodes, int nodeID, int nr_task_threads, int nr_pool_threads, int with_aff, - int verbose, const char *restart_file) { + int verbose, const char *restart_dir, + const char *restart_file, struct repartition *reparttype) { struct clocks_time tic, toc; if (nodeID == 0) clocks_gettime(&tic); @@ -184,11 +190,30 @@ void engine_config(int restart, int fof, struct engine *e, e->verbose = verbose; e->wallclock_time = 0.f; e->restart_dump = 0; + e->restart_dir = restart_dir; e->restart_file = restart_file; + e->resubmit = 0; e->restart_next = 0; e->restart_dt = 0; e->run_fof = 0; + /* Seed rand(). */ + srand(clocks_random_seed()); + + /* Allow repartitioning to be changed between restarts. On restart this is + * already allocated and freed on exit, so we need to copy over. */ +#ifdef WITH_MPI + if (restart) { + int *celllist = e->reparttype->celllist; + int ncelllist = e->reparttype->ncelllist; + memcpy(e->reparttype, reparttype, sizeof(struct repartition)); + e->reparttype->celllist = celllist; + e->reparttype->ncelllist = ncelllist; + } else { + e->reparttype = reparttype; + } +#endif + if (restart && fof) { error( "Can't configure the engine to be a stand-alone FOF and restarting " @@ -198,6 +223,21 @@ void engine_config(int restart, int fof, struct engine *e, /* Welcome message */ if (e->nodeID == 0) message("Running simulation '%s'.", e->run_name); + /* Check-pointing properties */ + + e->restart_stop_steps = + parser_get_opt_param_int(params, "Restarts:stop_steps", 100); + + e->restart_max_hours_runtime = + parser_get_opt_param_float(params, "Restarts:max_run_time", FLT_MAX); + + e->resubmit_after_max_hours = + parser_get_opt_param_int(params, "Restarts:resubmit_on_exit", 0); + + if (e->resubmit_after_max_hours) + parser_get_param_string(params, "Restarts:resubmit_command", + e->resubmit_command); + /* Get the number of queues */ int nr_queues = parser_get_opt_param_int(params, "Scheduler:nr_queues", e->nr_threads); @@ -212,6 +252,9 @@ void engine_config(int restart, int fof, struct engine *e, if (e->sched.frequency_dependency < 0) { error("Scheduler:dependency_graph_frequency should be >= 0"); } + /* Get cellID for extra dependency graph dumps of specific cell */ + e->sched.dependency_graph_cellID = parser_get_opt_param_longlong( + params, "Scheduler:dependency_graph_cell", 0LL); /* Get the frequency of the task level dumping */ e->sched.frequency_task_levels = parser_get_opt_param_int( @@ -461,7 +504,7 @@ void engine_config(int restart, int fof, struct engine *e, fprintf(e->file_timesteps, "# %6s %14s %12s %12s %14s %9s %12s %12s %12s %12s %12s %16s " - "[%s] %6s %s [%s]\n", + "[%s] %6s %12s [%s]\n", "Step", "Time", "Scale-factor", "Redshift", "Time-step", "Time-bins", "Updates", "g-Updates", "s-Updates", "Sink-Updates", "b-Updates", "Wall-clock time", clocks_getunit(), "Props", @@ -491,6 +534,7 @@ void engine_config(int restart, int fof, struct engine *e, /* Print information about the hydro scheme */ if (e->policy & engine_policy_hydro) { if (e->nodeID == 0) hydro_props_print(e->hydro_properties); + if (e->nodeID == 0) pressure_floor_print(e->pressure_floor_props); if (e->nodeID == 0) entropy_floor_print(e->entropy_floor); } @@ -502,6 +546,30 @@ void engine_config(int restart, int fof, struct engine *e, if (e->policy & engine_policy_stars) if (e->nodeID == 0) stars_props_print(e->stars_properties); + /* Print information about the RT scheme */ + if (e->policy & engine_policy_rt) { + rt_props_print(e->rt_props); + if (e->nodeID == 0) { + if (e->max_nr_rt_subcycles <= 1) + message("WARNING: running without RT sub-cycling."); + else { + /* Make sure max_nr_rt_subcycles is an acceptable power of 2 */ + timebin_t power_subcycles = 0; + while ((e->max_nr_rt_subcycles > (1 << power_subcycles)) && + power_subcycles < num_time_bins) + ++power_subcycles; + if (power_subcycles == num_time_bins) + error("TimeIntegration:max_nr_rt_subcycles=%d too big", + e->max_nr_rt_subcycles); + if ((1 << power_subcycles) > e->max_nr_rt_subcycles) + error("TimeIntegration:max_nr_rt_subcycles=%d not a power of 2", + e->max_nr_rt_subcycles); + message("Running up to %d RT sub-cycles per hydro step.", + e->max_nr_rt_subcycles); + } + } + } + /* Check we have sensible time bounds */ if (e->time_begin >= e->time_end) error( @@ -739,6 +807,12 @@ void engine_config(int restart, int fof, struct engine *e, if (e->nodeID == 0) message("Using %d threads in the thread-pool", nr_pool_threads); + /* Cells per thread buffer. */ + e->s->cells_sub = + (struct cell **)calloc(nr_pool_threads + 1, sizeof(struct cell *)); + e->s->multipoles_sub = (struct gravity_tensors **)calloc( + nr_pool_threads + 1, sizeof(struct gravity_tensors *)); + /* First of all, init the barrier and lock it. */ if (swift_barrier_init(&e->wait_barrier, NULL, e->nr_threads + 1) != 0 || swift_barrier_init(&e->run_barrier, NULL, e->nr_threads + 1) != 0) @@ -796,13 +870,13 @@ void engine_config(int restart, int fof, struct engine *e, 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); + 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); + 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); + 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); + params, "Scheduler:cell_extra_bparts", space_extra_bparts_default); /* Do we want any spare particles for on the fly creation? This condition should be the same than in space.c */ diff --git a/src/engine_drift.c b/src/engine_drift.c index ccabb07575afc638e8162d96516c380619d95422..3896640a796f8692807dd0b29475cb0889c786ca 100644 --- a/src/engine_drift.c +++ b/src/engine_drift.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * Angus Lepper (angus.lepper@ed.ac.uk) * 2016 John A. Regan (john.a.regan@durham.ac.uk) @@ -23,10 +23,11 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "engine.h" +#include "lightcone/lightcone_array.h" /** * @brief Mapper function to drift *all* the #part to the current time. @@ -71,7 +72,7 @@ void engine_do_drift_all_part_mapper(void *map_data, int num_elements, if (c->nodeID == e->nodeID) { /* Drift all the particles */ - cell_drift_part(c, e, /* force the drift=*/1); + cell_drift_part(c, e, /* force the drift=*/1, NULL); } } } @@ -119,7 +120,7 @@ void engine_do_drift_all_gpart_mapper(void *map_data, int num_elements, if (c->nodeID == e->nodeID) { /* Drift all the particles */ - cell_drift_gpart(c, e, /* force the drift=*/1); + cell_drift_gpart(c, e, /* force the drift=*/1, /*replication_list=*/NULL); } } } @@ -167,7 +168,7 @@ void engine_do_drift_all_spart_mapper(void *map_data, int num_elements, if (c->nodeID == e->nodeID) { /* Drift all the particles */ - cell_drift_spart(c, e, /* force the drift=*/1); + cell_drift_spart(c, e, /* force the drift=*/1, NULL); } } } @@ -215,7 +216,7 @@ void engine_do_drift_all_bpart_mapper(void *map_data, int num_elements, if (c->nodeID == e->nodeID) { /* Drift all the particles */ - cell_drift_bpart(c, e, /* force the drift=*/1); + cell_drift_bpart(c, e, /* force the drift=*/1, NULL); } } } @@ -323,15 +324,20 @@ void engine_drift_all(struct engine *e, const int drift_mpoles) { const ticks tic = getticks(); -#ifdef SWIFT_DEBUG_CHECKS - if (e->nodeID == 0) { + if (e->nodeID == 0 && e->verbose) { if (e->policy & engine_policy_cosmology) - message("Drifting all to a=%e", + message("Drifting all to a=%15.12e", exp(e->ti_current * e->time_base) * e->cosmology->a_begin); else - message("Drifting all to t=%e", + message("Drifting all to t=%15.12e", e->ti_current * e->time_base + e->time_begin); } + +#ifdef WITH_LIGHTCONE + /* Determine which periodic replications could contribute to the lightcone + during this time step */ + lightcone_array_prepare_for_step(e->lightcone_array_properties, e->cosmology, + e->ti_earliest_undrifted, e->ti_current); #endif if (!e->restarting) { @@ -421,9 +427,21 @@ void engine_drift_all(struct engine *e, const int drift_mpoles) { e->verbose); #endif + /* All particles have now been drifted to ti_current */ + e->ti_earliest_undrifted = e->ti_current; + if (e->verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); + +#ifdef WITH_LIGHTCONE + /* Drifting all of the particles can cause many particles to cross + the lightcone, so flush buffers now to reduce peak memory use . */ + lightcone_array_flush(e->lightcone_array_properties, &e->threadpool, + e->cosmology, e->internal_units, e->snapshot_units, + /*flush_map_updates=*/1, /*flush_particles=*/1, + /*end_file=*/0, /*dump_all_shells=*/0); +#endif } /** diff --git a/src/engine_fof.c b/src/engine_fof.c index 940143b852c0fac7a6cb737bf4a44800591e2158..7cda72195d7cd10fee4b4102bb4c8569cbf9f87f 100644 --- a/src/engine_fof.c +++ b/src/engine_fof.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "engine.h" diff --git a/src/engine_io.c b/src/engine_io.c index 4a3721385d820ea3dcc689ea2b54541c0ef62014..c8e86f4ee8bd451469d0d7de15e38f9730def089 100644 --- a/src/engine_io.c +++ b/src/engine_io.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -31,36 +31,172 @@ #include "engine.h" /* Local headers. */ +#include "active.h" #include "csds_io.h" #include "distributed_io.h" #include "kick.h" +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_array.h" #include "line_of_sight.h" #include "parallel_io.h" +#include "power_spectrum.h" #include "serial_io.h" #include "single_io.h" +#include "tracers.h" +/* Standard includes */ #include <stdio.h> +/** + * @brief Finalize the quantities recorded via the snapshot triggers + * + * This adds the small amount of time between the particles' last start + * of step and the current (snapshot) time. + * + * @param e The #engine to act on. + */ +void engine_finalize_trigger_recordings(struct engine *e) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + struct space *s = e->s; + + /* Finish the recording period for part triggers */ + if (num_snapshot_triggers_part) { + for (size_t k = 0; k < s->nr_parts; ++k) { + + /* Get a handle on the part. */ + struct part *p = &s->parts[k]; + struct xpart *xp = &s->xparts[k]; + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current, p->time_bin); + + /* Escape inhibited particles */ + if (part_is_inhibited(p, e)) continue; + + /* We need to escape the special case of a particle that + * actually ended its time-step on this very step */ + if (e->ti_current - ti_begin == get_integer_timestep(p->time_bin)) + continue; + + /* Time from the start of the particle's step to the snapshot (aka. + * current time) */ + double missing_time; + if (with_cosmology) { + missing_time = + cosmology_get_delta_time(e->cosmology, ti_begin, e->ti_current); + } else { + missing_time = (e->ti_current - ti_begin) * e->time_base; + } + + tracers_after_timestep_part( + p, xp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, e->hydro_properties, e->cooling_func, e->time, + missing_time, e->snapshot_recording_triggers_started_part); + } + } + + /* Finish the recording period for spart triggers */ + if (num_snapshot_triggers_spart) { + for (size_t k = 0; k < s->nr_sparts; ++k) { + + /* Get a handle on the part. */ + struct spart *sp = &s->sparts[k]; + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current, sp->time_bin); + + /* Escape inhibited particles */ + if (spart_is_inhibited(sp, e)) continue; + + /* We need to escape the special case of a particle that + * actually ended its time-step on this very step */ + if (e->ti_current - ti_begin == get_integer_timestep(sp->time_bin)) + continue; + + /* Time from the start of the particle's step to the snapshot (aka. + * current time) */ + double missing_time; + if (with_cosmology) { + missing_time = + cosmology_get_delta_time(e->cosmology, ti_begin, e->ti_current); + } else { + missing_time = (e->ti_current - ti_begin) * e->time_base; + } + + tracers_after_timestep_spart( + sp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, missing_time, + e->snapshot_recording_triggers_started_spart); + } + } + + /* Finish the recording period for spart triggers */ + if (num_snapshot_triggers_bpart) { + for (size_t k = 0; k < s->nr_bparts; ++k) { + + /* Get a handle on the part. */ + struct bpart *bp = &s->bparts[k]; + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current, bp->time_bin); + + /* Escape inhibited particles */ + if (bpart_is_inhibited(bp, e)) continue; + + /* We need to escape the special case of a particle that + * actually ended its time-step on this very step */ + if (e->ti_current - ti_begin == get_integer_timestep(bp->time_bin)) + continue; + + /* Time from the start of the particle's step to the snapshot (aka. + * current time) */ + double missing_time; + if (with_cosmology) { + missing_time = + cosmology_get_delta_time(e->cosmology, ti_begin, e->ti_current); + } else { + missing_time = (e->ti_current - ti_begin) * e->time_base; + } + + tracers_after_timestep_bpart( + bp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, missing_time, + e->snapshot_recording_triggers_started_bpart); + } + } +} + /** * @brief dump restart files if it is time to do so and dumps are enabled. * * @param e the engine. * @param drifted_all true if a drift_all has just been performed. * @param force force a dump, if dumping is enabled. + * @return Do we want to stop the run altogether? */ -void engine_dump_restarts(struct engine *e, int drifted_all, int force) { +int engine_dump_restarts(struct engine *e, const int drifted_all, + const int force) { + + /* Are any of the conditions to fully stop a run met? */ + const int end_run_time = e->runtime > e->restart_max_hours_runtime; + const int stop_file = (e->step % e->restart_stop_steps == 0 && + restart_stop_now(e->restart_dir, 0)); + + /* Exit run when told to */ + const int exit_run = (end_run_time || stop_file); if (e->restart_dump) { ticks tic = getticks(); + const int check_point_time = tic > e->restart_next; + /* Dump when the time has arrived, or we are told to. */ - int dump = ((tic > e->restart_next) || force); + int dump = (check_point_time || end_run_time || force || stop_file); #ifdef WITH_MPI /* Synchronize this action from rank 0 (ticks may differ between * machines). */ MPI_Bcast(&dump, 1, MPI_INT, 0, MPI_COMM_WORLD); #endif + if (dump) { if (e->nodeID == 0) { @@ -78,6 +214,24 @@ void engine_dump_restarts(struct engine *e, int drifted_all, int force) { /* Drift all particles first (may have just been done). */ if (!drifted_all) engine_drift_all(e, /*drift_mpole=*/1); + + /* Free the foreign particles to get more breathing space. */ +#ifdef WITH_MPI + if (e->free_foreign_when_dumping_restart) + space_free_foreign_parts(e->s, /*clear_cell_pointers=*/1); +#endif + +#ifdef WITH_LIGHTCONE + /* Flush lightcone buffers before dumping restarts */ + lightcone_array_flush(e->lightcone_array_properties, &(e->threadpool), + e->cosmology, e->internal_units, e->snapshot_units, + /*flush_map_updates=*/1, /*flush_particles=*/1, + /*end_file=*/1, /*dump_all_shells=*/0); +#ifdef WITH_MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif +#endif + restart_write(e, e->restart_file); #ifdef WITH_MPI @@ -85,6 +239,10 @@ void engine_dump_restarts(struct engine *e, int drifted_all, int force) { * sets of restart files should the code crash before all the ranks * are done */ MPI_Barrier(MPI_COMM_WORLD); + + /* Reallocate freed memory */ + if (e->free_foreign_when_dumping_restart) + engine_allocate_foreign_particles(e, /*fof=*/0); #endif if (e->verbose) @@ -98,6 +256,12 @@ void engine_dump_restarts(struct engine *e, int drifted_all, int force) { e->step_props |= engine_step_prop_restarts; } } + + /* If we stopped by reaching the time limit, flag that we need to + * run the resubmission command */ + if (end_run_time && e->resubmit_after_max_hours) e->resubmit = 1; + + return exit_run; } /** @@ -140,9 +304,12 @@ void engine_dump_snapshot(struct engine *e) { engine_collect_stars_counter(e); #endif + /* Finalize the recording periods */ + engine_finalize_trigger_recordings(e); + /* Get time-step since the last mesh kick */ if ((e->policy & engine_policy_self_gravity) && e->s->periodic) { - const int with_cosmology = e->policy & engine_policy_cosmology; + const int with_cosmology = (e->policy & engine_policy_cosmology); e->dt_kick_grav_mesh_for_io = kick_get_grav_kick_dt(e->mesh->ti_beg_mesh_next, e->ti_current, @@ -176,6 +343,22 @@ void engine_dump_snapshot(struct engine *e) { #endif #endif + /* Cancel any triggers that are switched on */ + if (num_snapshot_triggers_part || num_snapshot_triggers_spart || + num_snapshot_triggers_bpart) { + + /* Reset the trigger flags */ + for (int i = 0; i < num_snapshot_triggers_part; ++i) + e->snapshot_recording_triggers_started_part[i] = 0; + for (int i = 0; i < num_snapshot_triggers_spart; ++i) + e->snapshot_recording_triggers_started_spart[i] = 0; + for (int i = 0; i < num_snapshot_triggers_bpart; ++i) + e->snapshot_recording_triggers_started_bpart[i] = 0; + + /* Reser the tracers themselves */ + space_after_snap_tracer(e->s, e->verbose); + } + /* Flag that we dumped a snapshot */ e->step_props |= engine_step_prop_snapshot; @@ -224,17 +407,19 @@ void engine_run_on_dump(struct engine *e) { * * @param e The #engine. */ -void engine_check_for_dumps(struct engine *e) { +void engine_io(struct engine *e) { const int with_cosmology = (e->policy & engine_policy_cosmology); const int with_stf = (e->policy & engine_policy_structure_finding); const int with_los = (e->policy & engine_policy_line_of_sight); const int with_fof = (e->policy & engine_policy_fof); + const int with_power = (e->policy & engine_policy_power_spectra); /* What kind of output are we getting? */ enum output_type { output_none, output_snapshot, output_statistics, + output_ps, output_stf, output_los, }; @@ -262,6 +447,14 @@ void engine_check_for_dumps(struct engine *e) { } } + /* Do we want a power-spectrum? */ + if (e->ti_end_min > e->ti_next_ps && e->ti_next_ps > 0) { + if (e->ti_next_ps < ti_output) { + ti_output = e->ti_next_ps; + type = output_ps; + } + } + /* Do we want to perform structure finding? */ if (with_stf) { if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) { @@ -341,6 +534,12 @@ void engine_check_for_dumps(struct engine *e) { #endif } + /* Do we want power spectrum outputs? */ + if (with_power && e->snapshot_invoke_ps) { + calc_all_power_spectra(e->power_data, e->s, &e->threadpool, + e->verbose); + } + /* Dump... */ engine_dump_snapshot(e); @@ -415,6 +614,16 @@ void engine_check_for_dumps(struct engine *e) { break; + case output_ps: + + /* Compute the PS */ + calc_all_power_spectra(e->power_data, e->s, &e->threadpool, e->verbose); + + /* Move on */ + engine_compute_next_ps_time(e); + + break; + default: error("Invalid dump type"); } @@ -441,6 +650,14 @@ void engine_check_for_dumps(struct engine *e) { } } + /* Do we want a power-spectrum? */ + if (e->ti_end_min > e->ti_next_ps && e->ti_next_ps > 0) { + if (e->ti_next_ps < ti_output) { + ti_output = e->ti_next_ps; + type = output_ps; + } + } + /* Do we want to perform structure finding? */ if (with_stf) { if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) { @@ -538,6 +755,50 @@ void engine_compute_next_snapshot_time(struct engine *e) { if (e->verbose) message("Next snapshot time set to t=%e.", next_snapshot_time); } + + /* Time until the next snapshot */ + double time_to_next_snap; + if (e->policy & engine_policy_cosmology) { + time_to_next_snap = cosmology_get_delta_time(e->cosmology, e->ti_current, + e->ti_next_snapshot); + } else { + time_to_next_snap = (e->ti_next_snapshot - e->ti_current) * e->time_base; + } + + /* Do we need to reduce any of the recording trigger times? */ + for (int k = 0; k < num_snapshot_triggers_part; ++k) { + if (e->snapshot_recording_triggers_desired_part[k] > 0) { + if (e->snapshot_recording_triggers_desired_part[k] > + time_to_next_snap) { + e->snapshot_recording_triggers_part[k] = time_to_next_snap; + } else { + e->snapshot_recording_triggers_part[k] = + e->snapshot_recording_triggers_desired_part[k]; + } + } + } + for (int k = 0; k < num_snapshot_triggers_spart; ++k) { + if (e->snapshot_recording_triggers_desired_spart[k] > 0) { + if (e->snapshot_recording_triggers_desired_spart[k] > + time_to_next_snap) { + e->snapshot_recording_triggers_spart[k] = time_to_next_snap; + } else { + e->snapshot_recording_triggers_spart[k] = + e->snapshot_recording_triggers_desired_spart[k]; + } + } + } + for (int k = 0; k < num_snapshot_triggers_bpart; ++k) { + if (e->snapshot_recording_triggers_desired_bpart[k] > 0) { + if (e->snapshot_recording_triggers_desired_bpart[k] > + time_to_next_snap) { + e->snapshot_recording_triggers_bpart[k] = time_to_next_snap; + } else { + e->snapshot_recording_triggers_bpart[k] = + e->snapshot_recording_triggers_desired_bpart[k]; + } + } + } } } @@ -810,6 +1071,76 @@ void engine_compute_next_fof_time(struct engine *e) { } } +/** + * @brief Computes the next time (on the time line) for a power-spectrum dump + * + * @param e The #engine. + */ +void engine_compute_next_ps_time(struct engine *e) { + /* Do output_list file case */ + if (e->output_list_ps) { + output_list_read_next_time(e->output_list_ps, e, "power spectrum", + &e->ti_next_ps); + return; + } + + /* Find upper-bound on last output */ + double time_end; + if (e->policy & engine_policy_cosmology) + time_end = e->cosmology->a_end * e->delta_time_ps; + else + time_end = e->time_end + e->delta_time_ps; + + /* Find next ps above current time */ + double time; + if (e->policy & engine_policy_cosmology) + time = e->a_first_ps_output; + else + time = e->time_first_ps_output; + + int found_ps_time = 0; + while (time < time_end) { + + /* Output time on the integer timeline */ + if (e->policy & engine_policy_cosmology) + e->ti_next_ps = log(time / e->cosmology->a_begin) / e->time_base; + else + e->ti_next_ps = (time - e->time_begin) / e->time_base; + + /* Found it? */ + if (e->ti_next_ps > e->ti_current) { + found_ps_time = 1; + break; + } + + if (e->policy & engine_policy_cosmology) + time *= e->delta_time_ps; + else + time += e->delta_time_ps; + } + + /* Deal with last line of sight */ + if (!found_ps_time) { + e->ti_next_ps = -1; + if (e->verbose) message("No further PS output time."); + } else { + + /* Be nice, talk... */ + if (e->policy & engine_policy_cosmology) { + const double next_ps_time = + exp(e->ti_next_ps * e->time_base) * e->cosmology->a_begin; + if (e->verbose) + message("Next output time for power spectrum set to a=%e.", + next_ps_time); + } else { + const double next_ps_time = e->ti_next_ps * e->time_base + e->time_begin; + if (e->verbose) + message("Next output time for power spectrum set to t=%e.", + next_ps_time); + } + } +} + /** * @brief Initialize all the output_list required by the engine * @@ -859,31 +1190,262 @@ void engine_init_output_lists(struct engine *e, struct swift_params *params, } /* Deal with stf */ - e->output_list_stf = NULL; - output_list_init(&e->output_list_stf, e, "StructureFinding", - &e->delta_time_stf); + if (e->policy & engine_policy_structure_finding) { - if (e->output_list_stf) { - engine_compute_next_stf_time(e); + e->output_list_stf = NULL; + output_list_init(&e->output_list_stf, e, "StructureFinding", + &e->delta_time_stf); - if (e->policy & engine_policy_cosmology) - e->a_first_stf_output = - exp(e->ti_next_stf * e->time_base) * e->cosmology->a_begin; - else - e->time_first_stf_output = e->ti_next_stf * e->time_base + e->time_begin; + if (e->output_list_stf) { + engine_compute_next_stf_time(e); + + if (e->policy & engine_policy_cosmology) + e->a_first_stf_output = + exp(e->ti_next_stf * e->time_base) * e->cosmology->a_begin; + else + e->time_first_stf_output = + e->ti_next_stf * e->time_base + e->time_begin; + } } /* Deal with line of sight */ - e->output_list_los = NULL; - output_list_init(&e->output_list_los, e, "LineOfSight", &e->delta_time_los); + if (e->policy & engine_policy_line_of_sight) { - if (e->output_list_los) { - engine_compute_next_los_time(e); + e->output_list_los = NULL; + output_list_init(&e->output_list_los, e, "LineOfSight", &e->delta_time_los); - if (e->policy & engine_policy_cosmology) - e->a_first_los = - exp(e->ti_next_los * e->time_base) * e->cosmology->a_begin; - else - e->time_first_los = e->ti_next_los * e->time_base + e->time_begin; + if (e->output_list_los) { + engine_compute_next_los_time(e); + + if (e->policy & engine_policy_cosmology) + e->a_first_los = + exp(e->ti_next_los * e->time_base) * e->cosmology->a_begin; + else + e->time_first_los = e->ti_next_los * e->time_base + e->time_begin; + } + } + + /* Deal with power-spectra */ + if (e->policy & engine_policy_power_spectra) { + + e->output_list_ps = NULL; + output_list_init(&e->output_list_ps, e, "PowerSpectrum", &e->delta_time_ps); + + if (e->output_list_ps) { + engine_compute_next_ps_time(e); + + if (e->policy & engine_policy_cosmology) + e->a_first_ps_output = + exp(e->ti_next_ps * e->time_base) * e->cosmology->a_begin; + else + e->time_first_ps_output = e->ti_next_ps * e->time_base + e->time_begin; + } + } +} + +/** + * @brief Checks whether we passed a certain delta time before the next + * snapshot and need to trigger a recording. + * + * If a recording has to start, we also loop over the particles to correct + * for the time between the particles' end of time-step and the actual start + * of trigger. + * + * @param e The #engine. + */ +void engine_io_check_snapshot_triggers(struct engine *e) { + + struct space *s = e->s; + const int with_cosmology = (e->policy & engine_policy_cosmology); + + /* Time until the next snapshot */ + double time_to_next_snap; + if (e->policy & engine_policy_cosmology) { + time_to_next_snap = cosmology_get_delta_time(e->cosmology, e->ti_current, + e->ti_next_snapshot); + } else { + time_to_next_snap = (e->ti_next_snapshot - e->ti_current) * e->time_base; + } + + /* Should any not yet switched on trigger be activated? (part version) */ + for (int i = 0; i < num_snapshot_triggers_part; ++i) { + + if (time_to_next_snap <= e->snapshot_recording_triggers_part[i] && + e->snapshot_recording_triggers_part[i] > 0. && + !e->snapshot_recording_triggers_started_part[i]) { + e->snapshot_recording_triggers_started_part[i] = 1; + + /* Be vocal about this */ + if (e->verbose) + message( + "Snapshot will be dumped in %e U_t. Recording trigger for part " + "activated.", + e->snapshot_recording_triggers_part[i]); + + /* We now need to loop over the particles to preemptively deduct the + * extra time logged between the particles' start of step and the + * actual start of the trigger */ + for (size_t k = 0; k < s->nr_parts; ++k) { + + /* Get a handle on the part. */ + struct part *p = &s->parts[k]; + struct xpart *xp = &s->xparts[k]; + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current, p->time_bin); + + /* Escape inhibited particles */ + if (part_is_inhibited(p, e)) continue; + + /* Time from the start of the particle's step to the snapshot */ + double total_time; + if (with_cosmology) { + total_time = cosmology_get_delta_time(e->cosmology, ti_begin, + e->ti_next_snapshot); + } else { + total_time = (e->ti_next_snapshot - ti_begin) * e->time_base; + } + + /* Time to deduct = time since the start of the step - trigger time */ + const double time_to_remove = + total_time - e->snapshot_recording_triggers_part[i]; + +#ifdef SWIFT_DEBUG_CHECKS + if (time_to_remove < 0.) + error("Invalid time to deduct! %e", time_to_remove); +#endif + + /* Note that we need to use a separate array (not the raw + * e->snapshot_recording_triggers_part) as we only want to + * update one entry */ + int my_temp_array[num_snapshot_triggers_part]; + memset(my_temp_array, 0, sizeof(int) * num_snapshot_triggers_part); + my_temp_array[i] = 1; + + tracers_after_timestep_part( + p, xp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, e->hydro_properties, e->cooling_func, e->time, + -time_to_remove, my_temp_array); + } + } + } + + /* Should any not yet switched on trigger be activated? (spart version) */ + for (int i = 0; i < num_snapshot_triggers_spart; ++i) { + + if (time_to_next_snap <= e->snapshot_recording_triggers_spart[i] && + e->snapshot_recording_triggers_spart[i] > 0. && + !e->snapshot_recording_triggers_started_spart[i]) { + e->snapshot_recording_triggers_started_spart[i] = 1; + + /* Be vocal about this */ + if (e->verbose) + message( + "Snapshot will be dumped in %e U_t. Recording trigger for spart " + "activated.", + e->snapshot_recording_triggers_spart[i]); + + /* We now need to loop over the particles to preemptively deduct the + * extra time logged between the particles' start of step and the + * actual start of the trigger */ + for (size_t k = 0; k < s->nr_sparts; ++k) { + + /* Get a handle on the spart. */ + struct spart *sp = &s->sparts[k]; + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current, sp->time_bin); + + /* Escape inhibited particles */ + if (spart_is_inhibited(sp, e)) continue; + + /* Time from the start of the particle's step to the snapshot */ + double total_time; + if (with_cosmology) { + total_time = cosmology_get_delta_time(e->cosmology, ti_begin, + e->ti_next_snapshot); + } else { + total_time = (e->ti_next_snapshot - ti_begin) * e->time_base; + } + + /* Time to deduct = time since the start of the step - trigger time */ + const double time_to_remove = + total_time - e->snapshot_recording_triggers_part[i]; + +#ifdef SWIFT_DEBUG_CHECKS + if (time_to_remove < 0.) + error("Invalid time to deduct! %e", time_to_remove); +#endif + + /* Note that we need to use a separate array (not the raw + * e->snapshot_recording_triggers_part) as we only want to + * update one entry */ + int my_temp_array[num_snapshot_triggers_spart]; + memset(my_temp_array, 0, sizeof(int) * num_snapshot_triggers_spart); + my_temp_array[i] = 1; + + tracers_after_timestep_spart( + sp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, -time_to_remove, my_temp_array); + } + } + } + + /* Should any not yet switched on trigger be activated? (bpart version) */ + for (int i = 0; i < num_snapshot_triggers_bpart; ++i) { + + if (time_to_next_snap <= e->snapshot_recording_triggers_bpart[i] && + e->snapshot_recording_triggers_bpart[i] > 0. && + !e->snapshot_recording_triggers_started_bpart[i]) { + e->snapshot_recording_triggers_started_bpart[i] = 1; + + /* Be vocal about this */ + if (e->verbose) + message( + "Snapshot will be dumped in %e U_t. Recording trigger for bpart " + "activated.", + e->snapshot_recording_triggers_bpart[i]); + + /* We now need to loop over the particles to preemptively deduct the + * extra time logged between the particles' start of step and the + * actual start of the trigger */ + for (size_t k = 0; k < s->nr_bparts; ++k) { + + /* Get a handle on the bpart. */ + struct bpart *bp = &s->bparts[k]; + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current, bp->time_bin); + + /* Escape inhibited particles */ + if (bpart_is_inhibited(bp, e)) continue; + + /* Time from the start of the particle's step to the snapshot */ + double total_time; + if (with_cosmology) { + total_time = cosmology_get_delta_time(e->cosmology, ti_begin, + e->ti_next_snapshot); + } else { + total_time = (e->ti_next_snapshot - ti_begin) * e->time_base; + } + + /* Time to deduct = time since the start of the step - trigger time */ + const double time_to_remove = + total_time - e->snapshot_recording_triggers_part[i]; + +#ifdef SWIFT_DEBUG_CHECKS + if (time_to_remove < 0.) + error("Invalid time to deduct! %e", time_to_remove); +#endif + + /* Note that we need to use a separate array (not the raw + * e->snapshot_recording_triggers_part) as we only want to + * update one entry */ + int my_temp_array[num_snapshot_triggers_bpart]; + memset(my_temp_array, 0, sizeof(int) * num_snapshot_triggers_bpart); + my_temp_array[i] = 1; + + tracers_after_timestep_bpart( + bp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, -time_to_remove, my_temp_array); + } + } } } diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c index 671281213f5f3775a5674dd3ac2b131ab48f25bc..68628c7881e65b721af8cc6cdad6b297e0bedffb 100644 --- a/src/engine_maketasks.c +++ b/src/engine_maketasks.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * Angus Lepper (angus.lepper@ed.ac.uk) * 2016 John A. Regan (john.a.regan@durham.ac.uk) @@ -23,7 +23,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdlib.h> @@ -130,17 +130,23 @@ void engine_addtasks_send_gravity(struct engine *e, struct cell *ci, * @param t_gradient The send_gradient #task, if already created. * @param t_prep1 The send_prep1 #task, if it has already been created. * @param t_limiter The send_limiter #task, if it has already been created. + * @param t_rt_gradient The send_rt_gradient #task, if it has already been + * created. + * @param t_rt_transport The send_rt_transport #task, if it has already been * @param with_feedback Are we running with stellar feedback? * @param with_limiter Are we running with the time-step limiter? * @param with_sync Are we running with time-step synchronization? + * @param with_rt Are we running with radiative transfer? */ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, struct cell *cj, struct task *t_xv, struct task *t_rho, struct task *t_gradient, struct task *t_prep1, struct task *t_limiter, struct task *t_pack_limiter, + struct task *t_rt_gradient, + struct task *t_rt_transport, const int with_feedback, const int with_limiter, - const int with_sync) { + const int with_sync, const int with_rt) { #ifdef WITH_MPI struct link *l = NULL; @@ -169,10 +175,12 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, 0, ci, cj); t_rho = scheduler_addtask(s, task_type_send, task_subtype_rho, ci->mpi.tag, 0, ci, cj); + scheduler_addunlock(s, t_xv, t_rho); #ifdef EXTRA_HYDRO_LOOP t_gradient = scheduler_addtask(s, task_type_send, task_subtype_gradient, ci->mpi.tag, 0, ci, cj); + scheduler_addunlock(s, t_rho, t_gradient); #endif if (with_limiter) { @@ -191,6 +199,17 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, } #endif + if (with_rt) { + /* Add the RT sends */ + t_rt_gradient = + scheduler_addtask(s, task_type_send, task_subtype_rt_gradient, + ci->mpi.tag, 0, ci, cj); + + t_rt_transport = + scheduler_addtask(s, task_type_send, task_subtype_rt_transport, + ci->mpi.tag, 0, ci, cj); + } + #ifdef EXTRA_HYDRO_LOOP scheduler_addunlock(s, t_gradient, ci->hydro.super->hydro.end_force); @@ -235,7 +254,39 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, scheduler_addunlock(s, t_prep1, ci->hydro.super->stars.prep2_ghost); } #endif - } + + if (with_rt) { + /* Don't send the transport stuff before the gradient stuff */ + scheduler_addunlock(s, t_rt_gradient, t_rt_transport); + + /* The send_gradient task depends on the cell's ghost1 task. */ + scheduler_addunlock(s, ci->hydro.super->rt.rt_ghost1, t_rt_gradient); + + /* The send_transport task depends on the cell's ghost2 task. */ + scheduler_addunlock(s, ci->hydro.super->rt.rt_ghost2, t_rt_transport); + + /* Safety measure: collect dependencies and make sure data is sent + * before modifying it */ + scheduler_addunlock(s, t_rt_gradient, ci->hydro.super->rt.rt_ghost2); + + /* Safety measure: collect dependencies and make sure data is sent + * before modifying it */ + scheduler_addunlock(s, t_rt_transport, + ci->hydro.super->rt.rt_transport_out); + + /* Drift before you send. Especially intended to cover inactive cells + * being sent. */ + scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_gradient); + scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_transport); + + /* Make sure the gradient sends don't run before the xv is finished. + * This can occur when a cell itself is inactive for both hydro and + * RT, but needs to be sent over for some other cell's pair task. + * The rt_gradient - xv dependency is special because when received, + * these two tasks will/may activate the sorts.*/ + scheduler_addunlock(s, t_xv, t_rt_gradient); + } + } /* if t_xv == NULL */ /* Add them to the local cell. */ engine_addlink(e, &ci->mpi.send, t_xv); @@ -250,6 +301,12 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, #ifdef EXTRA_STAR_LOOPS if (with_feedback) engine_addlink(e, &ci->mpi.send, t_prep1); #endif + + if (with_rt) { + /* Add them to the local cell. */ + engine_addlink(e, &ci->mpi.send, t_rt_gradient); + engine_addlink(e, &ci->mpi.send, t_rt_transport); + } } /* Recurse? */ @@ -258,7 +315,8 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, if (ci->progeny[k] != NULL) engine_addtasks_send_hydro( e, ci->progeny[k], cj, t_xv, t_rho, t_gradient, t_prep1, t_limiter, - t_pack_limiter, with_feedback, with_limiter, with_sync); + t_pack_limiter, t_rt_gradient, t_rt_transport, with_feedback, + with_limiter, with_sync, with_rt); #else error("SWIFT was not compiled with MPI support."); @@ -494,18 +552,26 @@ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci, * @param t_limiter The recv_limiter #task, if it has already been created. * @param t_unpack_limiter The unpack_limiter #task, if it has already been * created. + * @param t_rt_gradient The recv_rt_gradient #task, if it has already been + * created. + * @param t_rt_transport The recv_rt_transport #task, if it has already been + * created. + * @param t_rt_sorts The rt_sort #task, if it has already been created. * @param tend The top-level time-step communication #task. * @param with_feedback Are we running with stellar feedback? * @param with_black_holes Are we running with black holes? * @param with_limiter Are we running with the time-step limiter? * @param with_sync Are we running with time-step synchronization? + * @param with_rt Are we running with radiative transfer? */ void engine_addtasks_recv_hydro( struct engine *e, struct cell *c, struct task *t_xv, struct task *t_rho, struct task *t_gradient, struct task *t_prep1, struct task *t_limiter, - struct task *t_unpack_limiter, struct task *const tend, - const int with_feedback, const int with_black_holes, const int with_limiter, - const int with_sync) { + struct task *t_unpack_limiter, struct task *t_rt_gradient, + struct task *t_rt_transport, struct task *t_rt_sorts, + struct task *const tend, const int with_feedback, + const int with_black_holes, const int with_limiter, const int with_sync, + const int with_rt) { #ifdef WITH_MPI struct scheduler *s = &e->sched; @@ -532,6 +598,8 @@ void engine_addtasks_recv_hydro( #ifdef EXTRA_HYDRO_LOOP t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_gradient, c->mpi.tag, 0, c, NULL); + scheduler_addunlock(s, t_xv, t_gradient); + scheduler_addunlock(s, t_rho, t_gradient); #endif if (with_limiter) { @@ -549,6 +617,88 @@ void engine_addtasks_recv_hydro( c->mpi.tag, 0, c, NULL); } #endif + + if (with_rt) { + /* Create the tasks. */ + t_rt_gradient = scheduler_addtask( + s, task_type_recv, task_subtype_rt_gradient, c->mpi.tag, 0, c, NULL); + t_rt_transport = scheduler_addtask( + s, task_type_recv, task_subtype_rt_transport, c->mpi.tag, 0, c, NULL); + /* Also create the rt_advance_cell_time tasks for the foreign cells + * for the sub-cycling. */ +#ifdef SWIFT_RT_DEBUG_CHECKS + if (c->super == NULL) + error("trying to add rt_advance_cell_time above super level..."); +#endif + + /* Create the RT collect times task at the top level, if it hasn't + * already. */ + if (c->top->rt.rt_collect_times == NULL) { + c->top->rt.rt_collect_times = + scheduler_addtask(s, task_type_rt_collect_times, task_subtype_none, + 0, 0, c->top, NULL); + } + + /* Create the RT advance times task at the super level, if it hasn't + * already. Also set all the dependencies */ + if (c->super->rt.rt_advance_cell_time == NULL) { + + c->super->rt.rt_advance_cell_time = + scheduler_addtask(s, task_type_rt_advance_cell_time, + task_subtype_none, 0, 0, c->super, NULL); + + /* Don't run collect times before you run advance cell time */ + scheduler_addunlock(s, c->super->rt.rt_advance_cell_time, + c->top->rt.rt_collect_times); + + /* In normal steps, tend mustn't run before rt_advance_cell_time or the + * cell's ti_rt_end_min will be updated wrongly. In sub-cycles, we don't + * have the tend tasks, so there's no worry about that. (Them missing is + * the reason we need the rt_advanced_cell_time to complete the + * sub-cycles in the first place) */ + scheduler_addunlock(s, c->super->rt.rt_advance_cell_time, tend); + } + + /* Make sure we sort after receiving RT data. The hydro sorts may or may + * not be active. Blocking them with dependencies deadlocks with MPI. So + * add a new sort task instead, which will just do nothing if the cell is + * already sorted. */ + t_rt_sorts = scheduler_addtask(s, task_type_rt_sort, task_subtype_none, 0, + 0, c, NULL); + c->rt.rt_sorts = t_rt_sorts; + if (c->hydro.sorts != NULL) { + /* Copy task flags. While these should always be empty for sorts, better + * be safe than spend hours looking for this. */ + t_rt_sorts->flags = c->hydro.sorts->flags; + /* Make sure the normal hydro sorts run before the RT sorts run. */ + scheduler_addunlock(s, c->hydro.sorts, t_rt_sorts); + /* Don't run gradients on unsorted cells. */ + scheduler_addunlock(s, c->hydro.sorts, t_rt_gradient); + } + + /* Make sure the second receive doesn't get enqueued before the first one + * is done */ + scheduler_addunlock(s, t_rt_gradient, t_rt_sorts); + scheduler_addunlock(s, t_rt_gradient, t_rt_transport); + /* Avoid situation where we receive while the sort hasn't finished yet. */ + scheduler_addunlock(s, t_rt_sorts, t_rt_transport); + /* If one or both recv tasks are active, make sure the + * rt_advance_cell_time tasks doesn't run before them */ + scheduler_addunlock(s, t_rt_gradient, c->super->rt.rt_advance_cell_time); + scheduler_addunlock(s, t_rt_transport, c->super->rt.rt_advance_cell_time); + /* Make sure the gradient recv don't run before the xv is finished. + * This can occur when a cell itself is inactive for both hydro and + * RT, but needs to be sent over for some other cell's pair task. + * For active cells, you must make sure that t_rho and t_gradient have + * been received first. As there is no guarantee which message will + * arrive first, you might overwrite data otherwise. */ + + scheduler_addunlock(s, t_xv, t_rt_gradient); + scheduler_addunlock(s, t_rho, t_rt_gradient); +#ifdef EXTRA_HYDRO_LOOP + scheduler_addunlock(s, t_gradient, t_rt_gradient); +#endif + } } if (t_xv != NULL) { @@ -569,6 +719,9 @@ void engine_addtasks_recv_hydro( if (c->hydro.sorts != NULL) { scheduler_addunlock(s, t_xv, c->hydro.sorts); scheduler_addunlock(s, c->hydro.sorts, t_rho); +#if defined(MPI_SYMMETRIC_FORCE_INTERACTION) && defined(EXTRA_HYDRO_LOOP) + scheduler_addunlock(s, c->hydro.sorts, t_gradient); +#endif } for (struct link *l = c->hydro.density; l != NULL; l = l->next) { @@ -628,16 +781,47 @@ void engine_addtasks_recv_hydro( scheduler_addunlock(s, t_rho, l->t); } } + + if (with_rt) { + engine_addlink(e, &c->mpi.recv, t_rt_gradient); + engine_addlink(e, &c->mpi.recv, t_rt_transport); + + /* RT recvs mustn't run before hydro force has completed. */ + for (struct link *l = c->hydro.force; l != NULL; l = l->next) { + scheduler_addunlock(s, l->t, t_rt_gradient); + } + + for (struct link *l = c->rt.rt_gradient; l != NULL; l = l->next) { + /* RT gradient tasks mustn't run before we receive necessary data */ + scheduler_addunlock(s, t_rt_gradient, l->t); + /* Don't run gradient tasks without sorting */ + scheduler_addunlock(s, t_rt_sorts, l->t); + /* Don't update local particles before gradient tasks are finished */ + scheduler_addunlock(s, l->t, t_rt_transport); + } + + for (struct link *l = c->rt.rt_transport; l != NULL; l = l->next) { + /* RT transport tasks (iact, not comm tasks!!) mustn't run before we + * receive necessary data */ + scheduler_addunlock(s, t_rt_transport, l->t); + /* add dependency for the timestep communication tasks. In cases where + * RT is inactive, rt_advance_cell_time won't run, so we need to make + * sure we don't receive data before we're done with all the work. */ + scheduler_addunlock(s, l->t, tend); + /* advance cell time mustn't run before transport is done */ + scheduler_addunlock(s, l->t, c->super->rt.rt_advance_cell_time); + } + } } /* Recurse? */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) - engine_addtasks_recv_hydro(e, c->progeny[k], t_xv, t_rho, t_gradient, - t_prep1, t_limiter, t_unpack_limiter, tend, - with_feedback, with_black_holes, - with_limiter, with_sync); + engine_addtasks_recv_hydro( + e, c->progeny[k], t_xv, t_rho, t_gradient, t_prep1, t_limiter, + t_unpack_limiter, t_rt_gradient, t_rt_transport, t_rt_sorts, tend, + with_feedback, with_black_holes, with_limiter, with_sync, with_rt); #else error("SWIFT was not compiled with MPI support."); @@ -944,6 +1128,7 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { const int with_timestep_limiter = (e->policy & engine_policy_timestep_limiter); const int with_timestep_sync = (e->policy & engine_policy_timestep_sync); + const int with_rt = (e->policy & engine_policy_rt); #ifdef WITH_CSDS const int with_csds = e->policy & engine_policy_csds; #endif @@ -964,16 +1149,21 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { if (with_star_formation_sink && (c->hydro.count > 0 || c->sinks.count > 0)) { - c->hydro.star_formation_sink = scheduler_addtask( + c->sinks.star_formation_sink = scheduler_addtask( s, task_type_star_formation_sink, task_subtype_none, 0, 0, c, NULL); } if (with_sinks) { - /* hydro.sink_formation plays the role of a ghost => always created when + /* sinks.sink_formation plays the role of a ghost => always created when * playing with sinks*/ - c->hydro.sink_formation = scheduler_addtask( + c->sinks.sink_formation = scheduler_addtask( s, task_type_sink_formation, task_subtype_none, 0, 0, c, NULL); } + + if (with_rt) { + c->rt.rt_collect_times = scheduler_addtask( + s, task_type_rt_collect_times, task_subtype_none, 0, 0, c, NULL); + } } /* Are we in a super-cell ? */ @@ -1033,8 +1223,8 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { if (with_star_formation_sink && (c->hydro.count > 0 || c->sinks.count > 0)) { scheduler_addunlock(s, kick2_or_csds, - c->top->hydro.star_formation_sink); - scheduler_addunlock(s, c->top->hydro.star_formation_sink, c->timestep); + c->top->sinks.star_formation_sink); + scheduler_addunlock(s, c->top->sinks.star_formation_sink, c->timestep); } /* Time-step limiter */ @@ -1297,7 +1487,7 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct 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_sink, + scheduler_addunlock(s, c->top->sinks.star_formation_sink, c->hydro.stars_resort); } } @@ -1351,6 +1541,9 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, c->stars.drift = scheduler_addtask(s, task_type_drift_spart, task_subtype_none, 0, 0, c, NULL); scheduler_addunlock(s, c->stars.drift, c->super->kick2); + + if (with_star_formation && c->top->hydro.count > 0) + scheduler_addunlock(s, c->stars.drift, c->top->hydro.star_formation); } /* Sinks */ @@ -1363,8 +1556,12 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, scheduler_addtask(s, task_type_sink_in, task_subtype_none, 0, /* implicit = */ 1, c, NULL); - c->sinks.ghost = - scheduler_addtask(s, task_type_sink_ghost, task_subtype_none, 0, + c->sinks.sink_ghost1 = + scheduler_addtask(s, task_type_sink_ghost1, task_subtype_none, 0, + /* implicit = */ 1, c, NULL); + + c->sinks.sink_ghost2 = + scheduler_addtask(s, task_type_sink_ghost2, task_subtype_none, 0, /* implicit = */ 1, c, NULL); c->sinks.sink_out = @@ -1378,7 +1575,7 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, if (with_stars && (c->top->hydro.count > 0 || c->top->sinks.count > 0)) { scheduler_addunlock(s, c->hydro.super->sinks.sink_out, - c->top->hydro.star_formation_sink); + c->top->sinks.star_formation_sink); } } @@ -1463,53 +1660,70 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c, /* Radiative Transfer */ if (with_rt) { /* RT ghost in task */ - c->hydro.rt_in = + c->rt.rt_in = scheduler_addtask(s, task_type_rt_in, task_subtype_none, 0, /* implicit= */ 1, c, NULL); - scheduler_addunlock(s, c->super->kick2, c->hydro.rt_in); + scheduler_addunlock(s, c->super->kick2, c->rt.rt_in); /* Star formation */ if (c->top->hydro.count > 0 && with_star_formation) - scheduler_addunlock(s, c->top->hydro.star_formation, c->hydro.rt_in); + scheduler_addunlock(s, c->top->hydro.star_formation, c->rt.rt_in); /* Star formation from sinks */ if (with_star_formation_sink && (c->top->hydro.count > 0 || c->top->sinks.count > 0)) - scheduler_addunlock(s, c->top->hydro.star_formation_sink, - c->hydro.rt_in); + scheduler_addunlock(s, c->top->sinks.star_formation_sink, + c->rt.rt_in); if (with_feedback) - scheduler_addunlock(s, c->stars.stars_out, c->hydro.rt_in); + scheduler_addunlock(s, c->stars.stars_out, c->rt.rt_in); /* TODO: check/add dependencies from Loic's new sink SF tasks */ /* RT ghost out task */ - c->hydro.rt_out = + c->rt.rt_out = scheduler_addtask(s, task_type_rt_out, task_subtype_none, 0, /* implicit= */ 1, c, NULL); - scheduler_addunlock(s, c->hydro.rt_out, c->super->timestep); + scheduler_addunlock(s, c->rt.rt_out, c->super->timestep); + + /* In cases where nothing but RT is active, don't allow the timestep + * collect to run before we've finished */ + scheduler_addunlock(s, c->rt.rt_out, c->super->timestep_collect); /* non-implicit ghost 1 */ - c->hydro.rt_ghost1 = scheduler_addtask( - s, task_type_rt_ghost1, task_subtype_none, 0, 0, c, NULL); - /* add the explicit dependency on kick2 for cases where injection - * gets skipped */ - scheduler_addunlock(s, c->super->kick2, c->hydro.rt_ghost1); - /* add the explicit dependency for the timestep task for the - * cases where we have active sparts, but no active parts */ - scheduler_addunlock(s, c->hydro.rt_ghost1, c->super->timestep); + c->rt.rt_ghost1 = scheduler_addtask(s, task_type_rt_ghost1, + task_subtype_none, 0, 0, c, NULL); + scheduler_addunlock(s, c->rt.rt_in, c->rt.rt_ghost1); /* non-implicit ghost 2 */ - c->hydro.rt_ghost2 = scheduler_addtask( - s, task_type_rt_ghost2, task_subtype_none, 0, 0, c, NULL); + c->rt.rt_ghost2 = scheduler_addtask(s, task_type_rt_ghost2, + task_subtype_none, 0, 0, c, NULL); /* implicit transport out */ - c->hydro.rt_transport_out = + c->rt.rt_transport_out = scheduler_addtask(s, task_type_rt_transport_out, task_subtype_none, 0, /*implicit= */ 1, c, NULL); - /* non-implicit ghost 2 */ - c->hydro.rt_tchem = scheduler_addtask(s, task_type_rt_tchem, - task_subtype_none, 0, 0, c, NULL); + /* thermochemistry */ + c->rt.rt_tchem = scheduler_addtask(s, task_type_rt_tchem, + task_subtype_none, 0, 0, c, NULL); + + /* Advance cell time for subcycling */ + /* We need to make sure that rt_advance_cell_time is at the same level + * as the timestep task, not below. Otherwise, the updated cell times + * won't propagate up the hierarchy enough, and the cell_is_rt_active + * will return bogus results. Note that c->super is not necessarily + * c->hydro.super in general. */ + /* Create the task only once ! */ + if (c->super->rt.rt_advance_cell_time == NULL) { + c->super->rt.rt_advance_cell_time = + scheduler_addtask(s, task_type_rt_advance_cell_time, + task_subtype_none, 0, 0, c->super, NULL); + /* Don't run the rt_collect_times before the rt_advance_cell_time */ + scheduler_addunlock(s, c->super->rt.rt_advance_cell_time, + c->top->rt.rt_collect_times); + } - scheduler_addunlock(s, c->hydro.rt_transport_out, c->hydro.rt_tchem); - scheduler_addunlock(s, c->hydro.rt_tchem, c->hydro.rt_out); + scheduler_addunlock(s, c->rt.rt_transport_out, c->rt.rt_tchem); + scheduler_addunlock(s, c->rt.rt_tchem, + c->super->rt.rt_advance_cell_time); + scheduler_addunlock(s, c->super->rt.rt_advance_cell_time, c->rt.rt_out); } /* Subgrid tasks: black hole feedback */ @@ -1606,24 +1820,35 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]}; struct cell *cells = s->cells_top; - const double theta_crit = e->gravity_properties->theta_crit; const double max_distance = e->mesh->r_cut_max; const double max_distance2 = max_distance * max_distance; - /* Compute how many cells away we need to walk */ - const double distance = 2.5 * cells[0].width[0] / theta_crit; - int delta = (int)(distance / cells[0].width[0]) + 1; + /* Compute maximal distance where we can expect a direct interaction */ + const float distance = gravity_M2L_min_accept_distance( + e->gravity_properties, sqrtf(3) * cells[0].width[0], s->max_softening, + s->min_a_grav, s->max_mpole_power, periodic); + + /* Convert the maximal search distance to a number of cells + * Define a lower and upper delta in case things are not symmetric */ + const int delta = max((int)(sqrt(3) * distance / cells[0].width[0]) + 1, 2); int delta_m = delta; int delta_p = delta; /* Special case where every cell is in range of every other one */ - if (delta >= cdim[0] / 2) { - if (cdim[0] % 2 == 0) { - delta_m = cdim[0] / 2; - delta_p = cdim[0] / 2 - 1; - } else { - delta_m = cdim[0] / 2; - delta_p = cdim[0] / 2; + if (periodic) { + if (delta >= cdim[0] / 2) { + if (cdim[0] % 2 == 0) { + delta_m = cdim[0] / 2; + delta_p = cdim[0] / 2 - 1; + } else { + delta_m = cdim[0] / 2; + delta_p = cdim[0] / 2; + } + } + } else { + if (delta > cdim[0]) { + delta_m = cdim[0]; + delta_p = cdim[0]; } } @@ -1638,8 +1863,9 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, const int j = (cid / cdim[2]) % cdim[1]; const int k = cid % cdim[2]; - /* Get the cell */ + /* Get the first cell */ struct cell *ci = &cells[cid]; + /* Skip cells without gravity particles */ if (ci->grav.count == 0) continue; @@ -1650,20 +1876,27 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, } /* Loop over every other cell within (Manhattan) range delta */ - for (int ii = -delta_m; ii <= delta_p; ii++) { - int iii = i + ii; - if (!periodic && (iii < 0 || iii >= cdim[0])) continue; - iii = (iii + cdim[0]) % cdim[0]; - for (int jj = -delta_m; jj <= delta_p; jj++) { - int jjj = j + jj; - if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue; - jjj = (jjj + cdim[1]) % cdim[1]; - for (int kk = -delta_m; kk <= delta_p; kk++) { - int kkk = k + kk; - if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue; - kkk = (kkk + cdim[2]) % cdim[2]; + for (int ii = i - delta_m; ii <= i + delta_p; ii++) { + + /* Escape if non-periodic and beyond range */ + if (!periodic && (ii < 0 || ii >= cdim[0])) continue; - /* Get the cell */ + for (int jj = j - delta_m; jj <= j + delta_p; jj++) { + + /* Escape if non-periodic and beyond range */ + if (!periodic && (jj < 0 || jj >= cdim[1])) continue; + + for (int kk = k - delta_m; kk <= k + delta_p; kk++) { + + /* Escape if non-periodic and beyond range */ + if (!periodic && (kk < 0 || kk >= cdim[2])) continue; + + /* Apply periodic BC (not harmful if not using periodic BC) */ + const int iii = (ii + cdim[0]) % cdim[0]; + const int jjj = (jj + cdim[1]) % cdim[1]; + const int kkk = (kk + cdim[2]) % cdim[2]; + + /* Get the second cell */ const int cjd = cell_getid(cdim, iii, jjj, kkk); struct cell *cj = &cells[cjd]; @@ -1672,6 +1905,7 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, (ci->nodeID != nodeID && cj->nodeID != nodeID)) continue; +#ifdef WITH_MPI /* Recover the multipole information */ const struct gravity_tensors *multi_i = ci->grav.multipole; const struct gravity_tensors *multi_j = cj->grav.multipole; @@ -1680,6 +1914,7 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, error("Multipole of ci was not exchanged properly via the proxies"); if (multi_j == NULL && cj->nodeID != nodeID) error("Multipole of cj was not exchanged properly via the proxies"); +#endif /* Minimal distance between any pair of particles */ const double min_radius2 = @@ -2145,12 +2380,11 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, struct task *t_do_gas_swallow = NULL; struct task *t_do_bh_swallow = NULL; struct task *t_bh_feedback = NULL; - struct task *t_rt_inject = NULL; - struct task *t_sink_formation = NULL; + struct task *t_sink_swallow = NULL; struct task *t_rt_gradient = NULL; struct task *t_rt_transport = NULL; - struct task *t_sink_merger = NULL; - struct task *t_sink_accretion = NULL; + struct task *t_sink_do_sink_swallow = NULL; + struct task *t_sink_do_gas_swallow = NULL; for (int ind = 0; ind < num_elements; ind++) { @@ -2217,15 +2451,15 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, /* The sink tasks */ if (with_sink) { - t_sink_formation = scheduler_addtask( - sched, task_type_self, task_subtype_sink_compute_formation, flags, - 0, ci, NULL); - t_sink_merger = - scheduler_addtask(sched, task_type_self, task_subtype_sink_merger, + t_sink_swallow = + scheduler_addtask(sched, task_type_self, task_subtype_sink_swallow, flags, 0, ci, NULL); - t_sink_accretion = - scheduler_addtask(sched, task_type_self, - task_subtype_sink_accretion, flags, 0, ci, NULL); + t_sink_do_sink_swallow = scheduler_addtask( + sched, task_type_self, task_subtype_sink_do_sink_swallow, flags, 0, + ci, NULL); + t_sink_do_gas_swallow = scheduler_addtask( + sched, task_type_self, task_subtype_sink_do_gas_swallow, flags, 0, + ci, NULL); } /* The black hole feedback tasks */ @@ -2246,8 +2480,6 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - t_rt_inject = scheduler_addtask( - sched, task_type_self, task_subtype_rt_inject, flags, 0, ci, NULL); t_rt_gradient = scheduler_addtask(sched, task_type_self, task_subtype_rt_gradient, flags, 0, ci, NULL); @@ -2270,9 +2502,9 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, #endif } if (with_sink) { - engine_addlink(e, &ci->sinks.compute_formation, t_sink_formation); - engine_addlink(e, &ci->sinks.merger, t_sink_merger); - engine_addlink(e, &ci->sinks.accretion, t_sink_accretion); + engine_addlink(e, &ci->sinks.swallow, t_sink_swallow); + engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow); + engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow); } if (with_black_holes && bcount_i > 0) { engine_addlink(e, &ci->black_holes.density, t_bh_density); @@ -2282,9 +2514,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); } if (with_rt) { - engine_addlink(e, &ci->hydro.rt_inject, t_rt_inject); - engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient); - engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport); + engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient); + engine_addlink(e, &ci->rt.rt_transport, t_rt_transport); } #ifdef EXTRA_HYDRO_LOOP @@ -2350,23 +2581,30 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, /* The sink's tasks. */ if (with_sink) { - /* Do the sink formation */ + /* Do the sink_formation */ scheduler_addunlock(sched, ci->hydro.super->sinks.drift, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in, - t_sink_formation); - scheduler_addunlock(sched, t_sink_formation, - ci->top->hydro.sink_formation); - /* Do the sink merger */ - scheduler_addunlock(sched, ci->top->hydro.sink_formation, - t_sink_merger); - scheduler_addunlock(sched, t_sink_merger, ci->hydro.super->sinks.ghost); - /* Do the sink accretion */ - scheduler_addunlock(sched, ci->hydro.super->sinks.ghost, - t_sink_accretion); - scheduler_addunlock(sched, t_sink_accretion, + ci->top->sinks.sink_formation); + scheduler_addunlock(sched, ci->top->sinks.sink_formation, + t_sink_swallow); + + /* Do the sink_swallow */ + scheduler_addunlock(sched, t_sink_swallow, + ci->hydro.super->sinks.sink_ghost1); + + /* Do the sink_do_gas_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1, + t_sink_do_gas_swallow); + scheduler_addunlock(sched, t_sink_do_gas_swallow, + ci->hydro.super->sinks.sink_ghost2); + + /* Do the sink_do_sink_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2, + t_sink_do_sink_swallow); + scheduler_addunlock(sched, t_sink_do_sink_swallow, ci->hydro.super->sinks.sink_out); } @@ -2420,20 +2658,15 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - scheduler_addunlock(sched, ci->hydro.super->stars.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_in, t_rt_inject); - scheduler_addunlock(sched, t_rt_inject, - ci->hydro.super->hydro.rt_ghost1); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1, + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1, t_rt_gradient); scheduler_addunlock(sched, t_rt_gradient, - ci->hydro.super->hydro.rt_ghost2); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2, + ci->hydro.super->rt.rt_ghost2); + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2, t_rt_transport); scheduler_addunlock(sched, t_rt_transport, - ci->hydro.super->hydro.rt_transport_out); + ci->hydro.super->rt.rt_transport_out); } } @@ -2461,6 +2694,19 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force, flags, 0, ci, cj); +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* The order of operations for an inactive local cell interacting + * with an active foreign cell is not guaranteed because the density + * (and gradient) iact loops don't exist in that case. So we need + * an explicit dependency here to have sorted cells. */ + + /* Make all force tasks depend on the sorts */ + scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_force); + if (ci->hydro.super != cj->hydro.super) { + scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_force); + } +#endif + /* and the task for the time-step limiter */ if (with_timestep_limiter) { t_limiter = scheduler_addtask(sched, task_type_pair, @@ -2485,14 +2731,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, /* The sink tasks */ if (with_sink) { - t_sink_formation = scheduler_addtask( - sched, task_type_pair, task_subtype_sink_compute_formation, flags, - 0, ci, cj); - t_sink_merger = scheduler_addtask( - sched, task_type_pair, task_subtype_sink_merger, flags, 0, ci, cj); - t_sink_accretion = - scheduler_addtask(sched, task_type_pair, - task_subtype_sink_accretion, flags, 0, ci, cj); + t_sink_swallow = scheduler_addtask( + sched, task_type_pair, task_subtype_sink_swallow, flags, 0, ci, cj); + t_sink_do_sink_swallow = scheduler_addtask( + sched, task_type_pair, task_subtype_sink_do_sink_swallow, flags, 0, + ci, cj); + t_sink_do_gas_swallow = scheduler_addtask( + sched, task_type_pair, task_subtype_sink_do_gas_swallow, flags, 0, + ci, cj); } /* The black hole feedback tasks */ @@ -2512,12 +2758,36 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - t_rt_inject = scheduler_addtask( - sched, task_type_pair, task_subtype_rt_inject, flags, 0, ci, cj); t_rt_gradient = scheduler_addtask( sched, task_type_pair, task_subtype_rt_gradient, flags, 0, ci, cj); t_rt_transport = scheduler_addtask( sched, task_type_pair, task_subtype_rt_transport, flags, 0, ci, cj); +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* The order of operations for an inactive local cell interacting + * with an active foreign cell is not guaranteed because the gradient + * iact loops don't exist in that case. So we need an explicit + * dependency here to have sorted cells. */ + + /* Make all force tasks depend on the sorts */ + if (ci->hydro.super->rt.rt_sorts != NULL) + scheduler_addunlock(sched, ci->hydro.super->rt.rt_sorts, + t_rt_transport); + if (ci->hydro.super != cj->hydro.super) { + if (cj->hydro.super->rt.rt_sorts != NULL) + scheduler_addunlock(sched, cj->hydro.super->rt.rt_sorts, + t_rt_transport); + } + /* We need to ensure that a local inactive cell is sorted before + * the interaction in the transport loop. Local cells don't have an + * rt_sorts task. */ + if (ci->hydro.super->hydro.sorts != NULL) + scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, + t_rt_transport); + if ((ci->hydro.super != cj->hydro.super) && + (cj->hydro.super->hydro.sorts != NULL)) + scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, + t_rt_transport); +#endif } engine_addlink(e, &ci->hydro.force, t_force); @@ -2540,14 +2810,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_sink) { /* Formation */ - engine_addlink(e, &ci->sinks.compute_formation, t_sink_formation); - engine_addlink(e, &cj->sinks.compute_formation, t_sink_formation); + engine_addlink(e, &ci->sinks.swallow, t_sink_swallow); + engine_addlink(e, &cj->sinks.swallow, t_sink_swallow); /* Merger */ - engine_addlink(e, &ci->sinks.merger, t_sink_merger); - engine_addlink(e, &cj->sinks.merger, t_sink_merger); + engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow); + engine_addlink(e, &cj->sinks.do_sink_swallow, t_sink_do_sink_swallow); /* Accretion */ - engine_addlink(e, &ci->sinks.accretion, t_sink_accretion); - engine_addlink(e, &cj->sinks.accretion, t_sink_accretion); + engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow); + engine_addlink(e, &cj->sinks.do_gas_swallow, t_sink_do_gas_swallow); } if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) { engine_addlink(e, &ci->black_holes.density, t_bh_density); @@ -2562,12 +2832,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback); } if (with_rt) { - engine_addlink(e, &ci->hydro.rt_inject, t_rt_inject); - engine_addlink(e, &cj->hydro.rt_inject, t_rt_inject); - engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient); - engine_addlink(e, &cj->hydro.rt_gradient, t_rt_gradient); - engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport); - engine_addlink(e, &cj->hydro.rt_transport, t_rt_transport); + engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient); + engine_addlink(e, &cj->rt.rt_gradient, t_rt_gradient); + engine_addlink(e, &ci->rt.rt_transport, t_rt_transport); + engine_addlink(e, &cj->rt.rt_transport, t_rt_transport); } #ifdef EXTRA_HYDRO_LOOP @@ -2617,21 +2885,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, t_star_density); } } - if (with_sink) { - scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, - t_sink_formation); - - if (ci->hydro.super != cj->hydro.super) { - scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, - t_sink_formation); - } - } if (with_rt) { - scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_inject); scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_gradient); if (ci->hydro.super != cj->hydro.super) { - scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_rt_inject); scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_rt_gradient); } @@ -2681,25 +2938,30 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, if (with_sink) { - /* Formation */ + /* Do the sink_formation */ scheduler_addunlock(sched, ci->hydro.super->sinks.drift, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in, - t_sink_formation); - scheduler_addunlock(sched, t_sink_formation, - ci->top->hydro.sink_formation); - - /* Merger */ - scheduler_addunlock(sched, ci->top->hydro.sink_formation, - t_sink_merger); - scheduler_addunlock(sched, t_sink_merger, - ci->hydro.super->sinks.ghost); - /* Accretion */ - scheduler_addunlock(sched, ci->hydro.super->sinks.ghost, - t_sink_accretion); - scheduler_addunlock(sched, t_sink_accretion, + ci->top->sinks.sink_formation); + scheduler_addunlock(sched, ci->top->sinks.sink_formation, + t_sink_swallow); + + /* Do the sink_swallow */ + scheduler_addunlock(sched, t_sink_swallow, + ci->hydro.super->sinks.sink_ghost1); + + /* Do the sink_do_gas_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1, + t_sink_do_gas_swallow); + scheduler_addunlock(sched, t_sink_do_gas_swallow, + ci->hydro.super->sinks.sink_ghost2); + + /* Do the sink_do_sink_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2, + t_sink_do_sink_swallow); + scheduler_addunlock(sched, t_sink_do_sink_swallow, ci->hydro.super->sinks.sink_out); } @@ -2758,22 +3020,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - scheduler_addunlock(sched, ci->hydro.super->stars.sorts, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->stars.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_in, t_rt_inject); - scheduler_addunlock(sched, t_rt_inject, - ci->hydro.super->hydro.rt_ghost1); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1, + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1, t_rt_gradient); scheduler_addunlock(sched, t_rt_gradient, - ci->hydro.super->hydro.rt_ghost2); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2, + ci->hydro.super->rt.rt_ghost2); + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2, t_rt_transport); scheduler_addunlock(sched, t_rt_transport, - ci->hydro.super->hydro.rt_transport_out); + ci->hydro.super->rt.rt_transport_out); } } else /*(ci->nodeID != nodeID) */ { @@ -2839,26 +3095,30 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, if (with_sink) { - /* Formation */ + /* Do the sink_formation */ scheduler_addunlock(sched, cj->hydro.super->sinks.drift, - t_sink_formation); + cj->top->sinks.sink_formation); scheduler_addunlock(sched, cj->hydro.super->hydro.drift, - t_sink_formation); + cj->top->sinks.sink_formation); scheduler_addunlock(sched, cj->hydro.super->sinks.sink_in, - t_sink_formation); - scheduler_addunlock(sched, t_sink_formation, - cj->top->hydro.sink_formation); - - /* Merger */ - scheduler_addunlock(sched, cj->top->hydro.sink_formation, - t_sink_merger); - scheduler_addunlock(sched, t_sink_merger, - cj->hydro.super->sinks.ghost); - - /* Accretion */ - scheduler_addunlock(sched, cj->hydro.super->sinks.ghost, - t_sink_accretion); - scheduler_addunlock(sched, t_sink_accretion, + cj->top->sinks.sink_formation); + scheduler_addunlock(sched, cj->top->sinks.sink_formation, + t_sink_swallow); + + /* Do the sink_swallow */ + scheduler_addunlock(sched, t_sink_swallow, + cj->hydro.super->sinks.sink_ghost1); + + /* Do the sink_do_gas_swallow */ + scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost1, + t_sink_do_gas_swallow); + scheduler_addunlock(sched, t_sink_do_gas_swallow, + cj->hydro.super->sinks.sink_ghost2); + + /* Do the sink_do_sink_swallow */ + scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost2, + t_sink_do_sink_swallow); + scheduler_addunlock(sched, t_sink_do_sink_swallow, cj->hydro.super->sinks.sink_out); } @@ -2904,26 +3164,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - scheduler_addunlock(sched, cj->hydro.super->stars.sorts, - t_rt_inject); - scheduler_addunlock(sched, cj->hydro.super->stars.drift, - t_rt_inject); - scheduler_addunlock(sched, cj->hydro.super->hydro.drift, - t_rt_inject); - scheduler_addunlock(sched, cj->hydro.super->hydro.rt_in, - t_rt_inject); - scheduler_addunlock(sched, t_rt_inject, - cj->hydro.super->hydro.rt_ghost1); scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t_rt_gradient); - scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost1, + scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost1, t_rt_gradient); scheduler_addunlock(sched, t_rt_gradient, - cj->hydro.super->hydro.rt_ghost2); - scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost2, + cj->hydro.super->rt.rt_ghost2); + scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost2, t_rt_transport); scheduler_addunlock(sched, t_rt_transport, - cj->hydro.super->hydro.rt_transport_out); + cj->hydro.super->rt.rt_transport_out); } if (with_timestep_limiter) { @@ -2980,6 +3230,7 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, /* Start by constructing the task for the second hydro loop */ t_force = scheduler_addtask(sched, task_type_sub_self, task_subtype_force, flags, 0, ci, NULL); + /* and the task for the time-step limiter */ if (with_timestep_limiter) { t_limiter = scheduler_addtask(sched, task_type_sub_self, @@ -3007,15 +3258,15 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, /* The sink tasks */ if (with_sink) { - t_sink_formation = scheduler_addtask( - sched, task_type_sub_self, task_subtype_sink_compute_formation, - flags, 0, ci, NULL); - t_sink_merger = - scheduler_addtask(sched, task_type_sub_self, - task_subtype_sink_merger, flags, 0, ci, NULL); - t_sink_accretion = + t_sink_swallow = scheduler_addtask(sched, task_type_sub_self, - task_subtype_sink_accretion, flags, 0, ci, NULL); + task_subtype_sink_swallow, flags, 0, ci, NULL); + t_sink_do_sink_swallow = scheduler_addtask( + sched, task_type_sub_self, task_subtype_sink_do_sink_swallow, flags, + 0, ci, NULL); + t_sink_do_gas_swallow = scheduler_addtask( + sched, task_type_sub_self, task_subtype_sink_do_gas_swallow, flags, + 0, ci, NULL); } /* The black hole feedback tasks */ @@ -3041,9 +3292,6 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - t_rt_inject = - scheduler_addtask(sched, task_type_sub_self, task_subtype_rt_inject, - flags, 0, ci, NULL); t_rt_gradient = scheduler_addtask(sched, task_type_sub_self, task_subtype_rt_gradient, flags, 0, ci, NULL); @@ -3066,9 +3314,9 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, #endif } if (with_sink) { - engine_addlink(e, &ci->sinks.compute_formation, t_sink_formation); - engine_addlink(e, &ci->sinks.merger, t_sink_merger); - engine_addlink(e, &ci->sinks.accretion, t_sink_accretion); + engine_addlink(e, &ci->sinks.swallow, t_sink_swallow); + engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow); + engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow); } if (with_black_holes && bcount_i > 0) { engine_addlink(e, &ci->black_holes.density, t_bh_density); @@ -3078,9 +3326,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback); } if (with_rt) { - engine_addlink(e, &ci->hydro.rt_inject, t_rt_inject); - engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient); - engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport); + engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient); + engine_addlink(e, &ci->rt.rt_transport, t_rt_transport); } #ifdef EXTRA_HYDRO_LOOP @@ -3151,27 +3398,30 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, if (with_sink) { - /* Formation */ + /* Do the sink_formation */ scheduler_addunlock(sched, ci->hydro.super->sinks.drift, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, - t_sink_formation); - scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in, - t_sink_formation); - scheduler_addunlock(sched, t_sink_formation, - ci->top->hydro.sink_formation); - - /* Merger */ - scheduler_addunlock(sched, ci->top->hydro.sink_formation, - t_sink_merger); - scheduler_addunlock(sched, t_sink_merger, ci->hydro.super->sinks.ghost); - - /* Accretion */ - scheduler_addunlock(sched, ci->hydro.super->sinks.ghost, - t_sink_accretion); - scheduler_addunlock(sched, t_sink_accretion, + ci->top->sinks.sink_formation); + scheduler_addunlock(sched, ci->top->sinks.sink_formation, + t_sink_swallow); + + /* Do the sink_swallow */ + scheduler_addunlock(sched, t_sink_swallow, + ci->hydro.super->sinks.sink_ghost1); + + /* Do the sink_do_gas_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1, + t_sink_do_gas_swallow); + scheduler_addunlock(sched, t_sink_do_gas_swallow, + ci->hydro.super->sinks.sink_ghost2); + + /* Do the sink_do_sink_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2, + t_sink_do_sink_swallow); + scheduler_addunlock(sched, t_sink_do_sink_swallow, ci->hydro.super->sinks.sink_out); } @@ -3225,23 +3475,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - scheduler_addunlock(sched, ci->hydro.super->stars.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->stars.sorts, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_in, t_rt_inject); - scheduler_addunlock(sched, t_rt_inject, - ci->hydro.super->hydro.rt_ghost1); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient); scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_gradient); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1, + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1, t_rt_gradient); scheduler_addunlock(sched, t_rt_gradient, - ci->hydro.super->hydro.rt_ghost2); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2, + ci->hydro.super->rt.rt_ghost2); + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2, t_rt_transport); scheduler_addunlock(sched, t_rt_transport, - ci->hydro.super->hydro.rt_transport_out); + ci->hydro.super->rt.rt_transport_out); } } @@ -3269,6 +3512,20 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, /* New task for the force */ t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force, flags, 0, ci, cj); + +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* The order of operations for an inactive local cell interacting + * with an active foreign cell is not guaranteed because the density + * (and gradient) iact loops don't exist in that case. So we need + * an explicit dependency here to have sorted cells. */ + + /* Make all force tasks depend on the sorts */ + scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_force); + if (ci->hydro.super != cj->hydro.super) { + scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_force); + } +#endif + /* and the task for the time-step limiter */ if (with_timestep_limiter) { t_limiter = scheduler_addtask(sched, task_type_sub_pair, @@ -3296,15 +3553,15 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, /* The sink tasks */ if (with_sink) { - t_sink_formation = scheduler_addtask( - sched, task_type_sub_pair, task_subtype_sink_compute_formation, - flags, 0, ci, cj); - t_sink_merger = - scheduler_addtask(sched, task_type_sub_pair, - task_subtype_sink_merger, flags, 0, ci, cj); - t_sink_accretion = + t_sink_swallow = scheduler_addtask(sched, task_type_sub_pair, - task_subtype_sink_accretion, flags, 0, ci, cj); + task_subtype_sink_swallow, flags, 0, ci, cj); + t_sink_do_sink_swallow = scheduler_addtask( + sched, task_type_sub_pair, task_subtype_sink_do_sink_swallow, flags, + 0, ci, cj); + t_sink_do_gas_swallow = scheduler_addtask( + sched, task_type_sub_pair, task_subtype_sink_do_gas_swallow, flags, + 0, ci, cj); } /* The black hole feedback tasks */ @@ -3327,15 +3584,38 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - t_rt_inject = - scheduler_addtask(sched, task_type_sub_pair, task_subtype_rt_inject, - flags, 0, ci, cj); t_rt_gradient = scheduler_addtask(sched, task_type_sub_pair, task_subtype_rt_gradient, flags, 0, ci, cj); t_rt_transport = scheduler_addtask(sched, task_type_sub_pair, task_subtype_rt_transport, flags, 0, ci, cj); +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* The order of operations for an inactive local cell interacting + * with an active foreign cell is not guaranteed because the gradient + * iact loops don't exist in that case. So we need an explicit + * dependency here to have sorted cells. */ + + /* Make all force tasks depend on the sorts */ + if (ci->hydro.super->rt.rt_sorts != NULL) + scheduler_addunlock(sched, ci->hydro.super->rt.rt_sorts, + t_rt_transport); + if (ci->hydro.super != cj->hydro.super) { + if (cj->hydro.super->rt.rt_sorts != NULL) + scheduler_addunlock(sched, cj->hydro.super->rt.rt_sorts, + t_rt_transport); + } + /* We need to ensure that a local inactive cell is sorted before + * the interaction in the transport loop. Local cells don't have + * an rt_sort task. */ + if (ci->hydro.super->hydro.sorts != NULL) + scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, + t_rt_transport); + if ((ci->hydro.super != cj->hydro.super) && + (cj->hydro.super->hydro.sorts != NULL)) + scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, + t_rt_transport); +#endif } engine_addlink(e, &ci->hydro.force, t_force); @@ -3358,16 +3638,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_sink) { /* Formation */ - engine_addlink(e, &ci->sinks.compute_formation, t_sink_formation); - engine_addlink(e, &cj->sinks.compute_formation, t_sink_formation); + engine_addlink(e, &ci->sinks.swallow, t_sink_swallow); + engine_addlink(e, &cj->sinks.swallow, t_sink_swallow); /* Merger */ - engine_addlink(e, &ci->sinks.merger, t_sink_merger); - engine_addlink(e, &cj->sinks.merger, t_sink_merger); + engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow); + engine_addlink(e, &cj->sinks.do_sink_swallow, t_sink_do_sink_swallow); /* Accretion */ - engine_addlink(e, &ci->sinks.accretion, t_sink_accretion); - engine_addlink(e, &cj->sinks.accretion, t_sink_accretion); + engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow); + engine_addlink(e, &cj->sinks.do_gas_swallow, t_sink_do_gas_swallow); } if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) { engine_addlink(e, &ci->black_holes.density, t_bh_density); @@ -3382,12 +3662,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback); } if (with_rt) { - engine_addlink(e, &ci->hydro.rt_inject, t_rt_inject); - engine_addlink(e, &cj->hydro.rt_inject, t_rt_inject); - engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient); - engine_addlink(e, &cj->hydro.rt_gradient, t_rt_gradient); - engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport); - engine_addlink(e, &cj->hydro.rt_transport, t_rt_transport); + engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient); + engine_addlink(e, &cj->rt.rt_gradient, t_rt_gradient); + engine_addlink(e, &ci->rt.rt_transport, t_rt_transport); + engine_addlink(e, &cj->rt.rt_transport, t_rt_transport); } #ifdef EXTRA_HYDRO_LOOP @@ -3437,20 +3715,9 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } } - if (with_sink) { - scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, - t_sink_formation); - if (ci->hydro.super != cj->hydro.super) { - scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, - t_sink_formation); - } - } - if (with_rt) { - scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_inject); scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_gradient); if (ci->hydro.super != cj->hydro.super) { - scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_rt_inject); scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_rt_gradient); } @@ -3499,26 +3766,31 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_sink) { - /* Formation */ + + /* Do the sink_formation */ scheduler_addunlock(sched, ci->hydro.super->sinks.drift, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, - t_sink_formation); + ci->top->sinks.sink_formation); scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in, - t_sink_formation); - scheduler_addunlock(sched, t_sink_formation, - ci->top->hydro.sink_formation); - - /* Merger */ - scheduler_addunlock(sched, ci->top->hydro.sink_formation, - t_sink_merger); - scheduler_addunlock(sched, t_sink_merger, - ci->hydro.super->sinks.ghost); - - /* Accretion */ - scheduler_addunlock(sched, ci->hydro.super->sinks.ghost, - t_sink_accretion); - scheduler_addunlock(sched, t_sink_accretion, + ci->top->sinks.sink_formation); + scheduler_addunlock(sched, ci->top->sinks.sink_formation, + t_sink_swallow); + + /* Do the sink_swallow */ + scheduler_addunlock(sched, t_sink_swallow, + ci->hydro.super->sinks.sink_ghost1); + + /* Do the sink_do_gas_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1, + t_sink_do_gas_swallow); + scheduler_addunlock(sched, t_sink_do_gas_swallow, + ci->hydro.super->sinks.sink_ghost2); + + /* Do the sink_do_sink_swallow */ + scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2, + t_sink_do_sink_swallow); + scheduler_addunlock(sched, t_sink_do_sink_swallow, ci->hydro.super->sinks.sink_out); } @@ -3577,22 +3849,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_rt) { - scheduler_addunlock(sched, ci->hydro.super->stars.sorts, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->stars.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_inject); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_in, t_rt_inject); - scheduler_addunlock(sched, t_rt_inject, - ci->hydro.super->hydro.rt_ghost1); scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1, + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1, t_rt_gradient); scheduler_addunlock(sched, t_rt_gradient, - ci->hydro.super->hydro.rt_ghost2); - scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2, + ci->hydro.super->rt.rt_ghost2); + scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2, t_rt_transport); scheduler_addunlock(sched, t_rt_transport, - ci->hydro.super->hydro.rt_transport_out); + ci->hydro.super->rt.rt_transport_out); } } else /* ci->nodeID != nodeID */ { @@ -3657,26 +3923,30 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } if (with_sink) { - /* Formation */ + /* Do the sink_formation */ scheduler_addunlock(sched, cj->hydro.super->sinks.drift, - t_sink_formation); + cj->top->sinks.sink_formation); scheduler_addunlock(sched, cj->hydro.super->hydro.drift, - t_sink_formation); + cj->top->sinks.sink_formation); scheduler_addunlock(sched, cj->hydro.super->sinks.sink_in, - t_sink_formation); - scheduler_addunlock(sched, t_sink_formation, - cj->top->hydro.sink_formation); - - /* Merger */ - scheduler_addunlock(sched, cj->top->hydro.sink_formation, - t_sink_merger); - scheduler_addunlock(sched, t_sink_merger, - cj->hydro.super->sinks.ghost); - - /* Accretion */ - scheduler_addunlock(sched, cj->hydro.super->sinks.ghost, - t_sink_accretion); - scheduler_addunlock(sched, t_sink_accretion, + cj->top->sinks.sink_formation); + scheduler_addunlock(sched, cj->top->sinks.sink_formation, + t_sink_swallow); + + /* Do the sink_swallow */ + scheduler_addunlock(sched, t_sink_swallow, + cj->hydro.super->sinks.sink_ghost1); + + /* Do the sink_do_gas_swallow */ + scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost1, + t_sink_do_gas_swallow); + scheduler_addunlock(sched, t_sink_do_gas_swallow, + cj->hydro.super->sinks.sink_ghost2); + + /* Do the sink_do_sink_swallow */ + scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost2, + t_sink_do_sink_swallow); + scheduler_addunlock(sched, t_sink_do_sink_swallow, cj->hydro.super->sinks.sink_out); } @@ -3721,26 +3991,16 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, cj->hydro.super->black_holes.black_holes_out); } if (with_rt) { - scheduler_addunlock(sched, cj->hydro.super->stars.sorts, - t_rt_inject); - scheduler_addunlock(sched, cj->hydro.super->stars.drift, - t_rt_inject); - scheduler_addunlock(sched, cj->hydro.super->hydro.drift, - t_rt_inject); - scheduler_addunlock(sched, cj->hydro.super->hydro.rt_in, - t_rt_inject); - scheduler_addunlock(sched, t_rt_inject, - cj->hydro.super->hydro.rt_ghost1); scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t_rt_gradient); - scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost1, + scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost1, t_rt_gradient); scheduler_addunlock(sched, t_rt_gradient, - cj->hydro.super->hydro.rt_ghost2); - scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost2, + cj->hydro.super->rt.rt_ghost2); + scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost2, t_rt_transport); scheduler_addunlock(sched, t_rt_transport, - cj->hydro.super->hydro.rt_transport_out); + cj->hydro.super->rt.rt_transport_out); } if (with_timestep_limiter) { @@ -3934,6 +4194,7 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements, const int with_limiter = (e->policy & engine_policy_timestep_limiter); const int with_feedback = (e->policy & engine_policy_feedback); const int with_sync = (e->policy & engine_policy_timestep_sync); + const int with_rt = (e->policy & engine_policy_rt); struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data; #ifdef SWIFT_DEBUG_CHECKS @@ -3948,11 +4209,30 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements, const int type = cell_type_pairs[k].type; #ifdef WITH_MPI - /* Add the timestep exchange task */ - struct task *tend = scheduler_addtask( - &e->sched, task_type_send, task_subtype_tend, ci->mpi.tag, 0, ci, cj); - scheduler_addunlock(&e->sched, ci->timestep_collect, tend); - engine_addlink(e, &ci->mpi.send, tend); + + if (!cell_is_empty(ci)) { + /* Add the timestep exchange task */ + struct task *tend = scheduler_addtask( + &e->sched, task_type_send, task_subtype_tend, ci->mpi.tag, 0, ci, cj); + scheduler_addunlock(&e->sched, ci->timestep_collect, tend); + engine_addlink(e, &ci->mpi.send, tend); + + if (with_rt && (type & proxy_cell_type_hydro)) { + + /* If we're running with RT subcycling, we need to ensure that nothing + * is sent before the advance cell time task has finished. This may + * overwrite the correct cell times, particularly so when we're sending + * over data for non-RT tasks, e.g. for gravity pair tasks. */ + if (ci->super->rt.rt_advance_cell_time != NULL) { + scheduler_addunlock(&e->sched, ci->super->rt.rt_advance_cell_time, + tend); +#ifdef SWIFT_RT_DEBUG_CHECKS + } else { + error("Got local super cell without rt_advance_cell_time task"); +#endif + } + } + } #endif /* Add the send tasks for the cells in the proxy that have a hydro @@ -3962,7 +4242,9 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements, /*t_rho=*/NULL, /*t_gradient=*/NULL, /*t_prep1=*/NULL, /*t_limiter=*/NULL, /*t_pack_limiter=*/NULL, - with_feedback, with_limiter, with_sync); + /*t_rt_gradient=*/NULL, + /*t_rt_transport=*/NULL, with_feedback, + with_limiter, with_sync, with_rt); /* Add the send tasks for the cells in the proxy that have a stars * connection. */ @@ -3997,6 +4279,7 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements, const int with_feedback = (e->policy & engine_policy_feedback); const int with_black_holes = (e->policy & engine_policy_black_holes); const int with_sync = (e->policy & engine_policy_timestep_sync); + const int with_rt = (e->policy & engine_policy_rt); struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data; #ifdef SWIFT_DEBUG_CHECKS @@ -4012,20 +4295,23 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements, #ifdef WITH_MPI /* Add the timestep exchange task */ - tend = scheduler_addtask(&e->sched, task_type_recv, task_subtype_tend, - ci->mpi.tag, 0, ci, NULL); - engine_addlink(e, &ci->mpi.recv, tend); + if (!cell_is_empty(ci)) { + tend = scheduler_addtask(&e->sched, task_type_recv, task_subtype_tend, + ci->mpi.tag, 0, ci, NULL); + engine_addlink(e, &ci->mpi.recv, tend); + } #endif /* Add the recv tasks for the cells in the proxy that have a hydro * connection. */ - if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) - engine_addtasks_recv_hydro(e, ci, /*t_xv=*/NULL, /*t_rho=*/NULL, - /*t_gradient=*/NULL, - /*t_prep1=*/NULL, - /*t_limiter=*/NULL, /*t_unpack_limiter=*/NULL, - tend, with_feedback, with_black_holes, - with_limiter, with_sync); + if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) { + engine_addtasks_recv_hydro( + e, ci, /*t_xv=*/NULL, /*t_rho=*/NULL, /*t_gradient=*/NULL, + /*t_prep1=*/NULL, /*t_limiter=*/NULL, /*t_unpack_limiter=*/NULL, + /*t_rt_gradient=*/NULL, /*t_rt_transport=*/NULL, + /*t_rt_sorts=*/NULL, tend, with_feedback, with_black_holes, + with_limiter, with_sync, with_rt); + } /* Add the recv tasks for the cells in the proxy that have a stars * connection. */ @@ -4140,6 +4426,8 @@ void engine_make_fof_tasks(struct engine *e) { struct scheduler *sched = &e->sched; ticks tic = getticks(); + if (e->restarting) error("Running FOF on a restart step!"); + /* Construct a FOF loop over neighbours */ if (e->policy & engine_policy_fof) threadpool_map(&e->threadpool, engine_make_fofloop_tasks_mapper, NULL, diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c index c1cc4567209b640ae5def59aba769ae174652b6f..1919b1b501cf2138d1e687edb98bf63060a8e563 100644 --- a/src/engine_marktasks.c +++ b/src/engine_marktasks.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * Angus Lepper (angus.lepper@ed.ac.uk) * 2016 John A. Regan (john.a.regan@durham.ac.uk) @@ -23,7 +23,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdlib.h> @@ -52,7 +52,6 @@ #include "error.h" #include "feedback.h" #include "proxy.h" -#include "rt_active.h" #include "timers.h" /** @@ -78,7 +77,6 @@ void engine_marktasks_mapper(void *map_data, int num_elements, const int with_star_formation = (e->policy & engine_policy_star_formation); const int with_star_formation_sink = with_sinks && with_stars; const int with_feedback = e->policy & engine_policy_feedback; - const int with_rt = e->policy & engine_policy_rt; for (int ind = 0; ind < num_elements; ind++) { @@ -96,6 +94,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #ifdef SWIFT_DEBUG_CHECKS if (ci->nodeID != nodeID) error("Non-local self task found"); #endif + const int ci_active_hydro = cell_is_active_hydro(ci, e); const int ci_active_gravity = cell_is_active_gravity(ci, e); const int ci_active_black_holes = cell_is_active_black_holes(ci, e); @@ -103,7 +102,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, cell_is_active_sinks(ci, e) || ci_active_hydro; const int ci_active_stars = cell_need_activating_stars( ci, e, with_star_formation, with_star_formation_sink); - const int ci_active_rt = with_rt && rt_should_do_unskip_cell(ci, e); + const int ci_active_rt = cell_is_rt_active(ci, e); /* Activate the hydro drift */ if (t_type == task_type_self && t_subtype == task_subtype_density) { @@ -212,7 +211,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Activate the sink formation */ else if (t_type == task_type_self && - t_subtype == task_subtype_sink_compute_formation) { + t_subtype == task_subtype_sink_swallow) { if (ci_active_sinks) { scheduler_activate(s, t); cell_activate_drift_part(ci, s); @@ -224,7 +223,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Store current values of dx_max and h_max. */ else if (t_type == task_type_sub_self && - t_subtype == task_subtype_sink_compute_formation) { + t_subtype == task_subtype_sink_swallow) { if (ci_active_sinks) { scheduler_activate(s, t); cell_activate_subcell_sinks_tasks(ci, NULL, s, with_timestep_sync); @@ -233,14 +232,14 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Activate the sink merger */ else if (t_type == task_type_self && - t_subtype == task_subtype_sink_merger) { + t_subtype == task_subtype_sink_do_sink_swallow) { if (ci_active_sinks) { scheduler_activate(s, t); } } else if (t_type == task_type_sub_self && - t_subtype == task_subtype_sink_merger) { + t_subtype == task_subtype_sink_do_sink_swallow) { if (ci_active_sinks) { scheduler_activate(s, t); } @@ -248,14 +247,14 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Activate the sink accretion */ else if (t_type == task_type_self && - t_subtype == task_subtype_sink_accretion) { + t_subtype == task_subtype_sink_do_gas_swallow) { if (ci_active_sinks) { scheduler_activate(s, t); } } else if (t_type == task_type_sub_self && - t_subtype == task_subtype_sink_accretion) { + t_subtype == task_subtype_sink_do_gas_swallow) { if (ci_active_sinks) { scheduler_activate(s, t); } @@ -268,6 +267,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, scheduler_activate(s, t); cell_activate_drift_part(ci, s); cell_activate_drift_bpart(ci, s); + if (with_timestep_sync) cell_activate_sync_part(ci, s); } } @@ -348,25 +348,20 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Activate RT tasks */ else if (t_type == task_type_self && - t_subtype == task_subtype_rt_inject) { - if (ci_active_rt) { - scheduler_activate(s, t); - cell_activate_drift_part(ci, s); - cell_activate_drift_spart(ci, s); - } + t_subtype == task_subtype_rt_gradient) { + if (ci_active_rt) scheduler_activate(s, t); } else if (t_type == task_type_sub_self && - t_subtype == task_subtype_rt_inject) { + t_subtype == task_subtype_rt_gradient) { if (ci_active_rt) { scheduler_activate(s, t); - cell_activate_subcell_rt_tasks(ci, NULL, s); + cell_activate_subcell_rt_tasks(ci, NULL, s, /*sub_cycle=*/0); } } - else if (t_subtype == task_subtype_rt_gradient || - t_subtype == task_subtype_rt_transport) { - if (ci_active_hydro) scheduler_activate(s, t); + else if (t_subtype == task_subtype_rt_transport) { + if (ci_active_rt) scheduler_activate(s, t); } #ifdef SWIFT_DEBUG_CHECKS @@ -408,8 +403,8 @@ void engine_marktasks_mapper(void *map_data, int num_elements, const int cj_active_stars = cell_need_activating_stars( cj, e, with_star_formation, with_star_formation_sink); - const int ci_active_rt = with_rt && rt_should_iact_cell_pair(ci, cj, e); - const int cj_active_rt = with_rt && rt_should_iact_cell_pair(cj, ci, e); + const int ci_active_rt = cell_is_rt_active(ci, e); + const int cj_active_rt = cell_is_rt_active(cj, e); /* Only activate tasks that involve a local active cell. */ if ((t_subtype == task_subtype_density || @@ -691,9 +686,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } /* Sink formation */ - else if ((t_subtype == task_subtype_sink_compute_formation || - t_subtype == task_subtype_sink_merger || - t_subtype == task_subtype_sink_accretion) && + else if ((t_subtype == task_subtype_sink_swallow || + t_subtype == task_subtype_sink_do_sink_swallow || + t_subtype == task_subtype_sink_do_gas_swallow) && (ci_active_sinks || cj_active_sinks) && (ci_nodeID == nodeID || cj_nodeID == nodeID)) { @@ -701,7 +696,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Set the correct sorting flags */ if (t_type == task_type_pair && - t_subtype == task_subtype_sink_compute_formation) { + t_subtype == task_subtype_sink_swallow) { /* Activate the sink drift for the sink merger */ if (ci_nodeID == nodeID) { @@ -757,7 +752,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } else if (t_type == task_type_sub_pair && - t_subtype == task_subtype_sink_compute_formation) { + t_subtype == task_subtype_sink_swallow) { /* Activate all sink_in tasks for each cell involved * in sub_pair type tasks */ if (ci_nodeID == nodeID) @@ -770,7 +765,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } else if ((t_type == task_type_pair || t_type == task_type_sub_pair) && - t_subtype == task_subtype_sink_accretion) { + t_subtype == task_subtype_sink_do_gas_swallow) { /* Activate sinks_out for each cell that is part of * a pair/sub_pair task as to not miss any dependencies */ if (ci_nodeID == nodeID) @@ -780,136 +775,136 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } } - /* RT injection tasks */ - else if (t_subtype == task_subtype_rt_inject) { + /* RT gradient and transport tasks */ + else if (t_subtype == task_subtype_rt_gradient) { /* We only want to activate the task if the cell is active and is - going to update some gas on the *local* node */ - if ((ci_nodeID == nodeID && cj_nodeID == nodeID) && - (ci_active_rt || cj_active_rt)) { + going to update some gas on the *local* node */ + + if ((ci_nodeID == nodeID && ci_active_rt) || + (cj_nodeID == nodeID && cj_active_rt)) { scheduler_activate(s, t); /* Set the correct sorting flags */ if (t_type == task_type_pair) { - /* Add rt_in dependencies for each cell that is part of - * a pair task as to not miss any dependencies */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_in); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_in); - - /* For the same reason, catch the dependencies with the RT ghost1 */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_ghost1); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_ghost1); - - /* Do ci */ - if (ci_active_rt) { - - /* stars for ci */ - atomic_or(&ci->stars.requires_sorts, 1 << t->flags); - ci->stars.dx_max_sort_old = ci->stars.dx_max_sort; - - /* hydro for cj */ - atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); - cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; - - /* Activate the drift tasks. */ - if (ci_nodeID == nodeID) cell_activate_drift_spart(ci, s); - if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); - - /* Check the sorts and activate them if needed. */ - cell_activate_hydro_sorts(cj, t->flags, s); - cell_activate_stars_sorts(ci, t->flags, s); - } - - /* Do cj */ - if (cj_active_rt) { - - /* hydro for ci */ - atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); - ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; - - /* stars for cj */ - atomic_or(&cj->stars.requires_sorts, 1 << t->flags); - cj->stars.dx_max_sort_old = cj->stars.dx_max_sort; - - /* Activate the drift tasks. */ - if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); - if (cj_nodeID == nodeID) cell_activate_drift_spart(cj, s); + /* Store some values. */ + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; - /* Check the sorts and activate them if needed. */ - cell_activate_hydro_sorts(ci, t->flags, s); - cell_activate_stars_sorts(cj, t->flags, s); - } + /* Check the sorts and activate them if needed. */ + cell_activate_rt_sorts(ci, t->flags, s); + cell_activate_rt_sorts(cj, t->flags, s); } /* Store current values of dx_max and h_max. */ else if (t_type == task_type_sub_pair) { - cell_activate_subcell_rt_tasks(ci, cj, s); - - /* Add rt_in dependencies for each cell that is part of - * a sub_pair task as to not miss any dependencies */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_in); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_in); - - /* For the same reason, catch the dependencies with the RT ghost1 */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_ghost1); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_ghost1); + cell_activate_subcell_rt_tasks(ci, cj, s, /*sub_cycle=*/0); } } } - /* RT gradient and transport tasks */ - else if (t_subtype == task_subtype_rt_gradient) { + else if (t_subtype == task_subtype_rt_transport) { /* We only want to activate the task if the cell is active and is going to update some gas on the *local* node */ - if ((ci_nodeID == nodeID && cj_nodeID == nodeID) && - (ci_active_hydro || cj_active_hydro)) { + + if ((ci_nodeID == nodeID && ci_active_rt) || + (cj_nodeID == nodeID && cj_active_rt)) { + /* The gradient and transport task subtypes mirror the hydro tasks. * Therefore all the (subcell) sorts and drifts should already have * been activated properly in the hydro part of the activation. */ scheduler_activate(s, t); if (t_type == task_type_pair || t_type == task_type_sub_pair) { - /* Activate rt_ghost1 dependencies for each cell that is part of + /* Activate transport_out for each cell that is part of * a pair/sub_pair task as to not miss any dependencies */ if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_ghost1); + scheduler_activate(s, ci->hydro.super->rt.rt_transport_out); if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_ghost1); + scheduler_activate(s, cj->hydro.super->rt.rt_transport_out); } } } - else if (t_subtype == task_subtype_rt_transport) { - /* We only want to activate the task if the cell is active and is - going to update some gas on the *local* node */ - if ((ci_nodeID == nodeID && cj_nodeID == nodeID) && - (ci_active_hydro || cj_active_hydro)) { - /* The gradient and transport task subtypes mirror the hydro tasks. - * Therefore all the (subcell) sorts and drifts should already have - * been activated properly in the hydro part of the activation. */ + /* Pair tasks between inactive local cells and active remote cells. */ + if ((ci_nodeID != nodeID && cj_nodeID == nodeID && ci_active_hydro && + !cj_active_hydro) || + (ci_nodeID == nodeID && cj_nodeID != nodeID && !ci_active_hydro && + cj_active_hydro)) { + +#if defined(WITH_MPI) && defined(MPI_SYMMETRIC_FORCE_INTERACTION) + if (t_subtype == task_subtype_force) { + scheduler_activate(s, t); - if (t_type == task_type_pair || t_type == task_type_sub_pair) { - /* Activate transport_out for each cell that is part of - * a pair/sub_pair task as to not miss any dependencies */ - if (ci_nodeID == nodeID) - scheduler_activate(s, ci->hydro.super->hydro.rt_transport_out); - if (cj_nodeID == nodeID) - scheduler_activate(s, cj->hydro.super->hydro.rt_transport_out); + /* Set the correct sorting flags */ + if (t_type == task_type_pair) { + /* Store some values. */ + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; + + /* Activate the hydro drift tasks. */ + if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); + if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); + + /* And the limiter */ + if (ci_nodeID == nodeID && with_timestep_limiter) + cell_activate_limiter(ci, s); + if (cj_nodeID == nodeID && with_timestep_limiter) + cell_activate_limiter(cj, s); + + /* Check the sorts and activate them if needed. */ + cell_activate_hydro_sorts(ci, t->flags, s); + cell_activate_hydro_sorts(cj, t->flags, s); + + } + + /* Store current values of dx_max and h_max. */ + else if (t_type == task_type_sub_pair) { + cell_activate_subcell_hydro_tasks(t->ci, t->cj, s, + with_timestep_limiter); } } } + /* Pair tasks between inactive local cells and active remote cells. */ + if ((ci_nodeID != nodeID && cj_nodeID == nodeID && ci_active_rt && + !cj_active_rt) || + (ci_nodeID == nodeID && cj_nodeID != nodeID && !ci_active_rt && + cj_active_rt)) { + + if (t_subtype == task_subtype_rt_transport) { + + scheduler_activate(s, t); + + /* Set the correct sorting flags */ + if (t_type == task_type_pair) { + + /* Store some values. */ + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; + + /* Check the sorts and activate them if needed. */ + cell_activate_rt_sorts(ci, t->flags, s); + cell_activate_rt_sorts(cj, t->flags, s); + } + + /* Store current values of dx_max and h_max. */ + else if (t_type == task_type_sub_pair) { + cell_activate_subcell_rt_tasks(ci, cj, s, /*sub_cycle=*/0); + } + } +#endif + } + /* Only interested in density tasks as of here. */ if (t_subtype == task_subtype_density) { @@ -930,6 +925,26 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #endif } } + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force interaction + * on this node as well. */ + else if (ci_active_hydro) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* NOTE: (yuyttenh, 09/2022) Since the particle communications send + * over whole particles currently, just activating the gradient + * send/recieve should be enough for now. The remote active + * particles are only needed for the sorts and the flux exchange on + * the node of the inactive cell, so sending over the xv and + * gradient suffices. If at any point the commutications change, we + * should probably also send over the rho separately. */ + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_xv); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rho); +#else + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_gradient); +#endif +#endif + } /* If the foreign cell is active, we want its particles for the * limiter */ @@ -959,6 +974,27 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #endif } } + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force interaction + * on both nodes */ + else if (cj_active_hydro) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* See NOTE on line 867 */ + struct link *l = scheduler_activate_send( + s, cj->mpi.send, task_subtype_xv, ci_nodeID); + /* Drift the cell which will be sent at the level at which it is + * sent, i.e. drift the cell specified in the send task (l->t) + * itself. */ + cell_activate_drift_part(l->t->ci, s); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_send(s, cj->mpi.send, task_subtype_rho, + ci_nodeID); +#else + scheduler_activate_send(s, cj->mpi.send, task_subtype_gradient, + ci_nodeID); +#endif +#endif + } /* If the local cell is active, send its particles for the limiting. */ @@ -994,6 +1030,20 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #endif } } + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force interaction + * on this node as well. */ + else if (cj_active_hydro) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* See NOTE on line 867. */ + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_xv); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rho); +#else + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_gradient); +#endif +#endif + } /* If the foreign cell is active, we want its particles for the * limiter */ @@ -1025,6 +1075,27 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #endif } } + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force interaction + * on both nodes */ + else if (ci_active_hydro) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* See NOTE on line 867. */ + struct link *l = scheduler_activate_send( + s, ci->mpi.send, task_subtype_xv, cj_nodeID); + /* Drift the cell which will be sent at the level at which it is + * sent, i.e. drift the cell specified in the send task (l->t) + * itself. */ + cell_activate_drift_part(l->t->ci, s); +#ifndef EXTRA_HYDRO_LOOP + scheduler_activate_send(s, ci->mpi.send, task_subtype_rho, + cj_nodeID); +#else + scheduler_activate_send(s, ci->mpi.send, task_subtype_gradient, + cj_nodeID); +#endif +#endif + } /* If the local cell is active, send its particles for the limiting. */ @@ -1145,8 +1216,8 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #endif } - /* Only interested in sink_compute_formation tasks as of here. */ - else if (t->subtype == task_subtype_sink_compute_formation) { + /* Only interested in sink_swallow tasks as of here. */ + else if (t->subtype == task_subtype_sink_swallow) { /* Too much particle movement? */ if (cell_need_rebuild_for_sinks_pair(ci, cj)) *rebuild_space = 1; @@ -1283,13 +1354,105 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } #endif } /* Only interested in RT tasks as of here. */ + + else if (t->subtype == task_subtype_rt_gradient) { + #ifdef WITH_MPI - else if (t_subtype == task_subtype_rt_inject || - t_subtype == task_subtype_rt_gradient || - t_subtype == task_subtype_rt_transport) { - error("RT doesn't work with MPI yet."); - } + /* Activate the send/recv tasks. */ + if (ci_nodeID != nodeID) { + + /* If the local cell is active, receive data from the foreign cell. */ + if (cj_active_rt) { + + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_gradient); + + if (ci_active_rt) { + /* We only need updates later on if the other cell is active too + */ + scheduler_activate_recv(s, ci->mpi.recv, + task_subtype_rt_transport); + } + } else if (ci_active_rt) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force + * interaction on this node as well. */ + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_gradient); + scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_transport); #endif + } + + /* Is the foreign cell active and will need stuff from us? */ + if (ci_active_rt) { + + scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_gradient, + ci_nodeID); + + if (cj_active_rt) { + scheduler_activate_send(s, cj->mpi.send, + task_subtype_rt_transport, ci_nodeID); + } + } else if (cj_active_rt) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force + * interaction on both nodes */ + scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_gradient, + ci_nodeID); + scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_transport, + ci_nodeID); +#endif + } + + } else if (cj_nodeID != nodeID) { + + /* If the local cell is active, receive data from the foreign cell. */ + if (ci_active_rt) { + + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_gradient); + + if (cj_active_rt) { + /* We only need updates later on if the other cell is active too + */ + scheduler_activate_recv(s, cj->mpi.recv, + task_subtype_rt_transport); + } + } else if (cj_active_rt) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the local cell is inactive and the remote cell is active, we + * still need to receive stuff to be able to do the force + * interaction on this node as well. */ + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_gradient); + scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_transport); +#endif + } + + /* Is the foreign cell active and will need stuff from us? */ + if (cj_active_rt) { + + scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_gradient, + cj_nodeID); + + if (ci_active_rt) { + /* We only need updates later on if the other cell is active too + */ + scheduler_activate_send(s, ci->mpi.send, + task_subtype_rt_transport, cj_nodeID); + } + } else if (ci_active_rt) { +#ifdef MPI_SYMMETRIC_FORCE_INTERACTION + /* If the foreign cell is inactive, but the local cell is active, + * we still need to send stuff to be able to do the force + * interaction on both nodes */ + scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_gradient, + cj_nodeID); + scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_transport, + cj_nodeID); +#endif + } + } +#endif + } } /* End force for hydro ? */ @@ -1383,7 +1546,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Sink implicit tasks? */ else if (t_type == task_type_sink_in || t_type == task_type_sink_out || - t_type == task_type_sink_ghost) { + t_type == task_type_sink_ghost1) { if (cell_is_active_sinks(t->ci, e) || cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t); } @@ -1408,15 +1571,21 @@ void engine_marktasks_mapper(void *map_data, int num_elements, t->ci->stars.updated = 0; t->ci->sinks.updated = 0; t->ci->black_holes.updated = 0; - if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e) || - cell_is_active_stars(t->ci, e) || cell_is_active_sinks(t->ci, e) || - cell_is_active_black_holes(t->ci, e)) - scheduler_activate(s, t); + + if (!cell_is_empty(t->ci)) { + if (cell_is_active_hydro(t->ci, e) || + cell_is_active_gravity(t->ci, e) || + cell_is_active_stars(t->ci, e) || cell_is_active_sinks(t->ci, e) || + cell_is_active_black_holes(t->ci, e)) + scheduler_activate(s, t); + } } else if ((t_type == task_type_send && t_subtype == task_subtype_tend) || (t_type == task_type_recv && t_subtype == task_subtype_tend)) { - scheduler_activate(s, t); + if (!cell_is_empty(t->ci)) { + scheduler_activate(s, t); + } } /* Subgrid tasks: cooling */ @@ -1443,23 +1612,20 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Radiative transfer implicit tasks */ else if (t->type == task_type_rt_in) { - if (rt_should_do_unskip_cell(t->ci, e)) scheduler_activate(s, t); + if (cell_is_rt_active(t->ci, e) || + cell_need_activating_stars(t->ci, e, with_star_formation, + with_star_formation_sink)) + scheduler_activate(s, t); } - else if (t->type == task_type_rt_transport_out || + else if (t->type == task_type_rt_ghost1 || t->type == task_type_rt_ghost2 || + t->type == task_type_rt_transport_out || + t->type == task_type_rt_tchem || + t->type == task_type_rt_advance_cell_time || t->type == task_type_rt_out) { - if (cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t); - } - - /* rt_ghost1 is special: Also check for stars activity to catch - * dependencies further down the line (e.g. timestep task) */ - else if (t->type == task_type_rt_ghost1) { - if (rt_should_do_unskip_cell(t->ci, e)) scheduler_activate(s, t); - } - - /* Radiative transfer ghosts and thermochemistry*/ - else if (t->type == task_type_rt_ghost2 || t->type == task_type_rt_tchem) { - if (cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t); + if (cell_is_rt_active(t->ci, e)) scheduler_activate(s, t); + /* Note that rt_collect_times never needs to be active on main steps, + * which is always what follows engine_marktasks().*/ } /* Subgrid tasks: sink formation */ diff --git a/src/engine_proxy.c b/src/engine_proxy.c index 2fb1d3e75d597f3d4aa1a09ffc67cf453d50228b..2958bd7b6009e34fdc27b706c3b930837202b4bf 100644 --- a/src/engine_proxy.c +++ b/src/engine_proxy.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -63,7 +63,6 @@ void engine_makeproxies(struct engine *e) { const int with_hydro = (e->policy & engine_policy_hydro); const int with_gravity = (e->policy & engine_policy_self_gravity); const double theta_crit = e->gravity_properties->theta_crit; - const double theta_crit_inv = 1. / e->gravity_properties->theta_crit; const double max_mesh_dist = e->mesh->r_cut_max; const double max_mesh_dist2 = max_mesh_dist * max_mesh_dist; @@ -88,7 +87,7 @@ void engine_makeproxies(struct engine *e) { /* Gravity needs to take the opening angle into account */ if (with_gravity) { - const double distance = 2. * r_max * theta_crit_inv; + const double distance = 2. * r_max / theta_crit; delta_cells = (int)(distance / cells[0].dmin) + 1; } diff --git a/src/engine_redistribute.c b/src/engine_redistribute.c index 22826334c26e2dddff40bab84dadfcd1b380fe37..276fd61894e337045272a8d84e15710bceeeccff 100644 --- a/src/engine_redistribute.c +++ b/src/engine_redistribute.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "engine.h" @@ -273,6 +273,7 @@ struct redist_mapper_data { parts[k].x[j] += s->dim[j]; \ else if (parts[k].x[j] >= s->dim[j]) \ parts[k].x[j] -= s->dim[j]; \ + if (parts[k].x[j] == s->dim[j]) parts[k].x[j] = 0.0; \ } \ const int cid = cell_getid(s->cdim, parts[k].x[0] * s->iwidth[0], \ parts[k].x[1] * s->iwidth[1], \ @@ -292,7 +293,7 @@ struct redist_mapper_data { * * part version. */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(part); +void ENGINE_REDISTRIBUTE_DEST_MAPPER(part); /** * @brief Accumulate the counts of star particles per cell. @@ -300,7 +301,7 @@ static void ENGINE_REDISTRIBUTE_DEST_MAPPER(part); * * spart version. */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(spart); +void ENGINE_REDISTRIBUTE_DEST_MAPPER(spart); /** * @brief Accumulate the counts of gravity particles per cell. @@ -308,7 +309,7 @@ static void ENGINE_REDISTRIBUTE_DEST_MAPPER(spart); * * gpart version. */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(gpart); +void ENGINE_REDISTRIBUTE_DEST_MAPPER(gpart); /** * @brief Accumulate the counts of black holes particles per cell. @@ -316,7 +317,7 @@ static void ENGINE_REDISTRIBUTE_DEST_MAPPER(gpart); * * bpart version. */ -static void ENGINE_REDISTRIBUTE_DEST_MAPPER(bpart); +void ENGINE_REDISTRIBUTE_DEST_MAPPER(bpart); #endif /* redist_mapper_data */ @@ -373,9 +374,9 @@ struct savelink_mapper_data { * Threadpool helper for accumulating the counts of particles per cell. */ #ifdef SWIFT_DEBUG_CHECKS -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 1); +void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 1); #else -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 0); +void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 0); #endif /** @@ -383,9 +384,9 @@ static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(part, 0); * Threadpool helper for accumulating the counts of particles per cell. */ #ifdef SWIFT_DEBUG_CHECKS -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 1); +void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 1); #else -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 0); +void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 0); #endif /** @@ -393,9 +394,9 @@ static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 0); * Threadpool helper for accumulating the counts of particles per cell. */ #ifdef SWIFT_DEBUG_CHECKS -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 1); +void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 1); #else -static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 0); +void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 0); #endif #endif /* savelink_mapper_data */ @@ -422,8 +423,8 @@ struct relink_mapper_data { * @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) { +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; diff --git a/src/engine_split_particles.c b/src/engine_split_particles.c index 2764bff02c95599c4557eeb22edece8a289fa217..f20684f206632d79602377f0ed9101732677329f 100644 --- a/src/engine_split_particles.c +++ b/src/engine_split_particles.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "engine.h" @@ -258,6 +258,10 @@ void engine_split_gas_particle_split_mapper(void *restrict map_data, int count, black_holes_mark_part_as_not_swallowed(&p->black_holes_data); black_holes_mark_part_as_not_swallowed( &global_parts[k_parts].black_holes_data); + + /* Mark the particles as not having been swallowed by a sink */ + sink_mark_part_as_not_swallowed(&p->sink_data); + sink_mark_part_as_not_swallowed(&global_parts[k_parts].sink_data); } } } @@ -279,7 +283,7 @@ void engine_split_gas_particles(struct engine *e) { /* Abort if we are not doing any splitting */ if (!e->hydro_properties->particle_splitting) return; - if (e->s->nr_parts == 0) return; + if (e->total_nr_parts == 0) return; /* Time this */ const ticks tic = getticks(); diff --git a/src/engine_strays.c b/src/engine_strays.c index c8cd1ed8c42b8b47778b68f1ccce32088c0bccaa..d55909c73e08c5b594d6ab30662f3b6e64d92a25 100644 --- a/src/engine_strays.c +++ b/src/engine_strays.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI diff --git a/src/engine_unskip.c b/src/engine_unskip.c index eb81c2ad42fb03b970054b2b4112fde79e4f907c..30fc66aa16a7e87911a16e149aa6d1c64c1793ce 100644 --- a/src/engine_unskip.c +++ b/src/engine_unskip.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "engine.h" @@ -28,7 +28,6 @@ #include "active.h" #include "cell.h" #include "memswap.h" -#include "rt_active.h" /* Load the profiler header, if needed. */ #ifdef WITH_PROFILER @@ -86,8 +85,10 @@ static void engine_do_unskip_hydro(struct cell *c, struct engine *e) { /* Ignore empty cells. */ if (c->hydro.count == 0) return; +#ifndef MPI_SYMMETRIC_FORCE_INTERACTION /* Skip inactive cells. */ if (!cell_is_active_hydro(c, e)) return; +#endif /* Recurse */ if (c->split) { @@ -248,8 +249,10 @@ static void engine_do_unskip_gravity(struct cell *c, struct engine *e) { * * @param c The cell. * @param e The engine. + * @param sub_cycle 1 if this is a call for a sub cycle, 0 otherwise */ -static void engine_do_unskip_rt(struct cell *c, struct engine *e) { +static void engine_do_unskip_rt(struct cell *c, struct engine *e, + const int sub_cycle) { /* Note: we only get this far if engine_policy_rt is flagged. */ #ifdef SWIFT_DEBUG_CHECKS @@ -261,20 +264,20 @@ static void engine_do_unskip_rt(struct cell *c, struct engine *e) { if (!cell_get_flag(c, cell_flag_has_tasks)) return; /* Do we have work to do? */ - if (!rt_should_do_unskip_cell(c, e)) return; + if (c->hydro.count == 0) return; + if (!cell_is_rt_active(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_rt(cp, e); + engine_do_unskip_rt(c->progeny[k], e, sub_cycle); } } } /* Unskip any active tasks. */ - const int forcerebuild = cell_unskip_rt_tasks(c, &e->sched); + const int forcerebuild = cell_unskip_rt_tasks(c, &e->sched, sub_cycle); if (forcerebuild) atomic_inc(&e->forcerebuild); } @@ -365,7 +368,7 @@ void engine_do_unskip_mapper(void *map_data, int num_elements, if (!(e->policy & engine_policy_rt)) error("Trying to unskip radiative transfer tasks in a non-rt run!"); #endif - engine_do_unskip_rt(c, e); + engine_do_unskip_rt(c, e, /*sub_cycle=*/0); break; default: #ifdef SWIFT_DEBUG_CHECKS @@ -409,6 +412,8 @@ void engine_unskip(struct engine *e) { for (int k = 0; k < s->nr_local_cells_with_tasks; k++) { struct cell *c = &s->cells_top[local_cells[k]]; + if (cell_is_empty(c)) continue; + if ((with_hydro && cell_is_active_hydro(c, e)) || (with_self_grav && cell_is_active_gravity(c, e)) || (with_ext_grav && c->nodeID == nodeID && @@ -417,7 +422,7 @@ void engine_unskip(struct engine *e) { (with_stars && c->nodeID == nodeID && cell_is_active_stars(c, e)) || (with_sinks && cell_is_active_sinks(c, e)) || (with_black_holes && cell_is_active_black_holes(c, e)) || - (with_rt && rt_should_do_unskip_cell(c, e))) { + (with_rt && cell_is_rt_active(c, e))) { if (num_active_cells != k) memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int)); @@ -508,3 +513,59 @@ void engine_unskip(struct engine *e) { message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); } + +void engine_do_unskip_sub_cycle_mapper(void *map_data, int num_elements, + void *extra_data) { + + struct engine *e = (struct engine *)extra_data; + struct cell *const cells_top = e->s->cells_top; + + /* 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]]; + + engine_do_unskip_rt(c, e, /*sub_cycle=*/1); + } +} + +/** + * @brief Unskip all the RT tasks that are active during this sub-cycle. + * + * @param e The #engine. + */ +void engine_unskip_rt_sub_cycle(struct engine *e) { + + const ticks tic = getticks(); + struct space *s = e->s; + const int with_rt = e->policy & engine_policy_rt; + + if (!with_rt) error("Unskipping sub-cycles when running without RT!"); + + 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 (c->hydro.count == 0) continue; + + if (cell_is_rt_active(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, engine_do_unskip_sub_cycle_mapper, local_cells, + num_active_cells, sizeof(int), /*chunk=*/1, e); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} diff --git a/src/entropy_floor.h b/src/entropy_floor.h index 58e87cf811687396178bddfbdb0eab2b89ff865f..08c934db241c0ab9abe3bea53acad0508e41f312 100644 --- a/src/entropy_floor.h +++ b/src/entropy_floor.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "common_io.h" diff --git a/src/equation_of_state.c b/src/equation_of_state.c index d59bd05a2c8eae69f3afad1157f05ce48d7e14ec..5b1c0dc50b18f321e57a90e00a0a0363d90a2f1e 100644 --- a/src/equation_of_state.c +++ b/src/equation_of_state.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 diff --git a/src/equation_of_state.h b/src/equation_of_state.h index d170ce1a7c5c64fe95e415c997c037caeb576258..769e3dbb6888f52efc16d52858b26ed0d8150515 100644 --- a/src/equation_of_state.h +++ b/src/equation_of_state.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 @@ -27,7 +27,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right functions */ #if defined(EOS_IDEAL_GAS) diff --git a/src/equation_of_state/ideal_gas/equation_of_state.h b/src/equation_of_state/ideal_gas/equation_of_state.h index 0aace801fbd1090e4a8b98d81fb3109a81615de2..cfdff5c7fd4af8052cf4a36b8bace66718e235e5 100644 --- a/src/equation_of_state/ideal_gas/equation_of_state.h +++ b/src/equation_of_state/ideal_gas/equation_of_state.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 diff --git a/src/equation_of_state/isothermal/equation_of_state.h b/src/equation_of_state/isothermal/equation_of_state.h index 540bf073cee106b91e2a9f4ecedb1b20238856fd..3ee1e18ebb0b8dcc6be8e02d4ee640edadfc35d6 100644 --- a/src/equation_of_state/isothermal/equation_of_state.h +++ b/src/equation_of_state/isothermal/equation_of_state.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 diff --git a/src/equation_of_state/planetary/equation_of_state.h b/src/equation_of_state/planetary/equation_of_state.h index 880d67693d728823a5247944b99ac782cc234f0f..56261eff292ebc67e0a9e6be559665bee74a79c2 100644 --- a/src/equation_of_state/planetary/equation_of_state.h +++ b/src/equation_of_state/planetary/equation_of_state.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl). * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify diff --git a/src/equation_of_state/planetary/hm80.h b/src/equation_of_state/planetary/hm80.h index bfd732a891f137eb855f94b8d9e580428c495e3f..1aa746a76c3c9a03596cceb822e7954621a8a76a 100644 --- a/src/equation_of_state/planetary/hm80.h +++ b/src/equation_of_state/planetary/hm80.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl). * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify diff --git a/src/equation_of_state/planetary/ideal_gas.h b/src/equation_of_state/planetary/ideal_gas.h index 08103989e08311e33a94b3f9f43d321e15834a08..c6da2445dad98d37cd03caa444efb9744f80d238 100644 --- a/src/equation_of_state/planetary/ideal_gas.h +++ b/src/equation_of_state/planetary/ideal_gas.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 diff --git a/src/equation_of_state/planetary/sesame.h b/src/equation_of_state/planetary/sesame.h index 51b923c28b85296050186e9bc7c7a3feadbb4265..f59c687acdaefa652e85b99eae19db8beb694257 100644 --- a/src/equation_of_state/planetary/sesame.h +++ b/src/equation_of_state/planetary/sesame.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl). * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify @@ -25,7 +25,6 @@ * * Contains the SESAME and ANEOS-in-SESAME-style EOS functions for * equation_of_state/planetary/equation_of_state.h - * */ /* Some standard headers. */ @@ -48,56 +47,98 @@ struct SESAME_params { float *table_P_rho_T; float *table_c_rho_T; float *table_log_s_rho_T; - int date, num_rho, num_T; + int version_date, num_rho, num_T; float u_tiny, P_tiny, c_tiny, s_tiny; enum eos_planetary_material_id mat_id; }; -// Parameter values for each material (cgs units) +// Parameter values for each material INLINE static void set_SESAME_iron(struct SESAME_params *mat, enum eos_planetary_material_id mat_id) { // SESAME 2140 mat->mat_id = mat_id; - mat->date = 20201003; + mat->version_date = 20220714; } INLINE static void set_SESAME_basalt(struct SESAME_params *mat, enum eos_planetary_material_id mat_id) { // SESAME 7530 mat->mat_id = mat_id; - mat->date = 20201003; + mat->version_date = 20220714; } INLINE static void set_SESAME_water(struct SESAME_params *mat, enum eos_planetary_material_id mat_id) { // SESAME 7154 mat->mat_id = mat_id; - mat->date = 20201003; + mat->version_date = 20220714; } INLINE static void set_SS08_water(struct SESAME_params *mat, enum eos_planetary_material_id mat_id) { // Senft & Stewart (2008) mat->mat_id = mat_id; - mat->date = 20201003; + mat->version_date = 20220714; } INLINE static void set_ANEOS_forsterite(struct SESAME_params *mat, enum eos_planetary_material_id mat_id) { // Stewart et al. (2019) mat->mat_id = mat_id; - mat->date = 20201104; + mat->version_date = 20220714; } INLINE static void set_ANEOS_iron(struct SESAME_params *mat, enum eos_planetary_material_id mat_id) { // Stewart (2020) mat->mat_id = mat_id; - mat->date = 20201104; + mat->version_date = 20220714; } INLINE static void set_ANEOS_Fe85Si15(struct SESAME_params *mat, enum eos_planetary_material_id mat_id) { // Stewart (2020) mat->mat_id = mat_id; - mat->date = 20201104; + mat->version_date = 20220714; +} + +/* + Skip a line while reading a file. +*/ +INLINE static int skip_line(FILE *f) { + int c; + + // Read each character until reaching the end of the line or file + do { + c = fgetc(f); + } while ((c != '\n') && (c != EOF)); + + return c; +} + +/* + Skip n lines while reading a file. +*/ +INLINE static int skip_lines(FILE *f, int n) { + int c; + + for (int i = 0; i < n; i++) c = skip_line(f); + + return c; } -// Read the tables from file +/* + Read the data from the table file. + + File contents (SESAME-like format plus header info etc) + ------------- + # header (12 lines) + version_date (YYYYMMDD) + num_rho num_T + rho[0] rho[1] ... rho[num_rho] (kg/m^3) + T[0] T[1] ... T[num_T] (K) + u[0, 0] P[0, 0] c[0, 0] s[0, 0] (J/kg, Pa, m/s, + J/K/kg) u[1, 0] ... ... ... + ... ... ... ... + u[num_rho-1, 0] ... ... ... + u[0, 1] ... ... ... + ... ... ... ... + u[num_rho-1, num_T-1] ... ... s[num_rho-1, num_T-1] +*/ INLINE static void load_table_SESAME(struct SESAME_params *mat, char *table_file) { @@ -105,30 +146,26 @@ INLINE static void load_table_SESAME(struct SESAME_params *mat, FILE *f = fopen(table_file, "r"); if (f == NULL) error("Failed to open the SESAME EoS file '%s'", table_file); - // Ignore header lines - char buffer[100]; - for (int i = 0; i < 6; i++) { - if (fgets(buffer, 100, f) == NULL) - error("Failed to read the SESAME EoS file header %s", table_file); - } - float ignore; + // Skip header lines + skip_lines(f, 12); // Table properties - int date; - int c = fscanf(f, "%d", &date); + int version_date; + int c = fscanf(f, "%d", &version_date); if (c != 1) error("Failed to read the SESAME EoS table %s", table_file); - if (date != mat->date) + if (version_date != mat->version_date) error( - "EoS file %s date %d does not match expected %d" + "EoS file %s version_date %d does not match expected %d (YYYYMMDD)." "\nPlease download the file using " "examples/Planetary/EoSTables/get_eos_tables.sh", - table_file, date, mat->date); + table_file, version_date, mat->version_date); c = fscanf(f, "%d %d", &mat->num_rho, &mat->num_T); if (c != 2) error("Failed to read the SESAME EoS table %s", table_file); // Ignore the first elements of rho = 0, T = 0 mat->num_rho--; mat->num_T--; + float ignore; // Allocate table memory mat->table_log_rho = (float *)malloc(mat->num_rho * sizeof(float)); @@ -206,8 +243,8 @@ INLINE static void prepare_table_SESAME(struct SESAME_params *mat) { mat->table_log_u_rho_T[i_rho * mat->num_T + i_T - 1]) { // Replace it and all elements below it with that value - for (int j_u = 0; j_u < i_T; j_u++) { - mat->table_log_u_rho_T[i_rho * mat->num_T + j_u] = + for (int j_T = 0; j_T < i_T; j_T++) { + mat->table_log_u_rho_T[i_rho * mat->num_T + j_T] = mat->table_log_u_rho_T[i_rho * mat->num_T + i_T]; } break; @@ -264,10 +301,10 @@ INLINE static void prepare_table_SESAME(struct SESAME_params *mat) { INLINE static void convert_units_SESAME(struct SESAME_params *mat, const struct unit_system *us) { + // Convert input table values from all-SI to internal units struct unit_system si; units_init_si(&si); - // All table values in SI, apart from sound speeds in km/s // Densities (log) for (int i_rho = 0; i_rho < mat->num_rho; i_rho++) { mat->table_log_rho[i_rho] += @@ -285,7 +322,7 @@ INLINE static void convert_units_SESAME(struct SESAME_params *mat, units_cgs_conversion_factor(&si, UNIT_CONV_PRESSURE) / units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); mat->table_c_rho_T[i_rho * mat->num_T + i_T] *= - 1e3f * units_cgs_conversion_factor(&si, UNIT_CONV_SPEED) / + units_cgs_conversion_factor(&si, UNIT_CONV_SPEED) / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); mat->table_log_s_rho_T[i_rho * mat->num_T + i_T] += logf(units_cgs_conversion_factor( @@ -301,7 +338,7 @@ INLINE static void convert_units_SESAME(struct SESAME_params *mat, units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); mat->P_tiny *= units_cgs_conversion_factor(&si, UNIT_CONV_PRESSURE) / units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); - mat->c_tiny *= 1e3f * units_cgs_conversion_factor(&si, UNIT_CONV_SPEED) / + mat->c_tiny *= units_cgs_conversion_factor(&si, UNIT_CONV_SPEED) / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); mat->s_tiny *= units_cgs_conversion_factor(&si, diff --git a/src/equation_of_state/planetary/tillotson.h b/src/equation_of_state/planetary/tillotson.h index 8ffbdbc0bd4173349950b13333ef81197b14f3e8..5831b1785485d5dc2563718c94fc9cc04a08b822 100644 --- a/src/equation_of_state/planetary/tillotson.h +++ b/src/equation_of_state/planetary/tillotson.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl). * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify diff --git a/src/error.h b/src/error.h index 900d7e1bddbae862ef92db083af8a80d76dddd2d..8ab8e2064574ac5c5cacd19509a20f538926d8a5 100644 --- a/src/error.h +++ b/src/error.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Matthieu Schaller (schaller@strw.leidenuniv.nl). * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_ERROR_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdio.h> @@ -183,4 +183,49 @@ extern int engine_rank; }) #endif +#ifdef SWIFT_DEBUG_CHECKS + +/* Define which cells you'd like to trace. Make them 0 to turn this off. */ +#define PROBLEMCELL1 0 +#define PROBLEMCELL2 0 + +/** + * @brief Macro to trace cells throughout the code. + */ +#ifdef WITH_MPI +extern int engine_rank; +#define celltrace(c, s, ...) \ + ({ \ + if (c->cellID == PROBLEMCELL1 || c->cellID == PROBLEMCELL2) \ + printf("[%04i] %s %s: cell %lld local=%d " s "\n", engine_rank, \ + clocks_get_timesincestart(), __FUNCTION__, c->cellID, \ + c->nodeID == engine_rank, ##__VA_ARGS__); \ + fflush(stdout); \ + }) +#else +#define celltrace(c, s, ...) \ + ({ \ + if (c->cellID == PROBLEMCELL1 || c->cellID == PROBLEMCELL2) \ + printf("%s %s: cell %lld " s "\n", clocks_get_timesincestart(), \ + __FUNCTION__, c->cellID, ##__VA_ARGS__); \ + fflush(stdout); \ + }) +#endif /* WITH_MPI */ +#endif /* SWIFT_DEBUG_CHECKS */ + +/* Define which particles you'd like to trace. */ +#define PROBLEMPART1 -1 +#define PROBLEMPART2 -1 + +/** + * @brief Macro to trace particles throughout the code. + */ +#define parttrace(p, s, ...) \ + ({ \ + if (p->id == PROBLEMPART1 || p->id == PROBLEMPART2) \ + printf("%s %s: part %lld " s "\n", clocks_get_timesincestart(), \ + __FUNCTION__, p->id, ##__VA_ARGS__); \ + fflush(stdout); \ + }) + #endif /* SWIFT_ERROR_H */ diff --git a/src/exchange_structs.c b/src/exchange_structs.c new file mode 100644 index 0000000000000000000000000000000000000000..6b4045d19896e009bd852c2caf3fb01c9bf8c7c6 --- /dev/null +++ b/src/exchange_structs.c @@ -0,0 +1,127 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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> + +/* Standard headers */ +#include <limits.h> +#include <stdlib.h> + +/* Local headers */ +#include "error.h" + +/** + * @brief Given an array of structs of size element_size, send + * nr_send[i] elements to each node i. Allocates the receive + * buffer recvbuf to the appropriate size and returns its size + * in nr_recv_tot. + * + * @param nr_send Number of elements to send to each node + * @param nr_recv Number of elements to receive from each node + * @param sendbuf The elements to send + * @param recvbuf The output buffer + * + */ +void exchange_structs(size_t *nr_send, void *sendbuf, size_t *nr_recv, + void *recvbuf, size_t element_size) { + +#ifdef WITH_MPI + + /* Determine rank, number of ranks */ + int nr_nodes, nodeID; + MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes); + MPI_Comm_rank(MPI_COMM_WORLD, &nodeID); + + /* Compute send offsets */ + size_t *send_offset = (size_t *)malloc(nr_nodes * sizeof(size_t)); + send_offset[0] = 0; + for (int i = 1; i < nr_nodes; i += 1) { + send_offset[i] = send_offset[i - 1] + nr_send[i - 1]; + } + + /* Compute receive offsets */ + size_t *recv_offset = (size_t *)malloc(nr_nodes * sizeof(size_t)); + recv_offset[0] = 0; + for (int i = 1; i < nr_nodes; i += 1) { + recv_offset[i] = recv_offset[i - 1] + nr_recv[i - 1]; + } + + /* Allocate request objects (one send and receive per node) */ + MPI_Request *request = + (MPI_Request *)malloc(2 * sizeof(MPI_Request) * nr_nodes); + + /* Make type to communicate the struct */ + MPI_Datatype value_mpi_type; + if (MPI_Type_contiguous(element_size, MPI_BYTE, &value_mpi_type) != + MPI_SUCCESS || + MPI_Type_commit(&value_mpi_type) != MPI_SUCCESS) { + error("Failed to create MPI type for struct to exchange."); + } + + /* + * Post the send operations. This is an alltoallv really but + * we want to avoid the limits imposed by int counts and offsets + * in MPI_Alltoallv. + */ + for (int i = 0; i < nr_nodes; i += 1) { + if (nr_send[i] > 0) { + + /* TODO: handle very large messages */ + if (nr_send[i] > INT_MAX) + error("exchange_structs() fails if nr_send > INT_MAX!"); + + char *buf = (char *)sendbuf; + MPI_Isend(&(buf[send_offset[i] * element_size]), (int)nr_send[i], + value_mpi_type, i, 0, MPI_COMM_WORLD, &(request[i])); + } else { + request[i] = MPI_REQUEST_NULL; + } + } + + /* Post the receives */ + for (int i = 0; i < nr_nodes; i += 1) { + if (nr_recv[i] > 0) { + + /* TODO: handle very large messages */ + if (nr_recv[i] > INT_MAX) + error("exchange_structs() fails if nr_recv > INT_MAX!"); + + char *buf = (char *)recvbuf; + MPI_Irecv(&(buf[recv_offset[i] * element_size]), (int)nr_recv[i], + value_mpi_type, i, 0, MPI_COMM_WORLD, &(request[i + nr_nodes])); + } else { + request[i + nr_nodes] = MPI_REQUEST_NULL; + } + } + + /* Wait for everything to complete */ + MPI_Waitall(2 * nr_nodes, request, MPI_STATUSES_IGNORE); + + /* Done with the MPI type */ + MPI_Type_free(&value_mpi_type); + + /* Tidy up */ + free(recv_offset); + free(send_offset); + free(request); +#else + error("should only be called in MPI mode"); +#endif +} diff --git a/src/pressure_floor.c b/src/exchange_structs.h similarity index 76% rename from src/pressure_floor.c rename to src/exchange_structs.h index 2901241f1a2ff42d526a0122008dcf6ab2d8ea6f..1266978987d1849cd4e1cce5c2a4680ff3926f73 100644 --- a/src/pressure_floor.c +++ b/src/exchange_structs.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2021 John Helly (j.c.helly@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 @@ -17,8 +17,10 @@ * ******************************************************************************/ -/* This object's header. */ -#include "pressure_floor.h" +#ifndef SWIFT_EXCHANGE_STRUCTS_H +#define SWIFT_EXCHANGE_STRUCTS_H -/* Pressure floor for the physics model. */ -struct pressure_floor_properties pressure_floor_props; +void exchange_structs(size_t *nr_send, void *sendbuf, size_t *nr_recv, + void *recvbuf, size_t element_size); + +#endif diff --git a/src/exp.h b/src/exp.h index 470876a23293bcf80c121f8868511f88fb971be4..cd3967b3e8bd83b323f1cc494a1a5f0943e5009e 100644 --- a/src/exp.h +++ b/src/exp.h @@ -20,7 +20,7 @@ #define SWIFT_OPTIMIZED_EXP_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" diff --git a/src/exp10.h b/src/exp10.h index fbae34af421c9d0bcc773fbfc50ac44df1e8fd42..d55faf35a10323f40d2b4f2743db17e179a43c75 100644 --- a/src/exp10.h +++ b/src/exp10.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_EXP10_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> diff --git a/src/extra_io.h b/src/extra_io.h new file mode 100644 index 0000000000000000000000000000000000000000..65471eb115c81881679ad39671b2febd7707f73c --- /dev/null +++ b/src/extra_io.h @@ -0,0 +1,88 @@ + +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 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_EXTRA_IO_H +#define SWIFT_EXTRA_IO_H + +/* Config parameters. */ +#include <config.h> + +/* Import the i/o routines the user asked for */ +#if defined(EXTRA_IO_EAGLE) +#include "extra_io/EAGLE/extra_io.h" +#elif defined(EXTRA_IO_NONE) + +struct extra_io_properties {}; + +INLINE static int extra_io_write_particles(const struct part* parts, + const struct xpart* xparts, + struct io_props* list, + const int with_cosmology) { + return 0; +} + +INLINE static int extra_io_write_sparticles(const struct spart* sparts, + struct io_props* list, + const int with_cosmology) { + return 0; +} + +INLINE static int extra_io_write_bparticles(const struct bpart* bparts, + struct io_props* list, + const int with_cosmology) { + return 0; +} + +#ifdef HAVE_HDF5 +INLINE static void extra_io_write_flavour(hid_t h_grp, hid_t h_grp_columns) {} +#endif + +INLINE static void extra_io_init(struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + const struct cosmology* cosmo, + struct extra_io_properties* props) {} + +INLINE static void extra_io_clean(struct extra_io_properties* props) {} + +INLINE static void extra_io_struct_dump(const struct extra_io_properties* props, + FILE* stream) {} + +INLINE static void extra_io_struct_restore(struct extra_io_properties* props, + FILE* stream) {} + +/* In this case there are no extra lightcone map types */ +static const struct lightcone_map_type extra_lightcone_map_types[] = { + { + /* .name = */ "", + /* .update_map = */ NULL, + /* .ptype_contributes = */ NULL, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_NO_UNITS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, +}; + +#else +#error "Invalid choice of extra-i/o." +#endif + +#endif /* SWIFT_EXTRA_IO_H */ diff --git a/src/extra_io/EAGLE/extra.h b/src/extra_io/EAGLE/extra.h new file mode 100644 index 0000000000000000000000000000000000000000..7ed12ef86fc022c511443eef4aa5df22370582dc --- /dev/null +++ b/src/extra_io/EAGLE/extra.h @@ -0,0 +1,976 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 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_EXTRA_EAGLE_H +#define SWIFT_EXTRA_EAGLE_H + +#include "chemistry.h" +#include "cooling.h" +#include "engine.h" +#include "star_formation.h" + +#define xray_table_date_string 20230110 + +#define xray_emission_N_temperature 46 +#define xray_emission_N_density 71 +#define xray_emission_N_helium 10 +#define xray_emission_N_element 10 +#define xray_emission_N_redshift 45 + +/** + * @brief X-ray bands available in the interpolation tables + */ +enum xray_band_types { + xray_band_types_erosita_low_intrinsic_photons, /*< eROSITA 0.2 - 2.3 keV */ + xray_band_types_erosita_high_intrinsic_photons, /*< eROSITA 2.3 - 8.0 keV */ + xray_band_types_ROSAT_intrinsic_photons, /*< ROSAT 0.5 - 2.0 keV */ + xray_band_types_erosita_low_intrinsic_energies, /*< eROSITA 0.2 - 2.3 keV */ + xray_band_types_erosita_high_intrinsic_energies, /*< eROSITA 2.3 - 8.0 keV */ + xray_band_types_ROSAT_intrinsic_energies, /*< ROSAT 0.5 - 2.0 keV */ + xray_band_types_count +}; + +/** + * @brief The general properties required for the extra i/o fields. + */ +struct extra_io_properties { + + struct xray_properties { + + /* Element masses for the chemistry elements (cgs) */ + float *element_mass; + + /* Temperature bins from xray table (cgs) */ + float *Temperatures; + + /* Minimum and maximum temperature the table exists for */ + float Temperature_min; + float Temperature_max; + + /* Density bins from xray table (physical cgs) */ + float *Densities; + + /* Minimum and maximum density the table exists for */ + float Density_min; + float Density_max; + + /* Helium fraction bins from xray table */ + float *He_bins; + + /* Redshift bins from xray table */ + float *Redshifts; + + /* Maximum redshift the table exists for */ + float Redshift_max; + + /* Solar metallicites from xray table */ + float *Solar_metallicity; + + /* Log of solar metallicites from xray table */ + float *Log10_solar_metallicity; + + /* Integrated photon emissivity in the erosita-low band (0.2-2.3 keV) + * (physical) */ + float *emissivity_erosita_low_intrinsic_photons; + + /* Integrated photon emissivity in the erosita-high band (2.3-8.0 keV) + * (physical) */ + float *emissivity_erosita_high_intrinsic_photons; + + /* Integrated photon emissivity in the ROSAT band (0.5-2.0 keV) (physical) + */ + float *emissivity_ROSAT_intrinsic_photons; + + /* Integrated emissivity in the erosita-low band (0.2-2.3 keV) + * (physical) */ + float *emissivity_erosita_low_intrinsic_energies; + + /* Integrated emissivity in the erosita-high band (2.3-8.0 keV) + * (physical) */ + float *emissivity_erosita_high_intrinsic_energies; + + /* Integrated emissivity in the ROSAT band (0.5-2.0 keV) (physical) + */ + float *emissivity_ROSAT_intrinsic_energies; + + /* Path to the xray table */ + char xray_table_path[500]; + + /* Photon emissivity unit conversion factor */ + double xray_photon_emissivity_unit_conversion; + + /* Energy emissivity unit conversion factor */ + double xray_energy_emissivity_unit_conversion; + } xray_data; +}; + +/** + * @brief Reads in xray table header. Consists of tables + * of values for temperature, hydrogen number density, helium fraction, + * solar metallicity, redshifts, and element masses. + * + * @param xrays Xray data structure + * @param fname Xray table path + */ +INLINE static void read_xray_header(struct xray_properties *xrays, + const char *fname) { + + hid_t tempfile_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + if (tempfile_id < 0) error("unable to open file %s\n", fname); + + /* Check whether the correct table version is being used */ + int datestring; + + hid_t dataset = H5Dopen(tempfile_id, "Date_String", H5P_DEFAULT); + herr_t status = H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, + H5P_DEFAULT, &datestring); + if (status < 0) printf("error reading the date string"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + if (datestring != xray_table_date_string) + error( + "The table and code version do not match, please use table version %i", + xray_table_date_string); + + /* Read temperature bins */ + if (posix_memalign((void **)&xrays->Temperatures, SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_temperature * sizeof(float)) != 0) + error("Failed to allocate temperatures array\n"); + + dataset = H5Dopen(tempfile_id, "Bins/Temperature_bins", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->Temperatures); + if (status < 0) printf("error reading temperatures"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* Read density bins */ + if (posix_memalign((void **)&xrays->Densities, SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_density * sizeof(float)) != 0) + error("Failed to allocate densities array\n"); + + dataset = H5Dopen(tempfile_id, "Bins/Density_bins", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->Densities); + if (status < 0) printf("error reading densities"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* Read Helium bins */ + if (posix_memalign((void **)&xrays->He_bins, SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_helium * sizeof(float)) != 0) + error("Failed to allocate He_bins array\n"); + + dataset = H5Dopen(tempfile_id, "Bins/He_bins", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->He_bins); + if (status < 0) printf("error reading Helium massfractions"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* Read solar metallicity */ + if (posix_memalign((void **)&xrays->Log10_solar_metallicity, + SWIFT_STRUCT_ALIGNMENT, + chemistry_element_count * sizeof(float)) != 0) + error("Failed to allocate Solar_metallicity array\n"); + + dataset = H5Dopen(tempfile_id, "Bins/Solar_metallicities", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->Log10_solar_metallicity); + if (status < 0) printf("error reading solar metalicities"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* Get Solar metallicities from log solar metallicities */ + if (posix_memalign((void **)&xrays->Solar_metallicity, SWIFT_STRUCT_ALIGNMENT, + chemistry_element_count * sizeof(float)) != 0) + error("Failed to allocate Solar_metallicity array\n"); + + for (int i = 0; i < chemistry_element_count; ++i) + xrays->Solar_metallicity[i] = exp10f(xrays->Log10_solar_metallicity[i]); + + /* Read redshift bins */ + if (posix_memalign((void **)&xrays->Redshifts, SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_redshift * sizeof(float)) != 0) + error("Failed to allocate Redshifts array\n"); + + dataset = H5Dopen(tempfile_id, "Bins/Redshift_bins", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->Redshifts); + if (status < 0) printf("error reading redshift bins"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* Read element mass */ + if (posix_memalign((void **)&xrays->element_mass, SWIFT_STRUCT_ALIGNMENT, + 10 * sizeof(float)) != 0) + error("Failed to allocate element_mass array\n"); + + dataset = H5Dopen(tempfile_id, "Bins/Element_masses", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->element_mass); + if (status < 0) printf("error reading element masses"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); +} + +/** + * @brief Reads in xray table. Consists of tables + * of values for xray emissivities in different bands. + * We read the erosita-low, erosita-high and ROSAT band + * in their intrinsic forms (no multiplication with response function) + * + * @param xrays Xray data structure + * @param fname Xray table path + */ +INLINE static void read_xray_table(struct xray_properties *xrays, + const char *fname) { + + /* Open File */ + hid_t file_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + + // erosita-low intrinsic photons + if (swift_memalign("xrays_table_erosita_low_photons", + (void **)&xrays->emissivity_erosita_low_intrinsic_photons, + SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_redshift * xray_emission_N_helium * + xray_emission_N_element * xray_emission_N_temperature * + xray_emission_N_density * sizeof(float)) != 0) + error( + "Failed to allocate xray emissivity_erosita_low_intrinsic_photons " + "array\n"); + + /* Read full table */ + hid_t dataset = + H5Dopen(file_id, "erosita-low/photons_intrinsic", H5P_DEFAULT); + herr_t status = + H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->emissivity_erosita_low_intrinsic_photons); + if (status < 0) printf("error reading X-Ray table\n"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* erosita-high intrinsic photons */ + if (swift_memalign("xrays_table_erosita_high_photons", + (void **)&xrays->emissivity_erosita_high_intrinsic_photons, + SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_redshift * xray_emission_N_helium * + xray_emission_N_element * xray_emission_N_temperature * + xray_emission_N_density * sizeof(float)) != 0) + error( + "Failed to allocate xray emissivity_erosita_high_intrinsic_photons " + "array\n"); + + /* Read full table */ + dataset = H5Dopen(file_id, "erosita-high/photons_intrinsic", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->emissivity_erosita_high_intrinsic_photons); + if (status < 0) printf("error reading X-Ray table\n"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* ROSAT intrinsic photons */ + if (swift_memalign("xray_table_ROSAT_photons", + (void **)&xrays->emissivity_ROSAT_intrinsic_photons, + SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_redshift * xray_emission_N_helium * + xray_emission_N_element * xray_emission_N_temperature * + xray_emission_N_density * sizeof(float)) != 0) + error("Failed to allocate xray emissivity_ROSAT_intrinsic_photons array\n"); + + /* Read full table */ + dataset = H5Dopen(file_id, "ROSAT/photons_intrinsic", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->emissivity_ROSAT_intrinsic_photons); + if (status < 0) printf("error reading X-Ray table\n"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + // erosita-low intrinsic energies + if (swift_memalign("xrays_table_erosita_low_energies", + (void **)&xrays->emissivity_erosita_low_intrinsic_energies, + SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_redshift * xray_emission_N_helium * + xray_emission_N_element * xray_emission_N_temperature * + xray_emission_N_density * sizeof(float)) != 0) + error( + "Failed to allocate xray emissivity_erosita_low_intrinsic_energies " + "array\n"); + + /* Read full table */ + dataset = H5Dopen(file_id, "erosita-low/energies_intrinsic", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->emissivity_erosita_low_intrinsic_energies); + if (status < 0) printf("error reading X-Ray table\n"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* erosita-high intrinsic energies */ + if (swift_memalign( + "xrays_table_erosita_high_energies", + (void **)&xrays->emissivity_erosita_high_intrinsic_energies, + SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_redshift * xray_emission_N_helium * + xray_emission_N_element * xray_emission_N_temperature * + xray_emission_N_density * sizeof(float)) != 0) + error( + "Failed to allocate xray emissivity_erosita_high_intrinsic_energies " + "array\n"); + + /* Read full table */ + dataset = H5Dopen(file_id, "erosita-high/energies_intrinsic", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->emissivity_erosita_high_intrinsic_energies); + if (status < 0) printf("error reading X-Ray table\n"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* ROSAT intrinsic energies */ + if (swift_memalign("xray_table_ROSAT_energies", + (void **)&xrays->emissivity_ROSAT_intrinsic_energies, + SWIFT_STRUCT_ALIGNMENT, + xray_emission_N_redshift * xray_emission_N_helium * + xray_emission_N_element * xray_emission_N_temperature * + xray_emission_N_density * sizeof(float)) != 0) + error( + "Failed to allocate xray emissivity_ROSAT_intrinsic_energies array\n"); + + /* Read full table */ + dataset = H5Dopen(file_id, "ROSAT/energies_intrinsic", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + xrays->emissivity_ROSAT_intrinsic_energies); + if (status < 0) printf("error reading X-Ray table\n"); + status = H5Dclose(dataset); + if (status < 0) printf("error closing dataset"); + + /* Close file */ + status = H5Fclose(file_id); + if (status < 0) printf("error closing file"); +} + +/** + * @brief Find the 1d index for a table dimension + * + * @param table 1d array with binned values + * @param size dimensionality + * @param x value for which we aim to find the index + * @param i (return) index + * @param dx (return) offset from index bin + */ +INLINE static void get_index_1d(const float *restrict table, const int size, + const float x, int *i, float *restrict dx) { + + const float epsilon = 1e-4f; + + const float delta = (size - 1) / (table[size - 1] - table[0]); + + if (x < table[0] + epsilon) { + /* We are below the first element */ + *i = 0; + *dx = 0.f; + } else if (x < table[size - 1] - epsilon) { + /* Normal case */ + *i = (x - table[0]) * delta; + *dx = (x - table[*i]) * delta; + } else { + /* We are after the last element */ + *i = size - 2; + *dx = 1.f; + } +} + +/** + * @brief Find the 1d index for a table dimension with irregularly spaced bins + * + * @param table 1d array with monotonically increasing binned values + * @param size dimensionality + * @param x value for which we aim to find the index + * @param i (return) index + * @param dx (return) offset from index bin + */ +INLINE static void get_index_1d_irregular(const float *restrict table, + const int size, const float x, int *i, + float *restrict dx) { + const float epsilon = 1e-6f; + + if (x < table[0] + epsilon) { + + *i = 0; + *dx = 0.f; + + } else if (x < table[size - 1] - epsilon) { + + int min_idx = -1; + + /* Do this the hard way: Search the table + * for the largest index i in table[] such + * that table[i] < x */ + for (int idx = 0; idx < size; idx++) { + + if (x - table[idx] <= 0.f) { + + /* Found the first entry that is larger than x, go back by 1. */ + min_idx = idx - 1; + break; + } + } + + *i = min_idx; + *dx = (x - table[min_idx]) / (table[min_idx + 1] - table[min_idx]); + + } else { + + *i = size - 2; + *dx = 1.f; + } +} + +/** + * @brief Returns the 1d index of element with 5d indices x,y,z,w + * from a flattened 5d array in row major order + * + * @param x, y, z, v, w Indices of element of interest + * @param Nx, Ny, Nz, Nv, Nw Sizes of array dimensions + */ +__attribute__((always_inline)) INLINE int row_major_index_5d( + const int x, const int y, const int z, const int w, const int v, + const int Nx, const int Ny, const int Nz, const int Nw, const int Nv) { + +#ifdef SWIFT_DEBUG_CHECKS + assert(x < Nx); + assert(y < Ny); + assert(z < Nz); + assert(w < Nw); + assert(v < Nv); +#endif + + return x * Ny * Nz * Nw * Nv + y * Nz * Nw * Nv + z * Nw * Nv + w * Nv + v; +} + +/** + * @brief 4d interpolation of the Xray table + * + * @param emissivity xray table + * @param element number table index for missing element + * @param n_H_index Index along the Hydrogen number density dimension + * @param He_index Index along the Helium abundance dimension + * @param T_index Index along temperature dimension + * @param z_index Index along redshift dimension + * @param d_nH Offset between Hydrogen density and table[n_H_index] + * @param d_He Offset between Helium abundance and table[He_index] + * @param d_T Offset between temperture and table[T_index] + * @param d_z Offset between redshift and table[red_index] + * + * @return The log10 of the emssisivity + */ +INLINE static float interpolate_xray(const float *emissivity, + const int element_number, + const int nH_index, const int He_index, + const int T_index, const int z_index, + const float d_nH, const float d_He, + const float d_T, const float d_z) { + const float t_nH = 1.f - d_nH; + const float t_He = 1.f - d_He; + const float t_T = 1.f - d_T; + const float t_z = 1.f - d_z; + + float result = 0.f; + + result += t_nH * t_He * t_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 0, element_number, T_index + 0, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += t_nH * t_He * d_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 0, element_number, T_index + 1, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += t_nH * d_He * t_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 1, element_number, T_index + 0, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * t_He * t_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 0, element_number, T_index + 0, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += t_nH * d_He * d_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 1, element_number, T_index + 1, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * t_He * d_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 0, element_number, T_index + 1, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * d_He * t_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 1, element_number, T_index + 0, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * d_He * d_T * t_z * + emissivity[row_major_index_5d( + z_index + 0, He_index + 1, element_number, T_index + 1, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += t_nH * t_He * t_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 0, element_number, T_index + 0, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += t_nH * t_He * d_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 0, element_number, T_index + 1, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += t_nH * d_He * t_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 1, element_number, T_index + 0, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * t_He * t_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 0, element_number, T_index + 0, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += t_nH * d_He * d_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 1, element_number, T_index + 1, + nH_index + 0, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * t_He * d_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 0, element_number, T_index + 1, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * d_He * t_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 1, element_number, T_index + 0, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + result += d_nH * d_He * d_T * d_z * + emissivity[row_major_index_5d( + z_index + 1, He_index + 1, element_number, T_index + 1, + nH_index + 1, xray_emission_N_redshift, xray_emission_N_helium, + xray_emission_N_element, xray_emission_N_temperature, + xray_emission_N_density)]; + + return result; +} + +/** + * @brief Find index of the Xray table to interpolate between and compute + * emissivities + * + * @param xrays Xray data structure + * @param He_fraction Helium fraction + * @param log_nH_cgs physical number density in CGS + * @param log_T temperature + * @param redshift redshift + * @param solar_ratio abundance ratio relative to solar + * @param band xray band to use + * + * @return The X-ray emmisivity in the corresponding band in CGS units. + */ +INLINE static float do_xray_interpolation( + const struct xray_properties *xrays, const float log10_He_fraction, + const float log_nH_cgs, const float log_T, const float redshift, + const float solar_ratio[colibre_cooling_N_elementtypes], + const enum xray_band_types band) { + + /* Get indices in the interpolation table along the He, nH, T + * and z dimensions */ + int He_index, log_nH_cgs_index, log_T_index, z_index; + float d_He, d_log_nH_cgs, d_log_T, d_z; + get_index_1d_irregular(xrays->He_bins, xray_emission_N_helium, + log10_He_fraction, &He_index, &d_He); + + get_index_1d(xrays->Densities, xray_emission_N_density, log_nH_cgs, + &log_nH_cgs_index, &d_log_nH_cgs); + + get_index_1d(xrays->Temperatures, xray_emission_N_temperature, log_T, + &log_T_index, &d_log_T); + + get_index_1d(xrays->Redshifts, xray_emission_N_redshift, redshift, &z_index, + &d_z); + + /* Select the table corresponding to this band */ + float *table; + switch (band) { + case xray_band_types_erosita_low_intrinsic_photons: + table = xrays->emissivity_erosita_low_intrinsic_photons; + break; + case xray_band_types_erosita_high_intrinsic_photons: + table = xrays->emissivity_erosita_high_intrinsic_photons; + break; + case xray_band_types_ROSAT_intrinsic_photons: + table = xrays->emissivity_ROSAT_intrinsic_photons; + break; + case xray_band_types_erosita_low_intrinsic_energies: + table = xrays->emissivity_erosita_low_intrinsic_energies; + break; + case xray_band_types_erosita_high_intrinsic_energies: + table = xrays->emissivity_erosita_high_intrinsic_energies; + break; + case xray_band_types_ROSAT_intrinsic_energies: + table = xrays->emissivity_ROSAT_intrinsic_energies; + break; + default: + error("Band doesn't exist"); + } + + /* The total flux is computed in two steps: + * - First, we compute the contribution excluding all elements. + * - Next, we loop over all the metals we trace and compute the flux + * in a case one metal is added + * - Finally, for each metal, we add a portion of the difference + * between the (one metal) and (no metals) case based on the + * the abundance of each metal + * + * The interpolation table structure is as follows: + * First we have the individual element contributions in the same order + * as the COLIBRE cooling model. As we only include metals, the first + * entry is Carbon, second Nitrogen, etc. + * The contribution of no metals is the last entry. + * + * The table size is hence: + * colibre_cooling_N_elementtypes - 3 (H + He + OA) + 1 (no metals) + * which is equal to xray_emission_N_element + */ + + /* Perform the interpolation of the no metal case + * Note: That is stored as the last entry of the table */ + const float log10_x_ray_no_metals_cgs = interpolate_xray( + table, xray_emission_N_element - 1, log_nH_cgs_index, He_index, + log_T_index, z_index, d_log_nH_cgs, d_He, d_log_T, d_z); + + const float x_ray_no_metals_cgs = exp10f(log10_x_ray_no_metals_cgs); + float x_ray_cgs = x_ray_no_metals_cgs; + + /* Loop over the *individual metals* used in the COLIBRE cooling */ + for (int elem = element_C; elem <= element_Fe; elem++) { + + /* Note: we deduct 2 since the interpolation tables do not include H and He + * and start straight with the metals */ + const float log10_x_ray_elem_cgs = interpolate_xray( + table, elem - 2, log_nH_cgs_index, He_index, log_T_index, z_index, + d_log_nH_cgs, d_He, d_log_T, d_z); + + const float x_ray_elem_cgs = exp10f(log10_x_ray_elem_cgs); + + /* Add the difference multiplied by the abundance to solar-abundance + * ratio */ + x_ray_cgs += x_ray_elem_cgs * solar_ratio[elem]; + } + + /* Convert from cm^3 to cm^-3 (i.e. multiply by nH^2) */ + x_ray_cgs *= exp10f(2.f * log_nH_cgs); + + return x_ray_cgs; +} + +/** + * @brief Compute the emmisivity of a particle in a given X-ray band. + * + * Particles that are star-forming or have a (rho,T) pair outside + * the table range return a flux of 0. + * + * @param p The #part. + * @param xp The corresponding #xpart. + * @param e The #engine. + * @param band xray band to use + * + * @return The emissivity in internal units. + */ +INLINE static double extra_io_get_xray_fluxes(const struct part *p, + const struct xpart *xp, + const struct engine *e, + const enum xray_band_types band) { + + /* Get gas particle temperature */ + const float T = cooling_get_temperature( + e->physical_constants, e->hydro_properties, e->internal_units, + e->cosmology, e->cooling_func, p, xp); + const float log10_T = log10f(T); + + /* Get gas particle Hydrogen number density in cgs */ + const float rho_phys = hydro_get_physical_density(p, e->cosmology); + const float XH = + chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const float nH = rho_phys * XH / e->physical_constants->const_proton_mass * + e->cooling_func->number_density_to_cgs; + const float log10_nH_cgs = log10f(nH); + + /* If the particle is not in the table range or star-forming, we return a flux + * of 0 */ + if ((log10_T < e->io_extra_props->xray_data.Temperature_min || + log10_T > e->io_extra_props->xray_data.Temperature_max) || + (log10_nH_cgs < e->io_extra_props->xray_data.Density_min || + log10_nH_cgs > e->io_extra_props->xray_data.Density_max) || + e->cosmology->z > e->io_extra_props->xray_data.Redshift_max || + star_formation_get_SFR(p, xp) > 0.) + return 0.; + + /* Get gas particle element mass fractions */ + const float *const mass_fractions = + chemistry_get_metal_mass_fraction_for_cooling(p); + + /* Convert to abundances. For now, ignore Ca and S that are not tracked */ + float abundances[chemistry_element_count]; + for (int el = 0; el < chemistry_element_count; el++) { + abundances[el] = (mass_fractions[el] / mass_fractions[0]) * + (e->io_extra_props->xray_data.element_mass[0] / + e->io_extra_props->xray_data.element_mass[el]); + } + + /* We now need to convert the array we received from the chemistry + * module (likely EAGLE) into the COLIBRE-cooling format. + * This means adding un-tracked elements and changing their order */ + + /* Finally onvert to abundances relative to solar */ + float abundance_ratio[colibre_cooling_N_elementtypes]; + for (int el = 0; el < colibre_cooling_N_elementtypes; el++) { + + /* Treat all regular elements */ + if (el <= element_Si) { + + abundance_ratio[el] = + abundances[el] / e->io_extra_props->xray_data.Solar_metallicity[el]; + + /* Special case for the two elements not traced in the chemistry */ + } else if (el == element_S || el == element_Ca) { + + /* S and Ca are fixed to have the same abundance ratio as Si */ + abundance_ratio[el] = abundance_ratio[element_Si]; + + /* Final special case: Iron. */ + } else if (el == element_Fe) { + + /* We need to fish it out of the chemistry where it was at a different + * location in the array */ + abundance_ratio[el] = + abundances[chemistry_element_Fe] / + e->io_extra_props->xray_data.Solar_metallicity[chemistry_element_Fe]; + } else { + + /* Any other element is not used in the Xray interpolation */ + abundance_ratio[el] = 0.f; + } + } + + /* Extract the (log of) Helium abundance */ + const float log10_He_fraction = log10f(abundances[chemistry_element_He]); + + /* Compute the X-ray emission in the given band */ + const double xray_em_cgs = do_xray_interpolation( + &e->io_extra_props->xray_data, log10_He_fraction, log10_nH_cgs, log10_T, + e->cosmology->z, abundance_ratio, band); + + /* Convert back to internal units */ + double xray_em; + switch (band) { + case xray_band_types_erosita_low_intrinsic_photons: + case xray_band_types_erosita_high_intrinsic_photons: + case xray_band_types_ROSAT_intrinsic_photons: + xray_em = + xray_em_cgs / + e->io_extra_props->xray_data.xray_photon_emissivity_unit_conversion; + break; + case xray_band_types_erosita_low_intrinsic_energies: + case xray_band_types_erosita_high_intrinsic_energies: + case xray_band_types_ROSAT_intrinsic_energies: + xray_em = + xray_em_cgs / + e->io_extra_props->xray_data.xray_energy_emissivity_unit_conversion; + break; + default: + error("Band doesn't exist"); + } + + /* Now compute the luminosity from the emissivity + * To do so, we multiply by the particle volume + * luminosity = emissivity * (mass / density) + */ + const double xray_lum = xray_em * (hydro_get_mass(p) / rho_phys); + + return xray_lum; +} + +/** + * @brief Initialises properties stored for the extra i/o fields + * + * @param parameter_file The parsed parameter file + * @param us Internal system of units data structure + * @param phys_const #phys_const data structure + * @param cosmo The cosmology model + * @param props #extra_io_properties struct to initialize + */ +INLINE static void extra_io_init(struct swift_params *parameter_file, + const struct unit_system *us, + const struct phys_const *phys_const, + const struct cosmology *cosmo, + struct extra_io_properties *props) { + + parser_get_param_string(parameter_file, "XrayEmissivity:xray_table_path", + props->xray_data.xray_table_path); + + read_xray_header(&props->xray_data, props->xray_data.xray_table_path); + read_xray_table(&props->xray_data, props->xray_data.xray_table_path); + + /* Find the minimum and maximum density and temperature and the maximum + redshift Print this information to the screen*/ + props->xray_data.Density_min = props->xray_data.Densities[0]; + props->xray_data.Density_max = + props->xray_data.Densities[xray_emission_N_density - 1]; + + props->xray_data.Temperature_min = props->xray_data.Temperatures[0]; + props->xray_data.Temperature_max = + props->xray_data.Temperatures[xray_emission_N_temperature - 1]; + + props->xray_data.Redshift_max = + props->xray_data.Redshifts[xray_emission_N_redshift - 1]; + + message( + "X-ray broad band interpolation for particles between densities of " + "nH=%f-%f cm-3" + "temperature of logT=%f-%f K" + "and redshift less than z<%f", + props->xray_data.Density_min, props->xray_data.Density_max, + props->xray_data.Temperature_min, props->xray_data.Temperature_max, + props->xray_data.Redshift_max); + + /* Compute unit conversions only once and use them throughout */ + props->xray_data.xray_photon_emissivity_unit_conversion = + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY_PER_TIME); + props->xray_data.xray_energy_emissivity_unit_conversion = + units_cgs_conversion_factor(us, UNIT_CONV_POWER_DENSITY); +} + +/** + * @brief Free the memory allocated for the extra i/o fields. + * + * @param props #extra_io_properties struct to clean + */ +INLINE static void extra_io_clean(struct extra_io_properties *props) { + + free(props->xray_data.Temperatures); + free(props->xray_data.Densities); + free(props->xray_data.He_bins); + free(props->xray_data.Solar_metallicity); + free(props->xray_data.Log10_solar_metallicity); + free(props->xray_data.Redshifts); + free(props->xray_data.element_mass); + + swift_free("xrays_table_erosita_low_photons", + props->xray_data.emissivity_erosita_low_intrinsic_photons); + swift_free("xrays_table_erosita_high_photons", + props->xray_data.emissivity_erosita_high_intrinsic_photons); + swift_free("xray_table_ROSAT_photons", + props->xray_data.emissivity_ROSAT_intrinsic_photons); + swift_free("xrays_table_erosita_low_energies", + props->xray_data.emissivity_erosita_low_intrinsic_energies); + swift_free("xrays_table_erosita_high_energies", + props->xray_data.emissivity_erosita_high_intrinsic_energies); + swift_free("xray_table_ROSAT_energies", + props->xray_data.emissivity_ROSAT_intrinsic_energies); +} + +/** + * @brief Write a extra i/o struct to the given FILE as a stream of bytes. + * + * @param feedback the struct + * @param stream the file stream + */ +INLINE static void extra_io_struct_dump(const struct extra_io_properties *props, + FILE *stream) { + + struct extra_io_properties props_copy = *props; + + props_copy.xray_data.Temperatures = NULL; + props_copy.xray_data.Densities = NULL; + props_copy.xray_data.He_bins = NULL; + props_copy.xray_data.Solar_metallicity = NULL; + props_copy.xray_data.Log10_solar_metallicity = NULL; + props_copy.xray_data.Redshifts = NULL; + props_copy.xray_data.element_mass = NULL; + + props_copy.xray_data.emissivity_erosita_low_intrinsic_photons = NULL; + props_copy.xray_data.emissivity_erosita_high_intrinsic_photons = NULL; + props_copy.xray_data.emissivity_ROSAT_intrinsic_photons = NULL; + props_copy.xray_data.emissivity_erosita_low_intrinsic_energies = NULL; + props_copy.xray_data.emissivity_erosita_high_intrinsic_energies = NULL; + props_copy.xray_data.emissivity_ROSAT_intrinsic_energies = NULL; + + restart_write_blocks((void *)&props_copy, sizeof(struct extra_io_properties), + 1, stream, "extra_io", "extra i/o properties"); +} + +/** + * @brief Restore a extra_io_properties struct from the given FILE as a + * stream of bytes. + * + * Read the structure from the stream and restore the extra i/o tables by + * re-reading them. + * + * @param feedback the struct + * @param stream the file stream + */ +INLINE static void extra_io_struct_restore(struct extra_io_properties *props, + FILE *stream) { + + restart_read_blocks((void *)props, sizeof(struct extra_io_properties), 1, + stream, NULL, "extra i/o properties"); + + read_xray_header(&props->xray_data, props->xray_data.xray_table_path); + read_xray_table(&props->xray_data, props->xray_data.xray_table_path); +} + +#endif /* SWIFT_EXTRA_EAGLE_H */ diff --git a/src/extra_io/EAGLE/extra_io.h b/src/extra_io/EAGLE/extra_io.h new file mode 100644 index 0000000000000000000000000000000000000000..538e0936068aea9cee49f6646d3209ada15c0482 --- /dev/null +++ b/src/extra_io/EAGLE/extra_io.h @@ -0,0 +1,327 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 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_EXTRA_IO_EAGLE_H +#define SWIFT_EXTRA_IO_EAGLE_H + +#include "extra.h" +#include "io_properties.h" + +INLINE static void convert_part_Xray_photons(const struct engine *e, + const struct part *p, + const struct xpart *xp, + double *ret) { + + ret[0] = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_low_intrinsic_photons); + ret[1] = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_high_intrinsic_photons); + ret[2] = extra_io_get_xray_fluxes(p, xp, e, + xray_band_types_ROSAT_intrinsic_photons); +} + +INLINE static void convert_part_Xray_energies(const struct engine *e, + const struct part *p, + const struct xpart *xp, + double *ret) { + + ret[0] = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_low_intrinsic_energies); + ret[1] = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_high_intrinsic_energies); + ret[2] = extra_io_get_xray_fluxes(p, xp, e, + xray_band_types_ROSAT_intrinsic_energies); +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extra particle array. + * @param list The list of i/o properties to write. + * @param with_cosmology Are we running with cosmology? + * + * @return Returns the number of fields to write. + */ +INLINE static int extra_io_write_particles(const struct part *parts, + const struct xpart *xparts, + struct io_props *list, + const int with_cosmology) { + + list[0] = io_make_output_field_convert_part( + "XrayPhotonLuminosities", DOUBLE, 3, UNIT_CONV_PHOTONS_PER_TIME, 0.f, + parts, xparts, convert_part_Xray_photons, + "Intrinsic X-ray photon luminosities in various bands. This is 0 for " + "star-forming particles."); + + list[1] = io_make_output_field_convert_part( + "XrayLuminosities", DOUBLE, 3, UNIT_CONV_POWER, 0.f, parts, xparts, + convert_part_Xray_energies, + "Intrinsic X-ray luminosities in various bands. This is 0 for " + "star-forming particles."); + + return 2; +} + +/** + * @brief Specifies which star particle fields to write to a dataset + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int extra_io_write_sparticles(const struct spart *sparts, + struct io_props *list, + const int with_cosmology) { + + return 0; +} + +/** + * @brief Specifies which black hole particle fields to write to a dataset + * + * @param bparts The BH particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int extra_io_write_bparticles(const struct bpart *bparts, + struct io_props *list, + const int with_cosmology) { + return 0; +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of extra-io to the file + * @param h_grp The HDF5 group in which to write + * @param h_grp_columns The HDF5 group containing named columns + */ +INLINE static void extra_io_write_flavour(hid_t h_grp, hid_t h_grp_columns) { + + /* Write the extra-io model */ + io_write_attribute_s(h_grp, "Extra-io", "EAGLE"); + + /* Create an array of xray band names */ + static const char xrayband_names[xray_band_types_count / 2][32] = { + "erosita_low", "erosita_high", "ROSAT"}; + + /* Add to the named columns. We do it twice. Once for + * the energies and once for the photons. + * The columns use the same names for both arrays. */ + hsize_t dims[1] = {xray_band_types_count / 2}; + hid_t type = H5Tcopy(H5T_C_S1); + H5Tset_size(type, 32); + hid_t space = H5Screate_simple(1, dims, NULL); + hid_t dset = H5Dcreate(h_grp_columns, "XrayLuminosities", type, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, xrayband_names[0]); + H5Dclose(dset); + dset = H5Dcreate(h_grp_columns, "XrayPhotonLuminosities", type, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, xrayband_names[0]); + H5Dclose(dset); + + H5Tclose(type); + H5Sclose(space); +} +#endif + +/* + Extra lightcone map types +*/ +/* + Healpix map of intrinsic erosita-low photons band +*/ +double lightcone_map_xray_erosita_low_intrinsic_photons_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +/* + Healpix map of intrinsic erosita-low energy band +*/ +double lightcone_map_xray_erosita_low_intrinsic_energy_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +/* + Healpix map of intrinsic erosita-high photons band +*/ +double lightcone_map_xray_erosita_high_intrinsic_photons_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +/* + Healpix map of intrinsic erosita-high energy band +*/ +double lightcone_map_xray_erosita_high_intrinsic_energy_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +/* + Healpix map of intrinsic ROSAT photons band +*/ +double lightcone_map_xray_rosat_intrinsic_photons_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +/* + Healpix map of intrinsic ROSAT energy band +*/ +double lightcone_map_xray_rosat_intrinsic_energy_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); +/* + Healpix map of compton y +*/ +int lightcone_map_compton_y_type_contributes(int ptype); + +double lightcone_map_compton_y_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); +/* + Healpix map of doppler b +*/ +int lightcone_map_doppler_b_type_contributes(int ptype); + +double lightcone_map_doppler_b_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); +/* + Healpix map of dispersion meassure +*/ +int lightcone_map_dispersion_meassure_type_contributes(int ptype); + +double lightcone_map_dispersion_meassure_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +static const struct lightcone_map_type extra_lightcone_map_types[] = { + { + /* .name = */ "XrayErositaLowIntrinsicPhotons", + /* .update_map = */ + lightcone_map_xray_erosita_low_intrinsic_photons_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_PHOTON_FLUX_PER_UNIT_SURFACE, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0e-62, /* to keep in range of a float */ + }, + { + /* .name = */ "XrayErositaLowIntrinsicEnergies", + /* .update_map = */ + lightcone_map_xray_erosita_low_intrinsic_energy_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "XrayErositaHighIntrinsicPhotons", + /* .update_map = */ + lightcone_map_xray_erosita_high_intrinsic_photons_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_PHOTON_FLUX_PER_UNIT_SURFACE, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0e-62, /* to keep in range of a float */ + }, + { + /* .name = */ "XrayErositaHighIntrinsicEnergies", + /* .update_map = */ + lightcone_map_xray_erosita_high_intrinsic_energy_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "XrayROSATIntrinsicPhotons", + /* .update_map = */ + lightcone_map_xray_rosat_intrinsic_photons_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_PHOTON_FLUX_PER_UNIT_SURFACE, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0e-62, /* to keep in range of a float */ + }, + { + /* .name = */ "XrayROSATIntrinsicEnergies", + /* .update_map = */ lightcone_map_xray_rosat_intrinsic_energy_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "ComptonY", + /* .update_map = */ lightcone_map_compton_y_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_NO_UNITS, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "DopplerB", + /* .update_map = */ lightcone_map_doppler_b_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_NO_UNITS, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "DM", + /* .update_map = */ lightcone_map_dispersion_meassure_get_value, + /* .ptype_contributes = */ lightcone_map_gas_only, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_INV_AREA, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 3.40367719e-68, /* convert 1/Mpc^2 to + pc/cm^3 so value fits in a + float */ + }, + { + /* NULL functions indicate end of array */ + /* .name = */ "", + /* .update_map = */ NULL, + /* .ptype_contributes = */ NULL, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_NO_UNITS, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, +}; + +#endif /* SWIFT_EXTRA_IO_EAGLE_H */ diff --git a/src/extra_io/EAGLE/extra_lightcone_map_types.c b/src/extra_io/EAGLE/extra_lightcone_map_types.c new file mode 100644 index 0000000000000000000000000000000000000000..73278919e5c49ce5d0c50e3eae749e54c8eec484 --- /dev/null +++ b/src/extra_io/EAGLE/extra_lightcone_map_types.c @@ -0,0 +1,588 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 includes */ +#include "black_holes.h" +#include "cooling.h" +#include "cosmology.h" +#include "engine.h" +#include "error.h" +#include "gravity.h" +#include "hydro.h" +#include "lightcone/lightcone_map.h" +#include "part.h" +#include "stars.h" + +/* This object's header */ +#include "lightcone/lightcone_map_types.h" + +/* Required for the xrays */ +#include "extra_io.h" +#include "io_properties.h" + +/** + * @brief Determine time since AGN injection at scale factor a_cross + * + * Returns -1 if no there has been no AGN injection. Result is in + * internal time units. + * + * @param xp the #xpart for which we're evaluating the time + * @param c the #cosmology struct + * @param a_cross expansion factor at which the particle crosses the lightcone + */ +INLINE static double get_time_since_AGN_injection(const struct xpart *xp, + const struct cosmology *c, + double a_cross) { + + /* Check for the case where there has been no AGN injection yet */ + const double last_AGN_injection_scale_factor = + xp->tracers_data.last_AGN_injection_scale_factor; + if (last_AGN_injection_scale_factor < 0.0) return -1.0; + + /* Check for heating after lightcone crossing - possible if heated on current + * time step? */ + if (last_AGN_injection_scale_factor > a_cross) return 0.0; + + /* Find time since the last injection in internal units */ + const double last_AGN_injection_time = + cosmology_get_time_since_big_bang(c, last_AGN_injection_scale_factor); + const double time_at_crossing = cosmology_get_time_since_big_bang(c, a_cross); + const double delta_time = time_at_crossing - last_AGN_injection_time; + + return delta_time; +} + +INLINE static int exclude_particle( + const struct lightcone_props *lightcone_props, const struct engine *e, + const struct part *p, const struct xpart *xp, double a_cross) { + + /* Get AGN heating temperature */ + const double AGN_delta_T = e->black_holes_properties->AGN_delta_T_desired; + + /* Check if we need to exclude this particle due to recent AGN heating */ + if (lightcone_props->xray_maps_recent_AGN_injection_exclusion_time > 0) { + const double t = get_time_since_AGN_injection(xp, e->cosmology, a_cross); + if (t >= 0 && + t < lightcone_props->xray_maps_recent_AGN_injection_exclusion_time) { + + /* Check if it is within the exclusion temperature range */ + const double temp_min = + AGN_delta_T * lightcone_props->xray_maps_recent_AGN_min_temp_factor; + const double temp_max = + AGN_delta_T * lightcone_props->xray_maps_recent_AGN_max_temp_factor; + const double part_temp = cooling_get_temperature( + e->physical_constants, e->hydro_properties, e->internal_units, + e->cosmology, e->cooling_func, p, xp); + if (part_temp > temp_min && part_temp < temp_max) return 1; + } + } + + /* Not excluding this particle */ + return 0; +} + +/** + * @brief Make a healpix map of projected erosita-low intrinsic photon flux in + * each pixel + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_xray_erosita_low_intrinsic_photons_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + const double z_cross = (1 / a_cross) - 1; + const double cdist_cross = + sqrt(pow(x_cross[0], 2) + pow(x_cross[1], 2) + pow(x_cross[2], 2)); + + const double luminosity = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_low_intrinsic_photons); + + const double flux = + luminosity / (4 * M_PI * pow(cdist_cross, 2) * + (1 + z_cross)); // photon luminosity distance + + return flux; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/* erosita-low energy flux */ + +/** + * @brief Make a healpix map of projected erosita-low intrinsic energy flux in + * each pixel + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_xray_erosita_low_intrinsic_energy_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + const double z_cross = (1 / a_cross) - 1; + const double cdist_cross = + sqrt(pow(x_cross[0], 2) + pow(x_cross[1], 2) + pow(x_cross[2], 2)); + + const double luminosity = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_low_intrinsic_energies); + + const double flux = + luminosity / (4 * M_PI * pow(cdist_cross, 2) * + pow((1 + z_cross), 2)); // energy luminosity distance + + return flux; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/* erosita_high photon flux */ + +/** + * @brief Make a healpix map of projected erosita-high intrinsic photon flux in + * each pixel + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_xray_erosita_high_intrinsic_photons_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + const double z_cross = (1 / a_cross) - 1; + const double cdist_cross = + sqrt(pow(x_cross[0], 2) + pow(x_cross[1], 2) + pow(x_cross[2], 2)); + + const double luminosity = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_high_intrinsic_photons); + + const double flux = + luminosity / (4 * M_PI * pow(cdist_cross, 2) * + (1 + z_cross)); // photon luminosity distance + + return flux; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/* erosita-high energy flux */ + +/** + * @brief Make a healpix map of projected erosita-high intrinsic energy flux in + * each pixel + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_xray_erosita_high_intrinsic_energy_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + const double z_cross = (1 / a_cross) - 1; + const double cdist_cross = + sqrt(pow(x_cross[0], 2) + pow(x_cross[1], 2) + pow(x_cross[2], 2)); + + const double luminosity = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_erosita_high_intrinsic_energies); + + const double flux = + luminosity / (4 * M_PI * pow(cdist_cross, 2) * + pow((1 + z_cross), 2)); // energy luminosity distance + + return flux; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/* ROSAT photon flux */ + +/** + * @brief Make a healpix map of projected ROSAT intrinsic photon flux in each + * pixel + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_xray_rosat_intrinsic_photons_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + const double z_cross = (1 / a_cross) - 1; + const double cdist_cross = + sqrt(pow(x_cross[0], 2) + pow(x_cross[1], 2) + pow(x_cross[2], 2)); + + const double luminosity = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_ROSAT_intrinsic_photons); + + const double flux = + luminosity / (4 * M_PI * pow(cdist_cross, 2) * + (1 + z_cross)); // photon luminosity distance + + return flux; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/* ROSAT energy flux */ + +/** + * @brief Make a healpix map of projected ROSAT intrinsic energy flux in each + * pixel + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_xray_rosat_intrinsic_energy_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + const double z_cross = (1 / a_cross) - 1; + const double cdist_cross = + sqrt(pow(x_cross[0], 2) + pow(x_cross[1], 2) + pow(x_cross[2], 2)); + + const double luminosity = extra_io_get_xray_fluxes( + p, xp, e, xray_band_types_ROSAT_intrinsic_energies); + + const double flux = + luminosity / (4 * M_PI * pow(cdist_cross, 2) * + pow((1 + z_cross), 2)); // energy luminosity distance + + return flux; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/** + * @brief Make a healpix map of the compton y parameter + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_compton_y_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = e->s->xparts; + + /* Handle on the physics modules */ + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct unit_system *us = e->internal_units; + const struct phys_const *phys_const = e->physical_constants; + const struct cooling_function_data *cool_func = e->cooling_func; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + double y_compton = cooling_get_ycompton(phys_const, hydro_props, us, + cosmo, cool_func, p, xp); + double x_squared = x_cross[0] * x_cross[0] * a_cross * a_cross; + double y_squared = x_cross[1] * x_cross[1] * a_cross * a_cross; + double z_squared = x_cross[2] * x_cross[2] * a_cross * a_cross; + double angular_diameter_distance_2 = x_squared + y_squared + z_squared; + + /* This angular diameter distance is only correct for flat cosmologies */ +#ifdef SWIFT_DEBUG_CHECKS + if (fabs(e->cosmology->Omega_k) > 0.001) + error("only implemented for flat cosmology"); +#endif + + double pixel_size_2 = lightcone_props->pixel_area_steradians; + double y_for_map = + y_compton / (pixel_size_2 * angular_diameter_distance_2); + + return y_for_map; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + break; + } +} + +/** + * @brief Make a healpix map of the doppler b parameter + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_doppler_b_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = e->s->xparts; + + /* Handle on the physics modules */ + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct unit_system *us = e->internal_units; + const struct phys_const *phys_const = e->physical_constants; + const struct cooling_function_data *cool_func = e->cooling_func; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + double n_e = cooling_get_electron_density(phys_const, hydro_props, us, + cosmo, cool_func, p, xp); + + double rho = hydro_get_physical_density(p, cosmo); + + double m = hydro_get_mass(p); + + const double c = phys_const->const_speed_light_c; + + const double sigma_thompson = phys_const->const_thomson_cross_section; + + double x_squared = x_cross[0] * x_cross[0] * a_cross * a_cross; + double y_squared = x_cross[1] * x_cross[1] * a_cross * a_cross; + double z_squared = x_cross[2] * x_cross[2] * a_cross * a_cross; + double angular_diameter_distance_2 = x_squared + y_squared + z_squared; + double angular_diameter_distance = sqrt(angular_diameter_distance_2); + + /* This angular diameter distance is only correct for flat cosmologies */ +#ifdef SWIFT_DEBUG_CHECKS + if (fabs(e->cosmology->Omega_k) > 0.001) + error("only implemented for flat cosmology"); +#endif + + /* beware: we need comoving radial peculiar velocities, so no a_cross + factor is required (reminder: p->v = a^2 \dot{x})! */ + double radial_velocity = + (p->v[0] * x_cross[0] + p->v[1] * x_cross[1] + p->v[2] * x_cross[2]) / + angular_diameter_distance; + + double pixel_size_2 = lightcone_props->pixel_area_steradians; + + double b_for_map = n_e * m * sigma_thompson * radial_velocity / + (pixel_size_2 * angular_diameter_distance_2 * rho * c); + + return b_for_map; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + break; + } +} + +/** + * @brief Make a healpix map of the dispersion meassure + * + * @param e the #engine structure + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_dispersion_meassure_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = e->s->xparts; + + /* Handle on the physics modules */ + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct unit_system *us = e->internal_units; + const struct phys_const *phys_const = e->physical_constants; + const struct cooling_function_data *cool_func = e->cooling_func; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + + /* Exclude recently AGN heated particles */ + if (exclude_particle(lightcone_props, e, p, xp, a_cross)) return 0.0; + + double n_e = cooling_get_electron_density(phys_const, hydro_props, us, + cosmo, cool_func, p, xp); + + double rho = hydro_get_physical_density(p, cosmo); + + double m = hydro_get_mass(p); + + double x_squared = x_cross[0] * x_cross[0] * a_cross * a_cross; + double y_squared = x_cross[1] * x_cross[1] * a_cross * a_cross; + double z_squared = x_cross[2] * x_cross[2] * a_cross * a_cross; + double angular_diameter_distance_2 = x_squared + y_squared + z_squared; + + /* This angular diameter distance is only correct for flat cosmologies */ +#ifdef SWIFT_DEBUG_CHECKS + if (fabs(e->cosmology->Omega_k) > 0.001) + error("only implemented for flat cosmology"); +#endif + + double pixel_size_2 = lightcone_props->pixel_area_steradians; + /* an additional a_cross = 1/(1+z) is part of the integrand */ + double dm_for_map = n_e * m * a_cross / + (pixel_size_2 * angular_diameter_distance_2 * rho); + + return dm_for_map; + } break; + default: + /* Not gas, nothing to do */ + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + break; + } +} diff --git a/src/feedback.h b/src/feedback.h index 49fe7816e525c4b51a8a6af6f4909f4913ba45a4..a403cd25a062d92ec9e5704b52ee39363f8794d0 100644 --- a/src/feedback.h +++ b/src/feedback.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,26 +20,18 @@ #define SWIFT_FEEDBACK_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct feedback model */ #if defined(FEEDBACK_NONE) #include "./feedback/none/feedback.h" -#include "./feedback/none/feedback_iact.h" -#define feedback_use_newborn_stars 0 #elif defined(FEEDBACK_EAGLE_THERMAL) #include "./feedback/EAGLE_thermal/feedback.h" -#include "./feedback/EAGLE_thermal/feedback_iact.h" -#define feedback_use_newborn_stars 0 #elif defined(FEEDBACK_EAGLE_KINETIC) #include "./feedback/EAGLE_kinetic/feedback.h" -#include "./feedback/EAGLE_kinetic/feedback_iact.h" -#define feedback_use_newborn_stars 0 #define EXTRA_STAR_LOOPS #elif defined(FEEDBACK_GEAR) #include "./feedback/GEAR/feedback.h" -#include "./feedback/GEAR/feedback_iact.h" -#define feedback_use_newborn_stars 1 #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback/EAGLE/imf.h b/src/feedback/EAGLE/imf.h index 52c6e25e4ef3ac4c6d4fefbd5ec7bbd8d1256bdb..564734bbfd9b66bf11eea7062689a9dea2648954 100644 --- a/src/feedback/EAGLE/imf.h +++ b/src/feedback/EAGLE/imf.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/feedback/EAGLE/interpolate.h b/src/feedback/EAGLE/interpolate.h index 115d75a71d28d1071766a73788a387a3d82bbbe2..76482957429e601e3f4be78270733c1d6fa43735 100644 --- a/src/feedback/EAGLE/interpolate.h +++ b/src/feedback/EAGLE/interpolate.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/feedback/EAGLE/yield_tables.h b/src/feedback/EAGLE/yield_tables.h index 036ea45bc2644e4c74af70f32533212ebd6fdd01..800ad608591d5d05c61e0eaf277b00bccf9f34cc 100644 --- a/src/feedback/EAGLE/yield_tables.h +++ b/src/feedback/EAGLE/yield_tables.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -723,8 +723,11 @@ INLINE static void compute_yields(struct feedback_props *feedback_props) { /* Resample yields for each element tracked in EAGLE */ int element_index = 0; - for (enum chemistry_element elem = chemistry_element_H; - elem < chemistry_element_count; elem++) { + for (int elem_nr = chemistry_element_H; elem_nr < chemistry_element_count; + elem_nr++) { + + enum chemistry_element elem = (enum chemistry_element)elem_nr; + /* SNIa */ element_index = get_element_index(chemistry_get_element_name(elem), feedback_props->SNIa_element_names, diff --git a/src/feedback/EAGLE_kinetic/feedback.c b/src/feedback/EAGLE_kinetic/feedback.c index 9f7910bcec54a835e2ce5f42d2e7e5d3f1ca96c2..d97c689d7305e4689f8b031054bd5dde432225e3 100644 --- a/src/feedback/EAGLE_kinetic/feedback.c +++ b/src/feedback/EAGLE_kinetic/feedback.c @@ -197,7 +197,7 @@ INLINE static void compute_SNII_feedback( if (prob_kinetic < 1.) { for (int i = 0; i < ngb_gas_N; i++) { - const double rand_kinetic = random_unit_interval_part_ID_and_ray_idx( + const double rand_kinetic = random_unit_interval_part_ID_and_index( sp->id, i, ti_begin, random_number_stellar_feedback_2); if (rand_kinetic < prob_kinetic) number_of_kin_SN_events++; diff --git a/src/feedback/EAGLE_kinetic/feedback_debug.h b/src/feedback/EAGLE_kinetic/feedback_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..a4c11e00236ebf594e998c0d446681ef825ea1eb --- /dev/null +++ b/src/feedback/EAGLE_kinetic/feedback_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_FEEDBACK_EAGLE_KINETIC_DEBUG_H +#define SWIFT_FEEDBACK_EAGLE_KINETIC_DEBUG_H + +__attribute__((always_inline)) INLINE static void feedback_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_FEEDBACK_EAGLE_KINETIC_DEBUG_H */ diff --git a/src/feedback/EAGLE_kinetic/feedback_iact.h b/src/feedback/EAGLE_kinetic/feedback_iact.h index d0c5b2e4bbde0508e40510001c80ad13aab64415..9efe800fbf421637f1f8b302f4b15ad3352270ed 100644 --- a/src/feedback/EAGLE_kinetic/feedback_iact.h +++ b/src/feedback/EAGLE_kinetic/feedback_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,6 +20,7 @@ #define SWIFT_EAGLE_FEEDBACK_KINETIC_IACT_H /* Local includes */ +#include "feedback.h" #include "random.h" #include "rays.h" #include "timestep_sync_part.h" @@ -93,9 +94,9 @@ runner_iact_nonsym_feedback_density(const float r2, const float dx[3], * and the associated mirror ray in SNII feedback */ /* Two random numbers in [0, 1[ */ - const double rand_theta_SNII = random_unit_interval_part_ID_and_ray_idx( + const double rand_theta_SNII = random_unit_interval_part_ID_and_index( si->id, i, ti_current, random_number_isotropic_SNII_feedback_ray_theta); - const double rand_phi_SNII = random_unit_interval_part_ID_and_ray_idx( + const double rand_phi_SNII = random_unit_interval_part_ID_and_index( si->id, i, ti_current, random_number_isotropic_SNII_feedback_ray_phi); /* Compute arclength for the true particle (SNII kinetic feedback) */ @@ -385,10 +386,10 @@ runner_iact_nonsym_feedback_apply( /* Two random numbers in [0, 1[ * Note: this are the same numbers we drew in the density loop! */ - const double rand_theta = random_unit_interval_part_ID_and_ray_idx( + const double rand_theta = random_unit_interval_part_ID_and_index( si->id, i, ti_current, random_number_isotropic_SNII_feedback_ray_theta); - const double rand_phi = random_unit_interval_part_ID_and_ray_idx( + const double rand_phi = random_unit_interval_part_ID_and_index( si->id, i, ti_current, random_number_isotropic_SNII_feedback_ray_phi); diff --git a/src/feedback/EAGLE_kinetic/feedback_properties.h b/src/feedback/EAGLE_kinetic/feedback_properties.h index 309d6bc92de049228671eb7bb8f0ce7a519de141..210d7aa0f9dfe2a681a0b0523ba2ac6b37165570 100644 --- a/src/feedback/EAGLE_kinetic/feedback_properties.h +++ b/src/feedback/EAGLE_kinetic/feedback_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_EAGLE_FEEDBACK_KINETIC_PROPERTIES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "chemistry.h" diff --git a/src/feedback/EAGLE_thermal/feedback.c b/src/feedback/EAGLE_thermal/feedback.c index 936d93635d97661eab3f5fa7419aa2c1fd9cc344..fe799296d364b8a0ddf1a64723513559e1a15398 100644 --- a/src/feedback/EAGLE_thermal/feedback.c +++ b/src/feedback/EAGLE_thermal/feedback.c @@ -205,7 +205,7 @@ INLINE static void compute_SNII_feedback( delta_u = delta_T * conv_factor; for (int i = 0; i < ngb_gas_N; i++) { - const double rand_thermal = random_unit_interval_part_ID_and_ray_idx( + const double rand_thermal = random_unit_interval_part_ID_and_index( sp->id, i, ti_begin, random_number_stellar_feedback_3); if (rand_thermal < prob) number_of_SN_events++; } diff --git a/src/feedback/EAGLE_thermal/feedback_debug.h b/src/feedback/EAGLE_thermal/feedback_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..04ef64d9f02733dd91181c0694b3ac498f9ed1e5 --- /dev/null +++ b/src/feedback/EAGLE_thermal/feedback_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_FEEDBACK_EAGLE_THERMAL_DEBUG_H +#define SWIFT_FEEDBACK_EAGLE_THERMAL_DEBUG_H + +__attribute__((always_inline)) INLINE static void feedback_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_FEEDBACK_EAGLE_THERMAL_DEBUG_H */ diff --git a/src/feedback/EAGLE_thermal/feedback_iact.h b/src/feedback/EAGLE_thermal/feedback_iact.h index 9172d19ab2fe6356bdda4f86df1963a51c1fbaad..89c91a63984be432776494b35e0794522da1246c 100644 --- a/src/feedback/EAGLE_thermal/feedback_iact.h +++ b/src/feedback/EAGLE_thermal/feedback_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,6 +20,7 @@ #define SWIFT_EAGLE_FEEDBACK_IACT_THERMAL_H /* Local includes */ +#include "feedback.h" #include "random.h" #include "rays.h" #include "timestep_sync_part.h" @@ -98,10 +99,10 @@ runner_iact_nonsym_feedback_density(const float r2, const float dx[3], * to randomly select the direction of the ith ray */ /* Two random numbers in [0, 1[ */ - const double rand_theta_SNII = random_unit_interval_part_ID_and_ray_idx( + const double rand_theta_SNII = random_unit_interval_part_ID_and_index( si->id, i, ti_current, random_number_isotropic_SNII_feedback_ray_theta); - const double rand_phi_SNII = random_unit_interval_part_ID_and_ray_idx( + const double rand_phi_SNII = random_unit_interval_part_ID_and_index( si->id, i, ti_current, random_number_isotropic_SNII_feedback_ray_phi); diff --git a/src/feedback/EAGLE_thermal/feedback_properties.h b/src/feedback/EAGLE_thermal/feedback_properties.h index e6f0245cc1ab17a8143bfa90378cd39d46306b5d..5e1e38b11f322aa858631bfc6c1c138d65c8297b 100644 --- a/src/feedback/EAGLE_thermal/feedback_properties.h +++ b/src/feedback/EAGLE_thermal/feedback_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_EAGLE_FEEDBACK_PROPERTIES_THERMAL_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "chemistry.h" diff --git a/src/feedback/GEAR/feedback.c b/src/feedback/GEAR/feedback.c index 57b2b54d1a6ae14c631a4fef781a606cf8dfdbc8..a97fc832418f0c36fd12574c931696015427e1c4 100644 --- a/src/feedback/GEAR/feedback.c +++ b/src/feedback/GEAR/feedback.c @@ -195,20 +195,19 @@ void feedback_will_do_feedback( /* Pick the correct table. (if only one table, threshold is < 0) */ const float metal = - chemistry_get_star_total_metal_mass_fraction_for_feedback(sp); + chemistry_get_star_total_iron_mass_fraction_for_feedback(sp); const float threshold = feedback_props->metallicity_max_first_stars; const struct stellar_model* model = metal < threshold ? &feedback_props->stellar_model_first_stars : &feedback_props->stellar_model; - /* Compute the stellar evolution */ + /* Compute the stellar evolution including SNe energy */ stellar_evolution_evolve_spart(sp, model, cosmo, us, phys_const, ti_begin, star_age_beg_step_safe, dt_enrichment); - /* Transform the number of SN to the energy */ - sp->feedback_data.energy_ejected = - sp->feedback_data.number_sn * feedback_props->energy_per_supernovae; + /* apply the energy efficiency factor */ + sp->feedback_data.energy_ejected *= feedback_props->supernovae_efficiency; /* Set the particle as doing some feedback */ sp->feedback_data.will_do_feedback = sp->feedback_data.energy_ejected != 0.; diff --git a/src/feedback/GEAR/feedback_debug.h b/src/feedback/GEAR/feedback_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..3225ede217b9551f70738ab768770360f848b598 --- /dev/null +++ b/src/feedback/GEAR/feedback_debug.h @@ -0,0 +1,36 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_FEEDBACK_GEAR_DEBUG_H +#define SWIFT_FEEDBACK_GEAR_DEBUG_H + +__attribute__((always_inline)) INLINE static void feedback_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] feedback_xpart_data:", p->id); + warning( + "[PID%lld] delta_mass = %.3e, delta_u = %.3e, delta_p = [%.3e, %.3e, " + "%.3e]", + p->id, xp->feedback_data.delta_mass, xp->feedback_data.delta_u, + xp->feedback_data.delta_p[0], xp->feedback_data.delta_p[1], + xp->feedback_data.delta_p[2]); + } +} + +#endif /* SWIFT_FEEDBACK_GEAR_DEBUG_H */ diff --git a/src/feedback/GEAR/feedback_iact.h b/src/feedback/GEAR/feedback_iact.h index 823c98889b7beb571c5e564624be69e774ed53f0..80c205d3f56c58efdf9a2e9d81400bf85e794c21 100644 --- a/src/feedback/GEAR/feedback_iact.h +++ b/src/feedback/GEAR/feedback_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,6 +20,7 @@ #define SWIFT_GEAR_FEEDBACK_IACT_H /* Local includes */ +#include "feedback.h" #include "hydro.h" #include "random.h" #include "timestep_sync_part.h" diff --git a/src/feedback/GEAR/feedback_properties.h b/src/feedback/GEAR/feedback_properties.h index 812693933b521fb28eade94065073a9b5290bf52..10d18896a9e58ca72d7c89236e0c593b0bf2ec22 100644 --- a/src/feedback/GEAR/feedback_properties.h +++ b/src/feedback/GEAR/feedback_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -28,8 +28,9 @@ * @brief Properties of the GEAR feedback model. */ struct feedback_props { - /*! Energy per supernovae */ - float energy_per_supernovae; + + /*! Supernovae energy effectively deposited */ + float supernovae_efficiency; /*! The stellar model */ struct stellar_model stellar_model; @@ -39,6 +40,9 @@ struct feedback_props { /* Metallicity limits for the first stars */ float metallicity_max_first_stars; + + /* Metallicity [Fe/H] transition for the first stars */ + float imf_transition_metallicity; }; /** @@ -69,8 +73,8 @@ __attribute__((always_inline)) INLINE static void feedback_props_print( } /* Print the feedback properties */ - message("Energy per supernovae = %.2g", - feedback_props->energy_per_supernovae); + message("Supernovae efficiency = %.2g", + feedback_props->supernovae_efficiency); message("Yields table = %s", feedback_props->stellar_model.yields_table); /* Print the stellar model */ @@ -81,6 +85,8 @@ __attribute__((always_inline)) INLINE static void feedback_props_print( message("Yields table first stars = %s", feedback_props->stellar_model_first_stars.yields_table); stellar_model_print(&feedback_props->stellar_model_first_stars); + message("Metallicity max for the first stars (in abundance) = %g", + feedback_props->imf_transition_metallicity); message("Metallicity max for the first stars (in mass fraction) = %g", feedback_props->metallicity_max_first_stars); } @@ -101,11 +107,10 @@ __attribute__((always_inline)) INLINE static void feedback_props_init( const struct unit_system* us, struct swift_params* params, const struct hydro_props* hydro_props, const struct cosmology* cosmo) { - /* Supernovae energy */ - double e_feedback = - parser_get_param_double(params, "GEARFeedback:supernovae_energy_erg"); - e_feedback /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); - fp->energy_per_supernovae = e_feedback; + /* Supernovae energy efficiency */ + double e_efficiency = + parser_get_param_double(params, "GEARFeedback:supernovae_efficiency"); + fp->supernovae_efficiency = e_efficiency; /* filename of the chemistry tables. */ parser_get_param_string(params, "GEARFeedback:yields_table", @@ -115,10 +120,25 @@ __attribute__((always_inline)) INLINE static void feedback_props_init( stellar_evolution_props_init(&fp->stellar_model, phys_const, us, params, cosmo); - /* Now the same for first stars. */ - fp->metallicity_max_first_stars = parser_get_opt_param_float( - params, "GEARFeedback:metallicity_max_first_stars", -1); + /* Read the metallicity threashold */ + fp->imf_transition_metallicity = parser_get_opt_param_float( + params, "GEARFeedback:imf_transition_metallicity", 0); + + /* Read and get the solar abundances */ + struct chemistry_global_data data; + bzero(&data, sizeof(struct chemistry_global_data)); + chemistry_read_solar_abundances(params, &data); + + const int iFe = stellar_evolution_get_element_index(&fp->stellar_model, "Fe"); + const float XFe = data.solar_abundances[iFe]; + + if (fp->imf_transition_metallicity == 0) + fp->metallicity_max_first_stars = -1; + else + fp->metallicity_max_first_stars = + pow(10, fp->imf_transition_metallicity) * XFe; + /* Now initialize the first stars. */ if (fp->metallicity_max_first_stars == -1) { message("First stars are disabled."); } else { diff --git a/src/feedback/GEAR/feedback_struct.h b/src/feedback/GEAR/feedback_struct.h index 93f0e6b9248348b75cd36621a8dcdaf4d0348a3e..ac24862ebf3328a7e9a6a7e531b4333833047356 100644 --- a/src/feedback/GEAR/feedback_struct.h +++ b/src/feedback/GEAR/feedback_struct.h @@ -48,14 +48,14 @@ struct feedback_spart_data { /*! Inverse of normalisation factor used for the enrichment. */ float enrichment_weight; - /* Assign two different names for clarification */ - union { - /*! Number of supernovae */ - float number_sn; + /*! Number of Ia supernovae */ + float number_snia; - /*! Energy injected in the surrounding particles */ - float energy_ejected; - }; + /*! Number of II supernovae */ + float number_snii; + + /*! Energy injected in the surrounding particles */ + float energy_ejected; /*! Total mass ejected by the supernovae */ float mass_ejected; diff --git a/src/feedback/GEAR/stellar_evolution.c b/src/feedback/GEAR/stellar_evolution.c index 937572019623b09d5035bbfdcef4ef95f935b144..8d945f67f8bc272828dc6e44715f91b00bcdc961 100644 --- a/src/feedback/GEAR/stellar_evolution.c +++ b/src/feedback/GEAR/stellar_evolution.c @@ -126,7 +126,8 @@ void stellar_evolution_compute_continuous_feedback_properties( if (negative_mass) { message("Negative mass, skipping current star: %lli", sp->id); /* Reset everything */ - sp->feedback_data.number_sn = 0; + sp->feedback_data.number_snia = 0; + sp->feedback_data.number_snii = 0; sp->feedback_data.mass_ejected = 0; return; } @@ -174,10 +175,6 @@ void stellar_evolution_compute_continuous_feedback_properties( * @param sp The particle to act upon * @param sm The #stellar_model structure. * @param phys_const The physical constants in the internal unit system. - * @param log_m_beg_step Mass of a star ending its life at the begining of the - * step (log10(solMass)) - * @param log_m_end_step Mass of a star ending its life at the end of the step - * (log10(solMass)) * @param m_beg_step Mass of a star ending its life at the begining of the step * (solMass) * @param m_end_step Mass of a star ending its life at the end of the step @@ -189,9 +186,9 @@ void stellar_evolution_compute_continuous_feedback_properties( */ void stellar_evolution_compute_discrete_feedback_properties( struct spart* restrict sp, const struct stellar_model* sm, - const struct phys_const* phys_const, const float log_m_beg_step, - const float log_m_end_step, const float m_beg_step, const float m_end_step, - const float m_init, const int number_snia, const int number_snii) { + const struct phys_const* phys_const, const float m_beg_step, + const float m_end_step, const float m_init, const int number_snia, + const int number_snii) { /* Limit the mass within the imf limits */ const float m_beg_lim = min(m_beg_step, sm->imf.mass_max); @@ -222,7 +219,8 @@ void stellar_evolution_compute_discrete_feedback_properties( if (negative_mass) { message("Negative mass, skipping current star: %lli", sp->id); /* Reset everything */ - sp->feedback_data.number_sn = 0; + sp->feedback_data.number_snia = 0; + sp->feedback_data.number_snii = 0; sp->feedback_data.mass_ejected = 0; return; } @@ -244,6 +242,7 @@ void stellar_evolution_compute_discrete_feedback_properties( /* Set the yields */ for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + /* Compute the mass fraction of metals */ sp->feedback_data.metal_mass_ejected[i] = /* Supernovae II yields */ @@ -268,6 +267,8 @@ void stellar_evolution_compute_discrete_feedback_properties( * * This function compute the SN rate and yields before sending * this information to a different MPI rank. + * It also compute the supernovae energy to be released by the + * star. * * Here I am using Myr-solar mass units internally in order to * avoid numerical errors. @@ -306,9 +307,16 @@ void stellar_evolution_evolve_spart( const float log_m_end_step = lifetime_get_log_mass_from_lifetime( &sm->lifetime, log10(star_age_beg_step_myr + dt_myr), metallicity); - const float m_beg_step = - star_age_beg_step == 0. ? FLT_MAX : exp10(log_m_beg_step); - const float m_end_step = exp10(log_m_end_step); + float m_beg_step = star_age_beg_step == 0. ? FLT_MAX : exp10(log_m_beg_step); + float m_end_step = exp10(log_m_end_step); + + /* Limit the mass interval to the IMF boundaries */ + m_end_step = max(m_end_step, sm->imf.mass_min); + m_beg_step = min(m_beg_step, sm->imf.mass_max); + + /* Here we are outside the IMF, i.e., both masses are too large or too small + */ + if (m_end_step >= m_beg_step) return; /* Check if the star can produce a supernovae */ const int can_produce_snia = @@ -355,22 +363,46 @@ void stellar_evolution_evolve_spart( if (number_snia == 0 && number_snii == 0) return; /* Save the number of supernovae */ - sp->feedback_data.number_sn = number_snia + number_snii; + sp->feedback_data.number_snia = number_snia; + sp->feedback_data.number_snii = number_snii; /* Compute the yields */ stellar_evolution_compute_discrete_feedback_properties( - sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step, - m_end_step, m_init, number_snia, number_snii); + sp, sm, phys_const, m_beg_step, m_end_step, m_init, number_snia, + number_snii); } else { /* Save the number of supernovae */ - sp->feedback_data.number_sn = number_snia_f + number_snii_f; + sp->feedback_data.number_snia = number_snia_f; + sp->feedback_data.number_snii = number_snii_f; /* Compute the yields */ stellar_evolution_compute_continuous_feedback_properties( sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step, m_end_step, m_init, number_snia_f, number_snii_f); } + + /* Compute the supernovae energy associated to the stellar particle */ + + /* Compute the average mass (in solar mass) */ + const float m_avg = 0.5 * (m_beg_step + m_end_step); + const float energy_conversion = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) / 1e51; + + /* initialize */ + sp->feedback_data.energy_ejected = 0; + + /* snia contribution */ + const float snia_energy = sm->snia.energy_per_supernovae; + sp->feedback_data.energy_ejected += + sp->feedback_data.number_snia * snia_energy; + + /* snii contribution */ + const float snii_energy = + supernovae_ii_get_energy_from_progenitor_mass(&sm->snii, m_avg) / + energy_conversion; + sp->feedback_data.energy_ejected += + sp->feedback_data.number_snii * snii_energy; } /** @@ -385,6 +417,23 @@ const char* stellar_evolution_get_element_name(const struct stellar_model* sm, return sm->elements_name + i * GEAR_LABELS_SIZE; } +/** + * @brief Get the index of the element . + * + * @param sm The #stellar_model. + * @param element_name The element name. + */ +int stellar_evolution_get_element_index(const struct stellar_model* sm, + const char* element_name) { + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + if (strcmp(stellar_evolution_get_element_name(sm, i), element_name) == 0) + return i; + } + error("Chemical element %s not found !", element_name); + + return -1; +} + /** * @brief Read the name of all the elements present in the tables. * @@ -471,7 +520,7 @@ void stellar_evolution_props_init(struct stellar_model* sm, supernovae_ia_init(&sm->snia, phys_const, us, params, sm); /* Initialize the supernovae II model */ - supernovae_ii_init(&sm->snii, params, sm); + supernovae_ii_init(&sm->snii, params, sm, us); } /** diff --git a/src/feedback/GEAR/stellar_evolution.h b/src/feedback/GEAR/stellar_evolution.h index 82bbea7dde5d4bf1277e566bedd572d67399b50f..66f6e3375600ee96671c04ca225f2df33c585e23 100644 --- a/src/feedback/GEAR/stellar_evolution.h +++ b/src/feedback/GEAR/stellar_evolution.h @@ -43,8 +43,8 @@ void stellar_evolution_compute_continuous_feedback_properties( void stellar_evolution_compute_discrete_feedback_properties( struct spart* restrict sp, const struct stellar_model* sm, const struct phys_const* phys_const, const float log_m_beg_step, - const float log_m_end_step, const float m_beg_step, const float m_end_step, - const float m_init, const int number_snia, const int number_snii); + const float m_end_step, const float m_init, const int number_snia, + const int number_snii); void stellar_evolution_evolve_spart( struct spart* restrict sp, const struct stellar_model* sm, @@ -54,6 +54,9 @@ void stellar_evolution_evolve_spart( const char* stellar_evolution_get_element_name(const struct stellar_model* sm, int i); +int stellar_evolution_get_element_index(const struct stellar_model* sm, + const char* element_name); + void stellar_evolution_read_elements(struct stellar_model* sm, struct swift_params* params); void stellar_evolution_props_init(struct stellar_model* sm, diff --git a/src/feedback/GEAR/stellar_evolution_struct.h b/src/feedback/GEAR/stellar_evolution_struct.h index e4b75a04ab19a30b4e611b4ebc3e66990417604d..4a629fe8e22c0dcd4e6db2ad106d2e18ad179fa2 100644 --- a/src/feedback/GEAR/stellar_evolution_struct.h +++ b/src/feedback/GEAR/stellar_evolution_struct.h @@ -106,6 +106,9 @@ struct supernovae_ia { /*! Minimal mass of the companion */ float mass_min; } companion[GEAR_NUMBER_TYPE_OF_COMPANION]; + + /*! Energy released per supernovae */ + float energy_per_supernovae; }; /** @@ -155,6 +158,12 @@ struct supernovae_ii { /*! Number of element in the interpolation array */ int interpolation_size; + + /*! Energy released as a function of progenitor mass */ + struct interpolation_1d energy_per_progenitor_mass; + + /*! Energy released per supernovae */ + float energy_per_supernovae; }; /** diff --git a/src/feedback/GEAR/supernovae_ia.c b/src/feedback/GEAR/supernovae_ia.c index 14fc5641e59953c69ad0f73dbc4463ae945fb16c..91a41551d3cedad822cba5163cfc0e6d0525294c 100644 --- a/src/feedback/GEAR/supernovae_ia.c +++ b/src/feedback/GEAR/supernovae_ia.c @@ -332,6 +332,12 @@ void supernovae_ia_init(struct supernovae_ia *snia, /* Compute the normalization coefficients of the companion IMF */ supernovae_ia_init_companion(snia); + + /* Supernovae energy */ + double e_feedback = + parser_get_param_double(params, "GEARFeedback:supernovae_energy_erg"); + e_feedback /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + snia->energy_per_supernovae = e_feedback; } /** diff --git a/src/feedback/GEAR/supernovae_ii.c b/src/feedback/GEAR/supernovae_ii.c index a6e4260e6336d52bdf3d5bad0f1849d126c85a74..14bca58078335b9721746cf332d1ce6c83a643e6 100644 --- a/src/feedback/GEAR/supernovae_ii.c +++ b/src/feedback/GEAR/supernovae_ii.c @@ -192,6 +192,20 @@ float supernovae_ii_get_ejected_mass_fraction_processed_from_raw( return interpolate_1d(&snii->raw.ejected_mass_processed, log_m); }; +/** + * @brief Get the supernova energy for a given progenitor stellar mass. + * + * @param The progenitor mass in units of solar mass. + * + * @return the energy released in ergs/1e51. + */ +float supernovae_ii_get_energy_from_progenitor_mass( + const struct supernovae_ii *snii, float mass) { + + float log_mass = log10(mass); + return interpolate_1d(&snii->energy_per_progenitor_mass, log_mass); +}; + /** * @brief Read an array of SNII yields from the table. * @@ -344,15 +358,76 @@ void supernovae_ii_read_from_tables(struct supernovae_ii *snii, h5_close_group(file_id, group_id); } +/** + * @brief Reads the supernovae II energy parameters from tables. + * + * @param snii The #supernovae_ii model. + * @param params The simulation parameters. + * @param filename The filename of the chemistry table. + */ +void supernovae_ii_read_energy_from_tables(struct supernovae_ii *snii, + struct swift_params *params, + const char *filename) { + + const char *hdf5_dataset_name = "Energy"; + hid_t file_id, group_id; + + /* Open IMF group */ + h5_open_group(filename, "Data/SNIIEnergy", &file_id, &group_id); + + /* Now let's get the number of elements */ + /* Open attribute */ + const hid_t h_dataset = H5Dopen(group_id, hdf5_dataset_name, H5P_DEFAULT); + if (h_dataset < 0) + error("Error while opening attribute '%s'", hdf5_dataset_name); + + /* Get the number of elements */ + hsize_t count = io_get_number_element_in_dataset(h_dataset); + + /* Read the minimal energy */ + float log_mass_min = 0; + io_read_attribute(h_dataset, "min", FLOAT, &log_mass_min); + + /* Read the step size (log step) */ + float step_size = 0; + io_read_attribute(h_dataset, "step", FLOAT, &step_size); + + /* Close the attribute */ + H5Dclose(h_dataset); + + /* Allocate the memory */ + float *data = (float *)malloc(sizeof(float) * count); + + if (data == NULL) + error("Failed to allocate the SNII energy for %s.", hdf5_dataset_name); + + /* Read the dataset */ + io_read_array_dataset(group_id, hdf5_dataset_name, FLOAT, data, count); + + /* Initialize the raw interpolation */ + interpolate_1d_init(&snii->energy_per_progenitor_mass, log10(snii->mass_min), + log10(snii->mass_max), snii->interpolation_size, + log_mass_min, step_size, count, data, + boundary_condition_zero); + + /* Cleanup the memory */ + free(data); + + /* Cleanup everything */ + h5_close_group(file_id, group_id); +} + /** * @brief Initialize the #supernovae_ii structure. * * @param snii The #supernovae_ii model. * @param params The simulation parameters. * @param sm The #stellar_model. + * @param us The unit system. */ void supernovae_ii_init(struct supernovae_ii *snii, struct swift_params *params, - const struct stellar_model *sm) { + const struct stellar_model *sm, + const struct unit_system *us) { /* Read the parameters from the tables */ supernovae_ii_read_from_tables(snii, params, sm->yields_table); @@ -366,6 +441,15 @@ void supernovae_ii_init(struct supernovae_ii *snii, struct swift_params *params, snii->coef_exp = initial_mass_function_get_coefficient( &sm->imf, snii->mass_min, snii->mass_max); snii->coef_exp /= snii->exponent; + + /* Read the energy parameters from the tables */ + supernovae_ii_read_energy_from_tables(snii, params, sm->yields_table); + + /* Supernovae energy */ + double e_feedback = + parser_get_param_double(params, "GEARFeedback:supernovae_energy_erg"); + e_feedback /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + snii->energy_per_supernovae = e_feedback; } /** @@ -404,6 +488,9 @@ void supernovae_ii_restore(struct supernovae_ii *snii, FILE *stream, snii->coef_exp = initial_mass_function_get_coefficient( &sm->imf, snii->mass_min, snii->mass_max); snii->coef_exp /= snii->exponent; + + /* Read the energy parameters from the tables */ + supernovae_ii_read_energy_from_tables(snii, NULL, sm->yields_table); } /** diff --git a/src/feedback/GEAR/supernovae_ii.h b/src/feedback/GEAR/supernovae_ii.h index 8107fff264756c85e8e336b7bbc1b94dae42c4e1..15823685193b97803cd8a0387ad257c6a457107d 100644 --- a/src/feedback/GEAR/supernovae_ii.h +++ b/src/feedback/GEAR/supernovae_ii.h @@ -47,6 +47,8 @@ float supernovae_ii_get_ejected_mass_fraction_processed_from_integral( const struct supernovae_ii *snii, float log_m1, float log_m2); float supernovae_ii_get_ejected_mass_fraction_processed_from_raw( const struct supernovae_ii *snii, float log_m); +float supernovae_ii_get_energy_from_progenitor_mass( + const struct supernovae_ii *snii, float mass); void supernovae_ii_read_yields_array( struct supernovae_ii *snii, struct interpolation_1d *interp_raw, struct interpolation_1d *interp_int, const struct stellar_model *sm, @@ -63,7 +65,8 @@ void supernovae_ii_read_from_tables(struct supernovae_ii *snii, struct swift_params *params, const char *filename); void supernovae_ii_init(struct supernovae_ii *snii, struct swift_params *params, - const struct stellar_model *sm); + const struct stellar_model *sm, + const struct unit_system *us); void supernovae_ii_dump(const struct supernovae_ii *snii, FILE *stream, const struct stellar_model *sm); void supernovae_ii_restore(struct supernovae_ii *snii, FILE *stream, diff --git a/src/feedback/none/feedback_debug.h b/src/feedback/none/feedback_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..e9d5cfb0a37a137fc0cb66ab3d4b76f896d80108 --- /dev/null +++ b/src/feedback/none/feedback_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_FEEDBACK_NONE_DEBUG_H +#define SWIFT_FEEDBACK_NONE_DEBUG_H + +__attribute__((always_inline)) INLINE static void feedback_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_FEEDBACK_NONE_DEBUG_H */ diff --git a/src/feedback/none/feedback_iact.h b/src/feedback/none/feedback_iact.h index 5c14b91c7c92fbf328012ffb014048ce7d819917..e630ff68048040d78f010365cb6e68761c060b4c 100644 --- a/src/feedback/none/feedback_iact.h +++ b/src/feedback/none/feedback_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -19,6 +19,9 @@ #ifndef SWIFT_NONE_FEEDBACK_IACT_H #define SWIFT_NONE_FEEDBACK_IACT_H +/* Local includes */ +#include "feedback.h" + /** * @brief Density interaction between two particles (non-symmetric). * diff --git a/src/feedback/none/feedback_properties.h b/src/feedback/none/feedback_properties.h index 45b275779780f35547dd158f6a2b44aaeab1dfef..e05946590950d0445da49add82628c01fc9ffe82 100644 --- a/src/feedback/none/feedback_properties.h +++ b/src/feedback/none/feedback_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/runner_doiact_sinks.c b/src/feedback_debug.h similarity index 57% rename from src/runner_doiact_sinks.c rename to src/feedback_debug.h index 541d968d451000ce853567983e918836c445d1c0..86806baed9360687c2038a299ec8d3d2c456144f 100644 --- a/src/runner_doiact_sinks.c +++ b/src/feedback_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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,29 +16,23 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_DEBUG_H +#define SWIFT_FEEDBACK_DEBUG_H /* Config parameters. */ -#include "../config.h" +#include <config.h> -/* Local headers. */ -#include "active.h" -#include "cell.h" -#include "engine.h" -#include "feedback.h" -#include "runner.h" -#include "sink.h" -#include "space_getsid.h" -#include "timers.h" +/* Import the debug routines of the right feedback definition */ +#if defined(FEEDBACK_NONE) +#include "./feedback/none/feedback_debug.h" +#elif defined(FEEDBACK_EAGLE_THERMAL) +#include "./feedback/EAGLE_thermal/feedback_debug.h" +#elif defined(FEEDBACK_EAGLE_KINETIC) +#include "./feedback/EAGLE_kinetic/feedback_debug.h" +#elif defined(FEEDBACK_GEAR) +#include "./feedback/GEAR/feedback_debug.h" +#else +#error "Invalid choice of feedback model" +#endif -/* Import the sink compute formation loop functions. */ -#define FUNCTION compute_formation -#define FUNCTION_TASK_LOOP TASK_LOOP_FORMATION -#include "runner_doiact_functions_sinks.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION - -#define FUNCTION accretion -#define FUNCTION_TASK_LOOP TASK_LOOP_ACCRETION -#include "runner_doiact_functions_sinks.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#endif /* SWIFT_FEEDBACK_DEBUG_H */ diff --git a/src/feedback_iact.h b/src/feedback_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..a6144ab2c58402d4feaeb1b3a8e53bd652c8be3f --- /dev/null +++ b/src/feedback_iact.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_FEEDBACK_IACT_H +#define SWIFT_FEEDBACK_IACT_H + +/* Config parameters. */ +#include <config.h> + +/* Select the correct feedback model */ +#if defined(FEEDBACK_NONE) +#include "./feedback/none/feedback_iact.h" +#elif defined(FEEDBACK_EAGLE_THERMAL) +#include "./feedback/EAGLE_thermal/feedback_iact.h" +#elif defined(FEEDBACK_EAGLE_KINETIC) +#include "./feedback/EAGLE_kinetic/feedback_iact.h" +#elif defined(FEEDBACK_GEAR) +#include "./feedback/GEAR/feedback_iact.h" +#else +#error "Invalid choice of feedback model" +#endif + +#endif /* SWIFT_FEEDBACK_IACT_H */ diff --git a/src/feedback_new_stars.h b/src/feedback_new_stars.h new file mode 100644 index 0000000000000000000000000000000000000000..010a32f2f74152e739b4299bcb96f05a4aa84d4b --- /dev/null +++ b/src/feedback_new_stars.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_FEEDBACK_NEW_STARS_H +#define SWIFT_FEEDBACK_NEW_STARS_H + +/* Config parameters. */ +#include <config.h> + +/* Select the correct feedback model and switches on/off the + * feedback from stars born in that very step. */ +#if defined(FEEDBACK_NONE) +#define feedback_use_newborn_stars 0 +#elif defined(FEEDBACK_EAGLE_THERMAL) +#define feedback_use_newborn_stars 0 +#elif defined(FEEDBACK_EAGLE_KINETIC) +#define feedback_use_newborn_stars 0 +#elif defined(FEEDBACK_GEAR) +#define feedback_use_newborn_stars 1 +#else +#error "Invalid choice of feedback model" +#endif + +#endif diff --git a/src/feedback_properties.h b/src/feedback_properties.h index ddf4e5c77e65a66dcf39d42529e7d689f85f274f..1e561611b3982b559f908835c48e8381797fda9b 100644 --- a/src/feedback_properties.h +++ b/src/feedback_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_FEEDBACK_PROPERTIES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct feedback model */ #if defined(FEEDBACK_NONE) diff --git a/src/feedback_struct.h b/src/feedback_struct.h index cf796bef72ddb2f0501ee39fc598fb0a462f9cc6..c0568e93b8e6365a36b2252eabf081e2b1d25978 100644 --- a/src/feedback_struct.h +++ b/src/feedback_struct.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right feedback definition */ #if defined(FEEDBACK_NONE) diff --git a/src/fof.c b/src/fof.c index 0cec2e3d3acfb1111ad6b6793855b9a8572319a9..5bae8294b3710cbb11959a8183d9d4976ec9d41e 100644 --- a/src/fof.c +++ b/src/fof.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef WITH_FOF @@ -45,6 +45,7 @@ #include "proxy.h" #include "threadpool.h" #include "tools.h" +#include "tracers.h" #define fof_props_default_group_id 2147483647 #define fof_props_default_group_id_offset 1 @@ -905,6 +906,7 @@ void fof_search_pair_cells(const struct fof_props *props, const double dim[3], error("Overlapping cells"); if (offset_i > offset_j && (offset_i < offset_j + count_j)) error("Overlapping cells"); + if (ci->nodeID != cj->nodeID) error("Searching foreign cells!"); #endif /* Account for boundary conditions.*/ @@ -926,7 +928,7 @@ void fof_search_pair_cells(const struct fof_props *props, const double dim[3], /* Loop over particles and find which particles belong in the same group. */ for (size_t i = 0; i < count_i; i++) { - struct gpart *pi = &gparts_i[i]; + struct gpart *restrict pi = &gparts_i[i]; /* Ignore inhibited particles */ if (pi->time_bin >= time_bin_inhibited) continue; @@ -948,7 +950,7 @@ void fof_search_pair_cells(const struct fof_props *props, const double dim[3], for (size_t j = 0; j < count_j; j++) { - struct gpart *pj = &gparts_j[j]; + struct gpart *restrict pj = &gparts_j[j]; /* Ignore inhibited particles */ if (pj->time_bin >= time_bin_inhibited) continue; @@ -1225,6 +1227,7 @@ void rec_fof_search_pair_foreign( #ifdef SWIFT_DEBUG_CHECKS if (ci == cj) error("Pair FOF called on same cell!!!"); + if (ci->nodeID == cj->nodeID) error("Fully local pair!"); #endif /* Find the shortest distance between cells, remembering to account for @@ -2213,7 +2216,10 @@ void fof_seed_black_holes(const struct fof_props *props, #endif /* Copy over all the gas properties that we want */ - black_holes_create_from_gas(bp, bh_props, constants, cosmo, p, xp); + black_holes_create_from_gas(bp, bh_props, constants, cosmo, p, xp, + s->e->ti_current); + tracers_first_init_bpart(bp, s->e->internal_units, + s->e->physical_constants, cosmo); /* Move to the next BH slot */ k++; @@ -2368,9 +2374,9 @@ void fof_search_foreign_cells(struct fof_props *props, const struct space *s) { #ifdef WITH_MPI struct engine *e = s->e; - int verbose = e->verbose; - size_t *group_index = props->group_index; - size_t *group_size = props->group_size; + const int verbose = e->verbose; + size_t *restrict group_index = props->group_index; + size_t *restrict group_size = props->group_size; const size_t nr_gparts = s->nr_gparts; const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; const double search_r2 = props->l_x2; @@ -2481,7 +2487,8 @@ void fof_search_foreign_cells(struct fof_props *props, const struct space *s) { /* Set the root of outgoing particles. */ /* Allocate array of outgoing cells and populate it */ - struct cell **local_cells = malloc(num_cells_out * sizeof(struct cell *)); + struct cell **local_cells = + (struct cell **)malloc(num_cells_out * sizeof(struct cell *)); int count = 0; for (int i = 0; i < e->nr_proxies; i++) { for (int j = 0; j < e->proxies[i].nr_cells_out; j++) { diff --git a/src/fof.h b/src/fof.h index 2a7a886bf651afed2defbaae1c581469927a3108..bff0c0a3d0f0933866b7d3949ebf5b5f6bdc8b30 100644 --- a/src/fof.h +++ b/src/fof.h @@ -20,7 +20,7 @@ #define SWIFT_FOF_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "align.h" diff --git a/src/fof_catalogue_io.c b/src/fof_catalogue_io.c index 2966b0085a8dff2c7d583fe7db7f45021e3252cb..393792ded0ee2c50c22f455a8de8088246ef73ac 100644 --- a/src/fof_catalogue_io.c +++ b/src/fof_catalogue_io.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2020 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2020 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 @@ -18,10 +18,13 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef HAVE_HDF5 +/* Some standard headers. */ +#include <libgen.h> + /* Local headers */ #include "engine.h" #include "fof.h" @@ -100,6 +103,8 @@ void write_fof_hdf5_header(hid_t h_file, const struct engine* e, swift_type_count); io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, swift_type_count); + io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total, + swift_type_count); double MassTable[swift_type_count] = {0}; io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count); io_write_attribute(h_grp, "InitialMassTable", DOUBLE, @@ -110,7 +115,13 @@ void write_fof_hdf5_header(hid_t h_file, const struct engine* e, swift_type_count); io_write_attribute_i(h_grp, "NumFilesPerSnapshot", e->nr_nodes); io_write_attribute_i(h_grp, "ThisFile", e->nodeID); + io_write_attribute_s(h_grp, "SelectOutput", "Default"); + io_write_attribute_i(h_grp, "Virtual", 0); + const int to_write[swift_type_count] = {0}; + io_write_attribute(h_grp, "CanHaveTypes", INT, to_write, swift_type_count); io_write_attribute_s(h_grp, "OutputType", "FOF"); + + /* FOF-specific counters */ io_write_attribute_ll(h_grp, "NumGroups_Total", num_groups_total); io_write_attribute_ll(h_grp, "NumGroups_ThisFile", num_groups_this_file); @@ -197,7 +208,8 @@ void write_fof_hdf5_array( h_err = H5Pset_chunk(h_prop, rank, chunk_shape); if (h_err < 0) error("Error while setting chunk size (%llu, %llu) for field '%s'.", - chunk_shape[0], chunk_shape[1], props.name); + (unsigned long long)chunk_shape[0], + (unsigned long long)chunk_shape[1], props.name); /* Are we imposing some form of lossy compression filter? */ if (lossy_compression != compression_write_lossless) diff --git a/src/fof_catalogue_io.h b/src/fof_catalogue_io.h index 345c03ae4eca661c60550a6101780d423f2d3045..8162a52a596af37a9f582a28c01a95901ea1deb6 100644 --- a/src/fof_catalogue_io.h +++ b/src/fof_catalogue_io.h @@ -20,7 +20,7 @@ #define SWIFT_FOF_CATALOGUE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef WITH_FOF diff --git a/src/fof_io.h b/src/fof_io.h index 24edefed3f0e04d40b9d6a1d444c8942426e4f41..90b0c13d14950559fc76e72430a69047bbbb3d3b 100644 --- a/src/fof_io.h +++ b/src/fof_io.h @@ -20,7 +20,7 @@ #define SWIFT_FOF_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef WITH_FOF diff --git a/src/fof_struct.h b/src/fof_struct.h index 38022b56db7d273291e2fa783c48adb7887f516f..ea788b5bf60d68d73a2a10eaf01875e3479f107e 100644 --- a/src/fof_struct.h +++ b/src/fof_struct.h @@ -20,7 +20,7 @@ #define SWIFT_FOF_STRUCT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef WITH_FOF diff --git a/src/ghost_stats.c b/src/ghost_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..883579d9f54580f01104fa6994dfa9e641acb5e4 --- /dev/null +++ b/src/ghost_stats.c @@ -0,0 +1,111 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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 "ghost_stats.h" + +#include "cell.h" +#include "space.h" + +#ifdef SWIFT_GHOST_STATS + +/** + * @brief Resets all the individual cell ghost counters to 0. + * + * @param c The #cell to reset. + */ +void cell_reset_ghost_histograms(struct cell *c) { + + ghost_stats_reset_entries(&c->ghost_statistics); + + /* recurse */ + for (int k = 0; k < 8; ++k) + if (c->progeny[k] != NULL) cell_reset_ghost_histograms(c->progeny[k]); +} + +/** + * @brief Write the ghost statistics for the given cell to the given file, + * using the given cell ID. + * + * This function recurses into the progenies. They all get different cell IDs + * by shifting the parent cell ID by 3 bits. Note that this does not + * guarantee unique cell IDs (but it is good enough to distinguish parents + * from their progeny). + * + * @param f File to write to. + * @param c Cell to write. + * @param cellID ID used to distinguish this cell from its progeny and + * immediate neigbours (not guaranteed to be unique!). + */ +void cell_write_ghost_stats(FILE *f, const struct cell *c, + const long long cellID) { + if (c == NULL) return; + + ghost_stats_write_cell_stats(f, &c->ghost_statistics, cellID); + + /* Write children */ + const long long pID = cellID << 3; + for (int i = 0; i < 8; i++) { + cell_write_ghost_stats(f, c->progeny[i], pID + i); + } +} + +/** + * @brief Reset the ghost histograms for all top level cells in the space. + * + * @param s Space. + */ +void space_reset_ghost_histograms(struct space *s) { + for (int i = 0; i < s->nr_cells; ++i) { + cell_reset_ghost_histograms(&s->cells_top[i]); + } +} + +/** + * @brief Write the ghost statistics for all the top level cells in the space + * to a file. + * + * The file name is composed as follows: + * ghost_stats_%04i_%04i.txt + * where the first counter is an argument to this function (intended to be the + * step counter), and the second counter is the rank that does the write. + * + * @param s Space. + * @param j First counter in the output file name. + */ +void space_write_ghost_stats(const struct space *s, int j) { + /* Open file */ + char filename[200]; + sprintf(filename, "ghost_stats_%04i_%04i.txt", j, engine_rank); + FILE *f = fopen(filename, "w"); + if (f == NULL) error("Error opening ghost stats file."); + + /* Write header */ + ghost_stats_write_header(f); + + /* Write all the top level cells (and their children) */ + for (int i = 0; i < s->nr_cells; i++) { + struct cell *c = &s->cells_top[i]; + if (c->nodeID == engine_rank) cell_write_ghost_stats(f, c, i); + } + + /* Cleanup */ + fclose(f); +} + +#endif diff --git a/src/ghost_stats.h b/src/ghost_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..23fbcc288c935292b14dcf6ee4fdcbe60ac366ee --- /dev/null +++ b/src/ghost_stats.h @@ -0,0 +1,500 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_GHOST_STATS_H +#define SWIFT_GHOST_STATS_H + +/* Config parameters. */ +#include "minmax.h" +#include "part.h" + +#include <config.h> +#include <float.h> +#include <stdio.h> + +#ifdef SWIFT_GHOST_STATS + +/***************************** + * ghost_stats functionality * + *****************************/ + +/** + * @brief Entry for a single iteration in the ghost statistics table. + */ +struct ghost_stats_entry { + /*! Number of particles processed during the iteration. */ + int count; + /*! Number of particles in the iteration that had no neighbours. */ + int count_no_ngb; + /*! Minimum initial smoothing length of the particles at the start of the + * iteration. */ + float hmin; + /*! Maximum initial smoothing length of the particles at the start of the + * iteration. */ + float hmax; + /*! Sum of the initial smoothing lengths, useful to compute averages in + * post-processing. */ + double hsum; + /*! Sum of the initial smoothing lengths squared, useful to compute variances + * and standard deviations in post-processing. */ + double hsum2; +}; + +/** + * @brief Ghost statistics stored in a cell. + */ +struct ghost_stats { + /* Hydro ghost statistics. */ + struct ghost_stats_entry hydro[SWIFT_GHOST_STATS + 1]; + /* Stars ghost statistics. */ + struct ghost_stats_entry stars[SWIFT_GHOST_STATS + 1]; + /* Black holes ghost statistics. */ + struct ghost_stats_entry black_holes[SWIFT_GHOST_STATS + 1]; +}; + +/* ghost_stats_entry struct functions */ + +/** + * @brief Reset the given ghost stats bin. + * + * @param bin Ghost stats bin to reset. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_reset_entry( + struct ghost_stats_entry *restrict bin) { + + bin->count = 0; + bin->count_no_ngb = 0; + bin->hmin = FLT_MAX; + bin->hmax = 0.0f; + bin->hsum = 0.; + bin->hsum2 = 0.; +} + +/** + * @brief Write the given ghost stats bin to the given file. + * + * @param f File to write to. + * @param bin Ghost stats bin to write. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_print_entry( + FILE *f, const struct ghost_stats_entry *restrict bin) { + + if (bin->count > 0) { + fprintf(f, "\t%i\t%i\t%g\t%g\t%g\t%g", bin->count, bin->count_no_ngb, + bin->hmin, bin->hmax, bin->hsum, bin->hsum2); + } else { + fprintf(f, "\t0\t0\t0\t0\t0\t0"); + } +} + +/* ghost_stats struct functions */ + +/** + * @brief Reset all the entries in the ghost_stats struct. + * + * @param gstats Ghost stats struct. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_reset_entries( + struct ghost_stats *restrict gstats) { + + for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { + ghost_stats_reset_entry(&gstats->hydro[b]); + } + for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { + ghost_stats_reset_entry(&gstats->stars[b]); + } + for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { + ghost_stats_reset_entry(&gstats->black_holes[b]); + } +} + +/** + * @brief Account for the star particles that are still under consideration at + * the start of a ghost decision loop (so after the neighbour loop but before + * the smoothing length is updated). + * + * @param gstats Ghost stats struct to update. + * @param iteration_number Iteration number in the high-level ghost iteration + * scheme. + * @param scount Number of star particles still under consideration (smoothing + * length has not converged yet or will do so during this iteration). + * @param sparts Star particle array. + * @param sid Indices of active star particles in the array. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_account_for_stars( + struct ghost_stats *restrict gstats, int iteration_number, int scount, + struct spart *restrict sparts, int *sid) { + + /* accumulate in highest bin, so we can spot out-of-bounds values */ + int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); + struct ghost_stats_entry *restrict sbin = &gstats->stars[binidx]; + sbin->count += scount; + for (int i = 0; i < scount; i++) { + const float hi = sparts[sid[i]].h; + sbin->hmin = min(sbin->hmin, hi); + sbin->hmax = max(sbin->hmax, hi); + sbin->hsum += hi; + sbin->hsum2 += hi * hi; + } +} + +/** + * @brief Account for the properties of the a converged star particle. + * + * @param gstats Ghost stats struct to update. + * @param sp Star particle that has a converged smoothing length. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_converged_star( + struct ghost_stats *restrict gstats, struct spart *restrict sp) { + + struct ghost_stats_entry *restrict sbin = &gstats->stars[SWIFT_GHOST_STATS]; + ++sbin->count; + const float hi = sp->h; + sbin->hmin = min(sbin->hmin, hi); + sbin->hmax = max(sbin->hmax, hi); + sbin->hsum += hi; + sbin->hsum2 += hi * hi; +} + +/** + * @brief Register the occurrence of a "no neighbour" event during the current + * star iteration step. + * + * @param gstats Ghost stats struct to update. + * @param iteration_number Number of the current iteration. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_star_iteration(struct ghost_stats *restrict gstats, + int iteration_number) { + + int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); + struct ghost_stats_entry *restrict sbin = &gstats->stars[binidx]; + ++sbin->count_no_ngb; +} + +/** + * @brief Register the occurrence of a converged star particle without + * neighbours. + * + * @param gstats Ghost stats struct to update. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_star_converged(struct ghost_stats *restrict gstats) { + + struct ghost_stats_entry *restrict sbin = &gstats->stars[SWIFT_GHOST_STATS]; + ++sbin->count_no_ngb; +} + +/** + * @brief Account for the black hole particles that are still under + * consideration at the start of a ghost decision loop (so after the neighbour + * loop but before the smoothing length is updated). + * + * @param gstats Ghost stats struct to update. + * @param iteration_number Iteration number in the high-level ghost iteration + * scheme. + * @param bcount Number of black hole particles still under consideration + * (smoothing length has not converged yet or will do so during this iteration). + * @param bparts Black hole particle array. + * @param sid Indices of active black hole particles in the array. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_account_for_black_holes(struct ghost_stats *restrict gstats, + int iteration_number, int bcount, + struct bpart *restrict bparts, int *sid) { + + /* accumulate in highest bin, so we can spot out-of-bounds values */ + int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); + struct ghost_stats_entry *restrict bbin = &gstats->black_holes[binidx]; + bbin->count += bcount; + for (int i = 0; i < bcount; i++) { + const float hi = bparts[sid[i]].h; + bbin->hmin = min(bbin->hmin, hi); + bbin->hmax = max(bbin->hmax, hi); + bbin->hsum += hi; + bbin->hsum2 += hi * hi; + } +} + +/** + * @brief Account for the properties of the a converged black hole particle. + * + * @param gstats Ghost stats struct to update. + * @param bp Black hole particle that has a converged smoothing length. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_converged_black_hole(struct ghost_stats *restrict gstats, + struct bpart *restrict bp) { + + struct ghost_stats_entry *restrict bbin = + &gstats->black_holes[SWIFT_GHOST_STATS]; + ++bbin->count; + const float hi = bp->h; + bbin->hmin = min(bbin->hmin, hi); + bbin->hmax = max(bbin->hmax, hi); + bbin->hsum += hi; + bbin->hsum2 += hi * hi; +} + +/** + * @brief Register the occurrence of a "no neighbour" event during the current + * black hole iteration step. + * + * @param gstats Ghost stats struct to update. + * @param iteration_number Number of the current iteration. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_black_hole_iteration(struct ghost_stats *restrict gstats, + int iteration_number) { + + int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); + struct ghost_stats_entry *restrict bbin = &gstats->black_holes[binidx]; + ++bbin->count_no_ngb; +} + +/** + * @brief Register the occurrence of a converged black hole particle without + * neighbours. + * + * @param gstats Ghost stats struct to update. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_black_hole_converged(struct ghost_stats *restrict gstats) { + + struct ghost_stats_entry *restrict bbin = + &gstats->black_holes[SWIFT_GHOST_STATS]; + ++bbin->count_no_ngb; +} + +/** + * @brief Account for the gas particles that are still under consideration at + * the start of a ghost decision loop (so after the neighbour loop but before + * the smoothing length is updated). + * + * @param gstats Ghost stats struct to update. + * @param iteration_number Iteration number in the high-level ghost iteration + * scheme. + * @param count Number of gas particles still under consideration (smoothing + * length has not converged yet or will do so during this iteration). + * @param parts Gas particle array. + * @param sid Indices of active gas particles in the array. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_account_for_hydro( + struct ghost_stats *restrict gstats, int iteration_number, int count, + struct part *restrict parts, int *pid) { + + /* accumulate in highest bin, so we can spot out-of-bounds values */ + int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); + struct ghost_stats_entry *restrict hbin = &gstats->hydro[binidx]; + hbin->count += count; + for (int i = 0; i < count; i++) { + const float hi = parts[pid[i]].h; + hbin->hmin = min(hbin->hmin, hi); + hbin->hmax = max(hbin->hmax, hi); + hbin->hsum += hi; + hbin->hsum2 += hi * hi; + } +} + +/** + * @brief Account for the properties of the a converged gas particle. + * + * @param gstats Ghost stats struct to update. + * @param p Gas particle that has a converged smoothing length. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_converged_hydro( + struct ghost_stats *restrict gstats, struct part *restrict p) { + + struct ghost_stats_entry *restrict hbin = &gstats->hydro[SWIFT_GHOST_STATS]; + ++hbin->count; + const float hi = p->h; + hbin->hmin = min(hbin->hmin, hi); + hbin->hmax = max(hbin->hmax, hi); + hbin->hsum += hi; + hbin->hsum2 += hi * hi; +} + +/** + * @brief Register the occurrence of a "no neighbour" event during the current + * hydro iteration step. + * + * @param gstats Ghost stats struct to update. + * @param iteration_number Number of the current iteration. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_hydro_iteration(struct ghost_stats *restrict gstats, + int iteration_number) { + + int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); + struct ghost_stats_entry *restrict hbin = &gstats->hydro[binidx]; + ++hbin->count_no_ngb; +} + +/** + * @brief Register the occurrence of a converged gas particle without + * neighbours. + * + * @param gstats Ghost stats struct to update. + */ +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_hydro_converged(struct ghost_stats *restrict gstats) { + + struct ghost_stats_entry *restrict hbin = &gstats->hydro[SWIFT_GHOST_STATS]; + ++hbin->count_no_ngb; +} + +/** + * @brief Write the header of a ghost statistics file. + * + * @param f File to write to. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_write_header( + FILE *f) { + + fprintf(f, "# Ghost statistics\n"); + fprintf(f, "# Values listed in blocks per particle type\n"); + fprintf(f, "# Order of types: hydro, stars, black holes\n"); + fprintf(f, "# Number of blocks per type: %i\n", SWIFT_GHOST_STATS + 1); + fprintf(f, "# Number of values per block: 6\n"); + fprintf(f, "# Last block contains converged values\n"); + fprintf(f, "# Fields per block:\n"); + fprintf(f, "# - count: i4\n"); + fprintf(f, "# - no neighbour count: i4\n"); + fprintf(f, "# - min h: f4\n"); + fprintf(f, "# - max h: f4\n"); + fprintf(f, "# - sum h: f8\n"); + fprintf(f, "# - sum h^2: f8\n"); + fprintf(f, "# First column is cellID\n"); + fprintf(f, "# Cells with no values are omitted\n"); +} + +/** + * @brief Write the ghost statistics for the given cell to the given file. + * + * @param f File to write to. + * @param gstats Ghost statistics to write. + * @param cellID Cell ID to use to identify the cell. + */ +__attribute__((always_inline)) INLINE static void ghost_stats_write_cell_stats( + FILE *f, const struct ghost_stats *restrict gstats, + const long long cellID) { + + if (gstats->hydro[0].count + gstats->stars[0].count > 0) { + fprintf(f, "%lld", cellID); + for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { + ghost_stats_print_entry(f, &gstats->hydro[b]); + } + for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { + ghost_stats_print_entry(f, &gstats->stars[b]); + } + for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { + ghost_stats_print_entry(f, &gstats->black_holes[b]); + } + fprintf(f, "\n"); + } +} + +/****************** + * cell interface * + ******************/ + +struct cell; + +void cell_reset_ghost_histograms(struct cell *c); +void cell_write_ghost_stats(FILE *f, const struct cell *c, + const long long cellID); +/******************* + * space interface * + *******************/ + +struct space; + +void space_reset_ghost_histograms(struct space *s); +void space_write_ghost_stats(const struct space *s, int j); + +#else + +struct ghost_stats {}; + +/* stars */ +__attribute__((always_inline)) INLINE static void ghost_stats_account_for_stars( + struct ghost_stats *restrict gstats, int iteration_number, int scount, + struct spart *restrict sparts, int *sid) {} + +__attribute__((always_inline)) INLINE static void ghost_stats_converged_star( + struct ghost_stats *restrict gstats, struct spart *restrict sp) {} + +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_star_iteration(struct ghost_stats *restrict gstats, + int iteration_number) {} + +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_star_converged(struct ghost_stats *restrict gstats) {} + +/* black holes */ +__attribute__((always_inline)) INLINE static void +ghost_stats_account_for_black_holes(struct ghost_stats *restrict gstats, + int iteration_number, int bcount, + struct bpart *restrict bparts, int *sid) {} + +__attribute__((always_inline)) INLINE static void +ghost_stats_converged_black_hole(struct ghost_stats *restrict gstats, + struct bpart *restrict bp) {} + +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_black_hole_iteration(struct ghost_stats *restrict gstats, + int iteration_number) {} + +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_black_hole_converged(struct ghost_stats *restrict gstats) {} + +/* hydro */ +__attribute__((always_inline)) INLINE static void ghost_stats_account_for_hydro( + struct ghost_stats *restrict gstats, int iteration_number, int count, + struct part *restrict parts, int *pid) {} + +__attribute__((always_inline)) INLINE static void ghost_stats_converged_hydro( + struct ghost_stats *restrict gstats, struct part *restrict p) {} + +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_hydro_iteration(struct ghost_stats *restrict gstats, + int iteration_number) {} + +__attribute__((always_inline)) INLINE static void +ghost_stats_no_ngb_hydro_converged(struct ghost_stats *restrict gstats) {} + +/// cell interface + +struct cell; + +__attribute__((always_inline)) INLINE static void cell_reset_ghost_histograms( + struct cell *c) {} + +/// space interface + +struct space; + +__attribute__((always_inline)) INLINE static void space_reset_ghost_histograms( + struct space *s) {} + +__attribute__((always_inline)) INLINE static void space_write_ghost_stats( + const struct space *s, int j) {} +#endif + +#endif /* SWIFT_GHOST_STATS */ diff --git a/src/gravity.c b/src/gravity.c index 9a52c1a141a051ae331cc5320f0a3ed94ecd5f0a..ae2efbe3f68beda4af906a95d90af8ee25bf8092 100644 --- a/src/gravity.c +++ b/src/gravity.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2017 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/gravity.h b/src/gravity.h index 967a88afead3c6e1093f8d65a8de2762c33856c8..7f10fefe6edf9dd012ec861a9e9683fdc5daa5ae 100644 --- a/src/gravity.h +++ b/src/gravity.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2015 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 @@ -20,7 +20,7 @@ #define SWIFT_GRAVITY_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "const.h" diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h index 06ebc522a977c7ccdcd870fe827f4939a1d34433..a5feefc11f632fb187ba9302a49af57f50aa049e 100644 --- a/src/gravity/Default/gravity.h +++ b/src/gravity/Default/gravity.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2015 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2016 Tom Theuns (tom.theuns@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify diff --git a/src/gravity/Default/gravity_debug.h b/src/gravity/Default/gravity_debug.h index ba79c95b707d4b6b5a0793753573a852a5ee7a70..e17af56e556ee5352fa3c15964c7fa9db6761141 100644 --- a/src/gravity/Default/gravity_debug.h +++ b/src/gravity/Default/gravity_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/gravity/Default/gravity_iact.h b/src/gravity/Default/gravity_iact.h index 2f66efa2fe5d9823d8af7d7e17b29225139a2fc0..18f87a03c1c95381e659c7f16073fa078f79d115 100644 --- a/src/gravity/Default/gravity_iact.h +++ b/src/gravity/Default/gravity_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 diff --git a/src/gravity/Default/gravity_io.h b/src/gravity/Default/gravity_io.h index 9587da6a48e1f767c627b7688dce9b8324db7c76..9cd4f1e051f9e24698949b98292f6af467626edd 100644 --- a/src/gravity/Default/gravity_io.h +++ b/src/gravity/Default/gravity_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -77,6 +77,12 @@ INLINE static void convert_gpart_vel(const struct engine* e, ret[2] *= cosmo->a_inv; } +INLINE static void convert_gpart_potential(const struct engine* e, + const struct gpart* gp, float* ret) { + + ret[0] = gravity_get_comoving_potential(gp); +} + /** * @brief Specifies which g-particle fields to read from a dataset * @@ -114,7 +120,7 @@ INLINE static void darkmatter_write_particles(const struct gpart* gparts, int* num_fields) { /* Say how much we want to write */ - *num_fields = 4; + *num_fields = 5; /* List what we want to write */ list[0] = io_make_output_field_convert_gpart( @@ -123,7 +129,7 @@ INLINE static void darkmatter_write_particles(const struct gpart* gparts, 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 " + "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, @@ -132,6 +138,10 @@ INLINE static void darkmatter_write_particles(const struct gpart* gparts, 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( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, gparts, + convert_gpart_potential, "Gravitational potentials of the particles"); } #endif /* SWIFT_DEFAULT_GRAVITY_IO_H */ diff --git a/src/gravity/MultiSoftening/gravity.h b/src/gravity/MultiSoftening/gravity.h index 8959f4b414fcbbac6988acd45a33b7078ead2a8a..b25cf51bfee13ffd17520cde025a8bafad95fc52 100644 --- a/src/gravity/MultiSoftening/gravity.h +++ b/src/gravity/MultiSoftening/gravity.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2015 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2016 Tom Theuns (tom.theuns@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify diff --git a/src/gravity/MultiSoftening/gravity_csds.h b/src/gravity/MultiSoftening/gravity_csds.h index 953d1d6c05aead130f23f87f49d4e570b7b5f656..f39da70d2a915f10f6b08e4ea4046b0998a382b4 100644 --- a/src/gravity/MultiSoftening/gravity_csds.h +++ b/src/gravity/MultiSoftening/gravity_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/gravity/MultiSoftening/gravity_debug.h b/src/gravity/MultiSoftening/gravity_debug.h index a0fe978bf61643adb490268ba369b1f63e10a542..ea23e7f3bfc0316a5e0c5d8d70b43290767736e7 100644 --- a/src/gravity/MultiSoftening/gravity_debug.h +++ b/src/gravity/MultiSoftening/gravity_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/gravity/MultiSoftening/gravity_iact.h b/src/gravity/MultiSoftening/gravity_iact.h index 36f96de082eae4f18f6ccd1674c10bae06d2ef64..2cc21fd9064b3c8262968413e5e72182f82fdd5a 100644 --- a/src/gravity/MultiSoftening/gravity_iact.h +++ b/src/gravity/MultiSoftening/gravity_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 diff --git a/src/gravity/MultiSoftening/gravity_io.h b/src/gravity/MultiSoftening/gravity_io.h index caf1b8d4fc86504fc489df77aff1c2a7069f9b08..81db9762dbb207b0cfbafc83bbabbbc8dc8dbafe 100644 --- a/src/gravity/MultiSoftening/gravity_io.h +++ b/src/gravity/MultiSoftening/gravity_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -84,6 +84,12 @@ INLINE static void convert_gpart_soft(const struct engine* e, gravity_get_softening(gp, e->gravity_properties); } +INLINE static void convert_gpart_potential(const struct engine* e, + const struct gpart* gp, float* ret) { + + ret[0] = gravity_get_comoving_potential(gp); +} + /** * @brief Specifies which g-particle fields to read from a dataset * @@ -121,7 +127,7 @@ 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 = 6; /* List what we want to write */ list[0] = io_make_output_field_convert_gpart( @@ -130,7 +136,7 @@ INLINE static void darkmatter_write_particles(const struct gpart* gparts, 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 " + "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, @@ -143,6 +149,10 @@ INLINE static void darkmatter_write_particles(const struct gpart* gparts, 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."); + + list[5] = io_make_output_field_convert_gpart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, gparts, + convert_gpart_potential, "Gravitational potentials of the particles"); } #endif /* SWIFT_MULTI_SOFTENING_GRAVITY_IO_H */ diff --git a/src/gravity_cache.h b/src/gravity_cache.h index 2e454844457351a5fd9d86a329d4d9415a5aead4..d8b33de3c56d4656819ca4c94beb017212e840d1 100644 --- a/src/gravity_cache.h +++ b/src/gravity_cache.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_GRAVITY_CACHE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "accumulate.h" diff --git a/src/gravity_csds.h b/src/gravity_csds.h index 708cf8b629aed946d7f54816c9ab76370a820dab..9b99d0f723488ba82b5e69994b9b9c2f51dc535e 100644 --- a/src/gravity_csds.h +++ b/src/gravity_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -20,7 +20,7 @@ #define SWIFT_GRAVITY_CSDS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "./const.h" diff --git a/src/gravity_derivatives.h b/src/gravity_derivatives.h index 9e34242565f04bca5d624219b628608fbda31e69..4129098631eb1c34435937fcf8f1075e4fec750b 100644 --- a/src/gravity_derivatives.h +++ b/src/gravity_derivatives.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -28,7 +28,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" diff --git a/src/gravity_iact.h b/src/gravity_iact.h index 19684d0f061092be604e65d53c847418bbbcffe3..b5025b0df991556d1006014ef44af01f4f3074dd 100644 --- a/src/gravity_iact.h +++ b/src/gravity_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2015 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 @@ -20,7 +20,7 @@ #define SWIFT_GRAVITY_IACT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "const.h" diff --git a/src/gravity_io.h b/src/gravity_io.h index e3fe6e60d69c096bf6b59c6aa1e5d0ddf7071c97..02af251c2150c4088475171bee960a04d7ebf4a1 100644 --- a/src/gravity_io.h +++ b/src/gravity_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_GRAVITY_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "./const.h" diff --git a/src/gravity_properties.c b/src/gravity_properties.c index 9b87bb8b67e5077af4ae59ca030b9a21592db3a5..b02e406d40800302ddd6d9520fba7c1af7c97e07 100644 --- a/src/gravity_properties.c +++ b/src/gravity_properties.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -37,8 +37,9 @@ #define gravity_props_default_a_smooth 1.25f #define gravity_props_default_r_cut_max 4.5f -#define gravity_props_default_r_cut_min 0.1f +#define gravity_props_default_r_cut_min 0.0f #define gravity_props_default_rebuild_frequency 0.01f +#define gravity_props_default_rebuild_active_fraction 1.01f // > 1 means never #define gravity_props_default_distributed_mesh 0 void gravity_props_init(struct gravity_props *p, struct swift_params *params, @@ -47,13 +48,18 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, const int with_external_potential, const int has_baryons, const int has_DM, const int has_neutrinos, const int is_zoom_simulation, - const int periodic, const double dim[3]) { + const int periodic, const double dim[3], + const int cdim[3]) { /* Tree updates */ p->rebuild_frequency = parser_get_opt_param_float(params, "Gravity:rebuild_frequency", gravity_props_default_rebuild_frequency); + p->rebuild_active_fraction = + parser_get_opt_param_float(params, "Gravity:rebuild_active_fraction", + gravity_props_default_rebuild_active_fraction); + if (p->rebuild_frequency < 0.f || p->rebuild_frequency > 1.f) error("Invalid tree rebuild frequency. Must be in [0., 1.]"); @@ -63,6 +69,8 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, p->distributed_mesh = parser_get_opt_param_int(params, "Gravity:distributed_mesh", gravity_props_default_distributed_mesh); + p->mesh_uses_local_patches = + parser_get_opt_param_int(params, "Gravity:mesh_uses_local_patches", 1); p->a_smooth = parser_get_opt_param_float(params, "Gravity:a_smooth", gravity_props_default_a_smooth); p->r_cut_max_ratio = parser_get_opt_param_float( @@ -90,6 +98,13 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, if (2. * p->a_smooth * p->r_cut_max_ratio > p->mesh_size) error("Mesh too small given r_cut_max. Should be at least %d cells wide.", (int)(2. * p->a_smooth * p->r_cut_max_ratio) + 1); + + if (p->mesh_size < max3(cdim[0], cdim[1], cdim[2])) + error( + "Mesh too small given the number of top-level cells. Should be at " + "least %d cells wide.", + max3(cdim[0], cdim[1], cdim[2])); + } else { p->mesh_size = 0; p->distributed_mesh = 0; @@ -109,12 +124,16 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, if (strcmp(buffer, "adaptive") == 0) { p->use_adaptive_tolerance = 1; + p->use_gadget_tolerance = 0; + } else if (strcmp(buffer, "gadget") == 0) { + p->use_adaptive_tolerance = 1; + p->use_gadget_tolerance = 1; } else if (strcmp(buffer, "geometric") == 0) { p->use_adaptive_tolerance = 0; } else { error( "Invalid choice of multipole acceptance criterion: '%s'. Should be " - "'adaptive' or 'geometric'", + "'adaptive', 'gadget', or 'geometric'", buffer); } @@ -280,9 +299,15 @@ void gravity_props_print(const struct gravity_props *p) { message("Self-gravity time integration: eta=%.4f", p->eta); if (p->use_adaptive_tolerance) { - message("Self-gravity opening angle scheme: adaptive"); - message("Self-gravity opening angle: epsilon_fmm=%.6f", - p->adaptive_tolerance); + if (p->use_gadget_tolerance) { + message("Self-gravity opening angle scheme: Gadget"); + message("Self-gravity opening angle: epsilon_fmm=%.6f", + p->adaptive_tolerance); + } else { + message("Self-gravity opening angle scheme: adaptive"); + message("Self-gravity opening angle: epsilon_fmm=%.6f", + p->adaptive_tolerance); + } } else { message("Self-gravity opening angle scheme: fixed"); message("Self-gravity opening angle: theta_cr=%.4f", p->theta_crit); diff --git a/src/gravity_properties.h b/src/gravity_properties.h index a81cb91548adb1a3cac343ac73769b73d820fa86..2a3be6c22497a62d816a34265e8e30951d4a87af 100644 --- a/src/gravity_properties.h +++ b/src/gravity_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_GRAVITY_PROPERTIES /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) #include <hdf5.h> @@ -66,6 +66,10 @@ struct gravity_props { /*! Are we using the adaptive opening angle? (as read from param file) */ int use_adaptive_tolerance; + /*! Are we using the Gadget adaptive opening angle? (as read from param file) + */ + int use_gadget_tolerance; + /*! Accuracy parameter of the advanced MAC */ float adaptive_tolerance; @@ -110,6 +114,9 @@ struct gravity_props { /*! Frequency of tree-rebuild in units of #gpart updates. */ float rebuild_frequency; + /*! Fraction of active #gparts needed to trigger a tree-rebuild */ + float rebuild_active_fraction; + /*! Time integration dimensionless multiplier */ float eta; @@ -121,6 +128,10 @@ struct gravity_props { /*! Whether mesh is distributed between MPI ranks when we use MPI */ int distributed_mesh; + /*! Whether or not to use local patches rather than + * direct atomic writes to the mesh when running without MPI */ + int mesh_uses_local_patches; + /*! Mesh smoothing scale in units of top-level cell size */ float a_smooth; @@ -152,7 +163,8 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, const int with_external_potential, const int has_baryons, const int has_DM, const int has_neutrinos, const int is_zoom_simulation, - const int periodic, const double dim[3]); + const int periodic, const double dim[3], + const int cdim[3]); void gravity_props_update(struct gravity_props *p, const struct cosmology *cosmo); void gravity_props_update_MAC_choice(struct gravity_props *p); diff --git a/src/gravity_softened_derivatives.h b/src/gravity_softened_derivatives.h index 6ef9a0b455a572d8ea6254f9f91941978e7729ac..1645efcde81e316a806ceac5442d49a10c80918c 100644 --- a/src/gravity_softened_derivatives.h +++ b/src/gravity_softened_derivatives.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -28,7 +28,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" diff --git a/src/hdf5_object_to_blob.c b/src/hdf5_object_to_blob.c index 67d7a0608db94ad8badcacdecb77281394f91917..943cb0f982106a6c42e9232f86d2779675402c01 100644 --- a/src/hdf5_object_to_blob.c +++ b/src/hdf5_object_to_blob.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdio.h> diff --git a/src/hdf5_object_to_blob.h b/src/hdf5_object_to_blob.h index 733a623eb2d806b37e6d38332b0c126489441bc8..30e83ae4a23a474e989fea91bbe226e088682bde 100644 --- a/src/hdf5_object_to_blob.h +++ b/src/hdf5_object_to_blob.h @@ -21,7 +21,7 @@ #define SWIFT_HDF5_OBJECT_TO_BLOB_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef HAVE_HDF5 diff --git a/src/hydro.c b/src/hydro.c index 4d2f7a28f676a106baf254b85239207ec9682e96..ae4cfd9742a2426ceaf46fd19984d10a9f3dd74c 100644 --- a/src/hydro.c +++ b/src/hydro.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/hydro.h b/src/hydro.h index 05ec1417f76e4cfc991b26858d43f914048bd712..1f96d1f3031033c85d5915ecb3ca1d0959738d70 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2015 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 @@ -20,7 +20,7 @@ #define SWIFT_HYDRO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "const.h" diff --git a/src/hydro/AnarchyPU/hydro.h b/src/hydro/AnarchyPU/hydro.h index cdf2021104563caf9d3ab8423ba0c051b06494d0..1a457a66b76dd601d5ca4ef3ac16324c22144c29 100644 --- a/src/hydro/AnarchyPU/hydro.h +++ b/src/hydro/AnarchyPU/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -485,6 +485,28 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -755,11 +777,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { /* Here we need to update the artificial viscosity */ @@ -903,13 +926,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -999,6 +1023,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -1007,7 +1032,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/AnarchyPU/hydro_debug.h b/src/hydro/AnarchyPU/hydro_debug.h index f52a63cd31c608b9f1f0e998a56224e9e133f157..759c5855a3474687e2432a77b63db5215b3bedb5 100644 --- a/src/hydro/AnarchyPU/hydro_debug.h +++ b/src/hydro/AnarchyPU/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -28,19 +28,28 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "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" - "p_dh=%.3e, p_bar=%.3e, alpha=%.3e \n" - "time_bin=%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->viscosity.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->density.pressure_bar_dh, p->pressure_bar, p->viscosity.alpha, - p->time_bin); + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e]", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2]); + warning( + "[PID%lld] a=[%.3e,%.3e,%.3e], " + "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e", + p->id, p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, + p->viscosity.v_sig, hydro_get_comoving_pressure(p)); + warning( + "[PID%lld] h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e", + p->id, p->h, p->force.h_dt, (int)p->density.wcount, p->mass, + p->density.rho_dh, p->rho); + warning("[PID%lld] p_dh=%.3e, p_bar=%.3e, alpha=%.3e", p->id, + p->density.pressure_bar_dh, p->pressure_bar, p->viscosity.alpha); + warning("[PID%lld] time_bin=%d", p->id, p->time_bin); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_ANARCHY_PU_HYDRO_DEBUG_H */ diff --git a/src/hydro/AnarchyPU/hydro_iact.h b/src/hydro/AnarchyPU/hydro_iact.h index 0c9171b2e3cb6d6a3f7460468067c547d745ea89..861851d9854a3b33b37e96b02759069678bf730d 100644 --- a/src/hydro/AnarchyPU/hydro_iact.h +++ b/src/hydro/AnarchyPU/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part* of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -32,6 +32,7 @@ #include "adiabatic_index.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -203,8 +204,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -222,7 +221,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -270,8 +270,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -289,7 +287,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); diff --git a/src/hydro/AnarchyPU/hydro_io.h b/src/hydro/AnarchyPU/hydro_io.h index 4506de784f302fb92489b9738bd979abcce84775..3f363780fbe42439b020eb3aa79c448bb76413bb 100644 --- a/src/hydro/AnarchyPU/hydro_io.h +++ b/src/hydro/AnarchyPU/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify diff --git a/src/hydro/AnarchyPU/hydro_parameters.h b/src/hydro/AnarchyPU/hydro_parameters.h index 1f1e1c2de1c02a1c0f1969eb00244dfc50a0ecc3..0fe3b61f78e213bd5d4ca2ba5e08c12c31b34bca 100644 --- a/src/hydro/AnarchyPU/hydro_parameters.h +++ b/src/hydro/AnarchyPU/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_ANARCHY_PU_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/AnarchyPU/hydro_part.h b/src/hydro/AnarchyPU/hydro_part.h index d0cb77452c9d329e7401e3e22e00affc7010051e..7cfa3f1cce1a637bf020e0bcda1c36977e2e2304 100644 --- a/src/hydro/AnarchyPU/hydro_part.h +++ b/src/hydro/AnarchyPU/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -33,8 +33,10 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -78,6 +80,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /** @@ -203,6 +208,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -215,9 +223,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index 362acd93cdc1b94a2aac2846d1d85d98561d6d43..827d43c6d88b178d94c35844c5f970837c78cf59 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2015 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 @@ -454,6 +454,28 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -533,6 +555,44 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.div_v *= h_inv_dim_plus_one * a_inv2 * rho_inv; } +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -581,11 +641,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps; @@ -723,13 +784,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -812,6 +874,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon * @param dt_therm The time-step for this kick (for thermodynamic quantities) * @param dt_grav The time-step for this kick (for gravity forces) + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro forces) * @param dt_kick_corr The time-step for this kick (for correction of the kick) * @param cosmo The cosmological model. @@ -820,7 +883,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/Gadget2/hydro_csds.h b/src/hydro/Gadget2/hydro_csds.h index a777c3668bcab880f0ee6538de6971cb939f5f30..5abad7bba4bf6146fe53283c801db4eb12b9cd75 100644 --- a/src/hydro/Gadget2/hydro_csds.h +++ b/src/hydro/Gadget2/hydro_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/hydro/Gadget2/hydro_debug.h b/src/hydro/Gadget2/hydro_debug.h index ffa21b861825fe0107f8e19aa4769f04edea7a9a..6923d013d83a0d1e2a66542247a4b97ad55d946b 100644 --- a/src/hydro/Gadget2/hydro_debug.h +++ b/src/hydro/Gadget2/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -21,20 +21,29 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "x=[%.3e,%.3e,%.3e], " - "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e],\n " - "h=%.3e, wcount=%.3f, wcount_dh=%.3e, m=%.3e, dh_drho=%.3e, rho=%.3e, " - "P=%.3e, P_over_rho2=%.3e, S=%.3e, dS/dt=%.3e, c=%.3e\n" - "divV=%.3e, rotV=[%.3e,%.3e,%.3e], balsara=%.3e \n " - "v_sig=%e dh/dt=%.3e 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->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e]", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2]); + warning( + "[PID%lld] a=[%.3e,%.3e,%.3e] h=%.3e, wcount=%.3f, wcount_dh=%.3e, " + "m=%.3e, dh_drho=%.3e, rho=%.3e, " + "P=%.3e, P_over_rho2=%.3e, S=%.3e, dS/dt=%.3e, c=%.3e", + p->id, p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, + p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, p->rho, hydro_get_comoving_pressure(p), p->force.P_over_rho2, p->entropy, - p->entropy_dt, p->force.soundspeed, p->density.div_v, p->density.rot_v[0], - p->density.rot_v[1], p->density.rot_v[2], p->force.balsara, - p->force.v_sig, p->force.h_dt, p->time_bin, p->limiter_data.wakeup); + p->entropy_dt, p->force.soundspeed); + warning("[PID%lld] divV=%.3e, rotV=[%.3e,%.3e,%.3e], balsara=%.3e", p->id, + p->density.div_v, p->density.rot_v[0], p->density.rot_v[1], + p->density.rot_v[2], p->force.balsara); + warning("[PID%lld] v_sig=%e dh/dt=%.3e time_bin=%d wakeup=%d", p->id, + p->force.v_sig, p->force.h_dt, p->time_bin, p->limiter_data.wakeup); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_GADGET2_HYDRO_DEBUG_H */ diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index d208dad11877a9d1cd93e2fdf795f7e20c48e40c..615d7c0c19fd5339c768def070ebe5539885e22f 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -35,6 +35,7 @@ #include "cache.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -435,6 +436,47 @@ runner_iact_nonsym_2_vec_density(float *R2, float *Dx, float *Dy, float *Dz, } #endif +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + /** * @brief Force interaction between two particles. * @@ -497,10 +539,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float P_over_rho2_i = pi->force.P_over_rho2; const float P_over_rho2_j = pj->force.P_over_rho2; - /* Compute sound speeds */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + @@ -518,7 +556,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Now construct the full viscosity term */ const float rho_ij = 0.5f * (rhoi + rhoj); @@ -626,10 +664,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float P_over_rho2_i = pi->force.P_over_rho2; const float P_over_rho2_j = pj->force.P_over_rho2; - /* Compute sound speeds */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + @@ -647,7 +681,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Now construct the full viscosity term */ const float rho_ij = 0.5f * (rhoi + rhoj); diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h index 4e5acda49d788e4ce7f0d5ca804c025c216b6ae6..79d86af264dd2172a09451f7795a5f56b1e1066e 100644 --- a/src/hydro/Gadget2/hydro_io.h +++ b/src/hydro/Gadget2/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/hydro/Gadget2/hydro_parameters.h b/src/hydro/Gadget2/hydro_parameters.h index d49cb90cd6589b331a51d354898388581070efe9..1278fede39a599ffc9c35e4fe2a21ad6e229e4c6 100644 --- a/src/hydro/Gadget2/hydro_parameters.h +++ b/src/hydro/Gadget2/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ #define SWIFT_GADGET2_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h index 2d9c0fdbffadc5f374136f853f959425bd480747..a2ad80a1da696a458467288586e59bcd59576ab9 100644 --- a/src/hydro/Gadget2/hydro_part.h +++ b/src/hydro/Gadget2/hydro_part.h @@ -36,9 +36,11 @@ #include "cooling_struct.h" #include "csds.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "pressure_floor_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -76,6 +78,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + #ifdef WITH_CSDS /* Additional data for the particle csds */ struct csds_part_data csds_data; @@ -160,6 +165,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -172,12 +180,18 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional data used by the pressure floor */ struct pressure_floor_part_data pressure_floor_data; /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Gasoline/hydro.h b/src/hydro/Gasoline/hydro.h index 82380ce00a9211387d438581fd801776feadc466..e9cad33611181eeb41aee1ce4d23a67807c23bfb 100644 --- a/src/hydro/Gasoline/hydro.h +++ b/src/hydro/Gasoline/hydro.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -449,6 +449,26 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return 0.5f * (ci + cj) - mu_ij; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -542,9 +562,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->smooth_pressure_gradient[1] *= hydro_gamma_minus_one * h_inv_dim_plus_one; p->smooth_pressure_gradient[2] *= hydro_gamma_minus_one * h_inv_dim_plus_one; - /* Finish calculation of the velocity gradient tensor */ + /* Finish calculation of the velocity gradient tensor, and + * guard against FPEs here. */ const float velocity_gradient_norm = - p->weighted_wcount > 0.f ? 3.f * cosmo->a2_inv / p->weighted_wcount : 0.f; + p->weighted_wcount == 0.f ? 0.f + : 3.f * cosmo->a2_inv / p->weighted_wcount; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -733,11 +755,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { /* Here we need to update the artificial viscosity alpha */ const float d_shock_indicator_dt = dt_alpha == 0.f ? 0.f @@ -855,13 +878,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { /* Predict the internal energy */ @@ -940,6 +964,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -948,7 +973,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { /* Integrate the internal energy forward in time */ diff --git a/src/hydro/Gasoline/hydro_debug.h b/src/hydro/Gasoline/hydro_debug.h index 8fbd6123357702deaabee61c98c56676e14684fa..84009a54910bc78e6935d5381dac2e9b2cf309e4 100644 --- a/src/hydro/Gasoline/hydro_debug.h +++ b/src/hydro/Gasoline/hydro_debug.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -29,22 +29,22 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "x=[%.3e,%.3e,%.3e],\n" - "v=[%.3e,%.3e,%.3e],\nv_full=[%.3e,%.3e,%.3e],\na=[%.3e,%.3e,%.3e],\n" - "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n" - "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e\n" - "alpha=%.3e, time_bin=%d, rho=%.3e, velocity_gradient=\n" - "[%.3e,%.3e,%.3e]\n" - "[%.3e,%.3e,%.3e]\n" - "[%.3e,%.3e,%.3e],\n" - "smooth_pressure_gradient=[%.3e,%.3e,%.3e],\n" - "weighted_wcount=%.3e\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->viscosity.v_sig, hydro_get_comoving_pressure(p), p->h, - p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, - p->viscosity.alpha, p->time_bin, p->rho, + warning("[PID%lld] part:", p->id); + warning("[PID%lld] x=[%.3e,%.3e,%.3e]", p->id, p->x[0], p->x[1], p->x[2]); + warning("[PID%lld] v=[%.3e,%.3e,%.3e]", p->id, p->v[0], p->v[1], p->v[2]); + warning("[PID%lld] a=[%.3e,%.3e,%.3e]", p->id, p->a_hydro[0], p->a_hydro[1], + p->a_hydro[2]); + warning("[PID%lld] u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e", p->id, p->u, + p->u_dt, p->viscosity.v_sig, hydro_get_comoving_pressure(p)); + warning("[PID%lld] h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e", p->id, + p->h, p->force.h_dt, (int)p->density.wcount, p->mass, + p->density.rho_dh); + warning( + "[PID%lld] alpha=%.3e, time_bin=%d, rho=%.3e, velocity_gradient=[" + "[%.3e,%.3e,%.3e]," + "[%.3e,%.3e,%.3e]," + "[%.3e,%.3e,%.3e]]", + p->id, p->viscosity.alpha, p->time_bin, p->rho, p->viscosity.velocity_gradient[0][0], p->viscosity.velocity_gradient[0][1], p->viscosity.velocity_gradient[0][2], @@ -53,9 +53,17 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( p->viscosity.velocity_gradient[1][2], p->viscosity.velocity_gradient[2][0], p->viscosity.velocity_gradient[2][1], - p->viscosity.velocity_gradient[2][2], p->smooth_pressure_gradient[0], - p->smooth_pressure_gradient[1], p->smooth_pressure_gradient[2], - p->weighted_wcount); + p->viscosity.velocity_gradient[2][2]); + warning("[PID%lld] smooth_pressure_gradient=[%.3e,%.3e,%.3e]", p->id, + p->smooth_pressure_gradient[0], p->smooth_pressure_gradient[1], + p->smooth_pressure_gradient[2]); + warning("[PID%lld] weighted_wcount=%.3e", p->id, p->weighted_wcount); + + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_GASOLINE_HYDRO_DEBUG_H */ diff --git a/src/hydro/Gasoline/hydro_iact.h b/src/hydro/Gasoline/hydro_iact.h index 1a3b8f4cbc8bbdd59a36e439405f3b6b7b1d9464..4e97f9445540475fac930be6cf26157c74bdf4b9 100644 --- a/src/hydro/Gasoline/hydro_iact.h +++ b/src/hydro/Gasoline/hydro_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -29,6 +29,7 @@ #include "adiabatic_index.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -196,8 +197,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -215,7 +214,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = 0.5f * (ci + cj) - mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -279,8 +279,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -298,7 +296,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = 0.5f * (ci + cj) - mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); diff --git a/src/hydro/Gasoline/hydro_io.h b/src/hydro/Gasoline/hydro_io.h index 8aff3d6eb419717709328f1416fd0ffbbe4cc14b..b1c69f9af06388dc7dfae8b8f24cc2bcc6ce62de 100644 --- a/src/hydro/Gasoline/hydro_io.h +++ b/src/hydro/Gasoline/hydro_io.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -179,7 +179,8 @@ INLINE static void hydro_write_particles(const struct part* parts, const struct xpart* xparts, struct io_props* list, int* num_fields) { - *num_fields = 12; + *num_fields = 14; + /* List what we want to write */ list[0] = io_make_output_field_convert_part( "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, @@ -239,10 +240,16 @@ INLINE static void hydro_write_particles(const struct part* parts, "Physical shock indicators (D in the paper) created " "from the velocity tensor."); - list[11] = io_make_output_field( + /* Units and cosmology TBD */ + list[12] = io_make_output_field( "DiffusionRates", FLOAT, 1, UNIT_CONV_THERMAL_DIFFUSIVITY, 0.f, parts, diffusion.rate, "Physical diffusion rates calculated from the shear tensor."); + + list[13] = 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"); } /** diff --git a/src/hydro/Gasoline/hydro_parameters.h b/src/hydro/Gasoline/hydro_parameters.h index d9a57d476b0ed163d86cde67afba274517a2c3f6..1e1cd26ae944ef50a7145bcd08d82c2236cff4c4 100644 --- a/src/hydro/Gasoline/hydro_parameters.h +++ b/src/hydro/Gasoline/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_GASOLINE_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/Gasoline/hydro_part.h b/src/hydro/Gasoline/hydro_part.h index 65130c2fb36f509a0e78ab21cbade975ed89dcfc..39ebe556ee8eee33eb156bc39a30a050f1b4911e 100644 --- a/src/hydro/Gasoline/hydro_part.h +++ b/src/hydro/Gasoline/hydro_part.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -31,9 +31,11 @@ #include "cooling_struct.h" #include "csds.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "pressure_floor_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -76,6 +78,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + #ifdef WITH_CSDS /* Additional data for the particle csds */ struct csds_part_data csds_data; @@ -208,6 +213,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -220,12 +228,18 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional data used by the pressure floor */ struct pressure_floor_part_data pressure_floor_data; /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Gizmo/MFM/hydro_debug.h b/src/hydro/Gizmo/MFM/hydro_debug.h index 08e91eb3b0bacce9e7c7591bdf6395f0d17f0d88..0d01a2081f74083db7d3147076d286709b8c3efd 100644 --- a/src/hydro/Gizmo/MFM/hydro_debug.h +++ b/src/hydro/Gizmo/MFM/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -21,8 +21,8 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "x=[%.16e,%.16e,%.16e], " + warning( + "[PID%lld] x=[%.16e,%.16e,%.16e], " "v=[%.3e,%.3e,%.3e], " "a=[%.3e,%.3e,%.3e], " "h=%.3e, " @@ -50,18 +50,18 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "vmax=%.3e}," "density={" "wcount_dh=%.3e, " - "wcount=%.3e}\n", - p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->a_hydro[0], - p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, p->limiter_data.wakeup, - p->rho, p->P, p->gradients.rho[0], p->gradients.rho[1], - p->gradients.rho[2], p->gradients.v[0][0], p->gradients.v[0][1], - p->gradients.v[0][2], p->gradients.v[1][0], p->gradients.v[1][1], - p->gradients.v[1][2], p->gradients.v[2][0], p->gradients.v[2][1], - p->gradients.v[2][2], p->gradients.P[0], p->gradients.P[1], - p->gradients.P[2], p->limiter.rho[0], p->limiter.rho[1], - p->limiter.v[0][0], p->limiter.v[0][1], p->limiter.v[1][0], - p->limiter.v[1][1], p->limiter.v[2][0], p->limiter.v[2][1], - p->limiter.P[0], p->limiter.P[1], p->limiter.maxr, + "wcount=%.3e}", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, + p->limiter_data.wakeup, p->rho, p->P, p->gradients.rho[0], + p->gradients.rho[1], p->gradients.rho[2], p->gradients.v[0][0], + p->gradients.v[0][1], p->gradients.v[0][2], p->gradients.v[1][0], + p->gradients.v[1][1], p->gradients.v[1][2], p->gradients.v[2][0], + p->gradients.v[2][1], p->gradients.v[2][2], p->gradients.P[0], + p->gradients.P[1], p->gradients.P[2], p->limiter.rho[0], + p->limiter.rho[1], p->limiter.v[0][0], p->limiter.v[0][1], + p->limiter.v[1][0], p->limiter.v[1][1], p->limiter.v[2][0], + p->limiter.v[2][1], p->limiter.P[0], p->limiter.P[1], p->limiter.maxr, p->conserved.momentum[0], p->conserved.momentum[1], p->conserved.momentum[2], p->conserved.mass, p->conserved.energy, p->geometry.volume, p->geometry.matrix_E[0][0], diff --git a/src/hydro/Gizmo/MFM/hydro_flux.h b/src/hydro/Gizmo/MFM/hydro_flux.h index 22b8109611ac147c5c523ae78daa9df2fdf61403..68b590e9b68d036072dbaeeceb5069e48fbd2b8c 100644 --- a/src/hydro/Gizmo/MFM/hydro_flux.h +++ b/src/hydro/Gizmo/MFM/hydro_flux.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 @@ -22,11 +22,11 @@ #include "riemann.h" /** - * @brief Reset the fluxes for the given particle. + * @brief Reset the hydrodynamical fluxes for the given particle. * * @param p Particle. */ -__attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes( +__attribute__((always_inline)) INLINE static void hydro_part_reset_hydro_fluxes( struct part* restrict p) { p->flux.momentum[0] = 0.0f; @@ -35,6 +35,16 @@ __attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes( p->flux.energy = 0.0f; } +/** + * @brief Reset the gravity fluxes for the given particle. + * + * Does nothing, since MFM does not have gravity fluxes. + * + * @param p Particle. + */ +__attribute__((always_inline)) INLINE static void +hydro_part_reset_gravity_fluxes(struct part* restrict p) {} + /** * @brief Get the fluxes for the given particle. * @@ -80,19 +90,21 @@ __attribute__((always_inline)) INLINE static void hydro_compute_flux( * @param p Particle. * @param fluxes Fluxes accross the interface. * @param dx Distance between the particles that share the interface. + * @param dt Time step for the flux exchange. */ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left( - struct part* restrict p, const float* fluxes, const float* dx) { + struct part* restrict p, const float* fluxes, const float* dx, + const float dt) { - p->flux.momentum[0] -= fluxes[1]; - p->flux.momentum[1] -= fluxes[2]; - p->flux.momentum[2] -= fluxes[3]; - p->flux.energy -= fluxes[4]; + p->flux.momentum[0] -= fluxes[1] * dt; + p->flux.momentum[1] -= fluxes[2] * dt; + p->flux.momentum[2] -= fluxes[3] * dt; + p->flux.energy -= fluxes[4] * dt; #ifndef GIZMO_TOTAL_ENERGY - p->flux.energy += fluxes[1] * p->v[0]; - p->flux.energy += fluxes[2] * p->v[1]; - p->flux.energy += fluxes[3] * p->v[2]; + p->flux.energy += fluxes[1] * p->v[0] * dt; + p->flux.energy += fluxes[2] * p->v[1] * dt; + p->flux.energy += fluxes[3] * p->v[2] * dt; #endif } @@ -103,20 +115,21 @@ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left( * @param p Particle. * @param fluxes Fluxes accross the interface. * @param dx Distance between the particles that share the interface. + * @param dt Time step for the flux exchange. */ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_right(struct part* restrict p, const float* fluxes, - const float* dx) { + const float* dx, const float dt) { - p->flux.momentum[0] += fluxes[1]; - p->flux.momentum[1] += fluxes[2]; - p->flux.momentum[2] += fluxes[3]; - p->flux.energy += fluxes[4]; + p->flux.momentum[0] += fluxes[1] * dt; + p->flux.momentum[1] += fluxes[2] * dt; + p->flux.momentum[2] += fluxes[3] * dt; + p->flux.energy += fluxes[4] * dt; #ifndef GIZMO_TOTAL_ENERGY - p->flux.energy -= fluxes[1] * p->v[0]; - p->flux.energy -= fluxes[2] * p->v[1]; - p->flux.energy -= fluxes[3] * p->v[2]; + p->flux.energy -= fluxes[1] * p->v[0] * dt; + p->flux.energy -= fluxes[2] * p->v[1] * dt; + p->flux.energy -= fluxes[3] * p->v[2] * dt; #endif } @@ -137,35 +150,46 @@ hydro_gizmo_mfv_density_drift_term(const float mass_flux, const float dt, /** * @brief Add the gravitational contribution to the fluid velocity drift. * - * This method does nothing, since this is Gizmo MFM. + * This just needs to reset the particle velocity, which was incorrectly + * drifted, since this is Gizmo MFM. * + * @param v (drifted) particle velocity. * @param fluid_v Fluid velocity. - * @param v (Undrifted) particle velocity. - * @param v_full (Drifted) particle velocity. + * @param v_full (Undrifted) particle velocity. + * @param a_grav Gravitational acceleration. + * @param dt_kick_grav Time-step to kick the particle gravitationally. */ __attribute__((always_inline)) INLINE static void -hydro_gizmo_mfv_extra_velocity_drift(float* fluid_v, const float* v, - const float* v_full) {} +hydro_gizmo_mfv_extra_velocity_drift(float* v, const float* restrict fluid_v, + const float* restrict v_full, + const float* restrict a_grav, + float dt_kick_grav) { + /* Just reset particle velocity */ + v[0] = v_full[0]; + v[1] = v_full[1]; + v[2] = v_full[2]; +} /** * @brief Get the term required to update the MFV energy due to the change in * gravitational energy. * * @param dt_kick_corr Time step for the potential energy correction. - * @param dt_grav Time step for the (optional) kinetic energy correction. * @param p Particle. * @param momentum Momentum of the particle, explicitly requested so that it is * clear from the code that the momentum needs to be updated after the call to * this function. * @param a_grav Gravitational acceleration. + * @param grav_kick_factor Gravitational kick factor + * (a_grav * dt + a_grav_mesh * dt_mesh) * @return 0, since this is Gizmo MFM. */ __attribute__((always_inline)) INLINE static float hydro_gizmo_mfv_gravity_energy_update_term(const float dt_kick_corr, - const float dt_grav, const struct part* restrict p, const float* momentum, - const float* a_grav) { + const float* a_grav, + const float* grav_kick_factor) { return 0.0f; } diff --git a/src/hydro/Gizmo/MFM/hydro_part.h b/src/hydro/Gizmo/MFM/hydro_part.h index 1f6f3c71473e4fdd3851b4238839a1b1dccdb877..f476b860eaaca8d54386fe3f2502db3b58c67df5 100644 --- a/src/hydro/Gizmo/MFM/hydro_part.h +++ b/src/hydro/Gizmo/MFM/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2014 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) + * Copyright (c) 2014 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) * * 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 @@ -84,32 +84,6 @@ struct part { } limiter; struct { - /* Fluxes. */ - struct { - - /* No mass flux, since it is always zero. */ - - /* Momentum flux. */ - float momentum[3]; - - /* Energy flux. */ - float energy; - - } flux; - - /* Variables used for timestep calculation. */ - struct { - - /* Maximum signal velocity among all the neighbours of the particle. The - * signal velocity encodes information about the relative fluid - * velocities - * AND particle velocities of the neighbour and this particle, as well - * as - * the sound speed of both particles. */ - float vmax; - - } timestepvars; - /* Quantities used during the force loop. */ struct { @@ -120,6 +94,21 @@ struct part { }; }; + /* Fluxes. */ + struct { + /* No mass flux, since it is always zero. */ + + /* Momentum flux. */ + float momentum[3]; + + /* Energy flux. */ + float energy; + + /* Particle time step. Used to compute time-integrated fluxes. */ + float dt; + + } flux; + /* Gradients of the primitive variables. */ struct { @@ -163,6 +152,19 @@ struct part { } geometry; + /* Variables used for timestep calculation. */ + struct { + + /* Maximum signal velocity among all the neighbours of the particle. The + * signal velocity encodes information about the relative fluid + * velocities + * AND particle velocities of the neighbour and this particle, as well + * as + * the sound speed of both particles. */ + float vmax; + + } timestepvars; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -175,6 +177,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + + /*! Additional Radiative Transfer Data */ + struct rt_part_data rt_data; + + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Gizmo/MFM/hydro_velocities.h b/src/hydro/Gizmo/MFM/hydro_velocities.h index e9f04dd45657f91767393f93fbe3461d4e06f02a..c3929cf2f3e4b864397cebed5490c15ba80c5332 100644 --- a/src/hydro/Gizmo/MFM/hydro_velocities.h +++ b/src/hydro/Gizmo/MFM/hydro_velocities.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 diff --git a/src/hydro/Gizmo/MFV/hydro_debug.h b/src/hydro/Gizmo/MFV/hydro_debug.h index 95a773712a487e50831a9f57f99620e46f266057..545951bedce38f7a7efb7f30fad6db1070150a66 100644 --- a/src/hydro/Gizmo/MFV/hydro_debug.h +++ b/src/hydro/Gizmo/MFV/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -21,8 +21,8 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "x=[%.16e,%.16e,%.16e], " + warning( + "[PID%lld] x=[%.16e,%.16e,%.16e], " "v=[%.3e,%.3e,%.3e], " "a=[%.3e,%.3e,%.3e], " "h=%.3e, " @@ -52,19 +52,20 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "vmax=%.3e}," "density={" "wcount_dh=%.3e, " - "wcount=%.3e}\n", - p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->a_hydro[0], - p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, p->limiter_data.wakeup, - p->fluid_v[0], p->fluid_v[1], p->fluid_v[2], p->rho, p->P, - p->gradients.rho[0], p->gradients.rho[1], p->gradients.rho[2], - p->gradients.v[0][0], p->gradients.v[0][1], p->gradients.v[0][2], - p->gradients.v[1][0], p->gradients.v[1][1], p->gradients.v[1][2], - p->gradients.v[2][0], p->gradients.v[2][1], p->gradients.v[2][2], - p->gradients.P[0], p->gradients.P[1], p->gradients.P[2], - p->limiter.rho[0], p->limiter.rho[1], p->limiter.v[0][0], - p->limiter.v[0][1], p->limiter.v[1][0], p->limiter.v[1][1], - p->limiter.v[2][0], p->limiter.v[2][1], p->limiter.P[0], p->limiter.P[1], - p->limiter.maxr, p->conserved.momentum[0], p->conserved.momentum[1], + "wcount=%.3e}", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, + p->limiter_data.wakeup, p->fluid_v[0], p->fluid_v[1], p->fluid_v[2], + p->rho, p->P, p->gradients.rho[0], p->gradients.rho[1], + p->gradients.rho[2], p->gradients.v[0][0], p->gradients.v[0][1], + p->gradients.v[0][2], p->gradients.v[1][0], p->gradients.v[1][1], + p->gradients.v[1][2], p->gradients.v[2][0], p->gradients.v[2][1], + p->gradients.v[2][2], p->gradients.P[0], p->gradients.P[1], + p->gradients.P[2], p->limiter.rho[0], p->limiter.rho[1], + p->limiter.v[0][0], p->limiter.v[0][1], p->limiter.v[1][0], + p->limiter.v[1][1], p->limiter.v[2][0], p->limiter.v[2][1], + p->limiter.P[0], p->limiter.P[1], p->limiter.maxr, + p->conserved.momentum[0], p->conserved.momentum[1], p->conserved.momentum[2], p->conserved.mass, p->conserved.energy, p->geometry.volume, p->geometry.matrix_E[0][0], p->geometry.matrix_E[0][1], p->geometry.matrix_E[0][2], diff --git a/src/hydro/Gizmo/MFV/hydro_flux.h b/src/hydro/Gizmo/MFV/hydro_flux.h index 5ce8563c80cd7e447a0c4e201394c8a8daa091cf..bab922bd10f40f18ac870d4a47e8fc8d925c220a 100644 --- a/src/hydro/Gizmo/MFV/hydro_flux.h +++ b/src/hydro/Gizmo/MFV/hydro_flux.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 @@ -22,11 +22,11 @@ #include "riemann.h" /** - * @brief Reset the fluxes for the given particle. + * @brief Reset the hydrodynamical fluxes for the given particle. * * @param p Particle. */ -__attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes( +__attribute__((always_inline)) INLINE static void hydro_part_reset_hydro_fluxes( struct part* restrict p) { p->flux.mass = 0.0f; @@ -34,6 +34,15 @@ __attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes( p->flux.momentum[1] = 0.0f; p->flux.momentum[2] = 0.0f; p->flux.energy = 0.0f; +} + +/** + * @brief Reset the gravity fluxes for the given particle. + * + * @param p Particle. + */ +__attribute__((always_inline)) INLINE static void +hydro_part_reset_gravity_fluxes(struct part* restrict p) { p->gravity.mflux[0] = 0.0f; p->gravity.mflux[1] = 0.0f; @@ -87,28 +96,30 @@ __attribute__((always_inline)) INLINE static void hydro_compute_flux( * @param p Particle. * @param fluxes Fluxes accross the interface. * @param dx Distance between the particles that share the interface. + * @param dt Time step for the flux exchange. */ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left( - struct part* restrict p, const float* fluxes, const float* dx) { + struct part* restrict p, const float* fluxes, const float* dx, + const float dt) { p->gravity.mflux[0] += fluxes[0] * dx[0]; p->gravity.mflux[1] += fluxes[0] * dx[1]; p->gravity.mflux[2] += fluxes[0] * dx[2]; - p->flux.mass -= fluxes[0]; - p->flux.momentum[0] -= fluxes[1]; - p->flux.momentum[1] -= fluxes[2]; - p->flux.momentum[2] -= fluxes[3]; - p->flux.energy -= fluxes[4]; + p->flux.mass -= fluxes[0] * dt; + p->flux.momentum[0] -= fluxes[1] * dt; + p->flux.momentum[1] -= fluxes[2] * dt; + p->flux.momentum[2] -= fluxes[3] * dt; + p->flux.energy -= fluxes[4] * dt; #ifndef GIZMO_TOTAL_ENERGY const float ekin = 0.5f * (p->fluid_v[0] * p->fluid_v[0] + p->fluid_v[1] * p->fluid_v[1] + p->fluid_v[2] * p->fluid_v[2]); - p->flux.energy += fluxes[1] * p->fluid_v[0]; - p->flux.energy += fluxes[2] * p->fluid_v[1]; - p->flux.energy += fluxes[3] * p->fluid_v[2]; - p->flux.energy -= fluxes[0] * ekin; + p->flux.energy += fluxes[1] * p->fluid_v[0] * dt; + p->flux.energy += fluxes[2] * p->fluid_v[1] * dt; + p->flux.energy += fluxes[3] * p->fluid_v[2] * dt; + p->flux.energy -= fluxes[0] * ekin * dt; #endif } @@ -119,29 +130,30 @@ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left( * @param p Particle. * @param fluxes Fluxes accross the interface. * @param dx Distance between the particles that share the interface. + * @param dt Time step for the flux exchange. */ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_right(struct part* restrict p, const float* fluxes, - const float* dx) { + const float* dx, const float dt) { p->gravity.mflux[0] += fluxes[0] * dx[0]; p->gravity.mflux[1] += fluxes[0] * dx[1]; p->gravity.mflux[2] += fluxes[0] * dx[2]; - p->flux.mass += fluxes[0]; - p->flux.momentum[0] += fluxes[1]; - p->flux.momentum[1] += fluxes[2]; - p->flux.momentum[2] += fluxes[3]; - p->flux.energy += fluxes[4]; + p->flux.mass += fluxes[0] * dt; + p->flux.momentum[0] += fluxes[1] * dt; + p->flux.momentum[1] += fluxes[2] * dt; + p->flux.momentum[2] += fluxes[3] * dt; + p->flux.energy += fluxes[4] * dt; #ifndef GIZMO_TOTAL_ENERGY const float ekin = 0.5f * (p->fluid_v[0] * p->fluid_v[0] + p->fluid_v[1] * p->fluid_v[1] + p->fluid_v[2] * p->fluid_v[2]); - p->flux.energy -= fluxes[1] * p->fluid_v[0]; - p->flux.energy -= fluxes[2] * p->fluid_v[1]; - p->flux.energy -= fluxes[3] * p->fluid_v[2]; - p->flux.energy += fluxes[0] * ekin; + p->flux.energy -= fluxes[1] * p->fluid_v[0] * dt; + p->flux.energy -= fluxes[2] * p->fluid_v[1] * dt; + p->flux.energy -= fluxes[3] * p->fluid_v[2] * dt; + p->flux.energy += fluxes[0] * ekin * dt; #endif } @@ -167,17 +179,27 @@ hydro_gizmo_mfv_density_drift_term(const float mass_flux, const float dt, /** * @brief Add the gravitational contribution to the fluid velocity drift. * + * @param v (drifted) particle velocity. * @param fluid_v Fluid velocity. - * @param v (Undrifted) particle velocity. - * @param v_full (Drifted) particle velocity. + * @param v_full (Undrifted) particle velocity. + * @param a_grav Gravitational acceleration. + * @param dt_kick_grav Time-step to kick the particle gravitationally. */ __attribute__((always_inline)) INLINE static void -hydro_gizmo_mfv_extra_velocity_drift(float* fluid_v, const float* v, - const float* v_full) { +hydro_gizmo_mfv_extra_velocity_drift(float* restrict v, float* restrict fluid_v, + const float* restrict v_full, + const float* restrict a_grav, + float dt_kick_grav) { + + /* Reset particle velocity */ + v[0] = v_full[0]; + v[1] = v_full[1]; + v[2] = v_full[2]; - fluid_v[0] += v[0] - v_full[0]; - fluid_v[1] += v[1] - v_full[1]; - fluid_v[2] += v[2] - v_full[2]; + /* Drift fluid velocity */ + fluid_v[0] += a_grav[0] * dt_kick_grav; + fluid_v[1] += a_grav[1] * dt_kick_grav; + fluid_v[2] += a_grav[2] * dt_kick_grav; } /** @@ -185,28 +207,29 @@ hydro_gizmo_mfv_extra_velocity_drift(float* fluid_v, const float* v, * gravitational energy. * * @param dt_kick_corr Time step for the potential energy correction. - * @param dt_grav Time step for the (optional) kinetic energy correction. * @param p Particle. * @param momentum Momentum of the particle, explicitly requested so that it is * clear from the code that the momentum needs to be updated after the call to * this function. * @param a_grav Gravitational acceleration. + * @param grav_kick_factor Gravitational kick factor + * (a_grav * dt + a_grav_mesh * dt_mesh) * @return Term used to update the energy variable. */ __attribute__((always_inline)) INLINE static float hydro_gizmo_mfv_gravity_energy_update_term(const float dt_kick_corr, - const float dt_grav, const struct part* restrict p, const float* momentum, - const float* a_grav) { + const float* a_grav, + const float* grav_kick_factor) { float dE = -0.5f * dt_kick_corr * (p->gravity.mflux[0] * a_grav[0] + p->gravity.mflux[1] * a_grav[1] + p->gravity.mflux[2] * a_grav[2]); #if defined(GIZMO_TOTAL_ENERGY) - dE += dt_grav * (momentum[0] * a_grav[0] + momentum[1] * a_grav[1] + - momentum[2] * a_grav[2]); + dE += momentum[0] * grav_kick_factor[0] + momentum[1] * grav_kick_factor[1] + + momentum[2] * grav_kick_factor[2]; #endif return dE; } @@ -220,7 +243,7 @@ hydro_gizmo_mfv_gravity_energy_update_term(const float dt_kick_corr, */ __attribute__((always_inline)) INLINE static float hydro_gizmo_mfv_mass_update_term(const float mass_flux, const float dt) { - return mass_flux * dt; + return mass_flux; } /** diff --git a/src/hydro/Gizmo/MFV/hydro_part.h b/src/hydro/Gizmo/MFV/hydro_part.h index e734c6ca9d5abf182627708120e9b9ffcf7de65f..e64b605f2d240a5dbda0266dcdc5c8277f994d55 100644 --- a/src/hydro/Gizmo/MFV/hydro_part.h +++ b/src/hydro/Gizmo/MFV/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2014 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) + * Copyright (c) 2014 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) * * 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 @@ -106,6 +106,9 @@ struct part { /* Energy flux. */ float energy; + /* Particle time step. Used to compute time-integrated fluxes. */ + float dt; + } flux; /* Geometrical quantities used for hydro. */ @@ -176,9 +179,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Gizmo/MFV/hydro_velocities.h b/src/hydro/Gizmo/MFV/hydro_velocities.h index 5fb11029e29105e6ee08d5df06d79cf04a0d3ddb..faba82652bc9cfbc32563fb5566508ed05fa101c 100644 --- a/src/hydro/Gizmo/MFV/hydro_velocities.h +++ b/src/hydro/Gizmo/MFV/hydro_velocities.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h index a5ff482dbcc0be39b604061814a1bb8661168168..7e3ce039b0d0b5daaa14bc0e3789cdaffb19ec4e 100644 --- a/src/hydro/Gizmo/hydro.h +++ b/src/hydro/Gizmo/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 @@ -75,9 +75,10 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( vrel[0] = W[1] - xp->v_full[0]; vrel[1] = W[2] - xp->v_full[1]; vrel[2] = W[3] - xp->v_full[2]; + const float rhoinv = (W[0] > 0.0f) ? 1.0f / W[0] : 0.0f; float vmax = sqrtf(vrel[0] * vrel[0] + vrel[1] * vrel[1] + vrel[2] * vrel[2]) + - sqrtf(hydro_gamma * W[4] / W[0]); + sqrtf(hydro_gamma * W[4] * rhoinv); vmax = max(vmax, p->timestepvars.vmax); const float psize = cosmo->a * cosmo->a * @@ -250,42 +251,51 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->geometry.volume = volume; /* we multiply with the smoothing kernel normalization */ - p->geometry.matrix_E[0][0] = ihdim * p->geometry.matrix_E[0][0]; - p->geometry.matrix_E[0][1] = ihdim * p->geometry.matrix_E[0][1]; - p->geometry.matrix_E[0][2] = ihdim * p->geometry.matrix_E[0][2]; - p->geometry.matrix_E[1][0] = ihdim * p->geometry.matrix_E[1][0]; - p->geometry.matrix_E[1][1] = ihdim * p->geometry.matrix_E[1][1]; - p->geometry.matrix_E[1][2] = ihdim * p->geometry.matrix_E[1][2]; - p->geometry.matrix_E[2][0] = ihdim * p->geometry.matrix_E[2][0]; - p->geometry.matrix_E[2][1] = ihdim * p->geometry.matrix_E[2][1]; - p->geometry.matrix_E[2][2] = ihdim * p->geometry.matrix_E[2][2]; + p->geometry.matrix_E[0][0] *= ihdim; + p->geometry.matrix_E[0][1] *= ihdim; + p->geometry.matrix_E[0][2] *= ihdim; + p->geometry.matrix_E[1][0] *= ihdim; + p->geometry.matrix_E[1][1] *= ihdim; + p->geometry.matrix_E[1][2] *= ihdim; + p->geometry.matrix_E[2][0] *= ihdim; + p->geometry.matrix_E[2][1] *= ihdim; + p->geometry.matrix_E[2][2] *= ihdim; /* normalise the centroids for MFV */ hydro_velocities_normalise_centroid(p, p->density.wcount); /* Check the condition number to see if we have a stable geometry. */ - float condition_number_E = 0.0f; - int i, j; - for (i = 0; i < 3; ++i) { - for (j = 0; j < 3; ++j) { - condition_number_E += - p->geometry.matrix_E[i][j] * p->geometry.matrix_E[i][j]; - } - } - - invert_dimension_by_dimension_matrix(p->geometry.matrix_E); - - float condition_number_Einv = 0.0f; - for (i = 0; i < 3; ++i) { - for (j = 0; j < 3; ++j) { - condition_number_Einv += - p->geometry.matrix_E[i][j] * p->geometry.matrix_E[i][j]; - } + const float condition_number_E = + p->geometry.matrix_E[0][0] * p->geometry.matrix_E[0][0] + + p->geometry.matrix_E[0][1] * p->geometry.matrix_E[0][1] + + p->geometry.matrix_E[0][2] * p->geometry.matrix_E[0][2] + + p->geometry.matrix_E[1][0] * p->geometry.matrix_E[1][0] + + p->geometry.matrix_E[1][1] * p->geometry.matrix_E[1][1] + + p->geometry.matrix_E[1][2] * p->geometry.matrix_E[1][2] + + p->geometry.matrix_E[2][0] * p->geometry.matrix_E[2][0] + + p->geometry.matrix_E[2][1] * p->geometry.matrix_E[2][1] + + p->geometry.matrix_E[2][2] * p->geometry.matrix_E[2][2]; + + float condition_number = 0.0f; + if (invert_dimension_by_dimension_matrix(p->geometry.matrix_E) != 0) { + /* something went wrong in the inversion; force bad condition number */ + condition_number = const_gizmo_max_condition_number + 1.0f; + } else { + const float condition_number_Einv = + p->geometry.matrix_E[0][0] * p->geometry.matrix_E[0][0] + + p->geometry.matrix_E[0][1] * p->geometry.matrix_E[0][1] + + p->geometry.matrix_E[0][2] * p->geometry.matrix_E[0][2] + + p->geometry.matrix_E[1][0] * p->geometry.matrix_E[1][0] + + p->geometry.matrix_E[1][1] * p->geometry.matrix_E[1][1] + + p->geometry.matrix_E[1][2] * p->geometry.matrix_E[1][2] + + p->geometry.matrix_E[2][0] * p->geometry.matrix_E[2][0] + + p->geometry.matrix_E[2][1] * p->geometry.matrix_E[2][1] + + p->geometry.matrix_E[2][2] * p->geometry.matrix_E[2][2]; + + condition_number = + hydro_dimension_inv * sqrtf(condition_number_E * condition_number_Einv); } - const float condition_number = - hydro_dimension_inv * sqrtf(condition_number_E * condition_number_Einv); - if (condition_number > const_gizmo_max_condition_number && p->geometry.wcorr > const_gizmo_min_wcorr) { #ifdef GIZMO_PATHOLOGICAL_ERROR @@ -302,9 +312,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* compute primitive variables */ /* eqns (3)-(5) */ - const float Q[5] = {p->conserved.mass, p->conserved.momentum[0], - p->conserved.momentum[1], p->conserved.momentum[2], - p->conserved.energy}; + float Q[5] = {p->conserved.mass, p->conserved.momentum[0], + p->conserved.momentum[1], p->conserved.momentum[2], + p->conserved.energy}; #ifdef SWIFT_DEBUG_CHECKS if (Q[0] < 0.) { @@ -319,16 +329,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( float W[5]; W[0] = Q[0] * volume_inv; - if (Q[0] == 0.0f) { - W[1] = 0.; - W[2] = 0.; - W[3] = 0.; - } else { - const float m_inv = 1.0f / Q[0]; - W[1] = Q[1] * m_inv; - W[2] = Q[2] * m_inv; - W[3] = Q[3] * m_inv; - } + const float m_inv = (Q[0] != 0.0f) ? 1.0f / Q[0] : 0.0f; + W[1] = Q[1] * m_inv; + W[2] = Q[2] * m_inv; + W[3] = Q[3] * m_inv; #ifdef EOS_ISOTHERMAL_GAS /* although the pressure is not formally used anywhere if an isothermal eos @@ -474,13 +478,15 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part* restrict p, struct xpart* restrict xp, const struct cosmology* cosmo, const struct hydro_props* hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { - hydro_part_reset_fluxes(p); + hydro_part_reset_gravity_fluxes(p); + p->flux.dt = dt_therm; } /** @@ -544,7 +550,8 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part* p, struct xpart* xp, float dt_drift, float dt_therm, - const struct cosmology* cosmo, const struct hydro_props* hydro_props, + float dt_kick_grav, const struct cosmology* cosmo, + const struct hydro_props* hydro_props, const struct entropy_floor_properties* floor_props) { /* skip the drift if we are using Lloyd's algorithm */ @@ -575,36 +582,39 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( float W[5]; hydro_part_get_primitive_variables(p, W); - float flux[5]; - hydro_part_get_fluxes(p, flux); - - W[0] += - hydro_gizmo_mfv_density_drift_term(flux[0], dt_therm, p->geometry.volume); - - if (p->conserved.mass > 0.0f) { - const float m_inv = 1.0f / p->conserved.mass; - - W[1] += flux[1] * dt_therm * m_inv; - W[2] += flux[2] * dt_therm * m_inv; - W[3] += flux[3] * dt_therm * m_inv; - -#if !defined(EOS_ISOTHERMAL_GAS) -#ifdef GIZMO_TOTAL_ENERGY - const float Etot = p->conserved.energy + flux[4] * dt_therm; - const float v2 = (W[1] * W[1] + W[2] * W[2] + W[3] * W[3]); - const float u = (Etot * m_inv - 0.5f * v2); -#else - const float u = (p->conserved.energy + flux[4] * dt_therm) * m_inv; -#endif - W[4] = hydro_gamma_minus_one * u * W[0]; -#endif + float gradrho[3], gradvx[3], gradvy[3], gradvz[3], gradP[3]; + hydro_part_get_gradients(p, gradrho, gradvx, gradvy, gradvz, gradP); + + const float divv = gradvx[0] + gradvy[1] + gradvz[2]; + + float Wprime[5]; + Wprime[0] = W[0] - dt_therm * (W[0] * divv + W[1] * gradrho[0] + + W[2] * gradrho[1] + W[3] * gradrho[2]); + if (W[0] != 0.0f) { + const float rhoinv = 1.0f / W[0]; + Wprime[1] = W[1] - dt_therm * (W[1] * divv + rhoinv * gradP[0]); + Wprime[2] = W[2] - dt_therm * (W[2] * divv + rhoinv * gradP[1]); + Wprime[3] = W[3] - dt_therm * (W[3] * divv + rhoinv * gradP[2]); + } else { + Wprime[1] = 0.0f; + Wprime[2] = 0.0f; + Wprime[3] = 0.0f; } + Wprime[4] = W[4] - dt_therm * (hydro_gamma * W[4] * divv + W[1] * gradP[0] + + W[2] * gradP[1] + W[3] * gradP[2]); + + W[0] = Wprime[0]; + W[1] = Wprime[1]; + W[2] = Wprime[2]; + W[3] = Wprime[3]; + W[4] = Wprime[4]; // MATTHIEU: Apply the entropy floor here. /* add the gravitational contribution to the fluid velocity drift */ /* (MFV only) */ - hydro_gizmo_mfv_extra_velocity_drift(&W[1], p->v, xp->v_full); + hydro_gizmo_mfv_extra_velocity_drift(p->v, p->fluid_v, xp->v_full, xp->a_grav, + dt_kick_grav); gizmo_check_physical_quantities("density", "pressure", W[0], W[1], W[2], W[3], W[4]); @@ -650,8 +660,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part* p, struct xpart* xp, float dt_therm, float dt_grav, - float dt_hydro, float dt_kick_corr, const struct cosmology* cosmo, - const struct hydro_props* hydro_props, + float dt_grav_mesh, float dt_hydro, float dt_kick_corr, + const struct cosmology* cosmo, const struct hydro_props* hydro_props, const struct entropy_floor_properties* floor_props) { /* Add gravity. We only do this if we have gravity activated. */ @@ -659,39 +669,67 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( /* Retrieve the current value of the gravitational acceleration from the gpart. We are only allowed to do this because this is the kick. We still need to check whether gpart exists though.*/ - float a_grav[3]; - - a_grav[0] = p->gpart->a_grav[0]; - a_grav[1] = p->gpart->a_grav[1]; - a_grav[2] = p->gpart->a_grav[2]; - - p->conserved.energy += hydro_gizmo_mfv_gravity_energy_update_term( - dt_kick_corr, dt_grav, p, p->conserved.momentum, a_grav); + float a_grav[3], grav_kick_factor[3]; + + a_grav[0] = p->gpart->a_grav[0] + p->gpart->a_grav_mesh[0]; + a_grav[1] = p->gpart->a_grav[1] + p->gpart->a_grav_mesh[1]; + a_grav[2] = p->gpart->a_grav[2] + p->gpart->a_grav_mesh[2]; + + grav_kick_factor[0] = dt_grav * p->gpart->a_grav[0]; + grav_kick_factor[1] = dt_grav * p->gpart->a_grav[1]; + grav_kick_factor[2] = dt_grav * p->gpart->a_grav[2]; + if (dt_grav_mesh != 0) { + grav_kick_factor[0] += dt_grav_mesh * p->gpart->a_grav_mesh[0]; + grav_kick_factor[1] += dt_grav_mesh * p->gpart->a_grav_mesh[1]; + grav_kick_factor[2] += dt_grav_mesh * p->gpart->a_grav_mesh[2]; + } /* Kick the momentum for half a time step */ /* Note that this also affects the particle movement, as the velocity for the particles is set after this. */ - p->conserved.momentum[0] += p->conserved.mass * a_grav[0] * dt_grav; - p->conserved.momentum[1] += p->conserved.mass * a_grav[1] * dt_grav; - p->conserved.momentum[2] += p->conserved.mass * a_grav[2] * dt_grav; + p->conserved.momentum[0] += p->conserved.mass * grav_kick_factor[0]; + p->conserved.momentum[1] += p->conserved.mass * grav_kick_factor[1]; + p->conserved.momentum[2] += p->conserved.mass * grav_kick_factor[2]; + + p->conserved.energy += hydro_gizmo_mfv_gravity_energy_update_term( + dt_kick_corr, p, p->conserved.momentum, a_grav, grav_kick_factor); } - float flux[5]; - hydro_part_get_fluxes(p, flux); + if (p->flux.dt > 0.0f) { + float flux[5]; + hydro_part_get_fluxes(p, flux); - /* Update conserved variables. */ - p->conserved.mass += hydro_gizmo_mfv_mass_update_term(flux[0], dt_therm); - p->conserved.momentum[0] += flux[1] * dt_therm; - p->conserved.momentum[1] += flux[2] * dt_therm; - p->conserved.momentum[2] += flux[3] * dt_therm; + /* Update conserved variables. */ + p->conserved.mass += hydro_gizmo_mfv_mass_update_term(flux[0], dt_therm); + p->conserved.momentum[0] += flux[1]; + p->conserved.momentum[1] += flux[2]; + p->conserved.momentum[2] += flux[3]; #if defined(EOS_ISOTHERMAL_GAS) - /* We use the EoS equation in a sneaky way here just to get the constant u */ - p->conserved.energy = - p->conserved.mass * gas_internal_energy_from_entropy(0.0f, 0.0f); + /* We use the EoS equation in a sneaky way here just to get the constant u + */ + p->conserved.energy = + p->conserved.mass * gas_internal_energy_from_entropy(0.0f, 0.0f); #else - p->conserved.energy += flux[4] * dt_therm; + p->conserved.energy += flux[4]; #endif + /* reset the fluxes, so that they do not get used again in kick1 */ + hydro_part_reset_hydro_fluxes(p); + /* invalidate the particle time step. It is considered to be inactive until + dt is set again in hydro_prepare_force() */ + p->flux.dt = -1.0f; + } else if (p->flux.dt == 0.0f) { + /* something tricky happens at the beginning of the simulation: the flux + exchange is done for all particles, but using a time step of 0. This + in itself is not a problem. However, it causes some issues with the + initialisation of flux.dt for inactive particles, since this value will + remain 0 until the particle is active again, and its flux.dt is set to + the actual time step in hydro_prepare_force(). We have to make sure it + is properly set to -1 here, so that inactive particles are indeed found + to be inactive during the flux loop. */ + p->flux.dt = -1.0f; + } + #ifndef HYDRO_GAMMA_5_3 const float Pcorr = (dt_hydro - dt_therm) * p->geometry.volume; diff --git a/src/hydro/Gizmo/hydro_flux.h b/src/hydro/Gizmo/hydro_flux.h index 0fba48fe6aafa6073d8af90ac57a4a6cc44508a7..0b3406d1d604fc3f1ab4b3a17e08225617cc3b7e 100644 --- a/src/hydro/Gizmo/hydro_flux.h +++ b/src/hydro/Gizmo/hydro_flux.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 diff --git a/src/hydro/Gizmo/hydro_getters.h b/src/hydro/Gizmo/hydro_getters.h index cfcf8b9370324c3529c937b3079951f348d42451..7a83e093680ed63466eedd8c3885cf19a9279b5c 100644 --- a/src/hydro/Gizmo/hydro_getters.h +++ b/src/hydro/Gizmo/hydro_getters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 @@ -113,7 +113,8 @@ __attribute__((always_inline)) INLINE static void hydro_part_get_slope_limiter( * @param p The particle of interest. */ __attribute__((always_inline)) INLINE static float -hydro_get_comoving_internal_energy(const struct part* restrict p) { +hydro_get_comoving_internal_energy(const struct part* restrict p, + const struct xpart* restrict xp) { if (p->rho > 0.0f) return gas_internal_energy_from_pressure(p->rho, p->P); @@ -134,7 +135,7 @@ hydro_get_physical_internal_energy(const struct part* restrict p, const struct cosmology* cosmo) { return cosmo->a_factor_internal_energy * - hydro_get_comoving_internal_energy(p); + hydro_get_comoving_internal_energy(p, xp); } /** @@ -158,7 +159,7 @@ hydro_get_drifted_physical_internal_energy(const struct part* restrict p, __attribute__((always_inline)) INLINE static float hydro_get_drifted_comoving_internal_energy(const struct part* restrict p) { - return hydro_get_comoving_internal_energy(p); + return hydro_get_comoving_internal_energy(p, NULL); } /** @@ -192,6 +193,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( return hydro_get_comoving_entropy(p, NULL); } +/** + * @brief Returns the comoving entropy of a particle drifted to the + * current time. + * + * @param p The particle of interest. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_comoving_entropy(const struct part* restrict p) { + + return hydro_get_comoving_entropy(p, NULL); +} + /** * @brief Returns the physical internal energy of a particle * @@ -310,8 +323,30 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( __attribute__((always_inline)) INLINE static float hydro_get_comoving_internal_energy_dt(const struct part* restrict p) { - error("Needs implementing"); - return 0.0f; + float W[5]; + hydro_part_get_primitive_variables(p, W); + + if (W[0] <= 0.0f) { + return 0.0f; + } + + const float rho_inv = 1.f / W[0]; + + float gradrho[3], gradvx[3], gradvy[3], gradvz[3], gradP[3]; + hydro_part_get_gradients(p, gradrho, gradvx, gradvy, gradvz, gradP); + + const float divv = gradvx[0] + gradvy[1] + gradvz[2]; + + float gradu[3] = {0.f, 0.f, 0.f}; + for (int i = 0; i < 3; i++) { + gradu[i] = hydro_one_over_gamma_minus_one * rho_inv * + (gradP[i] - rho_inv * W[4] * gradrho[i]); + } + + const float du_dt = -(W[1] * gradu[0] + W[2] * gradu[1] + W[3] * gradu[2]) - + rho_inv * W[4] * divv; + + return du_dt; } /** @@ -325,8 +360,9 @@ hydro_get_comoving_internal_energy_dt(const struct part* restrict p) { __attribute__((always_inline)) INLINE static float hydro_get_physical_internal_energy_dt(const struct part* restrict p, const struct cosmology* cosmo) { - error("Needs implementing"); - return 0.0f; + + return hydro_get_comoving_internal_energy_dt(p) * + cosmo->a_factor_internal_energy; } /** diff --git a/src/hydro/Gizmo/hydro_gradients.h b/src/hydro/Gizmo/hydro_gradients.h index eb23e3609fb1aca36e8f50387fe12ab78470314e..df6399fa25dd6ed533a4e105b1be0ebc1464319d 100644 --- a/src/hydro/Gizmo/hydro_gradients.h +++ b/src/hydro/Gizmo/hydro_gradients.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 diff --git a/src/hydro/Gizmo/hydro_gradients_gizmo.h b/src/hydro/Gizmo/hydro_gradients_gizmo.h index 99ae8e6b7cdda29059fb826545d167846eec78c0..7d55eea38dd562dab1cfb6b091f66bca646284d5 100644 --- a/src/hydro/Gizmo/hydro_gradients_gizmo.h +++ b/src/hydro/Gizmo/hydro_gradients_gizmo.h @@ -52,7 +52,7 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( /* Get r and 1/r. */ const float r = sqrtf(r2); - const float r_inv = 1.0f / r; + const float r_inv = (r > 0.0f) ? 1.0f / r : 0.0f; float wi, wj, wi_dx, wj_dx; float Bi[3][3]; @@ -181,7 +181,7 @@ hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj, /* Get r and 1/r. */ const float r = sqrtf(r2); - const float r_inv = 1.0f / r; + const float r_inv = (r > 0.0f) ? 1.0f / r : 0.0f; float Bi[3][3]; float Wi[5], Wj[5]; diff --git a/src/hydro/Gizmo/hydro_gradients_sph.h b/src/hydro/Gizmo/hydro_gradients_sph.h index cbe33514bc7b3f8046faf201aee59515f2d3b269..ec4db739f6d4baa91e57a9201300d763109d7496 100644 --- a/src/hydro/Gizmo/hydro_gradients_sph.h +++ b/src/hydro/Gizmo/hydro_gradients_sph.h @@ -52,7 +52,7 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( /* Get r and 1/r. */ const float r = sqrtf(r2); - const float r_inv = 1.0f / r; + const float r_inv = (r > 0.0f) ? 1.0f / r : 0.0f; float wi, wi_dx; const float hi_inv = 1.0f / hi; @@ -145,7 +145,7 @@ hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj, /* Get r and 1/r. */ const float r = sqrtf(r2); - const float r_inv = 1.0f / r; + const float r_inv = (r > 0.0f) ? 1.0f / r : 0.0f; float wi, wi_dx; const float hi_inv = 1.0f / hi; diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/Gizmo/hydro_iact.h index 4fd99bbc52cd6437a9b8639b986e35f8e9d49aee..3d96473879f78b79e2ee56bef32f449cef915894 100644 --- a/src/hydro/Gizmo/hydro_iact.h +++ b/src/hydro/Gizmo/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 @@ -406,15 +406,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( float totflux[5]; hydro_compute_flux(Wi, Wj, n_unit, vij, Anorm, totflux); - hydro_part_update_fluxes_left(pi, totflux, dx); + /* get the time step for the flux exchange. This is always the smallest time + step among the two particles */ + const float mindt = + (pj->flux.dt > 0.0f) ? fminf(pi->flux.dt, pj->flux.dt) : pi->flux.dt; - /* Note that this used to be much more complicated in early implementations of - * the GIZMO scheme, as we wanted manifest conservation of conserved variables - * and had to do symmetric flux exchanges. Now we don't care about manifest - * conservation anymore and just assume the current fluxes are representative - * for the flux over the entire time step. */ - if (mode == 1) { - hydro_part_update_fluxes_right(pj, totflux, dx); + hydro_part_update_fluxes_left(pi, totflux, dx, mindt); + + /* Unlike in SPH schemes, we do need to update inactive neighbours, so that + the fluxes are always exchanged symmetrically. Thanks to our sneaky use + of flux.dt, we can detect inactive neighbours through their negative + time step. */ + if (mode == 1 || (pj->flux.dt < 0.0f)) { + hydro_part_update_fluxes_right(pj, totflux, dx, mindt); } /* If we're working with RT, we need to pay additional attention to the diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/Gizmo/hydro_io.h index 2e00272924160ce555b65352e84e40e0cfa65e4e..171484205512e86718f4c5de60d826b0c77c7985 100644 --- a/src/hydro/Gizmo/hydro_io.h +++ b/src/hydro/Gizmo/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 @@ -76,7 +76,7 @@ INLINE static void hydro_read_particles(struct part* parts, INLINE static void convert_u(const struct engine* e, const struct part* p, const struct xpart* xp, float* ret) { - ret[0] = hydro_get_comoving_internal_energy(p); + ret[0] = hydro_get_comoving_internal_energy(p, xp); } /** @@ -103,13 +103,16 @@ INLINE static void convert_Etot(const struct engine* e, const struct part* p, #ifdef GIZMO_TOTAL_ENERGY ret[0] = p->conserved.energy; #else - float momentum2; + if (p->conserved.mass > 0.0f) { + const float momentum2 = + p->conserved.momentum[0] * p->conserved.momentum[0] + + p->conserved.momentum[1] * p->conserved.momentum[1] + + p->conserved.momentum[2] * p->conserved.momentum[2]; - momentum2 = p->conserved.momentum[0] * p->conserved.momentum[0] + - p->conserved.momentum[1] * p->conserved.momentum[1] + - p->conserved.momentum[2] * p->conserved.momentum[2]; - - ret[0] = p->conserved.energy + 0.5f * momentum2 / p->conserved.mass; + ret[0] = p->conserved.energy + 0.5f * momentum2 / p->conserved.mass; + } else { + ret[0] = 0.0f; + } #endif } diff --git a/src/hydro/Gizmo/hydro_lloyd.h b/src/hydro/Gizmo/hydro_lloyd.h index 4c281e479d170ab17f2efffd0f451cdd77d6a578..b8c4b06b21c5f048cca19299752ef70b6e1e6eed 100644 --- a/src/hydro/Gizmo/hydro_lloyd.h +++ b/src/hydro/Gizmo/hydro_lloyd.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 diff --git a/src/hydro/Gizmo/hydro_parameters.h b/src/hydro/Gizmo/hydro_parameters.h index 148d5e604643035808c6ce12974cafcb0edf992c..06b304df54b1484c552f379029c3327a36b49be1 100644 --- a/src/hydro/Gizmo/hydro_parameters.h +++ b/src/hydro/Gizmo/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_GIZMO_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/Gizmo/hydro_part.h index 04eba5cb9c82670eb17eba8285f7847451264a12..731efd00ec50cda71331cbe372fbc146d6f853df 100644 --- a/src/hydro/Gizmo/hydro_part.h +++ b/src/hydro/Gizmo/hydro_part.h @@ -25,6 +25,7 @@ #include "feedback_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" diff --git a/src/hydro/Gizmo/hydro_setters.h b/src/hydro/Gizmo/hydro_setters.h index 91219da87f08a3f694976c0c18258de8c3b6b98a..640912ccbb226a74f629afb0ac630079f6332854 100644 --- a/src/hydro/Gizmo/hydro_setters.h +++ b/src/hydro/Gizmo/hydro_setters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 @@ -195,7 +195,10 @@ __attribute__((always_inline)) INLINE static void hydro_set_mass( __attribute__((always_inline)) INLINE static void hydro_set_comoving_internal_energy_dt(struct part* restrict p, const float du_dt) { - error("Needs implementing"); + + const float old_du_dt = hydro_get_comoving_internal_energy_dt(p); + + p->flux.energy += p->conserved.mass * (du_dt - old_du_dt) * p->flux.dt; } /** @@ -211,8 +214,43 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_internal_energy_dt(struct part* restrict p, const struct cosmology* restrict cosmo, const float du_dt) { - error("Needs implementing"); + + hydro_set_comoving_internal_energy_dt( + p, du_dt / cosmo->a_factor_internal_energy); } + +/** + * @brief Sets the comoving internal energy of a particle + * + * @param p The particle of interest. + * @param u The comoving internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_comoving_internal_energy(struct part* p, const float u) { + + const float mass = p->conserved.mass; + if (mass <= 0.0f) { + return; + } + + const float Etherm = mass * u; + +#ifdef GIZMO_TOTAL_ENERGY + const float Ekin = 0.5f * + (p->conserved.momentum[0] * p->conserved.momentum[0] + + p->conserved.momentum[1] * p->conserved.momentum[1] + + p->conserved.momentum[2] * p->conserved.momentum[2]) / + mass; + + const float Etot = Ekin + Etherm; + p->conserved.energy = Etot; +#else + p->conserved.energy = Etherm; +#endif + + p->P = gas_pressure_from_internal_energy(p->rho, u); +} + /** * @brief Sets the physical entropy of a particle * @@ -225,7 +263,8 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( struct part* p, struct xpart* xp, const struct cosmology* cosmo, const float entropy) { - error("Needs implementing"); + const float u = gas_internal_energy_from_entropy(p->rho, entropy); + hydro_set_comoving_internal_energy(p, u); } /** @@ -240,7 +279,8 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_internal_energy(struct part* p, struct xpart* xp, const struct cosmology* cosmo, const float u) { - error("Need implementing"); + + hydro_set_comoving_internal_energy(p, u / cosmo->a_factor_internal_energy); } /** @@ -254,7 +294,8 @@ __attribute__((always_inline)) INLINE static void hydro_set_drifted_physical_internal_energy(struct part* p, const struct cosmology* cosmo, const float u) { - error("Need implementing"); + + hydro_set_comoving_internal_energy(p, u / cosmo->a_factor_internal_energy); } /** diff --git a/src/hydro/Gizmo/hydro_slope_limiters_face.h b/src/hydro/Gizmo/hydro_slope_limiters_face.h index d11c5aecf2174ecabc4f10f9b070339300a3e1a7..779abf7eef88e5dbea1a3fe7600470b7e9bfeb89 100644 --- a/src/hydro/Gizmo/hydro_slope_limiters_face.h +++ b/src/hydro/Gizmo/hydro_slope_limiters_face.h @@ -59,13 +59,15 @@ hydro_slope_limit_face_quantity(float phi_i, float phi_j, float phi_mid0, if (same_signf(phimax + delta1, phimax)) { phiplus = phimax + delta1; } else { - phiplus = phimax / (1.0f + delta1 / (fabsf(phimax) + FLT_MIN)); + phiplus = + (phimax != 0.0f) ? phimax / (1.0f + delta1 / fabsf(phimax)) : 0.0f; } if (same_signf(phimin - delta1, phimin)) { phiminus = phimin - delta1; } else { - phiminus = phimin / (1.0f + delta1 / (fabsf(phimin) + FLT_MIN)); + phiminus = + (phimin != 0.0f) ? phimin / (1.0f + delta1 / fabsf(phimin)) : 0.0f; } if (phi_i < phi_j) { @@ -102,7 +104,7 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_face( const float xij_j_norm = sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] + xij_j[2] * xij_j[2]); - const float r_inv = 1.f / r; + const float r_inv = (r > 0.0f) ? 1.0f / r : 0.0f; dWi[0] = hydro_slope_limit_face_quantity(Wi[0], Wj[0], Wi[0] + dWi[0], xij_i_norm, r_inv); diff --git a/src/hydro/Gizmo/hydro_velocities.h b/src/hydro/Gizmo/hydro_velocities.h index bb385f31728ac95ac478a1ff15852edf388a7d5e..21f63e836a5817bef9328bc6ed21beeffaad7d8a 100644 --- a/src/hydro/Gizmo/hydro_velocities.h +++ b/src/hydro/Gizmo/hydro_velocities.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index bc5501690c0fe3e10e2f43dfd8f47863f77f9e6d..6b67431123a0b87b1189510682eb9587c6cb00a9 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -450,6 +450,49 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + +/** + * @brief returns the signal velocity + * + * @brief p the particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_signal_velocity( + const struct part *restrict p) { + + return p->force.v_sig; +} +/** + * @brief returns the div_v + * + * @brief p the particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_div_v( + const struct part *restrict p) { + + return p->density.div_v; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -529,6 +572,44 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.div_v *= h_inv_dim_plus_one * a_inv2 * rho_inv; } +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -581,11 +662,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps; @@ -721,13 +803,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -803,6 +886,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -811,7 +895,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/Minimal/hydro_debug.h b/src/hydro/Minimal/hydro_debug.h index f66578cf85cf0c4602005905680e82fb618f2e29..08e17b7ffbef09d31226f9697c541794d5af1d03 100644 --- a/src/hydro/Minimal/hydro_debug.h +++ b/src/hydro/Minimal/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -35,19 +35,24 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "\n " - "x=[%.6g, %.6g, %.6g], v=[%.3g, %.3g, %.3g], \n " - "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 \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), - p->force.soundspeed, p->force.v_sig, p->h, p->force.h_dt, - p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] " + "x=[%.6g, %.6g, %.6g], v=[%.3g, %.3g, %.3g], " + "a=[%.3g, %.3g, %.3g], " + "m=%.3g, u=%.3g, du/dt=%.3g, P=%.3g, c_s=%.3g, " + "v_sig=%.3g, h=%.3g, dh/dt=%.3g, wcount=%.3g, rho=%.3g, " + "dh_drho=%.3g, time_bin=%d wakeup=%d", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[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), p->force.soundspeed, p->force.v_sig, p->h, + p->force.h_dt, p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, p->limiter_data.wakeup); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3g, %.3g, %.3g]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */ diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h index 221ed13fc5c4fb6d8e8a1fd3a2014066c154e7c8..77c50dd428b8968e66f99104866683b5ebe27936 100644 --- a/src/hydro/Minimal/hydro_iact.h +++ b/src/hydro/Minimal/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -35,6 +35,7 @@ #include "adiabatic_index.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -182,6 +183,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->density.rot_v[2] += faci * curlvr[2]; } +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + /** * @brief Force interaction between two particles. * @@ -258,10 +300,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float omega_ij = min(dvdr_Hubble, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Compute sound speeds and signal velocity */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + /* Compute signal velocity */ + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Grab balsara switches */ const float balsara_i = pi->force.balsara; @@ -391,10 +431,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float omega_ij = min(dvdr_Hubble, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Compute sound speeds and signal velocity */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + /* Compute signal velocity */ + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Grab balsara switches */ const float balsara_i = pi->force.balsara; diff --git a/src/hydro/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h index 2ce75c330be1156f183009ea914b2ce0db9dcf49..8c32aa7f020fd059fe556e12c147fa397b2d5163 100644 --- a/src/hydro/Minimal/hydro_io.h +++ b/src/hydro/Minimal/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -179,7 +179,7 @@ INLINE static void hydro_write_particles(const struct part* parts, struct io_props* list, int* num_fields) { - *num_fields = 9; + *num_fields = 10; /* List what we want to write */ list[0] = io_make_output_field_convert_part( @@ -219,6 +219,11 @@ INLINE static void hydro_write_particles(const struct part* parts, 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( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, + "Co-moving gravitational potential at position of the particles"); } /** diff --git a/src/hydro/Minimal/hydro_parameters.h b/src/hydro/Minimal/hydro_parameters.h index 9753933255b642b1d5abb7cebf20834c534aa705..74d3236f5a0d1fc095865f45e1db51973ad229c8 100644 --- a/src/hydro/Minimal/hydro_parameters.h +++ b/src/hydro/Minimal/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_MINIMAL_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h index 30a7cbb19a71ca9b9f6d21bd5ba68f451b07c25a..6709bb5e236bfd1f3ab1cffb6ecdc925115a9375 100644 --- a/src/hydro/Minimal/hydro_part.h +++ b/src/hydro/Minimal/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -36,8 +36,10 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -78,9 +80,12 @@ struct xpart { /* Additional data used by the tracers */ struct star_formation_xpart_data sf_data; - /* Additional data used by the feedback */ + /*! Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /** @@ -181,6 +186,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -193,9 +201,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/None/hydro.h b/src/hydro/None/hydro.h index 167a989e6586cf3da4c69748e3e3e80e06e415b3..4e7dd9ad6b6ad96e0f5170a93c3733e836fe70c1 100644 --- a/src/hydro/None/hydro.h +++ b/src/hydro/None/hydro.h @@ -446,6 +446,25 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return FLT_MAX; } +/** + * @brief Compute the signal velocity between two gas particles + * + * Just return -1 in this empty implementation. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + return -1.; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -489,6 +508,44 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( __attribute__((always_inline)) INLINE static void hydro_end_density( struct part *restrict p, const struct cosmology *cosmo) {} +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -520,11 +577,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) {} + const float dt_alpha, const float dt_therm) {} /** * @brief Reset acceleration fields of a particle @@ -562,13 +620,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) {} @@ -597,6 +656,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -605,7 +665,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) {} diff --git a/src/hydro/None/hydro_iact.h b/src/hydro/None/hydro_iact.h index ed235dec18fb39dc1df417bcc2428af897156fc7..cf7b794a1974a2c807cecaa3ba7c2032c52925fc 100644 --- a/src/hydro/None/hydro_iact.h +++ b/src/hydro/None/hydro_iact.h @@ -62,6 +62,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( struct part *restrict pi, const struct part *restrict pj, const float a, const float H) {} +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + /** * @brief Force interaction between two particles. * diff --git a/src/hydro/None/hydro_parameters.h b/src/hydro/None/hydro_parameters.h index 6e6163a9408161cf652cf8303fbc270a8aaab160..6470173d35c24e3d9ad230da9e3ab9190df1d941 100644 --- a/src/hydro/None/hydro_parameters.h +++ b/src/hydro/None/hydro_parameters.h @@ -20,7 +20,7 @@ #define SWIFT_NONE_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/None/hydro_part.h b/src/hydro/None/hydro_part.h index d735f312c0563544afeb405ad0d8e7b4c8107fb0..589c874e9716bb17c97d14da6d6e202ccffa6439 100644 --- a/src/hydro/None/hydro_part.h +++ b/src/hydro/None/hydro_part.h @@ -28,9 +28,11 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "pressure_floor_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -71,6 +73,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /** @@ -144,6 +149,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -156,12 +164,18 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional data used by the pressure floor */ struct pressure_floor_part_data pressure_floor_data; /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Phantom/hydro.h b/src/hydro/Phantom/hydro.h index 575363a89118f23744745cab1491582c2ba7796c..4259bd8bacaf558ffa4b6fc89aa0ddfeeb228cba 100644 --- a/src/hydro/Phantom/hydro.h +++ b/src/hydro/Phantom/hydro.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -467,6 +467,28 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -720,11 +742,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { /* Here we need to update the artificial viscosity */ @@ -845,13 +868,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -928,6 +952,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -936,7 +961,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/Phantom/hydro_debug.h b/src/hydro/Phantom/hydro_debug.h index 6eeb4777b644ccc32e74109f907bf7834bee7058..97aa66f43eb2422c2e856a260b8ce5e765a93c01 100644 --- a/src/hydro/Phantom/hydro_debug.h +++ b/src/hydro/Phantom/hydro_debug.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -33,18 +33,24 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "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" - "alpha=%.3e \n" - "time_bin=%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->viscosity.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, + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e], a=[%.3e,%.3e,%.3e], " + "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e, " + "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, " + "alpha=%.3e, " + "time_bin=%d", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, + p->viscosity.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->viscosity.alpha, p->time_bin); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_PHANTOM_HYDRO_DEBUG_H */ diff --git a/src/hydro/Phantom/hydro_iact.h b/src/hydro/Phantom/hydro_iact.h index b4711c62e758fbda2ffef8790ff6b1433e9da45c..3ae9c98ca5eaf48a93246947ae1d2e811f1419ca 100644 --- a/src/hydro/Phantom/hydro_iact.h +++ b/src/hydro/Phantom/hydro_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -33,6 +33,7 @@ #include "adiabatic_index.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -194,8 +195,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -213,7 +212,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -247,8 +247,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -266,7 +264,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -334,9 +333,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float omega_ij = min(dvdr_Hubble, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Compute sound speeds and signal velocity */ - const float v_sig = pi->force.soundspeed + pj->force.soundspeed - - const_viscosity_beta * mu_ij; + /* Compute the signal velocity */ + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Variable smoothing length term */ const float f_ij = 1.f - pi->force.f / mj; @@ -390,7 +388,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha); /* wi_dx + wj_dx / 2 is F_ij */ const float diff_du_term = alpha_diff * v_diff * (pi->u - pj->u) * 0.5f * - (wi_dr * f_ij / pi->rho + wj_dr * f_ji / pi->rho); + (wi_dr * f_ij / rhoi + wj_dr * f_ji / rhoj); /* Assemble the energy equation term */ const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; @@ -401,8 +399,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->u_dt += du_dt_j * mi; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * f_ij * r_inv / rhoj * wi_dr; - pj->force.h_dt -= mi * dvdr * f_ji * r_inv / rhoi * wj_dr; + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; } /** @@ -467,9 +465,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float omega_ij = min(dvdr_Hubble, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Compute sound speeds and signal velocity */ - const float v_sig = pi->force.soundspeed + pj->force.soundspeed - - const_viscosity_beta * mu_ij; + /* Compute the signal velocity */ + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Variable smoothing length term */ const float f_ij = 1.f - pi->force.f / mj; @@ -517,7 +514,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha); /* wi_dx + wj_dx / 2 is F_ij */ const float diff_du_term = alpha_diff * v_diff * (pi->u - pj->u) * 0.5f * - (wi_dr * f_ij / pi->rho + wj_dr * f_ji / pi->rho); + (wi_dr * f_ij / rhoi + wj_dr * f_ji / rhoj); /* Assemble the energy equation term */ const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; @@ -526,7 +523,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->u_dt += du_dt_i * mj; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * f_ij * r_inv / rhoj * wi_dr; + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; } #endif /* SWIFT_PHANTOM_HYDRO_IACT_H */ diff --git a/src/hydro/Phantom/hydro_io.h b/src/hydro/Phantom/hydro_io.h index b13584e80d9037c40669416f2680ad71ba74a526..7dbdef3424f02c5a4d3d09d2bcde679269b01455 100644 --- a/src/hydro/Phantom/hydro_io.h +++ b/src/hydro/Phantom/hydro_io.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 diff --git a/src/hydro/Phantom/hydro_parameters.h b/src/hydro/Phantom/hydro_parameters.h index 26f0239c304d712508b6a377d99987253f49ac04..73b110b9c0f8257aef9237b4815d5b2b872aad19 100644 --- a/src/hydro/Phantom/hydro_parameters.h +++ b/src/hydro/Phantom/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_PHANTOM_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/Phantom/hydro_part.h b/src/hydro/Phantom/hydro_part.h index 020eac811107c8b0916340cfdd4430bb50efd0a9..7c65800e3ccca7c069a993de95733163cb3bac45 100644 --- a/src/hydro/Phantom/hydro_part.h +++ b/src/hydro/Phantom/hydro_part.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -34,8 +34,10 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -79,6 +81,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /** @@ -201,6 +206,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -213,9 +221,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Planetary/hydro.h b/src/hydro/Planetary/hydro.h index 5c55cba3e9a50701400373d69c433748deeb5ac7..daa9c39b19c6225ed96bf8daedda9229341a5c73 100644 --- a/src/hydro/Planetary/hydro.h +++ b/src/hydro/Planetary/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify @@ -450,6 +450,50 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + +/** + * @brief returns the signal velocity + * + * @brief p the particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_signal_velocity( + const struct part *restrict p) { + + return p->force.v_sig; +} + +/** + * @brief returns the div_v + * + * @brief p the particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_div_v( + const struct part *restrict p) { + + return p->density.div_v; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -529,6 +573,44 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.div_v *= h_inv_dim_plus_one * a_inv2 * rho_inv; } +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -578,11 +660,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps; @@ -728,13 +811,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The constants used in the scheme * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -804,13 +888,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param p The particle to act upon. * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). + * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). + * @param dt_hydro The time-step for this kick (for hydro quantities). + * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. * @param hydro_props The constants used in the scheme * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/Planetary/hydro_debug.h b/src/hydro/Planetary/hydro_debug.h index db3e60d0864837db2caad4f627e635f4932f0799..d15500f17a3404bf3e87f2a3247b406fe2f221d6 100644 --- a/src/hydro/Planetary/hydro_debug.h +++ b/src/hydro/Planetary/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify @@ -22,33 +22,31 @@ /** * @file Planetary/hydro_debug.h - * @brief Minimal conservative implementation of SPH (Debugging routines) - * - * The thermal variable is the internal energy (u). Simple constant - * viscosity term without switches is implemented. No thermal conduction - * term is implemented. - * - * This corresponds to equations (43), (44), (45), (101), (103) and (104) with - * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of - * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3, - * pp. 759-794. + * @brief Debugging routines for planetary SPH. */ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "\n " - "x=[%.6g, %.6g, %.6g], v=[%.3g, %.3g, %.3g], \n " - "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", - 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), - p->force.soundspeed, p->force.v_sig, p->h, p->force.h_dt, - p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] " + "x=[%.6g, %.6g, %.6g], v=[%.3g, %.3g, %.3g], " + "a=[%.3g, %.3g, %.3g], " + "m=%.3g, u=%.3g, du/dt=%.3g, P=%.3g, c_s=%.3g, " + "v_sig=%.3g, h=%.3g, dh/dt=%.3g, wcount=%.3g, rho=%.3g, " + "dh_drho=%.3g, time_bin=%d, wakeup=%d, mat_id=%d", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[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), p->force.soundspeed, p->force.v_sig, p->h, + p->force.h_dt, p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, p->limiter_data.wakeup, p->mat_id); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning( + "[PID%lld] " + "v_full=[%.3g, %.3g, %.3g]", + p->id, xp->v_full[0], xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_PLANETARY_HYDRO_DEBUG_H */ diff --git a/src/hydro/Planetary/hydro_iact.h b/src/hydro/Planetary/hydro_iact.h index a19e1b0575b2b3bef13d4e5b8aad12b9e51dad73..455a8def43c4266833356e26fb4db74b5f698a56 100644 --- a/src/hydro/Planetary/hydro_iact.h +++ b/src/hydro/Planetary/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify @@ -37,6 +37,7 @@ #include "const.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -184,6 +185,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->density.rot_v[2] += faci * curlvr[2]; } +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + /** * @brief Force interaction between two particles. * @@ -262,9 +304,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Now construct the full viscosity term */ const float rho_ij = 0.5f * (rhoi + rhoj); @@ -391,12 +431,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float omega_ij = min(dvdr, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Compute sound speeds */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - /* Signal velocity */ - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Construct the full viscosity term */ const float rho_ij = 0.5f * (rhoi + rhoj); diff --git a/src/hydro/Planetary/hydro_io.h b/src/hydro/Planetary/hydro_io.h index 8050f86a713eb1e882b827975a5b6ff24b598f2f..f18e1180066513862470e46f99efd5308f0c4495 100644 --- a/src/hydro/Planetary/hydro_io.h +++ b/src/hydro/Planetary/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify diff --git a/src/hydro/Planetary/hydro_parameters.h b/src/hydro/Planetary/hydro_parameters.h index 75faa1e5a54d4c42b786d047c46af9a1d4993dde..292c23eabf967482810856d3b2cdb89dad31eb57 100644 --- a/src/hydro/Planetary/hydro_parameters.h +++ b/src/hydro/Planetary/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_PLANETARY_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/Planetary/hydro_part.h b/src/hydro/Planetary/hydro_part.h index b11bcd815dfe69b3f3850bcec5301f7d31ec60ad..ccc7ef3b9996ccd544a5b27af0eaca0f052b69c8 100644 --- a/src/hydro/Planetary/hydro_part.h +++ b/src/hydro/Planetary/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify @@ -38,8 +38,10 @@ #include "cooling_struct.h" #include "equation_of_state.h" // For enum material_id #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -77,12 +79,15 @@ struct xpart { /*! Additional data used by the tracers */ struct tracers_xpart_data tracers_data; - /* Additional data used by the star formation */ + /*! Additional data used by the star formation */ struct star_formation_xpart_data sf_data; - /* Additional data used by the feedback */ + /*! Additional data used by the feedback */ struct feedback_part_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /** @@ -183,6 +188,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -192,12 +200,18 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Material identifier flag */ enum eos_planetary_material_id mat_id; /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h index a622b014bc56c750263d3a1de8aacbab746d96e4..143f1abc88456857f9ebdaa30a527914476b3399 100644 --- a/src/hydro/PressureEnergy/hydro.h +++ b/src/hydro/PressureEnergy/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -498,6 +498,28 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -586,6 +608,44 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.div_v += cosmo->H * hydro_dimension; } +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -642,11 +702,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { const float fac_B = cosmo->a_factor_Balsara_eps; @@ -766,13 +827,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -862,6 +924,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -870,7 +933,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/PressureEnergy/hydro_debug.h b/src/hydro/PressureEnergy/hydro_debug.h index 9c9fdd7bd6b3932a3753a92fad6b8e4c5bbcd2e4..44f709fdfed4d7a6895903f4a982b5e5940604a0 100644 --- a/src/hydro/PressureEnergy/hydro_debug.h +++ b/src/hydro/PressureEnergy/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -26,19 +26,25 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "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, 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->rho, - p->density.pressure_bar_dh, p->pressure_bar, p->time_bin, - p->limiter_data.wakeup); + + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e],a=[%.3e,%.3e,%.3e], " + "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e, " + "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, rho=%.3e, " + "p_dh=%.3e, p_bar=%.3e, " + "time_bin=%d wakeup=%d", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[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->rho, p->density.pressure_bar_dh, + p->pressure_bar, p->time_bin, p->limiter_data.wakeup); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEnergy/hydro_iact.h b/src/hydro/PressureEnergy/hydro_iact.h index aad27fcf49f19a6f35234d1800bd57330c863aad..e2cae19b9fb0d9cd5f2d8f483901e116c296dc62 100644 --- a/src/hydro/PressureEnergy/hydro_iact.h +++ b/src/hydro/PressureEnergy/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part* of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include "adiabatic_index.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -181,6 +182,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->density.rot_v[2] += faci * curlvr[2]; } +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) {} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) {} + /** * @brief Force interaction between two particles. * @@ -247,9 +289,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Balsara term */ const float balsara_i = pi->force.balsara; @@ -384,9 +424,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Balsara term */ const float balsara_i = pi->force.balsara; diff --git a/src/hydro/PressureEnergy/hydro_io.h b/src/hydro/PressureEnergy/hydro_io.h index a3e509971a7d238ba0432834fe56b444ca9d6ca9..00cc3e4f0c69536a27db5b68374f444ad11098bb 100644 --- a/src/hydro/PressureEnergy/hydro_io.h +++ b/src/hydro/PressureEnergy/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify diff --git a/src/hydro/PressureEnergy/hydro_parameters.h b/src/hydro/PressureEnergy/hydro_parameters.h index 88051972f258f01e20ebb9bb10de0989cb196161..e9c282ca14bf663cef24252ad6fa4ba379117c21 100644 --- a/src/hydro/PressureEnergy/hydro_parameters.h +++ b/src/hydro/PressureEnergy/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_PRESSURE_ENERGY_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/PressureEnergy/hydro_part.h b/src/hydro/PressureEnergy/hydro_part.h index c0b1a2df48c643600942c669193d5aa1d507a65c..75d5df1835bde62933b590d0606ec7a60a51a130 100644 --- a/src/hydro/PressureEnergy/hydro_part.h +++ b/src/hydro/PressureEnergy/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -35,9 +35,11 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "pressure_floor_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -81,6 +83,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /** @@ -185,6 +190,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -197,12 +205,18 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional data used by the pressure floor */ struct pressure_floor_part_data pressure_floor_data; /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h index 47a1cb64e9bce146c408849763a6c7ccdafcbdab..2829053b32da9c25d0956d0de55c6b45355ab0a7 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -481,6 +481,28 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -568,6 +590,44 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.div_v += cosmo->H * hydro_dimension; } +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -624,11 +684,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { const float fac_B = cosmo->a_factor_Balsara_eps; @@ -763,13 +824,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -858,6 +920,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -866,7 +929,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h index 0f0b974252b4d3290366af9e65913ff92b666ee6..a17de436303a4fbf0af753a93bdffc8cb36bc7c3 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -30,19 +30,25 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "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" - "p_dh=%.3e, p_bar=%.3e \n" - "time_bin=%d, wakeup=%d alpha=%.3e\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, + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e], a=[%.3e,%.3e,%.3e], " + "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e, " + "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, " + "p_dh=%.3e, p_bar=%.3e, " + "time_bin=%d, wakeup=%d alpha=%.3e", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[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->density.pressure_bar_dh, p->pressure_bar, p->time_bin, p->limiter_data.wakeup, p->alpha); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_PRESSURE_ENERGY_MORRIS_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h index 1e3d71430abf8f0f2f15e5d31ba5f02bdf0e8dba..745702b15887e5ffcd38c982356525f8a04a2e69 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part* of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -181,6 +181,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->density.rot_v[2] += faci * curlvr[2]; } +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) {} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) {} + /** * @brief Force interaction between two particles. * diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h index e012df517bc93bc19b4fad95cc46b89fcda5c348..601a99b2d295506f5a7003776add7e2fe33d7d69 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h index cddb33500efd4777b7bc13259de139ba5df7fa5c..d337c482f9190bae3ca5b016703765c9db8fcb65 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_PRESSURE_ENERGY_MORRIS_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h index 942c136d176b961f52ab1c955d75f30a7622df0a..aae8ac721e462c916e003b3086ab127c76a928d7 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) & * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -36,8 +36,10 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -81,6 +83,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /** @@ -185,6 +190,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -197,9 +205,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h index 134bb87449d134c1a6dabca424921e7675305848..3939c59bc8fe2c418e734521ef3bb2e73f862269 100644 --- a/src/hydro/PressureEntropy/hydro.h +++ b/src/hydro/PressureEntropy/hydro.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -423,6 +423,28 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -509,6 +531,44 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.div_v *= h_inv_dim_plus_one * rho_inv; } +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) {} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do in this scheme as the gradient loop is not used. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) {} + /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * @@ -559,11 +619,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { const float fac_mu = cosmo->a_factor_mu; @@ -688,13 +749,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -770,13 +832,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param p The particle to act upon * @param xp The particle extended data to act upon * @param dt_therm The time-step for this kick (for thermodynamic quantities) + * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). + * @param dt_hydro The time-step for this kick (for hydro quantities). + * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. * @param hydro_props The constants used in the scheme * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/PressureEntropy/hydro_debug.h b/src/hydro/PressureEntropy/hydro_debug.h index c2c250c11f6f932c665c37f6f1c78bff8d3444d1..117519404f5e2e4fbbb31efb6dcb3cd23bb2328c 100644 --- a/src/hydro/PressureEntropy/hydro_debug.h +++ b/src/hydro/PressureEntropy/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -31,19 +31,25 @@ */ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "x=[%.3e,%.3e,%.3e], " - "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e],\n " + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e], a=[%.3e,%.3e,%.3e], " "h=%.3e, wcount=%.3f, wcount_dh=%.3e, m=%.3e, dh_drho=%.3e, rho=%.3e, " "rho_bar=%.3e, P=%.3e, dP_dh=%.3e, P_over_rho2=%.3e, S=%.3e, S^1/g=%.3e, " - "dS/dt=%.3e,\nc=%.3e v_sig=%e dh/dt=%.3e 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->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, - p->rho, p->rho_bar, hydro_get_comoving_pressure(p), - p->density.pressure_dh, p->force.P_over_rho2, p->entropy, - p->entropy_one_over_gamma, p->entropy_dt, p->force.soundspeed, - p->force.v_sig, p->force.h_dt, p->time_bin, p->limiter_data.wakeup); + "dS/dt=%.3e, c=%.3e v_sig=%e dh/dt=%.3e time_bin=%d wakeup=%d", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, p->density.wcount, + p->density.wcount_dh, p->mass, p->density.rho_dh, p->rho, p->rho_bar, + hydro_get_comoving_pressure(p), p->density.pressure_dh, + p->force.P_over_rho2, p->entropy, p->entropy_one_over_gamma, + p->entropy_dt, p->force.soundspeed, p->force.v_sig, p->force.h_dt, + p->time_bin, p->limiter_data.wakeup); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEntropy/hydro_iact.h b/src/hydro/PressureEntropy/hydro_iact.h index 97d9fd51e2e63d119cda0f7fe0943fa5bc43c6f5..7338e87da9b4ec09f7d97d5cb1de4ec867e36b76 100644 --- a/src/hydro/PressureEntropy/hydro_iact.h +++ b/src/hydro/PressureEntropy/hydro_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,6 +20,7 @@ #define SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H #include "hydro_parameters.h" +#include "signal_velocity.h" /** * @file PressureEntropy/hydro_iact.h @@ -187,6 +188,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->density.rot_v[2] += fac * curlvr[2]; } +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * Nothing to do here in this scheme. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) {} + /** * @brief Force interaction between two particles. * @@ -246,10 +288,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float S_gamma_i = pi->entropy_one_over_gamma; const float S_gamma_j = pj->entropy_one_over_gamma; - /* Compute sound speeds */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + @@ -264,7 +302,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Now construct the full viscosity term */ const float rho_ij = 0.5f * (rhoi + rhoj); @@ -360,10 +398,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float S_gamma_i = pi->entropy_one_over_gamma; const float S_gamma_j = pj->entropy_one_over_gamma; - /* Compute sound speeds */ - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; - /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + @@ -378,7 +412,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Now construct the full viscosity term */ const float rho_ij = 0.5f * (rhoi + rhoj); diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h index ad297d9b0982267391fa1bf6210f6f0043b28d60..99defbc1f1ce6b86b28c9eadf265d771b20de724 100644 --- a/src/hydro/PressureEntropy/hydro_io.h +++ b/src/hydro/PressureEntropy/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/hydro/PressureEntropy/hydro_parameters.h b/src/hydro/PressureEntropy/hydro_parameters.h index 124d9ec78f21666fad5356ae906bf52c48df6684..8eb336986d809467414f3517d55ad3f06db29220 100644 --- a/src/hydro/PressureEntropy/hydro_parameters.h +++ b/src/hydro/PressureEntropy/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_PRESSURE_ENTROPY_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h index 7ddfe212d0f130fa3a3476a510388edb1725d28a..8665b70098f98df1dbb81a2a0f3dca34b2a21eeb 100644 --- a/src/hydro/PressureEntropy/hydro_part.h +++ b/src/hydro/PressureEntropy/hydro_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -34,8 +34,10 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -73,6 +75,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + } SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ @@ -161,6 +166,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -173,9 +181,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/SPHENIX/hydro.h b/src/hydro/SPHENIX/hydro.h index 5f5396407244f0a4b1724dfad61be786e100315d..cd65b60499d5f6071b4433d886b37412bf5280c5 100644 --- a/src/hydro/SPHENIX/hydro.h +++ b/src/hydro/SPHENIX/hydro.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -474,6 +474,50 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( return dt_cfl; } +/** + * @brief Compute the signal velocity between two gas particles + * + * This is eq. (103) of Price D., JCoPh, 2012, Vol. 231, Issue 3. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + return ci + cj - beta * mu_ij; +} + +/** + * @brief returns the signal velocity + * + * @brief p the particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_signal_velocity( + const struct part *restrict p) { + + return p->viscosity.v_sig; +} + +/** + * @brief returns the div_v + * + * @brief p the particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_div_v( + const struct part *restrict p) { + + return p->viscosity.div_v; +} + /** * @brief Does some extra hydro operations once the actual physical time step * for the particle is known. @@ -770,11 +814,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { /* Here we need to update the artificial viscosity */ @@ -951,13 +996,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param xp The extended data of the particle. * @param dt_drift The drift time-step for positions. * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. * @param cosmo The cosmological model. * @param hydro_props The properties of the hydro scheme. * @param floor_props The properties of the entropy floor. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part *restrict p, const struct xpart *restrict xp, float dt_drift, - float dt_therm, const struct cosmology *cosmo, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { @@ -1038,6 +1084,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). * @param dt_hydro The time-step for this kick (for hydro quantities). * @param dt_kick_corr The time-step for this kick (for gravity corrections). * @param cosmo The cosmological model. @@ -1046,7 +1093,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( struct part *restrict p, struct xpart *restrict xp, float dt_therm, - float dt_grav, float dt_hydro, float dt_kick_corr, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { diff --git a/src/hydro/SPHENIX/hydro_csds.h b/src/hydro/SPHENIX/hydro_csds.h index f981a3cb30e9efbc5eb6a8407bd1b32820a9af95..e593ab97b026b0fd7ca9cd9ceb5737e3ff562b61 100644 --- a/src/hydro/SPHENIX/hydro_csds.h +++ b/src/hydro/SPHENIX/hydro_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/hydro/SPHENIX/hydro_debug.h b/src/hydro/SPHENIX/hydro_debug.h index 62d134f4e19411be3405bd29453bf2f20592fa8e..b92c599a809f1b45d9793e96aa30dfb396550300 100644 --- a/src/hydro/SPHENIX/hydro_debug.h +++ b/src/hydro/SPHENIX/hydro_debug.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -29,18 +29,24 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "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" - "alpha=%.3e \n" - "time_bin=%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->viscosity.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, + warning("[PID%lld] part:", p->id); + warning( + "[PID%lld] x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e], a=[%.3e,%.3e,%.3e], " + "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e, " + "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, " + "alpha=%.3e, " + "time_bin=%d", + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, + p->viscosity.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->viscosity.alpha, p->time_bin); + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } } #endif /* SWIFT_SPHENIX_HYDRO_DEBUG_H */ diff --git a/src/hydro/SPHENIX/hydro_iact.h b/src/hydro/SPHENIX/hydro_iact.h index 54d012e2cda230df081c20fa3c96be78cd460c5f..d8fdc7115860055cf96c7c7a4ab1f292c5472cd1 100644 --- a/src/hydro/SPHENIX/hydro_iact.h +++ b/src/hydro/SPHENIX/hydro_iact.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -29,6 +29,7 @@ #include "adiabatic_index.h" #include "hydro_parameters.h" #include "minmax.h" +#include "signal_velocity.h" /** * @brief Density interaction between two particles. @@ -202,8 +203,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -221,7 +220,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -283,8 +283,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; - const float ci = pi->force.soundspeed; - const float cj = pj->force.soundspeed; /* Cosmology terms for the signal velocity */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); @@ -302,7 +300,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Signal velocity */ - const float new_v_sig = ci + cj - const_viscosity_beta * mu_ij; + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -392,8 +391,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float v_sig = pi->force.soundspeed + pj->force.soundspeed - - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Variable smoothing length term */ const float f_ij = 1.f - pi->force.f / mj; @@ -538,8 +536,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float v_sig = pi->force.soundspeed + pj->force.soundspeed - - const_viscosity_beta * mu_ij; + const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Variable smoothing length term */ const float f_ij = 1.f - pi->force.f / mj; diff --git a/src/hydro/SPHENIX/hydro_io.h b/src/hydro/SPHENIX/hydro_io.h index 76b1aaa33d7be02e89991c0ab371c579c00d7615..37396f1dbe4ff3fa48580b976505cab0f93af956 100644 --- a/src/hydro/SPHENIX/hydro_io.h +++ b/src/hydro/SPHENIX/hydro_io.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -182,7 +182,8 @@ INLINE static void hydro_write_particles(const struct part* parts, struct io_props* list, int* num_fields) { - *num_fields = 14; + *num_fields = 15; + /* List what we want to write */ list[0] = io_make_output_field_convert_part( "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, @@ -253,6 +254,11 @@ INLINE static void hydro_write_particles(const struct part* parts, "cosmology as this includes a Hubble flow term. To get back to a " "peculiar velocity divergence time differential, x_pec = a^4 (x - a^{-2} " "n_D dH / dt)"); + + list[14] = 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"); } /** diff --git a/src/hydro/SPHENIX/hydro_parameters.h b/src/hydro/SPHENIX/hydro_parameters.h index 06b77e9eecaebccea5fc444e21772025d961af87..277be9562dd079d160ca1a9d5ab7208551c1e276 100644 --- a/src/hydro/SPHENIX/hydro_parameters.h +++ b/src/hydro/SPHENIX/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_SPHENIX_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/SPHENIX/hydro_part.h b/src/hydro/SPHENIX/hydro_part.h index f23b6d59d14759cfeab03f7cac865c61df93c649..bbc3bd717d205e5799bce97561fdf757e421a836 100644 --- a/src/hydro/SPHENIX/hydro_part.h +++ b/src/hydro/SPHENIX/hydro_part.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * 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 @@ -31,9 +31,11 @@ #include "cooling_struct.h" #include "csds.h" #include "feedback_struct.h" +#include "mhd_struct.h" #include "particle_splitting_struct.h" #include "pressure_floor_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -77,6 +79,9 @@ struct xpart { /* Additional data used by the feedback */ struct feedback_xpart_data feedback_data; + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + #ifdef WITH_CSDS /* Additional data for the particle csds */ struct csds_part_data csds_data; @@ -210,6 +215,9 @@ struct part { } force; }; + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + /*! Chemistry information */ struct chemistry_part_data chemistry_data; @@ -222,12 +230,18 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional data used by the pressure floor */ struct pressure_floor_part_data pressure_floor_data; /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h index ae1a5cb2cb0e73a5c32ecca285455d8a31e54a27..a4f88d08e97a86461f975ef7c8a9a47db20db9c7 100644 --- a/src/hydro/Shadowswift/hydro.h +++ b/src/hydro/Shadowswift/hydro.h @@ -302,11 +302,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * @param hydro_props Hydrodynamic properties. * @param dt_alpha The time-step used to evolve non-cosmological quantities such * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part* restrict p, struct xpart* restrict xp, const struct cosmology* cosmo, const struct hydro_props* hydro_props, - const float dt_alpha) { + const float dt_alpha, const float dt_therm) { /* Initialize time step criterion variables */ p->timestepvars.vmax = 0.0f; diff --git a/src/hydro/Shadowswift/hydro_debug.h b/src/hydro/Shadowswift/hydro_debug.h index 3ee8dddc54bf16b8fc5c07f8924b4a5e2a6bd45c..91ea90ef82880e51e0947090e48f2cb3c6927225 100644 --- a/src/hydro/Shadowswift/hydro_debug.h +++ b/src/hydro/Shadowswift/hydro_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -19,8 +19,8 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { - printf( - "x=[%.16e,%.16e,%.16e], " + warning( + "[PID%lld] x=[%.16e,%.16e,%.16e], " "v=[%.3e,%.3e,%.3e], " "a=[%.3e,%.3e,%.3e], " "time_bin=%d, " @@ -48,24 +48,24 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "density={" "wcount_dh=%.3e, " "wcount=%.3e}", - p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->a_hydro[0], - p->a_hydro[1], p->a_hydro[2], p->time_bin, p->limiter_data.wakeup, p->h, - p->primitives.v[0], p->primitives.v[1], p->primitives.v[2], - p->primitives.rho, p->primitives.P, p->primitives.gradients.rho[0], - p->primitives.gradients.rho[1], p->primitives.gradients.rho[2], - p->primitives.gradients.v[0][0], p->primitives.gradients.v[0][1], - p->primitives.gradients.v[0][2], p->primitives.gradients.v[1][0], - p->primitives.gradients.v[1][1], p->primitives.gradients.v[1][2], - p->primitives.gradients.v[2][0], p->primitives.gradients.v[2][1], - p->primitives.gradients.v[2][2], p->primitives.gradients.P[0], - p->primitives.gradients.P[1], p->primitives.gradients.P[2], - p->primitives.limiter.rho[0], p->primitives.limiter.rho[1], - p->primitives.limiter.v[0][0], p->primitives.limiter.v[0][1], - p->primitives.limiter.v[1][0], p->primitives.limiter.v[1][1], - p->primitives.limiter.v[2][0], p->primitives.limiter.v[2][1], - p->primitives.limiter.P[0], p->primitives.limiter.P[1], - p->primitives.limiter.maxr, p->conserved.momentum[0], - p->conserved.momentum[1], p->conserved.momentum[2], p->conserved.mass, - p->conserved.energy, p->timestepvars.vmax, p->density.wcount_dh, - p->density.wcount); + p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->time_bin, + p->limiter_data.wakeup, p->h, p->primitives.v[0], p->primitives.v[1], + p->primitives.v[2], p->primitives.rho, p->primitives.P, + p->primitives.gradients.rho[0], p->primitives.gradients.rho[1], + p->primitives.gradients.rho[2], p->primitives.gradients.v[0][0], + p->primitives.gradients.v[0][1], p->primitives.gradients.v[0][2], + p->primitives.gradients.v[1][0], p->primitives.gradients.v[1][1], + p->primitives.gradients.v[1][2], p->primitives.gradients.v[2][0], + p->primitives.gradients.v[2][1], p->primitives.gradients.v[2][2], + p->primitives.gradients.P[0], p->primitives.gradients.P[1], + p->primitives.gradients.P[2], p->primitives.limiter.rho[0], + p->primitives.limiter.rho[1], p->primitives.limiter.v[0][0], + p->primitives.limiter.v[0][1], p->primitives.limiter.v[1][0], + p->primitives.limiter.v[1][1], p->primitives.limiter.v[2][0], + p->primitives.limiter.v[2][1], p->primitives.limiter.P[0], + p->primitives.limiter.P[1], p->primitives.limiter.maxr, + p->conserved.momentum[0], p->conserved.momentum[1], + p->conserved.momentum[2], p->conserved.mass, p->conserved.energy, + p->timestepvars.vmax, p->density.wcount_dh, p->density.wcount); } diff --git a/src/hydro/Shadowswift/hydro_parameters.h b/src/hydro/Shadowswift/hydro_parameters.h index 20c05ff5ed5199e671aaca45d984c248f5fa05f2..4db2a31bbdc08cbc490ffa308105a0a4fc9783f8 100644 --- a/src/hydro/Shadowswift/hydro_parameters.h +++ b/src/hydro/Shadowswift/hydro_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) * * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_SHADOWSWIFT_HYDRO_PARAMETERS_H /* Configuration file */ -#include "config.h" +#include <config.h> /* Global headers */ #if defined(HAVE_HDF5) diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h index 7b87868a3901c168da001c4eeeac6737b2650736..397dba20d6b0d05fd473f6c1dc9c519cb8777493 100644 --- a/src/hydro/Shadowswift/hydro_part.h +++ b/src/hydro/Shadowswift/hydro_part.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * This program is free software: you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include "feedback_struct.h" #include "particle_splitting_struct.h" #include "rt_struct.h" +#include "sink_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" #include "voronoi_cell.h" @@ -199,9 +200,15 @@ struct part { /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + /*! Additional Radiative Transfer Data */ struct rt_part_data rt_data; + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro_csds.h b/src/hydro_csds.h index 0bf3065778a04ce4813e1e8ba7b6413df7ca1a8d..96cb374423a438c5c42ea1f6a284c6d06f6815b6 100644 --- a/src/hydro_csds.h +++ b/src/hydro_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -20,7 +20,7 @@ #define SWIFT_HYDRO_CSDS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "align.h" diff --git a/src/hydro_io.h b/src/hydro_io.h index 904bf73962c3128feaf80f2638cbbab31696cd8e..c0f82c2e7b4f52adc870b59f41d12c0e14e84a46 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_HYDRO_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right functions */ #if defined(NONE_SPH) diff --git a/src/hydro_parameters.h b/src/hydro_parameters.h index 8cbdbb08a76067f11d2f00c51cc3ee52411aef2c..5d1f3909632ac055b00587c01c276242b7bf76f9 100644 --- a/src/hydro_parameters.h +++ b/src/hydro_parameters.h @@ -26,7 +26,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "parser.h" @@ -62,4 +62,10 @@ #error "Invalid choice of SPH variant" #endif +#if defined(NONE_MHD) +#include "./mhd/None/mhd_parameters.h" +#else +#error "Invalid choice of MHD variant" +#endif + #endif /* SWIFT_HYDRO_PARAMETERS_H */ diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 754f963d2ccf2c11bfc557f3fc7863e2ec92b26f..c160e6c9891f4729907465c5f54c6a9477a74d96 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -33,6 +33,7 @@ #include "gravity_properties.h" #include "hydro.h" #include "kernel_hydro.h" +#include "mhd.h" #include "parser.h" #include "pressure_floor.h" #include "units.h" @@ -219,9 +220,6 @@ 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); @@ -273,6 +271,9 @@ void hydro_props_print(const struct hydro_props *p) { /* Same for the diffusion */ diffusion_print(&(p->diffusion)); + /* Same for MHD */ + message("MHD scheme: %s.", MHD_IMPLEMENTATION); + // MATTHIEU: Temporary location for this planetary SPH i/o business. #ifdef PLANETARY_SPH @@ -298,6 +299,7 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { io_write_attribute_s(h_grpsph, "Neighbour number definition", "unweighted"); } io_write_attribute_s(h_grpsph, "Scheme", SPH_IMPLEMENTATION); + io_write_attribute_s(h_grpsph, "MHD Scheme", MHD_IMPLEMENTATION); io_write_attribute_s(h_grpsph, "Kernel function", kernel_name); io_write_attribute_f(h_grpsph, "Kernel target N_ngb", p->target_neighbours); io_write_attribute_f(h_grpsph, "Kernel delta N_ngb", p->delta_neighbours); diff --git a/src/hydro_properties.h b/src/hydro_properties.h index a117f3b84c63e83f0fb85a3677fc46bb2e2f5e08..b86329737edaf7964367bf1260eefd0ea63a57b0 100644 --- a/src/hydro_properties.h +++ b/src/hydro_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) #include <hdf5.h> @@ -130,6 +130,11 @@ struct hydro_props { /*! Thermal diffusion parameters */ struct diffusion_global_data diffusion; + + /* ------ MHD properties ------------------------- */ + + /*! MHD parameters */ + struct mhd_global_data mhd; }; void hydro_props_print(const struct hydro_props *p); diff --git a/src/hydro_space.h b/src/hydro_space.h index e64b532d16a5b78526dfed22d99558a768d07400..6cda07e789e27583cbbdfd1842d5438683d8516d 100644 --- a/src/hydro_space.h +++ b/src/hydro_space.h @@ -19,7 +19,7 @@ #ifndef SWIFT_HYDRO_SPACE_H #define SWIFT_HYDRO_SPACE_H -#include "../config.h" +#include <config.h> struct space; diff --git a/src/ic_info.c b/src/ic_info.c index a8db918f1db781659a51c3ebaa7fe71ef4de2070..65ac006baecf3ae2022ac6e1818a1ed6c6dc9c0a 100644 --- a/src/ic_info.c +++ b/src/ic_info.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdio.h> diff --git a/src/ic_info.h b/src/ic_info.h index afcbd9383baed933a84f87c042a4bccd49749daa..1bbe4712c06bd59673a8d1230150b88d822399bd 100644 --- a/src/ic_info.h +++ b/src/ic_info.h @@ -21,7 +21,7 @@ #define SWIFT_IC_INFO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdio.h> diff --git a/src/inline.h b/src/inline.h index 7477153b5ba3b3a42a63982292909dd3f3a396d0..0d5c09481837972e19df38a91ec6949b58c72329 100644 --- a/src/inline.h +++ b/src/inline.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -21,7 +21,7 @@ #define SWIFT_INLINE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /** * @brief Defines inline diff --git a/src/integer_power.h b/src/integer_power.h index 3aec75fd4f828aedd800512060edccf8d18cb8da..49a917572be9d62a29e3ac01645d0615af7facd1 100644 --- a/src/integer_power.h +++ b/src/integer_power.h @@ -20,7 +20,7 @@ #define SWIFT_INTEGER_POWER_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "error.h" diff --git a/src/intrinsics.h b/src/intrinsics.h index 7e3b9108248ddd43303a9103394d818384a9b664..6bcf7461bf1254f356ba7cffb8fa4c7db60364aa 100644 --- a/src/intrinsics.h +++ b/src/intrinsics.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2015 Matthieu Schaller matthieu.schaller@durham.ac.uk) + * Copyright (c) 2015 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 @@ -20,7 +20,7 @@ #define SWIFT_INTRINSICS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" diff --git a/src/io_compression.c b/src/io_compression.c index eac0349f3ee33d7145468e0b9235c45916d42677..649674cd6cfb294825e9cf66ce745e6ae3af5d68 100644 --- a/src/io_compression.c +++ b/src/io_compression.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "io_compression.h" @@ -37,8 +37,8 @@ const char* lossy_compression_schemes_names[compression_level_count] = { "off", "on", "DScale1", "DScale2", "DScale3", "DScale4", "DScale5", "DScale6", "DMantissa9", "DMantissa13", - "FMantissa9", "FMantissa13", "HalfFloat", "BFloat16", "Nbit36", - "Nbit40", "Nbit44", "Nbit48", "Nbit56"}; + "FMantissa9", "FMantissa13", "HalfFloat", "BFloat16", "Nbit32", + "Nbit36", "Nbit40", "Nbit44", "Nbit48", "Nbit56"}; /** * @brief Returns the lossy compression scheme given its name @@ -481,13 +481,16 @@ void set_hdf5_lossy_compression(hid_t* h_prop, hid_t* h_type, } else if (comp == compression_write_Nbit_36 || + comp == compression_write_Nbit_32 || comp == compression_write_Nbit_40 || comp == compression_write_Nbit_44 || comp == compression_write_Nbit_48 || comp == compression_write_Nbit_56) { int n_bits = 0; - if (comp == compression_write_Nbit_36) + if (comp == compression_write_Nbit_32) + n_bits = 32; + else if (comp == compression_write_Nbit_36) n_bits = 36; else if (comp == compression_write_Nbit_40) n_bits = 40; diff --git a/src/io_compression.h b/src/io_compression.h index dd5a1612789dd53421a9f837e8ea77d19924efeb..5ebc68caea1c91489ea7ce3dea871c8938158acf 100644 --- a/src/io_compression.h +++ b/src/io_compression.h @@ -20,7 +20,7 @@ #define SWIFT_IO_COMPRESSION_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /** * @brief Compression levels for snapshot fields @@ -40,6 +40,7 @@ enum lossy_compression_schemes { compression_write_f_mantissa_13, /*!< Conversion to 13-bits mantissa float */ compression_write_half_float, /*!< Conversion to IEEE754 half-float */ compression_write_bfloat_16, /*!< Conversion to Bfloat16 */ + compression_write_Nbit_32, /*!< Conversion to 32-bit int (from long long) */ compression_write_Nbit_36, /*!< Conversion to 36-bit int (from long long) */ compression_write_Nbit_40, /*!< Conversion to 40-bit int (from long long) */ compression_write_Nbit_44, /*!< Conversion to 44-bit int (from long long) */ diff --git a/src/io_properties.h b/src/io_properties.h index 95387e5cec3a06d5076ec7ae57a277e4714bcb59..5c69a6ad760e7eb45a1fd5f1eda25a9427f08a4b 100644 --- a/src/io_properties.h +++ b/src/io_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_IO_PROPERTIES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "common_io.h" @@ -40,53 +40,55 @@ enum DATA_IMPORTANCE { COMPULSORY = 1, OPTIONAL = 0, UNUSED = -1 }; /* Helper typedefs */ -typedef void (*conversion_func_part_float)(const struct engine*, - const struct part*, - const struct xpart*, float*); -typedef void (*conversion_func_part_int)(const struct engine*, - const struct part*, - const struct xpart*, int*); -typedef void (*conversion_func_part_double)(const struct engine*, - const struct part*, - const struct xpart*, double*); -typedef void (*conversion_func_part_long_long)(const struct engine*, - const struct part*, - const struct xpart*, long long*); -typedef void (*conversion_func_gpart_float)(const struct engine*, - const struct gpart*, float*); -typedef void (*conversion_func_gpart_int)(const struct engine*, - const struct gpart*, int*); -typedef void (*conversion_func_gpart_double)(const struct engine*, - const struct gpart*, double*); -typedef void (*conversion_func_gpart_long_long)(const struct engine*, - const struct gpart*, - long long*); -typedef void (*conversion_func_spart_float)(const struct engine*, - const struct spart*, float*); -typedef void (*conversion_func_spart_int)(const struct engine*, - const struct spart*, int*); -typedef void (*conversion_func_spart_double)(const struct engine*, - const struct spart*, double*); -typedef void (*conversion_func_spart_long_long)(const struct engine*, - const struct spart*, - long long*); -typedef void (*conversion_func_bpart_float)(const struct engine*, - const struct bpart*, float*); -typedef void (*conversion_func_bpart_int)(const struct engine*, - const struct bpart*, int*); -typedef void (*conversion_func_bpart_double)(const struct engine*, - const struct bpart*, double*); -typedef void (*conversion_func_bpart_long_long)(const struct engine*, - const struct bpart*, - long long*); -typedef void (*conversion_func_sink_float)(const struct engine*, - const struct sink*, float*); -typedef void (*conversion_func_sink_int)(const struct engine*, - const struct sink*, int*); -typedef void (*conversion_func_sink_double)(const struct engine*, - const struct sink*, double*); -typedef void (*conversion_func_sink_long_long)(const struct engine*, - const struct sink*, long long*); +typedef void (*conversion_func_part_float)(const struct engine *, + const struct part *, + const struct xpart *, float *); +typedef void (*conversion_func_part_int)(const struct engine *, + const struct part *, + const struct xpart *, int *); +typedef void (*conversion_func_part_double)(const struct engine *, + const struct part *, + const struct xpart *, double *); +typedef void (*conversion_func_part_long_long)(const struct engine *, + const struct part *, + const struct xpart *, + long long *); +typedef void (*conversion_func_gpart_float)(const struct engine *, + const struct gpart *, float *); +typedef void (*conversion_func_gpart_int)(const struct engine *, + const struct gpart *, int *); +typedef void (*conversion_func_gpart_double)(const struct engine *, + const struct gpart *, double *); +typedef void (*conversion_func_gpart_long_long)(const struct engine *, + const struct gpart *, + long long *); +typedef void (*conversion_func_spart_float)(const struct engine *, + const struct spart *, float *); +typedef void (*conversion_func_spart_int)(const struct engine *, + const struct spart *, int *); +typedef void (*conversion_func_spart_double)(const struct engine *, + const struct spart *, double *); +typedef void (*conversion_func_spart_long_long)(const struct engine *, + const struct spart *, + long long *); +typedef void (*conversion_func_bpart_float)(const struct engine *, + const struct bpart *, float *); +typedef void (*conversion_func_bpart_int)(const struct engine *, + const struct bpart *, int *); +typedef void (*conversion_func_bpart_double)(const struct engine *, + const struct bpart *, double *); +typedef void (*conversion_func_bpart_long_long)(const struct engine *, + const struct bpart *, + long long *); +typedef void (*conversion_func_sink_float)(const struct engine *, + const struct sink *, float *); +typedef void (*conversion_func_sink_int)(const struct engine *, + const struct sink *, int *); +typedef void (*conversion_func_sink_double)(const struct engine *, + const struct sink *, double *); +typedef void (*conversion_func_sink_long_long)(const struct engine *, + const struct sink *, + long long *); /** * @brief The properties of a given dataset for i/o @@ -118,31 +120,31 @@ struct io_props { float scale_factor_exponent; /* Pointer to the field of the first particle in the array */ - char* field; + char *field; /* Lossy compression scheme to use for this field */ enum lossy_compression_schemes lossy_compression; /* Pointer to the start of the temporary buffer used in i/o */ - char* start_temp_c; - int* start_temp_i; - float* start_temp_f; - double* start_temp_d; - long long* start_temp_l; + char *start_temp_c; + int *start_temp_i; + float *start_temp_f; + double *start_temp_d; + long long *start_temp_l; /* Pointer to the engine */ - const struct engine* e; + const struct engine *e; /* The size of the particles */ size_t partSize; /* The particle arrays */ - const struct part* parts; - const struct xpart* xparts; - const struct gpart* gparts; - const struct spart* sparts; - const struct bpart* bparts; - const struct sink* sinks; + const struct part *parts; + const struct xpart *xparts; + const struct gpart *gparts; + const struct spart *sparts; + const struct bpart *bparts; + const struct sink *sinks; /* Are we converting? */ int conversion; @@ -178,6 +180,20 @@ struct io_props { conversion_func_sink_long_long convert_sink_l; }; +/** + * @brief Copies a string safely (avoids buffer overrun). + * + * @param dst Pointer to the destination array where the content is to be + * copied. + * @param src String to copy. + * @param dst_len Length of the destination array. + */ +INLINE static void safe_strcpy(char *restrict dst, const char *restrict src, + size_t dst_len) { + strncpy(dst, src, dst_len - 1); + dst[dst_len - 1] = '\0'; +} + /** * @brief Constructs an #io_props from its parameters * @@ -191,7 +207,7 @@ struct io_props { */ #define io_make_input_field(name, type, dim, importance, units, part, field) \ io_make_input_field_(name, type, dim, importance, units, \ - (char*)(&(part[0]).field), sizeof(part[0]), 0.) + (char *)(&(part[0]).field), sizeof(part[0]), 0.) /** * @brief Constructs an #io_props from its parameters with a user-defined @@ -210,7 +226,7 @@ struct io_props { #define io_make_input_field_default(name, type, dim, importance, units, part, \ field, def) \ io_make_input_field_(name, type, dim, importance, units, \ - (char*)(&(part[0]).field), sizeof(part[0]), def) + (char *)(&(part[0]).field), sizeof(part[0]), def) /** * @brief Construct an #io_props from its parameters @@ -226,13 +242,13 @@ struct io_props { * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_input_field_( - const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char *name, enum IO_DATA_TYPE type, int dimension, enum DATA_IMPORTANCE importance, enum unit_conversion_factor units, - char* field, size_t partSize, const float default_value) { + char *field, size_t partSize, const float default_value) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); r.type = type; r.dimension = dimension; r.importance = importance; @@ -256,7 +272,7 @@ INLINE static struct io_props io_make_input_field_( #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) + (char *)(&(part[0]).field), sizeof(part[0]), desc) /** * @brief Construct an #io_props from its parameters @@ -273,18 +289,18 @@ INLINE static struct io_props io_make_input_field_( * 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, float a_exponent, char* field, - size_t partSize, const char description[DESCRIPTION_BUFFER_SIZE]) { + const char *name, enum IO_DATA_TYPE type, int dimension, + enum unit_conversion_factor units, float a_exponent, char *field, + size_t partSize, const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -324,20 +340,19 @@ INLINE static struct io_props io_make_output_field_( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, float a_exponent, size_t partSize, - const struct part* parts, const struct xpart* xparts, - conversion_func_part_int functionPtr, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct part *parts, const struct xpart *xparts, + conversion_func_part_int functionPtr, const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -370,20 +385,19 @@ INLINE static struct io_props io_make_output_field_convert_part_INT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, float a_exponent, size_t partSize, - const struct part* parts, const struct xpart* xparts, - conversion_func_part_float functionPtr, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct part *parts, const struct xpart *xparts, + conversion_func_part_float functionPtr, const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -416,20 +430,19 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, float a_exponent, size_t partSize, - const struct part* parts, const struct xpart* xparts, - conversion_func_part_double functionPtr, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct part *parts, const struct xpart *xparts, + conversion_func_part_double functionPtr, const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -462,20 +475,19 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct part *parts, const struct xpart *xparts, + conversion_func_part_long_long functionPtr, const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -516,19 +528,19 @@ INLINE static struct io_props io_make_output_field_convert_part_LONGLONG( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct gpart *gparts, conversion_func_gpart_int functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -559,19 +571,19 @@ INLINE static struct io_props io_make_output_field_convert_gpart_INT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct gpart *gparts, conversion_func_gpart_float functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -602,19 +614,19 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct gpart *gparts, conversion_func_gpart_double functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -645,19 +657,19 @@ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct gpart *gparts, conversion_func_gpart_long_long functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -697,19 +709,19 @@ INLINE static struct io_props io_make_output_field_convert_gpart_LONGLONG( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct spart *sparts, conversion_func_spart_int functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -740,19 +752,19 @@ INLINE static struct io_props io_make_output_field_convert_spart_INT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct spart *sparts, conversion_func_spart_float functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -783,19 +795,19 @@ INLINE static struct io_props io_make_output_field_convert_spart_FLOAT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct spart *sparts, conversion_func_spart_double functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -826,19 +838,19 @@ INLINE static struct io_props io_make_output_field_convert_spart_DOUBLE( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct spart *sparts, conversion_func_spart_long_long functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -878,19 +890,19 @@ INLINE static struct io_props io_make_output_field_convert_spart_LONGLONG( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct bpart *bparts, conversion_func_bpart_int functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -921,19 +933,19 @@ INLINE static struct io_props io_make_output_field_convert_bpart_INT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct bpart *bparts, conversion_func_bpart_float functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -964,19 +976,19 @@ INLINE static struct io_props io_make_output_field_convert_bpart_FLOAT( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct bpart *bparts, conversion_func_bpart_double functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -1007,19 +1019,19 @@ INLINE static struct io_props io_make_output_field_convert_bpart_DOUBLE( * 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, + const char *name, enum IO_DATA_TYPE type, int dimension, 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]) { + const struct bpart *bparts, conversion_func_bpart_long_long functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -1059,19 +1071,19 @@ INLINE static struct io_props io_make_output_field_convert_bpart_LONGLONG( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_sink_INT( - const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char *name, enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, float a_exponent, size_t sinkSize, - const struct sink* sinks, conversion_func_sink_int functionPtr, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct sink *sinks, conversion_func_sink_int functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -1102,19 +1114,19 @@ INLINE static struct io_props io_make_output_field_convert_sink_INT( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_sink_FLOAT( - const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char *name, enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, float a_exponent, size_t sinkSize, - const struct sink* sinks, conversion_func_sink_float functionPtr, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct sink *sinks, conversion_func_sink_float functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -1145,19 +1157,19 @@ INLINE static struct io_props io_make_output_field_convert_sink_FLOAT( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_sink_DOUBLE( - const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char *name, enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, float a_exponent, size_t sinkSize, - const struct sink* sinks, conversion_func_sink_double functionPtr, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct sink *sinks, conversion_func_sink_double functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; @@ -1188,19 +1200,19 @@ INLINE static struct io_props io_make_output_field_convert_sink_DOUBLE( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_sink_LONGLONG( - const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char *name, enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, float a_exponent, size_t sinkSize, - const struct sink* sinks, conversion_func_sink_long_long functionPtr, - const char description[DESCRIPTION_BUFFER_SIZE]) { + const struct sink *sinks, conversion_func_sink_long_long functionPtr, + const char *description) { struct io_props r; bzero(&r, sizeof(struct io_props)); - strcpy(r.name, name); + safe_strcpy(r.name, name, FIELD_BUFFER_SIZE); if (strlen(description) == 0) { sprintf(r.description, "No description given"); } else { - strcpy(r.description, description); + safe_strcpy(r.description, description, DESCRIPTION_BUFFER_SIZE); } r.type = type; r.dimension = dimension; diff --git a/src/kernel_gravity.h b/src/kernel_gravity.h index 4f6385ca39d823ed2762b4cedf87cbfbe85ee67b..f9af4d71ce38ac8a2c3f5eb81e76aa615d542252 100644 --- a/src/kernel_gravity.h +++ b/src/kernel_gravity.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 diff --git a/src/kernel_hydro.c b/src/kernel_hydro.c index f1b3f7448ce8f2910ed24e468c9be9c9f55bdc1a..ee81be2c0bbd53a13997d386c34abaa5e0f3ec53 100644 --- a/src/kernel_hydro.c +++ b/src/kernel_hydro.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2015 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 diff --git a/src/kernel_hydro.h b/src/kernel_hydro.h index fcb6e91906421a2436f645f06fb03c9116fa4a32..dc0c721e5b17528ad28ebd12a05fa8d8a004420d 100644 --- a/src/kernel_hydro.h +++ b/src/kernel_hydro.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -29,7 +29,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> @@ -317,6 +317,44 @@ __attribute__((always_inline)) INLINE static void kernel_eval( *W = w * kernel_constant * kernel_gamma_inv_dim; } +/** + * @brief Computes the kernel function in double precision. + * + * Required for computing the projected kernel because rounding + * error causes problems for the GSL integration function if + * we evaluate in single precision. + * + * The kernel function needs to be mutliplied by \f$h^{-d}\f$, + * where \f$d\f$ is the dimensionality of the problem. + * + * Returns 0 if \f$u > \gamma = H/h\f$ + * + * @param u The ratio of the distance to the smoothing length \f$u = x/h\f$. + * @param W (return) The value of the kernel function \f$W(x,h)\f$. + */ +__attribute__((always_inline)) INLINE static void kernel_eval_double( + double u, double *restrict W) { + + /* Go to the range [0,1[ from [0,H[ */ + const double x = u * kernel_gamma_inv; + + /* Pick the correct branch of the kernel */ + const int temp = (int)(x * kernel_ivals_f); + const int ind = temp > kernel_ivals ? kernel_ivals : temp; + const float *const coeffs = &kernel_coeffs[ind * (kernel_degree + 1)]; + + /* First two terms of the polynomial ... */ + double w = ((double)coeffs[0]) * x + ((double)coeffs[1]); + + /* ... and the rest of them */ + for (int k = 2; k <= kernel_degree; k++) w = x * w + ((double)coeffs[k]); + + w = max(w, 0.); + + /* Return everything */ + *W = w * ((double)kernel_constant) * ((double)kernel_gamma_inv_dim); +} + /** * @brief Computes the kernel function derivative. * diff --git a/src/kernel_long_gravity.h b/src/kernel_long_gravity.h index 8a3ac794d6cf780f407b2cb91b2a74c6a5601d89..d81e7692ba96d623da32ec0e464f58119c1b82d7 100644 --- a/src/kernel_long_gravity.h +++ b/src/kernel_long_gravity.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_KERNEL_LONG_GRAVITY_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "const.h" diff --git a/src/kick.h b/src/kick.h index 5d92bfcd3270c554271a6a22d082d8eb1a97fa06..d81f8fb5e622d8ffc9a5db36b705c5ca43bb2dd7 100644 --- a/src/kick.h +++ b/src/kick.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,12 +20,13 @@ #define SWIFT_KICK_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "black_holes.h" #include "const.h" #include "debug.h" +#include "mhd.h" #include "rt.h" #include "sink.h" #include "stars.h" @@ -270,8 +271,11 @@ __attribute__((always_inline)) INLINE static void kick_part( * the particle masses in hydro_kick_extra */ rt_kick_extra(p, dt_kick_therm, dt_kick_grav, dt_kick_hydro, dt_kick_corr, cosmo, hydro_props); - hydro_kick_extra(p, xp, dt_kick_therm, dt_kick_grav, dt_kick_hydro, - dt_kick_corr, cosmo, hydro_props, floor_props); + hydro_kick_extra(p, xp, dt_kick_therm, dt_kick_grav, dt_kick_mesh_grav, + dt_kick_hydro, dt_kick_corr, cosmo, hydro_props, + floor_props); + mhd_kick_extra(p, xp, dt_kick_therm, dt_kick_grav, dt_kick_hydro, + dt_kick_corr, cosmo, hydro_props, floor_props); if (p->gpart != NULL) gravity_kick_extra(p->gpart, dt_kick_grav); } diff --git a/src/lightcone/healpix_util.c b/src/lightcone/healpix_util.c new file mode 100644 index 0000000000000000000000000000000000000000..d07aea40befaa252ea65706743099f1f92cd77ed --- /dev/null +++ b/src/lightcone/healpix_util.c @@ -0,0 +1,365 @@ +/******************************************************************************* + * This file is part of SWIFT. + * + * The functions in this file are based on code from the HEALPix + * 3.80 library (see http://healpix.sourceforge.net): + * + * Copyright (C) 1997-2013 Krzysztof M. Gorski, Eric Hivon, + * Benjamin D. Wandelt, Anthony J. Banday, + * Matthias Bartelmann, Hans K. Eriksen, + * Frode K. Hansen, Martin Reinecke + * + * Translated and modified for SWIFT by John Helly: + * + * Copyright (c) 2021 John Helly (j.c.helly@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 "lightcone/healpix_util.h" + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +/** + * @brief Integer modulus function, valid for b > 0 only + * + * Note that result is not the same as a % b for negative + * values of a. If b > 0 then result is in range 0 to b-1 + * inclusive. + * + * @param a the first integer + * @param a the second integer + * + */ +static int mod(int a, int b) { + int r = a % b; + return r < 0 ? r + b : r; +} + +/** + * @brief Given a normalized z coordinate, return the ring + * number in range 1 to 4*nside-1. + * + * @param nside HEALPix resolution parameter + * @param z the z coordinate to check + * + */ +static int ring_num(int nside, double z) { + + /* Equatorial regime */ + int iring = round(nside * (2.0 - 1.5 * z)); + + /* North cap */ + if (z > 2. / 3.) { + iring = round(nside * sqrt(3.0 * (1.0 - z))); + if (iring == 0) iring = 1; + } + + /* South cap */ + if (z < -2. / 3.) { + iring = round(nside * sqrt(3.0 * (1.0 + z))); + if (iring == 0) iring = 1; + iring = 4 * nside - iring; + } + + return iring; +} + +/** + * @brief Return information about the specified HEALPix ring + * + * @param nside HEALPix resolution parameter + * @param ring the ring index in range 1 to 4*Nside-1 + * @param npr returns the number of pixels in the ring + * @param kshift returns the shift of this ring + * @param npnorth returns total number of pixels in this ring + * all rings to the north of this one + * + */ +static void pixels_per_ring(int nside, int ring, int *npr, int *kshift, + long long *npnorth) { + + /* number of pixels in current ring */ + *npr = nside; + if (ring < *npr) *npr = ring; + if (4 * nside - ring < *npr) *npr = 4 * nside - ring; + *npr *= 4; + + /* Shift */ + *kshift = (ring + 1) % 2; /* 1 for even, 0 for odd */ + if (nside == 1) *kshift = 1 - *kshift; /* except for Nside=1 */ + if (*npr < 4 * nside) *kshift = 1; /* 1 on polar cap */ + + /* Number of pixels in current ring and above */ + if (ring <= nside) { + /* in North cap */ + *npnorth = ring * (ring + 1ll) * 2ll; + } else if (ring <= 3 * nside) { + /* in Equatorial region */ + long long ncap = nside * (nside + 1ll) * 2ll; + long long ir = ring - nside; + *npnorth = ncap + 4ll * nside * ir; + } else { + /* in South cap */ + long long npix = (12ll * nside) * nside; + long long ir = 4ll * nside - ring - 1; /* count ring from south */ + *npnorth = npix - ir * (ir + 1ll) * 2ll; + } +} + +/** + * @brief Compute the z coordinate of a HEALPix ring + * + * @param nside HEALPix resolution parameter + * @param ir the ring index in range 1 to 4*Nside-1 + * + */ +static double ring2z(int nside, int ir) { + + double z; + double fn = (double)nside; + if (ir < nside) { + /* north polar cap */ + double tmp = (double)ir; + z = 1.0 - (tmp * tmp) / (3.0 * fn * fn); + } else if (ir < 3 * nside) { + /* tropical band */ + z = ((double)(2 * nside - ir)) * 2.0 / (3.0 * fn); + } else { + /* south polar cap */ + double tmp = (double)(4 * nside - ir); + z = -1.0 + (tmp * tmp) / (3.0 * fn * fn); + } + return z; +} + +/** + * @brief Find pixels with centres within specified radius of + * the given vector + * + * Based on query_disc() from src/f90/mod/pixel_routines.F90. + * Assumes RING indexing and does not support inclusive mode + * (i.e. only returns pixels with centres within radius) + * + * If nr_ranges and range are both not NULL, returns a newly + * allocated array of struct pixel_range with the ranges of + * pixels which overlap the disc. + * + * @param nside HEALPix resolution parameter + * @param vec vector specifying the disc centre + * @param radius the radius to search + * @param pix_min returns minimum pixel index in the disc + * @param pix_max returns maximum pixel index in the disc + * @param nr_ranges returns the size of the range array + * @param range returns a new array of struct pixel_range + * + */ +void healpix_query_disc_range(int nside, double vec[3], double radius, + pixel_index_t *pix_min, pixel_index_t *pix_max, + int *nr_ranges, struct pixel_range **range) { + + /* Get normalized disc centre vector */ + double norm = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); + double x0 = vec[0] / norm; + double y0 = vec[1] / norm; + double z0 = vec[2] / norm; + + /* coordinate z of highest and lowest points in the disc */ + double rlat0 = asin(z0); /* latitude in RAD of the center */ + double rlat1 = rlat0 + radius; + double rlat2 = rlat0 - radius; + double zmin, zmax; + if (rlat1 >= 0.5 * M_PI) { + zmax = 1.0; + } else { + zmax = sin(rlat1); + } + if (rlat2 <= -0.5 * M_PI) { + zmin = -1.0; + } else { + zmin = sin(rlat2); + } + + /* Find which rings overlap the disc */ + int irmin = ring_num(nside, zmax); + if (irmin - 1 > 1) { + irmin = irmin - 1; + } else { + irmin = 1; + } + int irmax = ring_num(nside, zmin); + if (irmax + 1 < 4 * nside - 1) { + irmax = irmax + 1; + } else { + irmax = 4 * nside - 1; + } + + /* Get phi at disc centre */ + double phi0 = 0.0; + if ((x0 != 0) || (y0 != 0)) phi0 = atan2(y0, x0); + + /* Allocate output array: + need to allow for worst case where all rings cross the periodic + boundary and therefore contribute two disjoint ranges of pixels */ + int nout_max = 2 * (irmax - irmin + 1); + if (nout_max < 1) nout_max = 1; + if (nr_ranges && range) { + *range = + (struct pixel_range *)malloc(nout_max * sizeof(struct pixel_range)); + } + + /* Will return min and max pixel indexes in the disk */ + long long npix = (12ll * nside) * nside; + long long pix_min_ll = npix; + long long pix_max_ll = -1; + + /* Now have min/max ring index (in range 1 to 4*nside-1) */ + /* Loop over rings which overlap the disc */ + if (nr_ranges && range) *nr_ranges = 0; + for (int iring = irmin; iring <= irmax; iring += 1) { + + /* Find z coordinate of this ring */ + double z = ring2z(nside, iring); + + /* Find range in phi which overlaps the disc in this ring: + taken from discphirange_at_z() in pix_tools.F90 */ + double cosang = cos(radius); + double a = x0 * x0 + y0 * y0; + double dphi = -1000.0; /* Indicates outside disc */ + double b = cosang - z * z0; + if (a == 0.0) { /* Poles */ + if (b <= 0.0) dphi = M_PI; + } else { + double c = fmax(1.0 - z * z, 1.0e-12); + double cosdphi = b / sqrt(a * c); + if (cosdphi < -1.0) + dphi = M_PI; /* all the pixels at this elevation are in the disc */ + if (fabs(cosdphi) <= 1.0) dphi = acos(cosdphi); /* in [0,Pi] */ + } + + /* Look up number of pixels in this ring */ + int npr, kshift; + long long npnorth; + pixels_per_ring(nside, iring, &npr, &kshift, &npnorth); + + /* For each ring, store the range of pixels which overlaps the disc. + If the disc overlaps the periodic boundary at phi=2pi we need to split + the range into two pieces. */ + int my_low = -1; + int my_hi = -1; + if (dphi > M_PI) { + /* Full ring */ + my_low = 0; + my_hi = npr - 1; + } else if (dphi >= 0.0) { + /* Partial ring */ + double shift = kshift * 0.5; + int iphi_low = ceil(npr * (phi0 - dphi) / (2 * M_PI) - shift); + int iphi_hi = floor(npr * (phi0 + dphi) / (2 * M_PI) - shift); + if (iphi_hi >= iphi_low) { + my_low = mod(iphi_low, npr); + my_hi = mod(iphi_hi, npr); + } + } + if (my_low >= 0) { + long long first; + long long last; + if (my_hi >= my_low) { + /* Not crossing periodic boundary, so we can return a single range */ + first = (npnorth - npr) + my_low; + last = (npnorth - npr) + my_hi; + if (first < pix_min_ll) pix_min_ll = first; + if (last > pix_max_ll) pix_max_ll = last; + if (nr_ranges && range) { + (*range)[*nr_ranges].first = first; + (*range)[*nr_ranges].last = last; + *nr_ranges += 1; + } + } else { + /* Range overlaps periodic boundary, so will be split in two */ + /* Start of ring to my_hi */ + first = (npnorth - npr) + 0; + last = (npnorth - npr) + my_hi; + if (first < pix_min_ll) pix_min_ll = first; + if (nr_ranges && range) { + (*range)[*nr_ranges].first = first; + (*range)[*nr_ranges].last = last; + *nr_ranges += 1; + } + /* my_low to end of ring */ + first = (npnorth - npr) + my_low; + last = (npnorth - npr) + (npr - 1); + if (last > pix_max_ll) pix_max_ll = last; + if (nr_ranges && range) { + (*range)[*nr_ranges].first = first; + (*range)[*nr_ranges].last = last; + *nr_ranges += 1; + } + } + } + /* Next ring */ + } + + /* Return min and max pixel indexes */ + *pix_min = (pixel_index_t)pix_min_ll; + *pix_max = (pixel_index_t)pix_max_ll; +} + +/** + * @brief Make a 3D vector given z and phi coordinates + * + * @param v returns the new vector + * @param z normalized coordinate in the z axis + * @param phi angular coordinate + * + */ +static void set_z_phi(double *v, double z, double phi) { + + double sintheta = sqrt((1.0 - z) * (1.0 + z)); + v[0] = sintheta * cos(phi); + v[1] = sintheta * sin(phi); + v[2] = z; +} + +/** + * @brief Return the maximum radius of any pixel for a given nside. + * + * Based on Healpix_base::max_pixrad() from the C++ library. + * + * @param nside HEALPix resolution parameter + * + */ +double healpix_max_pixrad(int nside) { + + double va[3]; + set_z_phi(va, 2. / 3., M_PI / (4 * nside)); + + double t1 = 1. - 1. / nside; + t1 *= t1; + double vb[3]; + set_z_phi(vb, 1. - t1 / 3., 0.); + + double dotprod = va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2]; + double crossprod[3]; + crossprod[0] = va[1] * vb[2] - va[2] * vb[1]; + crossprod[1] = va[2] * vb[0] - va[0] * vb[2]; + crossprod[2] = va[0] * vb[1] - va[1] * vb[0]; + double length = + sqrt(crossprod[0] * crossprod[0] + crossprod[1] * crossprod[1] + + crossprod[2] * crossprod[2]); + return atan2(length, dotprod); +} diff --git a/src/lightcone/healpix_util.h b/src/lightcone/healpix_util.h new file mode 100644 index 0000000000000000000000000000000000000000..b3a40cdf108667d7d6629a7ca76bcd82d293dd4a --- /dev/null +++ b/src/lightcone/healpix_util.h @@ -0,0 +1,46 @@ +/******************************************************************************* + * This file is part of SWIFT. + * + * The functions in this file are based on code from the HEALPix + * 3.80 library (see http://healpix.sourceforge.net): + * + * Copyright (C) 1997-2013 Krzysztof M. Gorski, Eric Hivon, + * Benjamin D. Wandelt, Anthony J. Banday, + * Matthias Bartelmann, Hans K. Eriksen, + * Frode K. Hansen, Martin Reinecke + * + * Translated and modified for SWIFT by John Helly: + * + * Copyright (c) 2021 John Helly (j.c.helly@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 "lightcone/pixel_index.h" + +struct pixel_range { + pixel_index_t first; + pixel_index_t last; +}; + +/* + Functions we need which are missing from the HEALPix C API +*/ + +double healpix_max_pixrad(int nside); + +void healpix_query_disc_range(int nside, double vec[3], double radius, + pixel_index_t *pix_min, pixel_index_t *pix_max, + int *nr_ranges, struct pixel_range **range); diff --git a/src/lightcone/lightcone.c b/src/lightcone/lightcone.c new file mode 100644 index 0000000000000000000000000000000000000000..8c244b0c71a3d3e00bdcf1a8baab16887c1af9ea --- /dev/null +++ b/src/lightcone/lightcone.c @@ -0,0 +1,1789 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 <hdf5.h> +#include <limits.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +/* HEALPix C API */ +#ifdef HAVE_CHEALPIX +#include <chealpix.h> +#endif + +/* This object's header. */ +#include "lightcone/lightcone.h" + +/* Local headers */ +#include "common_io.h" +#include "cosmology.h" +#include "engine.h" +#include "error.h" +#include "extra_io.h" +#include "gravity_io.h" +#include "hydro.h" +#include "lightcone/lightcone_particle_io.h" +#include "lightcone/lightcone_replications.h" +#include "lock.h" +#include "neutrino_io.h" +#include "parser.h" +#include "part_type.h" +#include "particle_buffer.h" +#include "periodic.h" +#include "restart.h" +#include "space.h" +#include "timeline.h" +#include "tools.h" +#include "units.h" + +/* Whether to dump the replication list */ +//#define DUMP_REPLICATIONS +#ifdef DUMP_REPLICATIONS +static int output_nr = 0; +#endif + +/* MPI rank for diagnostic messages */ +extern int engine_rank; + +#ifdef HAVE_CHEALPIX +/** + * @brief Read in map types and compression info from a text file + * + * The first column in the file is the map type name and the second + * column is the compression method. Columns are separated by + * whitespace. + * + * @param map_types_file file with the map type names and compression methods + * @param nr_map_types returns the number of map types read + * @param map_types returns an array of nr_map_types map type structs + */ +static void read_map_types_file(const char *map_types_file, int *nr_map_types, + struct lightcone_map_type **map_types) { + + int map_type_nr = 0; + if (engine_rank == 0) { + + FILE *fd = fopen(map_types_file, "r"); + if (!fd) + error("Failed to open lightcone map types file %s", map_types_file); + + /* Count number of non-zero length lines */ + size_t len = 0; + char *line = NULL; + int nr_lines = 0; + while (getline(&line, &len, fd) != -1) nr_lines += 1; + rewind(fd); + + /* Allocate output arrays */ + *map_types = (struct lightcone_map_type *)calloc( + nr_lines, sizeof(struct lightcone_map_type)); + + /* Read lines */ + for (int i = 0; i < nr_lines; i += 1) { + + /* Get name and compression type from this line */ + char compression[PARSER_MAX_LINE_SIZE]; + if (fscanf(fd, "%s %s", (*map_types)[map_type_nr].name, compression) != 2) + error("Failed to read line from map types file"); + + /* Look up compression scheme */ + (*map_types)[map_type_nr].compression = + compression_scheme_from_name(compression); + + /* Only keep maps which have not been disabled */ + if ((*map_types)[map_type_nr].compression != compression_do_not_write) + map_type_nr += 1; + } + fclose(fd); + free(line); + } + +#ifdef WITH_MPI + MPI_Bcast(&map_type_nr, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (engine_rank != 0) + *map_types = (struct lightcone_map_type *)calloc( + map_type_nr, sizeof(struct lightcone_map_type)); + MPI_Bcast(*map_types, sizeof(struct lightcone_map_type) * map_type_nr, + MPI_BYTE, 0, MPI_COMM_WORLD); +#endif + + /* Return number of enabled map types */ + *nr_map_types = map_type_nr; +} +#endif + +/** + * @brief Identify which healpix map types we're making + * + * @param props the #lightcone_props structure + * + * For each requested map type find the update functions by matching names. + * Map types are defined in lightcone_map_types.h and there may be extra + * types defined by various physics modules. The array map_type_array + * below determines where we look for extra map types. + * + * This function assumes that props->map_type is already allocated and + * props->map_type[:].name has been set to the list of map names from the + * .yml file. It sets the update_map, ptype_contributes and units fields + * in the props->map_type array. + * + */ +static void lightcone_identify_map_types(struct lightcone_props *props) { + + /* Loop over requested map types */ + for (int map_nr = 0; map_nr < props->nr_maps; map_nr += 1) { + + /* Use null function pointer to indicate not found yet */ + props->map_type[map_nr].update_map = NULL; + + /* Places to search for lightcone map types: + extra map types are provided by various physics modules. */ + const int num_places = 3; + const struct lightcone_map_type *map_type_array[] = { + lightcone_map_types, extra_lightcone_map_types, + neutrino_lightcone_map_types}; + + /* Loop over places to search for map types */ + for (int i = 0; i < num_places; i += 1) { + + int type_nr = 0; + const struct lightcone_map_type *map_types_to_search = map_type_array[i]; + while (map_types_to_search[type_nr].update_map) { + if (strcmp(map_types_to_search[type_nr].name, + props->map_type[map_nr].name) == 0) { + props->map_type[map_nr] = map_types_to_search[type_nr]; + if (engine_rank == 0) + message("lightcone %d: lightcone map %d is of type %s", + props->index, map_nr, map_types_to_search[type_nr].name); + } + type_nr += 1; + } + + } /* Next place to search */ + + if (!props->map_type[map_nr].update_map) + error("Unable to locate lightcone map type %s", + props->map_type[map_nr].name); + } +} + +/** + * @brief Allocate particle I/O buffers for a lightcone + * + * @param props the #lightcone_props structure + */ +static void lightcone_allocate_buffers(struct lightcone_props *props) { + + /* Initialize particle output buffers */ + const size_t elements_per_block = (size_t)props->buffer_chunk_size; + + if (props->use_type[swift_type_gas]) { + particle_buffer_init(&props->buffer[swift_type_gas], + sizeof(struct lightcone_gas_data), elements_per_block, + "lightcone_gas"); + } + + if (props->use_type[swift_type_dark_matter]) { + particle_buffer_init(&props->buffer[swift_type_dark_matter], + sizeof(struct lightcone_dark_matter_data), + elements_per_block, "lightcone_dm"); + } + + if (props->use_type[swift_type_dark_matter_background]) { + particle_buffer_init(&props->buffer[swift_type_dark_matter_background], + sizeof(struct lightcone_dark_matter_data), + elements_per_block, "lightcone_dm_bg"); + } + + if (props->use_type[swift_type_stars]) { + particle_buffer_init(&props->buffer[swift_type_stars], + sizeof(struct lightcone_stars_data), + elements_per_block, "lightcone_stars"); + } + + if (props->use_type[swift_type_black_hole]) { + particle_buffer_init(&props->buffer[swift_type_black_hole], + sizeof(struct lightcone_black_hole_data), + elements_per_block, "lightcone_bh"); + } + + if (props->use_type[swift_type_neutrino]) { + particle_buffer_init(&props->buffer[swift_type_neutrino], + sizeof(struct lightcone_neutrino_data), + elements_per_block, "lightcone_neutrino"); + } +} + +/** + * @brief Dump lightcone_props struct to the output stream. + * + * @param props the #lightcone_props structure + * @param stream The stream to write to. + */ +void lightcone_struct_dump(const struct lightcone_props *props, FILE *stream) { + + /* Don't dump the replication list - will regenerate it as needed */ + struct lightcone_props tmp = *props; + tmp.replication_list.nrep = 0; + tmp.replication_list.replication = NULL; + tmp.have_replication_list = 0; + + /* Don't write out particle buffers - must flush before dumping restart. */ + memset(tmp.buffer, 0, sizeof(struct particle_buffer) * swift_type_count); + + /* Don't write array pointers */ + tmp.shell = NULL; + tmp.map_type = NULL; + for (int ptype = 0; ptype < swift_type_count; ptype += 1) + tmp.part_type[ptype].map_index = NULL; + + /* Dump the lightcone struct */ + restart_write_blocks((void *)&tmp, sizeof(struct lightcone_props), 1, stream, + "lightcone_props", "lightcone_props"); + + /* Dump the array of map types */ + restart_write_blocks((void *)props->map_type, + sizeof(struct lightcone_map_type), props->nr_maps, + stream, "lightcone_props", "lightcone_props"); + + /* Dump the array of shells */ + lightcone_shell_array_dump(props->shell, props->nr_shells, stream); + + /* For each particle type we have an array of lightcone map indexes to update. + * Dump these. */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + const struct lightcone_particle_type *this_type = + &(props->part_type[ptype]); + restart_write_blocks((void *)this_type->map_index, sizeof(int), + this_type->nr_maps, stream, "lightcone_props", + "lightcone_props"); + } +} + +/** + * @brief Initialise the particle output fields for each particle type. + * + * @param props the #lightcone_props structure + */ +void lightcone_define_output_fields(struct lightcone_props *props) { + + for (int ptype = 0; ptype < swift_type_count; ptype += 1) + lightcone_io_field_list_init(&props->particle_fields[ptype]); + + /* Add the default set of fields for all models, from lightcone_particle_io.c + */ + lightcone_io_append_gas_output_fields( + &props->particle_fields[swift_type_gas]); + lightcone_io_append_dark_matter_output_fields( + &props->particle_fields[swift_type_dark_matter]); + lightcone_io_append_dark_matter_background_output_fields( + &props->particle_fields[swift_type_dark_matter_background]); + lightcone_io_append_stars_output_fields( + &props->particle_fields[swift_type_stars]); + lightcone_io_append_black_hole_output_fields( + &props->particle_fields[swift_type_black_hole]); + lightcone_io_append_neutrino_output_fields( + &props->particle_fields[swift_type_neutrino]); +} + +/** + * @brief Restore lightcone_props struct from the input stream. + * + * @param props the #lightcone_props structure + * @param stream The stream to read from. + */ +void lightcone_struct_restore(struct lightcone_props *props, FILE *stream) { + + /* Restore lightcone struct */ + restart_read_blocks((void *)props, sizeof(struct lightcone_props), 1, stream, + NULL, "lightcone_props"); + + /* Read in the map types */ + props->map_type = (struct lightcone_map_type *)malloc( + sizeof(struct lightcone_map_type) * props->nr_maps); + restart_read_blocks((void *)props->map_type, + sizeof(struct lightcone_map_type), props->nr_maps, stream, + NULL, "lightcone_props"); + + /* Read in the shells */ + props->shell = lightcone_shell_array_restore( + stream, props->nr_shells, props->part_type, props->buffer_chunk_size); + + /* For each particle type we have an array of lightcone map indexes to update. + * Restore these. */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + struct lightcone_particle_type *this_type = &(props->part_type[ptype]); + this_type->map_index = (int *)malloc(sizeof(int) * this_type->nr_maps); + restart_read_blocks((void *)this_type->map_index, sizeof(int), + this_type->nr_maps, stream, NULL, "lightcone_props"); + } + + /* Restore pointers to functions for updating healpix maps */ + lightcone_identify_map_types(props); + + /* Update function pointers for each map */ + for (int shell_nr = 0; shell_nr < props->nr_shells; shell_nr += 1) { + for (int map_nr = 0; map_nr < props->nr_maps; map_nr += 1) { + props->shell[shell_nr].map[map_nr].type = props->map_type[map_nr]; + } + } + + /* Re-allocate particle data buffers */ + lightcone_allocate_buffers(props); + + /* Define output quantities */ + lightcone_define_output_fields(props); + + /* Tabulate the projected kernel */ + projected_kernel_init(&props->kernel_table); +} + +#ifdef HAVE_CHEALPIX +/** + * @brief Locate a lightcone parameter in the .yml file + * + * First check the section specific to this lightcone then + * fall back to LightconeCommon if not found. + * + * @param params the swift parameters struct + * @param index index of the lightcone + * @param name name of the parameter to find + * @param outbuf returns the parameter value + * + */ +static char *find_parameter(struct swift_params *params, const int index, + const char *name, char *outbuf) { + + char full_name[PARSER_MAX_LINE_SIZE]; + + /* Check section specific to this lightcone */ + check_snprintf(full_name, PARSER_MAX_LINE_SIZE, "Lightcone%d:%s", index, + name); + if (parser_does_param_exist(params, full_name)) { + strcpy(outbuf, full_name); + return outbuf; + } + + /* Will look in LightconeCommon section if parameter was not found */ + check_snprintf(full_name, PARSER_MAX_LINE_SIZE, "LightconeCommon:%s", name); + strcpy(outbuf, full_name); + return outbuf; +} +#endif + +/** + * @brief Initialise the properties of the lightcone code. + * + * @param props the #lightcone_props structure to fill. + * @param index index of the lightcone to initialize + * @param s the #space structure. + * @param cosmo the #cosmology structure. + * @param params the parameter file parser. + * @param internal_units swift internal unit system + * @param physical_constants swift physical constant values + * @param verbose the verbosity flag + */ +void lightcone_init(struct lightcone_props *props, const int index, + const struct space *s, const struct cosmology *cosmo, + struct swift_params *params, + const struct unit_system *internal_units, + const struct phys_const *physical_constants, + const int verbose) { + +#ifdef HAVE_CHEALPIX + + /* Macro to generate parameter names given section name */ + char buf[PARSER_MAX_LINE_SIZE]; +#define YML_NAME(x) find_parameter(params, index, x, buf) + + /* Store index of this lightcone in the .yml file */ + props->index = index; + + /* Verbose lightcone output - use passed in value of --verbose flag */ + props->verbose = verbose; + + /* Define output quantities */ + lightcone_define_output_fields(props); + + /* For each particle type, get redshift range for lightcone particle output */ + for (int i = 0; i < swift_type_count; i += 1) { + const int len = PARSER_MAX_LINE_SIZE; + char param_name[len]; + double zrange[2] = {0.0, -1.0}; /* default max < min means do not output */ + check_snprintf(param_name, len, "z_range_for_%s", part_type_names[i]); + parser_get_opt_param_double_array(params, YML_NAME(param_name), 2, zrange); + props->z_min_for_type[i] = zrange[0]; + props->z_max_for_type[i] = zrange[1]; + /* Will only output types with z_max > z_min */ + props->use_type[i] = props->z_max_for_type[i] > props->z_min_for_type[i]; + if (engine_rank == 0 && verbose) { + if (props->use_type[i]) { + message("lightcone %d: %s particles will be output from z=%f to z=%f", + props->index, part_type_names[i], zrange[0], zrange[1]); + } else { + message("lightcone %d: %s particle output is disabled", props->index, + part_type_names[i]); + } + } + } + + /* For each type, find range in comoving distance squared in which we output + * particles */ + for (int i = 0; i < swift_type_count; i += 1) { + if (props->use_type[i]) { + const double a_min = 1.0 / (1.0 + props->z_max_for_type[i]); + props->r2_max_for_type[i] = + pow(cosmology_get_comoving_distance(cosmo, a_min), 2.0); + const double a_max = 1.0 / (1.0 + props->z_min_for_type[i]); + props->r2_min_for_type[i] = + pow(cosmology_get_comoving_distance(cosmo, a_max), 2.0); + } else { + props->r2_min_for_type[i] = 0.0; + props->r2_max_for_type[i] = 0.0; + } + } + + /* + Allow selective output of gas particles at high redshift. + Will output gas particles if redshift < min_z_for_gas_filtering OR + (temperature > min_temp_for_filtered_gas AND nh > + min_nh_for_filtered_gas*(1+z)^4) + */ + props->gas_filtering_enabled = + parser_get_opt_param_int(params, YML_NAME("gas_filtering_enabled"), 0); + if (props->gas_filtering_enabled) { + props->min_z_for_gas_filtering = + parser_get_param_double(params, YML_NAME("min_z_for_gas_filtering")); + props->min_temp_for_filtered_gas = + parser_get_param_double(params, YML_NAME("min_temp_for_filtered_gas")); + props->min_nh_for_filtered_gas = + parser_get_param_double(params, YML_NAME("min_nh_for_filtered_gas")); + props->max_a_for_gas_filtering = + 1.0 / (1.0 + props->min_z_for_gas_filtering); + /* Convert temperature and density thresholds to internal units, assuming + * they're input in CGS */ + props->min_temp_for_filtered_gas /= + units_cgs_conversion_factor(internal_units, UNIT_CONV_TEMPERATURE); + props->min_nh_for_filtered_gas /= + units_cgs_conversion_factor(internal_units, UNIT_CONV_NUMBER_DENSITY); + } + + /* Exclude particles from xray and sz maps if they have been recently AGN + * heated */ + props->xray_maps_recent_AGN_injection_exclusion_time = + parser_get_opt_param_double( + params, YML_NAME("xray_maps_recent_AGN_injection_exclusion_time_myr"), + -1.0); + /* Assume supplied value is in megayears and physical constants are in + * internal units */ + props->xray_maps_recent_AGN_injection_exclusion_time *= + 1.0e6 * physical_constants->const_year; + + /* + Temperature limits for recently AGN heated gas to be excluded from xray and + sz maps. + + Gas is excluded if it has log(temperature) which is greater than + log(AGN_Delta_T)+xray_maps_recent_AGN_logdT_min and less than + log(AGN_Delta_T)+xray_maps_recent_AGN_logdT_max. + + Only takes effect if xray_maps_recent_AGN_injection_exclusion_time_myr is + set. + */ + if (props->xray_maps_recent_AGN_injection_exclusion_time > 0.0) { + double delta_logt_min = parser_get_param_double( + params, YML_NAME("xray_maps_recent_AGN_injection_delta_logT_min")); + if (delta_logt_min > 0.0) + error("xray_maps_recent_AGN_injection_delta_logT_min should be negative"); + props->xray_maps_recent_AGN_min_temp_factor = pow(10.0, delta_logt_min); + double delta_logt_max = parser_get_param_double( + params, YML_NAME("xray_maps_recent_AGN_injection_delta_logT_max")); + if (delta_logt_max < 0.0) + error("xray_maps_recent_AGN_injection_delta_logT_max should be positive"); + props->xray_maps_recent_AGN_max_temp_factor = pow(10.0, delta_logt_max); + if (delta_logt_max < delta_logt_min) + error( + "xray_maps_recent_AGN_injection_delta_logT_max should be greater " + "than _min!"); + } + + /* Directory in which to write this lightcone */ + parser_get_opt_param_string(params, YML_NAME("subdir"), props->subdir, "."); + + /* Base name for output files */ + parser_get_param_string(params, YML_NAME("basename"), props->basename); + + /* Coordinates of the observer in the simulation box */ + parser_get_param_double_array(params, YML_NAME("observer_position"), 3, + props->observer_position); + + /* Write particles to disk if this many or more are in the buffer */ + props->max_particles_buffered = parser_get_opt_param_int( + params, YML_NAME("max_particles_buffered"), 100000); + + /* Chunk size for particles buffered in memory */ + props->buffer_chunk_size = + parser_get_opt_param_int(params, YML_NAME("buffer_chunk_size"), 20000); + + /* Chunk size for particles in the HDF5 output files */ + props->hdf5_chunk_size = + parser_get_opt_param_int(params, YML_NAME("hdf5_chunk_size"), 16384); + + /* Maximum amount of data (in megabytes) to send from any one rank when + * updating healpix maps */ + props->max_map_update_send_size_mb = parser_get_opt_param_double( + params, YML_NAME("max_map_update_send_size_mb"), 512.0); + + /* Compression options */ + props->particles_lossy_compression = parser_get_opt_param_int( + params, YML_NAME("particles_lossy_compression"), 0); + props->particles_gzip_level = + parser_get_opt_param_int(params, YML_NAME("particles_gzip_level"), 0); + props->maps_gzip_level = + parser_get_opt_param_int(params, YML_NAME("maps_gzip_level"), 0); + + /* Get the size of the simulation box */ + props->boxsize = s->dim[0]; + if (s->dim[1] != s->dim[0] || s->dim[2] != s->dim[0]) + error("Lightcones require a cubic simulation box."); + + /* Get top level cell size */ + props->cell_width = s->width[0]; + if (s->width[1] != s->width[0] || s->width[2] != s->width[0]) + error("Lightcones require cubic top level cells."); + + /* Initially have no replication list */ + props->have_replication_list = 0; + props->ti_old = 0; + props->ti_current = 0; + + /* Initialize various counters */ + for (int i = 0; i < swift_type_count; i += 1) { + props->num_particles_written_this_rank[i] = 0; + props->num_particles_written_to_file[i] = 0; + } + props->current_file = -1; + props->file_needs_finalizing = 0; + + /* Always start a new file initially */ + props->start_new_file = 1; + + /* + Healpix map parameters for this lightcone + */ + + /* Healpix nside parameter */ + props->nside = parser_get_param_int(params, YML_NAME("nside")); + + /* Update lightcone pixel data if more than this number of updates are + * buffered */ + props->max_updates_buffered = parser_get_opt_param_int( + params, YML_NAME("max_updates_buffered"), 1000000); + + /*! Whether to write distributed maps in MPI mode */ + props->distributed_maps = + parser_get_opt_param_int(params, YML_NAME("distributed_maps"), 1); + + /* Name of the file with radii of spherical shells */ + parser_get_param_string(params, YML_NAME("radius_file"), props->radius_file); + + /* Get names of the healpix maps to make for this lightcone */ + char map_types_file[FILENAME_BUFFER_SIZE]; + parser_get_param_string(params, YML_NAME("map_names_file"), map_types_file); + read_map_types_file(map_types_file, &props->nr_maps, &props->map_type); + + /* For each requested map type find the update function by matching names */ + lightcone_identify_map_types(props); + + /* For each particle type, determine which healpix maps will be updated */ + const int nr_maps = props->nr_maps; + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + + struct lightcone_particle_type *this_type = &(props->part_type[ptype]); + + /* Count maps updated by this particle type */ + this_type->nr_maps = 0; + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + if (props->map_type[map_nr].ptype_contributes(ptype)) + this_type->nr_maps += 1; + } + + /* Store indexes of maps to update for this particle type */ + this_type->map_index = (int *)malloc(sizeof(int) * this_type->nr_maps); + this_type->nr_maps = 0; + this_type->nr_smoothed_maps = 0; + this_type->nr_unsmoothed_maps = 0; + + /* First the smoothed maps */ + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + const struct lightcone_map_type *map_type = &(props->map_type[map_nr]); + if (map_type->ptype_contributes(ptype) && + map_type->smoothing == map_smoothed) { + this_type->map_index[this_type->nr_maps] = map_nr; + this_type->nr_maps += 1; + this_type->nr_smoothed_maps += 1; + } + } + + /* Then the un-smoothed maps */ + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + const struct lightcone_map_type *map_type = &(props->map_type[map_nr]); + if (map_type->ptype_contributes(ptype) && + map_type->smoothing == map_unsmoothed) { + this_type->map_index[this_type->nr_maps] = map_nr; + this_type->nr_maps += 1; + this_type->nr_unsmoothed_maps += 1; + } + } + + /* Determine how much data we need to store per particle of this type. + We need theta and phi angular coordinates, angular size of the particle, + and the values to be added to the healpix maps */ + this_type->buffer_element_size = + (3 + this_type->nr_maps) * sizeof(union lightcone_map_buffer_entry); + } + + /* Check the number of healpix pixels doesn't overflow pixel_index_t */ + const unsigned long long nside_ull = props->nside; + const unsigned long long npix_ull = 12ull * nside_ull * nside_ull; + if (npix_ull > MAX_PIXEL_INDEX) + error( + "Number of HEALPix pixels is to large for pixel_index_t (see " + "lightcone/pixel_index.h)"); + + /* Set up the array of lightcone shells for this lightcone */ + const pixel_index_t total_nr_pix = nside2npix64(props->nside); + props->shell = lightcone_shell_array_init( + cosmo, props->radius_file, props->nr_maps, props->map_type, props->nside, + total_nr_pix, props->part_type, props->buffer_chunk_size, + &props->nr_shells); + + /* Compute area of a healpix pixel */ + props->pixel_area_steradians = 4 * M_PI / total_nr_pix; + + /* Report shell radii */ + const int nr_shells = props->nr_shells; + if (engine_rank == 0) { + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + message("lightcone %d: shell %d has inner radius %e and outer radius %e", + index, shell_nr, props->shell[shell_nr].rmin, + props->shell[shell_nr].rmax); + } + } + if (engine_rank == 0) + message("lightcone %d: there are %d lightcone shells and %d maps per shell", + index, nr_shells, nr_maps); + + /* For each particle type, find the full redshift range to search for + * lightcone crossings */ + int have_particle_output = 0; + for (int i = 0; i < swift_type_count; i += 1) { + + /* Initially set range to search to range used for particle output, if any + */ + if (props->use_type[i]) { + props->a_min_search_for_type[i] = 1.0 / (1.0 + props->z_max_for_type[i]); + props->a_max_search_for_type[i] = 1.0 / (1.0 + props->z_min_for_type[i]); + have_particle_output = 1; + } else { + props->a_min_search_for_type[i] = DBL_MAX; + props->a_max_search_for_type[i] = 0.0; + } + + /* Then expand the range to include any healpix maps this type contributes + * to */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + const double shell_a_min = props->shell[shell_nr].amin; + const double shell_a_max = props->shell[shell_nr].amax; + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + const struct lightcone_map_type *map_type = &(props->map_type[map_nr]); + if (map_type->ptype_contributes(i)) { + if (shell_a_min < props->a_min_search_for_type[i]) + props->a_min_search_for_type[i] = shell_a_min; + if (shell_a_max > props->a_max_search_for_type[i]) + props->a_max_search_for_type[i] = shell_a_max; + } + } + } + /* Next particle type */ + } + + /* Determine the full redshift range to search for all particle types */ + double a_min = DBL_MAX; + double a_max = 0.0; + for (int i = 0; i < swift_type_count; i += 1) { + if (props->a_max_search_for_type[i] > props->a_min_search_for_type[i]) { + if (props->a_min_search_for_type[i] < a_min) + a_min = props->a_min_search_for_type[i]; + if (props->a_max_search_for_type[i] > a_max) + a_max = props->a_max_search_for_type[i]; + } + } + + /* Check we have a valid range in expansion factor for the lightcone */ + if (a_min > a_max) + error( + "Code was run with --lightcone but no particle outputs or healpix maps " + "are enabled"); + props->a_min = a_min; + props->a_max = a_max; + if (engine_rank == 0) { + for (int i = 0; i < swift_type_count; i += 1) { + if (props->a_max_search_for_type[i] > props->a_min_search_for_type[i]) { + message("lightcone %d: range in expansion factor for %s: %e to %e", + index, part_type_names[i], props->a_min_search_for_type[i], + props->a_max_search_for_type[i]); + } else { + message("lightcone %d: no lightcone output for %s", index, + part_type_names[i]); + } + } + message("lightcone %d: range in expansion factor overall: %e to %e", index, + a_min, a_max); + } + + /* Store the corresponding comoving distance squared */ + props->r2_max = pow(cosmology_get_comoving_distance(cosmo, a_min), 2.0); + props->r2_min = pow(cosmology_get_comoving_distance(cosmo, a_max), 2.0); + + /* Allocate lightcone output buffers */ + lightcone_allocate_buffers(props); + + /* Tabulate the projected kernel */ + projected_kernel_init(&props->kernel_table); + + /* Ensure that the output directories exist */ + if (engine_rank == 0) { + const int len = FILENAME_BUFFER_SIZE; + char dirname[len]; + safe_checkdir(props->subdir, 1); + /* Directory for particle outputs */ + if (have_particle_output) { + check_snprintf(dirname, len, "%s/%s_particles", props->subdir, + props->basename); + safe_checkdir(dirname, 1); + } + /* Directory for shell outputs */ + if ((props->nr_shells > 0) && (props->nr_maps > 0)) { + check_snprintf(dirname, len, "%s/%s_shells", props->subdir, + props->basename); + safe_checkdir(dirname, 1); + } + } +#ifdef WITH_MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif +#else + error("Need HEALPix C API to make lightcones"); +#endif +} + +/** + * @brief Return the name of a lightcone particle output file + * + * @param buf returns the filename + * @param len length of the buffer buf + * @param subdir subdirectory in which to write output + * @param basename base name of this lightcone + * @param current_file lightcone particle file index, which is + * incremented after each restart dump + * @param comm_rank rank of this MPI communicator + */ +static void particle_file_name(char *buf, int len, char *subdir, char *basename, + int current_file, int comm_rank) { + + check_snprintf(buf, len, "%s/%s_particles/%s_%04d.%d.hdf5", subdir, basename, + basename, current_file, comm_rank); +} + +/** + * @brief Flush any buffers which exceed the specified size. + * + * Also used to flush buffers before dumping restart files, in + * which case we should have flush_all=1 and end_file=1 so that + * buffers are flushed regardless of size and we will start a + * new set of lightcone files after the restart dump. + * + * @param props the #lightcone_props structure. + * @param a the current expansion factor + * @param internal_units swift internal unit system + * @param snapshot_units swift snapshot unit system + * @param flush_all flag to force flush of all buffers + * @param end_file if true, subsequent calls write to a new file + * + */ +void lightcone_flush_particle_buffers(struct lightcone_props *props, double a, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + int flush_all, int end_file) { + + ticks tic = getticks(); + + /* Should never be called with end_file=1 and flush_all=0 */ + if (end_file && (!flush_all)) + error("Finalizing file without flushing buffers!"); + + /* Will flush any buffers with more particles than this */ + size_t max_to_buffer = (size_t)props->max_particles_buffered; + if (flush_all) max_to_buffer = 0; + + /* Count how many types have data to write out */ + int types_to_flush = 0; + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + if (props->use_type[ptype]) { + const size_t num_to_write = + particle_buffer_num_elements(&props->buffer[ptype]); + if (num_to_write >= max_to_buffer && num_to_write > 0) + types_to_flush += 1; + } + } + + /* Check if there's anything to do */ + if ((types_to_flush > 0) || (end_file && props->file_needs_finalizing)) { + + /* We have data to flush, so open or create the output file */ + hid_t file_id; + char fname[FILENAME_BUFFER_SIZE]; + if (props->start_new_file) { + + /* Get the name of the next file */ + props->current_file += 1; + particle_file_name(fname, FILENAME_BUFFER_SIZE, props->subdir, + props->basename, props->current_file, engine_rank); + + /* Create the file */ + file_id = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + if (file_id < 0) error("Unable to create new lightcone file: %s", fname); + + /* This new file has not been finalized yet */ + props->file_needs_finalizing = 1; + + /* We have now written no particles to the current file */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) + props->num_particles_written_to_file[ptype] = 0; + + /* Write the system of Units used in the snapshot */ + io_write_unit_system(file_id, snapshot_units, "Units"); + + /* Write the system of Units used internally */ + io_write_unit_system(file_id, internal_units, "InternalCodeUnits"); + + /* Write the observer position and redshift limits */ + hid_t group_id = H5Gcreate(file_id, "Lightcone", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + io_write_attribute(group_id, "observer_position", DOUBLE, + props->observer_position, 3); + + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + char name[PARSER_MAX_LINE_SIZE]; + check_snprintf(name, PARSER_MAX_LINE_SIZE, "minimum_redshift_%s", + part_type_names[ptype]); + io_write_attribute_d(group_id, name, props->z_min_for_type[ptype]); + check_snprintf(name, PARSER_MAX_LINE_SIZE, "maximum_redshift_%s", + part_type_names[ptype]); + io_write_attribute_d(group_id, name, props->z_max_for_type[ptype]); + } + + /* Record number of MPI ranks so we know how many files there are */ + int comm_rank = 0; + int comm_size = 1; +#ifdef WITH_MPI + MPI_Comm_size(MPI_COMM_WORLD, &comm_size); + MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank); +#endif + io_write_attribute_i(group_id, "mpi_rank", comm_rank); + io_write_attribute_i(group_id, "nr_mpi_ranks", comm_size); + io_write_attribute_i(group_id, "file_index", props->current_file); + + H5Gclose(group_id); + + /* We no longer need to create a new file */ + props->start_new_file = 0; + + } else { + + /* Re-open an existing file */ + particle_file_name(fname, FILENAME_BUFFER_SIZE, props->subdir, + props->basename, props->current_file, engine_rank); + file_id = H5Fopen(fname, H5F_ACC_RDWR, H5P_DEFAULT); + if (file_id < 0) + error("Unable to open current lightcone file: %s", fname); + } + + /* Loop over particle types */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + if (props->use_type[ptype]) { + const size_t num_to_write = + particle_buffer_num_elements(&props->buffer[ptype]); + if (num_to_write >= max_to_buffer && num_to_write > 0) { + lightcone_write_particles(props, internal_units, snapshot_units, + ptype, file_id); + particle_buffer_empty(&props->buffer[ptype]); + props->num_particles_written_to_file[ptype] += num_to_write; + props->num_particles_written_this_rank[ptype] += num_to_write; + } + } + } + + /* Check if this is the last write to this file */ + if (end_file) { + hid_t group_id = H5Gopen(file_id, "Lightcone", H5P_DEFAULT); + /* Flag the file as complete */ + io_write_attribute_i(group_id, "file_complete", 1); + /* Write the expected number of particles in all files written by this + rank up to and including this one. */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + char name[PARSER_MAX_LINE_SIZE]; + check_snprintf(name, PARSER_MAX_LINE_SIZE, "cumulative_count_%s", + part_type_names[ptype]); + io_write_attribute_ll(group_id, name, + props->num_particles_written_this_rank[ptype]); + } + /* Write the expansion factor at which we closed this file */ + io_write_attribute_d(group_id, "expansion_factor", a); + H5Gclose(group_id); + props->file_needs_finalizing = 0; + } + + /* We're done updating the output file */ + H5Fclose(file_id); + } + + /* If we need to start a new file next time, record this */ + if (end_file) props->start_new_file = 1; + + if (props->verbose && engine_rank == 0 && types_to_flush > 0) + message("lightcone %d: Flushing particle buffers took %.3f %s.", + props->index, clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Flush lightcone map update buffers for all shells + * + * @param props the #lightcone_props structure. + * @param tp the swift #threadpool struct to use + * + */ +void lightcone_flush_map_updates(struct lightcone_props *props, + struct threadpool *tp) { + + ticks tic = getticks(); + + /* Apply updates to all current shells */ + for (int shell_nr = 0; shell_nr < props->nr_shells; shell_nr += 1) { + if (props->shell[shell_nr].state == shell_current) { + lightcone_shell_flush_map_updates(&props->shell[shell_nr], tp, + props->part_type, + props->max_map_update_send_size_mb, + &props->kernel_table, props->verbose); + } + } + + /* Report runtime */ + if (props->verbose && engine_rank == 0) + message("lightcone %d: Applying lightcone map updates took %.3f %s.", + props->index, clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Write and deallocate any completed lightcone shells + * + * @param props the #lightcone_props structure. + * @param tp the swift #threadpool struct to use + * @param c the #cosmology structure + * @param internal_units swift internal unit system + * @param snapshot_units swift snapshot unit system + * @param dump_all flag to indicate that all shells should be dumped + * @param need_flush whether there might be buffered updates to apply + * + */ +void lightcone_dump_completed_shells(struct lightcone_props *props, + struct threadpool *tp, + const struct cosmology *c, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + const int dump_all, const int need_flush) { +#ifdef HAVE_HDF5 + + ticks tic = getticks(); + + int comm_size = 1; +#ifdef WITH_MPI + MPI_Comm_size(MPI_COMM_WORLD, &comm_size); +#endif + + /* Get number of shells and maps per shell */ + const int nr_shells = props->nr_shells; + const int nr_maps = props->nr_maps; + + /* Get conversion factor for shell radii */ + const double length_conversion_factor = + units_conversion_factor(internal_units, snapshot_units, UNIT_CONV_LENGTH); + + /* Compute expansion factor corresponding to time props->ti_old, + which is the earliest time any particle might have been drifted + from on this step. Here we assume that no particle remains to + be drifted from any time earlier than this so that any shell + whose redshift range is entirely before ti_old can be now be + written out and deallocated. */ + const double a_complete = c->a_begin * exp(props->ti_old * c->time_base); + + int num_shells_written = 0; + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + + /* Will write out this shell if it has been updated but not written + out yet and either we advanced past its redshift range or we're + dumping all remaining shells at the end of the simulation */ + if (props->shell[shell_nr].state == shell_current) { + if (props->shell[shell_nr].amax < a_complete || dump_all) { + + if (props->verbose && engine_rank == 0) + message("lightcone %d: writing out completed shell %d at a=%f", + props->index, shell_nr, c->a); + + num_shells_written += 1; + + /* Apply any buffered updates for this shell, if we didn't already */ + if (need_flush) { + lightcone_shell_flush_map_updates( + &props->shell[shell_nr], tp, props->part_type, + props->max_map_update_send_size_mb, &props->kernel_table, + props->verbose); + } + + /* Set the baseline value for the maps */ + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) + lightcone_map_set_baseline(c, props, + &(props->shell[shell_nr].map[map_nr])); + + /* Ensure output directory exists */ + char fname[FILENAME_BUFFER_SIZE]; + check_snprintf(fname, FILENAME_BUFFER_SIZE, "%s/%s_shells/shell_%d", + props->subdir, props->basename, shell_nr); + if (engine_rank == 0) safe_checkdir(fname, 1); +#ifdef WITH_MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + /* Get the name of the file to write: + In collective mode all ranks get the same file name. + In distributed mode we include engine_rank in the file name. */ + int file_num = props->distributed_maps ? engine_rank : 0; + check_snprintf(fname, FILENAME_BUFFER_SIZE, + "%s/%s_shells/shell_%d/%s.shell_%d.%d.hdf5", + props->subdir, props->basename, shell_nr, + props->basename, shell_nr, file_num); + + /* Create the output file for this shell */ + hid_t fapl_id = H5Pcreate(H5P_FILE_ACCESS); + + /* Set MPI collective mode, if necessary */ + int collective = 0; +#ifdef WITH_MPI +#ifdef HAVE_PARALLEL_HDF5 + if (!props->distributed_maps) { + if (H5Pset_fapl_mpio(fapl_id, MPI_COMM_WORLD, MPI_INFO_NULL) < 0) + error("Unable to set HDF5 MPI-IO file access mode"); + collective = 1; + } +#else + if (!props->distributed_maps) + error( + "Writing lightcone maps in MPI collective mode requires parallel " + "HDF5"); +#endif +#endif + /* Number of files to write */ + int nr_files_per_shell = collective ? 1 : comm_size; + + /* Create the output file(s) */ + hid_t file_id = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (file_id < 0) error("Unable to create file %s", fname); + + /* Write header with metadata */ + hid_t header = + H5Gcreate(file_id, "Shell", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + io_write_attribute_i(header, "nr_files_per_shell", nr_files_per_shell); + io_write_attribute_d( + header, "comoving_inner_radius", + props->shell[shell_nr].rmin * length_conversion_factor); + io_write_attribute_d( + header, "comoving_outer_radius", + props->shell[shell_nr].rmax * length_conversion_factor); + H5Gclose(header); + + /* Write the system of Units used in the snapshot */ + io_write_unit_system(file_id, snapshot_units, "Units"); + + /* Write the system of Units used internally */ + io_write_unit_system(file_id, internal_units, "InternalCodeUnits"); + + /* Write the lightcone maps for this shell */ + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) + lightcone_map_write(&(props->shell[shell_nr].map[map_nr]), file_id, + props->map_type[map_nr].name, internal_units, + snapshot_units, collective, + props->maps_gzip_level, props->hdf5_chunk_size, + props->map_type[map_nr].compression); + + /* Close the file */ + H5Pclose(fapl_id); + H5Fclose(file_id); + + /* Free the pixel data associated with this shell */ + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) + lightcone_map_free_pixels(&(props->shell[shell_nr].map[map_nr])); + + /* Update status of this shell */ + props->shell[shell_nr].state = shell_complete; + } + } + } + + if (props->verbose && engine_rank == 0 && num_shells_written > 0) + message("lightcone %d: Writing completed lightcone shells took %.3f %s.", + props->index, clocks_from_ticks(getticks() - tic), + clocks_getunit()); + +#else + error("Need HDF5 to write out lightcone maps"); +#endif +} + +/** + * @brief Deallocate lightcone data. + * + * @param props the #lightcone_props structure. + * + */ +void lightcone_clean(struct lightcone_props *props) { + + /* Deallocate particle buffers */ + for (int i = 0; i < swift_type_count; i += 1) { + if (props->use_type[i]) particle_buffer_free(&props->buffer[i]); + } + + /* Free replication list, if we have one */ + if (props->have_replication_list) + replication_list_clean(&props->replication_list); + + /* Clean lightcone maps and free the structs */ + const int nr_shells = props->nr_shells; + const int nr_maps = props->nr_maps; + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + lightcone_map_clean(&(props->shell[shell_nr].map[map_nr])); + } + free(props->shell[shell_nr].map); + } + + /* Free buffers associated with each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + particle_buffer_free(&(props->shell[shell_nr].buffer[ptype])); + } + } + + /* Free array of shells */ + free(props->shell); + + /* Free array of lightcone map types */ + free(props->map_type); + + /* Free data associated with particle types */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + struct lightcone_particle_type *this_type = &(props->part_type[ptype]); + free(this_type->map_index); + } + + /* Free lists of output quantities */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) + lightcone_io_field_list_clean(&props->particle_fields[ptype]); + + /* Free the projected kernel */ + projected_kernel_clean(&props->kernel_table); +} + +/** + * @brief Determine periodic copies of the simulation box which could + * contribute to the lightcone. + * + * \ + * \ \ + * | | + * Obs A | B | C + * | | + * / / + * R1 / + * R0 + * + * Consider a single particle being drifted. Here R0 is the comoving + * distance to the time the particle is drifted FROM. R1 is the comoving + * distance to the time the particle is drifted TO on this step. + * + * Particles which are beyond the lightcone surface at the start of + * their drift (C) cannot cross the lightcone on this step if v < c. + * Particles between the lightcone surfaces at the start and end of + * their drift (B) may cross the lightcone (and certainly will if they + * have zero velocity). + * + * Particles just within the lightcone surface at the start of their + * drift (A) may be able to cross the lightcone due to their velocity so + * we need to allow a boundary layer on the inside edge of the shell. + * If we assume v < c, then we can use a layer of thickness R0-R1. + * + * Here we compute the earliest and latest times particles may be drifted + * between, find the corresponding comoving distances R0 and R1, reduce + * the inner distance by R0-R1, and find all periodic copies of the + * simulation box which overlap this spherical shell. + * + * Later we use this list to know which periodic copies to check when + * particles are drifted. + * + * This routine also determines which lightcone healpix maps might be + * updated on this time step and allocates the pixel data if necessary. + * + * @param props The #lightcone_props structure + * @param cosmo The #cosmology structure + * @param ti_earliest_undrifted earliest integer time any particle might + * be drifted from on this step + * @param ti_current End of the timestep + * + */ +void lightcone_prepare_for_step(struct lightcone_props *props, + const struct cosmology *cosmo, + const integertime_t ti_earliest_undrifted, + const integertime_t ti_current) { + ticks tic = getticks(); + + /* Deallocate the old list, if there is one */ + if (props->have_replication_list) + replication_list_clean(&props->replication_list); + + /* Get the size of the simulation box */ + const double boxsize = props->boxsize; + + /* Get a lower limit on earliest time particle may be drifted from */ + const integertime_t ti_lim = ti_earliest_undrifted; + + /* Get expansion factor at earliest and latest times particles might be + * drifted between */ + double a_current = cosmo->a_begin * exp(ti_current * cosmo->time_base); + double a_old = cosmo->a_begin * exp(ti_lim * cosmo->time_base); + if (a_old < cosmo->a_begin) a_old = cosmo->a_begin; + + /* Convert redshift range to a distance range */ + double lightcone_rmin = cosmology_get_comoving_distance(cosmo, a_current); + double lightcone_rmax = cosmology_get_comoving_distance(cosmo, a_old); + if (lightcone_rmin > lightcone_rmax) error("Lightcone has rmin > rmax"); + + /* Allow inner boundary layer, assuming all particles have v < c. + This is to account for particles moving during the time step. */ + double boundary = lightcone_rmax - lightcone_rmin; + lightcone_rmin -= boundary; + if (lightcone_rmin < 0) lightcone_rmin = 0; + + if (a_current < props->a_min || a_old > props->a_max) { + /* Timestep does not overlap the lightcone redshift range */ + replication_list_init_empty(&props->replication_list); + } else { + /* Timestep may contribute particles to the lightcone */ + replication_list_init(&props->replication_list, boxsize, props->cell_width, + props->observer_position, lightcone_rmin, + lightcone_rmax); + } + + /* Record that we made the list */ + props->have_replication_list = 1; + + /* Store times we used to make the list, for consistency check later */ + props->ti_old = ti_lim; + props->ti_current = ti_current; + + /* Report the size of the list */ +#ifdef DUMP_REPLICATIONS + if (engine_rank == 0) { + message("lightcone %d: no. of replications to check: %d", props->index, + props->replication_list.nrep); + message("lightcone %d: shell to search inner radius=%e, outer radius=%e", + props->index, lightcone_rmin, lightcone_rmax); + } +#endif + + /* Write out the list, if required */ +#ifdef DUMP_REPLICATIONS + if (engine_rank == 0) { + char fname[500]; + sprintf(fname, "replication_list.%d.txt", output_nr); + FILE *fd_rep = fopen(fname, "w"); + fprintf(fd_rep, "# Observer x, y, z\n"); + fprintf(fd_rep, "%e, %e, %e\n", props->observer_position[0], + props->observer_position[1], props->observer_position[2]); + fprintf(fd_rep, "# Box size, inner radius, outer radius\n"); + fprintf(fd_rep, "%e, %e, %e\n", boxsize, lightcone_rmin - boundary, + lightcone_rmax); + fprintf(fd_rep, "# x, y, z, rmin2, rmax2\n"); + replication_list_write(&props->replication_list, fd_rep); + fclose(fd_rep); + output_nr += 1; + } +#endif + + /* Number of shells and maps per shell */ + const int nr_maps = props->nr_maps; + const int nr_shells = props->nr_shells; + + /* Range of shells that might be updated this step */ + int shell_nr_min = nr_shells; + int shell_nr_max = -1; + + /* Loop over healpix map shells */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + + const double shell_amin = props->shell[shell_nr].amin; + const double shell_amax = props->shell[shell_nr].amax; + const double step_amin = a_old; + const double step_amax = a_current; + + /* Check if this shell might be updated */ + if (step_amin <= shell_amax && step_amax >= shell_amin) { + + switch (props->shell[shell_nr].state) { + case shell_uninitialized: + /* This shell has not been allocated yet, so allocate it */ + if (props->verbose && engine_rank == 0) + message("lightcone %d: allocating pixels for shell %d at a=%f", + props->index, shell_nr, cosmo->a); + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) + lightcone_map_allocate_pixels(&(props->shell[shell_nr].map[map_nr]), + /* zero_pixels = */ 1); + props->shell[shell_nr].state = shell_current; + break; + case shell_complete: + /* Shell has already been written out and freed - should never happen + */ + error( + "Lightcone shell has been written out while particles could " + "still contribute"); + break; + case shell_current: + /* Already initialized, nothing to do */ + break; + } + + /* Record range of shells that might be updated this step */ + if (shell_nr < shell_nr_min) shell_nr_min = shell_nr; + if (shell_nr > shell_nr_max) shell_nr_max = shell_nr; + } + } + props->shell_nr_min = shell_nr_min; + props->shell_nr_max = shell_nr_max; + + /* Determine which particle types might contribute to lightcone outputs at + * this step */ + for (int i = 0; i < swift_type_count; i += 1) { + props->check_type_for_crossing[i] = 0; + if (props->a_max_search_for_type[i] >= props->a_min_search_for_type[i]) { + if (a_current >= props->a_min_search_for_type[i] && + a_old <= props->a_max_search_for_type[i]) + props->check_type_for_crossing[i] = 1; + } + } + + if (props->verbose && engine_rank == 0) + message("lightcone %d: Lightcone timestep preparations took %.3f %s.", + props->index, clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Determine whether lightcone map buffers should be flushed this step. + * + * @param props The #lightcone_props structure + * + */ +int lightcone_trigger_map_update(struct lightcone_props *props) { + + size_t total_updates = 0; + const int nr_shells = props->nr_shells; + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + if (props->shell[shell_nr].state == shell_current) { + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + total_updates += particle_buffer_num_elements( + &(props->shell[shell_nr].buffer[ptype])); + } + } + } + return total_updates >= ((size_t)props->max_updates_buffered); +} + +/** + * @brief Add a particle to the output buffer + * + * @param props The #lightcone_props structure + * @param e The #engine structure + * @param gp The #gpart to buffer + * @param a_cross Expansion factor of lightcone crossing + * @param x_cross Position of the gpart at lightcone crossing + */ +void lightcone_buffer_particle(struct lightcone_props *props, + const struct engine *e, const struct gpart *gp, + const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + const struct spart *sparts = s->sparts; + const struct bpart *bparts = s->bparts; + + switch (gp->type) { + case swift_type_gas: { + + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + struct lightcone_gas_data data; + if (lightcone_store_gas(e, props, gp, p, xp, a_cross, x_cross, &data)) + particle_buffer_append(props->buffer + swift_type_gas, &data); + + } break; + + case swift_type_stars: { + + const struct spart *sp = &sparts[-gp->id_or_neg_offset]; + struct lightcone_stars_data data; + if (lightcone_store_stars(e, props, gp, sp, a_cross, x_cross, &data)) + particle_buffer_append(props->buffer + swift_type_stars, &data); + + } break; + + case swift_type_black_hole: { + + const struct bpart *bp = &bparts[-gp->id_or_neg_offset]; + struct lightcone_black_hole_data data; + if (lightcone_store_black_hole(e, props, gp, bp, a_cross, x_cross, &data)) + particle_buffer_append(props->buffer + swift_type_black_hole, &data); + + } break; + + case swift_type_dark_matter: { + + struct lightcone_dark_matter_data data; + if (lightcone_store_dark_matter(e, props, gp, a_cross, x_cross, &data)) + particle_buffer_append(props->buffer + swift_type_dark_matter, &data); + + } break; + + case swift_type_dark_matter_background: { + + /* Assumed to have same properties as DM particles */ + struct lightcone_dark_matter_data data; + if (lightcone_store_dark_matter(e, props, gp, a_cross, x_cross, &data)) + particle_buffer_append( + props->buffer + swift_type_dark_matter_background, &data); + + } break; + + case swift_type_neutrino: { + + struct lightcone_neutrino_data data; + if (lightcone_store_neutrino(e, props, gp, a_cross, x_cross, &data)) + particle_buffer_append(props->buffer + swift_type_neutrino, &data); + + } break; + + default: + error("Particle type not supported in lightcones"); + } +} + +#ifdef HAVE_CHEALPIX +/** + * @brief Compute the angular smoothing length of a particle + * + * @param pos particle position vector relative to observer + * @param hsml physical smoothing length of the particle + * + */ +static double angular_smoothing_scale(const double *pos, const double hsml) { + + /* Compute distance to particle */ + double dist = 0; + for (int i = 0; i < 3; i += 1) dist += pos[i] * pos[i]; + dist = sqrt(dist); + + /* Avoid trig call for small angles (accurate to about 0.3%) */ + if (dist > 10.0 * hsml) + return hsml / dist; + else + return atan(hsml / dist); +} +#endif + +/** + * @brief Buffer a particle's contribution to the healpix map(s) + * + * @param props The #lightcone_props structure + * @param e The #engine structure + * @param gp The #gpart to buffer + * @param a_cross Expansion factor of lightcone crossing + * @param x_cross Position of the gpart at lightcone crossing + * + */ +void lightcone_buffer_map_update(struct lightcone_props *props, + const struct engine *e, const struct gpart *gp, + const double a_cross, + const double x_cross[3]) { +#ifdef HAVE_CHEALPIX + + /* Find information on healpix maps this particle type contributes to */ + const struct lightcone_particle_type *part_type_info = + &(props->part_type[gp->type]); + + /* If this particle type contributes to no healpix maps, do nothing */ + if (part_type_info->nr_maps == 0) return; + + /* Get angular coordinates of the particle */ + double theta, phi; + vec2ang(x_cross, &theta, &phi); + + /* Get angular size of the particle */ + double radius; + if (gp->type == swift_type_gas) { + const struct part *parts = e->s->parts; + const struct part *p = &parts[-gp->id_or_neg_offset]; + radius = angular_smoothing_scale(x_cross, p->h); + } else { + radius = 0.0; + } + + /* Loop over shells to update */ + for (int shell_nr = props->shell_nr_min; shell_nr <= props->shell_nr_max; + shell_nr += 1) { + if (a_cross > props->shell[shell_nr].amin && + a_cross <= props->shell[shell_nr].amax) { + + /* Make sure this shell is available for updating */ + if (props->shell[shell_nr].state == shell_uninitialized) + error("Attempt to update shell which has not been allocated"); + if (props->shell[shell_nr].state == shell_complete) + error("Attempt to update shell which has been written out"); + + /* Allocate storage for updates and set particle coordinates and radius */ + union lightcone_map_buffer_entry *data = + (union lightcone_map_buffer_entry *)malloc( + part_type_info->buffer_element_size); + data[0].i = angle_to_int(theta); + data[1].i = angle_to_int(phi); + data[2].f = radius; + + /* Loop over healpix maps which this particle type contributes to and find + * values to add */ + for (int i = 0; i < part_type_info->nr_maps; i += 1) { + int map_nr = part_type_info->map_index[i]; + /* The value to add to the map may need to be scaled to fit in a float + */ + const double fac = props->map_type[map_nr].buffer_scale_factor; + /* Fetch the value to add to the map */ + const double val = + props->map_type[map_nr].update_map(e, props, gp, a_cross, x_cross); + /* Store the scaled value */ + data[3 + i].f = fac * val; +#ifdef LIGHTCONE_MAP_CHECK_TOTAL + /* Accumulate total quantity added to each map for consistency check */ + atomic_add_d(&props->shell[shell_nr].map[map_nr].total, val); +#endif + } + + /* Buffer the updates */ + particle_buffer_append(&(props->shell[shell_nr].buffer[gp->type]), data); + + /* Free update info */ + free(data); + } + } /* Next shell */ +#else + error("Need HEALPix C API to make lightcones"); +#endif +} + +/** + * @brief Compute memory used by lightcones on this rank + * + * @param props The #lightcone_props structure + * @param particle_buffer_bytes returns bytes used to buffer particles + * @param map_buffer bytes returns bytes used to buffer map updates + * @param pixel_data_bytes returns bytes used to store map pixels + * + */ +void lightcone_memory_use(struct lightcone_props *props, + size_t *particle_buffer_bytes, + size_t *map_buffer_bytes, size_t *pixel_data_bytes) { + + *particle_buffer_bytes = 0; + *map_buffer_bytes = 0; + *pixel_data_bytes = 0; + + /* Accumulate memory used by particle buffers - one buffer per particle type + */ + for (int i = 0; i < swift_type_count; i += 1) { + if (props->use_type[i]) + *particle_buffer_bytes += particle_buffer_memory_use(props->buffer + i); + } + + /* Accumulate memory used by map update buffers and pixel data */ + const int nr_maps = props->nr_maps; + const int nr_shells = props->nr_shells; + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + + /* Healpix map updates - one buffer per particle type per shell */ + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + *map_buffer_bytes += + particle_buffer_memory_use(&(props->shell[shell_nr].buffer[ptype])); + } + + /* Pixel data - one buffer per map per shell */ + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + struct lightcone_map *map = &(props->shell[shell_nr].map[map_nr]); + if (map->data) *pixel_data_bytes += map->local_nr_pix * sizeof(double); + } + } +} + +/** + * @brief Write out number of files per rank for this lightcone + * + * @param props The #lightcone_props structure + * @param internal_units swift internal unit system + * @param snapshot_units swift snapshot unit system + * + */ +void lightcone_write_index(struct lightcone_props *props, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units) { + int comm_size = 1; +#ifdef WITH_MPI + MPI_Comm_size(MPI_COMM_WORLD, &comm_size); +#endif + + /* Collect current file index on each rank */ + int *current_file_on_rank = (int *)malloc(sizeof(int) * comm_size); +#ifdef WITH_MPI + MPI_Gather(&props->current_file, 1, MPI_INT, current_file_on_rank, 1, MPI_INT, + 0, MPI_COMM_WORLD); +#else + current_file_on_rank[0] = props->current_file; +#endif + + if (engine_rank == 0) { + + /* Get conversion factor for shell radii */ + const double length_conversion_factor = units_conversion_factor( + internal_units, snapshot_units, UNIT_CONV_LENGTH); + + /* Get the name of the index file */ + char fname[FILENAME_BUFFER_SIZE]; + check_snprintf(fname, FILENAME_BUFFER_SIZE, "%s/%s_index.hdf5", + props->subdir, props->basename); + + /* Create the file */ + hid_t file_id = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + + /* Write number of MPI ranks and number of files */ + hid_t group_id = + H5Gcreate(file_id, "Lightcone", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + io_write_attribute_i(group_id, "nr_mpi_ranks", comm_size); + io_write_attribute(group_id, "final_particle_file_on_rank", INT, + current_file_on_rank, comm_size); + + /* Write number of files the lightcone maps are distributed over */ + int nr_files_per_shell = props->distributed_maps ? comm_size : 1; + io_write_attribute_i(group_id, "nr_files_per_shell", nr_files_per_shell); + + /* Write observer position and redshift limits */ + io_write_attribute(group_id, "observer_position", DOUBLE, + props->observer_position, 3); + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + char name[PARSER_MAX_LINE_SIZE]; + check_snprintf(name, PARSER_MAX_LINE_SIZE, "minimum_redshift_%s", + part_type_names[ptype]); + io_write_attribute_d(group_id, name, props->z_min_for_type[ptype]); + check_snprintf(name, PARSER_MAX_LINE_SIZE, "maximum_redshift_%s", + part_type_names[ptype]); + io_write_attribute_d(group_id, name, props->z_max_for_type[ptype]); + } + + /* Write the number of shells and their radii */ + const int nr_shells = props->nr_shells; + io_write_attribute_i(group_id, "nr_shells", nr_shells); + double *shell_inner_radii = (double *)malloc(sizeof(double) * nr_shells); + double *shell_outer_radii = (double *)malloc(sizeof(double) * nr_shells); + for (int i = 0; i < nr_shells; i += 1) { + shell_inner_radii[i] = props->shell[i].rmin * length_conversion_factor; + shell_outer_radii[i] = props->shell[i].rmax * length_conversion_factor; + } + io_write_attribute(group_id, "shell_inner_radii", DOUBLE, shell_inner_radii, + nr_shells); + io_write_attribute(group_id, "shell_outer_radii", DOUBLE, shell_outer_radii, + nr_shells); + free(shell_outer_radii); + free(shell_inner_radii); + + H5Gclose(group_id); + H5Fclose(file_id); + } + + free(current_file_on_rank); +} + +/** + * @brief Add the baseline value to a lightcone map + * + * @param c the #cosmology struct + * @param props the properties of this lightcone + * @param map the #lightcone_map structure + */ +void lightcone_map_set_baseline(const struct cosmology *c, + struct lightcone_props *props, + struct lightcone_map *map) { + + /* Nothing to do if there is no baseline function */ + if (map->type.baseline_func == NULL) return; + + /* Fetch the baseline value */ + double baseline_value = map->type.baseline_func(c, props, map); + + /* Add it to the map if necessary */ + if (baseline_value != 0.0) { + for (pixel_index_t i = 0; i < map->local_nr_pix; i += 1) { +#ifdef LIGHTCONE_MAP_CHECK_TOTAL + map->total += baseline_value; +#endif + map->data[i] += baseline_value; + } + } +} diff --git a/src/lightcone/lightcone.h b/src/lightcone/lightcone.h new file mode 100644 index 0000000000000000000000000000000000000000..f0628fb85b234edbb6c0daa5cd30bac72732ac85 --- /dev/null +++ b/src/lightcone/lightcone.h @@ -0,0 +1,273 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_LIGHTCONE_H +#define SWIFT_LIGHTCONE_H + +/* Config parameters. */ +#include <config.h> + +/* Local headers */ +#include "lightcone/lightcone_map_types.h" +#include "lightcone/lightcone_particle_io.h" +#include "lightcone/lightcone_replications.h" +#include "lightcone/lightcone_shell.h" +#include "lightcone/pixel_index.h" +#include "lightcone/projected_kernel.h" +#include "parser.h" +#include "part_type.h" +#include "particle_buffer.h" +#include "threadpool.h" +#include "timeline.h" +#include "units.h" + +/* Avoid cyclic inclusions */ +struct cosmology; +struct engine; +struct space; + +/** + * @brief Lightcone data + */ +struct lightcone_props { + + /*! Index of this lightcone */ + int index; + + /*! Whether to write extra log messages */ + int verbose; + + /*! Which particle types we're doing */ + int use_type[swift_type_count]; + + /*! Minimum redshift for particle output for each type */ + double z_min_for_type[swift_type_count]; + + /*! Maximum redshift for particle output for each type */ + double z_max_for_type[swift_type_count]; + + /*! Minimum a to search for lightcone crossing for each type */ + double a_min_search_for_type[swift_type_count]; + + /*! Maximum a to search for lightcone crossing for each type */ + double a_max_search_for_type[swift_type_count]; + + /*! Whether we need to do lightcone crossing checks for each type at this step + */ + int check_type_for_crossing[swift_type_count]; + + /*! Enable selective output of high redshift gas */ + int gas_filtering_enabled; + + /*! Will output all gas below this redshift */ + double min_z_for_gas_filtering; + + /*! Will output all gas after this scale factor */ + double max_a_for_gas_filtering; + + /*! At z>min_z_for_gas_filtering require gas T>min_temp_for_high_z_gas */ + double min_temp_for_filtered_gas; + + /*! At z>min_z_for_gas_filtering require gas + * nh>min_nh_for_filtered_gas*(1+z)^4 */ + double min_nh_for_filtered_gas; + + /*! Exclude recently heated gas from xray and sz maps */ + double xray_maps_recent_AGN_injection_exclusion_time; + + /*! Don't exclude gas with temperature less than this factor times AGN_delta_T + */ + double xray_maps_recent_AGN_min_temp_factor; + + /*! Don't exclude gas with temperature more than this factor times AGN_delta_T + */ + double xray_maps_recent_AGN_max_temp_factor; + + /*! Output base name */ + char basename[PARSER_MAX_LINE_SIZE]; + + /*! Output directory */ + char subdir[PARSER_MAX_LINE_SIZE]; + + /*! Position of the observer in the simulation box */ + double observer_position[3]; + + /*! Range in distance squared in which we output particles of each type */ + double r2_min_for_type[swift_type_count], r2_max_for_type[swift_type_count]; + + /*! Range in expansion factor covered by particle outputs and healpix maps */ + double a_min, a_max; + + /*! Corresponding range in distance squared for a_max and a_min */ + double r2_min, r2_max; + + /*! Size of chunks in particle buffer */ + int buffer_chunk_size; + + /*! Size of chunks in HDF5 output files */ + int hdf5_chunk_size; + + /* Maximum amount of data (in megabytes) to send from any one rank when + * updating healpix maps */ + double max_map_update_send_size_mb; + + /*! Whether to apply lossy compression */ + int particles_lossy_compression; + + /*! Lossless compression level for particles (0 to disable) */ + int particles_gzip_level; + + /*! Lossless compression level for healpix maps (0 to disable) */ + int maps_gzip_level; + + /*! Simulation box size (volume must be a cube) */ + double boxsize; + + /*! Top level cell width */ + double cell_width; + + /*! Whether list of replications exists */ + int have_replication_list; + + /*! List of periodic replications to check on this timestep */ + struct replication_list replication_list; + + /*! Number of particles written to the current file by this MPI rank */ + long long num_particles_written_to_file[swift_type_count]; + + /*! Number of particles of each type which have been output on this rank */ + long long num_particles_written_this_rank[swift_type_count]; + + /*! Index of the current output file for this MPI rank */ + int current_file; + + /*! Range of times used to generate the replication list */ + integertime_t ti_old, ti_current; + + /*! Expansion factors corresponding to z_min, z_max */ + double a_at_z_min, a_at_z_max; + + /*! Buffers to store particles on the lightcone */ + struct particle_buffer buffer[swift_type_count]; + + /*! Will write particles to disk if buffer exceeds this size */ + int max_particles_buffered; + + /*! Whether we should make a new file on the next flush */ + int start_new_file; + + /*! Whether we have started a particle file and not finalized it yet */ + int file_needs_finalizing; + + /*! Number of pending map updates to trigger communication */ + int max_updates_buffered; + + /*! Whether to write distributed maps in MPI mode */ + int distributed_maps; + + /*! Name of the file with radii of spherical shells */ + char radius_file[PARSER_MAX_LINE_SIZE]; + + /*! Healpix nside parameter */ + int nside; + + /*! Healpix pixel area */ + double pixel_area_steradians; + + /*! Number of shells */ + int nr_shells; + + /*! Array of lightcone shells */ + struct lightcone_shell *shell; + + /*! Number of healpix maps we're making for each shell */ + int nr_maps; + + /*! Types of healpix map we're making for each shell */ + struct lightcone_map_type *map_type; + + /*! Range of shells that might be updated this step */ + int shell_nr_min, shell_nr_max; + + /*! Information about each particle type contributing to the maps */ + struct lightcone_particle_type part_type[swift_type_count]; + + /*! Output fields */ + struct lightcone_io_field_list particle_fields[swift_type_count]; + + /*! Tabulation of projected SPH smoothing kernel */ + struct projected_kernel_table kernel_table; +}; + +void lightcone_init(struct lightcone_props *props, const int index, + const struct space *s, const struct cosmology *cosmo, + struct swift_params *params, + const struct unit_system *internal_units, + const struct phys_const *physical_constants, + const int verbose); + +void lightcone_clean(struct lightcone_props *props); + +void lightcone_struct_dump(const struct lightcone_props *props, FILE *stream); + +void lightcone_struct_restore(struct lightcone_props *props, FILE *stream); + +void lightcone_prepare_for_step(struct lightcone_props *props, + const struct cosmology *cosmo, + const integertime_t ti_earliest_undrifted, + const integertime_t ti_current); + +void lightcone_buffer_particle(struct lightcone_props *props, + const struct engine *e, const struct gpart *gp, + const double a_cross, const double x_cross[3]); + +void lightcone_flush_particle_buffers(struct lightcone_props *props, double a, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + int flush_all, int end_file); + +void lightcone_buffer_map_update(struct lightcone_props *props, + const struct engine *e, const struct gpart *gp, + const double a_cross, const double x_cross[3]); + +void lightcone_flush_map_updates(struct lightcone_props *props, + struct threadpool *tp); + +void lightcone_dump_completed_shells(struct lightcone_props *props, + struct threadpool *tp, + const struct cosmology *c, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + const int dump_all, const int need_flush); + +int lightcone_trigger_map_update(struct lightcone_props *props); + +void lightcone_memory_use(struct lightcone_props *props, + size_t *particle_buffer_bytes, + size_t *map_buffer_bytes, size_t *pixel_data_bytes); + +void lightcone_write_index(struct lightcone_props *props, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units); + +void lightcone_map_set_baseline(const struct cosmology *c, + struct lightcone_props *props, + struct lightcone_map *map); + +#endif /* SWIFT_LIGHTCONE_H */ diff --git a/src/lightcone/lightcone_array.c b/src/lightcone/lightcone_array.c new file mode 100644 index 0000000000000000000000000000000000000000..f4bf79e56ad4badc226ad7741a0def61dc6bab7b --- /dev/null +++ b/src/lightcone/lightcone_array.c @@ -0,0 +1,342 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 <stdio.h> +#include <string.h> + +/* This object's header. */ +#include "lightcone/lightcone_array.h" + +/* Local headers */ +#include "common_io.h" +#include "cosmology.h" +#include "engine.h" +#include "error.h" +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_particle_io.h" +#include "lightcone/lightcone_replications.h" +#include "parser.h" +#include "particle_buffer.h" +#include "periodic.h" +#include "restart.h" +#include "space.h" +#include "timeline.h" +#include "tools.h" + +/** + * @brief Initialise the properties of the lightcone code. + * + */ +void lightcone_array_init(struct lightcone_array_props *props, + const struct space *s, const struct cosmology *cosmo, + struct swift_params *params, + const struct unit_system *internal_units, + const struct phys_const *physical_constants, + const int verbose) { + + /* Determine number of lightcones */ + props->nr_lightcones = 0; + for (int lightcone_nr = 0; lightcone_nr <= MAX_LIGHTCONES; + lightcone_nr += 1) { + char name[PARSER_MAX_LINE_SIZE]; + check_snprintf(name, PARSER_MAX_LINE_SIZE, "Lightcone%d:enabled", + props->nr_lightcones); + if (parser_get_opt_param_int(params, name, 0)) { + props->nr_lightcones += 1; + } + } + + if (engine_rank == 0) + message("found %d lightcones to generate", props->nr_lightcones); + + /* Allocate array of lightcones */ + props->lightcone = (struct lightcone_props *)malloc( + sizeof(struct lightcone_props) * props->nr_lightcones); + if (!props->lightcone) error("Failed to allocate lightcone array"); + + /* Initialise lightcones */ + props->nr_lightcones = 0; + for (int lightcone_nr = 0; lightcone_nr <= MAX_LIGHTCONES; + lightcone_nr += 1) { + char name[PARSER_MAX_LINE_SIZE]; + check_snprintf(name, PARSER_MAX_LINE_SIZE, "Lightcone%d:enabled", + props->nr_lightcones); + if (parser_get_opt_param_int(params, name, 0)) { + check_snprintf(name, PARSER_MAX_LINE_SIZE, "Lightcone%d", + props->nr_lightcones); + lightcone_init(props->lightcone + lightcone_nr, lightcone_nr, s, cosmo, + params, internal_units, physical_constants, verbose); + props->nr_lightcones += 1; + } + } + + /* Check lightcones have unique output file names */ + for (int i = 0; i < props->nr_lightcones; i += 1) { + for (int j = 0; j < props->nr_lightcones; j += 1) { + if (i != j) { + const struct lightcone_props *lc1 = props->lightcone + i; + const struct lightcone_props *lc2 = props->lightcone + j; + if (strcmp(lc1->basename, lc2->basename) == 0) + error("Lightcones must have unique basenames!"); + } + } + } + + props->verbose = verbose; +} + +void lightcone_array_clean(struct lightcone_array_props *props) { + + for (int i = 0; i < props->nr_lightcones; i += 1) + lightcone_clean(props->lightcone + i); + free(props->lightcone); +} + +void lightcone_array_struct_dump(const struct lightcone_array_props *props, + FILE *stream) { + + struct lightcone_array_props tmp = *props; + tmp.lightcone = NULL; + restart_write_blocks((void *)&tmp, sizeof(struct lightcone_array_props), 1, + stream, "lightcone_array_props", + "lightcone_array_props"); + + for (int i = 0; i < props->nr_lightcones; i += 1) + lightcone_struct_dump(props->lightcone + i, stream); +} + +void lightcone_array_struct_restore(struct lightcone_array_props *props, + FILE *stream) { + + restart_read_blocks((void *)props, sizeof(struct lightcone_array_props), 1, + stream, NULL, "lightcone_array_props"); + + props->lightcone = (struct lightcone_props *)malloc( + sizeof(struct lightcone_props) * props->nr_lightcones); + if (!props->lightcone) error("Failed to allocate lightcone array"); + + for (int i = 0; i < props->nr_lightcones; i += 1) + lightcone_struct_restore(props->lightcone + i, stream); +} + +void lightcone_array_prepare_for_step(struct lightcone_array_props *props, + const struct cosmology *cosmo, + const integertime_t ti_earliest_undrifted, + const integertime_t ti_current) { + + for (int i = 0; i < props->nr_lightcones; i += 1) + lightcone_prepare_for_step(props->lightcone + i, cosmo, + ti_earliest_undrifted, ti_current); + + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + props->check_type_for_crossing[ptype] = 0; + for (int i = 0; i < props->nr_lightcones; i += 1) { + if (props->lightcone[i].check_type_for_crossing[ptype]) { + props->check_type_for_crossing[ptype] = 1; + } + } + if (props->check_type_for_crossing[ptype] && props->verbose && + engine_rank == 0) { + message("need to check type %s for crossing at this step", + part_type_names[ptype]); + } + } +} + +int lightcone_array_trigger_map_update(struct lightcone_array_props *props) { + + for (int i = 0; i < props->nr_lightcones; i += 1) { + if (lightcone_trigger_map_update(props->lightcone + i)) return 1; + } + return 0; +} + +/** + * @brief Flush buffers for all lightcones in the array + * + * Buffers are flushed if they get large or a flush is forced + * by setting one of the input flags. + * + * props the #lightcone_array_props struct + * flush_map_updates force full update of the healpix maps + * flush_particles force output of all buffered particles + * end_file start a new file next time particles are written out + * dump_all_shells immediately output all remaining healpix maps + * + */ +void lightcone_array_flush(struct lightcone_array_props *props, + struct threadpool *tp, const struct cosmology *cosmo, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + int flush_map_updates, int flush_particles, + int end_file, int dump_all_shells) { + + if (props->verbose) lightcone_array_report_memory_use(props); + + /* Loop over lightcones */ + const int nr_lightcones = props->nr_lightcones; + for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) { + + /* Get a pointer to this lightcone */ + struct lightcone_props *lc_props = props->lightcone + lightcone_nr; + + /* Apply lightcone map updates if requested */ + if (flush_map_updates) lightcone_flush_map_updates(lc_props, tp); + + /* Flush particle buffers if they're large or flag is set */ + lightcone_flush_particle_buffers(lc_props, cosmo->a, internal_units, + snapshot_units, flush_particles, end_file); + + /* Write out any completed healpix maps */ + lightcone_dump_completed_shells(lc_props, tp, cosmo, internal_units, + snapshot_units, dump_all_shells, + /*need_flush=*/!flush_map_updates); + } +} + +/** + * @brief Make a refined replication list for each lightcone + * + * Returns an array of struct #replication_list. Must be freed + * with lightcone_array_free_replications(). + * + * props the #lightcone_array_props struct + * cell the #cell for which we're making replication lists + * + */ +struct replication_list *lightcone_array_refine_replications( + struct lightcone_array_props *props, const struct cell *cell) { + + /* Get number of lightcones */ + const int nr_lightcones = props->nr_lightcones; + + /* Allocate a replication list for each lightcone */ + struct replication_list *lists = (struct replication_list *)malloc( + sizeof(struct replication_list) * nr_lightcones); + + /* Loop over lightcones */ + for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) { + + /* Make refined replication list for this lightcone */ + struct lightcone_props *lightcone = props->lightcone + lightcone_nr; + replication_list_subset_for_cell(&lightcone->replication_list, cell, + lightcone->observer_position, + lists + lightcone_nr); + } + + return lists; +} + +/** + * @brief Free lists returned by lightcone_array_refine_replications + * + * props the #lightcone_array_props struct + * lists the array of struct #replication_list to free + * + */ +void lightcone_array_free_replications(struct lightcone_array_props *props, + struct replication_list *lists) { + + /* Get number of lightcones */ + const int nr_lightcones = props->nr_lightcones; + + /* Loop over lightcones and clean replication lists */ + for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) { + replication_list_clean(lists + lightcone_nr); + } + + /* Free replication list array */ + free(lists); +} + +/** + * @brief Write the index file for each lightcone + * + * props the #lightcone_array_props struct + * + */ +void lightcone_array_write_index(struct lightcone_array_props *props, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units) { + + /* Get number of lightcones */ + const int nr_lightcones = props->nr_lightcones; + + /* Loop over lightcones and clean replication lists */ + for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) { + lightcone_write_index(props->lightcone + lightcone_nr, internal_units, + snapshot_units); + } +} + +void lightcone_array_report_memory_use(struct lightcone_array_props *props) { + + long long memuse_local[4] = {0LL, 0LL, 0LL, 0LL}; + + /* Get number of lightcones */ + const int nr_lightcones = props->nr_lightcones; + + /* Loop over lightcones and clean replication lists */ + for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) { + + /* Accumulate mmeory use of this lightcone */ + size_t particle_buffer_bytes; + size_t map_buffer_bytes; + size_t pixel_data_bytes; + lightcone_memory_use(&props->lightcone[lightcone_nr], + &particle_buffer_bytes, &map_buffer_bytes, + &pixel_data_bytes); + memuse_local[0] += particle_buffer_bytes; + memuse_local[1] += map_buffer_bytes; + memuse_local[2] += pixel_data_bytes; + } + memuse_local[3] = memuse_local[0] + memuse_local[1] + memuse_local[2]; + + /* Find min and max memory over MPI ranks */ + long long memuse_min[4]; + long long memuse_max[4]; +#ifdef WITH_MPI + MPI_Reduce(memuse_local, memuse_min, 4, MPI_LONG_LONG, MPI_MIN, 0, + MPI_COMM_WORLD); + MPI_Reduce(memuse_local, memuse_max, 4, MPI_LONG_LONG, MPI_MAX, 0, + MPI_COMM_WORLD); +#else + for (int i = 0; i < 4; i += 1) { + memuse_min[i] = memuse_local[i]; + memuse_max[i] = memuse_local[i]; + } +#endif + + /* Report memory use, if non-zero */ + if (engine_rank == 0 && memuse_max[3] > 0) { + const long long MB = 1024 * 1024; + message("particle buffer Mbytes: min=%lldMB, max=%lldMB", + memuse_min[0] / MB, memuse_max[0] / MB); + message("map update buffer Mbytes: min=%lldMB, max=%lldMB", + memuse_min[1] / MB, memuse_max[1] / MB); + message("map pixel data Mbytes: min=%lldMB, max=%lldMB", memuse_min[2] / MB, + memuse_max[2] / MB); + message("total lightcone data Mbytes: min=%lldMB, max=%lldMB", + memuse_min[3] / MB, memuse_max[3] / MB); + } +} diff --git a/src/lightcone/lightcone_array.h b/src/lightcone/lightcone_array.h new file mode 100644 index 0000000000000000000000000000000000000000..a47932cb797ea5a12b567f3966b93475b405eca1 --- /dev/null +++ b/src/lightcone/lightcone_array.h @@ -0,0 +1,102 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_LIGHTCONE_ARRAY_H +#define SWIFT_LIGHTCONE_ARRAY_H + +/* Config parameters. */ +#include <config.h> + +/* Local headers */ +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_replications.h" +#include "parser.h" +#include "part_type.h" +#include "particle_buffer.h" +#include "threadpool.h" +#include "timeline.h" + +/* Avoid cyclic inclusions */ +struct cosmology; +struct engine; +struct space; + +#define MAX_LIGHTCONES 8 + +/** + * @brief Lightcone data for multiple lightcones + */ +struct lightcone_array_props { + + /*! Number of lightcones */ + int nr_lightcones; + + /*! Lightcone properties */ + struct lightcone_props *lightcone; + + /*! Whether we need to do lightcone crossing checks for each type at this step + */ + int check_type_for_crossing[swift_type_count]; + + /*! Whether to generate memory usage reports */ + int verbose; +}; + +void lightcone_array_init(struct lightcone_array_props *props, + const struct space *s, const struct cosmology *cosmo, + struct swift_params *params, + const struct unit_system *internal_units, + const struct phys_const *physical_constants, + const int verbose); + +void lightcone_array_clean(struct lightcone_array_props *props); + +void lightcone_array_struct_dump(const struct lightcone_array_props *props, + FILE *stream); + +void lightcone_array_struct_restore(struct lightcone_array_props *props, + FILE *stream); + +void lightcone_array_prepare_for_step(struct lightcone_array_props *props, + const struct cosmology *cosmo, + const integertime_t ti_earliest_undrifted, + const integertime_t ti_current); + +int lightcone_array_trigger_map_update(struct lightcone_array_props *props); + +void lightcone_array_flush(struct lightcone_array_props *props, + struct threadpool *tp, const struct cosmology *cosmo, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + int flush_map_updates, int flush_particles, + int end_file, int dump_all_shells); + +struct replication_list *lightcone_array_refine_replications( + struct lightcone_array_props *props, const struct cell *cell); + +void lightcone_array_free_replications(struct lightcone_array_props *props, + struct replication_list *lists); + +void lightcone_array_write_index(struct lightcone_array_props *props, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units); + +void lightcone_array_report_memory_use(struct lightcone_array_props *props); + +#endif /* SWIFT_LIGHTCONE_ARRAY_H */ diff --git a/src/lightcone/lightcone_crossing.h b/src/lightcone/lightcone_crossing.h new file mode 100644 index 0000000000000000000000000000000000000000..78777f1ff09b805074110601701c4c85aa6ffc2a --- /dev/null +++ b/src/lightcone/lightcone_crossing.h @@ -0,0 +1,252 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 "cosmology.h" +#include "gravity.h" +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_particle_io.h" +#include "lightcone/lightcone_replications.h" +#include "part.h" +#include "stars.h" +#include "timeline.h" + +#ifndef SWIFT_LIGHTCONE_CROSSING_H +#define SWIFT_LIGHTCONE_CROSSING_H + +/** + * @brief Check if a particle crosses the lightcone during a drift. + * + * Here we don't assume anything about the particle type except + * that it has a corresponding #gpart. The particle type is checked + * if we decide to output the particle. + * + * Note that x and v_full are values at the start of the time step but + * the particle has been drifted to the end of the time step when this + * function is called. + * + * @param e the #engine struct + * @param replication_list_array one replication list for each lightcone + * @param x the position of the particle BEFORE it is drifted + * @param v_full the velocity of the particle + * @param gp pointer to the #gpart to check + * @param dt_drift the time step size used to update the position + * @param ti_old begining of the time step on the integer time line + * @param ti_current end of the time step on the integer time line + * @param cell_loc coordinates of the #cell containing the #gpart + * + */ +__attribute__((always_inline)) INLINE static void +lightcone_check_particle_crosses( + const struct engine *e, struct replication_list *replication_list_array, + const double *x, const float *v_full, const struct gpart *gp, + const double dt_drift, const integertime_t ti_old, + const integertime_t ti_current, const double cell_loc[3]) { + + /* Does this particle type contribute to any lightcone outputs at this + * redshift? */ + if (e->lightcone_array_properties->check_type_for_crossing[gp->type] == 0) + return; + + /* Check if we have any replications to search */ + /* TODO: pre-calculate this for each cell to save time */ + int nrep_tot = 0; + const int nr_lightcones = e->lightcone_array_properties->nr_lightcones; + for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) { + nrep_tot += replication_list_array[lightcone_nr].nrep; + } + if (nrep_tot == 0) return; + + /* Unpack some variables we need */ + const struct cosmology *c = e->cosmology; + + /* Determine expansion factor at start and end of the drift */ + const double a_start = c->a_begin * exp(ti_old * c->time_base); + const double a_end = c->a_begin * exp(ti_current * c->time_base); + + /* Find comoving distance to these expansion factors */ + const double comoving_dist_start = + cosmology_get_comoving_distance(c, a_start); + const double comoving_dist_2_start = + comoving_dist_start * comoving_dist_start; + const double comoving_dist_end = cosmology_get_comoving_distance(c, a_end); + const double comoving_dist_2_end = comoving_dist_end * comoving_dist_end; + + /* Thickness of the 'shell' between the lightcone surfaces at start and end of + drift. + We use this as a limit on how far a particle can drift (i.e. assume v < + c).*/ + const double boundary = comoving_dist_2_start - comoving_dist_2_end; + + /* Wrap particle starting coordinates to nearest it's parent cell */ + const double boxsize = e->s->dim[0]; + const double x_wrapped[3] = { + box_wrap(x[0], cell_loc[0] - 0.5 * boxsize, cell_loc[0] + 0.5 * boxsize), + box_wrap(x[1], cell_loc[1] - 0.5 * boxsize, cell_loc[1] + 0.5 * boxsize), + box_wrap(x[2], cell_loc[2] - 0.5 * boxsize, cell_loc[2] + 0.5 * boxsize)}; + + /* Loop over lightcones to make */ + for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) { + + /* Find the current lightcone and its replication list */ + struct lightcone_props *props = + e->lightcone_array_properties->lightcone + lightcone_nr; + struct replication_list *replication_list = + replication_list_array + lightcone_nr; + + /* Consistency check - are our limits on the drift endpoints good? */ + if (ti_old < props->ti_old || ti_current > props->ti_current) + error( + "Particle drift is outside the range used to make replication list!"); + + /* Are there any replications to check at this timestep? */ + const int nreps = replication_list->nrep; + if (nreps == 0) continue; + const struct replication *rep = replication_list->replication; + + /* Find observer position for this lightcone */ + const double *observer_position = props->observer_position; + + /* Does this drift overlap the lightcone redshift range? If not, nothing to + * do. */ + if ((a_start > props->a_max) || (a_end < props->a_min)) continue; + + /* Get wrapped position relative to observer */ + const double x_wrapped_rel[3] = {x_wrapped[0] - observer_position[0], + x_wrapped[1] - observer_position[1], + x_wrapped[2] - observer_position[2]}; + + /* Loop over periodic copies of the volume: + + Here we're looking for cases where a periodic copy of the particle + is closer to the observer than the lightcone surface at the start + of the drift, and further away than the lightcone surface at the + end of the drift. I.e. the surface of the lightcone has swept over + the particle as it contracts towards the observer. + */ + for (int i = 0; i < nreps; i += 1) { + + /* If all particles in this periodic replica are beyond the lightcone + surface at the earlier time, then they already crossed the lightcone. + Since the replications are in ascending order of rmin we don't need to + check any more. */ + if (rep[i].rmin2 > comoving_dist_2_start) break; + + /* If all particles in this periodic replica start their drifts inside the + lightcone surface, and are sufficiently far inside that their velocity + can't cause them to cross the lightcone, then we don't need to consider + this replication */ + if (rep[i].rmax2 + boundary < comoving_dist_2_end) continue; + + /* Get the coordinates of this periodic copy of the gpart relative to the + * observer */ + const double x_start[3] = { + x_wrapped_rel[0] + rep[i].coord[0], + x_wrapped_rel[1] + rep[i].coord[1], + x_wrapped_rel[2] + rep[i].coord[2], + }; + + /* Get distance squared from the observer at start of drift */ + const double r2_start = x_start[0] * x_start[0] + + x_start[1] * x_start[1] + x_start[2] * x_start[2]; + + /* If particle is initially beyond the lightcone surface, it can't cross + */ + if (r2_start > comoving_dist_2_start) continue; + + /* Get position of this periodic copy at the end of the drift */ + const double x_end[3] = { + x_start[0] + dt_drift * v_full[0], + x_start[1] + dt_drift * v_full[1], + x_start[2] + dt_drift * v_full[2], + }; + + /* Get distance squared from the observer at end of drift */ + const double r2_end = + x_end[0] * x_end[0] + x_end[1] * x_end[1] + x_end[2] * x_end[2]; + + /* If particle is still within the lightcone surface at the end of the + drift, it didn't cross*/ + if (r2_end < comoving_dist_2_end) continue; + + /* This periodic copy of the gpart crossed the lightcone during this + drift. Now need to estimate when it crossed within the timestep. + + If r is the distance from the observer to this periodic copy of the + particle, and it crosses after a fraction f of the drift: + + r_cross = r_start + (r_end - r_start) * f + + and if R is the comoving distance to the lightcone surface + + R_cross = R_start + (R_end - R_start) * f + + The particle crosses the lightcone when r_cross = R_cross, so + + r_start + (r_end - r_start) * f = R_start + (R_end - R_start) * f + + Solving for f: + + f = (r_start - R_start) / (R_end - R_start - r_end + r_start) + + */ + const double f = (sqrt(r2_start) - comoving_dist_start) / + (comoving_dist_end - comoving_dist_start - sqrt(r2_end) + + sqrt(r2_start)); + + /* f should always be in the range 0-1 */ + const double eps = 1.0e-5; + if ((f < 0.0 - eps) || (f > 1.0 + eps)) + error("Particle interpolated outside time step!"); + + /* Compute position at crossing */ + const double x_cross[3] = { + x_start[0] + dt_drift * f * v_full[0], + x_start[1] + dt_drift * f * v_full[1], + x_start[2] + dt_drift * f * v_full[2], + }; + + /* Get distance squared at crossing */ + const double r2_cross = + (x_cross[0] * x_cross[0] + x_cross[1] * x_cross[1] + + x_cross[2] * x_cross[2]); + + /* Compute expansion factor at crossing */ + const double a_cross = + cosmology_scale_factor_at_comoving_distance(c, sqrt(r2_cross)); + + /* Add this particle to the particle output buffer if it's in the redshift + * range */ + if (r2_cross >= props->r2_min_for_type[gp->type] && + r2_cross <= props->r2_max_for_type[gp->type] && + props->use_type[gp->type]) + lightcone_buffer_particle(props, e, gp, a_cross, x_cross); + + /* Buffer this particle's contribution to the healpix maps */ + if (props->shell_nr_max >= props->shell_nr_min) + lightcone_buffer_map_update(props, e, gp, a_cross, x_cross); + + } /* Next periodic replication*/ + } /* Next lightcone */ +} + +#endif /* SWIFT_LIGHTCONE_CROSSING_H */ diff --git a/src/lightcone/lightcone_map.c b/src/lightcone/lightcone_map.c new file mode 100644 index 0000000000000000000000000000000000000000..0f4ae43b922d8ff146e2bf21bbc07b10190ef98a --- /dev/null +++ b/src/lightcone/lightcone_map.c @@ -0,0 +1,322 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* This object's header. */ +#include "lightcone/lightcone_map.h" + +/* Local headers */ +#include "align.h" +#include "common_io.h" +#include "error.h" +#include "memuse.h" +#include "restart.h" + +/* HDF5 */ +#ifdef HAVE_HDF5 +#include <hdf5.h> +#endif + +void lightcone_map_init(struct lightcone_map *map, const int nside, + const pixel_index_t total_nr_pix, + const pixel_index_t pix_per_rank, + const pixel_index_t local_nr_pix, + const pixel_index_t local_pix_offset, + const double r_min, const double r_max, + struct lightcone_map_type type) { + + /*Store number of pixels in the map etc */ + map->nside = nside; + map->total_nr_pix = total_nr_pix; + map->pix_per_rank = pix_per_rank; + map->local_nr_pix = local_nr_pix; + map->local_pix_offset = local_pix_offset; + + /* Pixel data is initially not allocated */ + map->data = NULL; + + /* Store resolution parameter, shell size, units etc */ + map->r_min = r_min; + map->r_max = r_max; + map->type = type; + + /* Store factor used to retrieve values from the update buffer */ + map->buffer_scale_factor_inv = 1.0 / (type.buffer_scale_factor); + +#ifdef LIGHTCONE_MAP_CHECK_TOTAL + /* Initialize total for consistency check */ + map->total = 0.0; +#endif +} + +/** + * @brief Deallocate the lightcone_map pixel data + * + * @param map the #lightcone_map structure + */ +void lightcone_map_clean(struct lightcone_map *map) { + + if (map->data) lightcone_map_free_pixels(map); +} + +/** + * @brief Allocate (and maybe initialize) the lightcone_map pixel data + * + * @param map the #lightcone_map structure + * @param zero_pixels if true, set allocated pixels to zero + */ +void lightcone_map_allocate_pixels(struct lightcone_map *map, + const int zero_pixels) { + + if (swift_memalign("lightcone_map_pixels", (void **)&map->data, + SWIFT_STRUCT_ALIGNMENT, + sizeof(double) * map->local_nr_pix) != 0) + error("Failed to allocate lightcone map pixel data"); + + if (zero_pixels) { + for (pixel_index_t i = 0; i < map->local_nr_pix; i += 1) map->data[i] = 0.0; + } +} + +void lightcone_map_free_pixels(struct lightcone_map *map) { + + swift_free("lightcone_map_pixels", (void *)map->data); + map->data = NULL; +} + +/** + * @brief Dump lightcone_map struct to the output stream. + * + * @param map the #lightcone_map structure + * @param stream The stream to write to. + */ +void lightcone_map_struct_dump(const struct lightcone_map *map, FILE *stream) { + + /* Write the struct */ + restart_write_blocks((void *)map, sizeof(struct lightcone_map), 1, stream, + "lightcone_map", "lightcone_map"); + + /* Write the pixel data if it is allocated */ + if (map->data) + restart_write_blocks((void *)map->data, sizeof(double), map->local_nr_pix, + stream, "lightcone_map_data", "lightcone_map_data"); +} + +/** + * @brief Restore lightcone_map struct from the input stream. + * + * @param map the #lightcone_map structure + * @param stream The stream to read from. + */ +void lightcone_map_struct_restore(struct lightcone_map *map, FILE *stream) { + + /* Read the struct */ + restart_read_blocks((void *)map, sizeof(struct lightcone_map), 1, stream, + NULL, "lightcone_map"); + + /* Read the pixel data if it was allocated. + map->data from the restart file is not a valid pointer now but we can + check if it is not null to see if the pixel data block was written out. */ + if (map->data) { + lightcone_map_allocate_pixels(map, /* zero_pixels = */ 0); + restart_read_blocks((void *)map->data, sizeof(double), map->local_nr_pix, + stream, NULL, "lightcone_map"); + } +} + +#ifdef HAVE_HDF5 +/** + * @brief Write a lightcone map to a HDF5 file + * + * @param map the #lightcone_map structure + * @param loc a HDF5 file or group identifier to write to + * @param name the name of the dataset to create + */ +void lightcone_map_write(struct lightcone_map *map, const hid_t loc_id, + const char *name, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + const int collective, const int gzip_level, + const int chunk_size, + enum lossy_compression_schemes compression) { + +#ifdef WITH_MPI + int comm_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank); +#endif + + /* Find unit conversion factor for this quantity */ + const double map_conversion_factor = + units_conversion_factor(internal_units, snapshot_units, map->type.units); + + /* Convert units of pixel data if necessary */ + if (map_conversion_factor != 1.0) { + for (pixel_index_t i = 0; i < map->local_nr_pix; i += 1) + map->data[i] *= map_conversion_factor; + } + + /* Get conversion factor for shell radii */ + const double length_conversion_factor = + units_conversion_factor(internal_units, snapshot_units, UNIT_CONV_LENGTH); + + /* Create dataspace in memory corresponding to local pixels */ + const hsize_t mem_dims[1] = {(hsize_t)map->local_nr_pix}; + hid_t mem_space_id = H5Screate_simple(1, mem_dims, NULL); + if (mem_space_id < 0) error("Unable to create memory dataspace"); + + /* Create dataspace corresponding to the part of the map in this file. */ + hsize_t file_dims[1]; + if (collective) { + /* For collective writes the file contains the full map */ + file_dims[0] = (hsize_t)map->total_nr_pix; + } else { + /* For distributed writes the file contains the local pixels only */ + file_dims[0] = (hsize_t)map->local_nr_pix; + } + hid_t file_space_id = H5Screate_simple(1, file_dims, NULL); + if (file_space_id < 0) error("Unable to create file dataspace"); + + /* Select the part of the dataset in the file to write to */ +#ifdef WITH_MPI +#ifdef HAVE_PARALLEL_HDF5 + if (collective) { + const pixel_index_t pixel_offset = map->local_pix_offset; + const hsize_t start[1] = {(hsize_t)pixel_offset}; + const hsize_t count[1] = {(hsize_t)map->local_nr_pix}; + if (H5Sselect_hyperslab(file_space_id, H5S_SELECT_SET, start, NULL, count, + NULL) < 0) + error("Unable to select part of file dataspace to write to"); + } +#else + if (collective) + error("Writing lightcone maps with collective I/O requires parallel HDF5"); +#endif +#endif + + /* Property list for creating the dataset */ + hid_t prop_id = H5Pcreate(H5P_DATASET_CREATE); + + /* Data type to write in the file */ + hid_t dtype_id = H5Tcopy(H5T_NATIVE_DOUBLE); + + /* Use chunked writes and possibly filters in non-collective mode */ + if (!collective) { + + /* Set the chunk size */ + const hsize_t dim[1] = {(hsize_t)chunk_size}; + if (H5Pset_chunk(prop_id, 1, dim) < 0) + error("Unable to set HDF5 chunk size for healpix map"); + + /* Set lossy compression, if requested. This might change the + output data type and add to the property list. */ + char filter_name[32]; + set_hdf5_lossy_compression(&prop_id, &dtype_id, compression, name, + filter_name); + + /* Set lossless compression */ + if (gzip_level > 0) { + H5Pset_shuffle(prop_id); + if (H5Pset_deflate(prop_id, gzip_level) < 0) + error("Unable to set HDF5 deflate filter for healpix map"); + } + } + + /* Create the dataset */ + hid_t dset_id = H5Dcreate(loc_id, name, dtype_id, file_space_id, H5P_DEFAULT, + prop_id, H5P_DEFAULT); + H5Pclose(prop_id); + H5Tclose(dtype_id); + if (dset_id < 0) error("Unable to create dataset %s", name); + + /* Write attributes */ + io_write_attribute_i(dset_id, "nside", map->nside); + io_write_attribute_ll(dset_id, "number_of_pixels", + (long long)map->total_nr_pix); + io_write_attribute_s(dset_id, "pixel_ordering_scheme", "ring"); + io_write_attribute_d(dset_id, "comoving_inner_radius", + map->r_min * length_conversion_factor); + io_write_attribute_d(dset_id, "comoving_outer_radius", + map->r_max * length_conversion_factor); + + /* Write unit conversion factors for this data set */ + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, snapshot_units, map->type.units, 0.f); + float baseUnitsExp[5]; + units_get_base_unit_exponents_array(baseUnitsExp, map->type.units); + io_write_attribute_f(dset_id, "U_M exponent", baseUnitsExp[UNIT_MASS]); + io_write_attribute_f(dset_id, "U_L exponent", baseUnitsExp[UNIT_LENGTH]); + io_write_attribute_f(dset_id, "U_t exponent", baseUnitsExp[UNIT_TIME]); + io_write_attribute_f(dset_id, "U_I exponent", baseUnitsExp[UNIT_CURRENT]); + io_write_attribute_f(dset_id, "U_T exponent", baseUnitsExp[UNIT_TEMPERATURE]); + io_write_attribute_f(dset_id, "h-scale exponent", 0.f); + io_write_attribute_f(dset_id, "a-scale exponent", 0.f); + io_write_attribute_s(dset_id, "Expression for physical CGS units", buffer); + + /* Write the actual number this conversion factor corresponds to */ + const double cgs_factor = + units_cgs_conversion_factor(snapshot_units, map->type.units); + io_write_attribute_d( + dset_id, + "Conversion factor to CGS (not including cosmological corrections)", + cgs_factor); + +#ifdef LIGHTCONE_MAP_CHECK_TOTAL + /* Consistency check: will write out expected sum over pixels */ + double total = map->total; +#ifdef WITH_MPI + MPI_Allreduce(&map->total, &total, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); +#endif + total *= map_conversion_factor; + io_write_attribute_d(dset_id, "expected_sum", total); +#endif + + /* Set up property list for the write */ + hid_t h_plist_id = H5Pcreate(H5P_DATASET_XFER); +#if defined(WITH_MPI) +#ifdef HAVE_PARALLEL_HDF5 + if (collective) { + if (H5Pset_dxpl_mpio(h_plist_id, H5FD_MPIO_COLLECTIVE) < 0) + error("Unable to set collective transfer mode"); + } +#else + if (collective) + error("Writing lightcone maps with MPI requires parallel HDF5"); +#endif +#endif + + /* Write the data */ + if (H5Dwrite(dset_id, H5T_NATIVE_DOUBLE, mem_space_id, file_space_id, + h_plist_id, map->data) < 0) + error("Unable to write dataset %s", name); + + /* Tidy up */ + H5Dclose(dset_id); + H5Sclose(mem_space_id); + H5Sclose(file_space_id); + H5Pclose(h_plist_id); +} +#endif /* HAVE_HDF5*/ diff --git a/src/lightcone/lightcone_map.h b/src/lightcone/lightcone_map.h new file mode 100644 index 0000000000000000000000000000000000000000..5f7b5bbd9225ddc59848ecbe9165c6fdbd3475d8 --- /dev/null +++ b/src/lightcone/lightcone_map.h @@ -0,0 +1,117 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_LIGHTCONE_MAP_H +#define SWIFT_LIGHTCONE_MAP_H + +/* Define this to write the expected sum of the map to the + output file. This is to check that the SPH smoothing and + communication code is conserving the quantity added to + the map. */ +#define LIGHTCONE_MAP_CHECK_TOTAL + +/* Standard headers */ +#include <limits.h> +#include <math.h> + +/* Config parameters. */ +#include <config.h> + +/* HDF5 */ +#ifdef HAVE_HDF5 +#include <hdf5.h> +#endif + +/* Local headers */ +#include "io_compression.h" +#include "lightcone/lightcone_map_types.h" +#include "lightcone/pixel_index.h" +#include "units.h" + +/** + * @brief Struct to store a single lightcone healpix map + */ +struct lightcone_map { + + /*! Healpix nside parameter */ + int nside; + + /*! Total pixels in the map */ + pixel_index_t total_nr_pix; + + /*! Number of pixels stored on this node */ + pixel_index_t local_nr_pix; + + /*! Offset of the first pixel stored on this rank */ + pixel_index_t local_pix_offset; + + /*! Number of pixels per rank (last node has any extra) */ + pixel_index_t pix_per_rank; + + /*! Local healpix map data */ + double *data; + + /*! Inner radius */ + double r_min; + + /*! Outer radius */ + double r_max; + + /*! Type of this map */ + struct lightcone_map_type type; + + /*! Factor for retrieving values from the update buffer */ + double buffer_scale_factor_inv; + +#ifdef LIGHTCONE_MAP_CHECK_TOTAL + /*! Total quantity accumulated to this map, for consistency check */ + double total; +#endif +}; + +void lightcone_map_init(struct lightcone_map *map, const int nside, + const pixel_index_t total_nr_pix, + const pixel_index_t pix_per_rank, + const pixel_index_t local_nr_pix, + const pixel_index_t local_pix_offset, + const double r_min, const double r_max, + struct lightcone_map_type type); + +void lightcone_map_clean(struct lightcone_map *map); + +void lightcone_map_struct_dump(const struct lightcone_map *map, FILE *stream); + +void lightcone_map_struct_restore(struct lightcone_map *map, FILE *stream); + +void lightcone_map_allocate_pixels(struct lightcone_map *map, + const int zero_pixels); + +void lightcone_map_free_pixels(struct lightcone_map *map); + +#ifdef HAVE_HDF5 +void lightcone_map_write(struct lightcone_map *map, const hid_t loc_id, + const char *name, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + const int collective, const int maps_gzip_level, + const int chunk_size, + enum lossy_compression_schemes compression); +#endif + +#endif /* #ifndef SWIFT_LIGHTCONE_MAP_H */ diff --git a/src/lightcone/lightcone_map_types.c b/src/lightcone/lightcone_map_types.c new file mode 100644 index 0000000000000000000000000000000000000000..8a3b7cf259c021f653400f0be97bf640ebcfaf6b --- /dev/null +++ b/src/lightcone/lightcone_map_types.c @@ -0,0 +1,363 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 includes */ +#include "black_holes.h" +#include "engine.h" +#include "gravity.h" +#include "hydro.h" +#include "lightcone/lightcone_map.h" +#include "neutrino.h" +#include "part.h" +#include "star_formation.h" +#include "stars.h" + +/* This object's header */ +#include "lightcone/lightcone_map_types.h" + +/* Required for the xrays */ +#include "extra_io.h" +#include "io_properties.h" + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_gas_only(int ptype) { + + switch (ptype) { + case swift_type_gas: + return 1; + default: + return 0; + } +} + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_total_mass_type_contributes(int ptype) { + + switch (ptype) { + case swift_type_gas: + case swift_type_stars: + case swift_type_black_hole: + case swift_type_dark_matter: + case swift_type_dark_matter_background: + case swift_type_neutrino: + return 1; + default: + return 0; + } +} + +/** + * @brief Make a healpix map of projected mass in each pixel + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_total_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + /* const struct xpart *xparts = s->xparts; */ /* Currently not used */ + const struct spart *sparts = s->sparts; + const struct bpart *bparts = s->bparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + return hydro_get_mass(p); + } break; + case swift_type_stars: { + const struct spart *sp = &sparts[-gp->id_or_neg_offset]; + return sp->mass; + } break; + case swift_type_black_hole: { + const struct bpart *bp = &bparts[-gp->id_or_neg_offset]; + return bp->mass; + } break; + case swift_type_dark_matter: + case swift_type_dark_matter_background: { + return gp->mass; + } break; + case swift_type_neutrino: { + struct neutrino_model nu_model; + bzero(&nu_model, sizeof(struct neutrino_model)); + if (e->neutrino_properties->use_delta_f_mesh_only) + gather_neutrino_consts(e->s, &nu_model); + double weight = 1.0; + gpart_neutrino_weight_mesh_only(gp, &nu_model, &weight); + return gp->mass * weight; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/** + * Return background neutrino density to add to the total mass map + * + * The neutrino particles trace density perturbations relative + * to a constant background so we need to add in the background + * to get the total mass. + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param map The lightcone map + * + */ +double lightcone_map_total_mass_baseline_value( + const struct cosmology *c, const struct lightcone_props *lightcone_props, + const struct lightcone_map *map) { + return lightcone_map_neutrino_baseline_value(c, lightcone_props, map); +} + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_gas_mass_type_contributes(int ptype) { + + switch (ptype) { + case swift_type_gas: + return 1; + default: + return 0; + } +} + +/** + * @brief Make a healpix map of projected gas mass in each pixel + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_gas_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + return hydro_get_mass(p); + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_dark_matter_mass_type_contributes(int ptype) { + + switch (ptype) { + case swift_type_dark_matter: + case swift_type_dark_matter_background: + return 1; + default: + return 0; + } +} + +/** + * @brief Make a healpix map of projected dark matter mass in each pixel + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_dark_matter_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + switch (gp->type) { + case swift_type_dark_matter: + case swift_type_dark_matter_background: { + return gp->mass; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_stellar_mass_type_contributes(int ptype) { + + switch (ptype) { + case swift_type_stars: + return 1; + default: + return 0; + } +} + +/** + * @brief Make a healpix map of stellar mass in each pixel + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_stellar_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct spart *sparts = s->sparts; + + switch (gp->type) { + case swift_type_stars: { + const struct spart *sp = &sparts[-gp->id_or_neg_offset]; + return sp->mass; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_black_hole_mass_type_contributes(int ptype) { + + switch (ptype) { + case swift_type_black_hole: + return 1; + default: + return 0; + } +} + +/** + * @brief Make a healpix map of black hole mass in each pixel + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_black_hole_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct bpart *bparts = s->bparts; + + switch (gp->type) { + case swift_type_black_hole: { + const struct bpart *bp = &bparts[-gp->id_or_neg_offset]; + return bp->mass; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_sfr_type_contributes(int ptype) { + + switch (ptype) { + case swift_type_gas: + return 1; + default: + return 0; + } +} + +/** + * @brief Make a healpix map of star formation rate in each pixel + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_sfr_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + /* Handle on the other particle types */ + const struct space *s = e->s; + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + + switch (gp->type) { + case swift_type_gas: { + const struct part *p = &parts[-gp->id_or_neg_offset]; + const struct xpart *xp = &xparts[-gp->id_or_neg_offset]; + return star_formation_get_SFR(p, xp); + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} diff --git a/src/lightcone/lightcone_map_types.h b/src/lightcone/lightcone_map_types.h new file mode 100644 index 0000000000000000000000000000000000000000..6dc94f935601fb70ce1937c5d6e5313301051fd8 --- /dev/null +++ b/src/lightcone/lightcone_map_types.h @@ -0,0 +1,222 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_LIGHTCONE_MAP_TYPES_H +#define SWIFT_LIGHTCONE_MAP_TYPES_H + +/* Config parameters. */ +#include <config.h> + +/* Local headers */ +#include "io_compression.h" +#include "parser.h" +#include "part_type.h" +#include "units.h" + +/* Avoid cyclic inclusions */ +struct cosmology; +struct engine; +struct lightcone_map; +struct lightcone_props; +struct gpart; + +/* Type to store pointer to function for updating a healpix map */ +typedef double (*map_update_function_t)( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +/* Type to store pointer to function for providing baseline map value */ +typedef double (*map_baseline_function_t)( + const struct cosmology *c, const struct lightcone_props *lightcone_props, + const struct lightcone_map *map); + +/* Type to store pointer to function to check which types contribute to a map */ +typedef int (*map_contrib_function_t)(int ptype); + +enum lightcone_map_smoothing { map_unsmoothed, map_smoothed }; + +/** + * @brief Struct to store information on one type of lightcone map + */ +struct lightcone_map_type { + char name[PARSER_MAX_LINE_SIZE]; + map_update_function_t update_map; + map_contrib_function_t ptype_contributes; + map_baseline_function_t baseline_func; + enum unit_conversion_factor units; + enum lightcone_map_smoothing smoothing; + enum lossy_compression_schemes compression; + double buffer_scale_factor; +}; + +/* + Function used for defining maps which only include gas (e.g. EAGLE x-ray + outputs) +*/ +int lightcone_map_gas_only(int ptype); + +/* + Healpix map of total mass +*/ +int lightcone_map_total_mass_type_contributes(int ptype); + +double lightcone_map_total_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +double lightcone_map_total_mass_baseline_value( + const struct cosmology *c, const struct lightcone_props *lightcone_props, + const struct lightcone_map *map); + +/* + Healpix map of gas mass +*/ +int lightcone_map_gas_mass_type_contributes(int ptype); + +double lightcone_map_gas_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); +/* + Healpix map of dark matter mass +*/ +int lightcone_map_dark_matter_mass_type_contributes(int ptype); + +double lightcone_map_dark_matter_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); +/* + Healpix map of stellar mass +*/ +int lightcone_map_stellar_mass_type_contributes(int ptype); + +double lightcone_map_stellar_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); +/* + Healpix map of black hole mass +*/ +int lightcone_map_black_hole_mass_type_contributes(int ptype); + +double lightcone_map_black_hole_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); +/* + Healpix map of star formation rate +*/ +int lightcone_map_sfr_type_contributes(int ptype); + +double lightcone_map_sfr_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]); + +/* + This associates map names to the appropriate update function and unit info. + + Note that field designators are commented out here so that the code will + compile as C++ using gcc. This is necesssary due to a gcc bug. + + See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55227 for details. +*/ +static const struct lightcone_map_type lightcone_map_types[] = { + { + /* .name = */ "TotalMass", + /* .update_map = */ lightcone_map_total_mass_get_value, + /* .ptype_contributes = */ lightcone_map_total_mass_type_contributes, + /* .baseline_func = */ lightcone_map_total_mass_baseline_value, + /* .units = */ UNIT_CONV_MASS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "SmoothedGasMass", + /* .update_map = */ lightcone_map_gas_mass_get_value, + /* .ptype_contributes = */ lightcone_map_gas_mass_type_contributes, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_MASS, + /* .smoothing = */ map_smoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "UnsmoothedGasMass", + /* .update_map = */ lightcone_map_gas_mass_get_value, + /* .ptype_contributes = */ lightcone_map_gas_mass_type_contributes, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_MASS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "DarkMatterMass", + /* .update_map = */ lightcone_map_dark_matter_mass_get_value, + /* .ptype_contributes = */ + lightcone_map_dark_matter_mass_type_contributes, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_MASS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "StellarMass", + /* .update_map = */ lightcone_map_stellar_mass_get_value, + /* .ptype_contributes = */ lightcone_map_stellar_mass_type_contributes, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_MASS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "BlackHoleMass", + /* .update_map = */ lightcone_map_black_hole_mass_get_value, + /* .ptype_contributes = */ + lightcone_map_black_hole_mass_type_contributes, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_MASS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* .name = */ "StarFormationRate", + /* .update_map = */ lightcone_map_sfr_get_value, + /* .ptype_contributes = */ lightcone_map_sfr_type_contributes, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_SFR, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* NULL functions indicate end of array */ + /* .name = */ "", + /* .update_map = */ NULL, + /* .ptype_contributes = */ NULL, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_NO_UNITS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, +}; + +#endif diff --git a/src/lightcone/lightcone_particle_io.c b/src/lightcone/lightcone_particle_io.c new file mode 100644 index 0000000000000000000000000000000000000000..2d98f46d20996ad78dbbd73c882125e08f985869 --- /dev/null +++ b/src/lightcone/lightcone_particle_io.c @@ -0,0 +1,892 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 <hdf5.h> + +/* This object's header. */ +#include "lightcone/lightcone_particle_io.h" + +/* Local headers */ +#include "black_holes.h" +#include "chemistry.h" +#include "chemistry_struct.h" +#include "cooling.h" +#include "engine.h" +#include "error.h" +#include "gravity.h" +#include "lightcone/lightcone.h" +#include "neutrino.h" +#include "particle_buffer.h" +#include "stars.h" + +void lightcone_io_field_list_init(struct lightcone_io_field_list *list) { + + list->first = NULL; + list->last = NULL; + list->num_fields = 0; +} + +void lightcone_io_field_list_clean(struct lightcone_io_field_list *list) { + + struct lightcone_io_field *current; + struct lightcone_io_field *next; + + current = list->first; + while (current) { + next = current->next; + free(current); + current = next; + } + + list->first = NULL; + list->last = NULL; + list->num_fields = 0; +} + +void lightcone_io_field_list_append(struct lightcone_io_field_list *list, + char *name, enum IO_DATA_TYPE type, + int dimension, size_t offset, + enum unit_conversion_factor units, + float scale_factor_exponent, + char *compression) { + + /* Make the new lightcone_io_field struct */ + struct lightcone_io_field *r = + (struct lightcone_io_field *)malloc(sizeof(struct lightcone_io_field)); + bzero(r, sizeof(struct lightcone_io_field)); + strcpy(r->name, name); + r->type = type; + r->dimension = dimension; + r->offset = offset; + r->units = units; + r->scale_factor_exponent = scale_factor_exponent; + r->compression = compression_scheme_from_name(compression); + r->next = NULL; + + /* Append to the linked list */ + if (list->last) { + list->last->next = r; + } else { + list->first = r; + } + list->last = r; + list->num_fields += 1; +} + +/** + * @brief Make a linked list of output fields for gas particles + */ +void lightcone_io_append_gas_output_fields( + struct lightcone_io_field_list *list) { + +#define OFFSET(x) offsetof(struct lightcone_gas_data, x) + lightcone_io_field_list_append(list, "ParticleIDs", LONGLONG, 1, OFFSET(id), + UNIT_CONV_NO_UNITS, 0.0, "Nbit40"); + lightcone_io_field_list_append(list, "Coordinates", DOUBLE, 3, OFFSET(x), + UNIT_CONV_LENGTH, 1.0, "DScale5"); + lightcone_io_field_list_append(list, "Velocities", FLOAT, 3, OFFSET(vel), + UNIT_CONV_SPEED, 0.0, "DScale1"); + lightcone_io_field_list_append(list, "Masses", FLOAT, 1, OFFSET(mass), + UNIT_CONV_MASS, 0.0, "on"); + lightcone_io_field_list_append(list, "ExpansionFactors", FLOAT, 1, OFFSET(a), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append(list, "SmoothingLengths", FLOAT, 1, OFFSET(h), + UNIT_CONV_LENGTH, 0.0, "on"); + lightcone_io_field_list_append(list, "Densities", FLOAT, 1, OFFSET(rho), + UNIT_CONV_DENSITY, -3.0, "FMantissa9"); + lightcone_io_field_list_append(list, "Temperatures", FLOAT, 1, + OFFSET(temperature), UNIT_CONV_TEMPERATURE, + 0.0, "FMantissa9"); +#ifdef CHEMISTRY_EAGLE + lightcone_io_field_list_append(list, "SmoothedElementMassFractions", FLOAT, + chemistry_element_count, + OFFSET(smoothed_metal_mass_fraction), + UNIT_CONV_NO_UNITS, 0.0, "FMantissa9"); + lightcone_io_field_list_append(list, "SmoothedMetalMassFractions", FLOAT, 1, + OFFSET(smoothed_metal_mass_fraction_total), + UNIT_CONV_NO_UNITS, 0.0, "FMantissa9"); + lightcone_io_field_list_append(list, "MetalMassFractions", FLOAT, 1, + OFFSET(metal_mass_fraction_total), + UNIT_CONV_NO_UNITS, 0.0, "FMantissa9"); +#endif +#ifdef COOLING_COLIBRE + lightcone_io_field_list_append(list, "ElectronNumberDensities", DOUBLE, 1, + OFFSET(electron_density), + UNIT_CONV_NUMBER_DENSITY, 0.0, "DMantissa9"); + lightcone_io_field_list_append(list, "ComptonYParameters", DOUBLE, 1, + OFFSET(ycompton), UNIT_CONV_AREA, 0.0, + "DMantissa9"); +#endif +#ifdef WITH_FOF + lightcone_io_field_list_append(list, "FOFGroupIDs", LONGLONG, 1, + OFFSET(group_id), UNIT_CONV_NO_UNITS, 0.0, + "on"); +#endif +#if defined(TRACERS_EAGLE) || defined(TRACERS_FLAMINGO) + lightcone_io_field_list_append(list, "LastAGNFeedbackScaleFactors", FLOAT, 1, + OFFSET(last_AGN_injection_scale_factor), + UNIT_CONV_NO_UNITS, 0.0, "BFloat16"); +#endif +#ifdef STAR_FORMATION_EAGLE + lightcone_io_field_list_append(list, "StarFormationRates", FLOAT, 1, + OFFSET(sfr), UNIT_CONV_SFR, 0.0, "on"); +#endif +#undef OFFSET +} + +/** + * @brief Make a linked list of output fields for DM particles + */ +void lightcone_io_append_dark_matter_output_fields( + struct lightcone_io_field_list *list) { + +#define OFFSET(x) offsetof(struct lightcone_dark_matter_data, x) + lightcone_io_field_list_append(list, "ParticleIDs", LONGLONG, 1, OFFSET(id), + UNIT_CONV_NO_UNITS, 0.0, "Nbit40"); + lightcone_io_field_list_append(list, "Coordinates", DOUBLE, 3, OFFSET(x), + UNIT_CONV_LENGTH, 1.0, "DScale5"); + lightcone_io_field_list_append(list, "Velocities", FLOAT, 3, OFFSET(vel), + UNIT_CONV_SPEED, 0.0, "DScale1"); + lightcone_io_field_list_append(list, "Masses", FLOAT, 1, OFFSET(mass), + UNIT_CONV_MASS, 0.0, "on"); + lightcone_io_field_list_append(list, "ExpansionFactors", FLOAT, 1, OFFSET(a), + UNIT_CONV_NO_UNITS, 0.0, "on"); +#undef OFFSET +} + +/** + * @brief Make a linked list of output fields for DM background particles + */ +void lightcone_io_append_dark_matter_background_output_fields( + struct lightcone_io_field_list *list) { + +#define OFFSET(x) \ + offsetof(struct lightcone_dark_matter_data, \ + x) /* Uses same struct as dark matter */ + lightcone_io_field_list_append(list, "ParticleIDs", LONGLONG, 1, OFFSET(id), + UNIT_CONV_NO_UNITS, 0.0, "Nbit40"); + lightcone_io_field_list_append(list, "Coordinates", DOUBLE, 3, OFFSET(x), + UNIT_CONV_LENGTH, 1.0, "DScale5"); + lightcone_io_field_list_append(list, "Velocities", FLOAT, 3, OFFSET(vel), + UNIT_CONV_SPEED, 0.0, "DScale1"); + lightcone_io_field_list_append(list, "Masses", FLOAT, 1, OFFSET(mass), + UNIT_CONV_MASS, 0.0, "on"); + lightcone_io_field_list_append(list, "ExpansionFactors", FLOAT, 1, OFFSET(a), + UNIT_CONV_NO_UNITS, 0.0, "on"); +#undef OFFSET +} + +/** + * @brief Make a linked list of output fields for star particles + */ +void lightcone_io_append_stars_output_fields( + struct lightcone_io_field_list *list) { + +#define OFFSET(x) offsetof(struct lightcone_stars_data, x) + lightcone_io_field_list_append(list, "ParticleIDs", LONGLONG, 1, OFFSET(id), + UNIT_CONV_NO_UNITS, 0.0, "Nbit40"); + lightcone_io_field_list_append(list, "Coordinates", DOUBLE, 3, OFFSET(x), + UNIT_CONV_LENGTH, 1.0, "DScale5"); + lightcone_io_field_list_append(list, "Velocities", FLOAT, 3, OFFSET(vel), + UNIT_CONV_SPEED, 0.0, "DScale1"); + lightcone_io_field_list_append(list, "Masses", FLOAT, 1, OFFSET(mass), + UNIT_CONV_MASS, 0.0, "on"); + lightcone_io_field_list_append(list, "ExpansionFactors", FLOAT, 1, OFFSET(a), + UNIT_CONV_NO_UNITS, 0.0, "on"); +#ifdef WITH_FOF + lightcone_io_field_list_append(list, "FOFGroupIDs", LONGLONG, 1, + OFFSET(group_id), UNIT_CONV_NO_UNITS, 0.0, + "on"); +#endif +#ifdef STARS_EAGLE + lightcone_io_field_list_append(list, "InitialMasses", FLOAT, 1, + OFFSET(mass_init), UNIT_CONV_MASS, 0.0, + "FMantissa9"); + lightcone_io_field_list_append(list, "BirthScaleFactors", FLOAT, 1, + OFFSET(birth_scale_factor), UNIT_CONV_NO_UNITS, + 0.0, "FMantissa9"); + lightcone_io_field_list_append(list, "BirthDensities", FLOAT, 1, + OFFSET(birth_density), UNIT_CONV_DENSITY, 0.0, + "BFloat16"); + lightcone_io_field_list_append(list, "Luminosities", FLOAT, + luminosity_bands_count, OFFSET(luminosities), + UNIT_CONV_NO_UNITS, 0.0, "FMantissa9"); +#endif +#ifdef CHEMISTRY_EAGLE + lightcone_io_field_list_append(list, "SmoothedElementMassFractions", FLOAT, + chemistry_element_count, + OFFSET(smoothed_metal_mass_fraction), + UNIT_CONV_NO_UNITS, 0.0, "FMantissa9"); + lightcone_io_field_list_append(list, "SmoothedMetalMassFractions", FLOAT, 1, + OFFSET(smoothed_metal_mass_fraction_total), + UNIT_CONV_NO_UNITS, 0.0, "FMantissa9"); + lightcone_io_field_list_append(list, "MetalMassFractions", FLOAT, 1, + OFFSET(metal_mass_fraction_total), + UNIT_CONV_NO_UNITS, 0.0, "FMantissa9"); +#endif +#if defined(TRACERS_EAGLE) || defined(TRACERS_FLAMINGO) + lightcone_io_field_list_append(list, "LastAGNFeedbackScaleFactors", FLOAT, 1, + OFFSET(last_AGN_injection_scale_factor), + UNIT_CONV_NO_UNITS, 0.0, "BFloat16"); +#endif +#undef OFFSET +} + +/** + * @brief Make a linked list of output fields for black hole particles + */ +void lightcone_io_append_black_hole_output_fields( + struct lightcone_io_field_list *list) { + +#define OFFSET(x) offsetof(struct lightcone_black_hole_data, x) + lightcone_io_field_list_append(list, "ParticleIDs", LONGLONG, 1, OFFSET(id), + UNIT_CONV_NO_UNITS, 0.0, "Nbit40"); + lightcone_io_field_list_append(list, "Coordinates", DOUBLE, 3, OFFSET(x), + UNIT_CONV_LENGTH, 1.0, "DScale5"); + lightcone_io_field_list_append(list, "Velocities", FLOAT, 3, OFFSET(vel), + UNIT_CONV_SPEED, 0.0, "DScale1"); + lightcone_io_field_list_append(list, "DynamicalMasses", FLOAT, 1, + OFFSET(mass), UNIT_CONV_MASS, 0.0, "on"); + lightcone_io_field_list_append(list, "ExpansionFactors", FLOAT, 1, OFFSET(a), + UNIT_CONV_NO_UNITS, 0.0, "on"); +#ifdef BLACK_HOLES_EAGLE + lightcone_io_field_list_append(list, "SubgridMasses", FLOAT, 1, + OFFSET(subgrid_mass), UNIT_CONV_MASS, 0.0, + "on"); + lightcone_io_field_list_append(list, "FormationScaleFactors", FLOAT, 1, + OFFSET(formation_scale_factor), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append(list, "AccretionRates", FLOAT, 1, + OFFSET(accretion_rate), + UNIT_CONV_MASS_PER_UNIT_TIME, 0.0, "on"); + lightcone_io_field_list_append(list, "TotalAccretedMasses", FLOAT, 1, + OFFSET(total_accreted_mass), UNIT_CONV_MASS, + 0.0, "on"); + lightcone_io_field_list_append(list, "LastMinorMergerScaleFactors", FLOAT, 1, + OFFSET(last_minor_merger_scale_factor), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append(list, "LastMajorMergerScaleFactors", FLOAT, 1, + OFFSET(last_major_merger_scale_factor), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append(list, "NumberOfMergers", INT, 1, + OFFSET(number_of_mergers), UNIT_CONV_NO_UNITS, + 0.0, "on"); + lightcone_io_field_list_append(list, "LastAGNFeedbackScaleFactors", FLOAT, 1, + OFFSET(last_AGN_event_scale_factor), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append(list, "NumberOfAGNEvents", INT, 1, + OFFSET(AGN_number_of_AGN_events), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append(list, "NumberOfHeatingEvents", INT, 1, + OFFSET(AGN_number_of_energy_injections), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append( + list, "LastHighEddingtonFractionScaleFactors", FLOAT, 1, + OFFSET(last_high_Eddington_fraction_scale_factor), UNIT_CONV_NO_UNITS, + 0.0, "on"); + lightcone_io_field_list_append(list, "CumulativeNumberOfSeeds", INT, 1, + OFFSET(cumulative_number_seeds), + UNIT_CONV_NO_UNITS, 0.0, "on"); +#ifdef WITH_FOF + lightcone_io_field_list_append(list, "FOFGroupIDs", LONGLONG, 1, + OFFSET(group_id), UNIT_CONV_NO_UNITS, 0.0, + "on"); +#endif +#endif +#undef OFFSET +} + +/** + * @brief Make a linked list of output fields for neutrino particles + */ +void lightcone_io_append_neutrino_output_fields( + struct lightcone_io_field_list *list) { + +#define OFFSET(x) offsetof(struct lightcone_neutrino_data, x) + lightcone_io_field_list_append(list, "ParticleIDs", LONGLONG, 1, OFFSET(id), + UNIT_CONV_NO_UNITS, 0.0, "Nbit40"); + lightcone_io_field_list_append(list, "Coordinates", DOUBLE, 3, OFFSET(x), + UNIT_CONV_LENGTH, 1.0, "DScale5"); + lightcone_io_field_list_append(list, "Velocities", FLOAT, 3, OFFSET(vel), + UNIT_CONV_SPEED, 0.0, "DScale1"); + lightcone_io_field_list_append(list, "Masses", FLOAT, 1, OFFSET(mass), + UNIT_CONV_MASS, 0.0, "on"); + lightcone_io_field_list_append(list, "Weights", FLOAT, 1, OFFSET(weight), + UNIT_CONV_NO_UNITS, 0.0, "on"); + lightcone_io_field_list_append(list, "ExpansionFactors", FLOAT, 1, OFFSET(a), + UNIT_CONV_NO_UNITS, 0.0, "on"); +#undef OFFSET +} + +/* + Functions to store particle properties in the lightcone_*_data structs. + + These should determine whether the particle should be included in the + lightcone and, if so, copy the needed quantities into the struct and + return 1. If the particle should be discarded the function should + return 0. + + */ + +/** + * @brief Store gas properties to write to the lightcone + * + * If the particle should be included in the lightcone output this function + * copies its information to the lightcone_gas_data struct and returns 1. + * If the particle should not be output the function returns 0. + * + * @param e the #engine structure + * @param gp the #gpart which crossed the lightcone + * @param p the #part associated with this #gpart + * @param xp the #xpart associated with this #gpart + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + * @param the #lightcone_gas_data struct to update + */ +int lightcone_store_gas(const struct engine *e, struct lightcone_props *props, + const struct gpart *gp, const struct part *p, + const struct xpart *xp, const double a_cross, + const double x_cross[3], + struct lightcone_gas_data *data) { + + /*! Check if we're filtering gas particles */ + if (props->gas_filtering_enabled) { + if (a_cross < props->max_a_for_gas_filtering) { + + /* Check hydrogen number density of this particle */ +#ifdef CHEMISTRY_EAGLE + const double density = p->rho; + const double proton_mass = e->physical_constants->const_proton_mass; + const double hydrogen_fraction = + p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + const double nh = density * hydrogen_fraction / proton_mass; + if (nh < props->min_nh_for_filtered_gas * pow(a_cross, -4.0)) return 0; +#else + error( + "Lightcone gas particle filtering is only implemented for EAGLE " + "chemistry"); +#endif + /* Check temperature of this particle */ + const double T = cooling_get_temperature( + e->physical_constants, e->hydro_properties, e->internal_units, + e->cosmology, e->cooling_func, p, xp); + if (T < props->min_temp_for_filtered_gas) return 0; + } + } + + data->id = p->id; + data->x[0] = x_cross[0]; + data->x[1] = x_cross[1]; + data->x[2] = x_cross[2]; + data->vel[0] = + xp->v_full[0] / a_cross; // TODO: extrapolate velocities to a_cross? + data->vel[1] = xp->v_full[1] / a_cross; + data->vel[2] = xp->v_full[2] / a_cross; + data->mass = hydro_get_mass(p); + data->a = a_cross; + data->h = p->h; + data->rho = p->rho; + data->temperature = cooling_get_temperature( + e->physical_constants, e->hydro_properties, e->internal_units, + e->cosmology, e->cooling_func, p, xp); +#ifdef WITH_FOF + data->group_id = (long long)gp->fof_data.group_id; +#endif + +#ifdef CHEMISTRY_EAGLE + for (int i = 0; i < chemistry_element_count; i += 1) + data->smoothed_metal_mass_fraction[i] = + p->chemistry_data.smoothed_metal_mass_fraction[i]; + data->metal_mass_fraction_total = p->chemistry_data.metal_mass_fraction_total; + data->smoothed_metal_mass_fraction_total = + p->chemistry_data.smoothed_metal_mass_fraction_total; +#endif + +#ifdef COOLING_COLIBRE + data->electron_density = cooling_get_electron_density( + e->physical_constants, e->hydro_properties, e->internal_units, + e->cosmology, e->cooling_func, p, xp); + data->ycompton = cooling_get_ycompton(e->physical_constants, + e->hydro_properties, e->internal_units, + e->cosmology, e->cooling_func, p, xp); +#endif + +#if defined(TRACERS_EAGLE) || defined(TRACERS_FLAMINGO) + data->last_AGN_injection_scale_factor = + xp->tracers_data.last_AGN_injection_scale_factor; +#endif + +#ifdef STAR_FORMATION_EAGLE + data->sfr = xp->sf_data.SFR; +#endif + + return 1; +} + +/** + * @brief Store dark matter properties to write to the lightcone + * + * If the particle should be included in the lightcone output this function + * copies its information to the lightcone_dark_matter_data struct and returns + * 1. If the particle should not be output the function returns 0. + * + * @param e the #engine structure + * @param gp the #gpart which crossed the lightcone + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + * @param the #lightcone_dark_matter_data struct to update + */ +int lightcone_store_dark_matter(const struct engine *e, + struct lightcone_props *props, + const struct gpart *gp, const double a_cross, + const double x_cross[3], + struct lightcone_dark_matter_data *data) { + data->id = gp->id_or_neg_offset; + data->x[0] = x_cross[0]; + data->x[1] = x_cross[1]; + data->x[2] = x_cross[2]; + data->vel[0] = + gp->v_full[0] / a_cross; // TODO: extrapolate velocities to a_cross? + data->vel[1] = gp->v_full[1] / a_cross; + data->vel[2] = gp->v_full[2] / a_cross; + data->mass = gp->mass; + data->a = a_cross; + + return 1; +} + +/** + * @brief Store star properties to write to the lightcone + * + * If the particle should be included in the lightcone output this function + * copies its information to the lightcone_star_data struct and returns + * 1. If the particle should not be output the function returns 0. + * + * @param e the #engine structure + * @param gp the #gpart which crossed the lightcone + * @param sp the #spart associated with the #gpart + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + * @param the #lightcone_stars_data struct to update + */ +int lightcone_store_stars(const struct engine *e, struct lightcone_props *props, + const struct gpart *gp, const struct spart *sp, + const double a_cross, const double x_cross[3], + struct lightcone_stars_data *data) { + data->id = sp->id; + data->x[0] = x_cross[0]; + data->x[1] = x_cross[1]; + data->x[2] = x_cross[2]; + data->vel[0] = + sp->v[0] / a_cross; // TODO: extrapolate velocities to a_cross? + data->vel[1] = sp->v[1] / a_cross; + data->vel[2] = sp->v[2] / a_cross; + data->mass = sp->mass; + data->a = a_cross; + +#ifdef WITH_FOF + data->group_id = (long long)gp->fof_data.group_id; +#endif + +#ifdef STARS_EAGLE + data->mass_init = sp->mass_init; + data->birth_scale_factor = sp->birth_scale_factor; + data->birth_density = sp->birth_density; + stars_get_luminosities(sp, e->policy & engine_policy_cosmology, e->cosmology, + e->time, e->physical_constants, e->stars_properties, + data->luminosities); +#endif + +#ifdef CHEMISTRY_EAGLE + for (int i = 0; i < chemistry_element_count; i += 1) + data->smoothed_metal_mass_fraction[i] = + sp->chemistry_data.smoothed_metal_mass_fraction[i]; + data->metal_mass_fraction_total = + sp->chemistry_data.metal_mass_fraction_total; + data->smoothed_metal_mass_fraction_total = + sp->chemistry_data.smoothed_metal_mass_fraction_total; +#endif + +#if defined(TRACERS_EAGLE) || defined(TRACERS_FLAMINGO) + data->last_AGN_injection_scale_factor = + sp->tracers_data.last_AGN_injection_scale_factor; +#endif + + return 1; +} + +/** + * @brief Store black hole properties to write to the lightcone + * + * If the particle should be included in the lightcone output this function + * copies its information to the lightcone_black_hole_data struct and returns + * 1. If the particle should not be output the function returns 0. + * + * @param e the #engine structure + * @param gp the #gpart which crossed the lightcone + * @param bp the #bpart associated with the #gpart + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + * @param the #lightcone_black_hole_data struct to update + */ +int lightcone_store_black_hole(const struct engine *e, + struct lightcone_props *props, + const struct gpart *gp, const struct bpart *bp, + const double a_cross, const double x_cross[3], + struct lightcone_black_hole_data *data) { + data->id = bp->id; + data->x[0] = x_cross[0]; + data->x[1] = x_cross[1]; + data->x[2] = x_cross[2]; + data->vel[0] = + bp->v[0] / a_cross; // TODO: extrapolate velocities to a_cross? + data->vel[1] = bp->v[1] / a_cross; + data->vel[2] = bp->v[2] / a_cross; + data->mass = bp->mass; + data->a = a_cross; +#ifdef BLACK_HOLES_EAGLE + data->subgrid_mass = bp->subgrid_mass; + data->formation_scale_factor = bp->formation_scale_factor; + data->accretion_rate = bp->accretion_rate; + data->total_accreted_mass = bp->total_accreted_mass; + data->last_minor_merger_scale_factor = bp->last_minor_merger_scale_factor; + data->last_major_merger_scale_factor = bp->last_major_merger_scale_factor; + data->number_of_mergers = bp->number_of_mergers; + data->last_AGN_event_scale_factor = bp->last_AGN_event_scale_factor; + data->AGN_number_of_AGN_events = bp->AGN_number_of_AGN_events; + data->AGN_number_of_energy_injections = bp->AGN_number_of_energy_injections; + data->last_high_Eddington_fraction_scale_factor = + bp->last_high_Eddington_fraction_scale_factor; + data->cumulative_number_seeds = bp->cumulative_number_seeds; +#ifdef WITH_FOF + data->group_id = (long long)gp->fof_data.group_id; +#endif +#endif + return 1; +} + +/** + * @brief Store neutrino properties to write to the lightcone + * + * If the particle should be included in the lightcone output this function + * copies its information to the lightcone_neutrino_data struct and returns + * 1. If the particle should not be output the function returns 0. + * + * @param e the #engine structure + * @param gp the #gpart which crossed the lightcone + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + * @param the #lightcone_neutrino_data struct to update + */ +int lightcone_store_neutrino(const struct engine *e, + struct lightcone_props *props, + const struct gpart *gp, const double a_cross, + const double x_cross[3], + struct lightcone_neutrino_data *data) { + + /* Compute neutrino weight */ + struct neutrino_model nu_model; + bzero(&nu_model, sizeof(struct neutrino_model)); + if (e->neutrino_properties->use_delta_f_mesh_only) + gather_neutrino_consts(e->s, &nu_model); + double weight = 1.0; + gpart_neutrino_weight_mesh_only(gp, &nu_model, &weight); + + data->id = gp->id_or_neg_offset; + data->x[0] = x_cross[0]; + data->x[1] = x_cross[1]; + data->x[2] = x_cross[2]; + data->vel[0] = + gp->v_full[0] / a_cross; // TODO: extrapolate velocities to a_cross? + data->vel[1] = gp->v_full[1] / a_cross; + data->vel[2] = gp->v_full[2] / a_cross; + data->mass = gp->mass; + data->weight = weight; + data->a = a_cross; + + return 1; +} + +/** + * @brief Write data to a HDF5 dataset, appending along first axis if it already + * exists + */ +void append_dataset(const struct unit_system *snapshot_units, + enum unit_conversion_factor units, + float scale_factor_exponent, hid_t loc_id, const char *name, + hid_t mem_type_id, hsize_t chunk_size, + int lossy_compression, + enum lossy_compression_schemes compression_scheme, + int gzip_level, const int rank, const hsize_t *dims, + const hsize_t num_written, const void *data) { + + const int max_rank = 2; + if (rank > max_rank) + error("HDF5 dataset has too may dimensions. Increase max_rank."); + if (rank < 1) error("HDF5 dataset must be at least one dimensional"); + + /* If we have zero elements to append, there's nothing to do */ + if (dims[0] == 0) return; + + /* Determine size of the dataset after we append our data */ + hsize_t full_dims[max_rank]; + for (int i = 0; i < rank; i += 1) full_dims[i] = dims[i]; + full_dims[0] += num_written; + + /* Determine maximum size in each dimension */ + hsize_t max_dims[max_rank]; + for (int i = 1; i < rank; i += 1) max_dims[i] = full_dims[i]; + max_dims[0] = H5S_UNLIMITED; + + /* Determine chunk size in each dimension */ + hsize_t chunk_dims[max_rank]; + for (int i = 1; i < rank; i += 1) chunk_dims[i] = full_dims[i]; + chunk_dims[0] = (hsize_t)chunk_size; + + /* Find offset to region to write in each dimension */ + hsize_t offset[max_rank]; + for (int i = 1; i < rank; i += 1) offset[i] = 0; + offset[0] = num_written; + + hid_t dataset_id; + hid_t file_space_id; + if (num_written == 0) { + + /* We need to create a new dataset */ + file_space_id = H5Screate_simple(rank, full_dims, max_dims); + hid_t prop_id = H5Pcreate(H5P_DATASET_CREATE); + + /* Type of the dataset to create - this is initially the same as the type + in memory but may be modified by lossy compression. */ + hid_t file_type_id = H5Tcopy(mem_type_id); + + /* Set chunk size and lossy compression scheme, if any */ + H5Pset_chunk(prop_id, rank, chunk_dims); + char filter_name[32]; + if (lossy_compression && (compression_scheme != compression_write_lossless)) + set_hdf5_lossy_compression(&prop_id, &file_type_id, compression_scheme, + name, filter_name); + + /* Set lossless compression, if any */ + if (gzip_level > 0) { + H5Pset_shuffle(prop_id); + H5Pset_deflate(prop_id, gzip_level); + } + + /* Create the dataset */ + dataset_id = H5Dcreate(loc_id, name, file_type_id, file_space_id, + H5P_DEFAULT, prop_id, H5P_DEFAULT); + if (dataset_id < 0) error("Failed to create new dataset: %s", name); + H5Pclose(prop_id); + H5Tclose(file_type_id); + + /* Write unit conversion factors for this data set */ + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, snapshot_units, units, + scale_factor_exponent); + float baseUnitsExp[5]; + units_get_base_unit_exponents_array(baseUnitsExp, units); + io_write_attribute_f(dataset_id, "U_M exponent", baseUnitsExp[UNIT_MASS]); + io_write_attribute_f(dataset_id, "U_L exponent", baseUnitsExp[UNIT_LENGTH]); + io_write_attribute_f(dataset_id, "U_t exponent", baseUnitsExp[UNIT_TIME]); + io_write_attribute_f(dataset_id, "U_I exponent", + baseUnitsExp[UNIT_CURRENT]); + io_write_attribute_f(dataset_id, "U_T exponent", + baseUnitsExp[UNIT_TEMPERATURE]); + io_write_attribute_f(dataset_id, "h-scale exponent", 0.f); + io_write_attribute_f(dataset_id, "a-scale exponent", scale_factor_exponent); + io_write_attribute_s(dataset_id, "Expression for physical CGS units", + buffer); + + /* Write the actual number this conversion factor corresponds to */ + const double factor = units_cgs_conversion_factor(snapshot_units, units); + io_write_attribute_d( + dataset_id, + "Conversion factor to CGS (not including cosmological corrections)", + factor); + + /* Note that we can't write the conversion factor including cosmological + corrections as an attribute because it will be different for each + particle. */ + + } else { + + /* We're appending to an existing dataset */ + dataset_id = H5Dopen(loc_id, name, H5P_DEFAULT); + if (dataset_id < 0) error("Failed to open existing dataset: %s", name); + if (H5Dset_extent(dataset_id, full_dims) < 0) + error("Unable to extend dataset: %s", name); + file_space_id = H5Dget_space(dataset_id); + } + + /* Create memory dataspace */ + hid_t mem_space_id = H5Screate_simple(rank, dims, NULL); + + /* Select region to write in the file */ + if (H5Sselect_hyperslab(file_space_id, H5S_SELECT_SET, offset, NULL, dims, + NULL) < 0) + error("Failed to select region in dataset: %s", name); + + /* Write the data */ + if (H5Dwrite(dataset_id, mem_type_id, mem_space_id, file_space_id, + H5P_DEFAULT, data) < 0) + error("Failed to write dataset: %s", name); + + /* Clean up*/ + H5Sclose(file_space_id); + H5Sclose(mem_space_id); + H5Dclose(dataset_id); +} + +hid_t init_write(struct lightcone_props *props, hid_t file_id, int ptype, + size_t *num_written, size_t *num_to_write) { + + /* Number of particles already written to the file */ + *num_written = props->num_particles_written_to_file[ptype]; + + /* Number of buffered particles */ + *num_to_write = particle_buffer_num_elements(&props->buffer[ptype]); + + /* Create or open the HDF5 group for this particle type */ + const char *name = part_type_names[ptype]; + hid_t group_id; + if (*num_written > 0) { + group_id = H5Gopen(file_id, name, H5P_DEFAULT); + if (group_id < 0) error("Failed to open existing group: %s", name); + } else { + group_id = H5Gcreate(file_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (group_id < 0) error("Failed to create new group: %s", name); + } + return group_id; +} + +/** + * @brief Append buffered particles to the output file. + */ +void lightcone_write_particles(struct lightcone_props *props, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + int ptype, hid_t file_id) { + + if (props->particle_fields[ptype].num_fields > 0) { + + /* Open group and get number and offset of particles to write */ + size_t num_written, num_to_write; + hid_t group_id = + init_write(props, file_id, ptype, &num_written, &num_to_write); + + /* Get size of the data struct for this type */ + const size_t data_struct_size = lightcone_io_struct_size(ptype); + + /* Loop over output fields */ + struct lightcone_io_field *f = props->particle_fields[ptype].first; + while (f) { + + /* Find output field info */ + hid_t dtype_id = io_hdf5_type(f->type); /* HDF5 data type */ + size_t type_size = io_sizeof_type(f->type); /* Bytes per value */ + const size_t field_size = + f->dimension * type_size; /* Bytes per particle */ + const enum lossy_compression_schemes compression_scheme = + f->compression; /* Compression scheme */ + + /* Find unit conversion factor for this quantity */ + const double conversion_factor = + units_conversion_factor(internal_units, snapshot_units, f->units); + + /* Allocate output buffer */ + char *outbuf = (char *)malloc(num_to_write * field_size); + if (!outbuf) error("Unable to allocate lightcone output buffer"); + char *outptr = outbuf; + + /* Loop over blocks of buffered particles and copy to output array */ + size_t num_elements; + struct particle_buffer_block *block = NULL; + char *block_data; + do { + particle_buffer_iterate(&props->buffer[ptype], &block, &num_elements, + (void **)&block_data); + for (size_t i = 0; i < num_elements; i += 1) { + char *src = block_data + i * data_struct_size + f->offset; + char *dest = outptr; + memcpy(dest, src, field_size); + outptr += field_size; + } + } while (block); + + /* Convert units if necessary */ + if (conversion_factor != 1.0) { + const size_t nr_values = num_to_write * f->dimension; + switch (f->type) { + case INT: { + int *values = (int *)outbuf; + for (size_t i = 0; i < nr_values; i += 1) + values[i] *= conversion_factor; + } break; + case LONGLONG: { + long long *values = (long long *)outbuf; + for (size_t i = 0; i < nr_values; i += 1) + values[i] *= conversion_factor; + } break; + case FLOAT: { + float *values = (float *)outbuf; + for (size_t i = 0; i < nr_values; i += 1) + values[i] *= conversion_factor; + } break; + case DOUBLE: { + double *values = (double *)outbuf; + for (size_t i = 0; i < nr_values; i += 1) + values[i] *= conversion_factor; + } break; + default: + error("Unhandled data type"); + } + } + + /* Write the data */ + const hsize_t chunk_size = props->hdf5_chunk_size; + hsize_t dims[] = {(hsize_t)num_to_write, (hsize_t)f->dimension}; + int rank = 1; + if (f->dimension > 1) rank = 2; + append_dataset(snapshot_units, f->units, f->scale_factor_exponent, + group_id, f->name, dtype_id, chunk_size, + props->particles_lossy_compression, compression_scheme, + props->particles_gzip_level, rank, dims, num_written, + outbuf); + + /* Free the output buffer */ + free(outbuf); + + /* Advance to next output field */ + f = f->next; + } + + /* If all fields are done, we can close the particle type group */ + H5Gclose(group_id); + } +} diff --git a/src/lightcone/lightcone_particle_io.h b/src/lightcone/lightcone_particle_io.h new file mode 100644 index 0000000000000000000000000000000000000000..8ebe555bf9c3737aec8ae32d334396bae1eb8022 --- /dev/null +++ b/src/lightcone/lightcone_particle_io.h @@ -0,0 +1,279 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_LIGHTCONE_PARTICLE_IO_H +#define SWIFT_LIGHTCONE_PARTICLE_IO_H + +/* Config parameters. */ +#include <config.h> + +/* Some standard headers. */ +#include <hdf5.h> +#include <string.h> + +/* Local headers. */ +#include "chemistry.h" +#include "common_io.h" +#include "error.h" +#include "io_compression.h" +#include "part_type.h" +#include "stars.h" +#include "units.h" + +/* Forward declarations */ +struct gpart; +struct part; +struct xpart; +struct spart; +struct bpart; +struct lightcone_props; +struct engine; + +/* + * Struct to describe an output field in the lightcone + */ +struct lightcone_io_field { + + /* Name */ + char name[FIELD_BUFFER_SIZE]; + + /* Type of the field */ + enum IO_DATA_TYPE type; + + /* Dimension (1D, 3D, ...) */ + int dimension; + + /* Offset to this field in the data struct */ + size_t offset; + + /* Units of this quantity */ + enum unit_conversion_factor units; + + /* Scale-factor exponent to apply for unit conversion to physical */ + float scale_factor_exponent; + + /* Lossy compression to use for this field */ + enum lossy_compression_schemes compression; + + /* Pointer to the next field */ + struct lightcone_io_field *next; +}; + +/* + * Struct to store a linked list of lightcone_io_props + */ +struct lightcone_io_field_list { + + /* Pointer to the first field */ + struct lightcone_io_field *first; + + /* Pointer to the last field */ + struct lightcone_io_field *last; + + /* Number of fields */ + int num_fields; +}; + +/** + * @brief Gas particle data for lightcone output + */ +struct lightcone_gas_data { + long long id; + double x[3]; + float vel[3]; + float mass; + float a; + float h; + float rho; + float temperature; +#ifdef CHEMISTRY_EAGLE + float smoothed_metal_mass_fraction[chemistry_element_count]; + float metal_mass_fraction_total; + float smoothed_metal_mass_fraction_total; +#endif +#ifdef COOLING_COLIBRE + double electron_density; + double ycompton; +#endif +#ifdef WITH_FOF + long long group_id; +#endif +#if defined(TRACERS_EAGLE) || defined(TRACERS_FLAMINGO) + float last_AGN_injection_scale_factor; +#endif +#ifdef STAR_FORMATION_EAGLE + float sfr; +#endif +}; + +int lightcone_store_gas(const struct engine *e, struct lightcone_props *props, + const struct gpart *gp, const struct part *p, + const struct xpart *xp, const double a_cross, + const double x_cross[3], + struct lightcone_gas_data *data); + +/** + * @brief Dark matter particle data for lightcone output + */ +struct lightcone_dark_matter_data { + long long id; + double x[3]; + float vel[3]; + float mass; + float a; +}; + +int lightcone_store_dark_matter(const struct engine *e, + struct lightcone_props *props, + const struct gpart *gp, const double a_cross, + const double x_cross[3], + struct lightcone_dark_matter_data *data); + +/** + * @brief Star particle data for lightcone output + */ +struct lightcone_stars_data { + long long id; + double x[3]; + float vel[3]; + float mass; + float a; +#ifdef WITH_FOF + long long group_id; +#endif +#ifdef STARS_EAGLE + float mass_init; + float birth_scale_factor; + float birth_density; + float luminosities[luminosity_bands_count]; +#endif +#ifdef CHEMISTRY_EAGLE + float smoothed_metal_mass_fraction[chemistry_element_count]; + float metal_mass_fraction_total; + float smoothed_metal_mass_fraction_total; +#endif +#if defined(TRACERS_EAGLE) || defined(TRACERS_FLAMINGO) + float last_AGN_injection_scale_factor; +#endif +}; + +int lightcone_store_stars(const struct engine *e, struct lightcone_props *props, + const struct gpart *gp, const struct spart *sp, + const double a_cross, const double x_cross[3], + struct lightcone_stars_data *data); + +/** + * @brief Black hole particle data for lightcone output + */ +struct lightcone_black_hole_data { + long long id; + double x[3]; + float vel[3]; + float mass; + float a; +#ifdef BLACK_HOLES_EAGLE + float subgrid_mass; + float formation_scale_factor; + float accretion_rate; + float total_accreted_mass; + float last_minor_merger_scale_factor; + float last_major_merger_scale_factor; + int number_of_mergers; + float last_AGN_event_scale_factor; + int AGN_number_of_AGN_events; + int AGN_number_of_energy_injections; + float last_high_Eddington_fraction_scale_factor; + int cumulative_number_seeds; +#ifdef WITH_FOF + long long group_id; +#endif +#endif +}; + +int lightcone_store_black_hole(const struct engine *e, + struct lightcone_props *props, + const struct gpart *gp, const struct bpart *bp, + const double a_cross, const double x_cross[3], + struct lightcone_black_hole_data *data); + +/** + * @brief Neutrino particle data for lightcone output + */ +struct lightcone_neutrino_data { + long long id; + double x[3]; + float vel[3]; + float mass; + float weight; + float a; +}; + +int lightcone_store_neutrino(const struct engine *e, + struct lightcone_props *props, + const struct gpart *gp, const double a_cross, + const double x_cross[3], + struct lightcone_neutrino_data *data); + +void lightcone_write_particles(struct lightcone_props *props, + const struct unit_system *internal_units, + const struct unit_system *snapshot_units, + int ptype, hid_t file_id); + +inline static size_t lightcone_io_struct_size(int ptype) { + switch (ptype) { + case swift_type_dark_matter: + case swift_type_dark_matter_background: + return sizeof(struct lightcone_dark_matter_data); + case swift_type_gas: + return sizeof(struct lightcone_gas_data); + case swift_type_stars: + return sizeof(struct lightcone_stars_data); + case swift_type_black_hole: + return sizeof(struct lightcone_black_hole_data); + case swift_type_neutrino: + return sizeof(struct lightcone_neutrino_data); + default: + error("Unhandled particle type"); + return 0; + } +} + +void lightcone_io_field_list_init(struct lightcone_io_field_list *list); +void lightcone_io_field_list_clean(struct lightcone_io_field_list *list); +void lightcone_io_field_list_append(struct lightcone_io_field_list *list, + char *name, enum IO_DATA_TYPE type, + int dimension, size_t offset, + enum unit_conversion_factor units, + float scale_factor_exponent, + char *compression); + +void lightcone_io_append_gas_output_fields( + struct lightcone_io_field_list *list); +void lightcone_io_append_dark_matter_output_fields( + struct lightcone_io_field_list *list); +void lightcone_io_append_dark_matter_background_output_fields( + struct lightcone_io_field_list *list); +void lightcone_io_append_stars_output_fields( + struct lightcone_io_field_list *list); +void lightcone_io_append_black_hole_output_fields( + struct lightcone_io_field_list *list); +void lightcone_io_append_neutrino_output_fields( + struct lightcone_io_field_list *list); + +#endif diff --git a/src/lightcone/lightcone_replications.c b/src/lightcone/lightcone_replications.c new file mode 100644 index 0000000000000000000000000000000000000000..f65044814c71fb72298bf3da83404f8ac9c8ef09 --- /dev/null +++ b/src/lightcone/lightcone_replications.c @@ -0,0 +1,286 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 "lightcone/lightcone_replications.h" + +#include "align.h" +#include "cell.h" +#include "error.h" +#include "memuse.h" + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +/** + * @brief Comparison function for sorting replications + * + * a The first replication + * b The second replication + * + */ +static int compare_replication_rmin(const void *a, const void *b) { + const struct replication *rep_a = (struct replication *)a; + const struct replication *rep_b = (struct replication *)b; + if (rep_a->rmin2 < rep_b->rmin2) + return -1; + else if (rep_a->rmin2 > rep_b->rmin2) + return 1; + else + return 0; +} + +/** + * @brief Make a list of periodic box replications which overlap + * the specified distance range from an observer. + * + * @param boxsize Size of the cubic simulation box. + * @param observer_position Location of the observer. + * @param lightcone_rmin Minimum distance from the observer. + * @param lightcone_rmax Maximum distance from the observer. + * of particles. + * @param replication_list Pointer to the struct to initialise. + */ + +void replication_list_init(struct replication_list *replication_list, + double boxsize, double cell_width, + double observer_position[3], double lightcone_rmin, + double lightcone_rmax) { + + /* Find range of replications to examine in each dimension */ + int rep_min[3]; + int rep_max[3]; + for (int i = 0; i < 3; i += 1) { + rep_min[i] = (int)floor( + (observer_position[i] - lightcone_rmax - 0.5 * cell_width) / boxsize); + rep_max[i] = (int)floor( + (observer_position[i] + lightcone_rmax + 0.5 * cell_width) / boxsize); + } + + /* On first pass just count replications */ + for (int ipass = 0; ipass < 2; ipass += 1) { + + replication_list->nrep = 0; + + /* Loop over periodic replications */ + for (int i = rep_min[0]; i <= rep_max[0]; i += 1) { + for (int j = rep_min[1]; j <= rep_max[1]; j += 1) { + for (int k = rep_min[2]; k <= rep_max[2]; k += 1) { + + /* Find centre of this replication relative to observer */ + double cx = boxsize * i + 0.5 * boxsize - observer_position[0]; + double cy = boxsize * j + 0.5 * boxsize - observer_position[1]; + double cz = boxsize * k + 0.5 * boxsize - observer_position[2]; + + /* Find distance to closest possible particle in this replication */ + double dx, dy, dz; + dx = fabs(cx) - 0.5 * boxsize - 0.5 * cell_width; + if (dx < 0) dx = 0; + dy = fabs(cy) - 0.5 * boxsize - 0.5 * cell_width; + if (dy < 0) dy = 0; + dz = fabs(cz) - 0.5 * boxsize - 0.5 * cell_width; + if (dz < 0) dz = 0; + double rep_rmin = sqrt(dx * dx + dy * dy + dz * dz); + + /* Find distance to most distant possible particle in this replication + */ + dx = fabs(cx) + 0.5 * boxsize + 0.5 * cell_width; + dy = fabs(cy) + 0.5 * boxsize + 0.5 * cell_width; + dz = fabs(cz) + 0.5 * boxsize + 0.5 * cell_width; + double rep_rmax = sqrt(dx * dx + dy * dy + dz * dz); + + /* Flag if any point in this replication could be in the lightcone */ + int in_lightcone = 1; + + /* Check distance limits */ + if (rep_rmax < lightcone_rmin || rep_rmin > lightcone_rmax) + in_lightcone = 0; + + if (in_lightcone) { + /* Store replications on second pass */ + if (ipass == 1) { + /* Get a pointer to the next replication */ + const int nrep = replication_list->nrep; + struct replication *rep = replication_list->replication + nrep; + /* Store info about this replication */ + rep->rmin2 = pow(rep_rmin, 2.0); + rep->rmax2 = pow(rep_rmax, 2.0); + rep->coord[0] = i * boxsize; + rep->coord[1] = j * boxsize; + rep->coord[2] = k * boxsize; + } + replication_list->nrep += 1; + } + } /* Next replication in z */ + } /* Next replication in y */ + } /* Next replication in x */ + + /* Allocate storage after first pass */ + if (ipass == 0) { + const int nrep = replication_list->nrep; + if (swift_memalign( + "lightcone_replications", (void **)&replication_list->replication, + SWIFT_STRUCT_ALIGNMENT, sizeof(struct replication) * nrep) != 0) { + error("Failed to allocate lightcone replication list"); + } + } + } /* Next pass */ + + /* Now sort replications by minimum distance */ + qsort(replication_list->replication, (size_t)replication_list->nrep, + sizeof(struct replication), compare_replication_rmin); + + /* Record the distance limits we used - may need these to refine the list + * later */ + replication_list->lightcone_rmin = lightcone_rmin; + replication_list->lightcone_rmax = lightcone_rmax; +} + +/** + * @brief Make an empty replication list + * + * @param replication_list Pointer to the struct to initialise. + */ +void replication_list_init_empty(struct replication_list *replication_list) { + + const int nrep = 0; + if (swift_memalign( + "lightcone_replications", (void **)&replication_list->replication, + SWIFT_STRUCT_ALIGNMENT, sizeof(struct replication) * nrep) != 0) { + error("Failed to allocate lightcone replication list"); + } + replication_list->lightcone_rmin = 0.0; + replication_list->lightcone_rmax = 0.0; + replication_list->nrep = 0; +} + +/** + * @brief Deallocate a replication list + * + * @param replication_list Pointer to the struct to deallocate. + */ +void replication_list_clean(struct replication_list *replication_list) { + swift_free("lightcone_replications", replication_list->replication); + replication_list->replication = NULL; + replication_list->nrep = 0; +} + +/** + * @brief Write a replication list to a file as text + * + * @param replication_list The replication list + * @param fd The file to write to + */ + +void replication_list_write(struct replication_list *replication_list, + FILE *fd) { + + for (int i = 0; i < replication_list->nrep; i += 1) { + fprintf(fd, "%e, %e, %e, %e, %e\n", + replication_list->replication[i].coord[0], + replication_list->replication[i].coord[1], + replication_list->replication[i].coord[2], + sqrt(replication_list->replication[i].rmin2), + sqrt(replication_list->replication[i].rmax2)); + } +} + +/** + * Determine subset of replications which overlap a #cell + * + * @param rep_in The input replication list + * @param cell The input cell + * @param rep_out The output replication list + * + * Initializes rep_out, which must then be freed with + * replication_list_clean(). + * + */ +void replication_list_subset_for_cell(const struct replication_list *rep_in, + const struct cell *cell, + const double observer_position[3], + struct replication_list *rep_out) { + + /* Find centre coordinates of this cell */ + const double cell_centre[] = {cell->loc[0] + 0.5 * cell->width[0], + cell->loc[1] + 0.5 * cell->width[1], + cell->loc[2] + 0.5 * cell->width[2]}; + + /* Find 'effective' width of this cell - particles can wander out of the cell + * by up to half a cell width */ + const double cell_eff_width[] = {2.0 * cell->width[0], 2.0 * cell->width[1], + 2.0 * cell->width[2]}; + + /* Allocate array of replications for the new list */ + const int nrep_max = rep_in->nrep; + if (swift_memalign("lightcone_replications", (void **)&rep_out->replication, + SWIFT_STRUCT_ALIGNMENT, + sizeof(struct replication) * nrep_max) != 0) { + error("Failed to allocate pruned lightcone replication list"); + } + + /* Get distance limits (squared) used to make the input list */ + const double lightcone_rmin2 = pow(rep_in->lightcone_rmin, 2.0); + const double lightcone_rmax2 = pow(rep_in->lightcone_rmax, 2.0); + + /* Loop over all replications */ + rep_out->nrep = 0; + for (int i = 0; i < nrep_max; i += 1) { + + /* Get a pointer to this input replication */ + const struct replication *rep = rep_in->replication + i; + + /* Find coordinates of centre of this replication of the cell relative to + * the observer */ + double cell_rep_centre[3]; + for (int j = 0; j < 3; j += 1) { + cell_rep_centre[j] = + rep->coord[j] + cell_centre[j] - observer_position[j]; + } + + /* Compute minimum possible distance squared from observer to this + * replication of this cell */ + double cell_rmin2 = 0.0; + for (int j = 0; j < 3; j += 1) { + double dx = fabs(cell_rep_centre[j]) - 0.5 * cell_eff_width[j]; + if (dx < 0.0) dx = 0.0; + cell_rmin2 += dx * dx; + } + + /* Compute maximum possible distance squared from observer to this + * replication of this cell */ + double cell_rmax2 = 0.0; + for (int j = 0; j < 3; j += 1) { + double dx = fabs(cell_rep_centre[j]) + 0.5 * cell_eff_width[j]; + cell_rmax2 += dx * dx; + } + + /* Decide whether this cell could contribute to this replication */ + if (cell_rmax2 >= lightcone_rmin2 && cell_rmin2 <= lightcone_rmax2) { + memcpy(rep_out->replication + rep_out->nrep, rep, + sizeof(struct replication)); + rep_out->nrep += 1; + } + /* Next input replication */ + } + + /* Not used currently */ + rep_out->lightcone_rmin = rep_in->lightcone_rmin; + rep_out->lightcone_rmax = rep_in->lightcone_rmax; +} diff --git a/src/lightcone/lightcone_replications.h b/src/lightcone/lightcone_replications.h new file mode 100644 index 0000000000000000000000000000000000000000..e65cbb47e6597d86df79c6f06414978a6ec5eedb --- /dev/null +++ b/src/lightcone/lightcone_replications.h @@ -0,0 +1,75 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 <stdio.h> + +#ifndef SWIFT_PERIODIC_REPLICATIONS_H +#define SWIFT_PERIODIC_REPLICATIONS_H + +/* Forward declarations */ +struct cell; + +/* Struct to store information about one periodic replication of the simulation + * box */ +struct replication { + + /* Minimum distance squared from the observer to any point in the replication + */ + double rmin2; + + /* Maximum distance squared from the observer to any point in the replication + */ + double rmax2; + + /* Coordinates of the replication */ + double coord[3]; +}; + +/* Struct to store an array of periodic replications */ +struct replication_list { + + /* Number of replications*/ + int nrep; + + /* Distance limits used to make this replication list */ + double lightcone_rmin; + double lightcone_rmax; + + /* Array of replications with nrep elements */ + struct replication *replication; +}; + +void replication_list_init(struct replication_list *replication_list, + double boxsize, double cell_width, + double observer_position[3], double lightcone_rmin, + double lightcone_rmax); + +void replication_list_init_empty(struct replication_list *replication_list); + +void replication_list_clean(struct replication_list *replication_list); + +void replication_list_write(struct replication_list *replication_list, + FILE *fd); + +void replication_list_subset_for_cell(const struct replication_list *rep_in, + const struct cell *cell, + const double observer_position[3], + struct replication_list *rep_out); + +#endif diff --git a/src/lightcone/lightcone_shell.c b/src/lightcone/lightcone_shell.c new file mode 100644 index 0000000000000000000000000000000000000000..e3ac45885fd57efa2b14a67b1d376ee44f0d1568 --- /dev/null +++ b/src/lightcone/lightcone_shell.c @@ -0,0 +1,1058 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 <stdio.h> +#include <stdlib.h> + +/* HEALPix C API */ +#ifdef HAVE_CHEALPIX +#include <chealpix.h> +#endif + +/* Local headers */ +#include "cosmology.h" +#include "engine.h" +#include "exchange_structs.h" +#include "hydro.h" +#include "lightcone/healpix_util.h" + +/* This object's header. */ +#include "lightcone/lightcone_shell.h" + +/** + * @brief Read in shell radii for lightcone healpix maps + * + * Allocates the output array, shell_out. + * + * @param cosmo the #cosmology structure + * @param radius_file name of the file with shell radii + * @param nr_shells returns the number of shells + * @param shell_out returns the array of shells + */ +static void read_shell_radii(const struct cosmology *cosmo, + const char *radius_file, int *nr_shells, + struct lightcone_shell **shell_out) { + + /* Allow shell radii to be specified in several different units */ + enum shell_units { + not_known = 0, + comoving_distance = 1, + redshift = 2, + expansion_factor = 3 + }; + + FILE *fd = fopen(radius_file, "r"); + if (!fd) error("Failed to open lightcone radius file %s", radius_file); + + /* Count number of non-zero length lines */ + size_t len = 0; + char *line = NULL; + int nr_lines = 0; + while (getline(&line, &len, fd) != -1 && strlen(line) > 0) nr_lines += 1; + rewind(fd); + + /* Allocate output array */ + struct lightcone_shell *shell = (struct lightcone_shell *)malloc( + sizeof(struct lightcone_shell) * (nr_lines - 1)); + + /* Check header */ + enum shell_units units = not_known; + if (getline(&line, &len, fd) != -1) { + if (strcmp(line, + "# Minimum comoving distance, Maximum comoving distance\n") == + 0) { + units = comoving_distance; + } else if (strcmp(line, "# Minimum redshift, Maximum redshift\n") == 0) { + units = redshift; + } else if (strcmp( + line, + "# Maximum expansion factor, Minimum expansion factor\n") == + 0) { + units = expansion_factor; + } else { + error("Unrecognized header in radius file"); + } + } else { + error("Unable to read header in radius file"); + } + + /* Read lines */ + for (int i = 0; i < nr_lines - 1; i += 1) { + if (fscanf(fd, "%le, %le\n", &shell[i].rmin, &shell[i].rmax) != 2) + error("Failed to read line from radius file"); + } + fclose(fd); + *nr_shells = nr_lines - 1; + const int nr = *nr_shells; + free(line); + + /* Convert units */ + switch (units) { + case comoving_distance: + /* Input is already comoving distance */ + break; + case redshift: + /* Convert redshift to comoving distance */ + for (int i = 0; i < nr; i += 1) { + const double a_at_rmin = 1.0 / (1.0 + shell[i].rmin); + shell[i].rmin = cosmology_get_comoving_distance(cosmo, a_at_rmin); + const double a_at_rmax = 1.0 / (1.0 + shell[i].rmax); + shell[i].rmax = cosmology_get_comoving_distance(cosmo, a_at_rmax); + } + break; + case expansion_factor: + /* Convert expansion factor to comoving distance */ + for (int i = 0; i < nr; i += 1) { + shell[i].rmin = cosmology_get_comoving_distance(cosmo, shell[i].rmin); + shell[i].rmax = cosmology_get_comoving_distance(cosmo, shell[i].rmax); + } + break; + default: + error("unknown unit type"); + } + + /* Do some sanity checks on the radii */ + /* All values should be monotonically increasing */ + for (int i = 1; i < nr; i += 1) { + if (shell[i].rmin <= shell[i - 1].rmin) + error("Minimum radii should be monotonically increasing"); + if (shell[i].rmax <= shell[i - 1].rmax) + error("Maximum radii should be monotonically increasing"); + } + + /* Maximum radius should be greater than minimum */ + for (int i = 0; i < nr; i += 1) + if (shell[i].rmin >= shell[i].rmax) + error("Maximum radius should be greater than minimum"); + + /* Shells should not overlap */ + for (int i = 1; i < nr; i += 1) + if (shell[i].rmin < shell[i - 1].rmax) error("Shells should not overlap"); + + /* Return pointer to array */ + *shell_out = shell; +} + +/** + * @brief Creates an array of struct lightcone_shell + * + * Returns a pointer to the newly allocated array. Each shell + * contains one #lightcone_map for each healpix map to be produced + * by this lightcone. + * + * @param cosmo the #cosmology structure + * @param radius_file file with the shell radii + * @param nr_maps number of lightcone_maps per shell + * @param map_type specifies the types of healpix maps to make + * @param nside healpix resolution parameter + * @param total_nr_pix number of pixels in each map + * @param part_type specifies which particle types update which maps + * @param elements_per_block size of blocks used in the update buffers + * @param nr_shells_out returns the number of lightcone shells in the array + * + */ +struct lightcone_shell *lightcone_shell_array_init( + const struct cosmology *cosmo, const char *radius_file, int nr_maps, + struct lightcone_map_type *map_type, int nside, pixel_index_t total_nr_pix, + struct lightcone_particle_type *part_type, size_t elements_per_block, + int *nr_shells_out) { + + /* Read in the shell radii */ + int nr_shells = 0; + struct lightcone_shell *shell = NULL; + if (engine_rank == 0) + read_shell_radii(cosmo, radius_file, &nr_shells, &shell); +#ifdef WITH_MPI + MPI_Bcast(&nr_shells, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (engine_rank != 0) + shell = (struct lightcone_shell *)malloc(sizeof(struct lightcone_shell) * + nr_shells); + MPI_Bcast(shell, sizeof(struct lightcone_shell) * nr_shells, MPI_BYTE, 0, + MPI_COMM_WORLD); +#endif + + /* Compute expansion factor at shell edges */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + /* Inner edge of the shell */ + shell[shell_nr].amax = cosmology_scale_factor_at_comoving_distance( + cosmo, shell[shell_nr].rmin); + /* Outer edge of the shell */ + shell[shell_nr].amin = cosmology_scale_factor_at_comoving_distance( + cosmo, shell[shell_nr].rmax); + } + + /* Set initial state of the lightcone shells */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) + shell[shell_nr].state = shell_uninitialized; + + /* Allocate lightcone_map structs for each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + shell[shell_nr].nr_maps = nr_maps; + shell[shell_nr].map = + (struct lightcone_map *)malloc(nr_maps * sizeof(struct lightcone_map)); + } + + int comm_rank = 0, comm_size = 1; +#ifdef WITH_MPI + MPI_Comm_size(MPI_COMM_WORLD, &comm_size); + MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank); +#endif + + /* Determine how healpix maps will be distributed between MPI ranks */ + const pixel_index_t pix_per_rank = total_nr_pix / comm_size; + if (pix_per_rank == 0) error("Must have healpix npix > number of MPI ranks!"); + const pixel_index_t local_pix_offset = comm_rank * pix_per_rank; + pixel_index_t local_nr_pix; + if (comm_rank < comm_size - 1) + local_nr_pix = pix_per_rank; + else + local_nr_pix = total_nr_pix - (comm_size - 1) * pix_per_rank; + + /* Store this information in the shells */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + shell[shell_nr].nside = nside; + shell[shell_nr].total_nr_pix = total_nr_pix; + shell[shell_nr].pix_per_rank = total_nr_pix / comm_size; + shell[shell_nr].local_nr_pix = local_nr_pix; + shell[shell_nr].local_pix_offset = local_pix_offset; + } + + /* Initialize lightcone_maps for each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + lightcone_map_init(&shell[shell_nr].map[map_nr], nside, total_nr_pix, + pix_per_rank, local_nr_pix, local_pix_offset, + shell[shell_nr].rmin, shell[shell_nr].rmax, + map_type[map_nr]); + } + } + + /* Initialize data buffers for map updates - one per particle type per shell + */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + particle_buffer_init(&shell[shell_nr].buffer[ptype], + part_type[ptype].buffer_element_size, + elements_per_block, "lightcone_map_updates"); + } + } + + /* Return the array of shells */ + *nr_shells_out = nr_shells; + return shell; +} + +/** + * @brief Free an array of struct lightcone_shell + * + * This also cleans up the lightcone_maps in the shell and the + * update buffers. + * + * @param shell pointer to the array of lightcone_shells + * @param nr_shells number of shells in the array + */ +void lightcone_shell_array_free(struct lightcone_shell *shell, int nr_shells) { + + /* Free the lightcone healpix maps for each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + const int nr_maps = shell[shell_nr].nr_maps; + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + lightcone_map_clean(&shell[shell_nr].map[map_nr]); + } + } + + /* Free the arrays of lightcone_map structs for each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + free(shell[shell_nr].map); + } + + /* Free the buffers associated with each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + particle_buffer_free(&shell[shell_nr].buffer[ptype]); + } + } + + /* Free the array of shells */ + free(shell); +} + +/** + * @brief Dump the shell array to a restart file + * + * @param shell pointer to the array of lightcone_shells + * @param nr_shells number of shells in the array + * @param stream the output stream to write to + */ +void lightcone_shell_array_dump(const struct lightcone_shell *shell, + int nr_shells, FILE *stream) { + + /* Dump the array of shell structs */ + restart_write_blocks((void *)shell, sizeof(struct lightcone_shell), nr_shells, + stream, "lightcone_shells", "lightcone_shells"); + + /* Dump the lightcone maps associated with each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + const int nr_maps = shell[shell_nr].nr_maps; + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + lightcone_map_struct_dump(&shell[shell_nr].map[map_nr], stream); + } + } +} + +/** + * @brief Restore the shell array from a restart file + * + * @param stream the output stream to write to + * @param nr_shells number of shells in the array + * @param part_type specifies which particle types update which maps + * @param elements_per_block size of blocks used in the update buffers + * + */ +struct lightcone_shell *lightcone_shell_array_restore( + FILE *stream, int nr_shells, struct lightcone_particle_type *part_type, + size_t elements_per_block) { + + /* Restore the array of lightcone_shell structs */ + struct lightcone_shell *shell = (struct lightcone_shell *)malloc( + sizeof(struct lightcone_shell) * nr_shells); + restart_read_blocks((void *)shell, sizeof(struct lightcone_shell), nr_shells, + stream, NULL, "lightcone_shells"); + + /* Restore the lightcone maps associated with each shell */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + const int nr_maps = shell[shell_nr].nr_maps; + shell[shell_nr].map = + (struct lightcone_map *)malloc(sizeof(struct lightcone_map) * nr_maps); + for (int map_nr = 0; map_nr < nr_maps; map_nr += 1) { + lightcone_map_struct_restore(&shell[shell_nr].map[map_nr], stream); + } + } + + /* Initialise the map update buffers */ + for (int shell_nr = 0; shell_nr < nr_shells; shell_nr += 1) { + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + particle_buffer_init(&shell[shell_nr].buffer[ptype], + part_type[ptype].buffer_element_size, + elements_per_block, "lightcone_map_updates"); + } + } + + return shell; +} + +struct healpix_smoothing_mapper_data { + + /*! MPI rank */ + int comm_rank, comm_size; + + /*! Pointer to the lightcone shell we're updating */ + struct lightcone_shell *shell; + + /*! Information about the particle type we're updating */ + struct lightcone_particle_type *part_type; + + /*! Pointer to the send buffer for communication */ + union lightcone_map_buffer_entry *sendbuf; + + /*! Pointer to the projected kernel table */ + struct projected_kernel_table *kernel_table; +}; + +#ifdef HAVE_CHEALPIX +static pixel_index_t angle_to_pixel(int nside, double theta, double phi) { + int64_t ipring; + ang2pix_ring64(nside, theta, phi, &ipring); + return ipring; +} +#endif + +#ifdef WITH_MPI + +struct buffer_block_info { + + /*! Pointer to the buffer block */ + struct particle_buffer_block *block; + + /*! Number of elements from this block to go to each MPI rank */ + size_t *count; + + /*! Offsets at which to write elements in the send buffer */ + size_t *offset; + + /*! First destination rank each entry is to be sent to */ + int *first_dest; + + /*! Last destination rank each entry is to be sent to */ + int *last_dest; +}; + +#ifdef HAVE_CHEALPIX +static int pixel_to_rank(int comm_size, pixel_index_t pix_per_rank, + pixel_index_t pixel) { + int rank = pixel / pix_per_rank; + if (rank >= comm_size) rank = comm_size - 1; + return rank; +} +#endif + +/** + * @brief Count elements to send to each rank from each buffer block + * + * For each buffer_block_info in the input array, this counts how + * many lightcone map updates are to be sent to each MPI rank. + * It also determines the range of MPI ranks which each update + * needs to be sent to. Updates must be copied to several ranks + * if we're smoothing the maps and the smoothing kernel overlaps parts + * of the healpix map which are stored on different ranks. + * + * @param map_data Pointer to an array of buffer_block_info + * @param num_elements Number of elements buffer_block_info array + * @param extra_data Pointer to healpix_smoothing_mapper_data struct + * + */ +static void count_elements_to_send_mapper(void *map_data, int num_elements, + void *extra_data) { +#ifdef HAVE_CHEALPIX + + /* Unpack information about the array of blocks to process */ + struct buffer_block_info *block_info = (struct buffer_block_info *)map_data; + + /* Unpack extra input parameters we need */ + struct healpix_smoothing_mapper_data *mapper_data = + (struct healpix_smoothing_mapper_data *)extra_data; + struct lightcone_particle_type *part_type = mapper_data->part_type; + struct lightcone_shell *shell = mapper_data->shell; + + /* Number of healpix maps we're updating */ + const int nr_maps = part_type->nr_maps; + + /* Number of MPI ranks we have */ + const int comm_size = mapper_data->comm_size; + + /* Maximum radius of a HEALPix pixel */ + const double max_pixrad = healpix_max_pixrad(shell->nside); + + /* Loop over buffer blocks to process */ + for (int block_nr = 0; block_nr < num_elements; block_nr += 1) { + + /* Find the count and offset for this block */ + size_t *count = block_info[block_nr].count; + size_t *offset = block_info[block_nr].offset; + int *first_dest = block_info[block_nr].first_dest; + int *last_dest = block_info[block_nr].last_dest; + + /* Get a pointer to the block itself */ + struct particle_buffer_block *block = block_info[block_nr].block; + + /* Initialise count and offset into the send buffer for this block */ + for (int i = 0; i < comm_size; i += 1) { + count[i] = 0; + offset[i] = 0; + } + + /* Loop over lightcone map contributions in this block */ + union lightcone_map_buffer_entry *update_data = + (union lightcone_map_buffer_entry *)block->data; + for (size_t i = 0; i < block->num_elements; i += 1) { + + /* Find the particle angular coordinates and size for this update */ + size_t index = i * (3 + nr_maps); + const double theta = int_to_angle(update_data[index + 0].i); + const double phi = int_to_angle(update_data[index + 1].i); + /* Retrieve angular smoothing length for this particle */ + const double smoothing_radius = update_data[index + 2].f; + /* Compute angular radius at which the projected kernel reaches zero */ + const double search_radius = smoothing_radius * kernel_gamma; + + /* Determine which MPI ranks this contribution needs to go to */ + pixel_index_t first_pixel, last_pixel; + + /* Check whether this particle updates multiple pixels */ + if (search_radius < max_pixrad) { + + /* If the radius is small, we'll just assign the contribution to one + * pixel */ + first_pixel = last_pixel = angle_to_pixel(shell->nside, theta, phi); + + } else { + + /* If the radius is large we will update a range of pixels */ + double vec[3]; + ang2vec(theta, phi, vec); + pixel_index_t pix_min, pix_max; + healpix_query_disc_range(shell->nside, vec, search_radius, &pix_min, + &pix_max, NULL, NULL); + first_pixel = pix_min; + last_pixel = pix_max; + } + + first_dest[i] = + pixel_to_rank(comm_size, shell->pix_per_rank, first_pixel); + last_dest[i] = pixel_to_rank(comm_size, shell->pix_per_rank, last_pixel); + + /* Update the counts for this block */ + for (int dest = first_dest[i]; dest <= last_dest[i]; dest += 1) + count[dest] += 1; + } + + /* Next block */ + } +#else + error("Need HEALPix C API for lightcone maps"); +#endif +} + +/** + * @brief Store elements to send to each MPI rank from each buffer block + * + * This stores the updates to be sent to MPI ranks in order of which + * rank they need to be sent to. It also duplicates updates which need + * to go to multiple ranks. + * + * @param map_data Pointer to an array of buffer_block_info + * @param num_elements Number of elements buffer_block_info array + * @param extra_data Pointer to healpix_smoothing_mapper_data struct + * + */ +static void store_elements_to_send_mapper(void *map_data, int num_elements, + void *extra_data) { + + /* Unpack input data */ + struct buffer_block_info *block_info = (struct buffer_block_info *)map_data; + struct healpix_smoothing_mapper_data *mapper_data = + (struct healpix_smoothing_mapper_data *)extra_data; + struct lightcone_particle_type *part_type = mapper_data->part_type; + + /* Find the send buffer where we will place the updates from this block */ + union lightcone_map_buffer_entry *sendbuf = mapper_data->sendbuf; + + /* Find how many elements we have per update */ + const int nr_elements_per_update = 3 + part_type->nr_maps; + + /* Loop over blocks to process on this call */ + for (int block_nr = 0; block_nr < num_elements; block_nr += 1) { + + /* Find the offset into the send buffer where we will place the + the first element from this block to go to each MPI rank. + Offset is in units of number of updates. */ + size_t *offset = block_info[block_nr].offset; + + /* Find range of MPI ranks to send each element in this block to */ + int *first_dest_rank = block_info[block_nr].first_dest; + int *last_dest_rank = block_info[block_nr].last_dest; + + /* Get a pointer to the block itself */ + struct particle_buffer_block *block = block_info[block_nr].block; + + /* Loop over lightcone map updates in this block */ + union lightcone_map_buffer_entry *update_data = + (union lightcone_map_buffer_entry *)block->data; + for (size_t i = 0; i < block->num_elements; i += 1) { + + /* Find the data to send for this update */ + union lightcone_map_buffer_entry *block_data = + &update_data[i * nr_elements_per_update]; + + /* Store this contribution to the send buffer (possibly multiple times) */ + for (int rank = first_dest_rank[i]; rank <= last_dest_rank[i]; + rank += 1) { + + /* Find where in the send buffer to write the update */ + union lightcone_map_buffer_entry *dest = + sendbuf + (offset[rank] * nr_elements_per_update); + + /* Copy the update to the send buffer */ + memcpy( + dest, block_data, + sizeof(union lightcone_map_buffer_entry) * nr_elements_per_update); + offset[rank] += 1; + } + + /* Next element in this block */ + } + /* Next block */ + } +} +#endif + +/** + * @brief Mapper function for updating the healpix map + * + * map_data is a pointer to an array of doubles. If there are + * N lightcone maps to update and M updates to apply then the array + * contains (3+N)*M doubles. Each group of 3+N doubles consists of + * (theta, phi, radius, value1, value2, ...) where theta and phi + * are angular coordinates of the particle, radius is the angular + * smoothing length and the values are the quantities to add to the + * healpix maps. + * + * @param map_data Pointer to an array of doubles + * @param num_elements Number of elements in map_data + * @param extra_data Pointer to healpix_smoothing_mapper_data struct + * + */ +void healpix_smoothing_mapper(void *map_data, int num_elements, + void *extra_data) { + +#ifdef HAVE_CHEALPIX + + /* Unpack pointers to the lightcone shell and particle_type structs */ + struct healpix_smoothing_mapper_data *mapper_data = + (struct healpix_smoothing_mapper_data *)extra_data; + struct lightcone_shell *shell = mapper_data->shell; + struct lightcone_particle_type *part_type = mapper_data->part_type; + struct projected_kernel_table *kernel_table = mapper_data->kernel_table; + + /* Get maximum radius of any pixel in the map */ + const double max_pixrad = healpix_max_pixrad(shell->nside); + + /* Find the array of updates to apply to the healpix maps */ + union lightcone_map_buffer_entry *update_data = + (union lightcone_map_buffer_entry *)map_data; + + /* Find range of pixel indexes stored locally. Here we assume all maps + have the same number of pixels and distribution between MPI ranks */ + if (shell->nr_maps < 1) + error("called on lightcone_shell which contributes to no maps"); + pixel_index_t local_pix_offset = shell->map[0].local_pix_offset; + pixel_index_t local_nr_pix = shell->map[0].local_nr_pix; + + /* Loop over updates to apply */ + for (int i = 0; i < num_elements; i += 1) { + + /* Find the data for this update */ + size_t index = i * (3 + part_type->nr_maps); + const double theta = int_to_angle(update_data[index + 0].i); + const double phi = int_to_angle(update_data[index + 1].i); + /* Retrieve angular smoothing length for this particle */ + const double smoothing_radius = update_data[index + 2].f; + /* Compute angular radius at which the projected kernel reaches zero */ + const double search_radius = smoothing_radius * kernel_gamma; + const union lightcone_map_buffer_entry *value = &update_data[index + 3]; + + if (search_radius < max_pixrad) { + + /* + Small particles are added to the maps directly regardless of + whether the map is smoothed. Find the pixel index. + */ + pixel_index_t global_pix = angle_to_pixel(shell->nside, theta, phi); + + /* Check the pixel is stored on this MPI rank */ + if ((global_pix >= local_pix_offset) && + (global_pix < local_pix_offset + local_nr_pix)) { + + /* Find local index of the pixel to update */ + const pixel_index_t local_pix = global_pix - local_pix_offset; + + /* Add this particle to all healpix maps */ + for (int j = 0; j < part_type->nr_maps; j += 1) { + const int map_index = part_type->map_index[j]; + const double buffered_value = value[j].f; + const double fac_inv = shell->map[map_index].buffer_scale_factor_inv; + const double value_to_add = buffered_value * fac_inv; + atomic_add_d(&shell->map[map_index].data[local_pix], value_to_add); + } + } + + } else { + + /* + Large particles are SPH smoothed onto smoothed maps and just added + to the appropriate pixel in un-smoothed maps. + + First do the smoothed maps + */ + if (part_type->nr_smoothed_maps > 0) { + + /* Get array of ranges of pixels to update */ + double part_vec[3]; + ang2vec(theta, phi, part_vec); + pixel_index_t pix_min, pix_max; + int nr_ranges; + struct pixel_range *range; + healpix_query_disc_range(shell->nside, part_vec, search_radius, + &pix_min, &pix_max, &nr_ranges, &range); + + /* Compute total weight of pixels to update */ + double total_weight = 0; + for (int range_nr = 0; range_nr < nr_ranges; range_nr += 1) { + for (pixel_index_t pix = range[range_nr].first; + pix <= range[range_nr].last; pix += 1) { + + /* Get vector at the centre of this pixel */ + double pixel_vec[3]; + pix2vec_ring64(shell->nside, pix, pixel_vec); + + /* Find angle between this pixel centre and the particle. + Dot product may be a tiny bit greater than one due to rounding + error */ + const double dp = + (pixel_vec[0] * part_vec[0] + pixel_vec[1] * part_vec[1] + + pixel_vec[2] * part_vec[2]); + const double angle = dp < 1.0 ? acos(dp) : 0.0; + + /* Evaluate the kernel at this radius */ + total_weight += + projected_kernel_eval(kernel_table, angle / smoothing_radius); + } + } + + /* Update the pixels */ + for (int range_nr = 0; range_nr < nr_ranges; range_nr += 1) { + for (pixel_index_t pix = range[range_nr].first; + pix <= range[range_nr].last; pix += 1) { + + /* Check if this pixel is stored locally */ + pixel_index_t global_pix = pix; + if ((global_pix >= local_pix_offset) && + (global_pix < local_pix_offset + local_nr_pix)) { + + /* Get vector at the centre of this pixel */ + double pixel_vec[3]; + pix2vec_ring64(shell->nside, pix, pixel_vec); + + /* Find angle between this pixel centre and the particle. + Dot product may be a tiny bit greater than one due to rounding + error */ + const double dp = + (pixel_vec[0] * part_vec[0] + pixel_vec[1] * part_vec[1] + + pixel_vec[2] * part_vec[2]); + const double angle = dp < 1.0 ? acos(dp) : 0.0; + + /* Evaluate the kernel at this radius */ + const double weight = + projected_kernel_eval(kernel_table, + angle / smoothing_radius) / + total_weight; + + /* Find local index of the pixel to update */ + const pixel_index_t local_pix = global_pix - local_pix_offset; + + /* Update the smoothed healpix maps */ + for (int j = 0; j < part_type->nr_smoothed_maps; j += 1) { + const int map_index = part_type->map_index[j]; + const double buffered_value = value[j].f; + const double fac_inv = + shell->map[map_index].buffer_scale_factor_inv; + const double value_to_add = buffered_value * fac_inv; + atomic_add_d(&shell->map[map_index].data[local_pix], + value_to_add * weight); + } /* Next smoothed map */ + } + } /* Next pixel in this range */ + } /* Next range of pixels */ + + /* Free array of pixel ranges */ + free(range); + + } /* if nr_smoothed_maps > 0*/ + + /* Then do any un-smoothed maps */ + if (part_type->nr_unsmoothed_maps > 0) { + + /* Find the index of the pixel containing the particle */ + pixel_index_t global_pix = angle_to_pixel(shell->nside, theta, phi); + + /* Check the pixel is stored on this MPI rank */ + if ((global_pix >= local_pix_offset) && + (global_pix < local_pix_offset + local_nr_pix)) { + + /* Find local index of the pixel to update */ + const pixel_index_t local_pix = global_pix - local_pix_offset; + + /* Update the un-smoothed healpix maps */ + for (int j = part_type->nr_smoothed_maps; j < part_type->nr_maps; + j += 1) { + const int map_index = part_type->map_index[j]; + const double buffered_value = value[j].f; + const double fac_inv = + shell->map[map_index].buffer_scale_factor_inv; + const double value_to_add = buffered_value * fac_inv; + atomic_add_d(&shell->map[map_index].data[local_pix], value_to_add); + } + } + } /* if part_type->nr_unsmoothed_maps > 0 */ + } + } /* End loop over updates to apply */ +#else + error("Need HEALPix C API for lightcone maps"); +#endif +} + +/** + * @brief Apply updates for one particle type to all lightcone maps in a shell + * + * When a particle of type ptype crosses the lightcone it generates an entry + * in shell->buffer[ptype] which contains the angular position and size of + * the particle and the values it contributes to the lightcone_maps in the + * shell. This function applies these buffered updates to the lightcone + * map pixel data. + * + * We carry out all the updates for one particle type at the same time so that + * we avoid repeating the healpix neighbour search for every healpix map. + * + * Applying the updates involves copying them to a send buffer then a receive + * buffer, so if there are a lot we process them in chunks of up to + * max_map_update_send_size_mb megabytes to save memory. + * + * @param shell the #lightcone_shell to update + * @param tp the #threadpool used to execute the updates + * @param part_type contains information about each particle type to be updated + * @param smoothing_info contains parameters relating to smoothing onto the + * sphere + * @param ptype index of the particle type to update + * @param max_map_update_send_size_mb maximum amount of data each ranks sends + * + */ +void lightcone_shell_flush_map_updates_for_type( + struct lightcone_shell *shell, struct threadpool *tp, + struct lightcone_particle_type *part_type, int ptype, + const double max_map_update_send_size_mb, + struct projected_kernel_table *kernel_table, int verbose) { + + int comm_rank = 0, comm_size = 1; +#ifdef WITH_MPI + MPI_Comm_size(MPI_COMM_WORLD, &comm_size); + MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank); +#endif + + /* Information needed by mapper functions */ + struct healpix_smoothing_mapper_data mapper_data; + mapper_data.shell = shell; + mapper_data.part_type = &part_type[ptype]; + mapper_data.comm_rank = comm_rank; + mapper_data.comm_size = comm_size; + mapper_data.sendbuf = NULL; + mapper_data.kernel_table = kernel_table; + +#ifdef WITH_MPI + + /* Count data blocks and ensure number of elements is in range */ + int nr_blocks = 0; + struct particle_buffer *buffer = &shell->buffer[ptype]; + struct particle_buffer_block *block = buffer->first_block; + while (block) { + if (block->num_elements > buffer->elements_per_block) + block->num_elements = buffer->elements_per_block; + nr_blocks += 1; + block = block->next; + } + + /* Allocate array with counts and offsets for each block */ + struct buffer_block_info *block_info = (struct buffer_block_info *)malloc( + sizeof(struct buffer_block_info) * nr_blocks); + + /* Initialize array of blocks */ + nr_blocks = 0; + block = buffer->first_block; + while (block) { + block_info[nr_blocks].block = block; + block_info[nr_blocks].count = (size_t *)malloc(sizeof(size_t) * comm_size); + block_info[nr_blocks].offset = (size_t *)malloc(sizeof(size_t) * comm_size); + block_info[nr_blocks].first_dest = + (int *)malloc(sizeof(int) * block->num_elements); + block_info[nr_blocks].last_dest = + (int *)malloc(sizeof(int) * block->num_elements); + nr_blocks += 1; + block = block->next; + } + + /* To minimize memory usage we don't process all of the blocks at once. + Determine the maximum number of blocks on any rank. */ + int max_nr_blocks; + MPI_Allreduce(&nr_blocks, &max_nr_blocks, 1, MPI_INT, MPI_MAX, + MPI_COMM_WORLD); + + /* Determine the maximum number of blocks to process per iteration */ + size_t max_bytes = max_map_update_send_size_mb * 1024.0 * 1024.0; + int max_blocks_per_iteration = + max_bytes / (buffer->element_size * buffer->elements_per_block); + if (max_blocks_per_iteration < 1) + error("max_map_update_send_size_mb is too small to process even one block"); + + /* Determine how many iterations we need */ + int nr_iterations = max_nr_blocks / max_blocks_per_iteration; + if (max_nr_blocks % max_blocks_per_iteration != 0) nr_iterations += 1; + if (engine_rank == 0 && nr_iterations > 0 && verbose) + message("will require %d iterations with %d blocks per iteration", + nr_iterations, max_blocks_per_iteration); + + /* Loop over iterations */ + int nr_blocks_done = 0; + for (int iter = 0; iter < nr_iterations; iter += 1) { + + /* Find number of blocks to do on this iteration (may be zero) */ + int nr_blocks_iter = nr_blocks - nr_blocks_done; + if (nr_blocks_iter > max_blocks_per_iteration) + nr_blocks_iter = max_blocks_per_iteration; + + /* Get a pointer to the blocks to do on this iteration */ + struct buffer_block_info *block_info_iter = block_info + nr_blocks_done; + + /* For each block, count how many elements are to be sent to each MPI rank + */ + threadpool_map(tp, count_elements_to_send_mapper, block_info_iter, + nr_blocks_iter, sizeof(struct buffer_block_info), 1, + &mapper_data); + + /* Find total number of elements to go to each rank */ + size_t *send_count = (size_t *)malloc(sizeof(size_t) * comm_size); + for (int i = 0; i < comm_size; i += 1) send_count[i] = 0; + for (int block_nr = 0; block_nr < nr_blocks_iter; block_nr += 1) { + for (int i = 0; i < comm_size; i += 1) + send_count[i] += block_info_iter[block_nr].count[i]; + } + + /* Find offset to the first element to go to each rank if we sort them by + * destination */ + size_t *send_offset = (size_t *)malloc(sizeof(size_t) * comm_size); + send_offset[0] = 0; + for (int i = 1; i < comm_size; i += 1) { + send_offset[i] = send_offset[i - 1] + send_count[i - 1]; + } + + /* For each block, find the location in the send buffer where we need to + place the first element to go to each MPI rank */ + for (int block_nr = 0; block_nr < nr_blocks_iter; block_nr += 1) { + for (int i = 0; i < comm_size; i += 1) { + if (block_nr == 0) { + /* This is the first block */ + block_info_iter[block_nr].offset[i] = send_offset[i]; + } else { + /* Not first, so elements are written after those of the previous + * block */ + block_info_iter[block_nr].offset[i] = + block_info_iter[block_nr - 1].offset[i] + + block_info_iter[block_nr - 1].count[i]; + } + } + } + + /* Find the total number of elements to be sent */ + size_t total_nr_send = 0; + for (int i = 0; i < comm_size; i += 1) total_nr_send += send_count[i]; + + /* Allocate the send buffer */ + union lightcone_map_buffer_entry *sendbuf = + (union lightcone_map_buffer_entry *)malloc( + part_type[ptype].buffer_element_size * total_nr_send); + mapper_data.sendbuf = sendbuf; + + /* Populate the send buffer */ + threadpool_map(tp, store_elements_to_send_mapper, block_info_iter, + nr_blocks_iter, sizeof(struct buffer_block_info), 1, + &mapper_data); + + /* Determine number of elements to receive */ + size_t *recv_count = (size_t *)malloc(comm_size * sizeof(size_t)); + MPI_Alltoall(send_count, sizeof(size_t), MPI_BYTE, recv_count, + sizeof(size_t), MPI_BYTE, MPI_COMM_WORLD); + size_t total_nr_recv = 0; + for (int i = 0; i < comm_size; i += 1) total_nr_recv += recv_count[i]; + + /* Allocate receive buffer */ + union lightcone_map_buffer_entry *recvbuf = + (union lightcone_map_buffer_entry *)malloc( + part_type[ptype].buffer_element_size * total_nr_recv); + + /* Exchange data */ + exchange_structs(send_count, sendbuf, recv_count, recvbuf, + part_type[ptype].buffer_element_size); + + /* Apply received updates to the healpix map */ + threadpool_map(tp, healpix_smoothing_mapper, recvbuf, total_nr_recv, + part_type[ptype].buffer_element_size, + threadpool_auto_chunk_size, &mapper_data); + + /* Tidy up */ + free(send_count); + free(send_offset); + free(sendbuf); + free(recv_count); + free(recvbuf); + + /* Advance to next set of blocks */ + nr_blocks_done += nr_blocks_iter; + } + if (nr_blocks_done != nr_blocks) + error("not all map update blocks were processed"); + + /* We no longer need the array of blocks */ + for (int block_nr = 0; block_nr < nr_blocks; block_nr += 1) { + free(block_info[block_nr].count); + free(block_info[block_nr].offset); + free(block_info[block_nr].first_dest); + free(block_info[block_nr].last_dest); + } + free(block_info); + + /* Empty the particle buffer now that we copied the data from it */ + particle_buffer_empty(buffer); + +#else + + /* If not using MPI, we can update the healpix maps directly from the buffer + */ + struct particle_buffer_block *block = NULL; + size_t num_elements; + double *update_data; + do { + particle_buffer_iterate(&shell->buffer[ptype], &block, &num_elements, + (void **)&update_data); + threadpool_map(tp, healpix_smoothing_mapper, update_data, num_elements, + part_type[ptype].buffer_element_size, + threadpool_auto_chunk_size, &mapper_data); + } while (block); + particle_buffer_empty(&shell->buffer[ptype]); + +#endif +} + +/** + * @brief Apply buffered updates to all lightcone maps in a shell + * + * @param shell the #lightcone_shell to update + * @param tp the #threadpool used to execute the updates + * @param part_type contains information about each particle type to be updated + * sphere + * + */ +void lightcone_shell_flush_map_updates( + struct lightcone_shell *shell, struct threadpool *tp, + struct lightcone_particle_type *part_type, + const double max_map_update_send_size_mb, + struct projected_kernel_table *kernel_table, int verbose) { + + if (shell->state != shell_current) + error("Attempt to flush updates for non-current shell!"); + + for (int ptype = 0; ptype < swift_type_count; ptype += 1) { + if ((shell->nr_maps > 0) && (part_type[ptype].nr_maps > 0)) { + lightcone_shell_flush_map_updates_for_type(shell, tp, part_type, ptype, + max_map_update_send_size_mb, + kernel_table, verbose); + } + } +} diff --git a/src/lightcone/lightcone_shell.h b/src/lightcone/lightcone_shell.h new file mode 100644 index 0000000000000000000000000000000000000000..e78f6969bc98089a307f4ba6e31c70b0d9cf5bdf --- /dev/null +++ b/src/lightcone/lightcone_shell.h @@ -0,0 +1,182 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_LIGHTCONE_SHELL_H +#define SWIFT_LIGHTCONE_SHELL_H + +/* Standard headers */ +#include <stdio.h> + +/* Config parameters. */ +#include <config.h> + +/* Local headers */ +#include "cosmology.h" +#include "error.h" +#include "lightcone/lightcone_map.h" +#include "lightcone/lightcone_map_types.h" +#include "lightcone/pixel_index.h" +#include "lightcone/projected_kernel.h" +#include "particle_buffer.h" + +enum lightcone_shell_state { + shell_uninitialized, + shell_current, + shell_complete, +}; + +union lightcone_map_buffer_entry { + int i; + float f; +}; + +/** + * @brief Encode an angle in the range 0 to 2pi as an int + * + * @param angle the angle to encode + */ +__attribute__((always_inline)) INLINE static int angle_to_int( + const double angle) { + + if (angle < 0.0 || angle > 2 * M_PI) error("angle is out of range!"); + const double fac = ((1 << 30) - 1) / M_PI; + return (int)(angle * fac); +} + +/** + * @brief Convert an encoded angle back to a double + * + * @param i the int containing the angle + */ +__attribute__((always_inline)) INLINE static double int_to_angle(const int i) { + + const double fac = M_PI / ((1 << 30) - 1); + return i * fac; +} + +/** + * @brief Information about a particle type contributing to the lightcone + * + * For each Swift particle type we store how many lightcone maps that type + * contributes to and their indexes in the array of lightcone_maps structs + * associated with each lightcone_shell. + * + * We also record the number of bytes needed to store one update: updates + * consist of the angular coordinates of the particle, its angular smoothing + * radius, and the quantities contributed to the lightcone maps. + * + */ +struct lightcone_particle_type { + + /*! Number of lightcone maps this particle type contributes to */ + int nr_maps; + + /*! Number of smoothed this particle type contributes to */ + int nr_smoothed_maps; + + /*! Number of un-smoothed this particle type contributes to */ + int nr_unsmoothed_maps; + + /*! Indices of the lightcone maps this particle type contributes to. + Smoothed maps will be stored first in the array. */ + int *map_index; + + /*! Amount of data to store per particle: theta, phi, radius and the value to + * add to each healpix map */ + size_t buffer_element_size; +}; + +/** + * @brief Information about each lightcone shell + * + * Each shell contains one lightcone_map for each healpix map + * we're making. This is where the pixel data is stored while + * the current simulation timestep overlaps the shell's redshift + * range. + * + * Each shell also contains one particle_buffer per particle type, + * which stores the updates to be applied to the pixel data. + * Updates are accumulated in the buffers during each time step + * and applied at the end of the step. + * + */ +struct lightcone_shell { + + /*! State of this shell */ + enum lightcone_shell_state state; + + /*! Inner radius of shell */ + double rmin; + + /*! Outer radius of shell */ + double rmax; + + /*! Minimum expansion factor for this shell */ + double amin; + + /*! Maximum expansion factor for this shell */ + double amax; + + /*! Number of maps associated with this shell */ + int nr_maps; + + /*! Array of lightcone maps for this shell */ + struct lightcone_map *map; + + /*! Buffers to store the map updates for each particle type */ + struct particle_buffer buffer[swift_type_count]; + + /*! Healpix nside parameter */ + int nside; + + /*! Total pixels in the maps */ + pixel_index_t total_nr_pix; + + /*! Number of pixels per map stored on this node */ + pixel_index_t local_nr_pix; + + /*! Offset of the first pixel stored on this rank */ + pixel_index_t local_pix_offset; + + /*! Number of pixels per rank (last node has any extra) */ + pixel_index_t pix_per_rank; +}; + +struct lightcone_shell *lightcone_shell_array_init( + const struct cosmology *cosmo, const char *radius_file, int nr_maps, + struct lightcone_map_type *map_type, int nside, pixel_index_t total_nr_pix, + struct lightcone_particle_type *part_type, size_t elements_per_block, + int *nr_shells_out); + +void lightcone_shell_array_free(struct lightcone_shell *shell, int nr_shells); + +void lightcone_shell_array_dump(const struct lightcone_shell *shell, + int nr_shells, FILE *stream); + +struct lightcone_shell *lightcone_shell_array_restore( + FILE *stream, int nr_shells, struct lightcone_particle_type *part_type, + size_t elements_per_block); + +void lightcone_shell_flush_map_updates( + struct lightcone_shell *shell, struct threadpool *tp, + struct lightcone_particle_type *part_type, + const double max_map_update_send_size_mb, + struct projected_kernel_table *kernel_table, int verbose); + +#endif /* SWIFT_LIGHTCONE_SHELL_H */ diff --git a/src/lightcone/pixel_index.h b/src/lightcone/pixel_index.h new file mode 100644 index 0000000000000000000000000000000000000000..84458111eecd9b556685e77e83cb96f625113228 --- /dev/null +++ b/src/lightcone/pixel_index.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_PIXEL_INDEX_H +#define SWIFT_PIXEL_INDEX_H + +#include <limits.h> +#include <stdint.h> + +/* Config parameters. */ +#include <config.h> + +/* Type to use for HEALPix pixel indexes */ +typedef int64_t pixel_index_t; + +/* Maximum pixel index (determines maximum map size) */ +#define MAX_PIXEL_INDEX INT64_MAX + +/* Corresponding MPI type */ +#define MPI_PIXEL_INDEX_T MPI_INT64_T + +#endif diff --git a/src/lightcone/projected_kernel.c b/src/lightcone/projected_kernel.c new file mode 100644 index 0000000000000000000000000000000000000000..2dddef6c2cab1d32c228d89838a4b216a8df74f5 --- /dev/null +++ b/src/lightcone/projected_kernel.c @@ -0,0 +1,156 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 "projected_kernel.h" + +/* Local headers */ +#include "error.h" +#include "kernel_hydro.h" +#include "math.h" + +#ifdef HAVE_LIBGSL +#include <gsl/gsl_integration.h> +#include <gsl/gsl_interp.h> +#endif + +/** + * @brief Integrand used in evaluating the projected kernel + * + * See section 4.3.1 in Price et al. 2007: + * https://ui.adsabs.harvard.edu/abs/2007PASA...24..159P/abstract + * + * This function is used to carry out the integral in equation 30. + * + * @param qz z coordinate at which to evaluate the kernel, in units of h + * @param param Ratio of distance in the xy plane in units of h + */ +#ifdef HAVE_LIBGSL +static double projected_kernel_integrand(double qz, void *param) { + + const double qxy = *((double *)param); + const double q = sqrt(pow(qxy, 2.0) + pow(qz, 2.0)); + double W; + kernel_eval_double(q, &W); + return W; +} +#endif + +/** + * @brief Computes 2D projection of the 3D kernel function. + * + * Given a distance in the xy plane, we integrate along the + * z axis to evaluate the projected kernel. + * + * @param u The ratio of the (2D) distance to the smoothing length + */ +double projected_kernel_integrate(double u) { + +#ifdef HAVE_LIBGSL + + /* Swift's hydro kernel can be evaluated with kernel_eval(u, W) + where u = r / h and W returns the result. The kernel goes to + zero at u=kernel_gamma. Projection is only implemented in 3D.*/ +#ifndef HYDRO_DIMENSION_3D + error("projected_kernel_eval() is only defined for the 3D case."); +#endif + + /* Initalise the GSL workspace */ + const size_t workspace_size = 100000; + gsl_integration_workspace *space = + gsl_integration_workspace_alloc(workspace_size); + + /* Compute the integral */ + double result; + double abserr; + double qxy = u; + const double qz_max = sqrt(pow(kernel_gamma, 2.0) - pow(qxy, 2.0)); + const double qz_min = -qz_max; + gsl_function F = {&projected_kernel_integrand, &qxy}; + gsl_integration_qag(&F, qz_min, qz_max, 1.0e-10, 1.0e-10, workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + + /* Free the workspace */ + gsl_integration_workspace_free(space); + + return result; + +#else + error("Need GSL library to evaluate the projected kernel"); + return 0.0; +#endif +} + +/** + * @brief Tabulate the projected kernel + * + * @param tab The projected_kernel_table struct + */ +void projected_kernel_init(struct projected_kernel_table *tab) { + + /* Allocate storage */ + tab->n = PROJECTED_KERNEL_NTAB; + tab->value = (double *)malloc(sizeof(double) * tab->n); + + /* Determine range to tabulate */ + tab->u_max = kernel_gamma; + tab->du = tab->u_max / (tab->n - 1); + tab->inv_du = 1.0 / tab->du; + + /* Evaluate the kernel at points in the table */ + for (int i = 0; i < tab->n - 1; i += 1) + tab->value[i] = projected_kernel_integrate(i * tab->du); + tab->value[tab->n - 1] = 0.0; +} + +/** + * @brief Deallocate the projected kernel table + */ +void projected_kernel_clean(struct projected_kernel_table *tab) { + free(tab->value); +} + +void projected_kernel_dump(void) { + + struct projected_kernel_table tab; + projected_kernel_init(&tab); + + const int N = 5000; + const double du = kernel_gamma / (N - 1); + FILE *fd; + + fd = fopen("projected_kernel.txt", "w"); + fprintf(fd, "u, kernel, projected kernel\n"); + for (int i = 0; i < N; i += 1) { + double u = i * du; + float kernel; + kernel_eval(u, &kernel); + double kernel_proj = projected_kernel_eval(&tab, u); + double kernel_proj_int = projected_kernel_integrate(u); + + fprintf(fd, "%e, %e, %e, %e\n", u, (double)kernel, kernel_proj, + kernel_proj_int); + } + + fclose(fd); + projected_kernel_clean(&tab); +} diff --git a/src/lightcone/projected_kernel.h b/src/lightcone/projected_kernel.h new file mode 100644 index 0000000000000000000000000000000000000000..3a9334e0d63c79d00e1fc0477f796446e42f68cd --- /dev/null +++ b/src/lightcone/projected_kernel.h @@ -0,0 +1,69 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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_PROJECTED_KERNEL_H +#define SWIFT_PROJECTED_KERNEL_H + +/* Config parameters. */ +#include <config.h> + +/* Local headers */ +#include "error.h" +#include "inline.h" + +#define PROJECTED_KERNEL_NTAB 1000 + +struct projected_kernel_table { + int n; + double du; + double inv_du; + double u_max; + double *value; +}; + +/** + * @brief Computes 2D projection of the 3D kernel function. + * + * This version interpolates the value from the supplied + * look up table. + * + * @param u The ratio of the (2D) distance to the smoothing length + */ +__attribute__((always_inline)) INLINE static double projected_kernel_eval( + struct projected_kernel_table *tab, double u) { + + /* Check u is in range */ + if (u >= tab->u_max) return 0.0; + if (u < 0.0) error("Negative u in projected kernel!"); + + /* Determine which interval we're in */ + int i = u * tab->inv_du; + + /* Find where we are in the interval */ + double f = (u - i * tab->du) * tab->inv_du; + + /* Linear interpolation */ + return (1.0 - f) * tab->value[i] + f * tab->value[i + 1]; +} + +void projected_kernel_init(struct projected_kernel_table *tab); +void projected_kernel_clean(struct projected_kernel_table *tab); +void projected_kernel_dump(void); + +#endif diff --git a/src/line_of_sight.c b/src/line_of_sight.c index da519662f4968cf076b6eb0012a3f55136dc5fda..d0ea412558242f14f2ac560fb658ab69962438cc 100644 --- a/src/line_of_sight.c +++ b/src/line_of_sight.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2020 Stuart McAlpine (stuart.mcalpine@helsinki.fi) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -316,7 +316,8 @@ void write_los_hdf5_dataset(const struct io_props props, const size_t N, h_err = H5Pset_chunk(h_prop, rank, chunk_shape); if (h_err < 0) error("Error while setting chunk size (%llu, %llu) for field '%s'.", - chunk_shape[0], chunk_shape[1], props.name); + (unsigned long long)chunk_shape[0], + (unsigned long long)chunk_shape[1], props.name); /* Impose check-sum to verify data corruption */ h_err = H5Pset_fletcher32(h_prop); @@ -524,6 +525,8 @@ void write_hdf5_header(hid_t h_file, const struct engine *e, swift_type_count); io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, swift_type_count); + io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total, + swift_type_count); double MassTable[swift_type_count] = {0}; io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count); io_write_attribute(h_grp, "InitialMassTable", DOUBLE, @@ -534,11 +537,19 @@ void write_hdf5_header(hid_t h_file, const struct engine *e, swift_type_count); io_write_attribute_i(h_grp, "NumFilesPerSnapshot", 1); io_write_attribute_i(h_grp, "ThisFile", 0); + io_write_attribute_s(h_grp, "SelectOutput", "Default"); + io_write_attribute_i(h_grp, "Virtual", 0); + const int to_write[swift_type_count] = {1}; /* We can only have gas */ + io_write_attribute(h_grp, "CanHaveTypes", INT, to_write, swift_type_count); io_write_attribute_s(h_grp, "OutputType", "LineOfSight"); /* Close group */ H5Gclose(h_grp); + /* Copy metadata from ICs to the file */ + ic_info_write_hdf5(e->ics_metadata, h_file); + + /* Write all the meta-data */ io_write_meta_data(h_file, e, e->internal_units, e->snapshot_units); /* Print the LOS properties */ diff --git a/src/line_of_sight.h b/src/line_of_sight.h index 0e79a9a50126a76cee152d9fc9612b7b33961407..158c0fce6cb348f9dde95da0302b170d3091de79 100644 --- a/src/line_of_sight.h +++ b/src/line_of_sight.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2020 Stuart McAlpine (stuart.mcalpine@helsinki.fi) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -22,7 +22,9 @@ #define SWIFT_LOS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "engine.h" #include "io_properties.h" diff --git a/src/log.h b/src/log.h index 6bee9643371323ec9984a11af5f7bf3df589f1e5..674377c7692309f01a394635a0d992ecaaca1e64 100644 --- a/src/log.h +++ b/src/log.h @@ -20,7 +20,7 @@ #define SWIFT_OPTIMIZED_LOG_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" diff --git a/src/map.c b/src/map.c index 68c3618fcdb10a618a97e5d1a2565d58db677cdb..b4641aad59cfbdd0edacff490ed75e52b177f00e 100644 --- a/src/map.c +++ b/src/map.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdio.h> diff --git a/src/map.h b/src/map.h index 6ad05e30df0644e1ee37b1b912bc11681ccf837c..a94ee261543ff061dcf7bd071c30b880804bdc86 100644 --- a/src/map.h +++ b/src/map.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 diff --git a/src/memswap.h b/src/memswap.h index adaab7fbd295f25bdbec14b757abc68b228e2e89..12de6d08830bdddee0b2ee0a17635763488a3560 100644 --- a/src/memswap.h +++ b/src/memswap.h @@ -20,8 +20,9 @@ #define SWIFT_MEMSWAP_H /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ #include <stdint.h> #ifdef HAVE_IMMINTRIN_H diff --git a/src/memuse.c b/src/memuse.c index aeff8ad59590a32a3cd0085f4761adee461ac4e2..29844fda0e323a3496ee2c972e1adbcf2182d1e3 100644 --- a/src/memuse.c +++ b/src/memuse.c @@ -24,7 +24,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard includes. */ #include <stdint.h> diff --git a/src/memuse.h b/src/memuse.h index dce03e949d8991ece16f1e31a14dd20ca89f26bc..b40869c87c1341265a07dae3e7aeea4b3da03c32 100644 --- a/src/memuse.h +++ b/src/memuse.h @@ -20,7 +20,7 @@ #define SWIFT_MEMUSE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include <stdlib.h> @@ -168,10 +168,10 @@ __attribute__((always_inline)) inline void *swift_realloc(const char *label, */ __attribute__((always_inline)) inline void swift_free(const char *label, void *ptr) { - free(ptr); #ifdef SWIFT_MEMUSE_REPORTS memuse_log_allocation(label, ptr, 0, 0); #endif + free(ptr); return; } diff --git a/src/memuse_rnodes.c b/src/memuse_rnodes.c index ae1229e63521dff55e12d2aa37f765095017208b..61973a5a6d645819b01314aa0c18255e773fbc3c 100644 --- a/src/memuse_rnodes.c +++ b/src/memuse_rnodes.c @@ -23,7 +23,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard includes. */ #include <stdint.h> diff --git a/src/memuse_rnodes.h b/src/memuse_rnodes.h index e38ae65a127a70c6b2f78f1e68826d8b667d8cf2..09afa499ba7cbd5943e9af2b118375a3be91d6c3 100644 --- a/src/memuse_rnodes.h +++ b/src/memuse_rnodes.h @@ -20,7 +20,7 @@ #define SWIFT_MEMUSE_RNODE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include <inttypes.h> diff --git a/src/mesh_gravity.c b/src/mesh_gravity.c index d19c70bebe47e438ed876ce26f069dfd2723e08d..3c2279cef6e01c1480d3b92483379617fb0cfb94 100644 --- a/src/mesh_gravity.c +++ b/src/mesh_gravity.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef HAVE_FFTW #include <fftw3.h> @@ -215,6 +215,7 @@ struct cic_mapper_data { double* rho; double* potential; int N; + int use_local_patches; double fac; double dim[3]; float const_G; @@ -260,10 +261,8 @@ void cell_gpart_to_mesh_CIC_mapper(void* map_data, int num, void* extra) { /* Pointer to the chunk to be processed */ int* local_cells = (int*)map_data; - // MATTHIEU: This could in principle be improved by creating a local mesh - // with just the extent required for the cell. Assignment can - // then be done without atomics. That local mesh is then added - // atomically to the global one. + /* A temporary patch of the global mesh */ + struct pm_mesh_patch patch; /* Loop over the elements assigned to this thread */ for (int i = 0; i < num; ++i) { @@ -271,8 +270,26 @@ void cell_gpart_to_mesh_CIC_mapper(void* map_data, int num, void* extra) { /* Pointer to local cell */ const struct cell* c = &cells[local_cells[i]]; - /* Assign this cell's content to the mesh */ - cell_gpart_to_mesh_CIC(c, rho, N, fac, dim, nu_model); + /* Skip empty cells */ + if (c->grav.count == 0) continue; + + if (data->use_local_patches) { + + /* Do a CIC interpolation of all the particles in this cell onto + the local patch (allocates memory in the patch) */ + accumulate_cell_to_local_patch(N, fac, dim, c, &patch, nu_model); + + /* Copy the local patch values back onto the global mesh */ + pm_add_patch_to_global_mesh(rho, &patch); + + /* Free the allocated memory */ + pm_mesh_patch_clean(&patch); + + } else { + + /* Assign this cell's content directly atomically to the mesh */ + cell_gpart_to_mesh_CIC(c, rho, N, fac, dim, nu_model); + } } } @@ -880,6 +897,7 @@ void compute_potential_global(struct pm_mesh* mesh, const struct space* s, data.rho = rho; data.potential = NULL; data.N = N; + data.use_local_patches = mesh->use_local_patches; data.fac = cell_fac; data.dim[0] = dim[0]; data.dim[1] = dim[1]; @@ -1136,6 +1154,7 @@ void pm_mesh_init(struct pm_mesh* mesh, const struct gravity_props* props, mesh->periodic = 1; mesh->N = N; mesh->distributed_mesh = props->distributed_mesh; + mesh->use_local_patches = props->mesh_uses_local_patches; mesh->dim[0] = dim[0]; mesh->dim[1] = dim[1]; mesh->dim[2] = dim[2]; diff --git a/src/mesh_gravity.h b/src/mesh_gravity.h index 5ae27a752c221849144af258b5447c1e0ce0bf24..02f451c4d5baa9bb379e54c1ebce00cf5c01938c 100644 --- a/src/mesh_gravity.h +++ b/src/mesh_gravity.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_MESH_GRAVITY_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "gravity_properties.h" @@ -50,6 +50,10 @@ struct pm_mesh { /*! Whether mesh is distributed between MPI ranks */ int distributed_mesh; + /*! Whether or not to use local patches rather than + * direct atomic writes to the mesh when running without MPI */ + int use_local_patches; + /*! Integer time-step end of the mesh force for the last step */ integertime_t ti_end_mesh_last; diff --git a/src/mesh_gravity_mpi.c b/src/mesh_gravity_mpi.c index fb7c5710dcfd4b0d715cc2c97efc0c7142d3ff9d..24191f312d581081422156918f4dc766c00278f0 100644 --- a/src/mesh_gravity_mpi.c +++ b/src/mesh_gravity_mpi.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -33,6 +33,7 @@ #include "debug.h" #include "engine.h" #include "error.h" +#include "exchange_structs.h" #include "lock.h" #include "mesh_gravity_patch.h" #include "mesh_gravity_sort.h" @@ -160,6 +161,9 @@ void accumulate_cell_to_local_patches_mapper(void *map_data, int num, /* Pointer to local cell */ const struct cell *c = &cells[local_cells[i]]; + /* Skip empty cells */ + if (c->grav.count == 0) continue; + /* Assign this cell's content to the mesh */ accumulate_cell_to_local_patch(N, fac, dim, c, &local_patches[i], nu_model); } @@ -254,106 +258,6 @@ void mesh_patches_to_sorted_array(const struct pm_mesh_patch *local_patches, if (count != size) error("Error flattening the mesh patches!"); } -/** - * @brief Given an array of structs of size element_size, send - * nr_send[i] elements to each node i. Allocates the receive - * buffer recvbuf to the appropriate size and returns its size - * in nr_recv_tot. - * - * TODO: can/should we replace this with a call to engine_do_redistribute()? - * - * @param nr_send Number of elements to send to each node - * @param nr_recv Number of elements to receive from each node - * @param sendbuf The elements to send - * @param recvbuf The output buffer - * - */ -void exchange_structs(size_t *nr_send, char *sendbuf, size_t *nr_recv, - char *recvbuf, size_t element_size) { - -#if defined(WITH_MPI) && defined(HAVE_MPI_FFTW) - - /* Determine rank, number of ranks */ - int nr_nodes, nodeID; - MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes); - MPI_Comm_rank(MPI_COMM_WORLD, &nodeID); - - /* Compute send offsets */ - size_t *send_offset = (size_t *)malloc(nr_nodes * sizeof(size_t)); - send_offset[0] = 0; - for (int i = 1; i < nr_nodes; i++) { - send_offset[i] = send_offset[i - 1] + nr_send[i - 1]; - } - - /* Compute receive offsets */ - size_t *recv_offset = (size_t *)malloc(nr_nodes * sizeof(size_t)); - recv_offset[0] = 0; - for (int i = 1; i < nr_nodes; i++) { - recv_offset[i] = recv_offset[i - 1] + nr_recv[i - 1]; - } - - /* Allocate request objects (one send and receive per node) */ - MPI_Request *request = - (MPI_Request *)malloc(2 * sizeof(MPI_Request) * nr_nodes); - - /* Make type to communicate mesh_key_value struct */ - MPI_Datatype mesh_key_value_mpi_type; - if (MPI_Type_contiguous(element_size, MPI_BYTE, &mesh_key_value_mpi_type) != - MPI_SUCCESS || - MPI_Type_commit(&mesh_key_value_mpi_type) != MPI_SUCCESS) { - error("Failed to create MPI type for mesh_key_value struct."); - } - - /* - * Post the send operations. This is an alltoallv really but - * we want to avoid the limits imposed by int counts and offsets - * in MPI_Alltoallv. - */ - for (int i = 0; i < nr_nodes; i++) { - if (nr_send[i] > 0) { - - /* TODO: handle very large messages */ - if (nr_send[i] > INT_MAX) - error("exchange_structs() fails if nr_send > INT_MAX!"); - - MPI_Isend(&(sendbuf[send_offset[i] * element_size]), (int)nr_send[i], - mesh_key_value_mpi_type, i, 0, MPI_COMM_WORLD, &(request[i])); - } else { - request[i] = MPI_REQUEST_NULL; - } - } - - /* Post the receives */ - for (int i = 0; i < nr_nodes; i++) { - if (nr_recv[i] > 0) { - - /* TODO: handle very large messages */ - if (nr_recv[i] > INT_MAX) - error("exchange_structs() fails if nr_recv > INT_MAX!"); - - MPI_Irecv(&(recvbuf[recv_offset[i] * element_size]), (int)nr_recv[i], - mesh_key_value_mpi_type, i, 0, MPI_COMM_WORLD, - &(request[i + nr_nodes])); - } else { - request[i + nr_nodes] = MPI_REQUEST_NULL; - } - } - - /* Wait for everything to complete */ - MPI_Waitall(2 * nr_nodes, request, MPI_STATUSES_IGNORE); - - /* Done with the MPI type */ - MPI_Type_free(&mesh_key_value_mpi_type); - - /* Tidy up */ - free(recv_offset); - free(send_offset); - free(request); -#else - error("FFTW MPI not found - unable to use distributed mesh"); -#endif -} - /** * @brief Convert the array of local patches to a slab-distributed 3D mesh * diff --git a/src/mesh_gravity_mpi.h b/src/mesh_gravity_mpi.h index fbe3a361cd9b3f2979120920fabc11288b7bc257..33d100ce3accce82846c37252109efea3a4d9820 100644 --- a/src/mesh_gravity_mpi.h +++ b/src/mesh_gravity_mpi.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_MESH_GRAVITY_MPI_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Forward declarations */ struct space; @@ -28,6 +28,12 @@ struct cell; struct threadpool; struct pm_mesh; struct pm_mesh_patch; +struct neutrino_model; + +void accumulate_cell_to_local_patch(const int N, const double fac, + const double *dim, const struct cell *cell, + struct pm_mesh_patch *patch, + const struct neutrino_model *nu_model); void mpi_mesh_accumulate_gparts_to_local_patches( struct threadpool *tp, const int N, const double fac, const struct space *s, diff --git a/src/mesh_gravity_patch.c b/src/mesh_gravity_patch.c index e9316f47a4b9f689ac9789f12f33c96c86cab37e..bce169f9901c026ea48994e3c5ecaa51ce8e896e 100644 --- a/src/mesh_gravity_patch.c +++ b/src/mesh_gravity_patch.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,14 +18,16 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ #include <math.h> /* This object's header. */ #include "mesh_gravity_patch.h" /* Local includes. */ +#include "atomic.h" #include "cell.h" #include "error.h" #include "row_major_id.h" @@ -99,6 +101,45 @@ void pm_mesh_patch_init(struct pm_mesh_patch *patch, const struct cell *cell, error("Failed to allocate array for mesh patch!"); } +/** + * @brief Write the content of a mesh patch back to the global mesh + * using atomic operations. + * + * @param global_mesh The global mesh to write to. + * @param patch The #pm_mesh_patch object to write from. + */ +void pm_add_patch_to_global_mesh(double *const global_mesh, + const struct pm_mesh_patch *patch) { + + const int N = patch->N; + const int size_i = patch->mesh_size[0]; + const int size_j = patch->mesh_size[1]; + const int size_k = patch->mesh_size[2]; + const int mesh_min_i = patch->mesh_min[0]; + const int mesh_min_j = patch->mesh_min[1]; + const int mesh_min_k = patch->mesh_min[2]; + + /* Remind the compiler that the arrays are nicely aligned */ + swift_declare_aligned_ptr(const double, mesh, patch->mesh, + SWIFT_CACHE_ALIGNMENT); + + for (int i = 0; i < size_i; ++i) { + for (int j = 0; j < size_j; ++j) { + for (int k = 0; k < size_k; ++k) { + + const int ii = i + mesh_min_i; + const int jj = j + mesh_min_j; + const int kk = k + mesh_min_k; + + const int patch_index = pm_mesh_patch_index(patch, i, j, k); + const int mesh_index = row_major_id_periodic(ii, jj, kk, N); + + atomic_add_d(&global_mesh[mesh_index], mesh[patch_index]); + } + } + } +} + /** * @brief Set all values in a mesh patch to zero * @@ -106,9 +147,12 @@ void pm_mesh_patch_init(struct pm_mesh_patch *patch, const struct cell *cell, */ void pm_mesh_patch_zero(struct pm_mesh_patch *patch) { + /* Remind the compiler that the arrays are nicely aligned */ + swift_declare_aligned_ptr(double, mesh, patch->mesh, SWIFT_CACHE_ALIGNMENT); + const int num = patch->mesh_size[0] * patch->mesh_size[1] * patch->mesh_size[2]; - memset(patch->mesh, 0, num * sizeof(double)); + memset(mesh, 0, num * sizeof(double)); } /** @@ -118,7 +162,7 @@ void pm_mesh_patch_zero(struct pm_mesh_patch *patch) { */ void pm_mesh_patch_clean(struct pm_mesh_patch *patch) { - swift_free("mesh_patch", patch->mesh); + if (patch->mesh) swift_free("mesh_patch", patch->mesh); /* Zero everything and give a silly mesh size to help debugging */ memset(patch, 0, sizeof(struct pm_mesh_patch)); diff --git a/src/mesh_gravity_patch.h b/src/mesh_gravity_patch.h index 9d66ba49c82b654ca99a9ca3ddfeacf96df0cfcc..cbd37bda64d1e12da96f59487a7bc3980f84ddf3 100644 --- a/src/mesh_gravity_patch.h +++ b/src/mesh_gravity_patch.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -21,7 +21,7 @@ #define SWIFT_MESH_GRAVITY_PATCH_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include "align.h" @@ -167,4 +167,7 @@ __attribute__((always_inline)) INLINE static void pm_mesh_patch_CIC_set( mesh[pm_mesh_patch_index(patch, i + 1, j + 1, k + 1)] += value * dx * dy * dz; } +void pm_add_patch_to_global_mesh(double *const global_mesh, + const struct pm_mesh_patch *patch); + #endif diff --git a/src/mesh_gravity_sort.c b/src/mesh_gravity_sort.c index 39eaa24a35158b37ee49c303a145ff7a45b9a429..25422815745ebd6322f24dbc378ef7d004419560 100644 --- a/src/mesh_gravity_sort.c +++ b/src/mesh_gravity_sort.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "mesh_gravity_sort.h" diff --git a/src/mesh_gravity_sort.h b/src/mesh_gravity_sort.h index 107227e921feff399924765c5077df30e331e1f5..fd7816d9ee97fa7d8f865c19ece9147a6d38603b 100644 --- a/src/mesh_gravity_sort.h +++ b/src/mesh_gravity_sort.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_MESH_GRAVITY_SORT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard includes */ #include <stdlib.h> diff --git a/src/runner_doiact_sinks_merger.c b/src/mhd.h similarity index 66% rename from src/runner_doiact_sinks_merger.c rename to src/mhd.h index 3627fb412555d29eb01ddc1c9462582bb9c8130d..913a7e55afb8caa065b8c1a16c57bf50357fd7d2 100644 --- a/src/runner_doiact_sinks_merger.c +++ b/src/mhd.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Coypright (c) 2022 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,23 +16,22 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_MHD_H +#define SWIFT_MHD_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ -#include "active.h" -#include "cell.h" -#include "engine.h" -#include "feedback.h" -#include "runner.h" -#include "sink.h" -#include "space_getsid.h" -#include "timers.h" +#include "part.h" -/* Import the sink compute formation loop functions. */ -#define FUNCTION merger -#define FUNCTION_TASK_LOOP TASK_LOOP_MERGER -#include "runner_doiact_functions_sinks_merger.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +/* Import the right functions */ +#if defined(NONE_MHD) +#include "./mhd/None/mhd.h" +#include "./mhd/None/mhd_iact.h" +#define MHD_IMPLEMENTATION "No MHD scheme" +#else +#error "Invalid choice of MHD variant" +#endif + +#endif /* SWIFT_MHD_H */ diff --git a/src/mhd/None/mhd.h b/src/mhd/None/mhd.h new file mode 100644 index 0000000000000000000000000000000000000000..07351133d291520398de3a181e55efb8f42e813b --- /dev/null +++ b/src/mhd/None/mhd.h @@ -0,0 +1,322 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_NONE_MHD_H +#define SWIFT_NONE_MHD_H + +/** + * @brief Returns the magnetic energy contained in the particle. + * + * @param p the #part. + * @param xp the #xpart. + */ +__attribute__((always_inline)) INLINE static float mhd_get_magnetic_energy( + const struct part *p, const struct xpart *xp) { + + return 0.f; +} + +/** + * @brief Returns the magnetic helicity contained in the particle. + * + * @param p the #part. + * @param xp the #xpart. + */ +__attribute__((always_inline)) INLINE static float mhd_get_magnetic_helicity( + const struct part *p, const struct xpart *xp) { + + return 0.f; +} + +/** + * @brief Returns the magnetic cross-helicity contained in the particle. + * + * @param p the #part. + * @param xp the #xpart. + */ +__attribute__((always_inline)) INLINE static float mhd_get_cross_helicity( + const struct part *p, const struct xpart *xp) { + + return 0.f; +} + +/** + * @brief Returns the magnetic field divergence of the particle. + * + * This is (div B) / (B / h) and is hence dimensionless. + * + * @param p the #part. + * @param xp the #xpart. + */ +__attribute__((always_inline)) INLINE static float mhd_get_divB_error( + const struct part *p, const struct xpart *xp) { + + return 0.f; +} + +/** + * @brief Computes the MHD time-step of a given particle + * + * This function returns the time-step of a particle given its hydro-dynamical + * state. A typical time-step calculation would be the use of the CFL condition. + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * @param hydro_properties The SPH parameters + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float mhd_compute_timestep( + const struct part *p, const struct xpart *xp, + const struct hydro_props *hydro_properties, const struct cosmology *cosmo) { + + return FLT_MAX; +} + +/** + * @brief Compute the signal velocity between two gas particles + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float mhd_signal_velocity( + const float dx[3], const struct part *pi, const struct part *pj, + const float mu_ij, const float beta) { + + error("Calling an MHD signal velocity when compiling without MHD!"); + return -1.f; +} + +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various density loop over neighbours. Typically, all fields of the + * density sub-structure of a particle get zeroed in here. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void mhd_init_part( + struct part *p) {} + +/** + * @brief Finishes the density calculation. + * + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * Additional quantities such as velocity gradients will also get the final + * terms added to them here. + * + * Also adds/multiplies the cosmological terms if need be. + * + * @param p The particle to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void mhd_end_density( + struct part *p, const struct cosmology *cosmo) {} + +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + */ +__attribute__((always_inline)) INLINE static void mhd_prepare_gradient( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const struct hydro_props *hydro_props) {} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after mhd_prepare_gradient. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void mhd_reset_gradient( + struct part *p) {} + +/** + * @brief Finishes the gradient calculation. + * + * This method also initializes the force loop variables. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void mhd_end_gradient( + struct part *p) {} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * In the desperate case where a particle has no neighbours (likely because + * of the h_max ceiling), set the particle fields to something sensible to avoid + * NaNs in the next calculations. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void mhd_part_has_no_neighbours( + struct part *p, struct xpart *xp, const struct cosmology *cosmo) {} + +/** + * @brief Prepare a particle for the force calculation. + * + * This function is called in the ghost task to convert some quantities coming + * from the density loop over neighbours into quantities ready to be used in the + * force loop over neighbours. Quantities are typically read from the density + * sub-structure and written to the force sub-structure. + * Examples of calculations done here include the calculation of viscosity term + * constants, thermal conduction terms, hydro conversions, etc. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + * @param hydro_props Hydrodynamic properties. + * @param dt_alpha The time-step used to evolve non-cosmological quantities such + * as the artificial viscosity. + */ +__attribute__((always_inline)) INLINE static void mhd_prepare_force( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const struct hydro_props *hydro_props, const float dt_alpha) {} + +/** + * @brief Reset acceleration fields of a particle + * + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the various force tasks. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void mhd_reset_acceleration( + struct part *p) {} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param p The particle. + * @param xp The extended data of this particle. + * @param cosmo The cosmological model + */ +__attribute__((always_inline)) INLINE static void mhd_reset_predicted_values( + struct part *p, const struct xpart *xp, const struct cosmology *cosmo) {} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * Note the different time-step sizes used for the different quantities as they + * include cosmological factors. + * + * @param p The particle. + * @param xp The extended data of the particle. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. + * @param cosmo The cosmological model. + * @param hydro_props The properties of the hydro scheme. + * @param floor_props The properties of the entropy floor. + */ +__attribute__((always_inline)) INLINE static void mhd_predict_extra( + struct part *p, const struct xpart *xp, const float dt_drift, + const float dt_therm, const struct cosmology *cosmo, + const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props) {} + +/** + * @brief Finishes the force calculation. + * + * Multiplies the force and accelerations by the appropiate constants + * and add the self-contribution term. In most cases, there is little + * to do here. + * + * Cosmological terms are also added/multiplied here. + * + * @param p The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void mhd_end_force( + struct part *p, const struct cosmology *cosmo) {} + +/** + * @brief Kick the additional variables + * + * Additional hydrodynamic quantites are kicked forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * @param p The particle to act upon. + * @param xp The particle extended data to act upon. + * @param dt_therm The time-step for this kick (for thermodynamic quantities). + * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_hydro The time-step for this kick (for hydro quantities). + * @param dt_kick_corr The time-step for this kick (for gravity corrections). + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme. + * @param floor_props The properties of the entropy floor. + */ +__attribute__((always_inline)) INLINE static void mhd_kick_extra( + struct part *p, struct xpart *xp, const float dt_therm, const float dt_grav, + const float dt_hydro, const float dt_kick_corr, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props) {} + +/** + * @brief Converts MHD quantities of a particle at the start of a run + * + * This function is called once at the end of the engine_init_particle() + * routine (at the start of a calculation) after the densities of + * particles have been computed. + * This can be used to convert internal energy into entropy in the case + * of hydro for instance. + * + * @param p The particle to act upon + * @param xp The extended particle to act upon + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme. + */ +__attribute__((always_inline)) INLINE static void mhd_convert_quantities( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const struct hydro_props *hydro_props) {} + +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions or assignments between the particle + * and extended particle fields. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void mhd_first_init_part( + struct part *p, struct xpart *xp, const struct mhd_global_data *mhd_data, + const double Lsize) { + + mhd_reset_acceleration(p); + mhd_init_part(p); +} + +#endif /* SWIFT_NONE_MHD_H */ diff --git a/src/mhd/None/mhd_debug.h b/src/mhd/None/mhd_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..d8a7cf67f01dcc1b50ad40adf89d94c68218ef80 --- /dev/null +++ b/src/mhd/None/mhd_debug.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_NONE_MHD_DEBUG_H +#define SWIFT_NONE_MHD_DEBUG_H + +/** + * @brief Print out the mhd fields of a particle. + * + * Function used for debugging purposes. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void mhd_debug_particle( + const struct part *p, const struct xpart *xp) {} + +#endif /* SWIFT_NONE_MHD_DEBUG_H */ diff --git a/src/mhd/None/mhd_iact.h b/src/mhd/None/mhd_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..3ec12221cf196263dfcafbb9ca9e922c682c6dfb --- /dev/null +++ b/src/mhd/None/mhd_iact.h @@ -0,0 +1,146 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_NONE_MHD_IACT_H +#define SWIFT_NONE_MHD_IACT_H + +/** + * @brief MHD-Density interaction between two particles. + * + * @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 mu_0 The vaccuum permeability constant in internal units. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_mhd_density( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const double mu_0, + const float a, const float H) {} + +/** + * @brief MHD-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 pi First particle. + * @param pj Second particle. + * @param mu_0 The vaccuum permeability constant in internal units. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_mhd_density(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + const struct part *restrict pj, + const double mu_0, const float a, + const float H) {} + +/** + * @brief Calculate the MHD-gradient interaction between particle i and particle + * j + * + * This method wraps around hydro_gradients_collect, which can be an empty + * method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param mu_0 The vaccuum permeability constant in internal units. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_mhd_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const double mu_0, + const float a, const float H) {} + +/** + * @brief Calculate the MHDgradient interaction between particle i and particle + * j (non-symmetric) + * + * This method wraps around hydro_gradients_collect, which can be an empty + * method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param mu_0 The vaccuum permeability constant in internal units. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_mhd_gradient(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + const struct part *restrict pj, + const double mu_0, const float a, + const float H) {} + +/** + * @brief MHD-Force interaction between two particles. + * + * @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 mu_0 The vaccuum permeability constant in internal units. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_mhd_force( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const double mu_0, + const float a, const float H) {} + +/** + * @brief MHD-Force interaction between two particles. 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. + * @param mu_0 The vaccuum permeability constant in internal units. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_mhd_force( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, const struct part *restrict pj, const double mu_0, + const float a, const float H) {} + +#endif /* SWIFT_NONE_MHD_H */ diff --git a/src/mhd/None/mhd_io.h b/src/mhd/None/mhd_io.h new file mode 100644 index 0000000000000000000000000000000000000000..7c0a4a23594f0e7631f842c745248e632995fbfd --- /dev/null +++ b/src/mhd/None/mhd_io.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Matthieu Schaller (schaller@strw.leideuniv.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_NONE_MHD_IO_H +#define SWIFT_NONE_MHD_IO_H + +#include "statistics.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * @returns num_fields The number of i/o fields readed. + */ +INLINE static int mhd_read_particles(struct part* parts, + struct io_props* list) { + + return 0; +} + +/** + * @brief Specifies which 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 i/o fields to write. + */ +INLINE static int mhd_write_particles(const struct part* parts, + const struct xpart* xparts, + struct io_props* list) { + + return 0; +} + +/** + * @brief Writes the current model of MHD to the file + * @param h_grpsph The HDF5 group in which to write + */ +INLINE static void mhd_write_flavour(hid_t h_grpsph) {} + +#endif /* SWIFT_NONE_MHD_IO_H */ diff --git a/src/mhd/None/mhd_parameters.h b/src/mhd/None/mhd_parameters.h new file mode 100644 index 0000000000000000000000000000000000000000..8c9dcb3e8da7ef2aa464f66d822a84380c3a8219 --- /dev/null +++ b/src/mhd/None/mhd_parameters.h @@ -0,0 +1,88 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 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_NONE_MHD_PARAMETERS_H +#define SWIFT_NONE_MHD_PARAMETERS_H + +/* Configuration file */ +#include <config.h> + +/* Global headers */ +#if defined(HAVE_HDF5) +#include <hdf5.h> +#endif + +/* Local headers */ +#include "common_io.h" +#include "error.h" +#include "inline.h" + +/** + * @file None/mhd_parameters.h + * @brief NO MHD but default parameters for other schemes + * + * This file defines a number of things that are used in + * mhd schemes as defaults for run-time parameters + * as well as a number of compile-time parameters. + */ + +/* Dedner cleaning -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ + +/* if set to 0 NO dedner cleaning + * hyperbolic term of Dender Scalar field evolution */ +#define mhd_propos_dedner_hyperbolic 0.0f + +/* + * parabolic term of Dender Scalar field evolution */ +#define mhd_propos_dedner_parabolic 0.0f + +/* Magnetic Diffusion parameters -- Defaults can be changed in RunTime */ + +/* Magnetic Diffusion, if set to 0 IDEAL mhd + * */ +#define mhd_propos_default_difussion_eta 0.0f + +/*! MHD parameters */ +struct mhd_global_data {}; + +/* Functions for reading from parameter file */ + +/** + * @brief Initialises the mhd parameters in the struct from + * the parameter file, or sets them to defaults. + * + * @param params: the pointer to the swift_params file + * @param us: pointer to the internal unit system + * @param phys_const: pointer to the physical constants system + * @param mhd: pointer to the mhd_global_data struct to be filled. + **/ +static INLINE void mhd_init(struct swift_params* params, + const struct unit_system* us, + const struct phys_const* phys_const, + struct mhd_global_data* mhd) {} + +/** + * @brief Prints out the mhd parameters at the start of a run. + * + * @param mhd: pointer to the mhd_global_data struct found in + * hydro_properties + **/ +static INLINE void mhd_print(const struct mhd_global_data* mhd) {} + +#endif /* SWIFT_NONE_MHD_PARAMETERS_H */ diff --git a/src/mhd/None/mhd_struct.h b/src/mhd/None/mhd_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..4e1c657b9b0d7a586ef29930c9a1ae69f3b5a015 --- /dev/null +++ b/src/mhd/None/mhd_struct.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_NONE_MHD_STRUCT_H +#define SWIFT_NONE_MHD_STRUCT_H + +/** + * @brief Particle-carried fields for the MHD scheme. + */ +struct mhd_part_data {}; + +/** + * @brief Particle-carried extra fields for the MHD scheme. + */ +struct mhd_xpart_data {}; + +#endif /* SWIFT_NONE_MHD_STRUCT_H */ diff --git a/src/mhd_io.h b/src/mhd_io.h new file mode 100644 index 0000000000000000000000000000000000000000..ce034485eae5eeabdb682c24d0bc6ecfa3a37c62 --- /dev/null +++ b/src/mhd_io.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 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_MHD_IO_H +#define SWIFT_MHD_IO_H + +#include <config.h> + +/* Load the correct MHD model */ +#if defined(NONE_MHD) +#include "./mhd/None/mhd_io.h" +#else +#error "Invalid choice of MHD model" +#endif + +#endif /* SWIFT_MHD_IO_H */ diff --git a/src/runner_doiact_rt.c b/src/mhd_struct.h similarity index 59% rename from src/runner_doiact_rt.c rename to src/mhd_struct.h index 3ce180d719b700a18ce7518e53e113b189265cfc..ad9c6b2213fccf085d8d2a253b7960ff12823f7b 100644 --- a/src/runner_doiact_rt.c +++ b/src/mhd_struct.h @@ -1,9 +1,6 @@ /******************************************************************************* * 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) - * 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Coypright (c) 2022 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 @@ -19,22 +16,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_MHD_STRUCT_H +#define SWIFT_MHD_STRUCT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ -#include "active.h" -#include "cell.h" -#include "engine.h" -#include "rt.h" -#include "runner.h" -#include "space_getsid.h" -#include "timers.h" +#include "part.h" -/* Import the rt injection loop functions. */ -#define FUNCTION inject -#define FUNCTION_TASK_LOOP TASK_LOOP_RT_INJECT -#include "runner_doiact_functions_rt.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +/* Import the right functions */ +#if defined(NONE_MHD) +#include "./mhd/None/mhd_struct.h" +#else +#error "Invalid choice of MHD variant" +#endif + +#endif /* SWIFT_MHD_STRUCT_H */ diff --git a/src/minmax.h b/src/minmax.h index 44e5c258bfbe49c0ad7de5bc8ff9a8d562d7d8ea..4e0557eceed971674c7d30493231fc8eac3ab5f2 100644 --- a/src/minmax.h +++ b/src/minmax.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 diff --git a/src/mpiuse.c b/src/mpiuse.c index b50bdc3458341bbd951012a0bf15047cae64644c..7d00934226e61372f2b10db4214da2957e823709 100644 --- a/src/mpiuse.c +++ b/src/mpiuse.c @@ -21,7 +21,7 @@ * @brief file of routines to report about MPI tasks used in SWIFT. */ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(SWIFT_MPIUSE_REPORTS) && defined(WITH_MPI) diff --git a/src/mpiuse.h b/src/mpiuse.h index 812af6a78ea98d138f45638e442f100fe23d8596..1bbf3ff81aeae5b468268e3b7980e596d84b4eae 100644 --- a/src/mpiuse.h +++ b/src/mpiuse.h @@ -20,7 +20,7 @@ #define SWIFT_MPIUSE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "cycle.h" diff --git a/src/multipole.c b/src/multipole.c index 03adccd0d4386d837c2294307e51fbeb144bcfb7..eddc7274d176907440a878aac96a1516e51a02ab 100644 --- a/src/multipole.c +++ b/src/multipole.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "multipole.h" diff --git a/src/multipole.h b/src/multipole.h index e2c55a6a20469174930d57d58813577632ec21f9..26c163a7c952361c63f923a60ca040647203a57e 100644 --- a/src/multipole.h +++ b/src/multipole.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -21,7 +21,7 @@ #define SWIFT_MULTIPOLE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> @@ -1031,7 +1031,7 @@ __attribute__((nonnull)) INLINE static void gravity_P2M( float min_delta_vel[3] = {0., 0., 0.}; #if SELF_GRAVITY_MULTIPOLE_ORDER > 0 - double M_100 = 0., M_010 = 0., M_001 = 0.; + /* double M_100 = 0., M_010 = 0., M_001 = 0.; */ #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 1 double M_200 = 0., M_020 = 0., M_002 = 0.; @@ -1086,9 +1086,9 @@ __attribute__((nonnull)) INLINE static void gravity_P2M( const double m = gparts[k].mass; /* 1st order terms */ - M_100 += -m * X_100(dx); - M_010 += -m * X_010(dx); - M_001 += -m * X_001(dx); + /* M_100 += -m * X_100(dx); */ + /* M_010 += -m * X_010(dx); */ + /* M_001 += -m * X_001(dx); */ #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 1 diff --git a/src/multipole_accept.h b/src/multipole_accept.h index 5d67e8f1d47a8f92c118993a4bd2a8d031652bd8..dd9c899512fd712cdd0deac42c79942e98e70dee 100644 --- a/src/multipole_accept.h +++ b/src/multipole_accept.h @@ -20,7 +20,7 @@ #define SWIFT_MULTIPOLE_ACCEPT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "binomial.h" @@ -84,12 +84,15 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept( const int use_rebuild_sizes, const int periodic) { /* Order of the expansion */ - const int p = SELF_GRAVITY_MULTIPOLE_ORDER; + const int p = 2; /* Sizes of the multipoles */ const float rho_A = use_rebuild_sizes ? A->r_max_rebuild : A->r_max; const float rho_B = use_rebuild_sizes ? B->r_max_rebuild : B->r_max; + /* Max size of both multipoles */ + const float rho_max = max(rho_A, rho_B); + /* Get the softening */ const float max_softening = max(A->m_pole.max_softening, B->m_pole.max_softening); @@ -102,19 +105,16 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept( } E_BA_term *= 8.f; if (rho_A + rho_B > 0.f) { - E_BA_term *= max(rho_A, rho_B); + E_BA_term *= rho_max; + ; E_BA_term /= (rho_A + rho_B); } - /* Compute r^p */ -#if SELF_GRAVITY_MULTIPOLE_ORDER % 2 == 1 - const float r_to_p = integer_powf(sqrtf(r2), p); -#else + /* Compute r^p = (r^2)^(p/2) */ const float r_to_p = integer_powf(r2, (p / 2)); -#endif float f_MAC_inv; - if (props->consider_truncation_in_MAC) { + if (periodic && props->consider_truncation_in_MAC) { f_MAC_inv = gravity_f_MAC_inverse(max_softening, props->r_s_inv, r2); } else { f_MAC_inv = r2; @@ -123,6 +123,9 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept( /* Get the mimimal acceleration in A */ const float min_a_grav = A->m_pole.min_old_a_grav_norm; + /* Maximal mass */ + const float M_max = max(A->m_pole.M_000, B->m_pole.M_000); + /* Get the relative tolerance */ const float eps = props->adaptive_tolerance; @@ -133,7 +136,19 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept( /* Get the sum of the multipole sizes */ const float rho_sum = rho_A + rho_B; - if (props->use_advanced_MAC) { + if (props->use_advanced_MAC && props->use_gadget_tolerance) { + + /* Gadget 4 paper -- eq. 36 */ + const int power = SELF_GRAVITY_MULTIPOLE_ORDER - 1; + const float ratio = integer_powf(rho_max / sqrtf(r2), power); + const int cond_1 = M_max * ratio < eps * min_a_grav * f_MAC_inv; + + const int cond_2 = + props->use_tree_below_softening || max_softening * max_softening < r2; + + return cond_1 && cond_2; + + } else if (props->use_advanced_MAC && !props->use_gadget_tolerance) { #ifdef SWIFT_DEBUG_CHECKS if (min_a_grav == 0.) error("Acceleration is 0"); @@ -191,6 +206,75 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept_symmetric( gravity_M2L_accept(props, B, A, r2, use_rebuild_sizes, periodic); } +/** + * Compute the distance above which an M2L kernel is allowed to be used. + * + * This uses conservative assumptions to guarantee that all the possible cell + * pair interactions that need a direct interaction are below this distance. + * + * @param props The properties of the gravity scheme. + * @param size The size of the multipoles (here the cell size). + * @param max_softening The maximal softening accross all particles. + * @param min_a_grav The minimal acceleration accross all particles. + * @param max_mpole_power The maximum multipole power accross all the + * multipoles. + * @param periodic Are we using periodic BCs? + */ +__attribute__((nonnull, pure)) INLINE static float +gravity_M2L_min_accept_distance( + const struct gravity_props *props, const float size, + const float max_softening, const float min_a_grav, + const float max_mpole_power[SELF_GRAVITY_MULTIPOLE_ORDER + 1], + const int periodic) { + + /* Order of the expansion */ + const int p = SELF_GRAVITY_MULTIPOLE_ORDER; + + float E_BA_term = 0.f; + for (int n = 0; n <= p; ++n) { + E_BA_term += + binomial(p, n) * max_mpole_power[n] * integer_powf(size, p - n); + } + E_BA_term *= 4.f; + + /* Get the basic geometric critical angle */ + const float theta_crit = props->theta_crit; + const float theta_crit2 = theta_crit * theta_crit; + + /* Get the sum of the multipole sizes */ + const float size_sum = 2. * size; + + /* Get the relative tolerance */ + const float eps = props->adaptive_tolerance; + + if (props->use_advanced_MAC) { + + /* Distance obtained by solving for the geometric criterion with theta = 1 + */ + const float dist_tree = size_sum; + + const float dist_adapt = + powf(E_BA_term / (eps * min_a_grav), 1.f / (p + 2.f)); + + /* Distance obtained by demanding > softening */ + const float dist_soft = + props->use_tree_below_softening ? 0.f : max_softening; + + return max3(dist_tree, dist_adapt, dist_soft); + + } else { + + /* Distance obtained by solving for the geometric criterion */ + const float dist_tree = sqrtf(size_sum * size_sum / theta_crit2); + + /* Distance obtained by demanding > softening */ + const float dist_soft = + props->use_tree_below_softening ? 0.f : max_softening; + + return max(dist_tree, dist_soft); + } +} + /** * @brief Checks whether The multipole in B can be used to update the particle * pa @@ -208,7 +292,7 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2P_accept( const struct gravity_tensors *B, const float r2, const int periodic) { /* Order of the expansion */ - const int p = SELF_GRAVITY_MULTIPOLE_ORDER; + const int p = 2; /* Sizes of the multipoles */ const float rho_B = B->r_max; @@ -224,15 +308,11 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2P_accept( /* Compute the error estimator (without the 1/M_B term that cancels out) */ const float E_BA_term = 8.f * B->m_pole.power[p]; - /* Compute r^p */ -#if SELF_GRAVITY_MULTIPOLE_ORDER % 2 == 1 - const float r_to_p = integer_powf(sqrtf(r2), p); -#else + /* Compute r^p = (r^2)^(p/2) */ const float r_to_p = integer_powf(r2, (p / 2)); -#endif float f_MAC_inv; - if (props->consider_truncation_in_MAC) { + if (periodic && props->consider_truncation_in_MAC) { f_MAC_inv = gravity_f_MAC_inverse(max_softening, props->r_s_inv, r2); } else { f_MAC_inv = r2; @@ -248,7 +328,19 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2P_accept( const float theta_crit = props->theta_crit; const float theta_crit2 = theta_crit * theta_crit; - if (props->use_advanced_MAC) { + if (props->use_advanced_MAC && props->use_gadget_tolerance) { + + /* Gadget 4 paper -- eq. 12 */ + const int power = SELF_GRAVITY_MULTIPOLE_ORDER; + const float ratio = integer_powf(rho_B / sqrtf(r2), power); + const int cond_1 = B->m_pole.M_000 * ratio < eps * old_a_grav * f_MAC_inv; + + const int cond_2 = + props->use_tree_below_softening || max_softening * max_softening < r2; + + return cond_1 && cond_2; + + } else if (props->use_advanced_MAC && !props->use_gadget_tolerance) { #ifdef SWIFT_DEBUG_CHECKS if (old_a_grav == 0.) error("Acceleration is 0"); diff --git a/src/multipole_struct.h b/src/multipole_struct.h index ffd615cd798aa3963491ad31fb86f6af8f9e5d89..6812259e6b42682106558637c189754ea498785c 100644 --- a/src/multipole_struct.h +++ b/src/multipole_struct.h @@ -20,7 +20,7 @@ #define SWIFT_MULTIPOLE_STRUCT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "timeline.h" diff --git a/src/neutrino.h b/src/neutrino.h index a19261b3a619a62fb1aae8892aaf36e83ca1eebb..27cd04403bd27fb7dec51353c2e19680bf46dc8a 100644 --- a/src/neutrino.h +++ b/src/neutrino.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Willem Elbers (whe@willemelbers.com) + * Copyright (c) 2021 Willem Elbers (whe@willemelbers.com) * * 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,7 +20,7 @@ #define SWIFT_NEUTRINO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct neutrino model */ #include "./neutrino/Default/neutrino.h" diff --git a/src/neutrino/Default/neutrino.c b/src/neutrino/Default/neutrino.c index b51e2521d3838b2c086bb1686e8456da9a09ee02..5b6e35ca0069034618d22a60f90ddb703f9cab1d 100644 --- a/src/neutrino/Default/neutrino.c +++ b/src/neutrino/Default/neutrino.c @@ -23,6 +23,10 @@ /* Standard headers */ #include <math.h> +/* Local includes */ +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_map_types.h" + /* Compute the dimensionless neutrino momentum (units of kb*T). * * @param v The internal 3-velocity @@ -339,3 +343,87 @@ void neutrino_check_cosmology(const struct space *s, cosmo->Omega_nu_0, Omega_particles_nu); } } + +/* + Lightcone map of neutrino mass perturbation +*/ + +/** + * @brief Determine if a particle type contributes to this map type + * + * @param part_type the particle type + */ +int lightcone_map_neutrino_mass_type_contributes(int ptype) { + + switch (ptype) { + case swift_type_neutrino: + return 1; + default: + return 0; + } +} + +/** + * @brief Make a healpix map of the neutrino mass perturbation + * + * When a neutrino particle crosses the lightcone this function + * should return the value to accumulate to the corresponding + * pixel in the healpix map. + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param gp the #gpart to add to the map + * @param a_cross expansion factor at which the particle crosses the lightcone + * @param x_cross comoving coordinates at which the particle crosses the + * lightcone + */ +double lightcone_map_neutrino_mass_get_value( + const struct engine *e, const struct lightcone_props *lightcone_props, + const struct gpart *gp, const double a_cross, const double x_cross[3]) { + + switch (gp->type) { + case swift_type_neutrino: { + struct neutrino_model nu_model; + bzero(&nu_model, sizeof(struct neutrino_model)); + if (e->neutrino_properties->use_delta_f_mesh_only) + gather_neutrino_consts(e->s, &nu_model); + double weight = 1.0; + gpart_neutrino_weight_mesh_only(gp, &nu_model, &weight); + return gp->mass * weight; + } break; + default: + error("lightcone map function called on wrong particle type"); + return -1.0; /* Prevent 'missing return' error */ + } +} + +/** + * @brief Return baseline value for neutrino mass lightcone maps. + * + * This is the mean neutrino density integrated over the volume of the pixel. + * + * @param e the #engine structure + * @param lightcone_props properties of the lightcone to update + * @param map The lightcone map + */ +double lightcone_map_neutrino_baseline_value( + const struct cosmology *c, const struct lightcone_props *lightcone_props, + const struct lightcone_map *map) { + + /* Fetch the area of healpix pixels */ + const double area = lightcone_props->pixel_area_steradians; + + /* Fetch the inner and outer radii */ + const double r_inner = map->r_min; + const double r_outer = map->r_max; + const double r_inner_3 = r_inner * r_inner * r_inner; + const double r_outer_3 = r_outer * r_outer * r_outer; + + /* The volume mapped into a healpix pixel */ + const double volume = area * (r_outer_3 - r_inner_3) / 3.0; + + /* The mean comoving neutrino density at z = 0 */ + const double rho_nu_0 = c->critical_density_0 * c->Omega_nu_0; + + return rho_nu_0 * volume; +} diff --git a/src/neutrino/Default/neutrino.h b/src/neutrino/Default/neutrino.h index e728274d6b2af7bd0c187ad93b3d9884c5750cb1..aaccc5a56b3f7cfd384813ff46d63ab05b276b4b 100644 --- a/src/neutrino/Default/neutrino.h +++ b/src/neutrino/Default/neutrino.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Willem Elbers (willem.h.elbers@durham.ac.uk) + * Copyright (c) 2021 Willem Elbers (willem.h.elbers@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 @@ -20,7 +20,7 @@ #define SWIFT_DEFAULT_NEUTRINO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "../../engine.h" @@ -155,5 +155,7 @@ void neutrino_check_cosmology(const struct space *s, struct swift_params *params, const struct neutrino_props *neutrino_props, const int rank, const int verbose); - +double lightcone_map_neutrino_baseline_value( + const struct cosmology *c, const struct lightcone_props *lightcone_props, + const struct lightcone_map *map); #endif /* SWIFT_DEFAULT_NEUTRINO_H */ diff --git a/src/neutrino/Default/neutrino_io.h b/src/neutrino/Default/neutrino_io.h index 312b3a042ef6571847e6511245d712ef15afbb0b..1f5dbb135a7a464742b5f014845d08c9ea2db861 100644 --- a/src/neutrino/Default/neutrino_io.h +++ b/src/neutrino/Default/neutrino_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Willem Elbers (willem.h.elbers@durham.ac.uk) + * Copyright (c) 2021 Willem Elbers (willem.h.elbers@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 @@ -23,6 +23,9 @@ /* Local includes */ #include "fermi_dirac.h" +#include "io_properties.h" +#include "lightcone/lightcone.h" +#include "lightcone/lightcone_map_types.h" #include "neutrino.h" #include "neutrino_properties.h" @@ -163,4 +166,45 @@ __attribute__((always_inline)) INLINE static int neutrino_write_particles( return 3; } +/* + Lightcone map of neutrino mass perturbation +*/ + +int lightcone_map_neutrino_mass_type_contributes(int ptype); +double lightcone_map_neutrino_mass_get_value( + const struct engine* e, const struct lightcone_props* lightcone_props, + const struct gpart* gp, const double a_cross, const double x_cross[3]); + +/* + This associates map names to the appropriate update function and unit info. + + Note that field designators are commented out here so that the code will + compile as C++ using gcc. This is necessary due to a gcc bug. + + See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55227 for details. +*/ +static const struct lightcone_map_type neutrino_lightcone_map_types[] = { + { + /* .name = */ "NeutrinoMass", + /* .update_map = */ lightcone_map_neutrino_mass_get_value, + /* .ptype_contributes = */ lightcone_map_neutrino_mass_type_contributes, + /* .baseline_func = */ lightcone_map_neutrino_baseline_value, + /* .units = */ UNIT_CONV_MASS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, + { + /* NULL functions indicate end of array */ + /* .name = */ "", + /* .update_map = */ NULL, + /* .ptype_contributes = */ NULL, + /* .baseline_func = */ NULL, + /* .units = */ UNIT_CONV_NO_UNITS, + /* .smoothing = */ map_unsmoothed, + /* .compression = */ compression_write_lossless, + /* .buffer_scale_factor = */ 1.0, + }, +}; + #endif /* SWIFT_DEFAULT_NEUTRINO_IO_H */ diff --git a/src/neutrino/Default/neutrino_properties.h b/src/neutrino/Default/neutrino_properties.h index 24add9619555f70aca486b237a8ccd21618ab3a5..389da9ee7b545639a8640a908abe3d36e05806e3 100644 --- a/src/neutrino/Default/neutrino_properties.h +++ b/src/neutrino/Default/neutrino_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Willem Elbers (willem.h.elbers@durham.ac.uk) + * Copyright (c) 2021 Willem Elbers (willem.h.elbers@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 diff --git a/src/neutrino/Default/neutrino_response.c b/src/neutrino/Default/neutrino_response.c index 511b0a43089f6e25dd75f63e3b4402607ccb85fb..4d841a633c7efb598349652ee33bc05279e4c5ee 100644 --- a/src/neutrino/Default/neutrino_response.c +++ b/src/neutrino/Default/neutrino_response.c @@ -18,7 +18,9 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "parser.h" #ifdef HAVE_LIBGSL diff --git a/src/neutrino/Default/relativity.h b/src/neutrino/Default/relativity.h index f9f88f511c53f425ed4f2f3f406388346b37668a..b3c134900a930cf016f3aa3b8aea0b054243a106 100644 --- a/src/neutrino/Default/relativity.h +++ b/src/neutrino/Default/relativity.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Willem Elbers (willem.h.elbers@durham.ac.uk) + * Copyright (c) 2021 Willem Elbers (willem.h.elbers@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 diff --git a/src/neutrino_io.h b/src/neutrino_io.h index f23d169dc5449ce8423f80f8a037ac0a4a2b1fc4..2c28c33670e95a35062822a7eda49f2e7096ecac 100644 --- a/src/neutrino_io.h +++ b/src/neutrino_io.h @@ -20,7 +20,7 @@ #define SWIFT_NEUTRINO_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct neutrino model */ #include "./neutrino/Default/neutrino_io.h" diff --git a/src/neutrino_properties.h b/src/neutrino_properties.h index 756d4e0c6c1a3bebb3e1849c950b5ab20aca76a1..456614d2672ca5d0a3ccd76de0210f96f8a310d4 100644 --- a/src/neutrino_properties.h +++ b/src/neutrino_properties.h @@ -20,7 +20,7 @@ #define SWIFT_NEUTRINO_PROPERTIES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct neutrino model */ #include "./neutrino/Default/neutrino_properties.h" diff --git a/src/output_list.c b/src/output_list.c index be06e585032e7b726f8020cb57e429456c410cbd..806be846a193764c82d8b6e58043e12bd0cc00d1 100644 --- a/src/output_list.c +++ b/src/output_list.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "output_list.h" @@ -90,6 +90,8 @@ void output_list_read_file(struct output_list *output_list, type = OUTPUT_LIST_AGE; } else if (strcasecmp(line, "# Scale Factor") == 0) { type = OUTPUT_LIST_SCALE_FACTOR; + } else if (strcasecmp(line, "# Comoving Distance") == 0) { + type = OUTPUT_LIST_COMOVING_DISTANCE; } else if (strcasecmp(line, "# Redshift, Select Output") == 0) { type = OUTPUT_LIST_REDSHIFT; output_list->select_output_on = 1; @@ -99,6 +101,9 @@ void output_list_read_file(struct output_list *output_list, } else if (strcasecmp(line, "# Scale Factor, Select Output") == 0) { type = OUTPUT_LIST_SCALE_FACTOR; output_list->select_output_on = 1; + } else if (strcasecmp(line, "# Comoving Distance, Select Output") == 0) { + type = OUTPUT_LIST_COMOVING_DISTANCE; + output_list->select_output_on = 1; } else if (strcasecmp(line, "# Redshift, Select Output, Label") == 0) { type = OUTPUT_LIST_REDSHIFT; output_list->select_output_on = 1; @@ -111,12 +116,18 @@ void output_list_read_file(struct output_list *output_list, type = OUTPUT_LIST_SCALE_FACTOR; output_list->select_output_on = 1; output_list->alternative_labels_on = 1; + } else if (strcasecmp(line, "# Comoving Distance, Select Output, Label") == + 0) { + type = OUTPUT_LIST_COMOVING_DISTANCE; + output_list->select_output_on = 1; + output_list->alternative_labels_on = 1; } else { error("Unable to interpret the header (%s) in file '%s'", line, filename); } if (!cosmo && - (type == OUTPUT_LIST_SCALE_FACTOR || type == OUTPUT_LIST_REDSHIFT)) + (type == OUTPUT_LIST_SCALE_FACTOR || type == OUTPUT_LIST_REDSHIFT || + type == OUTPUT_LIST_COMOVING_DISTANCE)) error( "Unable to compute a redshift or a scale factor without cosmology. " "Please change the header in '%s'", @@ -162,6 +173,9 @@ void output_list_read_file(struct output_list *output_list, if (cosmo && type == OUTPUT_LIST_AGE) *time = cosmology_get_scale_factor(cosmo, *time); + if (cosmo && type == OUTPUT_LIST_COMOVING_DISTANCE) + *time = cosmology_scale_factor_at_comoving_distance(cosmo, *time); + /* Search to find index for select output - select_output_index is the index * in the select_output_names array that corresponds to this select output * name. */ @@ -212,6 +226,11 @@ void output_list_read_file(struct output_list *output_list, if ((type == OUTPUT_LIST_SCALE_FACTOR) && (output_list->times[i] <= output_list->times[i - 1])) error("Output list not having monotonically increasing scale-factors."); + + if ((type == OUTPUT_LIST_COMOVING_DISTANCE) && + (output_list->times[i] <= output_list->times[i - 1])) + error( + "Output list not having monotonically decreasing comoving distance."); } /* set current indice to 0 */ diff --git a/src/output_list.h b/src/output_list.h index 14c7a6a1d07be8af85c7669dc9164e5852586a89..27a345a6f9c4d112d72334e4f0d0f0a294225691 100644 --- a/src/output_list.h +++ b/src/output_list.h @@ -20,7 +20,7 @@ #define SWIFT_OUTPUT_LIST_H /* Config parameters. */ -#include "config.h" +#include <config.h> /* Local headers */ #include "common_io.h" @@ -43,6 +43,7 @@ enum output_list_type { OUTPUT_LIST_AGE, OUTPUT_LIST_REDSHIFT, OUTPUT_LIST_SCALE_FACTOR, + OUTPUT_LIST_COMOVING_DISTANCE, }; /** diff --git a/src/output_options.c b/src/output_options.c index 214dc9d101f551202350ede6b21a623914353b27..350a4016bf49157bafd6f8e9ed0fb8a5ff5abfc0 100644 --- a/src/output_options.c +++ b/src/output_options.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdlib.h> diff --git a/src/parallel_io.c b/src/parallel_io.c index 170b32a117df475c4d26cadabd6541f9579d4c33..c6096fa6b0fe5a533505aedaff8e0c50ca53e84f 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5) @@ -50,6 +50,7 @@ #include "ic_info.h" #include "io_properties.h" #include "memuse.h" +#include "mhd_io.h" #include "output_list.h" #include "output_options.h" #include "part.h" @@ -392,7 +393,8 @@ void read_array_parallel(hid_t grp, struct io_props props, size_t N, */ void prepare_array_parallel( struct engine* e, hid_t grp, const char* fileName, FILE* xmfFile, - char* partTypeGroupName, struct io_props props, long long N_total, + const char* partTypeGroupName, const struct io_props props, + const long long N_total, const enum lossy_compression_schemes lossy_compression, const struct unit_system* snapshot_units) { @@ -519,9 +521,9 @@ void prepare_array_parallel( * @param internal_units The #unit_system used internally. * @param snapshot_units The #unit_system used in the snapshots. */ -void write_array_parallel_chunk(struct engine* e, hid_t h_data, - const struct io_props props, size_t N, - long long offset, +void write_array_parallel_chunk(const struct engine* e, hid_t h_data, + const struct io_props props, const size_t N, + const long long offset, const struct unit_system* internal_units, const struct unit_system* snapshot_units) { @@ -646,9 +648,10 @@ void write_array_parallel_chunk(struct engine* e, hid_t h_data, * @param internal_units The #unit_system used internally. * @param snapshot_units The #unit_system used in the snapshots. */ -void write_array_parallel(struct engine* e, hid_t grp, char* fileName, - char* partTypeGroupName, struct io_props props, - size_t N, long long N_total, int mpi_rank, +void write_array_parallel(const struct engine* e, hid_t grp, + const char* fileName, char* partTypeGroupName, + struct io_props props, size_t N, + const long long N_total, const int mpi_rank, long long offset, const struct unit_system* internal_units, const struct unit_system* snapshot_units) { @@ -761,12 +764,14 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, struct bpart** bparts, size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, size_t* Nnuparts, size_t* Nsinks, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_sink, 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, - int remap_ids, struct ic_info* ics_metadata) { + int* flag_entropy, const int with_hydro, + const int with_gravity, const int with_sink, + const int with_stars, const int with_black_holes, + const int with_cosmology, const int cleanup_h, + const int cleanup_sqrt_a, const double h, const double a, + const int mpi_rank, const int mpi_size, MPI_Comm comm, + MPI_Info info, const int n_threads, const int dry_run, + const int remap_ids, struct ic_info* ics_metadata) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -1003,6 +1008,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, if (with_hydro) { Nparticles = *Ngas; hydro_read_particles(*parts, list, &num_fields); + num_fields += mhd_read_particles(*parts, list + num_fields); num_fields += chemistry_read_particles(*parts, list + num_fields); num_fields += rt_read_particles(*parts, list + num_fields); } @@ -1138,6 +1144,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, * @param e The #engine. * @param fileName The file name to write to. * @param N_total The total number of particles of each type to write. + * @param to_write Whether or not specific particle types must be written. * @param numFields The number of fields to write for each particle type. * @param internal_units The #unit_system used internally. * @param snapshot_units The #unit_system used in the snapshots. @@ -1145,9 +1152,11 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, * @param subsample_fraction The subsampling fraction of each particle type. */ void prepare_file(struct engine* e, const char* fileName, - const char* xmfFileName, long long N_total[swift_type_count], + const char* xmfFileName, + const long long N_total[swift_type_count], + const int to_write[swift_type_count], const int numFields[swift_type_count], - char current_selection_name[FIELD_BUFFER_SIZE], + const char current_selection_name[FIELD_BUFFER_SIZE], const struct unit_system* internal_units, const struct unit_system* snapshot_units, const int subsample_any, @@ -1253,6 +1262,8 @@ void prepare_file(struct engine* e, const char* fileName, swift_type_count); io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, swift_type_count); + io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total, + swift_type_count); double MassTable[swift_type_count] = {0}; io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count); io_write_attribute(h_grp, "InitialMassTable", DOUBLE, @@ -1265,6 +1276,7 @@ void prepare_file(struct engine* e, const char* fileName, io_write_attribute_i(h_grp, "ThisFile", 0); io_write_attribute_s(h_grp, "SelectOutput", current_selection_name); io_write_attribute_i(h_grp, "Virtual", 0); + io_write_attribute(h_grp, "CanHaveTypes", INT, to_write, swift_type_count); if (subsample_any) { io_write_attribute_s(h_grp, "OutputType", "SubSampled"); @@ -1286,9 +1298,10 @@ void prepare_file(struct engine* e, const char* fileName, /* Loop over all particle types */ for (int ptype = 0; ptype < swift_type_count; ptype++) { - /* Don't do anything if there are (a) no particles of this kind, or (b) - * if we have disabled every field of this particle type. */ - if (N_total[ptype] == 0 || numFields[ptype] == 0) continue; + /* Don't do anything if there are + * (a) no particles of this kind in this run, or + * (b) if we have disabled every field of this particle type. */ + if (!to_write[ptype] || numFields[ptype] == 0) continue; /* Add the global information for that particle type to * the XMF meta-file */ @@ -1313,7 +1326,8 @@ void prepare_file(struct engine* e, const char* fileName, if (h_err < 0) error("Error while creating alias for particle group.\n"); /* Write the number of particles as an attribute */ - io_write_attribute_l(h_grp, "NumberOfParticles", N_total[ptype]); + io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]); + io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]); int num_fields = 0; struct io_props list[100]; @@ -1424,8 +1438,8 @@ void prepare_file(struct engine* e, const char* fileName, void write_output_parallel(struct engine* e, const struct unit_system* internal_units, const struct unit_system* snapshot_units, - int mpi_rank, int mpi_size, MPI_Comm comm, - MPI_Info info) { + const int mpi_rank, const int mpi_size, + MPI_Comm comm, MPI_Info info) { const struct part* parts = e->s->parts; const struct xpart* xparts = e->s->xparts; @@ -1440,7 +1454,12 @@ void write_output_parallel(struct engine* e, 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; + const int with_DM = e->s->with_DM; const int with_neutrinos = e->s->with_neutrinos; + const int with_hydro = (e->policy & engine_policy_hydro) ? 1 : 0; + const int with_stars = (e->policy & engine_policy_stars) ? 1 : 0; + const int with_black_hole = (e->policy & engine_policy_black_holes) ? 1 : 0; + const int with_sink = (e->policy & engine_policy_sinks) ? 1 : 0; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -1592,9 +1611,18 @@ void write_output_parallel(struct engine* e, /* Now everybody konws its offset and the total number of * particles of each type */ + /* List what fields to write. + * Note that we want to want to write a 0-size dataset for some species + * in case future snapshots will contain them (e.g. star formation) */ + const int to_write[swift_type_count] = { + with_hydro, with_DM, with_DM_background, with_sink, + with_stars, with_black_hole, with_neutrinos + + }; + /* Rank 0 prepares the file */ if (mpi_rank == 0) - prepare_file(e, fileName, xmfFileName, N_total, numFields, + prepare_file(e, fileName, xmfFileName, N_total, to_write, numFields, current_selection_name, internal_units, snapshot_units, subsample_any, subsample_fraction); @@ -1627,8 +1655,8 @@ void write_output_parallel(struct engine* e, io_write_cell_offsets(h_grp_cells, e->s->cdim, e->s->dim, e->s->cells_top, e->s->nr_cells, e->s->width, mpi_rank, /*distributed=*/0, subsample, subsample_fraction, - e->snapshot_output_count, N_total, offset, numFields, - internal_units, snapshot_units); + e->snapshot_output_count, N_total, offset, to_write, + numFields, internal_units, snapshot_units); /* Close everything */ if (mpi_rank == 0) { @@ -1698,7 +1726,12 @@ void write_output_parallel(struct engine* e, /* Loop over all particle types */ for (int ptype = 0; ptype < swift_type_count; ptype++) { - /* Don't do anything if no particle of this kind */ + /* Don't do anything if there are + * (a) no particles of this kind in this snapshot + * (b) if we have disabled every field of this particle type. + * + * Note: we have already created the array so we can escape if there are + * no particles but the corresponding to_write[] was set. */ if (N_total[ptype] == 0 || numFields[ptype] == 0) continue; /* Open the particle group in the file */ diff --git a/src/parallel_io.h b/src/parallel_io.h index 2395da81b43a80f92a850d0608db72c31759b2f0..aaac00aa4c8b1153fe844e61b9146843834030cf 100644 --- a/src/parallel_io.h +++ b/src/parallel_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 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 @@ -20,7 +20,7 @@ #define SWIFT_PARALLEL_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5) @@ -40,12 +40,14 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, struct bpart** bparts, size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, size_t* Nnuparts, size_t* Nsinks, size_t* Nsparts, size_t* Nbparts, - int* flag_entropy, int with_hydro, int with_gravity, - int with_sinks, 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, - int remap_ids, struct ic_info* ics_metadata); + int* flag_entropy, const int with_hydro, + const int with_gravity, const int with_sinks, + const int with_stars, const int with_black_holes, + const int with_cosmology, const int cleanup_h, + const int cleanup_sqrt_a, const double h, const double a, + const int mpi_rank, const int mpi_size, MPI_Comm comm, + MPI_Info info, const int nr_threads, const int dry_run, + const int remap_ids, struct ic_info* ics_metadata); void write_output_parallel(struct engine* e, const struct unit_system* internal_units, diff --git a/src/parser.c b/src/parser.c index ce4aba4b27ab0eb5db295ce9adb3bf715aa7d949..ae684607017f2f6b5476f14172ee370e56edc2b1 100644 --- a/src/parser.c +++ b/src/parser.c @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ /* Needs to be included so that strtok returns char * instead of a int *. */ @@ -1268,10 +1268,11 @@ void parser_write_params_to_file(const struct swift_params *params, fprintf(file, "# SWIFT used parameter file\n"); else fprintf(file, "# SWIFT unused parameter file\n"); - fprintf(file, "# Code version: %s\n", package_version()); + fprintf(file, "# code version: %s\n", package_version()); fprintf(file, "# git revision: %s\n", git_revision()); fprintf(file, "# git branch: %s\n", git_branch()); fprintf(file, "# git date: %s\n", git_date()); + fprintf(file, "# current date: %s\n", clocks_now(1 /* swift */)); /* Flags to track which parameters are written. */ int *written = (int *)calloc(params->paramCount, sizeof(int)); @@ -1397,3 +1398,38 @@ int parser_get_section_id(const struct swift_params *params, const char *name) { } return -1; } + +/** + * @brief Compares two param structs and sets the used flag of any + * parameters with different values to 1, all other parameters + * are set to unused. + * + * @param refparams Structure that holds the parameters to compare to. + * @param params Structure that holds the parameters to check. + * + * @result the number of changed values found. + */ +int parser_compare_params(const struct swift_params *refparams, + struct swift_params *params) { + + int changed = 0; + for (int j = 0; j < params->paramCount; j++) { + + /* All parameters are unused until found to differ to a reference + * parameter. */ + params->data[j].used = 0; + + for (int i = 0; i < refparams->paramCount; i++) { + if (strcmp(refparams->data[i].name, params->data[j].name) == 0) { + if (strcmp(refparams->data[i].value, params->data[j].value) != 0) { + + /* Same parameter, values differ. */ + params->data[j].used = 1; + changed++; + } + break; + } + } + } + return changed; +} diff --git a/src/parser.h b/src/parser.h index f60f4a0672cfbd9ffa84d98ec89b65c26bba81a5..072bbe03b719be025d2ddf04523caa8f13ef28af 100644 --- a/src/parser.h +++ b/src/parser.h @@ -21,7 +21,7 @@ #define SWIFT_PARSER_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers */ #include <stdio.h> @@ -130,4 +130,7 @@ void parser_struct_restore(const struct swift_params *params, FILE *stream); /* Lookup functions */ int parser_get_section_id(const struct swift_params *params, const char *name); +/* Compare two param structs for changed values. */ +int parser_compare_params(const struct swift_params *refparams, + struct swift_params *params); #endif /* SWIFT_PARSER_H */ diff --git a/src/part.c b/src/part.c index d52875ba651d62e939cd9e03ee862271e1839a45..aec016bea44387afa5b155e7f1330c23625b3892 100644 --- a/src/part.c +++ b/src/part.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI diff --git a/src/part.h b/src/part.h index ed1f914e730e62376d785a9432e4718a802a7a20..dce15ece9df269b51f5ceebefa32e3f9b52abc0b 100644 --- a/src/part.h +++ b/src/part.h @@ -20,7 +20,7 @@ #define SWIFT_PART_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers. */ #include <stddef.h> @@ -72,6 +72,7 @@ struct threadpool; #include "./hydro/Gizmo/hydro_part.h" #define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP +#define MPI_SYMMETRIC_FORCE_INTERACTION #elif defined(SHADOWFAX_SPH) #include "./hydro/Shadowswift/hydro_part.h" #define hydro_need_extra_init_loop 0 @@ -122,6 +123,8 @@ struct threadpool; #include "./black_holes/Default/black_holes_part.h" #elif defined(BLACK_HOLES_EAGLE) #include "./black_holes/EAGLE/black_holes_part.h" +#elif defined(BLACK_HOLES_SPIN_JET) +#include "./black_holes/SPIN_JET/black_holes_part.h" #else #error "Invalid choice of black hole particle" #endif diff --git a/src/part_type.c b/src/part_type.c index 18e47304190f923ed2880e6ce5ee7e7020594f1b..15bd75b86d67980deb64315f1815a0e90afb1890 100644 --- a/src/part_type.c +++ b/src/part_type.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 diff --git a/src/part_type.h b/src/part_type.h index 65fe0a3529a64506305ed49c4ebd3689de7e83da..79af0f5a06a12078bdef931dc1d5ec08b389afa2 100644 --- a/src/part_type.h +++ b/src/part_type.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 diff --git a/src/particle_buffer.c b/src/particle_buffer.c new file mode 100644 index 0000000000000000000000000000000000000000..0d971585feacb6c58bb0f2fecd8d31db85440222 --- /dev/null +++ b/src/particle_buffer.c @@ -0,0 +1,273 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 "particle_buffer.h" + +#include "align.h" +#include "error.h" +#include "memuse.h" + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * @brief Initialize a particle buffer. + * + * Stores a sequence of data objects of size element_size, + * allowing new elements to be appended by multiple threads + * simultaneously. Note that ONLY the append operation is + * thread safe. + * + * Objects are stored in a linked list of blocks and new blocks + * are allocated as needed. + * + * @param buffer The #particle_buffer + * @param element_size Size of a single element + * @param elements_per_block Number of elements to store in each block + * @param name Name to use when logging memory allocations + * + */ +void particle_buffer_init(struct particle_buffer *buffer, size_t element_size, + size_t elements_per_block, char *name) { + + buffer->element_size = element_size; + buffer->elements_per_block = elements_per_block; + buffer->first_block = NULL; + buffer->last_block = NULL; + lock_init(&buffer->lock); + + int len = snprintf(buffer->name, PARTICLE_BUFFER_NAME_LENGTH, "%s", name); + if (len >= PARTICLE_BUFFER_NAME_LENGTH || len < 0) + error("Buffer name truncated or encoding error"); +} + +/** + * @brief Deallocate a particle buffer. + * + * The buffer is no longer in a usable state after this. + * + * @param buffer The #particle_buffer + * + */ +void particle_buffer_free(struct particle_buffer *buffer) { + + struct particle_buffer_block *block = buffer->first_block; + while (block) { + struct particle_buffer_block *next = block->next; + swift_free(buffer->name, block->data); + free(block); + block = next; + } + buffer->first_block = NULL; + buffer->last_block = NULL; + if (lock_destroy(&buffer->lock) != 0) + error("Failed to destroy lock on particle buffer"); +} + +/** + * @brief Empty a particle buffer + * + * This leaves the buffer ready to accept new elements. + * + * @param buffer The #particle_buffer + * + */ +void particle_buffer_empty(struct particle_buffer *buffer) { + + const size_t element_size = buffer->element_size; + const size_t elements_per_block = buffer->elements_per_block; + char name[PARTICLE_BUFFER_NAME_LENGTH]; + strncpy(name, buffer->name, PARTICLE_BUFFER_NAME_LENGTH); + particle_buffer_free(buffer); + particle_buffer_init(buffer, element_size, elements_per_block, name); +} + +/** + * @brief Allocate a new particle buffer block + * + * @param buffer The #particle_buffer + * @param previous_block The previous final block in the linked list + */ +static struct particle_buffer_block *allocate_block( + struct particle_buffer *buffer, + struct particle_buffer_block *previous_block) { + + const size_t element_size = buffer->element_size; + const size_t elements_per_block = buffer->elements_per_block; + + /* Allocate the struct */ + struct particle_buffer_block *block = (struct particle_buffer_block *)malloc( + sizeof(struct particle_buffer_block)); + if (!block) + error("Failed to allocate new particle buffer block: %s", buffer->name); + + /* Allocate data buffer */ + char *data; + if (swift_memalign(buffer->name, (void **)&data, SWIFT_STRUCT_ALIGNMENT, + element_size * elements_per_block) != 0) { + error("Failed to allocate particle buffer data block: %s", buffer->name); + } + + /* Initalise the struct */ + block->data = data; + block->num_elements = 0; + block->next = NULL; + + if (previous_block) previous_block->next = block; + + return block; +} + +/** + * @brief Append an element to a particle buffer. + * + * May be called from multiple threads simultaneously. + * + * @param buffer The #particle_buffer + * @param data The element to append + * + */ +void particle_buffer_append(struct particle_buffer *buffer, void *data) { + + const size_t element_size = buffer->element_size; + const size_t elements_per_block = buffer->elements_per_block; + + while (1) { + + /* Find the current block (atomic because last_block may be modified by + * other threads) */ + struct particle_buffer_block *block = + __atomic_load_n(&buffer->last_block, __ATOMIC_SEQ_CST); + + /* It may be that no blocks exist yet */ + if (!block) { + lock_lock(&buffer->lock); + /* Check no-one else allocated the first block before we got the lock */ + if (!buffer->last_block) { + block = allocate_block(buffer, NULL); + buffer->first_block = block; + __atomic_thread_fence(__ATOMIC_SEQ_CST); + /* After this store other threads will write to the new block, + so all initialization must complete before this. */ + __atomic_store_n(&buffer->last_block, block, __ATOMIC_SEQ_CST); + } + if (lock_unlock(&buffer->lock) != 0) + error("Failed to unlock particle buffer"); + /* Now try again */ + continue; + } + + /* Find next available index in current block */ + size_t index = + __atomic_fetch_add(&block->num_elements, 1, __ATOMIC_SEQ_CST); + + if (index < elements_per_block) { + /* We reserved a valid index, so copy the data */ + memcpy(block->data + index * element_size, data, element_size); + return; + } else { + /* No space left, so we need to allocate a new block */ + lock_lock(&buffer->lock); + /* Check no-one else already did it before we got the lock */ + if (!block->next) { + /* Allocate and initialize the new block */ + struct particle_buffer_block *new_block = allocate_block(buffer, block); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + /* After this store other threads will write to the new block, + so all initialization must complete before this. */ + __atomic_store_n(&buffer->last_block, new_block, __ATOMIC_SEQ_CST); + } + if (lock_unlock(&buffer->lock) != 0) + error("Failed to unlock particle buffer"); + /* Now we have space, will try again */ + } + } +} + +/** + * @brief Iterate over data blocks in particle buffer. + * + * @param buffer The #particle_buffer + * @param block Initially null, returns pointer to next data block + * @param num_elements Returns number of elements in this block + * @param data Returns pointer to data in this block + * + */ +void particle_buffer_iterate(struct particle_buffer *buffer, + struct particle_buffer_block **block, + size_t *num_elements, void **data) { + + if (!*block) { + *block = buffer->first_block; + } else { + *block = (*block)->next; + } + + if (*block) { + *data = (*block)->data; + *num_elements = (*block)->num_elements; + if (*num_elements > buffer->elements_per_block) + *num_elements = buffer->elements_per_block; + } else { + *data = NULL; + *num_elements = 0; + } +} + +/** + * @brief Return number of elements in particle buffer. + * + * @param buffer The #particle_buffer + * + */ +size_t particle_buffer_num_elements(struct particle_buffer *buffer) { + + size_t num_elements = 0; + struct particle_buffer_block *block = buffer->first_block; + while (block) { + if (block->num_elements < buffer->elements_per_block) { + /* Non-full block, so block->num_elements is correct */ + num_elements += block->num_elements; + } else { + /* Full block, so block->num_elements may be out of range */ + num_elements += buffer->elements_per_block; + } + block = block->next; + } + return num_elements; +} + +/** + * @brief Return memory used by a particle buffer. + * + * @param buffer The #particle_buffer + * + */ +size_t particle_buffer_memory_use(struct particle_buffer *buffer) { + + size_t num_bytes = 0; + struct particle_buffer_block *block = buffer->first_block; + while (block) { + num_bytes += (buffer->elements_per_block * buffer->element_size); + block = block->next; + } + return num_bytes; +} diff --git a/src/particle_buffer.h b/src/particle_buffer.h new file mode 100644 index 0000000000000000000000000000000000000000..08217b5afa8b54d6e454c0ef460fd0286eb09cd9 --- /dev/null +++ b/src/particle_buffer.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 John Helly (j.c.helly@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 "lock.h" +#include "threadpool.h" + +#ifndef SWIFT_PARTICLE_BUFFER_H +#define SWIFT_PARTICLE_BUFFER_H + +#define PARTICLE_BUFFER_NAME_LENGTH 100 + +struct particle_buffer_block { + size_t num_elements; + char *data; + struct particle_buffer_block *next; +}; + +struct particle_buffer { + size_t element_size; + size_t elements_per_block; + struct particle_buffer_block *first_block; + struct particle_buffer_block *last_block; + swift_lock_type lock; + char name[PARTICLE_BUFFER_NAME_LENGTH]; +}; + +void particle_buffer_init(struct particle_buffer *buffer, size_t element_size, + size_t elements_per_block, char *name); + +void particle_buffer_free(struct particle_buffer *buffer); + +void particle_buffer_empty(struct particle_buffer *buffer); + +void particle_buffer_append(struct particle_buffer *buffer, void *data); + +void particle_buffer_iterate(struct particle_buffer *buffer, + struct particle_buffer_block **block, + size_t *num_elements, void **data); + +size_t particle_buffer_num_elements(struct particle_buffer *buffer); + +size_t particle_buffer_memory_use(struct particle_buffer *buffer); + +#endif /* SWIFT_PARTICLE_BUFFER_H */ diff --git a/src/particle_splitting.h b/src/particle_splitting.h index 78483ae97091faebd84a03b8609c74b1f4ed2420..202f2e3b7195351145c12200ab1eb2401bb17245 100644 --- a/src/particle_splitting.h +++ b/src/particle_splitting.h @@ -196,4 +196,24 @@ INLINE static int particle_splitting_write_bparticles( return 3; } +/** + * @brief Write particle splitting data to the stdout for debugging purposes. + * + * @param p Particle data. + * @param xp Extra particle data. + */ +__attribute__((always_inline)) INLINE static void +particle_splitting_debug_particle(const struct part* p, + const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] particle_splitting_data:", p->id); + warning( + "[PID%lld] progenitor_id = %lli, split_tree = %lli, " + "split_count = %hhu", + p->id, xp->split_data.progenitor_id, xp->split_data.split_tree, + xp->split_data.split_count); + } +} + #endif /* SWIFT_PARTICLE_SPLITTING_H */ diff --git a/src/partition.c b/src/partition.c index 8befe0734eca25dcd99dc535cb9717877920dab0..00b8d0c2c360f085e3a45ac2353db5bab03e79b0 100644 --- a/src/partition.c +++ b/src/partition.c @@ -28,7 +28,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers. */ #include <float.h> @@ -337,8 +337,8 @@ struct counts_mapper_data { * local memory to reduce contention, the amount of memory required is * precalculated by an additional loop determining the range of cell IDs. */ #define ACCUMULATE_SIZES_MAPPER(TYPE) \ - accumulate_sizes_mapper_##TYPE(void *map_data, int num_elements, \ - void *extra_data) { \ + partition_accumulate_sizes_mapper_##TYPE(void *map_data, int num_elements, \ + void *extra_data) { \ struct TYPE *parts = (struct TYPE *)map_data; \ struct counts_mapper_data *mydata = \ (struct counts_mapper_data *)extra_data; \ @@ -383,7 +383,7 @@ struct counts_mapper_data { * * part version. */ -static void ACCUMULATE_SIZES_MAPPER(part); +void ACCUMULATE_SIZES_MAPPER(part); /** * @brief Accumulate the sized counts of particles per cell. @@ -391,7 +391,7 @@ static void ACCUMULATE_SIZES_MAPPER(part); * * gpart version. */ -static void ACCUMULATE_SIZES_MAPPER(gpart); +void ACCUMULATE_SIZES_MAPPER(gpart); /** * @brief Accumulate the sized counts of particles per cell. @@ -399,7 +399,7 @@ static void ACCUMULATE_SIZES_MAPPER(gpart); * * spart version. */ -static void ACCUMULATE_SIZES_MAPPER(spart); +void ACCUMULATE_SIZES_MAPPER(spart); /* qsort support. */ static int ptrcmp(const void *p1, const void *p2) { @@ -440,9 +440,9 @@ static void accumulate_sizes(struct space *s, int verbose, double *counts) { mapper_data.counts = gcounts; mapper_data.size = gsize; - threadpool_map(&s->e->threadpool, accumulate_sizes_mapper_gpart, s->gparts, - s->nr_gparts, sizeof(struct gpart), space_splitsize, - &mapper_data); + threadpool_map(&s->e->threadpool, partition_accumulate_sizes_mapper_gpart, + s->gparts, s->nr_gparts, sizeof(struct gpart), + space_splitsize, &mapper_data); /* Get all the counts from all the nodes. */ if (MPI_Allreduce(MPI_IN_PLACE, gcounts, s->nr_cells, MPI_DOUBLE, MPI_SUM, @@ -480,17 +480,17 @@ static void accumulate_sizes(struct space *s, int verbose, double *counts) { mapper_data.counts = counts; hsize = (double)sizeof(struct part); mapper_data.size = hsize; - threadpool_map(&s->e->threadpool, accumulate_sizes_mapper_part, s->parts, - s->nr_parts, sizeof(struct part), space_splitsize, + threadpool_map(&s->e->threadpool, partition_accumulate_sizes_mapper_part, + s->parts, s->nr_parts, sizeof(struct part), space_splitsize, &mapper_data); } if (s->nr_sparts > 0) { ssize = (double)sizeof(struct spart); mapper_data.size = ssize; - threadpool_map(&s->e->threadpool, accumulate_sizes_mapper_spart, s->sparts, - s->nr_sparts, sizeof(struct spart), space_splitsize, - &mapper_data); + threadpool_map(&s->e->threadpool, partition_accumulate_sizes_mapper_spart, + s->sparts, s->nr_sparts, sizeof(struct spart), + space_splitsize, &mapper_data); } /* Merge the counts arrays across all nodes, if needed. Doesn't include any @@ -1379,8 +1379,8 @@ static void check_weights(struct task *tasks, int nr_tasks, * @param num_elements the number of data elements to process. * @param extra_data additional data for the mapper context. */ -static void partition_gather_weights(void *map_data, int num_elements, - void *extra_data) { +void partition_gather_weights(void *map_data, int num_elements, + void *extra_data) { struct task *tasks = (struct task *)map_data; struct weights_mapper_data *mydata = (struct weights_mapper_data *)extra_data; @@ -1430,9 +1430,14 @@ static void partition_gather_weights(void *map_data, int num_elements, /* Different weights for different tasks. */ if (t->type == task_type_drift_part || t->type == task_type_drift_gpart || + t->type == task_type_drift_spart || t->type == task_type_drift_bpart || t->type == task_type_ghost || t->type == task_type_extra_ghost || - t->type == task_type_kick1 || t->type == task_type_kick2 || - t->type == task_type_end_hydro_force || + t->type == task_type_stars_ghost || + t->type == task_type_bh_density_ghost || t->type == task_type_kick1 || + t->type == task_type_kick2 || t->type == task_type_timestep || + t->type == task_type_timestep_limiter || + t->type == task_type_timestep_sync || t->type == task_type_kick1 || + t->type == task_type_kick2 || t->type == task_type_end_hydro_force || t->type == task_type_end_grav_force || t->type == task_type_cooling || t->type == task_type_star_formation || t->type == task_type_timestep || t->type == task_type_init_grav || t->type == task_type_grav_down || @@ -2442,15 +2447,24 @@ static void check_weights(struct task *tasks, int nr_tasks, /* Now do the comparisons. */ double refsum = 0.0; double sum = 0.0; - for (int k = 0; k < nr_cells; k++) { - refsum += ref_weights_v[k]; - sum += weights_v[k]; + if (vweights) { + if (engine_rank == 0) message("checking vertex weight consistency"); + if (ref_weights_v == NULL) + error("vertex partition weights are inconsistent"); + for (int k = 0; k < nr_cells; k++) { + refsum += ref_weights_v[k]; + sum += weights_v[k]; + } + if (fabs(sum - refsum) > 1.0) { + error("vertex partition weights are not consistent (%f!=%f)", sum, + refsum); + } } - if (fabs(sum - refsum) > 1.0) { - error("vertex partition weights are not consistent (%f!=%f)", sum, refsum); - } else { + if (eweights) { + if (engine_rank == 0) message("checking edge weight consistency"); refsum = 0.0; sum = 0.0; + if (ref_weights_e == NULL) error("edge partition weights are inconsistent"); for (int k = 0; k < 26 * nr_cells; k++) { refsum += ref_weights_e[k]; sum += weights_e[k]; @@ -2459,7 +2473,7 @@ static void check_weights(struct task *tasks, int nr_tasks, error("edge partition weights are not consistent (%f!=%f)", sum, refsum); } } - message("partition weights checked successfully"); + if (engine_rank == 0) message("partition weights checked successfully"); } #endif #endif diff --git a/src/periodic.h b/src/periodic.h index e3656efc614b983ecfd300d30984b51bf8f01054..f0e18288a7526c71112923ef4d0cd6055698b967 100644 --- a/src/periodic.h +++ b/src/periodic.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_PERIODIC_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include "inline.h" @@ -39,6 +39,20 @@ _x < _a ? (_x + (_b - _a)) : ((_x >= _b) ? (_x - (_b - _a)) : _x); \ }) +/** + * @brief Limits the value of x to be between a and b + */ +__attribute__((always_inline, const)) INLINE static double box_wrap_multiple( + double x, const double a, const double b) { + while (x < a) { + x += (b - a); + } + while (x >= b) { + x -= (b - a); + } + return x; +} + /** * @brief Find the smallest distance dx along one axis within a box of size * box_size diff --git a/src/physical_constants.c b/src/physical_constants.c index 27ebc9276f3d93517ae01535b082b9190d3e2cae..c4f32892309689d13ec7dfeaf6b67b9e9ea4eead 100644 --- a/src/physical_constants.c +++ b/src/physical_constants.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "physical_constants.h" @@ -97,6 +97,18 @@ void phys_const_init(const struct unit_system *us, struct swift_params *params, const_electron_charge_cgs / units_general_cgs_conversion_factor(us, dimension_charge); + const float dimension_permea[5] = {1, 1, -2, -2, 0}; /* [g cm A^-2 s^-2] */ + internal_const->const_vacuum_permeability = + const_vacuum_permeability_cgs / + units_general_cgs_conversion_factor(us, dimension_permea); + + /* Overwrite mu0 if present in the file */ + if (params != NULL) { + internal_const->const_vacuum_permeability = + parser_get_opt_param_double(params, "PhysicalConstants:mu_0", + internal_const->const_vacuum_permeability); + } + const float dimension_mass[5] = {1, 0, 0, 0, 0}; /* [g] */ internal_const->const_electron_mass = const_electron_mass_cgs / @@ -125,6 +137,17 @@ void phys_const_init(const struct unit_system *us, struct swift_params *params, internal_const->const_light_year = const_light_year_cgs / units_general_cgs_conversion_factor(us, dimension_length); + internal_const->const_solar_radius = + const_solar_radius_cgs / + units_general_cgs_conversion_factor(us, dimension_length); + internal_const->const_earth_radius = + const_earth_radius_cgs / + units_general_cgs_conversion_factor(us, dimension_length); + + const float dimension_power[5] = {1, 2, -3, 0, 0}; /* [g cm^2 s^-3] */ + internal_const->const_solar_luminosity = + const_solar_luminosity_cgs / + units_general_cgs_conversion_factor(us, dimension_power); const float dimension_temperature[5] = {0, 0, 0, 0, 1}; /* [K] */ internal_const->const_T_CMB_0 = @@ -162,12 +185,17 @@ void phys_const_print(const struct phys_const *internal_const) { message("%25s = %e", "Thomson cross-section", internal_const->const_thomson_cross_section); message("%25s = %e", "Electron-Volt", internal_const->const_electron_volt); + message("%25s = %e", "Vacuum permeability", + internal_const->const_vacuum_permeability); message("%25s = %e", "Proton mass", internal_const->const_proton_mass); message("%25s = %e", "Year", internal_const->const_year); + message("%25s = %e", "Parsec", internal_const->const_parsec); message("%25s = %e", "Astronomical Unit", internal_const->const_astronomical_unit); - message("%25s = %e", "Parsec", internal_const->const_parsec); + message("%25s = %e", "Earth radius", internal_const->const_earth_radius); message("%25s = %e", "Solar mass", internal_const->const_solar_mass); + message("%25s = %e", "Solar luminosity", + internal_const->const_solar_luminosity); message("%25s = %e", "H_0 / h = 100 km/s/Mpc", internal_const->const_reduced_hubble); message("%25s = %e", "T_CMB0", internal_const->const_T_CMB_0); @@ -213,14 +241,20 @@ void phys_const_print_snapshot(hid_t h_file, const struct phys_const *p) { io_write_attribute_d(h_grp_cgs, "electron_charge", const_electron_charge_cgs); io_write_attribute_d(h_grp_cgs, "electron_volt", const_electron_volt_cgs); io_write_attribute_d(h_grp_cgs, "electron_mass", const_electron_mass_cgs); + io_write_attribute_d(h_grp_cgs, "vacuum_permeability", + const_vacuum_permeability_cgs); io_write_attribute_d(h_grp_cgs, "proton_mass", const_proton_mass_cgs); io_write_attribute_d(h_grp_cgs, "year", const_year_cgs); io_write_attribute_d(h_grp_cgs, "astronomical_unit", const_astronomical_unit_cgs); io_write_attribute_d(h_grp_cgs, "parsec", const_parsec_cgs); io_write_attribute_d(h_grp_cgs, "light_year", const_light_year_cgs); + io_write_attribute_d(h_grp_cgs, "solar_radius", const_solar_radius_cgs); + io_write_attribute_d(h_grp_cgs, "earth_radius", const_earth_radius_cgs); io_write_attribute_d(h_grp_cgs, "solar_mass", const_solar_mass_cgs); io_write_attribute_d(h_grp_cgs, "earth_mass", const_earth_mass_cgs); + io_write_attribute_d(h_grp_cgs, "solar_luminosity", + const_solar_luminosity_cgs); io_write_attribute_d(h_grp_cgs, "T_CMB_0", const_T_CMB_0_cgs); io_write_attribute_d(h_grp_cgs, "primordial_He_fraction", const_primordial_He_fraction_cgs); @@ -247,14 +281,20 @@ void phys_const_print_snapshot(hid_t h_file, const struct phys_const *p) { io_write_attribute_d(h_grp_int, "electron_charge", p->const_electron_charge); io_write_attribute_d(h_grp_int, "electron_volt", p->const_electron_volt); io_write_attribute_d(h_grp_int, "electron_mass", p->const_electron_mass); + io_write_attribute_d(h_grp_int, "vacuum_permeability", + p->const_vacuum_permeability); io_write_attribute_d(h_grp_int, "proton_mass", p->const_proton_mass); io_write_attribute_d(h_grp_int, "year", p->const_year); io_write_attribute_d(h_grp_int, "astronomical_unit", p->const_astronomical_unit); io_write_attribute_d(h_grp_int, "parsec", p->const_parsec); io_write_attribute_d(h_grp_int, "light_year", p->const_light_year); + io_write_attribute_d(h_grp_int, "solar_radius", p->const_solar_radius); + io_write_attribute_d(h_grp_int, "earth_radius", p->const_earth_radius); io_write_attribute_d(h_grp_int, "solar_mass", p->const_solar_mass); io_write_attribute_d(h_grp_int, "earth_mass", p->const_earth_mass); + io_write_attribute_d(h_grp_int, "solar_luminosity", + p->const_solar_luminosity); io_write_attribute_d(h_grp_int, "T_CMB_0", p->const_T_CMB_0); io_write_attribute_d(h_grp_int, "primordial_He_fraction", p->const_primordial_He_fraction); diff --git a/src/physical_constants.h b/src/physical_constants.h index 48d05f36254b468f7662a7e3f3e65b880d1781c5..2eaa7d98db51b4afd256e0f717bd634382bad394 100644 --- a/src/physical_constants.h +++ b/src/physical_constants.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -26,7 +26,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "parser.h" @@ -61,9 +61,12 @@ struct phys_const { /*! Stefan-Boltzmann constant */ double const_stefan_boltzmann; - /*! Charge of the electron */ + /*! Charge of the electron */ double const_electron_charge; + /*! Vacuum permeability */ + double const_vacuum_permeability; + /*! Electron-Volt */ double const_electron_volt; @@ -91,6 +94,15 @@ struct phys_const { /*! Mass of the Earth */ double const_earth_mass; + /*! Radius of the Sun */ + double const_solar_radius; + + /*! Radius of the Earth */ + double const_earth_radius; + + /*! Luminosity of the Sun */ + double const_solar_luminosity; + /*! Temperature of the CMB at present day */ double const_T_CMB_0; diff --git a/src/physical_constants_cgs.h b/src/physical_constants_cgs.h index 562e404669dd95da20e02b37a029f2c8bfe22cf1..940d6071e28ccc0bfd90942434d953e9b06c3dd3 100644 --- a/src/physical_constants_cgs.h +++ b/src/physical_constants_cgs.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -22,7 +22,7 @@ /** * @file physical_constants_cgs.h - * @brief Physical constants in the CGS unit system. + * @brief Physical constants in the rationalized-CGS unit system. * * The constants declared in this file should _NOT_ be used directly * Users should use the converted values in the phys_const structure @@ -79,6 +79,9 @@ const double const_stefan_boltzmann_cgs = 5.670374419e-5; /*! Elementary charge [A s] */ const double const_electron_charge_cgs = 1.602176634e-19; +/*! Vacuum permeability [g cm s^-2 A^-2] */ +const double const_vacuum_permeability_cgs = 1.256637061435e1; /* 4 pi */ + /*! Electron-Volt [g cm^2 s^-2] */ const double const_electron_volt_cgs = 1.602176634e-12; @@ -109,6 +112,12 @@ const double const_parsec_cgs = 3.08567758149e18; /*! Light-year [cm] */ const double const_light_year_cgs = 9.46063e17; +/*! Solar radius [cm] */ +const double const_solar_radius_cgs = 6.957e10; + +/*! Earth radius [cm] */ +const double const_earth_radius_cgs = 6.3781e8; + #ifdef SWIFT_USE_GADGET2_PHYSICAL_CONSTANTS /*! Mass of the Sun [g] */ @@ -124,6 +133,9 @@ const double const_solar_mass_cgs = 1.98841e33; /*! Mass of the Earth [g] */ const double const_earth_mass_cgs = 5.97217e27; +/*! Luminosity of the Sun [g cm^2 s^-3] */ +const double const_solar_luminosity_cgs = 3.828e33; + /*! Temperature of the CMB at present day [K] */ const double const_T_CMB_0_cgs = 2.7255; diff --git a/src/potential.c b/src/potential.c index a313598dae36569f8de9bf15078719886805a2a3..beebdcd17a32255756f76eccb9a6771da9ae7c0d 100644 --- a/src/potential.c +++ b/src/potential.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "potential.h" diff --git a/src/potential.h b/src/potential.h index f5ab1b9bc9c160f94ceff9138f5a8a8f46d2521f..9011eee11123f0d04883a69119ac6ee86aeb354d 100644 --- a/src/potential.h +++ b/src/potential.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right external potential definition */ #if defined(EXTERNAL_POTENTIAL_NONE) @@ -36,6 +36,8 @@ #include "./potential/isothermal/potential.h" #elif defined(EXTERNAL_POTENTIAL_HERNQUIST) #include "./potential/hernquist/potential.h" +#elif defined(EXTERNAL_POTENTIAL_HERNQUIST_SDMH05) +#include "./potential/hernquist_sdmh05/potential.h" #elif defined(EXTERNAL_POTENTIAL_NFW) #include "./potential/nfw/potential.h" #elif defined(EXTERNAL_POTENTIAL_NFW_MN) @@ -44,8 +46,6 @@ #include "./potential/disc_patch/potential.h" #elif defined(EXTERNAL_POTENTIAL_SINE_WAVE) #include "./potential/sine_wave/potential.h" -#elif defined(EXTERNAL_POTENTIAL_POINTMASS_RING) -#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) diff --git a/src/potential/constant/potential.h b/src/potential/constant/potential.h index f91d2b3fc8a13792901fee680678c9b93af20cbf..de0fb726042ae712c4c200168481ed3c4e2c7b4d 100644 --- a/src/potential/constant/potential.h +++ b/src/potential/constant/potential.h @@ -20,7 +20,7 @@ #define SWIFT_POTENTIAL_CONSTANT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -28,6 +28,7 @@ /* Local includes. */ #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -72,9 +73,14 @@ __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) { + const float gh = g->x[0] * potential->g[0] + g->x[1] * potential->g[1] + + g->x[2] * potential->g[2]; + const float pot = -gh / 3.f; + g->a_grav[0] += potential->g[0]; g->a_grav[1] += potential->g[1]; g->a_grav[2] += potential->g[2]; + gravity_add_comoving_potential(g, pot); } /** diff --git a/src/potential/disc_patch/potential.h b/src/potential/disc_patch/potential.h index 40c747314994cfdc4a38679d747e3351e4fbd4d1..d8486ed85fe7eae399e7082e051b41d859bf31d7 100644 --- a/src/potential/disc_patch/potential.h +++ b/src/potential/disc_patch/potential.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -21,15 +21,15 @@ #define SWIFT_DISC_PATCH_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> #include <math.h> /* Local includes. */ -#include "const.h" #include "error.h" +#include "gravity.h" #include "minmax.h" #include "parser.h" #include "part.h" @@ -115,7 +115,7 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep( const float norm = potential->norm; /* absolute value of height above disc */ - const float dx = fabsf(g->x[0] - potential->x_disc); + const float dx = fabs(g->x[0] - potential->x_disc); /* vertical acceleration */ const float x_accel = norm * tanhf(dx * b_inv); @@ -173,6 +173,7 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( const float abs_dx = fabsf(dx); const float t_growth = potential->growth_time; const float t_growth_inv = potential->growth_time_inv; + const float b = potential->scale_height; const float b_inv = potential->scale_height_inv; const float x_trunc = potential->x_trunc; const float x_max = potential->x_max; @@ -184,25 +185,30 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( /* Truncated or not ? */ float a_x; + float pot; if (abs_dx < x_trunc) { /* Acc. 2 pi sigma tanh(x/b) */ a_x = reduction_factor * norm_over_G * tanhf(abs_dx * b_inv); + pot = -reduction_factor * norm_over_G * logf(coshf(abs_dx * b_inv)) * b; } else if (abs_dx < x_max) { /* Acc. 2 pi sigma tanh(x/b) [1/2 + 1/2cos((x-xmax)/(pi x_trans))] */ a_x = reduction_factor * norm_over_G * tanhf(abs_dx * b_inv) * (0.5f + 0.5f * cosf((float)(M_PI) * (abs_dx - x_trunc) * x_trans_inv)); + pot = 0.f; } else { /* Acc. 0 */ a_x = 0.f; + pot = 0.f; } /* Get the correct sign. Recall G is multipiled in later on */ if (dx > 0) g->a_grav[0] -= a_x; if (dx < 0) g->a_grav[0] += a_x; + gravity_add_comoving_potential(g, pot); } /** @@ -283,8 +289,6 @@ static INLINE void potential_init_backend( parameter_file, "DiscPatchPotential:x_trunc", FLT_MAX); potential->x_max = parser_get_opt_param_double( parameter_file, "DiscPatchPotential:x_max", FLT_MAX); - potential->x_disc = - parser_get_param_double(parameter_file, "DiscPatchPotential:x_disc"); potential->timestep_mult = parser_get_param_double( parameter_file, "DiscPatchPotential:timestep_mult"); potential->growth_time = parser_get_opt_param_double( diff --git a/src/potential/hernquist/potential.h b/src/potential/hernquist/potential.h index 0f9ec03eb901b27e1aec3bff34d4be3b37bab8bb..144181f432f1a754e2934964c6b7b7e4bfda9cf4 100644 --- a/src/potential/hernquist/potential.h +++ b/src/potential/hernquist/potential.h @@ -20,13 +20,14 @@ #define SWIFT_POTENTIAL_HERNQUIST_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> /* Local includes. */ #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -72,6 +73,9 @@ struct external_potential { * time to get the time steps */ double timestep_mult; + /*! Inverse of the sqrt of G*M, a common factor */ + double sqrtgm_inv; + /*! Mode to use 0 for simplest form of potential purely 1 for idealized * galaxies */ int usedisk; @@ -91,21 +95,18 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep( const struct phys_const* restrict phys_const, const struct gpart* restrict g) { - const float G_newton = phys_const->const_newton_G; - /* Calculate the relative potential with respect to the centre of the * potential */ const float dx = g->x[0] - potential->x[0]; const float dy = g->x[1] - potential->x[1]; const float dz = g->x[2] - potential->x[2]; - /* calculate the radius */ + /* Calculate the radius */ const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2); - const float sqrtgm_inv = 1.f / sqrtf(G_newton * potential->mass); /* Calculate the circular orbital period */ - const float period = 2.f * M_PI * sqrtf(r) * potential->al * - (1 + r / potential->al) * sqrtgm_inv; + const float period = + 2.f * M_PI * sqrtf(r) * (potential->al + r) * potential->sqrtgm_inv; /* Time-step as a fraction of the cirecular orbital time */ const float time_step = potential->timestep_mult * period; @@ -138,14 +139,18 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( const float dz = g->x[2] - potential->x[2]; /* Calculate the acceleration */ - const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2); + const float r2 = dx * dx + dy * dy + dz * dz + potential->epsilon2; + const float r = sqrtf(r2); const float r_plus_a_inv = 1.f / (r + potential->al); const float r_plus_a_inv2 = r_plus_a_inv * r_plus_a_inv; - const float term = -potential->mass * r_plus_a_inv2 / r; - g->a_grav[0] += term * dx; - g->a_grav[1] += term * dy; - g->a_grav[2] += term * dz; + const float acc = -potential->mass * r_plus_a_inv2 / r; + const float pot = -potential->mass * r_plus_a_inv; + + g->a_grav[0] += acc * dx; + g->a_grav[1] += acc * dy; + g->a_grav[2] += acc * dz; + gravity_add_comoving_potential(g, pot); } /** @@ -167,7 +172,7 @@ external_gravity_get_potential_energy( const float dx = g->x[0] - potential->x[0]; const float dy = g->x[1] - potential->x[1]; const float dz = g->x[2] - potential->x[2]; - const float r = sqrtf(dx * dx + dy * dy + dz * dz); + const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2); const float r_plus_alinv = 1.f / (r + potential->al); return -phys_const->const_newton_G * potential->mass * r_plus_alinv; } @@ -206,7 +211,7 @@ static INLINE void potential_init_backend( potential->x[2] += s->dim[2] / 2.; } - /* check whether we use the more advanced idealized disk setting */ + /* Check whether we use the more advanced idealized disk setting */ potential->usedisk = parser_get_opt_param_int( parameter_file, "HernquistPotential:idealizeddisk", idealized_disk_default); @@ -264,7 +269,7 @@ static INLINE void potential_init_backend( potential->M200 = M200; potential->R200 = R200; - /* get the concentration from the parameter file */ + /* Get the concentration from the parameter file */ potential->c = parser_get_param_double(parameter_file, "HernquistPotential:concentration"); @@ -281,7 +286,7 @@ static INLINE void potential_init_backend( const double b = 2. * cc_inv * cc_inv * (log(1. + cc) - cc / (1. + cc)); /* Calculate the Hernquist equivalent scale length */ - potential->al = (b + sqrt(b)) / (1 - b) * R200; + potential->al = R200 * (b + sqrt(b)) / (1 - b); /* Define R200 inv*/ const double R200_inv = 1. / R200; @@ -291,9 +296,9 @@ static INLINE void potential_init_backend( (potential->R200 + potential->al) * R200_inv * R200_inv * potential->M200; - /* Depending on the disk mass and and the bulge mass the halo - * gets a different mass, because of this we read the fractions - * from the parameter file and calculate the absolute mass*/ + /* Depending on the disk mass and and the bulge mass, the halo + * gets a different mass. Because of this, we read the fractions + * from the parameter file and calculate the absolute mass */ const double diskfraction = parser_get_param_double( parameter_file, "HernquistPotential:diskfraction"); const double bulgefraction = parser_get_param_double( @@ -313,12 +318,14 @@ static INLINE void potential_init_backend( parser_get_param_double(parameter_file, "HernquistPotential:epsilon"); potential->epsilon2 = epsilon * epsilon; - /* Compute the minimal time-step. */ - /* This is the circular orbital time at the softened radius */ + /* Calculate a common factor in the calculation, i.e. 1/sqrt(GM)*/ const float sqrtgm = sqrtf(phys_const->const_newton_G * potential->mass); - potential->mintime = 2.f * sqrtf(epsilon) * potential->al * M_PI * - (1. + epsilon / potential->al) / sqrtgm * - potential->timestep_mult; + potential->sqrtgm_inv = 1. / sqrtgm; + + /* Compute the minimal time-step. */ + /* This is a fraction of the circular orbital time at the softened radius */ + potential->mintime = potential->timestep_mult * 2.f * sqrtf(epsilon) * M_PI * + (potential->al + epsilon) * potential->sqrtgm_inv; } /** diff --git a/src/potential/hernquist_sdmh05/potential.h b/src/potential/hernquist_sdmh05/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..8755d24ef939c70c012a1f00fd693e6fc5b8668b --- /dev/null +++ b/src/potential/hernquist_sdmh05/potential.h @@ -0,0 +1,316 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_HERNQUIST_SDMH05_H +#define SWIFT_POTENTIAL_HERNQUIST_SDMH05_H + +/* Config parameters. */ +#include <config.h> + +/* Some standard headers. */ +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "gravity.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Hernquist potential + * + * We follow the definition of + * Springel, Di Matteo & Hernquist, 2005, MNRAS, Vol. 361, Issue 3, 776-794 + */ +struct external_potential { + + /*! Position of the centre of potential */ + double x[3]; + + /*! Mass of the halo (total Hernquist mass) */ + double mass; + + /*! Scale length (often as a, to prevent confusion with the cosmological + * scale-factor we use al) */ + double al; + + /*! Square of the softening length. Acceleration tends to zero within this + * distance from the origin */ + double epsilon2; + + /* Minimum timestep of the potential given by the timestep multiple + * times the orbital time at the softening length */ + double mintime; + + /*! Time-step condition pre-factor, is multiplied times the circular orbital + * time to get the time steps */ + double timestep_mult; + + /* Additional common parameter inverse of sqrt(GM)*/ + double sqrtgm_inv; +}; + +/** + * @brief Computes the time-step in a Hernquist potential based on a + * fraction of the circular orbital time + * + * @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) { + + /* Calculate the relative potential with respect to the centre of the + * potential */ + const float dx = g->x[0] - potential->x[0]; + const float dy = g->x[1] - potential->x[1]; + const float dz = g->x[2] - potential->x[2]; + + /* calculate the radius */ + const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2); + + /* Calculate the circular orbital period */ + const float period = + 2.f * M_PI * sqrtf(r) * (potential->al + r) * potential->sqrtgm_inv; + + /* Time-step as a fraction of the cirecular orbital time */ + const float time_step = potential->timestep_mult * period; + + return max(time_step, potential->mintime); +} + +/** + * @brief Computes the gravitational acceleration from an Hernquist potential. + * + * Note that the accelerations are multiplied by Newton's G constant + * later on. + * + * a_x = - GM / (a+r)^2 * x/r + * a_y = - GM / (a+r)^2 * y/r + * a_z = - GM / (a+r)^2 * z/r + * + * @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) { + + /* Determine the position relative to the centre of the potential */ + const float dx = g->x[0] - potential->x[0]; + const float dy = g->x[1] - potential->x[1]; + const float dz = g->x[2] - potential->x[2]; + + /* Calculate the acceleration */ + const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2); + const float r_plus_a_inv = 1.f / (r + potential->al); + const float r_plus_a_inv2 = r_plus_a_inv * r_plus_a_inv; + const float acc = -potential->mass * r_plus_a_inv2 / r; + const float pot = -potential->mass * r_plus_a_inv; + + g->a_grav[0] += acc * dx; + g->a_grav[1] += acc * dy; + g->a_grav[2] += acc * dz; + gravity_add_comoving_potential(g, pot); +} + +/** + * @brief Computes the gravitational potential energy of a particle in an + * Hernquist potential. + * + * phi = - GM/(r+a) + * + * @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 dx = g->x[0] - potential->x[0]; + const float dy = g->x[1] - potential->x[1]; + const float dz = g->x[2] - potential->x[2]; + const float r = sqrtf(dx * dx + dy * dy + dz * dz); + const float r_plus_alinv = 1.f / (r + potential->al); + return -phys_const->const_newton_G * potential->mass * r_plus_alinv; +} + +/** + * @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) { + + /* Define the default value */ + static const int idealized_disk_default = 0; + static const double M200_default = 0.; + static const double V200_default = 0.; + static const double R200_default = 0.; + + /* Read in the position of the centre of potential */ + parser_get_param_double_array(parameter_file, "HernquistPotential:position", + 3, potential->x); + + /* Is the position absolute or relative to the centre of the box? */ + const int useabspos = + parser_get_param_int(parameter_file, "HernquistPotential:useabspos"); + + if (!useabspos) { + potential->x[0] += s->dim[0] / 2.; + potential->x[1] += s->dim[1] / 2.; + potential->x[2] += s->dim[2] / 2.; + } + + /* check whether we use the more advanced idealized disk setting */ + const int usedisk = parser_get_opt_param_int( + parameter_file, "HernquistPotential:idealizeddisk", + idealized_disk_default); + + if (!usedisk) { + /* Read the parameters of the model in the case of the simple + * potential form \f$ \Phi = - \frac{GM}{r+a} \f$ */ + potential->mass = + parser_get_param_double(parameter_file, "HernquistPotential:mass"); + potential->al = parser_get_param_double(parameter_file, + "HernquistPotential:scalelength"); + } else { + + /* Read the parameters in the case of a idealized disk + * There are 3 different possible input parameters M200, V200 and R200 + * First read in the mandatory parameters in this case */ + + const float G_newton = phys_const->const_newton_G; + const float kmoversoverMpc = phys_const->const_reduced_hubble; + + /* Initialize the variables */ + double M200 = parser_get_opt_param_double( + parameter_file, "HernquistPotential:M200", M200_default); + double V200 = parser_get_opt_param_double( + parameter_file, "HernquistPotential:V200", V200_default); + double R200 = parser_get_opt_param_double( + parameter_file, "HernquistPotential:R200", R200_default); + const double h = + parser_get_param_double(parameter_file, "HernquistPotential:h"); + + /* Hubble constant assumed for halo masses conversion */ + const double H0 = h * kmoversoverMpc; + + /* There are 3 legit runs possible with use disk, + * with a known M200, V200 or R200 */ + if (M200 != 0.0) { + /* Calculate V200 and R200 from M200 */ + V200 = cbrt(10. * M200 * G_newton * H0); + R200 = V200 / (10 * H0); + + } else if (V200 != 0.0) { + + /* Calculate M200 and R200 from V200 */ + M200 = V200 * V200 * V200 / (10. * G_newton * H0); + R200 = V200 / (10 * H0); + } else if (R200 != 0.0) { + + /* Calculate M200 and V200 from R200 */ + V200 = 10. * H0 * R200; + M200 = V200 * V200 * V200 / (10. * G_newton * H0); + } else { + error("Please specify one of the 3 variables M200, V200 or R200"); + } + + /* message("M200 = %g, R200 = %g, V200 = %g", M200, R200, V200); */ + /* message("H0 = %g", H0); */ + + /* Get the concentration from the parameter file */ + const double concentration = parser_get_param_double( + parameter_file, "HernquistPotential:concentration"); + + /* Calculate the Scale radius using the NFW definition */ + const double RS = R200 / concentration; + + /* Calculate the Hernquist equivalent scale length */ + potential->al = RS * sqrt(2. * (log(1. + concentration) - + concentration / (1. + concentration))); + + /* Depending on the disk mass and the bulge mass, the halo + * gets a different mass. Because of this, we read the fractions + * from the parameter file and calculate the absolute mass */ + const double diskfraction = parser_get_param_double( + parameter_file, "HernquistPotential:diskfraction"); + const double bulgefraction = parser_get_param_double( + parameter_file, "HernquistPotential:bulgefraction"); + /* Calculate the mass of the bulge and disk from the parameters */ + const double Mdisk = M200 * diskfraction; + const double Mbulge = M200 * bulgefraction; + + /* Store the mass of the DM halo */ + potential->mass = M200 - Mdisk - Mbulge; + } + + /* Retrieve the timestep and softening of the potential */ + potential->timestep_mult = parser_get_param_float( + parameter_file, "HernquistPotential:timestep_mult"); + const float epsilon = + parser_get_param_double(parameter_file, "HernquistPotential:epsilon"); + potential->epsilon2 = epsilon * epsilon; + + /* Calculate a common factor in the calculation, i.e. 1/sqrt(GM)*/ + const float sqrtgm = sqrtf(phys_const->const_newton_G * potential->mass); + potential->sqrtgm_inv = 1. / sqrtgm; + + /* Compute the minimal time-step. */ + /* This is a fraction of the circular orbital time at the softened radius */ + potential->mintime = potential->timestep_mult * 2.f * sqrtf(epsilon) * M_PI * + (potential->al + epsilon) * potential->sqrtgm_inv; +} + +/** + * @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 'hernquist Springel, Di Matteo & Hernquist 2005' " + "with properties are (x,y,z) = (%e, %e, %e), mass = %e scale length = %e " + ", minimum time = %e timestep multiplier = %e", + potential->x[0], potential->x[1], potential->x[2], potential->mass, + potential->al, potential->mintime, potential->timestep_mult); +} + +#endif /* SWIFT_POTENTIAL_HERNQUIST_SDMH05_H */ diff --git a/src/potential/isothermal/potential.h b/src/potential/isothermal/potential.h index 160372210e41036f2737c10a4aa3d2ddac1077f2..408561116bcc75c9d947cbb4433c1f28d1ad578d 100644 --- a/src/potential/isothermal/potential.h +++ b/src/potential/isothermal/potential.h @@ -2,7 +2,7 @@ * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) * Stefan Arridge (stefan.arridge@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -22,13 +22,14 @@ #define SWIFT_POTENTIAL_ISOTHERMAL_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> /* Local includes. */ #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -115,17 +116,22 @@ __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) { + const float G = phys_const->const_newton_G; const float dx = g->x[0] - potential->x[0]; const float dy = g->x[1] - potential->x[1]; const float dz = g->x[2] - potential->x[2]; - const float r2_plus_epsilon2_inv = - 1.f / (dx * dx + dy * dy + dz * dz + potential->epsilon2); - - const float term = -potential->vrot2_over_G * r2_plus_epsilon2_inv; - - g->a_grav[0] += term * dx; - g->a_grav[1] += term * dy; - g->a_grav[2] += term * dz; + const float r2_plus_epsilon2 = + dx * dx + dy * dy + dz * dz + potential->epsilon2; + const float r2_plus_epsilon2_inv = 1.f / r2_plus_epsilon2; + + const float acc = -potential->vrot2_over_G * r2_plus_epsilon2_inv; + const float pot = -potential->vrot2_over_G * logf(sqrtf(r2_plus_epsilon2)) / + (4. * M_PI * G); + + g->a_grav[0] += acc * dx; + g->a_grav[1] += acc * dy; + g->a_grav[2] += acc * dz; + gravity_add_comoving_potential(g, pot); } /** @@ -148,7 +154,7 @@ external_gravity_get_potential_energy( const float dy = g->x[1] - potential->x[1]; const float dz = g->x[2] - potential->x[2]; - return 0.5f * potential->vrot * potential->vrot * + return potential->vrot * potential->vrot * logf(dx * dx + dy * dy + dz * dz + potential->epsilon2); } @@ -182,8 +188,8 @@ static INLINE void potential_init_backend( potential->vrot = parser_get_param_double(parameter_file, "IsothermalPotential:vrot"); - potential->timestep_mult = parser_get_param_float( - parameter_file, "IsothermalPotential:timestep_mult"); + potential->timestep_mult = parser_get_opt_param_float( + parameter_file, "IsothermalPotential:timestep_mult", FLT_MAX); const double epsilon = parser_get_param_double(parameter_file, "IsothermalPotential:epsilon"); potential->vrot2_over_G = diff --git a/src/potential/nfw/potential.h b/src/potential/nfw/potential.h index dc9675ddaf74bbebfbf0535d242f1dd8b5fa0076..f3ea96b349696a3a46e693ed32016f34335d8d15 100644 --- a/src/potential/nfw/potential.h +++ b/src/potential/nfw/potential.h @@ -21,7 +21,7 @@ #define SWIFT_POTENTIAL_NFW_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -29,6 +29,7 @@ /* Local includes. */ #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -49,9 +50,6 @@ struct external_potential { /*! The scale radius of the NFW potential */ double r_s; - /*! The pre-factor \f$ 4 M200 / (4 pi f(c)) \f$ */ - double pre_factor; - /*! The critical density of the universe */ double rho_c; @@ -76,8 +74,8 @@ struct external_potential { /*! Common log term \f$ \ln(1+c_{200}) - \frac{c_{200}}{1 + c_{200}} \f$ */ double log_c200_term; - /*! inverse of common log term \f$ \ln(1+c_{200}) - \frac{c_{200}}{1 + - * c_{200}} \f$ */ + /*! M_200 times inverse of common log term \f$ \ln(1+c_{200}) - + * \frac{c_{200}}{1 + c_{200}} \f$ */ double M_200_times_log_c200_term_inv; /*! Softening length */ @@ -159,23 +157,26 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( double time, const struct external_potential* restrict potential, const struct phys_const* restrict phys_const, struct gpart* restrict g) { + /* Determine the position relative to the centre of the potential */ const float dx = g->x[0] - potential->x[0]; const float dy = g->x[1] - potential->x[1]; const float dz = g->x[2] - potential->x[2]; - const float r2 = dx * dx + dy * dy + dz * dz; - const float r2_inv = 1.f / r2; - + /* Calculate the acceleration */ + const float r2 = + dx * dx + dy * dy + dz * dz + potential->eps * potential->eps; const float r = sqrtf(r2); const float r_inv = 1.f / r; - const float M_encl = enclosed_mass_NFW(potential, r); - const float minus_M_encl_over_r3 = -M_encl * r_inv * r2_inv; + const float acc = -M_encl * r_inv * r_inv * r_inv; + const float pot = -potential->M_200_times_log_c200_term_inv * r_inv * + logf(1.f + r / potential->r_s); - g->a_grav[0] += minus_M_encl_over_r3 * dx; - g->a_grav[1] += minus_M_encl_over_r3 * dy; - g->a_grav[2] += minus_M_encl_over_r3 * dz; + g->a_grav[0] += acc * dx; + g->a_grav[1] += acc * dy; + g->a_grav[2] += acc * dz; + gravity_add_comoving_potential(g, pot); } /** @@ -200,10 +201,10 @@ external_gravity_get_potential_energy( const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->eps * potential->eps); - const float term1 = -potential->pre_factor / r; + const float term1 = -potential->M_200_times_log_c200_term_inv / r; const float term2 = logf(1.0f + r / potential->r_s); - return term1 * term2; + return phys_const->const_newton_G * term1 * term2; } /** @@ -271,11 +272,6 @@ static INLINE void potential_init_backend( (1 - potential->bulgefraction - potential->diskfraction) / potential->log_c200_term; - potential->pre_factor = - potential->M_200 * - (1 - potential->bulgefraction - potential->diskfraction) / - (4 * M_PI * potential->log_c200_term); - /* Compute the orbital time at the softening radius */ const double sqrtgm = sqrt(phys_const->const_newton_G * potential->M_200); const double epslnthing = log(1.f + potential->eps / potential->r_s) - diff --git a/src/potential/nfw_mn/potential.h b/src/potential/nfw_mn/potential.h index 355f1cbcd358d5fc7017886a433f547ab6399e85..c32ffe9130b7b4623c9ff0d331257449b0ddb2a8 100644 --- a/src/potential/nfw_mn/potential.h +++ b/src/potential/nfw_mn/potential.h @@ -21,7 +21,7 @@ #define SWIFT_POTENTIAL_NFW_MN_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -29,6 +29,7 @@ /* Local includes. */ #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -157,22 +158,31 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( /* First for the NFW part */ const float R2 = dx * dx + dy * dy; const float r = sqrtf(R2 + dz * dz + potential->eps * potential->eps); + const float r_inv = 1.f / r; const float term1 = potential->pre_factor; - const float term2 = (1.0f / ((r + potential->r_s) * r * r) - - logf(1.0f + r / potential->r_s) / (r * r * r)); + const float term2 = + r / (r + potential->r_s) - logf(1.0f + r / potential->r_s); - g->a_grav[0] += term1 * term2 * dx; - g->a_grav[1] += term1 * term2 * dy; - g->a_grav[2] += term1 * term2 * dz; + const float acc_nfw = term1 * term2 * r_inv * r_inv * r_inv; + const float pot_nfw = + -potential->pre_factor * logf(1.0f + r / potential->r_s) * r_inv; + + g->a_grav[0] += acc_nfw * dx; + g->a_grav[1] += acc_nfw * dy; + g->a_grav[2] += acc_nfw * dz; + gravity_add_comoving_potential(g, pot_nfw); /* Now the the MN disk */ const float f1 = sqrtf(potential->Zdisk * potential->Zdisk + dz * dz); const float f2 = potential->Rdisk + f1; - const float f3 = pow(R2 + f2 * f2, -3.0 / 2.0); + const float f3 = powf(R2 + f2 * f2, -1.5f); + const float mn_term = potential->Rdisk + sqrtf(potential->Zdisk + dz * dz); + const float pot_mn = -potential->Mdisk / sqrtf(R2 + mn_term * mn_term); g->a_grav[0] -= potential->Mdisk * f3 * dx; g->a_grav[1] -= potential->Mdisk * f3 * dy; g->a_grav[2] -= potential->Mdisk * f3 * (f2 / f1) * dz; + gravity_add_comoving_potential(g, pot_mn); } /** @@ -203,10 +213,10 @@ external_gravity_get_potential_energy( const float term2 = logf(1.0f + r / potential->r_s); /* Now for the MN disk */ - const float MN_term = potential->Rdisk + sqrtf(potential->Zdisk + dz * dz); - const float MN_pot = -potential->Mdisk / sqrtf(R2 + MN_term * MN_term); + const float mn_term = potential->Rdisk + sqrtf(potential->Zdisk + dz * dz); + const float mn_pot = -potential->Mdisk / sqrtf(R2 + mn_term * mn_term); - return term1 * term2 + MN_pot; + return phys_const->const_newton_G * (term1 * term2 + mn_pot); } /** diff --git a/src/potential/none/potential.h b/src/potential/none/potential.h index 2303f2531d059660a063a92be999405f05e9e6aa..9e1a83e5a0ba94b8f6f9c66a8fdf6f25a5018436 100644 --- a/src/potential/none/potential.h +++ b/src/potential/none/potential.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_POTENTIAL_NONE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/potential/point_mass/potential.h b/src/potential/point_mass/potential.h index 5ae03f8637708d75800a6a7fb283b98bdb42cec2..27f5a08afab229da1126a528c8851c5e16103f16 100644 --- a/src/potential/point_mass/potential.h +++ b/src/potential/point_mass/potential.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -21,7 +21,7 @@ #define SWIFT_POTENTIAL_POINT_MASS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -29,6 +29,7 @@ /* Local includes. */ #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -118,6 +119,7 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( g->a_grav[0] += -potential->mass * dx * rinv3; g->a_grav[1] += -potential->mass * dy * rinv3; g->a_grav[2] += -potential->mass * dz * rinv3; + gravity_add_comoving_potential(g, -potential->mass * rinv); } /** @@ -173,8 +175,8 @@ static INLINE void potential_init_backend( /* Read the other parameters of the model */ potential->mass = parser_get_param_double(parameter_file, "PointMassPotential:mass"); - potential->timestep_mult = parser_get_param_float( - parameter_file, "PointMassPotential:timestep_mult"); + potential->timestep_mult = parser_get_opt_param_float( + parameter_file, "PointMassPotential:timestep_mult", FLT_MAX); } /** diff --git a/src/potential/point_mass_ring/potential.h b/src/potential/point_mass_ring/potential.h deleted file mode 100644 index b384c3df6771c9f3342f5aed6514e75c77bf2c17..0000000000000000000000000000000000000000 --- a/src/potential/point_mass_ring/potential.h +++ /dev/null @@ -1,222 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Tom Theuns (tom.theuns@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_POTENTIAL_POINT_MASS_H -#define SWIFT_POTENTIAL_POINT_MASS_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 - Point mass case - */ -struct external_potential { - - /*! Position of the point mass */ - double x[3]; - - /*! Mass */ - double mass; - - /*! Time-step condition pre-factor */ - float timestep_mult; -}; - -/** - * @brief Computes the time-step due to the acceleration from a point mass - * based on Hopkins' central potential stuff (i.e. using a 'softened' - * gravitational edge). - * - * We pass in the time for simulations where the potential evolves with time. - * - * @param time The current time. - * @param potential The properties of the external potential. - * @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) { - - const float G_newton = phys_const->const_newton_G; - const float dx = g->x[0] - potential->x[0]; - const float dy = g->x[1] - potential->x[1]; - const float dz = g->x[2] - potential->x[2]; - const float r = sqrtf(dx * dx + dy * dy + dz * dz); - const float rinv = 1.f / r; - const float rinv2 = rinv * rinv; - const float rinv3 = rinv2 * rinv; - const float drdv = (g->x[0] - potential->x[0]) * (g->v_full[0]) + - (g->x[1] - potential->x[1]) * (g->v_full[1]) + - (g->x[2] - potential->x[2]) * (g->v_full[2]); - float factor; - - if (r < 0.175) { - // We need to flatten gravity as in SWIFT the timestepping is different - // than in GIZMO. This causes particles to become trapped close to the - // central point mass! - factor = 0.; - } else if (r < 0.35) { - factor = (8.16 * r * r) + 1.f - r / 0.35; - } else if (r > 2.1) { - factor = 1.f + (r - 2.1) / 0.1; - } else { - // 0.35 > r > 2.1 - factor = 1.f; - } - - const float dota_x = G_newton * potential->mass * rinv3 * - (-g->v_full[0] + 3.f * rinv2 * drdv * dx) * factor; - const float dota_y = G_newton * potential->mass * rinv3 * - (-g->v_full[1] + 3.f * rinv2 * drdv * dy) * factor; - const float dota_z = G_newton * potential->mass * rinv3 * - (-g->v_full[2] + 3.f * rinv2 * drdv * dz) * factor; - - const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; - const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + - g->a_grav[2] * g->a_grav[2]; - - if (fabsf(dota_2) > 0.f) - return potential->timestep_mult * sqrtf(a_2 / dota_2); - else - return FLT_MAX; -} - -/** - * @brief Computes the gravitational acceleration of a particle due to a - * point mass - * - * Note that the accelerations are multiplied by Newton's G constant later - * on. - * - * We pass in the time for simulations where the potential evolves with time. - * - * @param time The current time. - * @param potential The proerties of the external potential. - * @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* restrict potential, - const struct phys_const* restrict phys_const, struct gpart* restrict g) { - - const float dx = g->x[0] - potential->x[0]; - const float dy = g->x[1] - potential->x[1]; - const float dz = g->x[2] - potential->x[2]; - const float r = sqrtf(dx * dx + dy * dy + dz * dz); - const float rinv = 1.f / r; - const float rinv2 = rinv * rinv; - const float rinv3 = rinv * rinv2; - float factor = 1.f; - - if (r < 0.175) { - // We need to flatten gravity as in SWIFT the timestepping is different - // than in GIZMO. This causes particles to become trapped close to the - // central point mass! - factor = 0.; - printf("Help me, I'm trapped! (r = %f id = %lld)\n", r, - g->id_or_neg_offset); - } else if (r < 0.35) { - factor = (8.16 * r * r) + 1.f - r / 0.35; - printf("Factor is %f, r is %f \n", factor, r); - } else if (r > 2.1) { - factor = 1.f + (r - 2.1) / 0.1; - } else { - // 0.35 > r > 2.1 - factor = 1.f; - } - - g->a_grav[0] += -potential->mass * dx * rinv3 * factor; - g->a_grav[1] += -potential->mass * dy * rinv3 * factor; - g->a_grav[2] += -potential->mass * dz * rinv3 * factor; -} - -/** - * @brief Computes the gravitational potential energy of a particle in a point - * mass potential. - * - * @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 dx = g->x[0] - potential->x[0]; - const float dy = g->x[1] - potential->x[1]; - const float dz = g->x[2] - potential->x[2]; - const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz); - return -phys_const->const_newton_G * potential->mass * rinv; -} - -/** - * @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 s The #space we run in. - * @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) { - - parser_get_param_double_array(parameter_file, "PointMassPotential:position", - 3, potential->x); - potential->mass = - parser_get_param_double(parameter_file, "PointMassPotential:mass"); - potential->timestep_mult = parser_get_param_float( - parameter_file, "PointMassPotential:timestep_mult"); -} - -/** - * @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 'Point mass' with properties (x,y,z) = (%e, %e, " - "%e), M = %e timestep multiplier = %e.", - potential->x[0], potential->x[1], potential->x[2], potential->mass, - potential->timestep_mult); -} - -#endif /* SWIFT_POTENTIAL_POINT_MASS_H */ diff --git a/src/potential/point_mass_softened/potential.h b/src/potential/point_mass_softened/potential.h index 050bc1a00c98da4c350e59cf1ef8ef855094e552..1e710c9c04ae7d4885aa1101c70fd59c37e4c51b 100644 --- a/src/potential/point_mass_softened/potential.h +++ b/src/potential/point_mass_softened/potential.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * Josh Borrow (joshua.borrow@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ #define SWIFT_POTENTIAL_POINT_MASS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -30,6 +30,7 @@ /* Local includes. */ #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -37,7 +38,7 @@ #include "units.h" /** - * @brief External Potential Properties - Point mass case + * @brief External Potential Properties - Softened point mass case * * This file contains the softened central potential. This has a 'softening * factor' that prevents the potential from becoming singular at the centre @@ -143,6 +144,7 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( g->a_grav[0] += -potential->mass * dx * rinv3; g->a_grav[1] += -potential->mass * dy * rinv3; g->a_grav[2] += -potential->mass * dz * rinv3; + gravity_add_comoving_potential(g, -potential->mass * rinv); } /** @@ -200,8 +202,8 @@ static INLINE void potential_init_backend( /* Read the other parameters of the model */ potential->mass = parser_get_param_double(parameter_file, "PointMassPotential:mass"); - potential->timestep_mult = parser_get_param_float( - parameter_file, "PointMassPotential:timestep_mult"); + potential->timestep_mult = parser_get_opt_param_float( + parameter_file, "PointMassPotential:timestep_mult", FLT_MAX); potential->softening = parser_get_param_float(parameter_file, "PointMassPotential:softening"); } diff --git a/src/potential/sine_wave/potential.h b/src/potential/sine_wave/potential.h index 8a1786baaf9ed0b0683ea16fdefee997ecb4eceb..b759c340afcfc0e41013954e9a7258bfe4848c01 100644 --- a/src/potential/sine_wave/potential.h +++ b/src/potential/sine_wave/potential.h @@ -20,15 +20,15 @@ #define SWIFT_SINE_WAVE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> #include <math.h> /* Local includes. */ -#include "const.h" #include "error.h" +#include "gravity.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -36,7 +36,7 @@ #include "units.h" /** - * @brief External Potential Properties - Sine wave case + * @brief External Potential Properties - Sine wave along the x-axis case */ struct external_potential { @@ -80,13 +80,15 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration( const struct phys_const* restrict phys_const, struct gpart* restrict g) { float Acorr = 1.; + if (time < potential->growth_time) Acorr = time / potential->growth_time; - if (time < potential->growth_time) { - Acorr = time / potential->growth_time; - } - - g->a_grav[0] = potential->amplitude * Acorr * sin(2. * M_PI * g->x[0]) / + g->a_grav[0] = potential->amplitude * Acorr * sinf(2. * M_PI * g->x[0]) / phys_const->const_newton_G; + + const float pot = potential->amplitude * Acorr * cosf(2. * M_PI * g->x[0]) / + (phys_const->const_newton_G * 2. * M_PI); + + gravity_add_comoving_potential(g, pot); } /** @@ -103,8 +105,11 @@ external_gravity_get_potential_energy( double time, const struct external_potential* potential, const struct phys_const* const phys_const, const struct gpart* gp) { - /* this potential does not really have a potential energy */ - return 0.; + float Acorr = 1.; + if (time < potential->growth_time) Acorr = time / potential->growth_time; + + return potential->amplitude * Acorr * cosf(2. * M_PI * gp->x[0]) / + (phys_const->const_newton_G * 2. * M_PI); } /** @@ -125,8 +130,8 @@ static INLINE void potential_init_backend( parser_get_param_double(parameter_file, "SineWavePotential:amplitude"); potential->growth_time = parser_get_opt_param_double( parameter_file, "SineWavePotential:growth_time", 0.); - potential->timestep_limit = parser_get_param_double( - parameter_file, "SineWavePotential:timestep_limit"); + potential->timestep_limit = parser_get_opt_param_double( + parameter_file, "SineWavePotential:timestep_limit", FLT_MAX); } /** diff --git a/src/power_spectrum.c b/src/power_spectrum.c new file mode 100644 index 0000000000000000000000000000000000000000..324d48c231b6a6189973a40476640e4ec62461a2 --- /dev/null +++ b/src/power_spectrum.c @@ -0,0 +1,1490 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Marcel van Daalen (daalen@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> + +#ifdef WITH_MPI +#include <mpi.h> +#endif + +#ifdef HAVE_FFTW +#include <fftw3.h> +#endif + +/* Standard headers */ +#include <stdio.h> +#include <string.h> + +/* This object's header. */ +#include "power_spectrum.h" + +/* Local includes. */ +#include "cooling.h" +#include "engine.h" +#include "minmax.h" +#include "neutrino.h" +#include "random.h" +#include "row_major_id.h" +#include "space.h" +#include "threadpool.h" +#include "tools.h" +#include "units.h" + +#define power_data_default_grid_side_length 256 +#define power_data_default_fold_factor 4 +#define power_data_default_window_order 3 + +#ifdef HAVE_FFTW + +/** + * @brief Return the #power_type corresponding to a given string. + */ +INLINE static enum power_type power_spectrum_get_type(const char* name) { + if (strcasecmp(name, "matter") == 0) + return pow_type_matter; + else if (strcasecmp(name, "cdm") == 0) + return pow_type_cdm; + else if (strcasecmp(name, "gas") == 0) + return pow_type_gas; + else if (strcasecmp(name, "starBH") == 0) + return pow_type_starBH; + else if (strcasecmp(name, "pressure") == 0) + return pow_type_pressure; + else if (strcasecmp(name, "neutrino") == 0) + return pow_type_neutrino; + else if (strcasecmp(name, "neutrino0") == 0) + return pow_type_neutrino_0; + else if (strcasecmp(name, "neutrino1") == 0) + return pow_type_neutrino_1; + else + error("Do not recognize the power spectrum type '%s'.", name); + return pow_type_count; +} + +/** + * @brief Get the name of the type of power component. + * + * @param type The #power_type for which we want the name. + */ +INLINE static const char* get_powtype_name(const enum power_type type) { + + static const char* powtype_names[pow_type_count] = {"Matter", + "CDM", + "gas", + "stars/BHs", + "electron pressure", + "neutrino", + "neutrino (even)", + "neutrino (odd)"}; + + return powtype_names[type]; +} + +INLINE static const char* get_powtype_filename(const enum power_type type) { + + static const char* powtype_filenames[pow_type_count] = { + "matter", "cdm", "gas", "starBH", + "pressure", "neutrino", "neutrino0", "neutrino1"}; + + return powtype_filenames[type]; +} + +/** + * @brief Shared information for shot noise to be used by all the threads in the + * pool. + */ +struct shot_mapper_data { + const struct cell* cells; + double tot12; + enum power_type type1; + enum power_type type2; + const struct engine* e; + struct neutrino_model* nu_model; +}; + +/** + * @brief Shared information about the mesh to be used by all the threads in the + * pool. + */ +struct grid_mapper_data { + const struct cell* cells; + double* dens; + int N; + enum power_type type; + int windoworder; + double dim[3]; + double fac; + const struct engine* e; + struct neutrino_model* nu_model; +}; + +/** + * @brief Shared information about the mesh to be used by all the threads in the + * pool. + */ +struct conv_mapper_data { + double* grid; + int Ngrid; + double invcellmean; +}; + +/** + * @brief Shared information needed for calculating power from a Fourier grid. + */ +struct pow_mapper_data { + fftw_complex* powgridft; + fftw_complex* powgridft2; + int Ngrid; + int windoworder; + int* kbin; + int* modecounts; + double* powersum; + double jfac; +}; + +/** + * @brief Decided whether or not a given particle should be considered or + * not for the specific #power_type we are computing. + */ +int should_collect_mass(const enum power_type type, const struct gpart* gp, + const integertime_t ti_current) { + + if (type == pow_type_matter) { + /* Select all particles */ + return 1; + } else if (type == pow_type_cdm) { + if (gp->type == swift_type_dark_matter || + gp->type == swift_type_dark_matter_background) + return 1; + } else if (type == pow_type_gas) { + if (gp->type == swift_type_gas) return 1; + } else if (type == pow_type_starBH) { + if (gp->type == swift_type_stars || gp->type == swift_type_black_hole) + return 1; + } else if (type == pow_type_neutrino) { + if (gp->type == swift_type_neutrino) return 1; + } else if (type == pow_type_neutrino_0) { + if (gp->type == swift_type_neutrino) { + /* Randomly divide the neutrino ensemble in half */ + return random_unit_interval(gp->id_or_neg_offset, ti_current, + random_number_powerspectrum_split) < 0.5; + } + } else if (type == pow_type_neutrino_1) { + if (gp->type == swift_type_neutrino) + /* Randomly divide the neutrino ensemble in half */ + return random_unit_interval(gp->id_or_neg_offset, ti_current, + random_number_powerspectrum_split) >= 0.5; + } else { +#ifdef SWIFT_DEBUG_CHECKS + error("Invalid type!"); +#endif + } + + return 0; +} + +/** + * @brief Calculates the necessary mass terms for shot noise. + * + * @param c The #cell. + * @param tot12 The shot noise contribution returned. + * @param type1 The component type of field 1. + * @param type2 The component type of field 2. + * @param e The #engine. + */ +void shotnoiseterms(const struct cell* c, double* tot12, + const enum power_type type1, const enum power_type type2, + const struct engine* e, struct neutrino_model* nu_model) { + + const int gcount = c->grav.count; + const struct gpart* gparts = c->grav.parts; + + /* Handle on the other particle types */ + const struct part* parts = e->s->parts; + const struct xpart* xparts = e->s->xparts; + + /* Handle on the physics modules */ + const struct cosmology* cosmo = e->cosmology; + const struct hydro_props* hydro_props = e->hydro_properties; + const struct unit_system* us = e->internal_units; + const struct phys_const* phys_const = e->physical_constants; + const struct cooling_function_data* cool_func = e->cooling_func; + + /* Local accumulator for this cell */ + double local_tot12 = 0.; + + /* Calculate the value each particle adds to the grids */ + for (int i = 0; i < gcount; ++i) { + + /* Skip invalid particles */ + if (gparts[i].time_bin == time_bin_inhibited) continue; + + double quantity1; + + /* Special case first for the electron pressure */ + if (type1 == pow_type_pressure) { + + /* Skip non-gas particles */ + if (gparts[i].type != swift_type_gas) continue; + + const struct part* p = &parts[-gparts[i].id_or_neg_offset]; + const struct xpart* xp = &xparts[-gparts[i].id_or_neg_offset]; + quantity1 = cooling_get_electron_pressure(phys_const, hydro_props, us, + cosmo, cool_func, p, xp); + } else { + + /* We are collecting a mass of some kind. + * We skip any particle not matching the PS type we want */ + if (!should_collect_mass(type1, &gparts[i], e->ti_current)) continue; + + /* Compute weight (for neutrino delta-f weighting) */ + double weight1 = 1.0; + if (gparts[i].type == swift_type_neutrino) + gpart_neutrino_weight_mesh_only(&gparts[i], nu_model, &weight1); + + /* And eventually... collect */ + quantity1 = gparts[i].mass * weight1; + } + + /* Can we assign already? (i.e. this is an auto-spectrum) */ + if (type1 == type2) { + + /* Now assign to the shot noise collection */ + local_tot12 += quantity1 * quantity1; + } + + /* This is a cross-spectrum */ + else { + + double quantity2; + + /* Special case first for the electron pressure */ + if (type2 == pow_type_pressure) { + + /* Skip non-gas particles */ + if (gparts[i].type != swift_type_gas) continue; + + const struct part* p = &parts[-gparts[i].id_or_neg_offset]; + const struct xpart* xp = &xparts[-gparts[i].id_or_neg_offset]; + quantity2 = cooling_get_electron_pressure(phys_const, hydro_props, us, + cosmo, cool_func, p, xp); + } else { + + /* We are collecting a mass of some kind. + * We skip any particle not matching the PS type we want */ + if (!should_collect_mass(type2, &gparts[i], e->ti_current)) continue; + + /* Compute weight (for neutrino delta-f weighting) */ + double weight2 = 1.0; + if (gparts[i].type == swift_type_neutrino) + gpart_neutrino_weight_mesh_only(&gparts[i], nu_model, &weight2); + + /* And eventually... collect */ + quantity2 = gparts[i].mass * weight2; + } + + /* Now assign to the shot noise collection */ + local_tot12 += quantity1 * quantity2; + } + + } /* Loop over particles */ + + /* Now that we are done with this cell, write back to the global accumulator + */ + atomic_add_d(tot12, local_tot12); +} + +/** + * @brief Threadpool mapper function for the shot noise calculation. + * + * @param map_data A chunk of the list of local cells. + * @param num The number of cells in the chunk. + * @param extra The information about the cells. + */ +void shotnoise_mapper(void* map_data, int num, void* extra) { + + /* Unpack the shared information */ + struct shot_mapper_data* data = (struct shot_mapper_data*)extra; + const struct cell* cells = data->cells; + const struct engine* e = data->e; + struct neutrino_model* nu_model = data->nu_model; + + /* Pointer to the chunk to be processed */ + int* local_cells = (int*)map_data; + + /* Loop over the elements assigned to this thread */ + for (int i = 0; i < num; ++i) { + /* Pointer to local cell */ + const struct cell* c = &cells[local_cells[i]]; + + /* Calculate the necessary mass terms */ + shotnoiseterms(c, &data->tot12, data->type1, data->type2, e, nu_model); + } +} + +__attribute__((always_inline)) INLINE static void TSC_set( + double* mesh, const int N, const int i, const int j, const int k, + const double dx, const double dy, const double dz, const double value) { + + const double lx = 0.5 * (0.5 - dx) * (0.5 - dx); /* left side, dist 1 + dx */ + const double mx = 0.75 - dx * dx; /* center, dist |dx| */ + const double rx = 0.5 * (0.5 + dx) * (0.5 + dx); /* right side, dist 1 - dx */ + + const double ly = 0.5 * (0.5 - dy) * (0.5 - dy); /* left side, dist 1 + dy */ + const double my = 0.75 - dy * dy; /* center, dist |dy| */ + const double ry = 0.5 * (0.5 + dy) * (0.5 + dy); /* right side, dist 1 - dy */ + + const double lz = 0.5 * (0.5 - dz) * (0.5 - dz); /* left side, dist 1 + dz */ + const double mz = 0.75 - dz * dz; /* center, dist |dz| */ + const double rz = 0.5 * (0.5 + dz) * (0.5 + dz); /* right side, dist 1 - dz */ + + /* TSC interpolation */ + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j - 1, k - 1, N, 2)], + value * lx * ly * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j - 1, k + 0, N, 2)], + value * lx * ly * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j - 1, k + 1, N, 2)], + value * lx * ly * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j + 0, k - 1, N, 2)], + value * lx * my * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j + 0, k + 0, N, 2)], + value * lx * my * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j + 0, k + 1, N, 2)], + value * lx * my * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j + 1, k - 1, N, 2)], + value * lx * ry * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j + 1, k + 0, N, 2)], + value * lx * ry * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i - 1, j + 1, k + 1, N, 2)], + value * lx * ry * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j - 1, k - 1, N, 2)], + value * mx * ly * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j - 1, k + 0, N, 2)], + value * mx * ly * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j - 1, k + 1, N, 2)], + value * mx * ly * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 0, k - 1, N, 2)], + value * mx * my * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 0, k + 0, N, 2)], + value * mx * my * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 0, k + 1, N, 2)], + value * mx * my * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 1, k - 1, N, 2)], + value * mx * ry * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 1, k + 0, N, 2)], + value * mx * ry * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 1, k + 1, N, 2)], + value * mx * ry * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j - 1, k - 1, N, 2)], + value * rx * ly * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j - 1, k + 0, N, 2)], + value * rx * ly * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j - 1, k + 1, N, 2)], + value * rx * ly * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 0, k - 1, N, 2)], + value * rx * my * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 0, k + 0, N, 2)], + value * rx * my * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 0, k + 1, N, 2)], + value * rx * my * rz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 1, k - 1, N, 2)], + value * rx * ry * lz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 1, k + 0, N, 2)], + value * rx * ry * mz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 1, k + 1, N, 2)], + value * rx * ry * rz); +} + +INLINE static void gpart_to_grid_TSC(const struct gpart* gp, double* rho, + const int N, const double fac, + const double dim[3], const double value) { + + /* Fold the particle position position */ + const double pos_x = box_wrap_multiple(gp->x[0], 0., dim[0]) * fac; + const double pos_y = box_wrap_multiple(gp->x[1], 0., dim[1]) * fac; + const double pos_z = box_wrap_multiple(gp->x[2], 0., dim[2]) * fac; + + /* Workout the TSC coefficients */ + const int i = (int)(pos_x + 0.5); + const double dx = pos_x - i; + + const int j = (int)(pos_y + 0.5); + const double dy = pos_y - j; + + const int k = (int)(pos_z + 0.5); + const double dz = pos_z - k; + +#ifdef SWIFT_DEBUG_CHECKS + if (i < 0 || i > N) error("Invalid gpart position in x"); + if (j < 0 || j > N) error("Invalid gpart position in y"); + if (k < 0 || k > N) error("Invalid gpart position in z"); +#endif + + TSC_set(rho, N, i, j, k, dx, dy, dz, value); +} + +__attribute__((always_inline)) INLINE static void CIC_set( + double* mesh, const int N, const int i, const int j, const int k, + const double tx, const double ty, const double tz, const double dx, + const double dy, const double dz, const double value) { + + /* Classic CIC interpolation */ + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 0, k + 0, N, 2)], + value * tx * ty * tz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 0, k + 1, N, 2)], + value * tx * ty * dz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 1, k + 0, N, 2)], + value * tx * dy * tz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 0, j + 1, k + 1, N, 2)], + value * tx * dy * dz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 0, k + 0, N, 2)], + value * dx * ty * tz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 0, k + 1, N, 2)], + value * dx * ty * dz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 1, k + 0, N, 2)], + value * dx * dy * tz); + atomic_add_d( + &mesh[row_major_id_periodic_with_padding(i + 1, j + 1, k + 1, N, 2)], + value * dx * dy * dz); +} + +INLINE static void gpart_to_grid_CIC(const struct gpart* gp, double* rho, + const int N, const double fac, + const double dim[3], const double value) { + + /* Fold the particle position position */ + const double pos_x = box_wrap_multiple(gp->x[0], 0., dim[0]) * fac; + const double pos_y = box_wrap_multiple(gp->x[1], 0., dim[1]) * fac; + const double pos_z = box_wrap_multiple(gp->x[2], 0., dim[2]) * fac; + + /* Workout the CIC coefficients */ + const int i = (int)pos_x; + const double dx = pos_x - i; + const double tx = 1. - dx; + + const int j = (int)pos_y; + const double dy = pos_y - j; + const double ty = 1. - dy; + + const int k = (int)pos_z; + const double dz = pos_z - k; + const double tz = 1. - dz; + +#ifdef SWIFT_DEBUG_CHECKS + if (i < 0 || i > N) error("Invalid gpart position in x"); + if (j < 0 || j > N) error("Invalid gpart position in y"); + if (k < 0 || k > N) error("Invalid gpart position in z"); +#endif + + CIC_set(rho, N, i, j, k, tx, ty, tz, dx, dy, dz, value); +} + +INLINE static void gpart_to_grid_NGP(const struct gpart* gp, double* rho, + const int N, const double fac, + const double dim[3], const double value) { + + /* Fold the particle position position */ + const double pos_x = box_wrap_multiple(gp->x[0], 0., dim[0]) * fac; + const double pos_y = box_wrap_multiple(gp->x[1], 0., dim[1]) * fac; + const double pos_z = box_wrap_multiple(gp->x[2], 0., dim[2]) * fac; + + const int xi = (int)(pos_x + 0.5) % N; + const int yi = (int)(pos_y + 0.5) % N; + const int zi = (int)(pos_z + 0.5) % N; + atomic_add_d(&rho[(xi * N + yi) * (N + 2) + zi], value); +} + +/** + * @brief Assigns all the #gpart of a #cell to the power grid using the + * chosen mass assignment method. + * + * @param c The #cell. + * @param rho The density grid. + * @param N the size of the grid along one axis. + * @param fac Conversion factor of wrapped position to grid. + * @param type The #power_type we want to assign to the grid. + * @param windoworder The window to use for grid assignment. + * @param e The #engine. + */ +void cell_to_powgrid(const struct cell* c, double* rho, const int N, + const double fac, const enum power_type type, + const int windoworder, const double dim[3], + const struct engine* e, struct neutrino_model* nu_model) { + + const int gcount = c->grav.count; + const struct gpart* gparts = c->grav.parts; + + /* Handle on the other particle types */ + const struct part* parts = e->s->parts; + const struct xpart* xparts = e->s->xparts; + + /* Handle on the physics modules */ + const struct cosmology* cosmo = e->cosmology; + const struct hydro_props* hydro_props = e->hydro_properties; + const struct unit_system* us = e->internal_units; + const struct phys_const* phys_const = e->physical_constants; + const struct cooling_function_data* cool_func = e->cooling_func; + + /* Assign all the gpart of that cell to the mesh */ + for (int i = 0; i < gcount; ++i) { + + /* Skip invalid particles */ + if (gparts[i].time_bin == time_bin_inhibited) continue; + + /* Collect the quantity to assign to the mesh */ + double quantity; + + /* Special case first for the electron pressure */ + if (type == pow_type_pressure) { + + /* Skip non-gas particles */ + if (gparts[i].type != swift_type_gas) continue; + + const struct part* p = &parts[-gparts[i].id_or_neg_offset]; + const struct xpart* xp = &xparts[-gparts[i].id_or_neg_offset]; + quantity = cooling_get_electron_pressure(phys_const, hydro_props, us, + cosmo, cool_func, p, xp); + } else { + + /* We are collecting a mass of some kind. + * We skip any particle not matching the PS type we want */ + if (!should_collect_mass(type, &gparts[i], e->ti_current)) continue; + + /* Compute weight (for neutrino delta-f weighting) */ + double weight = 1.0; + if (gparts[i].type == swift_type_neutrino) + gpart_neutrino_weight_mesh_only(&gparts[i], nu_model, &weight); + + /* And eventually... collect */ + quantity = gparts[i].mass * weight; + } + + /* Assign the quantity to the grid */ + switch (windoworder) { + case 1: + gpart_to_grid_NGP(&gparts[i], rho, N, fac, dim, quantity); + break; + case 2: + gpart_to_grid_CIC(&gparts[i], rho, N, fac, dim, quantity); + break; + case 3: + gpart_to_grid_TSC(&gparts[i], rho, N, fac, dim, quantity); + break; + default: +#ifdef SWIFT_DEBUG_CHECKS + error("Not implemented!"); +#endif + break; + } + + } /* Loop over particles */ +} + +/** + * @brief Threadpool mapper function for the power grid assignment of a cell. + * + * @param map_data A chunk of the list of local cells. + * @param num The number of cells in the chunk. + * @param extra The information about the grid and cells. + */ +void cell_to_powgrid_mapper(void* map_data, int num, void* extra) { + + /* Unpack the shared information */ + const struct grid_mapper_data* data = (struct grid_mapper_data*)extra; + const struct cell* cells = data->cells; + double* grid = data->dens; + const int Ngrid = data->N; + const enum power_type type = data->type; + const int order = data->windoworder; + const double dim[3] = {data->dim[0], data->dim[1], data->dim[2]}; + const double gridfac = data->fac; + const struct engine* e = data->e; + struct neutrino_model* nu_model = data->nu_model; + + /* Pointer to the chunk to be processed */ + int* local_cells = (int*)map_data; + + /* Loop over the elements assigned to this thread */ + for (int i = 0; i < num; ++i) { + /* Pointer to local cell */ + const struct cell* c = &cells[local_cells[i]]; + + /* Assign this cell's content to the grid */ + cell_to_powgrid(c, grid, Ngrid, gridfac, type, order, dim, e, nu_model); + } +} + +/** + * @brief Mapper function for the conversion of mass to density contrast. + * + * @param map_data The density field array. + * @param num The number of elements to iterate on (along the x-axis). + * @param extra The information about the grid and conversion. + */ +void mass_to_contrast_mapper(void* map_data, int num, void* extra) { + + /* Unpack the shared information */ + const struct conv_mapper_data* data = (struct conv_mapper_data*)extra; + double* grid = data->grid; + const int Ngrid = data->Ngrid; + const double invcellmean = data->invcellmean; + + /* Range handled by this call */ + const int xi_start = (double*)map_data - grid; + const int xi_end = xi_start + num; + + /* Loop over the assigned cells, convert to density contrast */ + for (int xi = xi_start; xi < xi_end; ++xi) { + for (int yi = 0; yi < Ngrid; ++yi) { + for (int zi = 0; zi < Ngrid; ++zi) { + const int index = (xi * Ngrid + yi) * (Ngrid + 2) + + zi; /* Ngrid+2 because of padding */ + grid[index] = grid[index] * invcellmean; /* -1.0; -1 only affects k=0 */ + } + } + } +} + +/** + * @brief Mapper function for calculating the power from a Fourier grid. + * + * @param map_data The array of the density field Fourier transform. + * @param num The number of elements to iterate on (along the x-axis). + * @param extra Arrays to store the results/helper variables. + */ +void pow_from_grid_mapper(void* map_data, const int num, void* extra) { + + struct pow_mapper_data* data = (struct pow_mapper_data*)extra; + + /* Unpack the data struct */ + fftw_complex* restrict powgridft = data->powgridft; + fftw_complex* restrict powgridft2 = data->powgridft2; + const int Ngrid = data->Ngrid; + const int Nhalf = Ngrid / 2; + const int nyq2 = Nhalf * Nhalf; + const int windoworder = data->windoworder; + const int* restrict kbin = data->kbin; + const double jfac = data->jfac; + + /* Output data */ + int* restrict modecounts = data->modecounts; + double* restrict powersum = data->powersum; + + /* Range handled by this call */ + const int xi_start = (fftw_complex*)map_data - powgridft; + const int xi_end = xi_start + num; + + /* Loop over the assigned FT'd cells, get deconvolved power from them */ + for (int xi = xi_start; xi < xi_end; ++xi) { + + int kx = xi; + if (kx > Nhalf) kx -= Ngrid; + const double fx = kx * jfac; + const double invsincx = (xi == 0) ? 1.0 : fx / sin(fx); + + for (int yi = 0; yi < Ngrid; ++yi) { + + int ky = yi; + if (ky > Nhalf) ky -= Ngrid; + const double fy = ky * jfac; + const double invsincy = (yi == 0) ? 1.0 : fy / sin(fy); + + /* Real-data FFT, so there is no second z half (symmetric) */ + for (int zi = 0; zi < (Nhalf + 1); ++zi) { + + const int kz = zi; + const double fz = kz * jfac; + const double invsincz = (zi == 0) ? 1.0 : fz / sin(fz); + + const int kk = kx * kx + ky * ky + kz * kz; + + /* Avoid singularity and don't go beyond Nyquist (otherwise we spend + lots of time for little additional info */ + if (kk == 0 || kk > nyq2) continue; + + /* De-aliasing/deconvolution of mass assignment + (Jing 2005 but without iterations) */ + double W = invsincx * invsincy * invsincz; + + /* W = W^windoworder */ + W = integer_pow(W, windoworder); + + /* Avoid sqrt with a lookup table kbin[kk] (does cost more memory) + * bin = (int)(sqrt(kk) + 0.5); */ + const int bin = kbin[kk]; + + const int mult = (zi == 0) ? 1 : 2; + atomic_add(&modecounts[bin], mult); + + const int index = (xi * Ngrid + yi) * (Nhalf + 1) + zi; + atomic_add_d(&powersum[bin], + mult * W * W * + (powgridft[index][0] * powgridft2[index][0] + + powgridft[index][1] * powgridft2[index][1])); + } /* Loop over z */ + } /* Loop over y */ + } /* Loop over z */ +} + +/** + * @brief Initialize a power spectrum output file + * + * @param fp the file pointer + * @param us The current internal system of units. + * @param phys_const Physical constants in internal units + */ +INLINE static void power_init_output_file(FILE* fp, const enum power_type type1, + const enum power_type type2, + const struct unit_system* restrict us, + const struct phys_const* phys_const) { + + /* Write a header to the output file */ + if (type1 != type2) + fprintf(fp, "# %s-%s cross-spectrum\n", get_powtype_name(type1), + get_powtype_name(type2)); + else + fprintf(fp, "# %s power spectrum\n", get_powtype_name(type1)); + fprintf(fp, "######################################################\n"); + fprintf(fp, "#\n"); + fprintf(fp, "# (0) Redshift\n"); + fprintf(fp, "# Unit = dimensionless\n"); + fprintf(fp, "# (1) Fourier scale k\n"); + fprintf(fp, "# Unit = %e cm**-1\n", pow(us->UnitLength_in_cgs, -1.)); + fprintf(fp, "# Unit = %e pc**-1\n", + pow(1. / phys_const->const_parsec, -1.)); + fprintf(fp, "# Unit = %e Mpc**-1\n", + pow(1. / phys_const->const_parsec / 1e6, -1.)); + fprintf(fp, "# (2) The shot-noise subtracted power\n"); + if (type1 != pow_type_pressure && type2 != pow_type_pressure) { + fprintf(fp, "# Unit = %e cm**3\n", pow(us->UnitLength_in_cgs, 3.)); + fprintf(fp, "# Unit = %e pc**3\n", + pow(1. / phys_const->const_parsec, 3.)); + fprintf(fp, "# Unit = %e Mpc**3\n", + pow(1. / phys_const->const_parsec / 1e6, 3.)); + } else if (type1 == pow_type_pressure && type2 == pow_type_pressure) { + fprintf(fp, "# Unit = %e Mpc**3 * eV**2 * cm**-6\n", + pow(1. / phys_const->const_parsec / 1e6, 3.)); + } else { + fprintf(fp, "# Unit = %e Mpc**3 * eV * cm**-3\n", + pow(1. / phys_const->const_parsec / 1e6, 3.)); + } + fprintf(fp, "# (3) The shot-noise power (scale-independant)\n"); + if (type1 != pow_type_pressure && type2 != pow_type_pressure) { + fprintf(fp, "# Unit = %e cm**3\n", pow(us->UnitLength_in_cgs, 3.)); + fprintf(fp, "# Unit = %e pc**3\n", + pow(1. / phys_const->const_parsec, 3.)); + fprintf(fp, "# Unit = %e Mpc**3\n", + pow(1. / phys_const->const_parsec / 1e6, 3.)); + } else if (type1 == pow_type_pressure && type2 == pow_type_pressure) { + fprintf(fp, "# Unit = %e Mpc**3 * eV**2 * cm**-6\n", + pow(1. / phys_const->const_parsec / 1e6, 3.)); + } else { + fprintf(fp, "# Unit = %e Mpc**3 * eV * cm**-3\n", + pow(1. / phys_const->const_parsec / 1e6, 3.)); + } + fprintf(fp, "#\n"); + fprintf(fp, "# %13s %15s %15s %15s\n", "(0) ", "(1) ", "(2) ", + "(3) "); + fprintf(fp, "# %13s %15s %15s %15s\n", "z", "k", "P(k)", "P_noise"); +} + +/** + * @brief Compute the power spectrum between type1 and type2, including + * foldings and dealiasing. Only the real part of the power is returned. + * + * Distributes the cells into slabs, performs the FFT, calculates the dealiased + * power and writes this to file. Then repeats this procedure for a given + * number of foldings. + * + * @param type1 The #enum power_type indicating the first quantity to correlate. + * @param type2 The #enum power_type indicating the second quantity to + * correlate. + * @param powdata The #power_spectrum_data containing power spectrum + * parameters, FFT plans and pointers to the grids. + * @param s The #space containing the particles. + * @param tp The #threadpool object used for parallelisation. + * @param verbose Are we talkative? + */ +void power_spectrum(const enum power_type type1, const enum power_type type2, + struct power_spectrum_data* pow_data, const struct space* s, + struct threadpool* tp, const int verbose) { + + const int* local_cells = s->local_cells_top; + const int nr_local_cells = s->nr_local_cells; + const struct engine* e = s->e; + const struct unit_system* us = e->internal_units; + const struct phys_const* phys_const = e->physical_constants; + const int snapnum = e->ps_output_count; /* -1 if after snapshot dump */ + + /* Extract some useful constants */ + const int Ngrid = pow_data->Ngrid; + const int Ngrid2 = Ngrid * Ngrid; + const int Ngrid3 = Ngrid2 * Ngrid; + const int Nhalf = Ngrid / 2; + const int Nfold = pow_data->Nfold; + const int foldfac = pow_data->foldfac; + const double jfac = M_PI / Ngrid; + + /* Gather some neutrino constants if using delta-f weighting on the mesh */ + struct neutrino_model nu_model; + bzero(&nu_model, sizeof(struct neutrino_model)); + if (s->e->neutrino_properties->use_delta_f_mesh_only) + gather_neutrino_consts(s, &nu_model); + + if (verbose) + message("Preparing to calculate the %s-%s power spectrum.", + get_powtype_name(type1), get_powtype_name(type2)); + + /* could loop over particles but for now just abort */ + if (nr_local_cells == 0) + error("Cell infrastructure is not in place for power spectra."); + + /* Allocate the grids based on whether this is an auto- or cross-spectrum*/ + pow_data->powgrid = fftw_alloc_real(Ngrid2 * (Ngrid + 2)); + memuse_log_allocation("fftw_grid.grid", pow_data->powgrid, 1, + sizeof(double) * Ngrid2 * (Ngrid + 2)); + pow_data->powgridft = (fftw_complex*)pow_data->powgrid; + if (type1 != type2) { + pow_data->powgrid2 = fftw_alloc_real(Ngrid2 * (Ngrid + 2)); + memuse_log_allocation("fftw_grid.grid2", pow_data->powgrid2, 1, + sizeof(double) * Ngrid2 * (Ngrid + 2)); + pow_data->powgridft2 = (fftw_complex*)pow_data->powgrid2; + } else { + pow_data->powgrid2 = pow_data->powgrid; + pow_data->powgridft2 = pow_data->powgridft; + } + + /* Constants used for the normalization */ + double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; + const double volume = dim[0] * dim[1] * dim[2]; /* units Mpc^3 */ + const double Omega_m = s->e->cosmology->Omega_cdm + s->e->cosmology->Omega_b + + s->e->cosmology->Omega_nu_0; + const double meanrho = + Omega_m * s->e->cosmology->critical_density_0; /* 1e10 Msun/Mpc^3 */ + const double conv_EV = units_cgs_conversion_factor(us, UNIT_CONV_INV_VOLUME) / + phys_const->const_electron_volt; + + /* Inverse of the cosmic mean mass per grid cell in code units */ + double invcellmean, invcellmean2; + + if (type1 != pow_type_pressure) + invcellmean = Ngrid3 / (meanrho * volume); + else + invcellmean = Ngrid3 / volume * conv_EV; + + if (type2 != pow_type_pressure) + invcellmean2 = Ngrid3 / (meanrho * volume); + else + invcellmean2 = Ngrid3 / volume * conv_EV; + + /* When splitting the neutrino ensemble in half, double the inverse mean */ + if (type1 == pow_type_neutrino_0 || type1 == pow_type_neutrino_1) + invcellmean *= 2.0; + if (type2 == pow_type_neutrino_0 || type2 == pow_type_neutrino_1) + invcellmean2 *= 2.0; + + /* Gather the shared information to be used by the threads + for density computation */ + struct grid_mapper_data densdata; + struct grid_mapper_data densdata2; + + densdata.cells = s->cells_top; + densdata.dens = pow_data->powgrid; + densdata.N = Ngrid; + densdata.type = type1; + densdata.windoworder = pow_data->windoworder; + densdata.e = s->e; + densdata.nu_model = &nu_model; + if (type1 != type2) { + densdata2.cells = s->cells_top; + densdata2.dens = pow_data->powgrid2; + densdata2.N = Ngrid; + densdata2.type = type2; + densdata2.windoworder = pow_data->windoworder; + densdata2.e = s->e; + densdata2.nu_model = &nu_model; + } + + if (verbose) message("Calculating the shot noise."); + + /* Calculate mass terms for shot noise */ + double shot = 0.; + if (type1 == pow_type_matter || type2 == pow_type_matter || type1 == type2 || + (type1 == pow_type_gas && type2 == pow_type_pressure) || + (type2 == pow_type_gas && type1 == pow_type_pressure)) { + + /* Note that for cross-power, there is only shot noise for particles + that occur in both fields */ + struct shot_mapper_data shotdata; + shotdata.cells = s->cells_top; + shotdata.tot12 = 0; + shotdata.type1 = type1; + shotdata.type2 = type2; + shotdata.e = s->e; + shotdata.nu_model = &nu_model; + threadpool_map(tp, shotnoise_mapper, (void*)local_cells, nr_local_cells, + sizeof(int), threadpool_auto_chunk_size, (void*)&shotdata); +#ifdef WITH_MPI + /* Add up everybody's shot noise term */ + MPI_Allreduce(MPI_IN_PLACE, &shotdata.tot12, 1, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); +#endif + + /* Store shot noise */ + shot = shotdata.tot12 / volume; + if (type1 != pow_type_pressure) + shot /= meanrho; + else + shot *= conv_EV; + if (type2 != pow_type_pressure) + shot /= meanrho; + else + shot *= conv_EV; + } + + /* Gather the shared information to be used by the threads + for density conversion */ + struct conv_mapper_data convdata; + convdata.Ngrid = Ngrid; + + /* Create a lookup table for k (could also do this when initializing) */ + int* kbin = (int*)malloc((Nhalf * Nhalf + 1) * sizeof(int)); + for (int i = 0; i < Nhalf; ++i) { + for (int j = 0; j <= i; ++j) kbin[i * i + j] = i; + for (int j = i + 1; j <= 2 * i; ++j) kbin[i * i + j] = i + 1; + } + kbin[Nhalf * Nhalf] = Nhalf; + + /* Allocate arrays for power computation from FFT */ + int* modecounts = (int*)malloc((Nhalf + 1) * sizeof(int)); + double* powersum = (double*)malloc((Nhalf + 1) * sizeof(double)); + + struct pow_mapper_data powmapdata; + powmapdata.powgridft = pow_data->powgridft; + powmapdata.powgridft2 = pow_data->powgridft2; + powmapdata.Ngrid = Ngrid; + powmapdata.windoworder = pow_data->windoworder; + powmapdata.modecounts = modecounts; + powmapdata.powersum = powersum; + powmapdata.kbin = kbin; + powmapdata.jfac = jfac; + + /* Allocate arrays for combined power spectrum */ + const int kcutn = (pow_data->windoworder >= 3) ? 90 : 70; + const int kcutleft = (int)(Ngrid / 256.0 * kcutn); + const int kcutright = (int)(Ngrid / 256.0 * (double)kcutn / foldfac); + /* numtot = 76 * Nfold + 14; + * assumes a 256 grid, foldfac=6 and windoworder=2 */ + const int numtot = kcutleft + (Nfold - 1) * (kcutleft - kcutright + 1); + int numstart = 0; + + double* kcomb = (double*)malloc(numtot * sizeof(double)); + double* pcomb = (double*)malloc(numtot * sizeof(double)); + + /* Determine output file name */ + char outputfileBase[200] = ""; + char outputfileName[256] = ""; + + sprintf(outputfileBase, "power_%s", get_powtype_filename(type1)); + if (type1 != type2) { + const int length = strlen(outputfileBase); + sprintf(outputfileBase + length, "-%s", get_powtype_filename(type2)); + } + + /* Loop over foldings */ + for (int i = 0; i < Nfold; ++i) { + + if (verbose) message("Calculating the power for folding num. %d.", i); + + /* Note: implicitly assuming a cubic box here */ + densdata.fac = Ngrid / dim[0]; + densdata2.fac = Ngrid / dim[0]; + densdata.dim[0] = dim[0]; + densdata.dim[1] = dim[1]; + densdata.dim[2] = dim[2]; + densdata2.dim[0] = dim[0]; + densdata2.dim[1] = dim[1]; + densdata2.dim[2] = dim[2]; + const double kfac = 2 * M_PI / dim[0]; + + /* Empty the grid(s) */ + bzero(pow_data->powgrid, Ngrid2 * (Ngrid + 2) * sizeof(double)); + if (type1 != type2) + bzero(pow_data->powgrid2, Ngrid2 * (Ngrid + 2) * sizeof(double)); + + /* Fill out the folded grid(s) */ + threadpool_map(tp, cell_to_powgrid_mapper, (void*)local_cells, + nr_local_cells, sizeof(int), threadpool_auto_chunk_size, + (void*)&densdata); + if (type1 != type2) + threadpool_map(tp, cell_to_powgrid_mapper, (void*)local_cells, + nr_local_cells, sizeof(int), threadpool_auto_chunk_size, + (void*)&densdata2); +#ifdef WITH_MPI + /* Merge everybody's share of the grid onto rank 0 */ + if (e->nodeID == 0) + MPI_Reduce(MPI_IN_PLACE, pow_data->powgrid, Ngrid2 * (Ngrid + 2), + MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + else + MPI_Reduce(pow_data->powgrid, NULL, Ngrid2 * (Ngrid + 2), MPI_DOUBLE, + MPI_SUM, 0, MPI_COMM_WORLD); + + /* Same for the secondary grid */ + if (type1 != type2) { + + if (e->nodeID == 0) + MPI_Reduce(MPI_IN_PLACE, pow_data->powgrid2, Ngrid2 * (Ngrid + 2), + MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + else + MPI_Reduce(pow_data->powgrid2, NULL, Ngrid2 * (Ngrid + 2), MPI_DOUBLE, + MPI_SUM, 0, MPI_COMM_WORLD); + } +#endif + + /* Only rank 0 needs to perform all the remaining work */ + if (e->nodeID == 0) { + + /* Convert mass to density contrast or pressure to eV/cm^3 */ + convdata.grid = pow_data->powgrid; + convdata.invcellmean = invcellmean; + if (Ngrid < 32) { + mass_to_contrast_mapper(pow_data->powgrid, Ngrid, &convdata); + } else { + threadpool_map(tp, mass_to_contrast_mapper, pow_data->powgrid, Ngrid, + sizeof(double), threadpool_auto_chunk_size, &convdata); + } + + if (type1 != type2) { + convdata.grid = pow_data->powgrid2; + convdata.invcellmean = invcellmean2; + if (Ngrid < 32) { + mass_to_contrast_mapper(pow_data->powgrid2, Ngrid, &convdata); + } else { + threadpool_map(tp, mass_to_contrast_mapper, pow_data->powgrid2, Ngrid, + sizeof(double), threadpool_auto_chunk_size, &convdata); + } + } + + /* Perform FFT(s) */ + fftw_execute_dft_r2c(pow_data->fftplanpow, pow_data->powgrid, + pow_data->powgridft); + if (type1 != type2) + fftw_execute_dft_r2c(pow_data->fftplanpow2, pow_data->powgrid2, + pow_data->powgridft2); + + powmapdata.powgridft = pow_data->powgridft; + powmapdata.powgridft2 = pow_data->powgridft2; + + /* Zero the mode arrays */ + bzero(modecounts, (Nhalf + 1) * sizeof(int)); + bzero(powersum, (Nhalf + 1) * sizeof(double)); + + /* Calculate compensated mode contributions */ + if (Ngrid < 32) { + pow_from_grid_mapper(pow_data->powgridft, Ngrid, &powmapdata); + } else { + threadpool_map(tp, pow_from_grid_mapper, pow_data->powgridft, Ngrid, + sizeof(fftw_complex), threadpool_auto_chunk_size, + &powmapdata); + } + + /* Write this folding to the detail file */ + const double volfac = (volume / Ngrid3) / Ngrid3; + sprintf(outputfileName, "%s/%s_%04d_%d.txt", "power_spectra/foldings", + outputfileBase, snapnum, i); + FILE* outputfile = fopen(outputfileName, "w"); + + /* Determine units of power */ + char powunits[32] = ""; + if (type1 != pow_type_pressure && type2 != pow_type_pressure) + sprintf(powunits, "Mpc^3"); + else if (type1 == pow_type_pressure && type2 == pow_type_pressure) + sprintf(powunits, "Mpc^3 (eV cm^(-3))^2"); + else + sprintf(powunits, "Mpc^3 eV cm^(-3)"); + + fprintf(outputfile, "# Folding %d, all lengths/volumes are comoving\n", + i); + fprintf(outputfile, "# Shotnoise [%s]\n", powunits); + fprintf(outputfile, "%g\n", shot); + fprintf(outputfile, "# Redshift [dimensionless]\n"); + fprintf(outputfile, "%g\n", s->e->cosmology->z); + fprintf(outputfile, "# k [Mpc^(-1)] p [%s]\n", powunits); + + for (int j = 1; j <= Nhalf; ++j) { + fprintf(outputfile, "%g %g\n", j * kfac, + powersum[j] / modecounts[j] * volfac); + } + fclose(outputfile); + + /* Combine most accurate measurements from foldings */ + if (i == 0) { + + for (int j = 0; j < kcutleft; ++j) { + kcomb[j] = (j + 1) * kfac; + pcomb[j] = powersum[j + 1] / modecounts[j + 1] * volfac; + } + + numstart += kcutleft; + + } else { + + const int off = kcutright + 1; + for (int j = 0; j < (kcutleft - kcutright + 1); ++j) { + kcomb[j + numstart] = (j + off) * kfac; + pcomb[j + numstart] = + powersum[j + off] / modecounts[j + off] * volfac; + } + numstart += (kcutleft - kcutright + 1); + } + + } /* Work of rank 0 */ + + /* Fold the box */ + for (int j = 0; j < 3; ++j) dim[j] /= foldfac; + + } /* Loop over the foldings */ + + if (e->nodeID == 0) { + + /* Output attempt at combined measurement */ + sprintf(outputfileName, "%s/%s_%04d.txt", "power_spectra", outputfileBase, + snapnum); + + FILE* outputfile = fopen(outputfileName, "w"); + + /* Header and units */ + power_init_output_file(outputfile, type1, type2, us, phys_const); + + for (int j = 0; j < numtot; ++j) { + fprintf(outputfile, "%15.8f %15.8e %15.8e %15.8e\n", s->e->cosmology->z, + kcomb[j], (pcomb[j] - shot), shot); + } + fclose(outputfile); + } + + /* Done. Just clean up memory */ + if (verbose) message("Calculated the power! Cleaning up and leaving."); + + free(pcomb); + free(kcomb); + free(powersum); + free(modecounts); + free(kbin); + if (type1 != type2) { + memuse_log_allocation("fftw_grid.grid2", pow_data->powgrid2, 0, 0); + fftw_free(pow_data->powgrid2); + } + pow_data->powgrid2 = NULL; + pow_data->powgridft2 = NULL; + memuse_log_allocation("fftw_grid.grid", pow_data->powgrid, 0, 0); + fftw_free(pow_data->powgrid); + pow_data->powgrid = NULL; + pow_data->powgridft = NULL; +} + +#endif /* HAVE_FFTW */ + +/** + * @brief Initialize power spectra calculation. + * + * Reads in the power spectrum parameters, sets up FFTW + * (likely already done for PM), then sets up an FFT plan + * that can be reused every time. + * + * @param nr_threads The number of threads used. + */ +void power_init(struct power_spectrum_data* p, struct swift_params* params, + int nr_threads) { + +#ifdef HAVE_FFTW + + /* Get power spectrum parameters */ + p->Ngrid = parser_get_opt_param_int(params, "PowerSpectrum:grid_side_length", + power_data_default_grid_side_length); + p->Nfold = parser_get_param_int(params, "PowerSpectrum:num_folds"); + p->foldfac = parser_get_opt_param_int(params, "PowerSpectrum:fold_factor", + power_data_default_fold_factor); + p->windoworder = parser_get_opt_param_int( + params, "PowerSpectrum:window_order", power_data_default_window_order); + + if (p->windoworder > 3 || p->windoworder < 1) + error("Power spectrum calculation is not implemented for %dth order!", + p->windoworder); + if (p->windoworder == 1) + message("WARNING: window order is recommended to be at least 2 (CIC)."); + if (p->windoworder <= 2 && p->foldfac > 4) + message( + "WARNING: fold factor is recommended not to exceed 4 for a " + "mass assignment order of 2 (CIC) or below."); + if (p->windoworder == 3 && p->foldfac > 6) + message( + "WARNING: fold factor is recommended not to exceed 6 for a " + "mass assignment order of 3 (TSC) or below."); + + /* Make sensible choices for the k-cuts */ + const int kcutn = (p->windoworder >= 3) ? 90 : 70; + const int kcutleft = (int)(p->Ngrid / 256.0 * kcutn); + const int kcutright = (int)(p->Ngrid / 256.0 * (double)kcutn / p->foldfac); + if (kcutright < 10 || (kcutleft - kcutright) < 30) + error( + "Combination of power grid size and fold factor do not allow for " + "enough overlap between foldings!"); + + p->nr_threads = nr_threads; + +#ifdef HAVE_THREADED_FFTW + /* Initialise the thread-parallel FFTW version + (probably already done for the PM, but does not matter) */ + if (p->Ngrid >= 64) { + fftw_init_threads(); + fftw_plan_with_nthreads(nr_threads); + } +#else + message("Note that FFTW is not threaded!"); +#endif + + char** requested_spectra; + parser_get_param_string_array(params, "PowerSpectrum:requested_spectra", + &p->spectrumcount, &requested_spectra); + + p->types1 = + (enum power_type*)malloc(p->spectrumcount * sizeof(enum power_type)); + p->types2 = + (enum power_type*)malloc(p->spectrumcount * sizeof(enum power_type)); + + /* Parse which spectra are being requested */ + for (int i = 0; i < p->spectrumcount; ++i) { + + char* pstr = strtok(requested_spectra[i], "-"); + if (pstr == NULL) + error("Requested power spectra are not in the format type1-type2!"); + char type1[32]; + strcpy(type1, pstr); + + pstr = strtok(NULL, "-"); + if (pstr == NULL) + error("Requested power spectra are not in the format type1-type2!"); + char type2[32]; + strcpy(type2, pstr); + + p->types1[i] = power_spectrum_get_type(type1); + p->types2[i] = power_spectrum_get_type(type2); + } + + /* Initialize the plan only once -- much faster for FFTs run often! + * Does require us to allocate the grids, but we delete them right away. + * Plan can only be used for the same FFTW call */ + const int Ngrid = p->Ngrid; + + /* Grid is padded to allow for in-place FFT */ + p->powgrid = fftw_alloc_real(Ngrid * Ngrid * (Ngrid + 2)); + /* Pointer to grid to interpret it as complex data */ + p->powgridft = (fftw_complex*)p->powgrid; + + p->fftplanpow = fftw_plan_dft_r2c_3d(Ngrid, Ngrid, Ngrid, p->powgrid, + p->powgridft, FFTW_MEASURE); + + fftw_free(p->powgrid); + p->powgrid = NULL; + p->powgridft = NULL; + + /* Do the same for a second grid/plan to allow for cross power */ + + /* Grid is padded to allow for in-place FFT */ + p->powgrid2 = fftw_alloc_real(Ngrid * Ngrid * (Ngrid + 2)); + /* Pointer to grid to interpret it as complex data */ + p->powgridft2 = (fftw_complex*)p->powgrid2; + + p->fftplanpow2 = fftw_plan_dft_r2c_3d(Ngrid, Ngrid, Ngrid, p->powgrid2, + p->powgridft2, FFTW_MEASURE); + + fftw_free(p->powgrid2); + p->powgrid2 = NULL; + p->powgridft2 = NULL; + + /* Create directories for power spectra and foldings */ + if (engine_rank == 0) { + safe_checkdir("power_spectra", /*create=*/1); + safe_checkdir("power_spectra/foldings", /*create=*/1); + } + +#else + error("Trying to initialize the PS code without FFTW present!"); +#endif +} + +void calc_all_power_spectra(struct power_spectrum_data* pow_data, + const struct space* s, struct threadpool* tp, + const int verbose) { +#ifdef HAVE_FFTW + + const ticks tic = getticks(); + + /* Loop over all type combinations the user requested */ + for (int i = 0; i < pow_data->spectrumcount; ++i) + power_spectrum(pow_data->types1[i], pow_data->types2[i], pow_data, s, tp, + verbose); + + /* Increment the PS output counter */ + s->e->ps_output_count++; + + /* Flag we did something special this step */ + s->e->step_props |= engine_step_prop_power_spectra; + + if (verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +#else + error("Can't use the PS code without FFTW present!"); +#endif /* HAVE_FFTW */ +} + +void power_clean(struct power_spectrum_data* pow_data) { +#ifdef HAVE_FFTW + fftw_destroy_plan(pow_data->fftplanpow); + fftw_destroy_plan(pow_data->fftplanpow2); + free(pow_data->types2); + free(pow_data->types1); +#ifdef HAVE_THREADED_FFTW + // Probably already done for PM at this point + fftw_cleanup_threads(); +#endif +#else + error("Can't use the PS code without FFTW present!"); +#endif /* HAVE_FFTW */ +} + +/** + * @brief Write a power_spectrum_data struct to the given FILE as a stream of + * bytes. + * + * @param p the struct + * @param stream the file stream + */ +void power_spectrum_struct_dump(const struct power_spectrum_data* p, + FILE* stream) { +#ifdef HAVE_FFTW + restart_write_blocks((void*)p, sizeof(struct power_spectrum_data), 1, stream, + "power spectrum data", "power spectrum data"); + restart_write_blocks(p->types1, p->spectrumcount, sizeof(enum power_type), + stream, "power types 1", "power types 1"); + restart_write_blocks(p->types2, p->spectrumcount, sizeof(enum power_type), + stream, "power types 2", "power types 2"); +#endif +} + +/** + * @brief Restore a power_spectrum_data struct from the given FILE as a stream + * of bytes. + * + * @param p the struct + * @param stream the file stream + */ +void power_spectrum_struct_restore(struct power_spectrum_data* p, + FILE* stream) { +#ifdef HAVE_FFTW + restart_read_blocks((void*)p, sizeof(struct power_spectrum_data), 1, stream, + NULL, "power spectrum data"); + p->types1 = + (enum power_type*)malloc(p->spectrumcount * sizeof(enum power_type)); + restart_read_blocks(p->types1, p->spectrumcount, sizeof(enum power_type), + stream, NULL, "power types 1"); + p->types2 = + (enum power_type*)malloc(p->spectrumcount * sizeof(enum power_type)); + restart_read_blocks(p->types2, p->spectrumcount, sizeof(enum power_type), + stream, NULL, "power types 2"); + +#ifdef HAVE_THREADED_FFTW + /* Initialise the thread-parallel FFTW version + (probably already done for the PM, but does not matter) */ + if (p->Ngrid >= 64) { + fftw_init_threads(); + fftw_plan_with_nthreads(p->nr_threads); + } // if +#else + message("Note that FFTW is not threaded!"); +#endif + + /* Initialize the plan only once -- much faster for FFTs run often! + Does require us to allocate the grids, but we delete them right away */ + int Ngrid = p->Ngrid; + + /* Grid is padded to allow for in-place FFT */ + p->powgrid = fftw_alloc_real(Ngrid * Ngrid * (Ngrid + 2)); + /* Pointer to grid to interpret it as complex data */ + p->powgridft = (fftw_complex*)p->powgrid; + + p->fftplanpow = fftw_plan_dft_r2c_3d(Ngrid, Ngrid, Ngrid, p->powgrid, + p->powgridft, FFTW_MEASURE); + + fftw_free(p->powgrid); + p->powgrid = NULL; + p->powgridft = NULL; + + /* Do the same for a second grid/plan to allow for cross power */ + + /* Grid is padded to allow for in-place FFT */ + p->powgrid2 = fftw_alloc_real(Ngrid * Ngrid * (Ngrid + 2)); + /* Pointer to grid to interpret it as complex data */ + p->powgridft2 = (fftw_complex*)p->powgrid2; + + p->fftplanpow2 = fftw_plan_dft_r2c_3d(Ngrid, Ngrid, Ngrid, p->powgrid2, + p->powgridft2, FFTW_MEASURE); + + fftw_free(p->powgrid2); + p->powgrid2 = NULL; + p->powgridft2 = NULL; +#endif /* HAVE_FFTW */ +} diff --git a/src/power_spectrum.h b/src/power_spectrum.h new file mode 100644 index 0000000000000000000000000000000000000000..97b7feefde6225e590a05f0331b607838a5a24bb --- /dev/null +++ b/src/power_spectrum.h @@ -0,0 +1,113 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Marcel van Daalen (daalen@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_POWER_H +#define SWIFT_POWER_H + +/* Config parameters. */ +#include <config.h> + +/* Local headers */ +#ifdef HAVE_FFTW +#include <fftw3.h> +#endif + +/* Forward declarations */ +struct space; +struct gpart; +struct threadpool; +struct swift_params; + +/** + * @brief The different types of components we can calculate the power for. + */ +enum power_type { + pow_type_matter, + pow_type_cdm, + pow_type_gas, + pow_type_starBH, + pow_type_pressure, + pow_type_neutrino, + pow_type_neutrino_0, + pow_type_neutrino_1, + pow_type_count /* Nb. of power spect. types (always last elem. in the enum) */ +} __attribute__((packed)); + +/** + * @brief Data structure for power spectrum variables and parameters + */ +struct power_spectrum_data { + + /*! Number of grid cells on a side */ + int Ngrid; + + /*! The number of threads used by the FFTW library */ + int nr_threads; + + /*! Number of foldings to use (1 means no folding) */ + int Nfold; + + /*! Factor by which to fold along each dimension */ + int foldfac; + + /*! Number of different power spectra to calculate */ + int spectrumcount; + + /*! The order of the mass assignment window */ + int windoworder; + + /*! Array of component types to correlate on the "left" side */ + enum power_type* types1; + + /*! Array of component types to correlate on the "right" side */ + enum power_type* types2; + + /*! Pointer to the grid to be reused */ + double* powgrid; + + /*! Pointer to a second grid for cross-power spectra */ + double* powgrid2; + +#ifdef HAVE_FFTW + /*! Pointer to the grid in Fourier space */ + fftw_complex* powgridft; + + /*! Pointer to the second grid in Fourier space */ + fftw_complex* powgridft2; + + /*! The FFT plan to be reused */ + fftw_plan fftplanpow; + + /*! The FFT plan to be reused for the second grid */ + fftw_plan fftplanpow2; +#endif +}; + +void power_init(struct power_spectrum_data* p, struct swift_params* params, + int nr_threads); +void calc_all_power_spectra(struct power_spectrum_data* pow_data, + const struct space* s, struct threadpool* tp, + const int verbose); +void power_clean(struct power_spectrum_data* pow_data); + +/* Dump/restore. */ +void power_spectrum_struct_dump(const struct power_spectrum_data* p, + FILE* stream); +void power_spectrum_struct_restore(struct power_spectrum_data* p, FILE* stream); + +#endif /* SWIFT_POWER_H */ diff --git a/src/pressure_floor.h b/src/pressure_floor.h index 8880d82c68962d80fbe8e0c434e81c19200dccd3..07f46c6e26434ca0b6acd061ba772c4cc019f2ed 100644 --- a/src/pressure_floor.h +++ b/src/pressure_floor.h @@ -25,15 +25,13 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "common_io.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) || defined(SPHENIX_SPH) diff --git a/src/pressure_floor/GEAR/pressure_floor.h b/src/pressure_floor/GEAR/pressure_floor.h index 3b49eba649245a514f0d713595a95e614f8ae5f7..e5e7083139dbe4894a2c40bbd46b9fd9dd0e94b1 100644 --- a/src/pressure_floor/GEAR/pressure_floor.h +++ b/src/pressure_floor/GEAR/pressure_floor.h @@ -43,7 +43,7 @@ pressure_floor_get_comoving_pressure(const struct part* p, const float pressure, /** * @brief Properties of the pressure floor in the GEAR model. */ -struct pressure_floor_properties { +struct pressure_floor_props { /*! Jeans factor */ float n_jeans; @@ -93,9 +93,9 @@ pressure_floor_get_comoving_pressure(const struct part* p, * @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) { + struct pressure_floor_props* 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 = @@ -112,10 +112,11 @@ __attribute__((always_inline)) static INLINE void pressure_floor_init( * @param props The pressure floor properties. */ __attribute__((always_inline)) static INLINE void pressure_floor_print( - const struct pressure_floor_properties* props) { + const struct pressure_floor_props* props) { message("Pressure floor is 'GEAR' with:"); - message("Jeans factor: %g", props->n_jeans); + message("Jeans factor : %g", props->n_jeans); + message(" constant: %f", props->constants); } #ifdef HAVE_HDF5 @@ -184,4 +185,43 @@ pressure_floor_first_init_part(const struct phys_const* restrict phys_const, pressure_floor_init_part(p, xp); } +/** + * @brief Write a pressure_floor struct to the given FILE as a stream of bytes. + * + * @param pressure_floor the struct + * @param stream the file stream + */ +__attribute__((always_inline)) INLINE static void pressure_floor_struct_dump( + const struct pressure_floor_props* pressure_floor, FILE* stream) { + + restart_write_blocks((void*)pressure_floor, + sizeof(struct pressure_floor_props), 1, stream, + "pressure_floor", "pressure_floor"); + + message("dumping pressure_floor..."); + pressure_floor_print(pressure_floor); +} + +/** + * @brief Restore a pressure_floor struct from the given FILE as a stream of + * bytes. + * + * @param pressure_floor the struct + * @param stream the file stream + */ +__attribute__((always_inline)) INLINE static void pressure_floor_struct_restore( + struct pressure_floor_props* pressure_floor, FILE* stream) { + + restart_read_blocks((void*)pressure_floor, + sizeof(struct pressure_floor_props), 1, stream, NULL, + "pressure_floor"); + + message("restoring pressure_floor..."); + pressure_floor_print(pressure_floor); + + /* copy to the global variable */ + pressure_floor_props.n_jeans = pressure_floor->n_jeans; + pressure_floor_props.constants = pressure_floor->constants; +} + #endif /* SWIFT_PRESSURE_FLOOR_GEAR_H */ diff --git a/src/pressure_floor/GEAR/pressure_floor_debug.h b/src/pressure_floor/GEAR/pressure_floor_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..960a3e2e131652a8118572caaecd0cd1d9e10dee --- /dev/null +++ b/src/pressure_floor/GEAR/pressure_floor_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_DEBUG_H +#define SWIFT_PRESSURE_FLOOR_GEAR_DEBUG_H + +__attribute__((always_inline)) INLINE static void pressure_floor_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_PRESSURE_FLOOR_GEAR_DEBUG_H */ diff --git a/src/pressure_floor/GEAR/pressure_floor_iact.h b/src/pressure_floor/GEAR/pressure_floor_iact.h index 32d1d5122e083fb69754fecb7aa49ca7c195a30b..5a4f398b0937be114d1b0b314fd5250644ac0194 100644 --- a/src/pressure_floor/GEAR/pressure_floor_iact.h +++ b/src/pressure_floor/GEAR/pressure_floor_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (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 diff --git a/src/pressure_floor/none/pressure_floor.h b/src/pressure_floor/none/pressure_floor.h index 491c02a14b0cb06b1640b00fac534325b299dca6..148a8303d81cb5be766a34585bd7ebe943e87b46 100644 --- a/src/pressure_floor/none/pressure_floor.h +++ b/src/pressure_floor/none/pressure_floor.h @@ -36,7 +36,7 @@ struct unit_system; /** * @brief Properties of the pressure floor in the NONE model. */ -struct pressure_floor_properties {}; +struct pressure_floor_props {}; /** * @brief Compute the physical pressure floor of a given #part. @@ -90,7 +90,7 @@ static INLINE float pressure_floor_get_comoving_pressure( * @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, +static INLINE void pressure_floor_init(struct pressure_floor_props* props, const struct phys_const* phys_const, const struct unit_system* us, const struct hydro_props* hydro_props, @@ -102,7 +102,7 @@ static INLINE void pressure_floor_init(struct pressure_floor_properties* props, * @param props The pressure floor properties. */ static INLINE void pressure_floor_print( - const struct pressure_floor_properties* props) { + const struct pressure_floor_props* props) { message("Pressure floor is 'none'"); } @@ -176,4 +176,23 @@ pressure_floor_first_init_part(const struct phys_const* restrict phys_const, struct part* restrict p, struct xpart* restrict xp) {} +/** + * @brief Write a pressure_floor struct to the given FILE as a stream of bytes. + * + * @param pressure_floor the struct + * @param stream the file stream + */ +static INLINE void pressure_floor_struct_dump( + const struct pressure_floor_props* pressure_floor, FILE* stream) {} + +/** + * @brief Restore a pressure_floor struct from the given FILE as a stream of + * bytes. + * + * @param pressure_floor the struct + * @param stream the file stream + */ +static INLINE void pressure_floor_struct_restore( + struct pressure_floor_props* pressure_floor, FILE* stream) {} + #endif /* SWIFT_PRESSURE_FLOOR_NONE_H */ diff --git a/src/pressure_floor/none/pressure_floor_debug.h b/src/pressure_floor/none/pressure_floor_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..1f56301388808064f3525c84d200565a1a69f229 --- /dev/null +++ b/src/pressure_floor/none/pressure_floor_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_DEBUG_H +#define SWIFT_PRESSURE_FLOOR_NONE_DEBUG_H + +__attribute__((always_inline)) INLINE static void pressure_floor_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_PRESSURE_FLOOR_NONE_DEBUG_H */ diff --git a/src/pressure_floor/none/pressure_floor_iact.h b/src/pressure_floor/none/pressure_floor_iact.h index 123da3e9eaed181e7138b7b107b467992d445cd9..79c3fd10e7355794a29ed63a1197e263aa08107c 100644 --- a/src/pressure_floor/none/pressure_floor_iact.h +++ b/src/pressure_floor/none/pressure_floor_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (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 diff --git a/src/pressure_floor_debug.h b/src/pressure_floor_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..57b4e628b7fe3b0a40b18928d6abfed14032dff8 --- /dev/null +++ b/src/pressure_floor_debug.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_DEBUG_H +#define SWIFT_PRESSURE_FLOOR_DEBUG_H + +/* Config parameters. */ +#include <config.h> + +/* Import the debug routines of the right pressure floor definition */ +#if defined(PRESSURE_FLOOR_NONE) +#include "./pressure_floor/none/pressure_floor_debug.h" +#elif defined(PRESSURE_FLOOR_GEAR) +#include "./pressure_floor/GEAR/pressure_floor_debug.h" +#else +#error "Invalid choice of pressure floor structure." +#endif + +#endif /* SWIFT_PRESSURE_FLOOR_DEBUG_H */ diff --git a/src/pressure_floor_iact.h b/src/pressure_floor_iact.h index 258bf25a5a8429668c30b1d78532159ff32916b9..0a1ffc56fa77783ccebe3474820df908af90cf33 100644 --- a/src/pressure_floor_iact.h +++ b/src/pressure_floor_iact.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right pressure floor definition */ #if defined(PRESSURE_FLOOR_NONE) diff --git a/src/pressure_floor_struct.h b/src/pressure_floor_struct.h index e5538b37e03258fab45f9a2cb067f571ff7d16fd..9c3f21cffc244e5952299b4b1592abfef3bb6d74 100644 --- a/src/pressure_floor_struct.h +++ b/src/pressure_floor_struct.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right pressure floor definition */ #if defined(PRESSURE_FLOOR_NONE) diff --git a/src/profiler.c b/src/profiler.c index 2089729256ac1e4f4a4d77dce858adfbfc5a7cf8..c75caf487de44763b052e2b9fb77de049571473c 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> diff --git a/src/profiler.h b/src/profiler.h index 911d4747912d64dd46d2cf59369670ff26a92012..54261aa5bd146efcfabc3f0c9ecab74853b565fe 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -20,7 +20,7 @@ #define SWIFT_PROFILER_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "engine.h" diff --git a/src/proxy.c b/src/proxy.c index 2207b34ac8b7bca1dbff881c147d3506ec28f5e0..608ee89ea69125667630464a7f03a550291e7659 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/queue.c b/src/queue.c index 2a6c6cd8796e6fc5cdf0598d1fa6bde177db3ec4..30601667cdc5ad3fde6ff1cfc9468187df429c6c 100644 --- a/src/queue.c +++ b/src/queue.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdio.h> diff --git a/src/random.h b/src/random.h index 6d65068ea87a759caec9b37c1abe87f738a6a527..530233b68fb4b32e2961e6112c90f50c778f7e3b 100644 --- a/src/random.h +++ b/src/random.h @@ -21,7 +21,7 @@ #define SWIFT_RANDOM_H /* Code configuration */ -#include "../config.h" +#include <config.h> /* Standard header */ #include <stdint.h> @@ -29,6 +29,9 @@ #include <string.h> #include <sys/types.h> +/* Local headers */ +#include "sincos.h" + /** * @brief The categories of random number generated. * @@ -40,14 +43,6 @@ * Only change when you know what you are doing, changing * the numbers to bad values will break the random number * generator. - * In case new numbers need to be added other possible - * numbers could be: - * 126247697 - * 193877777 - * 303595777 - * 384160001 - * 406586897 - * 562448657 */ enum random_number_type { random_number_star_formation = 0LL, @@ -57,13 +52,26 @@ enum random_number_type { random_number_stellar_feedback_3 = 9762399103LL, random_number_isotropic_SNII_feedback_ray_theta = 3298327511LL, random_number_isotropic_SNII_feedback_ray_phi = 6311114273LL, + random_number_isotropic_SNIa_feedback_ray_theta = 11134675471LL, + random_number_isotropic_SNIa_feedback_ray_phi = 5165786851LL, random_number_isotropic_AGN_feedback_ray_theta = 8899891613LL, random_number_isotropic_AGN_feedback_ray_phi = 10594523341LL, random_number_stellar_enrichment = 2936881973LL, random_number_BH_feedback = 1640531371LL, random_number_BH_swallow = 4947009007LL, random_number_BH_reposition = 59969537LL, + random_number_BH_spin = 193877777LL, + random_number_BH_kick = 303595777LL, random_number_snapshot_sampling = 6561001LL, + random_number_stellar_winds = 5947309451LL, + random_number_HII_regions = 8134165677LL, + random_number_enrichment_1 = 7245742351LL, + random_number_enrichment_2 = 1156895281LL, + random_number_enrichment_3 = 2189989727LL, + random_number_mosaic_powerlaw = 406586897LL, + random_number_mosaic_schechter = 562448657LL, + random_number_mosaic_poisson = 384160001LL, + random_number_powerspectrum_split = 126247697LL, }; #ifndef __APPLE__ @@ -204,7 +212,7 @@ INLINE static double random_unit_interval(int64_t id, * @return a random number in the interval [0, 1.[. */ INLINE static double random_unit_interval_two_IDs( - int64_t id_star, int64_t id_gas, const integertime_t ti_current, + const int64_t id_star, const int64_t id_gas, const integertime_t ti_current, const enum random_number_type type) { /* We need to combine the gas and star IDs such that we do not get correlation @@ -220,27 +228,175 @@ INLINE static double random_unit_interval_two_IDs( /** * @brief Returns a pseudo-random number in the range [0, 1[. * - * We generate numbers that are always reproducible for a given stellar particle - * ID, ray index and simulation time (on the integer time-line). If more than - * one number per time-step per particle is needed, additional randomness can be - * obtained by using the type argument. + * We generate numbers that are always reproducible for a given particle + * ID, integer index and simulation time (on the integer time-line). If more + * than one number per time-step per particle is needed, additional randomness + * can be obtained by using the type argument. * - * @param id_star The ID of the (stellar) particle for which to generate a - * number. - * @param ray_idx The index of the ray used in the isotropic feedback - * @param ti_current The time (on the time-line) for which to generate a number. - * @param type The #random_number_type to generate. - * @return a random number in the interval [0, 1.[. + * @param id the particle ID. + * @param index A integer added to the randomness. + * @param ti_current The current integer time. + * @param type The type of random number (aka. #random_number_type seed). */ -INLINE static double random_unit_interval_part_ID_and_ray_idx( - int64_t id_star, const int ray_idx, const integertime_t ti_current, +INLINE static double random_unit_interval_part_ID_and_index( + const int64_t id, const int index, const integertime_t ti_current, const enum random_number_type type) { /* For better mixing, we apply a non-linear transformation y=1+x^3 */ - const long long ray_idx_3 = ray_idx * ray_idx * ray_idx; - const long long ray_idx_3_one = ray_idx_3 + 1LL; + const long long index_3 = index * index * index; + const long long index_3_one = index_3 + 1LL; + + return random_unit_interval_two_IDs(id, index_3_one, ti_current, type); +} + +/** + * @brief Return a random integer following a Poisson distribution. + * + * Uses the Knuth-Junhao method to avoid underflow when lambda is large. + * + * @param id The ID of the particle for which to generate a number. + * @param lambda The parameter of the Poisson distribution. + * @param ti_current The time (on the time-line) for which to generate a number. + * @param type The #random_number_type to generate. + */ +INLINE static int random_poisson(const int64_t id, const double lambda, + const integertime_t ti_current, + const enum random_number_type type) { + + const double step = 500.; + const double exp_step = exp(step); + + double lambda_left = lambda; + int k = 0; + double p = 1.; + + do { + + k++; + const double r = + random_unit_interval_part_ID_and_index(id, k, ti_current, type); + p *= r; + + while (p < 1. && lambda_left > 0.) { + + if (lambda_left > step) { + p *= exp_step; + lambda_left -= step; + } else { + p *= exp(lambda_left); + lambda_left = 0.; + } + }; + } while (p > 1.); + + return k - 1; +} +/** + * @brief Generates a unit vector within a cone. + * + * We draw a random unit vector around the unit vector a = (a0, a1, a2), + * such that it is uniformly distributed in solid angle from 0 opening + * angle (perfectly aligned with the vector a) to a maximum of + * opening_angle radians. + * + * @param id_bh The ID of the (BH) particle which is doing the jet feedback. + * @param id_gas The ID of the gas particle being kicked by jet feedback. + * @param ti_current The time (on the time-line) for which to generate the + * random kick direction. + * @param type The #random_number_type to generate. + * @param opening_angle Opening angle of the cone. + * @param a Reference direction that defines the cone. + * @param rand_cone_direction Return value. + */ +INLINE static void random_direction_in_cone( + const int64_t id_bh, const int64_t id_gas, const integertime_t ti_current, + const enum random_number_type type, const float opening_angle, + const float a[3], float rand_cone_direction[3]) { + + /* We want to draw a random unit vector from a cone around the unit + * vector a = (a0, a1, a2). We do this in a frame x'y'z', where the z' + * axis is aligned with the a vector, i.e. the a vector is one of its + * basis vectors. The choice of the other two axes, x' and y', is + * arbitrary. We can use any two unit vectors that are orthogonal to the + * a vector. These two vectors can be obtained using cross products. + * However, we need to first start from an initial vector that is not + * perfectly aligned with a. The choice of this vector is also arbitrary; + * we choose a vector that is perfectly aligned with the smallest + * component of the spin vector a. */ + float init_unit[3] = {0.f, 0.f, 1.f}; + + /* Find which of the x, y or z is the smallest components of a. We also + * need to take into account the possibility that two of the components + * are equal. In this case it doesn't matter which of the two we + * choose. */ + const float a0_abs = fabsf(a[0]); + const float a1_abs = fabsf(a[1]); + const float a2_abs = fabsf(a[2]); + if (((a0_abs < a1_abs) && (a0_abs < a2_abs)) || + ((a0_abs == a1_abs) && (a0_abs < a2_abs))) { + init_unit[0] = 1.f; + } else if (((a1_abs < a2_abs) && (a1_abs < a0_abs)) || + ((a1_abs == a2_abs) && (a1_abs < a0_abs))) { + init_unit[1] = 1.f; + } else if (((a2_abs < a0_abs) && (a2_abs < a1_abs)) || + ((a2_abs == a0_abs) && (a2_abs < a1_abs))) { + init_unit[2] = 1.f; + } - return random_unit_interval_two_IDs(id_star, ray_idx_3_one, ti_current, type); + /* Using this vector and the a vector, we can find the first basis + * vector x (alongside a) that will also be orthogonal to a, by using a + * cross product, such that x = init_unit cross a. This vector needs to be + * normalized to get an actual unit vector. */ + float basis_vec_x[3]; + basis_vec_x[0] = init_unit[1] * a[2] - init_unit[2] * a[1]; + basis_vec_x[1] = init_unit[2] * a[0] - init_unit[0] * a[2]; + basis_vec_x[2] = init_unit[0] * a[1] - init_unit[1] * a[0]; + const float basis_vec_x_magn = + sqrtf(basis_vec_x[0] * basis_vec_x[0] + basis_vec_x[1] * basis_vec_x[1] + + basis_vec_x[2] * basis_vec_x[2]); + basis_vec_x[0] = basis_vec_x[0] / basis_vec_x_magn; + basis_vec_x[1] = basis_vec_x[1] / basis_vec_x_magn; + basis_vec_x[2] = basis_vec_x[2] / basis_vec_x_magn; + + /* The other basis vector, y, follows as x cross a. It is already + * normalized since x and a are orthogonal. */ + const float basis_vec_y[3] = {basis_vec_x[1] * a[2] - basis_vec_x[2] * a[1], + basis_vec_x[2] * a[0] - basis_vec_x[0] * a[2], + basis_vec_x[0] * a[1] - basis_vec_x[1] * a[0]}; + + /* Draw a random cosine confined to the range [cos(opening_angle), 1] */ + const float rand_cos_theta = + 1.f - (1.f - cosf(opening_angle)) * + random_unit_interval(id_bh + id_gas, ti_current, type); + + /* Get the corresponding sine */ + const float rand_sin_theta = + sqrtf(max(0.f, (1.f - rand_cos_theta) * (1.f + rand_cos_theta))); + + /* Get a random equitorial angle from [0, 180] deg */ + const float rand_phi = + ((float)(2. * M_PI)) * + random_unit_interval((id_bh + id_gas) * (id_bh + id_gas), ti_current, + type); + + float rand_cos_phi, rand_sin_phi; + sincosf(rand_phi, &rand_sin_phi, &rand_cos_phi); + + /* We now calculate the direction of the final vector by picking a random + * direction within a cone around a, with basis_vec_x and basis_vec_y + * playing the role of x and y unit vectors, and a playing the role of z + * unit vector. In other words, in vector notation rand_cone_direction = + * sin(theta)cos(phi) * basis_vec_x + sin(theta)sin(phi) * basis_vec_y + * + cos(theta) * a . */ + rand_cone_direction[0] = rand_sin_theta * rand_cos_phi * basis_vec_x[0] + + rand_sin_theta * rand_sin_phi * basis_vec_y[0] + + rand_cos_theta * a[0], + rand_cone_direction[1] = rand_sin_theta * rand_cos_phi * basis_vec_x[1] + + rand_sin_theta * rand_sin_phi * basis_vec_y[1] + + rand_cos_theta * a[1], + rand_cone_direction[2] = rand_sin_theta * rand_cos_phi * basis_vec_x[2] + + rand_sin_theta * rand_sin_phi * basis_vec_y[2] + + rand_cos_theta * a[2]; } #endif /* SWIFT_RANDOM_H */ diff --git a/src/rays.h b/src/rays.h index bddd601c8ee2d1aaeab94c2cb3747fbbb4a18565..cf1179873e5702dbf28007557226d70dd8294bee 100644 --- a/src/rays.h +++ b/src/rays.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Evgenii Chaikin (chaikin@strw.leidenuniv.nl) + * Copyright (c) 2020 Evgenii Chaikin (chaikin@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 diff --git a/src/rays_struct.h b/src/rays_struct.h index 70bb29cbc13f4ce8ac456e41ed305bbbe576ba9c..c3dfdb4c3b556a32880d5ddddbb3ec91056a378f 100644 --- a/src/rays_struct.h +++ b/src/rays_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Evgenii Chaikin (chaikin@strw.leidenuniv.nl) + * Copyright (c) 2020 Evgenii Chaikin (chaikin@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 diff --git a/src/restart.c b/src/restart.c index 3e0b5e8c4477d359f33decf4271f21904a43406e..9b166a5670f0048761226eace6d947dda5a495de 100644 --- a/src/restart.c +++ b/src/restart.c @@ -23,7 +23,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers. */ #include "engine.h" diff --git a/src/riemann.h b/src/riemann.h index ab6d162514326778e8d6478e07c9bae2947a7c2a..dbc44bc8d25548623c35c13131fdc1dfa89450f2 100644 --- a/src/riemann.h +++ b/src/riemann.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) + * Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) * * 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,7 +20,7 @@ #define SWIFT_RIEMANN_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(RIEMANN_SOLVER_EXACT) diff --git a/src/riemann/riemann_checks.h b/src/riemann/riemann_checks.h index 2a158f2f84b3c383d75750ca9600f73cbf280400..3c3f0d6ecd82f711a20646fd0089053fa49114d0 100644 --- a/src/riemann/riemann_checks.h +++ b/src/riemann/riemann_checks.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) + * Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) * * 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 diff --git a/src/riemann/riemann_hllc.h b/src/riemann/riemann_hllc.h index a34b907ba5c6142755f2c4107d7b456393c12b42..de44983fc07977f7e1c247a5be1482c3598ecd44 100644 --- a/src/riemann/riemann_hllc.h +++ b/src/riemann/riemann_hllc.h @@ -54,8 +54,8 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux( /* STEP 0: obtain velocity in interface frame */ const float uL = WL[1] * n[0] + WL[2] * n[1] + WL[3] * n[2]; const float uR = WR[1] * n[0] + WR[2] * n[1] + WR[3] * n[2]; - const float rhoLinv = 1.0f / WL[0]; - const float rhoRinv = 1.0f / WR[0]; + const float rhoLinv = (WL[0] > 0.0f) ? 1.0f / WL[0] : 0.0f; + const float rhoRinv = (WR[0] > 0.0f) ? 1.0f / WR[0] : 0.0f; const float aL = sqrtf(hydro_gamma * WL[4] * rhoLinv); const float aR = sqrtf(hydro_gamma * WR[4] * rhoRinv); @@ -109,11 +109,11 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux( if (SL < 0.0f) { - const float starfac = SLmuL / (SL - Sstar) - 1.0f; + const float starfac = SLmuL / (SL - Sstar); const float rhoLSL = WL[0] * SL; const float SstarmuL = Sstar - uL; - const float rhoLSLstarfac = rhoLSL * starfac; - const float rhoLSLSstarmuL = rhoLSL * SstarmuL; + const float rhoLSLstarfac = rhoLSL * (starfac - 1.0f); + const float rhoLSLSstarmuL = rhoLSL * SstarmuL * starfac; totflux[0] += rhoLSLstarfac; totflux[1] += rhoLSLstarfac * WL[1] + rhoLSLSstarmuL * n[0]; @@ -138,11 +138,11 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux( if (SR > 0.0f) { - const float starfac = SRmuR / (SR - Sstar) - 1.0f; + const float starfac = SRmuR / (SR - Sstar); const float rhoRSR = WR[0] * SR; const float SstarmuR = Sstar - uR; - const float rhoRSRstarfac = rhoRSR * starfac; - const float rhoRSRSstarmuR = rhoRSR * SstarmuR; + const float rhoRSRstarfac = rhoRSR * (starfac - 1.f); + const float rhoRSRSstarmuR = rhoRSR * SstarmuR * starfac; totflux[0] += rhoRSRstarfac; totflux[1] += rhoRSRstarfac * WR[1] + rhoRSRSstarmuR * n[0]; diff --git a/src/riemann/riemann_vacuum.h b/src/riemann/riemann_vacuum.h index c1144a2557889df20ece7dea6918af804ee03232..cec5acc79ef7461e0f1e79b5757e3ca76589f337 100644 --- a/src/riemann/riemann_vacuum.h +++ b/src/riemann/riemann_vacuum.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 diff --git a/src/row_major_id.h b/src/row_major_id.h index 8629ac441a70909d189ec4026f4ffb025f481130..68655cb5421790ad10278f56cf38527c5c767db4 100644 --- a/src/row_major_id.h +++ b/src/row_major_id.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_ROW_MAJOR_ID_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include "inline.h" @@ -42,6 +42,26 @@ __attribute__((always_inline, const)) INLINE static int row_major_id_periodic( return (((i + N) % N) * N * N + ((j + N) % N) * N + ((k + N) % N)); } +/** + * @brief Returns 1D index of a 3D NxNxN array using row-major style. + * + * Wraps around in the corresponding dimension if any of the 3 indices is >= N + * or < 0. + * + * Padding is added along the x axis. + * + * @param i Index along x. + * @param j Index along y. + * @param k Index along z. + * @param N Size of the array along one axis. + */ +__attribute__((always_inline, const)) INLINE static int +row_major_id_periodic_with_padding(const int i, const int j, const int k, + const int N, const int pad) { + + return ((((i + N) % N) * N + ((j + N) % N)) * (N + pad) + ((k + N) % N)); +} + /** * @brief Returns 1D index of a FFTW-padded 3D NxNxN array using row-major * style. diff --git a/src/rt.h b/src/rt.h index 6b6fe2351fef88aeb179c0943f4210aa4aa2d51c..21482ce3618876c785e2a04926feab2dfb204151 100644 --- a/src/rt.h +++ b/src/rt.h @@ -25,27 +25,95 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right RT definition */ #if defined(RT_NONE) -#define RT_IMPLEMENTATION "none" #include "./rt/none/rt.h" #include "./rt/none/rt_iact.h" #elif defined(RT_DEBUG) -#define RT_IMPLEMENTATION "debug" #include "./rt/debug/rt.h" #include "./rt/debug/rt_iact.h" #elif defined(RT_GEAR) -#define RT_IMPLEMENTATION "GEAR M1closure" #include "./rt/GEAR/rt.h" #include "./rt/GEAR/rt_iact.h" #elif defined(RT_SPHM1RT) -#define RT_IMPLEMENTATION "SPH M1closure" #include "./rt/SPHM1RT/rt.h" #include "./rt/SPHM1RT/rt_iact.h" #else #error "Invalid choice of radiation scheme" #endif +/** + * @brief Prepare the rt *time step* quantities for a *hydro force* calculation. + * + * @param p The #part. + */ +__attribute__((always_inline)) INLINE static void rt_timestep_prepare_force( + struct part *restrict p) { + + p->rt_time_data.min_ngb_time_bin = num_time_bins + 1; +} + +/** + * @brief Gathers neighbor data for RT during *hydro* force loops. + * This is something all RT schemes should have in common, and something + * users most likely never should be touching, so it's 'hidden' in the + * top-level header file all schemes have in common. + * + * @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_rt_timebin( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) { + +#ifndef RT_NONE + /* Update the minimal time-bin */ + if (pj->rt_time_data.time_bin > 0) + pi->rt_time_data.min_ngb_time_bin = + min(pi->rt_time_data.min_ngb_time_bin, pj->rt_time_data.time_bin); + + if (pi->rt_time_data.time_bin > 0) + pj->rt_time_data.min_ngb_time_bin = + min(pj->rt_time_data.min_ngb_time_bin, pi->rt_time_data.time_bin); +#endif +} + +/** + * @brief Gathers neighbor data for RT during *hydro* force loops + * (non-symmetric). This is something all RT schemes should have in common, and + * something users most likely never should be touching, so it's 'hidden' in the + * top-level header file all schemes have in common. + * + * + * @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_rt_timebin( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, const struct part *restrict pj, const float a, + const float H) { + +#ifndef RT_NONE + /* Update the minimal time-bin */ + if (pj->rt_time_data.time_bin > 0) + pi->rt_time_data.min_ngb_time_bin = + min(pi->rt_time_data.min_ngb_time_bin, pj->rt_time_data.time_bin); +#endif +} + #endif /* SWIFT_RT_H */ diff --git a/src/rt/GEAR/rt.h b/src/rt/GEAR/rt.h index 90350dffaa6108495b894b78ff82b4cd78324b21..9d0b21ce119ec36317028f78a9d358dc9cc0207a 100644 --- a/src/rt/GEAR/rt.h +++ b/src/rt/GEAR/rt.h @@ -24,7 +24,7 @@ #include "rt_gradients.h" #include "rt_properties.h" /* #include "rt_slope_limiters_cell.h" [> skipped for now <] */ -#include "rt_stellar_emission_rate.h" +#include "rt_stellar_emission_model.h" #include "rt_thermochemistry.h" #include <float.h> @@ -34,6 +34,69 @@ * @brief Main header file for the GEAR M1 Closure radiative transfer scheme. */ +/** + * @brief Compute the photon emission rates for this stellar particle. + * This function is called every time the spart is being reset + * (during start-up and during stars ghost if spart is active) + * and assumes that the photon emission rate is an intrinsic + * stellar property, i.e. doesn't depend on the environment. + * + * @param sp star particle to work on + * @param time current system time + * @param star_age age of the star *at the end of the step* + * @param dt star time step + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param internal_units struct holding internal units + */ +__attribute__((always_inline)) INLINE static void +rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, + double star_age, double dt, + const struct rt_props* rt_props, + const struct phys_const* phys_const, + const struct unit_system* internal_units) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + sp->rt_data.debug_emission_rate_set += 1; +#endif + + /* Skip initial fake time-step */ + if (dt == 0.0l) return; + + if (time == 0.l) { + /* if function is called before the first actual step, time is still + * at zero unless specified otherwise in parameter file.*/ + /* We're going to need the star age later for more sophistiscated models, + * but for now the compiler won't let me get away with keeping this here, + * so keep it as a comment. */ + /* star_age = dt; */ + } + + /* TODO: this is for later, when we use more sophisticated models. */ + /* now get the emission rates */ + /* double star_age_begin_of_step = star_age - dt; */ + /* star_age_begin_of_step = max(0.l, star_age_begin_of_step); */ + + double emission_this_step[RT_NGROUPS]; + for (int g = 0; g < RT_NGROUPS; g++) emission_this_step[g] = 0.; + + if (rt_props->stellar_emission_model == rt_stellar_emission_model_const) { + rt_get_emission_this_step_const(emission_this_step, + rt_props->stellar_const_emission_rates, dt); + } else if (rt_props->stellar_emission_model == + rt_stellar_emission_model_IlievTest) { + rt_get_emission_this_step_IlievTest( + emission_this_step, sp->mass, dt, rt_props->photon_number_integral, + rt_props->average_photon_energy, phys_const, internal_units); + } else { + error("Unknown stellar emission rate model %d", + rt_props->stellar_emission_model); + } + + for (int g = 0; g < RT_NGROUPS; g++) + sp->rt_data.emission_this_step[g] = emission_this_step[g]; +} + /** * @brief Initialisation of the RT density loop related particle data. * Note: during initalisation (space_init), rt_reset_part and rt_init_part @@ -42,95 +105,74 @@ * @param p Particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_part( - struct part* restrict p) { - - rt_gradients_init(p); - /* the Gizmo-style slope limiting doesn't help for RT as is, - * so we're skipping it for now. */ - /* rt_slope_limit_cell_init(p); */ -} + struct part* restrict p) {} /** - * @brief Reset of the RT hydro particle data not related to the density. + * @brief Reset the RT hydro particle data not related to the hydro density. * Note: during initalisation (space_init), rt_reset_part and rt_init_part - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_part is made in space_convert_rt_quantities() after - * the zeroth time step is finished. + * are both called individually. To reset RT data needed in each RT sub-cycle, + * use rt_reset_part_each_subcycle(). * - * @param p the particle to work on + * @param p particle to work on + * @param cosmo Cosmology. */ __attribute__((always_inline)) INLINE static void rt_reset_part( - struct part* restrict p) { + struct part* restrict p, const struct cosmology* cosmo) { #ifdef SWIFT_RT_DEBUG_CHECKS /* reset this here as well as in the rt_debugging_checks_end_of_step() * routine to test task dependencies are done right */ p->rt_data.debug_iact_stars_inject = 0; - p->rt_data.debug_iact_stars_inject_prep = 0; + p->rt_data.debug_nsubcycles = 0; + p->rt_data.debug_kicked = 0; +#endif +} - /* skip this for GEAR */ - /* p->rt_data.debug_injection_check = 0; */ - p->rt_data.debug_calls_iact_gradient_interaction = 0; - p->rt_data.debug_calls_iact_transport_interaction = 0; +/** + * @brief Reset RT particle data which needs to be reset each sub-cycle. + * + * @param p the particle to work on + * @param cosmo Cosmology. + * @param dt the current particle RT time step + */ +__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle( + struct part* restrict p, const struct cosmology* cosmo, double dt) { - p->rt_data.debug_injection_done = 0; - p->rt_data.debug_gradients_done = 0; - p->rt_data.debug_transport_done = 0; - p->rt_data.debug_thermochem_done = 0; +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debugging_reset_each_subcycle(p); #endif - rt_part_reset_fluxes(p); + rt_gradients_init(p); + /* the Gizmo-style slope limiting doesn't help for RT as is, + * so we're skipping it for now. */ + /* rt_slope_limit_cell_init(p); */ + + p->rt_data.flux_dt = dt; } /** * @brief First initialisation of the RT hydro particle data. * * @param p particle to work on + * @param cosmo #cosmology data structure. + * @param rt_props RT properties struct */ __attribute__((always_inline)) INLINE static void rt_first_init_part( - struct part* restrict p) { + struct part* restrict p, const struct cosmology* cosmo, + const struct rt_props* restrict rt_props) { + /* Don't reset conserved quantities here! ICs will be overwritten */ rt_init_part(p); - rt_reset_part(p); - - for (int g = 0; g < RT_NGROUPS; g++) { - p->rt_data.density[g].energy = 0.f; - p->rt_data.density[g].flux[0] = 0.f; - p->rt_data.density[g].flux[1] = 0.f; - p->rt_data.density[g].flux[2] = 0.f; - } - + rt_reset_part(p, cosmo); rt_part_reset_mass_fluxes(p); + rt_reset_part_each_subcycle(p, cosmo, 0.); + rt_part_reset_fluxes(p); #ifdef SWIFT_RT_DEBUG_CHECKS p->rt_data.debug_radiation_absorbed_tot = 0ULL; - p->rt_data.debug_iact_stars_inject_prep_tot = 0ULL; #endif } -/** - * @brief Initialises particle quantities that can't be set - * otherwise before the zeroth step is finished. E.g. because - * they require the particle density to be known. - * - * @param p particle to work on - * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param us unit_system struct - * @param cosmo cosmology struct - */ -__attribute__((always_inline)) INLINE static void -rt_init_part_after_zeroth_step(struct part* restrict p, - const struct rt_props* rt_props, - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo) { - - /* If we're setting up ionising equilibrium initial conditions, - * then the particles need to have their densities known first. */ - rt_tchem_first_init_part(p, rt_props, phys_const, us, cosmo); -} - /** * @brief Initialisation of the RT density loop related star particle data. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart @@ -144,15 +186,28 @@ __attribute__((always_inline)) INLINE static void rt_init_spart( for (int i = 0; i < 8; i++) { sp->rt_data.octant_weights[i] = 0.f; } + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* reset this here as well as in the rt_debugging_checks_end_of_step() + * routine to test task dependencies are done right */ + sp->rt_data.debug_iact_hydro_inject_prep = 0; + sp->rt_data.debug_iact_hydro_inject = 0; + sp->rt_data.debug_emission_rate_set = 0; + + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.debug_injected_energy[g] = 0.f; + } + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] = 0.f; + } + sp->rt_data.debug_psi_sum = 0.f; +#endif } /** * @brief Reset of the RT star particle data not related to the density. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_spart is made in - * space_convert_rt_quantities_after_zeroth_step() after the zeroth time - * step is finished. + * are both called individually. * * @param sp star particle to work on */ @@ -162,21 +217,6 @@ __attribute__((always_inline)) INLINE static void rt_reset_spart( for (int g = 0; g < RT_NGROUPS; g++) { sp->rt_data.emission_this_step[g] = 0.f; } - -#ifdef SWIFT_RT_DEBUG_CHECKS - /* reset this here as well as in the rt_debugging_checks_end_of_step() - * routine to test task dependencies are done right */ - sp->rt_data.debug_iact_hydro_inject = 0; - sp->rt_data.debug_iact_hydro_inject_prep = 0; - - sp->rt_data.debug_emission_rate_set = 0; - /* skip this for GEAR */ - /* sp->rt_data.debug_injection_check = 0; */ - - for (int g = 0; g < RT_NGROUPS; g++) { - sp->rt_data.debug_injected_energy[g] = 0.f; - } -#endif } /** @@ -221,7 +261,7 @@ __attribute__((always_inline)) INLINE static void rt_part_has_no_neighbours( /** * @brief Exception handle a star part not having any neighbours in ghost task * - * @param sp The star particle to work on + * @param sp The #spart. */ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( struct spart* sp) { @@ -238,10 +278,18 @@ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( * @brief Do checks/conversions on particles on startup. * * @param p The particle to work on - * @param rtp The RT properties struct + * @param rt_props The RT properties struct + * @param hydro_props The hydro properties struct + * @param phys_const physical constants struct + * @param us unit_system struct + * @param cosmo cosmology struct */ __attribute__((always_inline)) INLINE static void rt_convert_quantities( - struct part* p, const struct rt_props* rtp) { + struct part* restrict p, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo) { /* If we're reducing the speed of light, then we may encounter * photon fluxes which are way too high than the physically @@ -252,43 +300,52 @@ __attribute__((always_inline)) INLINE static void rt_convert_quantities( * We only read in conserved quantities, so only check those. */ struct rt_part_data* rtd = &p->rt_data; - for (int g = 0; g < RT_NGROUPS; g++) { - - if (rtd->conserved[g].energy <= 0.f) { - rtd->conserved[g].energy = 0.f; - rtd->conserved[g].flux[0] = 0.f; - rtd->conserved[g].flux[1] = 0.f; - rtd->conserved[g].flux[2] = 0.f; - continue; - } + const float Vinv = 1.f / p->geometry.volume; + /* If we read in radiation energy, we read in + * total energy and store it as energy density. + * Same for fluxes. + * Correct that now. */ + for (int g = 0; g < RT_NGROUPS; g++) { + rtd->radiation[g].energy_density *= Vinv; + rtd->radiation[g].flux[0] *= Vinv; + rtd->radiation[g].flux[1] *= Vinv; + rtd->radiation[g].flux[2] *= Vinv; + + /* Additional check with possible exit for ICs */ + rt_check_unphysical_state_ICs(p, g, &rtd->radiation[g].energy_density, + rtd->radiation[g].flux, + phys_const->const_speed_light_c); /* Check for too high fluxes */ - const float flux2 = rtd->conserved[g].flux[0] * rtd->conserved[g].flux[0] + - rtd->conserved[g].flux[1] * rtd->conserved[g].flux[1] + - rtd->conserved[g].flux[2] * rtd->conserved[g].flux[2]; - const float flux_norm = sqrtf(flux2); - const float flux_max = - rt_params.reduced_speed_of_light * rtd->conserved[g].energy; - if (flux_norm > flux_max) { - const float correct = flux_max / flux_norm; - rtd->conserved[g].flux[0] *= correct; - rtd->conserved[g].flux[1] *= correct; - rtd->conserved[g].flux[2] *= correct; - } + rt_check_unphysical_state(&rtd->radiation[g].energy_density, + rtd->radiation[g].flux, /*e_old =*/0.f, + /*callloc=*/0); } -}; + + /* If we're setting up ionising equilibrium initial conditions, + * then the particles need to have their densities known first. + * So we can call the mass fractions initialization now. */ + rt_tchem_first_init_part(p, rt_props, hydro_props, phys_const, us, cosmo); +} /** * @brief Computes the next radiative transfer time step size * of a given particle (during timestep tasks) * - * @param p particle to work on - * @param rt_props the RT properties struct - * @param cosmo the cosmology + * @param p Particle to work on. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. */ __attribute__((always_inline)) INLINE static float rt_compute_timestep( - const struct part* restrict p, const struct rt_props* restrict rt_props, - const struct cosmology* restrict cosmo) { + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us) { /* just mimic the gizmo particle "size" for now */ const float psize = cosmo->a * cosmo->a * @@ -297,9 +354,16 @@ __attribute__((always_inline)) INLINE static float rt_compute_timestep( float dt = psize * rt_params.reduced_speed_of_light_inverse * rt_props->CFL_condition; - /* TODO: Add cooling time */ + if (rt_props->skip_thermochemistry) return dt; + + float dt_cool = FLT_MAX; + if (rt_props->f_limit_cooling_time > 0.f) + /* Note: cooling time may be negative if the gas is being heated */ + dt_cool = rt_props->f_limit_cooling_time * + rt_tchem_get_tchem_time(p, xp, rt_props, cosmo, hydro_props, + phys_const, us); - return dt; + return min(dt, fabsf(dt_cool)); } /** @@ -320,7 +384,10 @@ __attribute__((always_inline)) INLINE static float rt_compute_spart_timestep( /** * @brief Compute the time-step length for an RT step of a particle from given - * integer times ti_beg and ti_end + * integer times ti_beg and ti_end. This time-step length is then used to + * compute the actual time integration of the transport/force step and the + * thermochemistry. This is not used to determine the time-step length during + * the time-step tasks. * * @param ti_beg Start of the time-step (on the integer time-line). * @param ti_end End of the time-step (on the integer time-line). @@ -348,84 +415,39 @@ __attribute__((always_inline)) INLINE static double rt_part_dt( * @param p particle to work on * @param props struct #rt_props that contains global RT properties */ -__attribute__((always_inline)) INLINE static void -rt_injection_update_photon_density(struct part* restrict p, - struct rt_props* props) { - - const float V = p->geometry.volume; - const float Vinv = 1.f / V; - for (int g = 0; g < RT_NGROUPS; g++) { - p->rt_data.density[g].energy = p->rt_data.conserved[g].energy * Vinv; - p->rt_data.density[g].flux[0] = p->rt_data.conserved[g].flux[0] * Vinv; - p->rt_data.density[g].flux[1] = p->rt_data.conserved[g].flux[1] * Vinv; - p->rt_data.density[g].flux[2] = p->rt_data.conserved[g].flux[2] * Vinv; - rt_check_unphysical_density(&p->rt_data.flux[g].energy, - p->rt_data.flux[g].flux, 0); - } +__attribute__((always_inline)) INLINE static void rt_finalise_injection( + struct part* restrict p, struct rt_props* props) { #ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(p, 1, "rt_ghost1/rt_finalise_injection"); + p->rt_data.debug_injection_done += 1; #endif -} - -/** - * @brief Compute the photon emission rates for this stellar particle. - * This function is called every time the spart is being reset - * (during start-up and during stars ghost if spart is active) - * and assumes that the photon emission rate is an intrinsic - * stellar property, i.e. doesn't depend on the environment. - * - * @param sp star particle to work on - * @param time current system time - * @param star_age age of the star *at the end of the step* - * @param dt star time step - * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param internal_units struct holding internal units - */ -__attribute__((always_inline)) INLINE static void -rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, - double star_age, double dt, - const struct rt_props* rt_props, - const struct phys_const* phys_const, - const struct unit_system* internal_units) { - - /* Skip initial fake time-step */ - if (dt == 0.0l) return; - if (time == 0.l) { - /* if function is called before the first actual step, time is still - * at zero unless specified otherwise in parameter file.*/ - star_age = dt; + for (int g = 0; g < RT_NGROUPS; g++) { + rt_check_unphysical_state(&p->rt_data.radiation[g].energy_density, + p->rt_data.radiation[g].flux, /*e_old=*/0.f, + /*callloc=*/3); } - - /* now get the emission rates */ - double star_age_begin_of_step = star_age - dt; - star_age_begin_of_step = max(0.l, star_age_begin_of_step); - rt_set_stellar_emission_rate(sp, star_age_begin_of_step, star_age, rt_props, - phys_const, internal_units); } /** * @brief finishes up the gradient computation * * @param p particle to work on + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_end_gradient( - struct part* restrict p) { + struct part* restrict p, const struct cosmology* cosmo) { #ifdef SWIFT_RT_DEBUG_CHECKS - if (p->rt_data.debug_injection_done != 1) - error( - "Called finalise gradient on particle " - "where injection count = %d", - p->rt_data.debug_injection_done); + rt_debug_sequence_check(p, 2, __func__); if (p->rt_data.debug_calls_iact_gradient_interaction == 0) message( - "WARNING: Called finalise gradient on particle " + "WARNING: Called finalise gradient on particle %lld" "with iact gradient count from rt_iact = %d", - p->rt_data.debug_calls_iact_gradient_interaction); + p->id, p->rt_data.debug_calls_iact_gradient_interaction); p->rt_data.debug_gradients_done += 1; #endif @@ -438,42 +460,43 @@ __attribute__((always_inline)) INLINE static void rt_end_gradient( * * @param p particle to work on * @param dt the current time step of the particle + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_finalise_transport( - struct part* restrict p, const double dt) { + struct part* restrict p, const double dt, + const struct cosmology* restrict cosmo) { #ifdef SWIFT_RT_DEBUG_CHECKS - if (p->rt_data.debug_injection_done != 1) - error( - "Trying to do finalise_transport when " - "injection count is %d", - p->rt_data.debug_injection_done); - - if (p->rt_data.debug_gradients_done != 1) - error( - "Trying to do finalise_transport when " - "rt_finalise_gradient count is %d", - p->rt_data.debug_gradients_done); + rt_debug_sequence_check(p, 3, __func__); if (p->rt_data.debug_calls_iact_transport_interaction == 0) message( - "WARNING: Called finalise transport on particle " + "WARNING: Called finalise transport on particle %lld" "with iact transport count from rt_iact = %d", - p->rt_data.debug_calls_iact_transport_interaction); + p->id, p->rt_data.debug_calls_iact_transport_interaction); p->rt_data.debug_transport_done += 1; #endif struct rt_part_data* restrict rtd = &p->rt_data; + const float Vinv = 1.f / p->geometry.volume; for (int g = 0; g < RT_NGROUPS; g++) { - rtd->conserved[g].energy += rtd->flux[g].energy * dt; - rtd->conserved[g].flux[0] += rtd->flux[g].flux[0] * dt; - rtd->conserved[g].flux[1] += rtd->flux[g].flux[1] * dt; - rtd->conserved[g].flux[2] += rtd->flux[g].flux[2] * dt; - rt_check_unphysical_conserved(&rtd->conserved[g].energy, - rtd->conserved[g].flux, 1); + const float e_old = rtd->radiation[g].energy_density; + /* Note: in this scheme, we're updating d/dt (U * V) + sum F * A * dt = 0. + * So we'll need the division by the volume here. */ + rtd->radiation[g].energy_density += rtd->flux[g].energy * Vinv; + rtd->radiation[g].flux[0] += rtd->flux[g].flux[0] * Vinv; + rtd->radiation[g].flux[1] += rtd->flux[g].flux[1] * Vinv; + rtd->radiation[g].flux[2] += rtd->flux[g].flux[2] * Vinv; + rt_check_unphysical_state(&rtd->radiation[g].energy_density, + rtd->radiation[g].flux, e_old, /*callloc=*/4); } + + /* Reset the fluxes now that they have been applied. */ + rt_part_reset_fluxes(p); + /* Mark the particle as inactive for now. */ + rtd->flux_dt = -1.f; } /** @@ -497,16 +520,20 @@ __attribute__((always_inline)) INLINE static void rt_tchem( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const double dt) { +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(p, 4, __func__); + p->rt_data.debug_thermochem_done += 1; +#endif + /* Note: Can't pass rt_props as const struct because of grackle * accessinging its properties there */ - - rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, us, - dt); + rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, us, dt, + 0); } /** * @brief Extra operations done during the kick. This needs to be - * done before the particle mass is updated in the hydro_kick_extra + * done before the particle mass is updated in the hydro_kick_extra. * * @param p Particle to act upon. * @param dt_therm Thermal energy time-step @f$\frac{dt}{a^2}@f$. @@ -522,54 +549,90 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra( float dt_kick_corr, const struct cosmology* cosmo, const struct hydro_props* hydro_props) { - /* Update the mass fraction changes due to interparticle fluxes */ - const float current_mass = p->conserved.mass; - - const float current_mass_HI = - current_mass * p->rt_data.tchem.mass_fraction_HI; - const float current_mass_HII = - current_mass * p->rt_data.tchem.mass_fraction_HII; - const float current_mass_HeI = - current_mass * p->rt_data.tchem.mass_fraction_HeI; - const float current_mass_HeII = - current_mass * p->rt_data.tchem.mass_fraction_HeII; - const float current_mass_HeIII = - current_mass * p->rt_data.tchem.mass_fraction_HeIII; - - const float new_mass_HI = - current_mass_HI + p->rt_data.mass_flux.HI * dt_therm; - const float new_mass_HII = - current_mass_HII + p->rt_data.mass_flux.HII * dt_therm; - const float new_mass_HeI = - current_mass_HeI + p->rt_data.mass_flux.HeI * dt_therm; - const float new_mass_HeII = - current_mass_HeII + p->rt_data.mass_flux.HeII * dt_therm; - const float new_mass_HeIII = - current_mass_HeIII + p->rt_data.mass_flux.HeIII * dt_therm; - - const float new_mass_tot = new_mass_HI + new_mass_HII + new_mass_HeI + - new_mass_HeII + new_mass_HeIII; - - /* During the initial fake time step, if the mass fractions haven't been set - * up yet, we could encounter divisions by zero here, so skip that. If it - * isn't the initial time step, the error will be caught down the line by - * another call - * to rt_check_unphysical_mass_fractions() (not the one 10 lines below this) - */ - if (new_mass_tot == 0.f) return; - - const float new_mass_tot_inv = 1.f / new_mass_tot; - - p->rt_data.tchem.mass_fraction_HI = new_mass_HI * new_mass_tot_inv; - p->rt_data.tchem.mass_fraction_HII = new_mass_HII * new_mass_tot_inv; - p->rt_data.tchem.mass_fraction_HeI = new_mass_HeI * new_mass_tot_inv; - p->rt_data.tchem.mass_fraction_HeII = new_mass_HeII * new_mass_tot_inv; - p->rt_data.tchem.mass_fraction_HeIII = new_mass_HeIII * new_mass_tot_inv; +#ifdef SWIFT_RT_DEBUG_CHECKS + /* Don't account for timestep_sync backward kicks */ + if (dt_therm >= 0.f && dt_grav >= 0.f && dt_hydro >= 0.f && + dt_kick_corr >= 0.f) { - rt_check_unphysical_mass_fractions(p); + rt_debug_sequence_check(p, 0, __func__); + p->rt_data.debug_kicked += 1; + } +#endif - /* Don't update actual particle mass, that'll be done in the - * hydro_kick_extra calls */ + /* Note: We need to mimick here what Gizmo does for the mass fluxes. + * The relevant time scale is the hydro time step for the mass fluxes, + * not the RT times. We also need to prevent the kick to apply the mass + * fluxes twice, so exit if the particle time step < 0 */ + + if (p->flux.dt > 0.0f) { + /* Update the mass fraction changes due to interparticle fluxes */ + const float current_mass = p->conserved.mass; + + if (current_mass <= 0.f || p->rho <= 0.f) { + /* Deal with vacuum. Let hydro deal with actuall mass < 0, just do your + * mass fractions thing. */ + p->rt_data.tchem.mass_fraction_HI = 0.f; + p->rt_data.tchem.mass_fraction_HII = 0.f; + p->rt_data.tchem.mass_fraction_HeI = 0.f; + p->rt_data.tchem.mass_fraction_HeII = 0.f; + p->rt_data.tchem.mass_fraction_HeIII = 0.f; + rt_part_reset_mass_fluxes(p); + return; + } + + const float current_mass_HI = + current_mass * p->rt_data.tchem.mass_fraction_HI; + const float current_mass_HII = + current_mass * p->rt_data.tchem.mass_fraction_HII; + const float current_mass_HeI = + current_mass * p->rt_data.tchem.mass_fraction_HeI; + const float current_mass_HeII = + current_mass * p->rt_data.tchem.mass_fraction_HeII; + const float current_mass_HeIII = + current_mass * p->rt_data.tchem.mass_fraction_HeIII; + + /* At this point, we're exchanging (time integrated) mass fluxes, + * which in rare cases can lead to unphysical results, i.e. negative + * masses. Make sure we prevent unphysical solutions propagating by + * enforcing a minumum of zero. */ + const float new_mass_HI = + max(current_mass_HI + p->rt_data.mass_flux.HI, 0.f); + const float new_mass_HII = + max(current_mass_HII + p->rt_data.mass_flux.HII, 0.f); + const float new_mass_HeI = + max(current_mass_HeI + p->rt_data.mass_flux.HeI, 0.f); + const float new_mass_HeII = + max(current_mass_HeII + p->rt_data.mass_flux.HeII, 0.f); + const float new_mass_HeIII = + max(current_mass_HeIII + p->rt_data.mass_flux.HeIII, 0.f); + + const float new_mass_tot = new_mass_HI + new_mass_HII + new_mass_HeI + + new_mass_HeII + new_mass_HeIII; + + /* During the initial fake time step, if the mass fractions haven't been set + * up yet, we could encounter divisions by zero here, so skip that. If it + * isn't the initial time step, the error will be caught down the line by + * another call to rt_check_unphysical_mass_fractions() (not the one 10 + * lines below this) */ + if (new_mass_tot == 0.f) return; + + const float new_mass_tot_inv = 1.f / new_mass_tot; + + p->rt_data.tchem.mass_fraction_HI = new_mass_HI * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HII = new_mass_HII * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HeI = new_mass_HeI * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HeII = new_mass_HeII * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HeIII = new_mass_HeIII * new_mass_tot_inv; + + /* Reset fluxes after they have been applied, so they can be collected + * again even when particle is inactive. */ + rt_part_reset_mass_fluxes(p); + + /* Don't update actual particle mass, that'll be done in the + * hydro_kick_extra calls */ + } + + rt_check_unphysical_mass_fractions(p); } /** @@ -582,35 +645,53 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra( * @param p particle to work on **/ __attribute__((always_inline)) INLINE static void rt_prepare_force( - struct part* p) { + struct part* p) {} - rt_part_reset_mass_fluxes(p); +/** + * @brief Extra operations to be done during the drift + * + * @param p Particle to act upon. + * @param xp The extended particle data to act upon. + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void rt_predict_extra( + struct part* p, struct xpart* xp, float dt_drift) { + + float dx[3] = {xp->v_full[0] * dt_drift, xp->v_full[1] * dt_drift, + xp->v_full[2] * dt_drift}; + + for (int g = 0; g < RT_NGROUPS; g++) { + float Unew[4]; + rt_gradients_predict_drift(p, Unew, g, dx); + p->rt_data.radiation[g].energy_density = Unew[0]; + p->rt_data.radiation[g].flux[0] = Unew[1]; + p->rt_data.radiation[g].flux[1] = Unew[2]; + p->rt_data.radiation[g].flux[2] = Unew[3]; + } } /** * @brief Clean the allocated memory inside the RT properties struct. * * @param props the #rt_props. + * @param restart did we restart? */ __attribute__((always_inline)) INLINE static void rt_clean( - struct rt_props* props) { + struct rt_props* props, int restart) { - /* Clean up grackle data. This is a call to a grackle function */ - _free_chemistry_data(&props->grackle_chemistry_data, - props->grackle_chemistry_rates); + /* If we were restarting, free-ing manually will lead to + * segfaults since we didn't malloc the stuff */ + if (!restart) { + /* Clean up grackle data. This is a call to a grackle function */ + _free_chemistry_data(&props->grackle_chemistry_data, &grackle_rates); - for (int g = 0; g < RT_NGROUPS; g++) { - free(props->energy_weighted_cross_sections[g]); - free(props->number_weighted_cross_sections[g]); + for (int g = 0; g < RT_NGROUPS; g++) { + free(props->energy_weighted_cross_sections[g]); + free(props->number_weighted_cross_sections[g]); + } + free(props->energy_weighted_cross_sections); + free(props->number_weighted_cross_sections); } - free(props->energy_weighted_cross_sections); - free(props->number_weighted_cross_sections); - -#ifdef SWIFT_RT_DEBUG_CHECKS - fclose(props->conserved_energy_filep); - fclose(props->energy_density_filep); - fclose(props->star_emitted_energy_filep); -#endif } #endif /* SWIFT_RT_GEAR_H */ diff --git a/src/rt/GEAR/rt_additions.h b/src/rt/GEAR/rt_additions.h index c637b9fbc4b5b8e1e429133a6817e2db87258830..6e2e269c3cca08b23d547c992006d9b3407c8b51 100644 --- a/src/rt/GEAR/rt_additions.h +++ b/src/rt/GEAR/rt_additions.h @@ -32,6 +32,8 @@ * @param pi first interacting particle * @param pj second interacting particle * @param mass_flux the mass flux between these two particles + * @param mode 0: non-symmetric interaction, update i only. 1: symmetric + * interaction. **/ __attribute__((always_inline)) INLINE static void rt_part_update_mass_fluxes( struct part* restrict pi, struct part* restrict pj, float mass_flux, @@ -41,58 +43,49 @@ __attribute__((always_inline)) INLINE static void rt_part_update_mass_fluxes( * its own mass fractions. If it's gaining mass, it's gaining mass * according to the interacting particle's mass fractions. */ + /* get the time step for the flux exchange. This is always the smallest time + step among the two particles */ + const float mindt = + (pj->flux.dt > 0.0f) ? fminf(pi->flux.dt, pj->flux.dt) : pi->flux.dt; + + const float mdt = mass_flux * mindt; + /* Convention: negative flux for "left" particle pi */ if (mass_flux < 0.f) { /* "left" particle is gaining mass */ - pi->rt_data.mass_flux.HI -= pj->rt_data.tchem.mass_fraction_HI * mass_flux; - pi->rt_data.mass_flux.HII -= - pj->rt_data.tchem.mass_fraction_HII * mass_flux; - pi->rt_data.mass_flux.HeI -= - pj->rt_data.tchem.mass_fraction_HeI * mass_flux; - pi->rt_data.mass_flux.HeII -= - pj->rt_data.tchem.mass_fraction_HeII * mass_flux; - pi->rt_data.mass_flux.HeIII -= - pj->rt_data.tchem.mass_fraction_HeIII * mass_flux; + pi->rt_data.mass_flux.HI -= pj->rt_data.tchem.mass_fraction_HI * mdt; + pi->rt_data.mass_flux.HII -= pj->rt_data.tchem.mass_fraction_HII * mdt; + pi->rt_data.mass_flux.HeI -= pj->rt_data.tchem.mass_fraction_HeI * mdt; + pi->rt_data.mass_flux.HeII -= pj->rt_data.tchem.mass_fraction_HeII * mdt; + pi->rt_data.mass_flux.HeIII -= pj->rt_data.tchem.mass_fraction_HeIII * mdt; } else { /* "left" particle is losing mass */ - pi->rt_data.mass_flux.HI -= pi->rt_data.tchem.mass_fraction_HI * mass_flux; - pi->rt_data.mass_flux.HII -= - pi->rt_data.tchem.mass_fraction_HII * mass_flux; - pi->rt_data.mass_flux.HeI -= - pi->rt_data.tchem.mass_fraction_HeI * mass_flux; - pi->rt_data.mass_flux.HeII -= - pi->rt_data.tchem.mass_fraction_HeII * mass_flux; - pi->rt_data.mass_flux.HeIII -= - pi->rt_data.tchem.mass_fraction_HeIII * mass_flux; + pi->rt_data.mass_flux.HI -= pi->rt_data.tchem.mass_fraction_HI * mdt; + pi->rt_data.mass_flux.HII -= pi->rt_data.tchem.mass_fraction_HII * mdt; + pi->rt_data.mass_flux.HeI -= pi->rt_data.tchem.mass_fraction_HeI * mdt; + pi->rt_data.mass_flux.HeII -= pi->rt_data.tchem.mass_fraction_HeII * mdt; + pi->rt_data.mass_flux.HeIII -= pi->rt_data.tchem.mass_fraction_HeIII * mdt; } - if (mode == 1) { - /* Update right particle as well */ + if (mode == 1 || (pj->flux.dt < 0.f)) { + /* Update right particle as well, even if it's inactive */ if (mass_flux > 0.f) { /* "right" particle is gaining mass */ - pj->rt_data.mass_flux.HI += - pi->rt_data.tchem.mass_fraction_HI * mass_flux; - pj->rt_data.mass_flux.HII += - pi->rt_data.tchem.mass_fraction_HII * mass_flux; - pj->rt_data.mass_flux.HeI += - pi->rt_data.tchem.mass_fraction_HeI * mass_flux; - pj->rt_data.mass_flux.HeII += - pi->rt_data.tchem.mass_fraction_HeII * mass_flux; + pj->rt_data.mass_flux.HI += pi->rt_data.tchem.mass_fraction_HI * mdt; + pj->rt_data.mass_flux.HII += pi->rt_data.tchem.mass_fraction_HII * mdt; + pj->rt_data.mass_flux.HeI += pi->rt_data.tchem.mass_fraction_HeI * mdt; + pj->rt_data.mass_flux.HeII += pi->rt_data.tchem.mass_fraction_HeII * mdt; pj->rt_data.mass_flux.HeIII += - pi->rt_data.tchem.mass_fraction_HeIII * mass_flux; + pi->rt_data.tchem.mass_fraction_HeIII * mdt; } else { /* "right" particle is losing mass */ - pj->rt_data.mass_flux.HI += - pj->rt_data.tchem.mass_fraction_HI * mass_flux; - pj->rt_data.mass_flux.HII += - pj->rt_data.tchem.mass_fraction_HII * mass_flux; - pj->rt_data.mass_flux.HeI += - pj->rt_data.tchem.mass_fraction_HeI * mass_flux; - pj->rt_data.mass_flux.HeII += - pj->rt_data.tchem.mass_fraction_HeII * mass_flux; + pj->rt_data.mass_flux.HI += pj->rt_data.tchem.mass_fraction_HI * mdt; + pj->rt_data.mass_flux.HII += pj->rt_data.tchem.mass_fraction_HII * mdt; + pj->rt_data.mass_flux.HeI += pj->rt_data.tchem.mass_fraction_HeI * mdt; + pj->rt_data.mass_flux.HeII += pj->rt_data.tchem.mass_fraction_HeII * mdt; pj->rt_data.mass_flux.HeIII += - pj->rt_data.tchem.mass_fraction_HeIII * mass_flux; + pj->rt_data.tchem.mass_fraction_HeIII * mdt; } } } diff --git a/src/rt/GEAR/rt_blackbody.h b/src/rt/GEAR/rt_blackbody.h index be9e95d23a046449d3c0ed6fb792865ee7dfebfe..dd2c1e90170ad56e25ca6140909720f67a694278 100644 --- a/src/rt/GEAR/rt_blackbody.h +++ b/src/rt/GEAR/rt_blackbody.h @@ -24,6 +24,10 @@ * @brief Functions related to the blackbody spectrum */ +#include "inline.h" + +#include <math.h> + /** * @brief Return the specific intensity of the blackbody spectrum * @@ -31,6 +35,7 @@ * @param T temperature characterizing the spectrum * @param kB Boltzmann constant * @param h_planck Planck's constant + * @param c speed of light */ __attribute__((always_inline)) INLINE double blackbody_spectrum_intensity( const double nu, const double T, const double kB, const double h_planck, @@ -59,6 +64,7 @@ __attribute__((always_inline)) INLINE double blackbody_spectrum_intensity( * @param T temperature characterizing the spectrum * @param kB Boltzmann constant * @param h_planck Planck's constant + * @param c speed of light */ __attribute__((always_inline)) INLINE double blackbody_spectrum_energy_density( const double nu, const double T, const double kB, const double h_planck, diff --git a/src/rt/GEAR/rt_debugging.h b/src/rt/GEAR/rt_debugging.h index 75982779c623c4a182959cd5678ca29a1266771b..361992c2bf884fc2762b935cb4d6f8e60d7f3024 100644 --- a/src/rt/GEAR/rt_debugging.h +++ b/src/rt/GEAR/rt_debugging.h @@ -21,7 +21,9 @@ #ifdef SWIFT_RT_DEBUG_CHECKS +#include "active.h" #include "rt_properties.h" +#include "timeline.h" /** * @file src/rt/GEAR/rt_debugging.h @@ -29,6 +31,90 @@ * extra debugging functions. */ +/** + * @brief Check whether RT time step size is valid compared to the hydro one. + * + * @param p particle to work on + * @param dti_rt current RT integer time step + * @param dti_hydro current hydro integer time step + * @param max_nr_rt_subcycles max number of RT sub-cycles. + * @param time_base minimal time step in this run + */ +__attribute__((always_inline)) INLINE static void rt_debugging_check_timestep( + const struct part *restrict p, integertime_t *dti_rt, + const integertime_t *dti_hydro, int max_nr_rt_subcycles, double time_base) { + + if (*dti_hydro < *dti_rt) + error("part %lld has hydro time (%lld/%g) < RT time step (%lld/%g", p->id, + *dti_hydro, *dti_hydro * time_base, *dti_rt, *dti_rt * time_base); +} + +/** + * @brief This resets particle carried quantities after each subcycling + * step such that the internal checks are still consistent. + * @param p the particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_debugging_count_subcycle( + struct part *restrict p) { + p->rt_data.debug_nsubcycles += 1; +} + +/** + * @brief Check that the particle completed the correct number of subcycles. + * This is checked in every rt_reset_part, before the subcycling count is reset. + * @param p the particle to work on + * @param rt_props RT properties struct + */ +__attribute__((always_inline)) INLINE static void +rt_debugging_check_nr_subcycles(struct part *restrict p, + const struct rt_props *rt_props) { + + /* TODO: this check may fail when running with limiter/sync. */ + + /* NOTE: we need to do this check somewhere in the hydro tasks. + * (1) it needs to be done when all tasks are active and before the + * particle hydro time step is changed. + * (2) If you do it during RT tasks, it won't properly check how + * many sub-cycles you did during a single hydro task. + * (3) You can't do it during the timestep task, since between + * the hydro and the timestep we already do an RT step. */ + + /* skip initialization */ + if (p->time_bin == 0) return; + if (p->rt_time_data.time_bin == 0) + error("Got part %lld with RT time bin 0", p->id); + + timebin_t bindiff = p->time_bin - p->rt_time_data.time_bin; + + if (rt_props->debug_max_nr_subcycles <= 1) { + /* Running without subcycling. */ + if (bindiff != 0) + error("Running without subcycling but got bindiff=%d for part=%lld", + bindiff, p->id); + if (p->rt_data.debug_nsubcycles != 1) + error("Running without subcycling but got part=%lld subcycle count=%d", + p->id, p->rt_data.debug_nsubcycles); + return; + } +} + +/** + * @brief This resets particle carried quantities after each subcycling + * step such that the internal checks are still consistent. + * @param p the particle to work on + */ +__attribute__((always_inline)) INLINE static void +rt_debugging_reset_each_subcycle(struct part *restrict p) { + + p->rt_data.debug_calls_iact_gradient_interaction = 0; + p->rt_data.debug_calls_iact_transport_interaction = 0; + + p->rt_data.debug_injection_done = 0; + p->rt_data.debug_gradients_done = 0; + p->rt_data.debug_transport_done = 0; + p->rt_data.debug_thermochem_done = 0; +} + /** * @brief Debugging checks loop over all star particles after each time step */ @@ -39,26 +125,21 @@ static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data, struct spart *restrict sparts = (struct spart *)map_data; const struct engine *restrict e = (struct engine *)extra_data; - int emission_sum = 0; - int iacts_with_parts_sum = 0; + unsigned long long emission_sum_this_step = 0ULL; unsigned long long emission_sum_tot = 0ULL; - unsigned long long iacts_with_parts_sum_tot = 0ULL; - float emitted_energy[RT_NGROUPS]; - for (int g = 0; g < RT_NGROUPS; g++) emitted_energy[g] = 0.f; for (int k = 0; k < scount; k++) { struct spart *restrict sp = &sparts[k]; - emission_sum += sp->rt_data.debug_iact_hydro_inject; + emission_sum_this_step += sp->rt_data.debug_iact_hydro_inject; emission_sum_tot += sp->rt_data.debug_radiation_emitted_tot; /* Reset all values here in case stars won't be active next step */ sp->rt_data.debug_iact_hydro_inject = 0; - - iacts_with_parts_sum += sp->rt_data.debug_iact_hydro_inject_prep; - iacts_with_parts_sum_tot += sp->rt_data.debug_iact_hydro_inject_prep_tot; - /* Reset all values here in case stars won't be active next step */ sp->rt_data.debug_iact_hydro_inject_prep = 0; +#ifndef WITH_MPI + /* These checks will fail with MPI since we're not + * sending data back */ for (int g = 0; g < RT_NGROUPS; g++) { /* also check now that we actually injected the correct * amount of energy @@ -70,31 +151,38 @@ static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data, float diff = 1.f - sp->rt_data.emission_this_step[g] / sp->rt_data.debug_injected_energy[g]; - if (fabs(diff) > 1e-3) { - message( - "Incorrect injection ID %lld: " - "group %d expected %.3g got %.3g diff %.3g", - sp->id, g, sp->rt_data.emission_this_step[g], - sp->rt_data.debug_injected_energy[g], diff); + if (fabsf(diff) > 1e-4) { + /* Dividing the total into several parts and summing them up again + * while hoping to obtain the same results may lead to diappointment + * due to roundoff errors. Check that the sum of the individual + * weights and the ones we collected for the injection are close + * enough. */ + float psi_sum_now = 0.f; + for (int i = 0; i < 8; i++) + psi_sum_now += sp->rt_data.octant_weights[i]; + float diff_weights = 1.f - sp->rt_data.debug_psi_sum / psi_sum_now; + if (fabsf(diff_weights) > 1e-4) + message( + "Incorrect injection ID %lld: " + "group %d expected %.3g got %.3g diff %.3g diff_weights %.3g", + sp->id, g, sp->rt_data.emission_this_step[g], + sp->rt_data.debug_injected_energy[g], diff, diff_weights); } } - emitted_energy[g] += sp->rt_data.debug_injected_energy[g]; } +#endif for (int g = 0; g < RT_NGROUPS; g++) { sp->rt_data.debug_injected_energy[g] = 0.f; } + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] = 0.f; + } } - atomic_add(&e->rt_props->debug_radiation_emitted_this_step, emission_sum); + atomic_add(&e->rt_props->debug_radiation_emitted_this_step, + emission_sum_this_step); atomic_add(&e->rt_props->debug_radiation_emitted_tot, emission_sum_tot); - atomic_add(&e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step, - iacts_with_parts_sum); - atomic_add(&e->rt_props->debug_star_injection_prep_iacts_with_parts_tot, - iacts_with_parts_sum_tot); - for (int g = 0; g < RT_NGROUPS; g++) - atomic_add_f(&e->rt_props->debug_total_star_emitted_energy[g], - emitted_energy[g]); } /** @@ -107,48 +195,30 @@ static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data, struct part *restrict parts = (struct part *)map_data; const struct engine *restrict e = (struct engine *)extra_data; - int absorption_sum = 0; - int iacts_with_stars_sum = 0; + unsigned long long absorption_sum_this_step = 0ULL; unsigned long long absorption_sum_tot = 0ULL; - unsigned long long iacts_with_stars_sum_tot = 0ULL; float energy_sum[RT_NGROUPS]; for (int g = 0; g < RT_NGROUPS; g++) energy_sum[g] = 0.f; - float energy_density_sum[RT_NGROUPS]; - for (int g = 0; g < RT_NGROUPS; g++) energy_density_sum[g] = 0.f; for (int k = 0; k < count; k++) { struct part *restrict p = &parts[k]; - absorption_sum += p->rt_data.debug_iact_stars_inject; + absorption_sum_this_step += p->rt_data.debug_iact_stars_inject; absorption_sum_tot += p->rt_data.debug_radiation_absorbed_tot; - /* Reset all values here in case particles won't be active next step */ - p->rt_data.debug_iact_stars_inject = 0; - iacts_with_stars_sum += p->rt_data.debug_iact_stars_inject_prep; - iacts_with_stars_sum_tot += p->rt_data.debug_iact_stars_inject_prep_tot; /* Reset all values here in case particles won't be active next step */ - p->rt_data.debug_iact_stars_inject_prep = 0; + p->rt_data.debug_iact_stars_inject = 0; /* Sum up total energies for budget */ for (int g = 0; g < RT_NGROUPS; g++) { - energy_density_sum[g] += p->rt_data.density[g].energy; - energy_sum[g] += p->rt_data.conserved[g].energy; + energy_sum[g] += + p->rt_data.radiation[g].energy_density * p->geometry.volume; } } - atomic_add(&e->rt_props->debug_radiation_absorbed_this_step, absorption_sum); + atomic_add(&e->rt_props->debug_radiation_absorbed_this_step, + absorption_sum_this_step); atomic_add(&e->rt_props->debug_radiation_absorbed_tot, absorption_sum_tot); - atomic_add(&e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step, - iacts_with_stars_sum); - atomic_add(&e->rt_props->debug_part_injection_prep_iacts_with_stars_tot, - iacts_with_stars_sum_tot); - - for (int g = 0; g < RT_NGROUPS; g++) { - atomic_add_f(&(e->rt_props->debug_total_radiation_conserved_energy[g]), - energy_sum[g]); - atomic_add_f(&(e->rt_props->debug_total_radiation_energy_density[g]), - energy_density_sum[g]); - } } /** @@ -156,7 +226,7 @@ static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data, * particles and do whatever checks for this particular time step you * want done. * - * @param s The #space. + * @param e The #engine. * @param verbose Are we talkative? */ __attribute__((always_inline)) INLINE static void @@ -167,22 +237,13 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) { const ticks tic = getticks(); - /* reset values before the particle loops */ - e->rt_props->debug_radiation_emitted_this_step = 0; - e->rt_props->debug_radiation_absorbed_this_step = 0; - e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step = 0; - e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step = 0; - /* reset total counts as well. We track the totals since the beginning + /* reset values before the particle loops. + * reset total counts as well. We track the totals since the beginning * of time in particles individually. */ - e->rt_props->debug_radiation_emitted_tot = 0LL; - e->rt_props->debug_radiation_absorbed_tot = 0LL; - e->rt_props->debug_star_injection_prep_iacts_with_parts_tot = 0LL; - e->rt_props->debug_part_injection_prep_iacts_with_stars_tot = 0LL; - for (int g = 0; g < RT_NGROUPS; g++) { - e->rt_props->debug_total_radiation_energy_density[g] = 0.f; - e->rt_props->debug_total_radiation_conserved_energy[g] = 0.f; - e->rt_props->debug_total_star_emitted_energy[g] = 0.f; - } + e->rt_props->debug_radiation_emitted_this_step = 0ULL; + e->rt_props->debug_radiation_absorbed_this_step = 0ULL; + e->rt_props->debug_radiation_emitted_tot = 0ULL; + e->rt_props->debug_radiation_absorbed_tot = 0ULL; /* hydro particle loop */ if (s->nr_parts > 0) @@ -196,142 +257,106 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) { s->sparts, s->nr_sparts, sizeof(struct spart), threadpool_auto_chunk_size, /*extra_data=*/e); - /* cell loop */ - /* for (int i = 0; i < s->nr_cells; ++i){ */ - /* if (s->cells_top[i].nodeID == engine_rank) { */ - /* rt_debugging_check_cell(&s->cells_top[i]); */ - /* } */ - /* } */ - - /* message("This step: %12d %12d %12d %12d", */ - /* e->rt_props->debug_radiation_emitted_this_step, */ - /* e->rt_props->debug_radiation_absorbed_this_step, */ - /* e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step, - */ - /* e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step - */ - /* ); */ - /* message("Over lifetime: %12lld %12lld %12lld %12lld", */ - /* e->rt_props->debug_radiation_emitted_tot, */ - /* e->rt_props->debug_radiation_absorbed_tot, */ - /* e->rt_props->debug_star_injection_prep_iacts_with_parts_tot, */ - /* e->rt_props->debug_part_injection_prep_iacts_with_stars_tot */ - /* ); */ +#ifdef WITH_MPI + /* Since we aren't sending data back, none of these checks will + * pass a run over MPI. Make sure you run the threadpool functions + * first though so certain variables can get reset properly. */ + return; +#endif /* Have we accidentally invented or deleted some radiation somewhere? */ + if ((e->rt_props->debug_radiation_emitted_this_step != e->rt_props->debug_radiation_absorbed_this_step) || (e->rt_props->debug_radiation_emitted_tot != e->rt_props->debug_radiation_absorbed_tot)) error( "Emitted and absorbed radiation vary.\n" - " This step: star emission %12d; gas absorption %12d\n" + " This step: star emission %12lld; gas absorption %12lld\n" "Since start: star emission %12lld; gas absorption %12lld", e->rt_props->debug_radiation_emitted_this_step, e->rt_props->debug_radiation_absorbed_this_step, e->rt_props->debug_radiation_emitted_tot, e->rt_props->debug_radiation_absorbed_tot); - if ((e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step != - e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step) || - (e->rt_props->debug_part_injection_prep_iacts_with_stars_tot != - e->rt_props->debug_star_injection_prep_iacts_with_parts_tot)) - error( - "Injection prep counts parts vs stars disagree.\n" - " This step: star iacts: %12d; gas iacts: %12d\n" - "Since start: star iacts: %12lld; gas iacts: %12lld", - e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step, - e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step, - e->rt_props->debug_star_injection_prep_iacts_with_parts_tot, - e->rt_props->debug_part_injection_prep_iacts_with_stars_tot); - - if ((e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step != - e->rt_props->debug_radiation_emitted_this_step) || - (e->rt_props->debug_part_injection_prep_iacts_with_stars_tot != - e->rt_props->debug_radiation_emitted_tot)) - error( - "Injection prep iact counts vs actual iact counts disagree.\n" - " This step: prep iacts: %12d; inject iacts: %12d\n" - "Since start: prep iacts: %12lld; inject iacts: %12lld", - e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step, - e->rt_props->debug_radiation_emitted_this_step, - e->rt_props->debug_part_injection_prep_iacts_with_stars_tot, - e->rt_props->debug_radiation_emitted_tot); - - /* Write down energy budget for this timestep. */ - if (e->step > 1) { - fprintf(e->rt_props->conserved_energy_filep, "\n"); - } else { - fprintf(e->rt_props->conserved_energy_filep, "# nstars: %lld\n", - e->total_nr_sparts); - } - fprintf(e->rt_props->conserved_energy_filep, "%6d %12.6e ", e->step, e->time); - for (int g = 0; g < RT_NGROUPS; g++) - fprintf(e->rt_props->conserved_energy_filep, "%12.6e ", - e->rt_props->debug_total_radiation_conserved_energy[g]); - - if (e->step > 1) { - fprintf(e->rt_props->energy_density_filep, "\n"); - } else { - fprintf(e->rt_props->energy_density_filep, "# nstars: %lld\n", - e->total_nr_sparts); - } - fprintf(e->rt_props->energy_density_filep, "%6d %12.6e ", e->step, e->time); - for (int g = 0; g < RT_NGROUPS; g++) - fprintf(e->rt_props->energy_density_filep, "%12.6e ", - e->rt_props->debug_total_radiation_energy_density[g]); - - if (e->step > 1) { - fprintf(e->rt_props->star_emitted_energy_filep, "\n"); - } else { - fprintf(e->rt_props->star_emitted_energy_filep, "# nstars: %lld\n", - e->total_nr_sparts); - } - fprintf(e->rt_props->star_emitted_energy_filep, "%6d %12.6e ", e->step, - e->time); - for (int g = 0; g < RT_NGROUPS; g++) - fprintf(e->rt_props->star_emitted_energy_filep, "%12.6e ", - e->rt_props->debug_total_star_emitted_energy[g]); - if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); } /** - * @brief This function is intended for debugging purposes only. It is called - * during the self injection tasks, (regardless whether the particle actually - * has neighbours to interact with) and intended to mark star or gas particles - * to have been called during the step so further checks can be performed - * further down the task system. + * @brief Perform a series of consistency and sanity checks. + * + * @param p particle to check + * @param loc location where this is called from. This determines which checks + * will be done: + * + * 0: during kicks/after drifts. + * 1: during rt_ghost1/finalise_injection / after kicks. + * 2: during gradients / after injection. + * 3: during transport / after gradients. + * 4: during thermochem / after transport. + * 5: after thermochem. * - * @param p Hydro particle. + * @param function_name: Function name (or message) you want printed on error. */ -__attribute__((always_inline)) INLINE static void -rt_debugging_check_injection_part(struct part *restrict p, - struct rt_props *props) { +__attribute__((always_inline)) INLINE static void rt_debug_sequence_check( + struct part *restrict p, int loc, const char *function_name) { + + /* Note: Checking whether a particle has been drifted at all is not + * compatible with subcycling. There is no reliable point where to + * reset the counters and have sensible results. */ + + if (loc > 0) { + /* Are kicks done? */ + if (p->rt_data.debug_nsubcycles == 0) { + if (p->rt_data.debug_kicked != 1) + error( + "called %s on particle %lld with wrong kick count=%d (expected " + "1) cycle=%d", + function_name, p->id, p->rt_data.debug_kicked, + p->rt_data.debug_nsubcycles); + } else if (p->rt_data.debug_nsubcycles > 0) { + /* This covers case 1, 2, 3, 4, 5 */ + if (p->rt_data.debug_kicked != 2) + error( + "called %s on particle %lld with wrong kick count=%d (expected 2) " + "cycle=%d", + function_name, p->id, p->rt_data.debug_kicked, + p->rt_data.debug_nsubcycles); + } else { + error("Got negative subcycle???"); + } + } - /* skip this for GEAR */ - /* if (props->do_all_parts_have_stars_checks) */ - /* p->rt_data.debug_injection_check += 1; */ -} + if (loc > 1) { + /* is injection done? */ + if (p->rt_data.debug_injection_done != 1) + error("called %s on part %lld when finalise injection count is %d ID", + function_name, p->id, p->rt_data.debug_injection_done); + } -/** - * @brief This function is intended for debugging purposes only. It is called - * during the self injection tasks, (regardless whether the particle actually - * has neighbours to interact with) and intended to mark star or gas particles - * to have been called during the step so further checks can be performed - * further down the task system. - * - * @param s Star particle. - */ -__attribute__((always_inline)) INLINE static void -rt_debugging_check_injection_spart(struct spart *restrict s, - struct rt_props *props) { + if (loc > 2) { + /* are gradients done? */ + if (p->rt_data.debug_gradients_done != 1) + error("called %s on part %lld when gradients_done count is %d", + function_name, p->id, p->rt_data.debug_gradients_done); + } - /* skip this for GEAR */ - /* if (props->do_all_parts_have_stars_checks) */ - /* s->rt_data.debug_injection_check += 1; */ + if (loc > 3) { + /* is transport done? */ + if (p->rt_data.debug_transport_done != 1) + error("called %s on part %lld when transport_done != 1: %d", + function_name, p->id, p->rt_data.debug_transport_done); + } + + if (loc > 4) { + /* is thermochemistry done? */ + if (p->rt_data.debug_thermochem_done != 1) + error("called %s on part %lld with thermochem_done count=%d", + function_name, p->id, p->rt_data.debug_thermochem_done); + } } + #endif /* SWIFT_RT_DEBUG_CHECKS */ #endif /* SWIFT_RT_DEBUGGING_GEAR_H */ diff --git a/src/rt/GEAR/rt_flux.h b/src/rt/GEAR/rt_flux.h index 4c578670812aef9fdd4ad513da950450d1f274f0..f16ac13b0f28af05515eca2aeb68479e9edc58d9 100644 --- a/src/rt/GEAR/rt_flux.h +++ b/src/rt/GEAR/rt_flux.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 @@ -58,18 +58,18 @@ __attribute__((always_inline)) INLINE static void rt_part_reset_fluxes( * state UR along the direction of the unit vector n_unit * through a surface of size Anorm. * - * @param UL left state - * @param UR right state + * @param UL left state (energy density, fluxes) + * @param UR right state (energy density, fluxes) * @param n_unit unit vector of the direction of the surface * @param Anorm size of the surface through which the flux goes - * @param fluxes the resulting flux + * @param fluxes (return) the resulting flux */ __attribute__((always_inline)) INLINE static void rt_compute_flux( float UL[4], float UR[4], const float n_unit[3], const float Anorm, float fluxes[4]) { - /* Unphysical density check not necessary here. - * It's done in gradients predict as well. */ + /* Unphysical check not necessary here. + * It's done in gradients_predict as well. */ const float FLnorm = sqrtf(UL[1] * UL[1] + UL[2] * UL[2] + UL[3] * UL[3]); const float FRnorm = sqrtf(UR[1] * UR[1] + UR[2] * UR[2] + UR[3] * UR[3]); @@ -86,7 +86,7 @@ __attribute__((always_inline)) INLINE static void rt_compute_flux( #endif rt_riemann_solve_for_flux(UL, UR, FLnorm, FRnorm, hyperFluxL, hyperFluxR, - fluxes, n_unit); + n_unit, fluxes); /* get the actual flux */ fluxes[0] *= Anorm; diff --git a/src/rt/GEAR/rt_getters.h b/src/rt/GEAR/rt_getters.h index 5c04f4755c6b05812535863938148ee45381f284..dc9e3e6b9cb4a5337e357aad05cb4da1976cb4bb 100644 --- a/src/rt/GEAR/rt_getters.h +++ b/src/rt/GEAR/rt_getters.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 @@ -29,39 +29,56 @@ */ /** - * @brief Get a 4-element state vector Q containing the density photon - * quantities for a specific photon group + * @brief Get the radiation energy densities of a particle. + * + * @param p Particle. + * @param E (return) Pointer to the array in which the result needs to be stored + */ +__attribute__((always_inline)) INLINE static void +rt_part_get_radiation_energy_density(const struct part *restrict p, + float E[RT_NGROUPS]) { + + for (int g = 0; g < RT_NGROUPS; g++) { + E[g] = p->rt_data.radiation[g].energy_density; + } +} + +/** + * @brief Get a 4-element state vector U containing the radiation energy + * density and fluxes for a specific photon group. * * @param p Particle. * @param group Index of photon group * @param U Pointer to the array in which the result needs to be stored */ -__attribute__((always_inline)) INLINE static void rt_part_get_density_vector( - const struct part *restrict p, int group, float U[4]) { - - U[0] = p->rt_data.density[group].energy; - U[1] = p->rt_data.density[group].flux[0]; - U[2] = p->rt_data.density[group].flux[1]; - U[3] = p->rt_data.density[group].flux[2]; +__attribute__((always_inline)) INLINE static void +rt_part_get_radiation_state_vector(const struct part *restrict p, int group, + float U[4]) { + + U[0] = p->rt_data.radiation[group].energy_density; + U[1] = p->rt_data.radiation[group].flux[0]; + U[2] = p->rt_data.radiation[group].flux[1]; + U[3] = p->rt_data.radiation[group].flux[2]; } /** - * @brief Get the gradients of energy and fluxes for a given photon group + * @brief Get the gradients of energy density and fluxes for a given photon + * group * * @param p Particle. * @param group Index of photon group - * @param dE Array to write energy gradient into - * @param dFx Array to write flux x component gradient into - * @param dFy Array to write flux y component gradient into - * @param dFz Array to write flux z component gradient into + * @param dE (return) Array to write energy density gradient into + * @param dFx (return) Array to write flux x component gradient into + * @param dFy (return) Array to write flux y component gradient into + * @param dFz (return) Array to write flux z component gradient into */ __attribute__((always_inline)) INLINE static void rt_part_get_gradients( const struct part *restrict p, int group, float dE[3], float dFx[3], float dFy[3], float dFz[3]) { - dE[0] = p->rt_data.gradient[group].energy[0]; - dE[1] = p->rt_data.gradient[group].energy[1]; - dE[2] = p->rt_data.gradient[group].energy[2]; + dE[0] = p->rt_data.gradient[group].energy_density[0]; + dE[1] = p->rt_data.gradient[group].energy_density[1]; + dE[2] = p->rt_data.gradient[group].energy_density[2]; dFx[0] = p->rt_data.gradient[group].flux[0][0]; dFx[1] = p->rt_data.gradient[group].flux[0][1]; @@ -77,12 +94,12 @@ __attribute__((always_inline)) INLINE static void rt_part_get_gradients( } /** - * @brief compute the pressure tensor for a given state U + * @brief compute the pressure tensor for a given radiation state U * - * @param U the state (radiation energy, radiation energy flux) to use + * @param U the state (radiation energy density, radiation flux) to use * @param Fnorm the norm of the radiation flux - * @param pressure_tensor 3x3 array to write resulting Eddington pressure tensor - * into + * @param pressure_tensor (return) 3x3 array to write resulting Eddington + * pressure tensor into */ __attribute__((always_inline)) INLINE static void rt_get_pressure_tensor( const float U[4], const float Fnorm, float pressure_tensor[3][3]) { @@ -104,10 +121,12 @@ __attribute__((always_inline)) INLINE static void rt_get_pressure_tensor( return; } - const float f = rt_params.reduced_speed_of_light_inverse * Fnorm / U[0]; - /* f^2 mustn't be > 1. This may happen since we use the reduced speed of - * light. */ - const float f2 = min(1.f, f * f); + /* f mustn't be > 1. This may happen because of the reduced speed of light. + * Energy density U[0] is nonzero at this point, or this function wouldn't + * have been called. */ + const float f = + min(1.f, rt_params.reduced_speed_of_light_inverse * Fnorm / U[0]); + const float f2 = f * f; const float rootterm = 4.f - 3.f * f2; const float chi = (3.f + 4.f * f2) / (5.f + 2.f * sqrtf(rootterm)); @@ -150,9 +169,10 @@ __attribute__((always_inline)) INLINE static void rt_get_pressure_tensor( * @brief compute the flux of the hyperbolic conservation law for a given * state U * - * @param U the state (radiation energy, radiation energy flux) to use + * @param U the state (radiation energy density, radiation flux) to use * @param Fnorm the norm of the radiation flux - * @param hypflux the resulting flux F(U) of the hyperbolic conservation law + * @param hypflux (return) resulting flux F(U) of the hyperbolic conservation + * law */ __attribute__((always_inline)) INLINE static void rt_get_hyperbolic_flux( const float U[4], const float Fnorm, float hypflux[4][3]) { diff --git a/src/rt/GEAR/rt_grackle_utils.h b/src/rt/GEAR/rt_grackle_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..815e36ca8777bacd54d83c9b71721d3bba08633f --- /dev/null +++ b/src/rt/GEAR/rt_grackle_utils.h @@ -0,0 +1,441 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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_RT_GRACKLE_UTILS_H +#define SWIFT_RT_GRACKLE_UTILS_H + +/* need hydro gamma */ +#include "hydro.h" + +#include <grackle.h> + +/* need to rework (and check) code if changed */ +#define FIELD_SIZE 1 + +/** + * @file src/rt/GEAR/rt_grackle_utils.h + * @brief Utility and helper functions related to using grackle. + */ + +/** + * @brief initialize grackle during rt_props_init + * + * @param grackle_units grackle units struct to fill up correctly. + * @param grackle_chemistry_dat grackle chemistry data struct to fill up + *correctly. + * @param hydrogen_mass_fraction global hydrogen mass fraction. + * @param grackle_verb run grackle in verbose mode? + * @param case_B_recombination use grackle with case B recombination? + * @param us #unit_system struct + **/ +__attribute__((always_inline)) INLINE static void rt_init_grackle( + code_units *grackle_units, chemistry_data *grackle_chemistry_data, + float hydrogen_mass_fraction, const int grackle_verb, + const int case_B_recombination, const struct unit_system *us) { + + grackle_verbose = grackle_verb; + + /* Initialize units */ + /* ---------------- */ + /* we assume all quantities to be physical, not comoving */ + grackle_units->a_units = 1.0; + grackle_units->a_value = 1.0; + grackle_units->comoving_coordinates = 0; + grackle_units->density_units = + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + grackle_units->length_units = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + grackle_units->time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + /* Set velocity units */ + set_velocity_units(grackle_units); + + /* Chemistry Parameters */ + /* -------------------- */ + /* More details on https://grackle.readthedocs.io/en/latest/Parameters.html */ + + /* TODO: cleanup later with all other grackle stuff */ + /* rtp->grackle_chemistry_data = _set_default_chemistry_parameters(); */ + if (set_default_chemistry_parameters(grackle_chemistry_data) == 0) { + error("Error in set_default_chemistry_parameters."); + } + /* chemistry on */ + grackle_chemistry_data->use_grackle = 1; + /* cooling on */ + /* NOTE: without cooling on, it also won't heat... */ + grackle_chemistry_data->with_radiative_cooling = 1; + /* 6 species atomic H and He-> */ + grackle_chemistry_data->primordial_chemistry = 1; + /* dust processes */ + grackle_chemistry_data->dust_chemistry = 0; + /* H2 formation on dust */ + grackle_chemistry_data->h2_on_dust = 0; + /* metal cooling (uses Cloudy) off (for now) */ + grackle_chemistry_data->metal_cooling = 0; + /* no cooling below CMB temperature */ + grackle_chemistry_data->cmb_temperature_floor = 1; + /* UV background off */ + grackle_chemistry_data->UVbackground = 0; + /* data file - currently not used-> */ + grackle_chemistry_data->grackle_data_file = ""; + /* adiabatic index */ + grackle_chemistry_data->Gamma = hydro_gamma; + /* we'll provide grackle with ionization and heating rates from RT */ + grackle_chemistry_data->use_radiative_transfer = 1; + /* fraction by mass of Hydrogen in the metal-free portion of the gas */ + grackle_chemistry_data->HydrogenFractionByMass = hydrogen_mass_fraction; + /* Use case B recombination? (On-the-spot approximation) */ + grackle_chemistry_data->CaseBRecombination = case_B_recombination; + + /* TODO: cleanup later with all other grackle stuff */ + /* Initialize the chemistry_data_storage object to be + * able to use local functions */ + /* chemistry_data_storage* chem_data_storage = */ + /* malloc(sizeof(chemistry_data_storage)); */ + /* rtp->grackle_chemistry_rates = chem_data_storage; */ + /* if (!_initialize_chemistry_data(&rtp->grackle_chemistry_data, */ + /* rtp->grackle_chemistry_rates, */ + /* &rtp->grackle_units)) { */ + if (initialize_chemistry_data(grackle_units) == 0) { + error("Error in initialize_chemistry_data"); + } +} + +/** + * @brief fill out a grackle field struct with the relevant (gas) data from a + *particle + * + * @param grackle_fields (return) grackle field to copy into + * @param density array of particle density + * @param internal_energy array of particle internal_energy + * @param species_densities array of species densities of particle (HI, HII, + *HeI, HeII, HeIII, e-) + * @param iact_rates array of interaction rates (heating, 3 ioniziation, H2 + *dissociation) + * + **/ +__attribute__((always_inline)) INLINE static void +rt_get_grackle_particle_fields(grackle_field_data *grackle_fields, + gr_float density, gr_float internal_energy, + gr_float species_densities[6], + gr_float iact_rates[5]) { + + int *dimension = malloc(3 * sizeof(int)); + int *start = malloc(3 * sizeof(int)); + int *end = malloc(3 * sizeof(int)); + + dimension[0] = FIELD_SIZE; + dimension[1] = 0; + dimension[2] = 0; + start[0] = 0; + start[1] = 0; + start[2] = 0; + end[0] = FIELD_SIZE - 1; + end[1] = 0; + end[2] = 0; + + grackle_fields->grid_dx = 0.; + grackle_fields->grid_rank = 3; + grackle_fields->grid_dimension = dimension; + grackle_fields->grid_start = start; + grackle_fields->grid_end = end; + + /* Set initial quantities */ + grackle_fields->density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->internal_energy = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->x_velocity = NULL; + grackle_fields->y_velocity = NULL; + grackle_fields->z_velocity = NULL; + /* for primordial_chemistry >= 1 */ + grackle_fields->HI_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HII_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HeI_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HeII_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HeIII_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->e_density = malloc(FIELD_SIZE * sizeof(gr_float)); + /* for primordial_chemistry >= 2 */ + grackle_fields->HM_density = NULL; + grackle_fields->H2I_density = NULL; + grackle_fields->H2II_density = NULL; + /* for primordial_chemistry >= 3 */ + grackle_fields->DI_density = NULL; + grackle_fields->DII_density = NULL; + grackle_fields->HDI_density = NULL; + /* for metal_cooling = 1 */ + grackle_fields->metal_density = NULL; + + /* volumetric heating rate (provide in units [erg s^-1 cm^-3]) */ + grackle_fields->volumetric_heating_rate = NULL; + /* specific heating rate (provide in units [egs s^-1 g^-1] */ + grackle_fields->specific_heating_rate = NULL; + + /* radiative transfer ionization / dissociation rate fields (provide in units + * [1/s]) */ + grackle_fields->RT_HI_ionization_rate = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->RT_HeI_ionization_rate = + malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->RT_HeII_ionization_rate = + malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->RT_H2_dissociation_rate = + malloc(FIELD_SIZE * sizeof(gr_float)); + /* radiative transfer heating rate field + * (provide in units [erg s^-1 cm^-3] / nHI in cgs) */ + grackle_fields->RT_heating_rate = malloc(FIELD_SIZE * sizeof(gr_float)); + + for (int i = 0; i < FIELD_SIZE; i++) { + + grackle_fields->density[i] = density; + grackle_fields->internal_energy[i] = internal_energy; + + grackle_fields->HI_density[i] = species_densities[0]; + grackle_fields->HII_density[i] = species_densities[1]; + grackle_fields->HeI_density[i] = species_densities[2]; + grackle_fields->HeII_density[i] = species_densities[3]; + grackle_fields->HeIII_density[i] = species_densities[4]; + /* e_density = electron density*mh/me = n_e * m_h */ + grackle_fields->e_density[i] = species_densities[5]; + + /* grackle_fields->HM_density[i] = species_densities[6]; */ + /* grackle_fields->H2I_density[i] = species_densities[7]; */ + /* grackle_fields->H2II_density[i] = species_densities[8]; */ + /* grackle_fields->DI_density[i] = species_densities[9]; */ + /* grackle_fields->DII_density[i] = species_densities[10]; */ + /* grackle_fields->HDI_density[i] = species_densities[11]; */ + + /* grackle_fields->metal_density[i] = 0.0; */ + /* solar metallicity */ + /* grackle_chemistry_data.SolarMetalFractionByMass * + * grackle_fields->density[i]; */ + + /* grackle_fields->x_velocity[i] = 0.0; */ + /* grackle_fields->y_velocity[i] = 0.0; */ + /* grackle_fields->z_velocity[i] = 0.0; */ + + /* grackle_fields->volumetric_heating_rate[i] = 0.0; */ + /* grackle_fields->specific_heating_rate[i] = 0.0; */ + + grackle_fields->RT_heating_rate[i] = iact_rates[0]; + grackle_fields->RT_HI_ionization_rate[i] = iact_rates[1]; + grackle_fields->RT_HeI_ionization_rate[i] = iact_rates[2]; + grackle_fields->RT_HeII_ionization_rate[i] = iact_rates[3]; + grackle_fields->RT_H2_dissociation_rate[i] = iact_rates[4]; + } +} + +/** + * @brief free arrays allocated in grackle_fields. + * + * @param grackle_fields grackle fields to clean up + * + **/ +__attribute__((always_inline)) INLINE static void rt_clean_grackle_fields( + grackle_field_data *grackle_fields) { + + free(grackle_fields->grid_dimension); + free(grackle_fields->grid_start); + free(grackle_fields->grid_end); + + free(grackle_fields->density); + free(grackle_fields->internal_energy); + /* free(grackle_fields->x_velocity); */ + /* free(grackle_fields->y_velocity); */ + /* free(grackle_fields->z_velocity); */ + free(grackle_fields->HI_density); + free(grackle_fields->HII_density); + free(grackle_fields->HeI_density); + free(grackle_fields->HeII_density); + free(grackle_fields->HeIII_density); + free(grackle_fields->e_density); + /* free(grackle_fields->HM_density); */ + /* free(grackle_fields->H2I_density); */ + /* free(grackle_fields->H2II_density); */ + /* free(grackle_fields->DI_density); */ + /* free(grackle_fields->DII_density); */ + /* free(grackle_fields->HDI_density); */ + /* free(grackle_fields->metal_density); */ + /* free(grackle_fields->volumetric_heating_rate); */ + /* free(grackle_fields->specific_heating_rate); */ + + free(grackle_fields->RT_HI_ionization_rate); + free(grackle_fields->RT_HeI_ionization_rate); + free(grackle_fields->RT_HeII_ionization_rate); + free(grackle_fields->RT_H2_dissociation_rate); + free(grackle_fields->RT_heating_rate); +} + +/** + * @brief Write out all available grackle field data for a given index + * and setup to a file. + * This function is intended for debugging. + * + * @param fp FILE pointer to write into. + * @param grackle_fields grackle field data + * @param grackle_chemistry_data grackle chemistry data. + * @param grackle_units units used by grackle + * @param field_index grackle field index to print out. + **/ +__attribute__((always_inline)) INLINE static void +rt_write_grackle_setup_and_field(FILE *fp, grackle_field_data grackle_fields, + chemistry_data *grackle_chemistry_data, + code_units *grackle_units, int field_index) { + + fprintf(fp, "Grackle chemistry parameters:\n"); + + fprintf(fp, "use_grackle = %d\n", + grackle_chemistry_data->use_grackle); + fprintf(fp, "with_radiative_cooling = %d\n", + grackle_chemistry_data->with_radiative_cooling); + fprintf(fp, "primordial_chemistry = %d\n", + grackle_chemistry_data->primordial_chemistry); + fprintf(fp, "dust_chemistry = %d\n", + grackle_chemistry_data->dust_chemistry); + fprintf(fp, "metal_cooling = %d\n", + grackle_chemistry_data->metal_cooling); + fprintf(fp, "UVbackground = %d\n", + grackle_chemistry_data->UVbackground); + fprintf(fp, "grackle_data_file = %s\n", + grackle_chemistry_data->grackle_data_file); + fprintf(fp, "cmb_temperature_floor = %d\n", + grackle_chemistry_data->cmb_temperature_floor); + fprintf(fp, "Gamma = %g\n", + grackle_chemistry_data->Gamma); + fprintf(fp, "h2_on_dust = %d\n", + grackle_chemistry_data->h2_on_dust); + fprintf(fp, "use_dust_density_field = %d\n", + grackle_chemistry_data->use_dust_density_field); + fprintf(fp, "dust_recombination_cooling = %d\n", + grackle_chemistry_data->dust_recombination_cooling); + fprintf(fp, "photoelectric_heating = %d\n", + grackle_chemistry_data->photoelectric_heating); + fprintf(fp, "photoelectric_heating_rate = %g\n", + grackle_chemistry_data->photoelectric_heating_rate); + fprintf(fp, "use_isrf_field = %d\n", + grackle_chemistry_data->use_isrf_field); + fprintf(fp, "interstellar_radiation_field = %g\n", + grackle_chemistry_data->interstellar_radiation_field); + fprintf(fp, "use_volumetric_heating_rate = %d\n", + grackle_chemistry_data->use_volumetric_heating_rate); + fprintf(fp, "use_specific_heating_rate = %d\n", + grackle_chemistry_data->use_specific_heating_rate); + fprintf(fp, "three_body_rate = %d\n", + grackle_chemistry_data->three_body_rate); + fprintf(fp, "cie_cooling = %d\n", + grackle_chemistry_data->cie_cooling); + fprintf(fp, "h2_optical_depth_approximation = %d\n", + grackle_chemistry_data->h2_optical_depth_approximation); + fprintf(fp, "ih2co = %d\n", + grackle_chemistry_data->ih2co); + fprintf(fp, "ipiht = %d\n", + grackle_chemistry_data->ipiht); + fprintf(fp, "HydrogenFractionByMass = %g\n", + grackle_chemistry_data->HydrogenFractionByMass); + fprintf(fp, "DeuteriumToHydrogenRatio = %g\n", + grackle_chemistry_data->DeuteriumToHydrogenRatio); + fprintf(fp, "SolarMetalFractionByMass = %g\n", + grackle_chemistry_data->SolarMetalFractionByMass); + fprintf(fp, "local_dust_to_gas_ratio = %g\n", + grackle_chemistry_data->local_dust_to_gas_ratio); + fprintf(fp, "NumberOfTemperatureBins = %d\n", + grackle_chemistry_data->NumberOfTemperatureBins); + fprintf(fp, "CaseBRecombination = %d\n", + grackle_chemistry_data->CaseBRecombination); + fprintf(fp, "TemperatureStart = %g\n", + grackle_chemistry_data->TemperatureStart); + fprintf(fp, "TemperatureEnd = %g\n", + grackle_chemistry_data->TemperatureEnd); + fprintf(fp, "NumberOfDustTemperatureBins = %d\n", + grackle_chemistry_data->NumberOfDustTemperatureBins); + fprintf(fp, "DustTemperatureStart = %g\n", + grackle_chemistry_data->DustTemperatureStart); + fprintf(fp, "DustTemperatureEnd = %g\n", + grackle_chemistry_data->DustTemperatureEnd); + fprintf(fp, "Compton_xray_heating = %d\n", + grackle_chemistry_data->Compton_xray_heating); + fprintf(fp, "LWbackground_sawtooth_suppression = %d\n", + grackle_chemistry_data->LWbackground_sawtooth_suppression); + fprintf(fp, "LWbackground_intensity = %g\n", + grackle_chemistry_data->LWbackground_intensity); + fprintf(fp, "UVbackground_redshift_on = %g\n", + grackle_chemistry_data->UVbackground_redshift_on); + fprintf(fp, "UVbackground_redshift_off = %g\n", + grackle_chemistry_data->UVbackground_redshift_off); + fprintf(fp, "UVbackground_redshift_fullon = %g\n", + grackle_chemistry_data->UVbackground_redshift_fullon); + fprintf(fp, "UVbackground_redshift_drop = %g\n", + grackle_chemistry_data->UVbackground_redshift_drop); + fprintf(fp, "cloudy_electron_fraction_factor = %g\n", + grackle_chemistry_data->cloudy_electron_fraction_factor); + fprintf(fp, "use_radiative_transfer = %d\n", + grackle_chemistry_data->use_radiative_transfer); + fprintf(fp, "radiative_transfer_coupled_rate_solver = %d\n", + grackle_chemistry_data->radiative_transfer_coupled_rate_solver); + fprintf(fp, "radiative_transfer_intermediate_step = %d\n", + grackle_chemistry_data->radiative_transfer_intermediate_step); + fprintf(fp, "radiative_transfer_hydrogen_only = %d\n", + grackle_chemistry_data->radiative_transfer_hydrogen_only); + fprintf(fp, "self_shielding_method = %d\n", + grackle_chemistry_data->self_shielding_method); + fprintf(fp, "H2_custom_shielding = %d\n", + grackle_chemistry_data->H2_custom_shielding); + fprintf(fp, "H2_self_shielding = %d\n", + grackle_chemistry_data->H2_self_shielding); + + fprintf(fp, "\nUnits:\n"); + fprintf(fp, "a_units = %g\n", grackle_units->a_units); + fprintf(fp, "a_value = %g\n", grackle_units->a_value); + fprintf(fp, "comoving_coordinates = %d\n", + grackle_units->comoving_coordinates); + fprintf(fp, "density_units = %g\n", grackle_units->density_units); + fprintf(fp, "length_units = %g\n", grackle_units->length_units); + fprintf(fp, "time_units = %g\n", grackle_units->time_units); + fprintf(fp, "velocity_units = %g\n", grackle_units->velocity_units); + +#define rt_print_grackle_field(v) \ + if (grackle_fields.v != NULL) \ + fprintf(fp, "grackle_fields." #v " = %g\n", grackle_fields.v[field_index]) + + fprintf(fp, "\nGrackle field data:\n"); + rt_print_grackle_field(density); + rt_print_grackle_field(internal_energy); + rt_print_grackle_field(HI_density); + rt_print_grackle_field(HII_density); + rt_print_grackle_field(HeI_density); + rt_print_grackle_field(HeII_density); + rt_print_grackle_field(HeIII_density); + rt_print_grackle_field(e_density); + rt_print_grackle_field(HM_density); + rt_print_grackle_field(H2I_density); + rt_print_grackle_field(H2II_density); + rt_print_grackle_field(DI_density); + rt_print_grackle_field(DII_density); + rt_print_grackle_field(HDI_density); + rt_print_grackle_field(metal_density); + rt_print_grackle_field(x_velocity); + rt_print_grackle_field(y_velocity); + rt_print_grackle_field(z_velocity); + rt_print_grackle_field(volumetric_heating_rate); + rt_print_grackle_field(specific_heating_rate); + rt_print_grackle_field(RT_HI_ionization_rate); + rt_print_grackle_field(RT_HeI_ionization_rate); + rt_print_grackle_field(RT_HeII_ionization_rate); + rt_print_grackle_field(RT_H2_dissociation_rate); + rt_print_grackle_field(RT_heating_rate); + +#undef rt_print_grackle_field +} + +#endif /* SWIFT_RT_GRACKLE_UTILS_H */ diff --git a/src/rt/GEAR/rt_gradients.h b/src/rt/GEAR/rt_gradients.h index dbea52ad8af80d9ce9c5d120828740ecca83ed3d..dc116c940240f68255dbb3523ddd20d1340498eb 100644 --- a/src/rt/GEAR/rt_gradients.h +++ b/src/rt/GEAR/rt_gradients.h @@ -49,7 +49,7 @@ __attribute__((always_inline)) INLINE static void rt_gradients_init( for (int g = 0; g < RT_NGROUPS; g++) { for (int i = 0; i < 3; i++) { - rtd->gradient[g].energy[i] = 0.f; + rtd->gradient[g].energy_density[i] = 0.f; rtd->gradient[g].flux[i][0] = 0.f; rtd->gradient[g].flux[i][1] = 0.f; rtd->gradient[g].flux[i][2] = 0.f; @@ -63,7 +63,7 @@ __attribute__((always_inline)) INLINE static void rt_gradients_init( * * @param p Particle * @param g photon group index to update (0 <= g < RT_NGROUPS) - * @param dE energy gradient + * @param dE energy density gradient * @param dFx gradient of the x direction flux component * @param dFy gradient of the y direction flux component * @param dFz gradient of the z direction flux component @@ -74,9 +74,9 @@ __attribute__((always_inline)) INLINE static void rt_gradients_update_part( struct rt_part_data *rtd = &p->rt_data; - rtd->gradient[g].energy[0] += dE[0]; - rtd->gradient[g].energy[1] += dE[1]; - rtd->gradient[g].energy[2] += dE[2]; + rtd->gradient[g].energy_density[0] += dE[0]; + rtd->gradient[g].energy_density[1] += dE[1]; + rtd->gradient[g].energy_density[2] += dE[2]; rtd->gradient[g].flux[0][0] += dFx[0]; rtd->gradient[g].flux[0][1] += dFx[1]; @@ -117,9 +117,9 @@ __attribute__((always_inline)) INLINE static void rt_finalise_gradient_part( struct rt_part_data *rtd = &p->rt_data; for (int g = 0; g < RT_NGROUPS; g++) { - rtd->gradient[g].energy[0] *= norm; - rtd->gradient[g].energy[1] *= norm; - rtd->gradient[g].energy[2] *= norm; + rtd->gradient[g].energy_density[0] *= norm; + rtd->gradient[g].energy_density[1] *= norm; + rtd->gradient[g].energy_density[2] *= norm; for (int i = 0; i < 3; i++) { rtd->gradient[g].flux[i][0] *= norm; rtd->gradient[g].flux[i][1] *= norm; @@ -143,21 +143,12 @@ __attribute__((always_inline)) INLINE static void rt_finalise_gradient_part( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void rt_gradients_collect( - float r2, const float *dx, float hi, float hj, struct part *restrict pi, + float r2, const float dx[3], float hi, float hj, struct part *restrict pi, struct part *restrict pj) { #ifdef SWIFT_RT_DEBUG_CHECKS - if (pi->rt_data.debug_injection_done != 1) - error( - "Trying to do symmetric iact gradient when finalise injection count is " - "%d ID %lld", - pi->rt_data.debug_injection_done, pi->id); - - if (pj->rt_data.debug_injection_done != 1) - error( - "Trying to do symmetric iact gradient when finalise injection count is " - "%d ID %lld", - pj->rt_data.debug_injection_done, pj->id); + rt_debug_sequence_check(pi, 2, __func__); + rt_debug_sequence_check(pj, 2, __func__); pi->rt_data.debug_calls_iact_gradient_interaction += 1; pj->rt_data.debug_calls_iact_gradient_interaction += 1; @@ -224,8 +215,8 @@ __attribute__((always_inline)) INLINE static void rt_gradients_collect( for (int g = 0; g < RT_NGROUPS; g++) { float Ui[4], Uj[4]; - rt_part_get_density_vector(pi, g, Ui); - rt_part_get_density_vector(pj, g, Uj); + rt_part_get_radiation_state_vector(pi, g, Ui); + rt_part_get_radiation_state_vector(pj, g, Uj); const float dU[4] = {Ui[0] - Uj[0], Ui[1] - Uj[1], Ui[2] - Uj[2], Ui[3] - Uj[3]}; @@ -291,16 +282,11 @@ __attribute__((always_inline)) INLINE static void rt_gradients_collect( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void rt_gradients_nonsym_collect( - float r2, const float *dx, float hi, float hj, struct part *restrict pi, + float r2, const float dx[3], float hi, float hj, struct part *restrict pi, struct part *restrict pj) { #ifdef SWIFT_RT_DEBUG_CHECKS - if (pi->rt_data.debug_injection_done != 1) - error( - "Trying to do nonsym iact gradients when finalise injection count is " - "%d ID %lld", - pi->rt_data.debug_injection_done, pi->id); - + rt_debug_sequence_check(pi, 2, __func__); pi->rt_data.debug_calls_iact_gradient_interaction += 1; #endif @@ -342,8 +328,8 @@ __attribute__((always_inline)) INLINE static void rt_gradients_nonsym_collect( for (int g = 0; g < RT_NGROUPS; g++) { float Ui[4], Uj[4]; - rt_part_get_density_vector(pi, g, Ui); - rt_part_get_density_vector(pj, g, Uj); + rt_part_get_radiation_state_vector(pi, g, Ui); + rt_part_get_radiation_state_vector(pj, g, Uj); const float dU[4] = {Ui[0] - Uj[0], Ui[1] - Uj[1], Ui[2] - Uj[2], Ui[3] - Uj[3]}; @@ -393,8 +379,11 @@ __attribute__((always_inline)) INLINE static float rt_gradients_extrapolate( * * @param pi Particle i * @param pj Particle j - * @param Ui Resulting predicted and limited (density) state of particle i - * @param Uj Resulting predicted and limited (density) state of particle j + * @param Ui (return) Resulting predicted and limited radiation state of + * particle i + * @param Uj (return) Resulting predicted and limited radiation state of + * particle j + * @param group which photon group to use * @param dx Comoving distance vector between the particles (dx = pi->x - * pj->x). * @param r Comoving distance between particle i and particle j. @@ -405,11 +394,11 @@ __attribute__((always_inline)) INLINE static void rt_gradients_predict( float Uj[4], int group, const float *dx, const float r, const float xij_i[3]) { - rt_part_get_density_vector(pi, group, Ui); - rt_part_get_density_vector(pj, group, Uj); - /* No need to check unphysical density here: - * the densities haven't been touched since - * the rt_injection_update_photon_density */ + rt_part_get_radiation_state_vector(pi, group, Ui); + rt_part_get_radiation_state_vector(pj, group, Uj); + /* No need to check unphysical state here: + * they haven't been touched since the call + * to rt_injection_update_photon_density */ float dE_i[3], dFx_i[3], dFy_i[3], dFz_i[3]; float dE_j[3], dFx_j[3], dFy_j[3], dFz_j[3]; @@ -446,9 +435,45 @@ __attribute__((always_inline)) INLINE static void rt_gradients_predict( Uj[2] += dUj[2]; Uj[3] += dUj[3]; - /* Check and correct unphysical extrapolated densities */ - rt_check_unphysical_density(Ui, &Ui[1], 1); - rt_check_unphysical_density(Uj, &Uj[1], 1); + /* Check and correct unphysical extrapolated states */ + rt_check_unphysical_state(Ui, &Ui[1], /*e_old=*/0.f, /*callloc=*/1); + rt_check_unphysical_state(Uj, &Uj[1], /*e_old=*/0.f, /*callloc=*/1); +} + +/** + * @brief Gradients reconstruction. Predict the value at point x_ij given + * current values at particle positions and gradients at particle positions. + * + * @param p Particle to work on + * @param U (return) Resulting predicted and limited radiation state of + * particle + * @param group which photon group to use + * @param dx The drift distance + */ +__attribute__((always_inline)) INLINE static void rt_gradients_predict_drift( + const struct part *restrict p, float U[4], int group, const float dx[3]) { + + rt_part_get_radiation_state_vector(p, group, U); + /* No need to check unphysical state here: + * they haven't been touched since the call + * to rt_injection_update_photon_density */ + + float dE[3], dFx[3], dFy[3], dFz[3]; + rt_part_get_gradients(p, group, dE, dFx, dFy, dFz); + + float dU[4]; + dU[0] = rt_gradients_extrapolate(dE, dx); + dU[1] = rt_gradients_extrapolate(dFx, dx); + dU[2] = rt_gradients_extrapolate(dFy, dx); + dU[3] = rt_gradients_extrapolate(dFz, dx); + + U[0] += dU[0]; + U[1] += dU[1]; + U[2] += dU[2]; + U[3] += dU[3]; + + /* Check and correct unphysical extrapolated states */ + rt_check_unphysical_state(U, &U[1], /*e_old=*/0.f, /*callloc=*/1); } #endif /* SWIFT_RT_GRADIENT_GEAR_H */ diff --git a/src/rt/GEAR/rt_iact.h b/src/rt/GEAR/rt_iact.h index cc339297dc5a68e8fba62681119a702fbb403900..3ad71d3b66616369ee45851cca73b4cbb9fae0ed 100644 --- a/src/rt/GEAR/rt_iact.h +++ b/src/rt/GEAR/rt_iact.h @@ -19,6 +19,7 @@ #ifndef SWIFT_RT_IACT_GEAR_H #define SWIFT_RT_IACT_GEAR_H +#include "rt_debugging.h" #include "rt_flux.h" #include "rt_gradients.h" @@ -45,24 +46,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, const float hi, const float hj, - struct spart *si, struct part *pj, + struct spart *si, const struct part *pj, const struct cosmology *cosmo, const struct rt_props *rt_props) { - /* NOTE: `struct part *pj` should be `const struct part *pj`, - * but I allow changes to it for debugging routines at the moment. - * Nevertheless, you shouldn't be changing anything in a particle - * in this function. */ - /* If the star doesn't have any neighbours, we * have nothing to do here. */ if (si->density.wcount == 0.f) return; #ifdef SWIFT_RT_DEBUG_CHECKS si->rt_data.debug_iact_hydro_inject_prep += 1; - si->rt_data.debug_iact_hydro_inject_prep_tot += 1ULL; - pj->rt_data.debug_iact_stars_inject_prep += 1; - pj->rt_data.debug_iact_stars_inject_prep_tot += 1ULL; #endif /* Compute the weight of the neighbouring particle */ @@ -73,7 +66,11 @@ runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, kernel_eval(xi, &wi); const float hi_inv_dim = pow_dimension(hi_inv); /* psi(x_star - x_gas, h_star) */ - const float psi = wi * hi_inv_dim / si->density.wcount; + /* Note: skip the devision by si->density.wcount here. It'll cancel out by the + * normalization anyway, and furthermore now that the injection prep is done + * during the star density loop, si->density.wcount won't be computed at this + * stage yet. */ + const float psi = wi * hi_inv_dim; /* Now add that weight to the appropriate octant */ int octant_index = 0; @@ -96,31 +93,27 @@ runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, * @param pj Hydro particle. * @param a Current scale factor. * @param H Current Hubble parameter. + * @param rt_props Properties of the RT scheme. */ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( const float r2, float *dx, const float hi, const float hj, - struct spart *restrict si, struct part *restrict pj, float a, float H) { + struct spart *restrict si, struct part *restrict pj, float a, float H, + const struct rt_props *rt_props) { /* If the star doesn't have any neighbours, we * have nothing to do here. */ if (si->density.wcount == 0.f) return; #ifdef SWIFT_RT_DEBUG_CHECKS + /* Do some checks and increase neighbour counts * before other potential early exits */ if (si->rt_data.debug_iact_hydro_inject_prep == 0) error( - "Injecting energy from star that wasn't called" - " during injection prep"); - if (pj->rt_data.debug_iact_stars_inject_prep == 0) { + "Injecting energy from star that wasn't called during injection prep"); - const float hig2 = hi * hi * kernel_gamma2; - const float res = sqrtf(r2 / hig2); - error( - "Injecting energy into part that wasn't called" - " during injection prep: sID %lld pID %lld r/H_s %.6f", - si->id, pj->id, res); - } + if (!si->rt_data.debug_emission_rate_set) + error("Injecting energy from star without setting emission rate"); si->rt_data.debug_iact_hydro_inject += 1; si->rt_data.debug_radiation_emitted_tot += 1ULL; @@ -128,18 +121,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( pj->rt_data.debug_iact_stars_inject += 1; pj->rt_data.debug_radiation_absorbed_tot += 1ULL; - /* Attempt to catch race condition/dependency error */ - if (si->rt_data.debug_iact_hydro_inject_prep < - si->rt_data.debug_iact_hydro_inject) - error( - "Star interacts with more particles during" - " injection than during injection prep"); - - if (pj->rt_data.debug_iact_stars_inject_prep < - pj->rt_data.debug_iact_stars_inject) - error( - "Part interacts with more stars during" - " injection than during injection prep"); #endif /* Compute the weight of the neighbouring particle */ @@ -150,7 +131,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( kernel_eval(xi, &wi); const float hi_inv_dim = pow_dimension(hi_inv); /* psi(x_star - x_gas, h_star) */ - const float psi = wi * hi_inv_dim / si->density.wcount; + /* Skip the division by si->density.wcount to remain consistent */ + const float psi = wi * hi_inv_dim; #if defined(HYDRO_DIMENSION_3D) const int maxind = 8; @@ -172,30 +154,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( if (dx[1] > 0.f) octant_index += 2; if (dx[2] > 0.f) octant_index += 4; - const float qw = si->rt_data.octant_weights[octant_index]; + const float octw = si->rt_data.octant_weights[octant_index]; /* We might end up in this scenario due to roundoff errors */ - if (psi == 0.f || qw == 0.f) return; - - const float weight = psi / nonempty_octants / qw; + if (psi == 0.f || octw == 0.f) return; - const float minus_r_inv = -1.f / r; - const float n_unit[3] = {dx[0] * minus_r_inv, dx[1] * minus_r_inv, - dx[2] * minus_r_inv}; + const float weight = psi / (nonempty_octants * octw); + const float Vinv = 1.f / pj->geometry.volume; /* Nurse, the patient is ready now */ - /* TODO: this is done differently for RT_HYDRO_CONTROLLED_INJECTION */ for (int g = 0; g < RT_NGROUPS; g++) { /* Inject energy. */ - const float injected_energy = si->rt_data.emission_this_step[g] * weight; - pj->rt_data.conserved[g].energy += injected_energy; - - /* Inject flux. */ - /* We assume the path from the star to the gas is optically thin */ - const float injected_flux = - injected_energy * rt_params.reduced_speed_of_light; - pj->rt_data.conserved[g].flux[0] += injected_flux * n_unit[0]; - pj->rt_data.conserved[g].flux[1] += injected_flux * n_unit[1]; - pj->rt_data.conserved[g].flux[2] += injected_flux * n_unit[2]; + const float injected_energy_density = + si->rt_data.emission_this_step[g] * weight * Vinv; + pj->rt_data.radiation[g].energy_density += injected_energy_density; + + /* Don't inject flux. */ } #ifdef SWIFT_RT_DEBUG_CHECKS @@ -207,10 +180,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( "Injecting abnormal energy spart %lld part %lld group %d | %.6e %.6e " "%.6e", si->id, pj->id, g, injected_energy, weight, + si->rt_data.emission_this_step[g]); si->rt_data.debug_injected_energy[g] += injected_energy; si->rt_data.debug_injected_energy_tot[g] += injected_energy; } + si->rt_data.debug_psi_sum += psi; #endif } @@ -235,34 +210,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common( struct part *restrict pj, float a, float H, int mode) { #ifdef SWIFT_RT_DEBUG_CHECKS - if (pi->rt_data.debug_injection_done != 1) - error( - "Trying to do iact transport when " - "finalise injection count is %d", - pi->rt_data.debug_injection_done); - - if (pi->rt_data.debug_gradients_done != 1) - error( - "Trying to do iact transport when " - "rt_finalise_gradient count is %d", - pi->rt_data.debug_gradients_done); - + const char *func_name = (mode == 1) ? "sym flux iact" : "nonsym flux iact"; + rt_debug_sequence_check(pi, 3, func_name); pi->rt_data.debug_calls_iact_transport_interaction += 1; if (mode == 1) { - - if (pj->rt_data.debug_injection_done != 1) - error( - "Trying to do iact transport when " - "finalise injection count is %d", - pj->rt_data.debug_injection_done); - - if (pj->rt_data.debug_gradients_done != 1) - error( - "Trying to do iact transport when " - "rt_finalise_gradient count is %d", - pj->rt_data.debug_gradients_done); - + rt_debug_sequence_check(pj, 3, func_name); pj->rt_data.debug_calls_iact_transport_interaction += 1; } #endif @@ -373,18 +326,22 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common( struct rt_part_data *restrict rti = &pi->rt_data; struct rt_part_data *restrict rtj = &pj->rt_data; + /* Get the time step for the flux exchange. This is always the smallest time + * step among the two particles. */ + const float mindt = + (rtj->flux_dt > 0.f) ? fminf(rti->flux_dt, rtj->flux_dt) : rti->flux_dt; for (int g = 0; g < RT_NGROUPS; g++) { - /* density state to be used to compute the flux */ + /* radiation state to be used to compute the flux */ float Ui[4], Uj[4]; rt_gradients_predict(pi, pj, Ui, Uj, g, dx, r, xij_i); /* For first order method, skip the gradients */ /* float Ui[4], Uj[4]; */ - /* rt_part_get_density_vector(pi, g, Ui); */ - /* rt_part_get_density_vector(pj, g, Uj); */ - /* No need to check for unphysical densities, they + /* rt_part_get_radiation_state_vector(pi, g, Ui); */ + /* rt_part_get_radiation_state_vector(pj, g, Uj); */ + /* No need to check for unphysical quantities, they * haven't been touched since * rt_injection_update_photon_densities */ @@ -396,16 +353,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common( * pj is right state. The sign convention is that a positive total * flux is subtracted from the left state, and added to the right * state, based on how we chose the unit vector. By this convention, - * the time integration results in conserved += flux * dt */ - rti->flux[g].energy -= totflux[0]; - rti->flux[g].flux[0] -= totflux[1]; - rti->flux[g].flux[1] -= totflux[2]; - rti->flux[g].flux[2] -= totflux[3]; - if (mode == 1) { - rtj->flux[g].energy += totflux[0]; - rtj->flux[g].flux[0] += totflux[1]; - rtj->flux[g].flux[1] += totflux[2]; - rtj->flux[g].flux[2] += totflux[3]; + * the time integration results in conserved quantity += flux * dt */ + /* Unlike in SPH schemes, we do need to update inactive neighbours, so that + * the fluxes are always exchanged symmetrically. Thanks to our sneaky use + * of flux_dt, we can detect inactive neighbours through their negative time + * step. */ + rti->flux[g].energy -= totflux[0] * mindt; + rti->flux[g].flux[0] -= totflux[1] * mindt; + rti->flux[g].flux[1] -= totflux[2] * mindt; + rti->flux[g].flux[2] -= totflux[3] * mindt; + if (mode == 1 || (rtj->flux_dt < 0.f)) { + rtj->flux[g].energy += totflux[0] * mindt; + rtj->flux[g].flux[0] += totflux[1] * mindt; + rtj->flux[g].flux[1] += totflux[2] * mindt; + rtj->flux[g].flux[2] += totflux[3] * mindt; } } } diff --git a/src/rt/GEAR/rt_interaction_cross_sections.c b/src/rt/GEAR/rt_interaction_cross_sections.c new file mode 100644 index 0000000000000000000000000000000000000000..42c9d4d822f7f1c8e445d59133ce2a28bc6eb2de --- /dev/null +++ b/src/rt/GEAR/rt_interaction_cross_sections.c @@ -0,0 +1,367 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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 "rt_interaction_cross_sections.h" + +#include "error.h" +#include "rt_blackbody.h" +#include "rt_properties.h" +#include "rt_species.h" + +#include <gsl/gsl_integration.h> + +/** + * @brief compute the chosen spectrum for a given frequency nu. + * This function is intended to be used to integrate the spectra + * over a frequency range in order to obtain averaged ionization + * cross sections. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +__attribute__((always_inline)) INLINE static double +rt_interaction_rates_get_spectrum(const double nu, void *params) { + /* Keep this function in the .c file so you don't have to + * include the spectra .h files like rt_blackbody.h anywhere + * else */ + + struct rt_spectrum_integration_params *pars = + (struct rt_spectrum_integration_params *)params; + + if (pars->spectrum_type == 0) { + /* Constant spectrum */ + if (nu <= pars->const_stellar_spectrum_frequency_max) { + return 1.; + } else { + return 0.; + } + } else if (pars->spectrum_type == 1) { + /* Blackbody spectrum */ + const double T = pars->T; + const double kB = pars->kB; + const double h_planck = pars->h_planck; + const double c = pars->c; + return blackbody_spectrum_intensity(nu, T, kB, h_planck, c); + } else { + error("Unknown stellar spectrum type selected: %d", pars->spectrum_type); + return 0.; + } +} + +/** + * Spectrum function to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_integrand(double nu, void *params) { + return rt_interaction_rates_get_spectrum(nu, params); +} + +/** + * Spectrum function divided by photon energy h*nu to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_over_hnu_integrand(double nu, void *params) { + struct rt_spectrum_integration_params *p = + (struct rt_spectrum_integration_params *)params; + const double E = nu * p->h_planck; + const double J = rt_interaction_rates_get_spectrum(nu, params); + if (E > 0.) { + return J / E; + } else { + return 0.; + } +} + +/** + * Spectrum times cross section function to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_times_sigma_integrand(double nu, void *params) { + struct rt_spectrum_integration_params *p = + (struct rt_spectrum_integration_params *)params; + const double E = nu * p->h_planck; + const double sigma = + photoionization_cross_section(E, p->species, p->cs_params); + const double J = rt_interaction_rates_get_spectrum(nu, params); + return J * sigma; +} + +/** + * Spectrum times cross section divided by h*nu function to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_times_sigma_over_hnu_integrand(double nu, void *params) { + struct rt_spectrum_integration_params *p = + (struct rt_spectrum_integration_params *)params; + const double E = nu * p->h_planck; + const double sigma = + photoionization_cross_section(E, p->species, p->cs_params); + const double J = rt_interaction_rates_get_spectrum(nu, params); + return J * sigma / E; +} + +/** + * Integrate a function from nu_start to nu_stop with GSL routines + * + * @param function function to integrate + * @param nu_start lower boundary of the integral + * @param nu_stop upper boundary of the integral + * @param params spectrum integration params. + * */ +static double rt_cross_sections_integrate_gsl( + double (*function)(double, void *), double nu_start, double nu_stop, + int npoints, struct rt_spectrum_integration_params *params) { + + gsl_function F; + gsl_integration_workspace *w = gsl_integration_workspace_alloc(npoints); + double result, error; + + F.function = function; + F.params = (void *)params; + /* NOTE: there is an option to use the integrator with an upper limit + * of infinity, but this is accurate enough for now when setting a + * high enough maximal frequency. */ + gsl_integration_qags(&F, nu_start, nu_stop, /*espabs=*/0., /*epsrel=*/1e-7, + npoints, w, &result, &error); + + /* Clean up after yourself. */ + gsl_integration_workspace_free(w); + + return result; +} + +/** + * @brief allocate and pre-compute the averaged cross sections + * for each photon group and ionizing species. + * + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param us internal units struct + **/ +void rt_cross_sections_init(struct rt_props *restrict rt_props, + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us) { + + /* Allocate the space to store the (cross section) integrals */ + /* --------------------------------------------------------- */ + + double **cse = malloc(RT_NGROUPS * sizeof(double *)); + double **csn = malloc(RT_NGROUPS * sizeof(double *)); + double *av_energy = rt_props->average_photon_energy; + double *photon_number_integral = rt_props->photon_number_integral; + for (int group = 0; group < RT_NGROUPS; group++) { + cse[group] = malloc(rt_ionizing_species_count * sizeof(double)); + csn[group] = malloc(rt_ionizing_species_count * sizeof(double)); + av_energy[group] = 0.; + photon_number_integral[group] = 0.; + } + + double integral_E[RT_NGROUPS]; + double integral_N[RT_NGROUPS]; + double integral_sigma_E[RT_NGROUPS][rt_ionizing_species_count]; + double integral_sigma_E_over_hnu[RT_NGROUPS][rt_ionizing_species_count]; + + /* Grab constants and conversions in cgs */ + /* ------------------------------------- */ + const double T_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + const float dimension_kB[5] = {1, 2, -2, 0, -1}; /* [g cm^2 s^-2 K^-1] */ + const double kB_to_cgs = + units_general_cgs_conversion_factor(us, dimension_kB); + const double kB_cgs = phys_const->const_boltzmann_k * kB_to_cgs; + + const float dimension_h[5] = {1, 2, -1, 0, 0}; /* [g cm^2 s^-1] */ + const double h_to_cgs = units_general_cgs_conversion_factor(us, dimension_h); + const double h_planck_cgs = phys_const->const_planck_h * h_to_cgs; + + const double c_cgs = phys_const->const_speed_light_c * + units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + + /* Prepare parameter struct for integration functions */ + /* -------------------------------------------------- */ + const int spectype = rt_props->stellar_spectrum_type; + const double maxfreq_const_spectrum = + rt_props->const_stellar_spectrum_max_frequency; + const double T_bb = rt_props->stellar_spectrum_blackbody_T * T_cgs; + struct rt_photoion_cs_parameters cs_params = rt_init_photoion_cs_params_cgs(); + + struct rt_spectrum_integration_params integration_params = { + /*species=*/0, + /*spectrum_type=*/spectype, + /*freq_max for const spectrum=*/maxfreq_const_spectrum, + /*T=*/T_bb, + /*kB=*/kB_cgs, + /*h_planck=*/h_planck_cgs, + /*c=*/c_cgs, + /*cross section params=*/&cs_params}; + + /* Set up Integration Limits */ + /* ------------------------- */ + + /* Get start and end points of the integrals */ + double nu_start[RT_NGROUPS]; + double nu_stop[RT_NGROUPS]; + for (int group = 0; group < RT_NGROUPS; group++) + nu_start[group] = rt_props->photon_groups[group]; + for (int group = 0; group < RT_NGROUPS - 1; group++) + nu_stop[group] = rt_props->photon_groups[group + 1]; + + if (RT_NGROUPS == 1) { + /* If we only have one group, start integrating from the Hydrogen + * ionization frequency, not from zero. The reasoning here is that + * typically you define the *ionizing* radiation as stellar emission + * rates, not the *total* radiation. */ + nu_start[0] = cs_params.E_ion[rt_ionizing_species_HI] / h_planck_cgs; + if (engine_rank == 0) + message( + "WARNING: with only 1 photon group, I'll start integrating" + " the cross sections at the first ionizing frequency %.3g", + nu_start[0]); + } else { + /* don't start at exactly 0 to avoid unlucky divisions */ + if (nu_start[0] == 0.) nu_start[0] = min(1e-20, 1e-12 * nu_start[1]); + } + + /* Get frequency at which we stop integrating */ + double nu_stop_final; + if (rt_props->stellar_spectrum_type == 0) { + nu_stop_final = rt_props->const_stellar_spectrum_max_frequency; + } else if (rt_props->stellar_spectrum_type == 1) { + nu_stop_final = 10. * blackbody_peak_frequency(T_bb, kB_cgs, h_planck_cgs); + } else { + nu_stop_final = -1.; + error("Unknown stellar spectrum type %d", rt_props->stellar_spectrum_type); + } + nu_stop[RT_NGROUPS - 1] = nu_stop_final; + + /* Compute Integrals */ + /* ----------------- */ + for (int g = 0; g < RT_NGROUPS; g++) { + /* This is independent of species. */ + integral_E[g] = rt_cross_sections_integrate_gsl( + spectrum_integrand, nu_start[g], nu_stop[g], RT_INTEGRAL_NPOINTS, + &integration_params); + integral_N[g] = rt_cross_sections_integrate_gsl( + spectrum_over_hnu_integrand, nu_start[g], nu_stop[g], + RT_INTEGRAL_NPOINTS, &integration_params); + + for (int s = 0; s < rt_ionizing_species_count; s++) { + integration_params.species = s; + integral_sigma_E[g][s] = rt_cross_sections_integrate_gsl( + spectrum_times_sigma_integrand, nu_start[g], nu_stop[g], + RT_INTEGRAL_NPOINTS, &integration_params); + integral_sigma_E_over_hnu[g][s] = rt_cross_sections_integrate_gsl( + spectrum_times_sigma_over_hnu_integrand, nu_start[g], nu_stop[g], + RT_INTEGRAL_NPOINTS, &integration_params); + } + } + + /* Now compute the actual average cross sections */ + /* --------------------------------------------- */ + for (int g = 0; g < RT_NGROUPS; g++) { + photon_number_integral[g] = integral_N[g]; + if (integral_N[g] > 0.) { + av_energy[g] = integral_E[g] / integral_N[g]; + } else { + av_energy[g] = 0.; + } + for (int s = 0; s < rt_ionizing_species_count; s++) { + if (integral_E[g] > 0.) { + cse[g][s] = integral_sigma_E[g][s] / integral_E[g]; + csn[g][s] = integral_sigma_E_over_hnu[g][s] / integral_N[g]; + } else { + /* No radiation = no interaction */ + cse[g][s] = 0.; + csn[g][s] = 0.; + } + } + } + + /* for (int g = 0; g < RT_NGROUPS; g++) { */ + /* printf("\nGroup %d\n", g); */ + /* printf("nu_start: %12.6g\n", nu_start[g]); */ + /* printf("nu_end: %12.6g\n", nu_stop[g]); */ + /* printf("spectrum energy integral: %12.6g\n", integral_E[g]); */ + /* printf("spectrum number integral: %12.6g\n", integral_N[g]); */ + /* printf("average photon energy: %12.6g\n", av_energy[g]); */ + /* */ + /* printf("species: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12d ", s); + */ + /* printf("\n"); */ + /* */ + /* printf("integral sigma * E: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* integral_sigma_E[g][s]); */ + /* printf("\n"); */ + /* */ + /* printf("integral sigma * E / h nu: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* integral_sigma_E_over_hnu[g][s]); */ + /* printf("\n"); */ + /* */ + /* printf("energy weighted c.section: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* cse[g][s]); */ + /* printf("\n"); */ + /* */ + /* printf("number weighted c.section: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* csn[g][s]); */ + /* printf("\n"); */ + /* } */ + + /* Store the results */ + /* ----------------- */ + + if (rt_props->energy_weighted_cross_sections != NULL) { + for (int g = 0; g < RT_NGROUPS; g++) { + if (rt_props->energy_weighted_cross_sections[g] != NULL) + free(rt_props->energy_weighted_cross_sections[g]); + } + free(rt_props->energy_weighted_cross_sections); + } + rt_props->energy_weighted_cross_sections = cse; + + if (rt_props->number_weighted_cross_sections != NULL) { + for (int g = 0; g < RT_NGROUPS; g++) { + if (rt_props->number_weighted_cross_sections[g] != NULL) + free(rt_props->number_weighted_cross_sections[g]); + } + free(rt_props->number_weighted_cross_sections); + } + rt_props->number_weighted_cross_sections = csn; +} diff --git a/src/rt/GEAR/rt_interaction_cross_sections.h b/src/rt/GEAR/rt_interaction_cross_sections.h new file mode 100644 index 0000000000000000000000000000000000000000..02be07c5a4b8972d9ca19c09809abe19158b74ef --- /dev/null +++ b/src/rt/GEAR/rt_interaction_cross_sections.h @@ -0,0 +1,153 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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_RT_GEAR_INTERACION_CROSS_SECTIONS_H +#define SWIFT_RT_GEAR_INTERACION_CROSS_SECTIONS_H + +#include "inline.h" +#include "rt_species.h" + +#include <math.h> +#include <stdlib.h> + +/** + * @file src/rt/GEAR/rt_interaction_cross_sections.h + * @brief header file concerning photoionization cross sections + **/ + +#define RT_INTEGRAL_NPOINTS 10000 + +/*! Struct containing the parametrized cross section parameters + * for each photoionizing species. + * Correct usage is to call rt_init_photoion_cs_params_cgs(), + * which returns a fully initialized struct. */ +struct rt_photoion_cs_parameters { + double E_ion[rt_ionizing_species_count]; /* erg */ + double E_zero[rt_ionizing_species_count]; /* erg */ + double sigma_zero[rt_ionizing_species_count]; /* cm^-2 */ + double P[rt_ionizing_species_count]; + double ya[rt_ionizing_species_count]; + double yw[rt_ionizing_species_count]; + double y0[rt_ionizing_species_count]; + double y1[rt_ionizing_species_count]; +}; + +/*! Parameters needed to compute the stellar spectra integrals - + * the data needs to be encapsulated in a single struct to be passed + * as an argument for GSL integrators. */ +struct rt_spectrum_integration_params { + /* Which species are we dealing with? */ + int species; + /* Which spectrum type are we dealing with? */ + int spectrum_type; + /* Max frequency for const spectrum */ + double const_stellar_spectrum_frequency_max; + /* Temperature of blackbody in correct units */ + double T; + /* Boltzmann constant in correct units */ + double kB; + /* Planck constant in correct units */ + double h_planck; + /* speed of light in correct units */ + double c; + /* Values for the cross section parametrization */ + struct rt_photoion_cs_parameters *cs_params; +}; + +/** + * Initialize the parameters for the cross section computation in cgs, + * and return a fully and correctly initialized struct. + * The data is taken from Verner et al. 1996 + * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013 + * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R) (Table E1) + */ +__attribute__((always_inline)) INLINE static struct rt_photoion_cs_parameters +rt_init_photoion_cs_params_cgs(void) { + + struct rt_photoion_cs_parameters photoion_cs_params_cgs; + double E_ion[rt_ionizing_species_count]; + rt_species_get_ionizing_energy(E_ion); + + photoion_cs_params_cgs.E_ion[rt_ionizing_species_HI] = + E_ion[rt_ionizing_species_HI]; + photoion_cs_params_cgs.E_zero[rt_ionizing_species_HI] = 6.886e-13; + photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HI] = 5.475e-14; + photoion_cs_params_cgs.P[rt_ionizing_species_HI] = 2.963; + photoion_cs_params_cgs.ya[rt_ionizing_species_HI] = 32.88; + photoion_cs_params_cgs.yw[rt_ionizing_species_HI] = 0.; + photoion_cs_params_cgs.y0[rt_ionizing_species_HI] = 0.; + photoion_cs_params_cgs.y1[rt_ionizing_species_HI] = 0.; + + photoion_cs_params_cgs.E_ion[rt_ionizing_species_HeI] = + E_ion[rt_ionizing_species_HeI]; + /* Note: The value of 0.1361 eV for E_0 of HeI given in table 5.1 + * in Rosdahl et al. is an error. The correct value is 13.61 eV */ + photoion_cs_params_cgs.E_zero[rt_ionizing_species_HeI] = 2.181e-11; + photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HeI] = 9.492e-16; + photoion_cs_params_cgs.P[rt_ionizing_species_HeI] = 3.188; + photoion_cs_params_cgs.ya[rt_ionizing_species_HeI] = 1.469; + photoion_cs_params_cgs.yw[rt_ionizing_species_HeI] = 2.039; + photoion_cs_params_cgs.y0[rt_ionizing_species_HeI] = 0.4434; + photoion_cs_params_cgs.y1[rt_ionizing_species_HeI] = 2.136; + + photoion_cs_params_cgs.E_ion[rt_ionizing_species_HeII] = + E_ion[rt_ionizing_species_HeII]; + photoion_cs_params_cgs.E_zero[rt_ionizing_species_HeII] = 2.756e-12; + photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HeII] = 1.369e-14; + photoion_cs_params_cgs.P[rt_ionizing_species_HeII] = 2.963; + photoion_cs_params_cgs.ya[rt_ionizing_species_HeII] = 32.88; + photoion_cs_params_cgs.yw[rt_ionizing_species_HeII] = 0.; + photoion_cs_params_cgs.y0[rt_ionizing_species_HeII] = 0.; + photoion_cs_params_cgs.y1[rt_ionizing_species_HeII] = 0.; + + return photoion_cs_params_cgs; +} + +/** + * Compute the parametrized cross section for a given energy and species. + * The parametrization is taken from Verner et al. 1996 + * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013 + * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R) + * + * @param E energy for which to compute the cross section, in erg + * @param species index of species, 0 < species < rt_ionizing_species_count + * @param params cross section parameters struct + */ +__attribute__((always_inline)) INLINE static double +photoionization_cross_section(const double E, const int species, + const struct rt_photoion_cs_parameters *params) { + + const double E0 = params->E_zero[species]; + const double E_ion = params->E_ion[species]; + const double y0 = params->y0[species]; + const double y1 = params->y1[species]; + const double yw = params->yw[species]; + const double ya = params->ya[species]; + const double P = params->P[species]; + const double sigma_0 = params->sigma_zero[species]; + + if (E < E_ion) return 0.; + + const double x = E / E0 - y0; + const double y = sqrt(x * x + y1 * y1); + const double temp1 = pow(y, 0.5 * P - 5.5); + const double temp2 = pow(1. + sqrt(y / ya), -P); + return sigma_0 * ((x - 1.) * (x - 1.) + yw * yw) * temp1 * temp2; +} + +#endif /* SWIFT_RT_GEAR_INTERACION_CROSS_SECTIONS_H */ diff --git a/src/rt/GEAR/rt_interaction_rates.h b/src/rt/GEAR/rt_interaction_rates.h index 62db9467a1d32546a7638020f07859db2d341496..ebc14a307ec59a621811b91c31206f4b3f84550f 100644 --- a/src/rt/GEAR/rt_interaction_rates.h +++ b/src/rt/GEAR/rt_interaction_rates.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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,464 +16,162 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_RT_GEAR_INTERACION_RATES_H -#define SWIFT_RT_GEAR_INTERACION_RATES_H +#ifndef SWIFT_RT_GEAR_INTERACTION_RATES_H +#define SWIFT_RT_GEAR_INTERACTION_RATES_H -#include "rt_blackbody.h" #include "rt_parameters.h" #include "rt_properties.h" - -#include <gsl/gsl_integration.h> - -#define RT_INTEGRAL_NPOINTS 1000 - -/* HI, HeI, HeII */ -#define RT_NIONIZING_SPECIES 3 - -/*! Struct containing the parametrized cross section parameters - * for each photoionizing species. - * Correct usage is to call rt_init_photoion_cs_params_cgs(), - * which returns a fully initialized struct. */ -struct rt_photoion_cs_parameters { - double E_ion[RT_NIONIZING_SPECIES]; - double E_zero[RT_NIONIZING_SPECIES]; - double sigma_zero[RT_NIONIZING_SPECIES]; - double P[RT_NIONIZING_SPECIES]; - double ya[RT_NIONIZING_SPECIES]; - double yw[RT_NIONIZING_SPECIES]; - double y0[RT_NIONIZING_SPECIES]; - double y1[RT_NIONIZING_SPECIES]; -}; - -/*! Parameters needed to compute the stellar spectra integrals - - * the data needs to be encapsulated in a single struct to be passed - * as an argument for GSL integrators. */ -struct rt_spectrum_integration_params { - /* Which species are we dealing with? */ - int species; - /* Which spectrum type are we dealing with? */ - int spectrum_type; - /* Max frequency for const spectrum */ - double const_stellar_spectrum_frequency_max; - /* Temperature of blackbody in correct units */ - double T; - /* Boltzmann constant in correct units */ - double kB; - /* Planck constant in correct units */ - double h_planck; - /* speed of light in correct units */ - double c; - /* Values for the cross section parametrization */ - struct rt_photoion_cs_parameters *cs_params; -}; - -/** - * Initialize the parameters for the cross section computation in cgs, - * and return a fully and correctly initialized struct. - * The data is taken from Verner et al. 1996 - * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013 - * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R) - */ -static struct rt_photoion_cs_parameters rt_init_photoion_cs_params_cgs(void) { - - struct rt_photoion_cs_parameters photoion_cs_params_cgs = { - /* E_ion = {13.60, 24.59, 54.42} eV */ - /* E_zero = {0.4298, 0.1361, 1.720}, eV */ - /* E_ion = */ {2.179e-11, 3.940e-11, 8.719e-11}, /* erg */ - /* E_zero = */ {6.886e-13, 2.181 - 13, 2.756e-12}, /* erg */ - /* sigma_zero = */ {5.475e-14, 9.492e-16, 1.369e-14}, /* cm^-2 */ - /* P = */ {2.963, 3.188, 2.963}, - /* ya = */ {32.88, 1.469, 32.88}, - /* yw = */ {0., 2.039, 0.}, - /* y0 = */ {0., 0.4434, 0.}, - /* y1 = */ {0., 2.136, 0.}}; - - return photoion_cs_params_cgs; -} - -/** - * Compute the parametrized cross section for a given energy and species. - * The parametrization is taken from Verner et al. 1996 - * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013 - * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R) - * - * @param E energy for which to compute the cross section. - * @param species index of species, 0 < species < RT_NIONIZING_SPECIES - * @param params cross section parameters struct - */ -static double photoionization_cross_section( - const double E, const int species, - const struct rt_photoion_cs_parameters *params) { - - const double E0 = params->E_zero[species]; - const double E_ion = params->E_ion[species]; - const double y0 = params->y0[species]; - const double y1 = params->y1[species]; - const double yw = params->yw[species]; - const double ya = params->ya[species]; - const double P = params->P[species]; - const double sigma_0 = params->sigma_zero[species]; - - if (E < E_ion) return 0.; - - const double x = E / E0 - y0; - const double y = sqrt(x * x + y1 * y1); - const double temp1 = pow(y, 0.5 * P - 5.5); - const double temp2 = pow(1. + sqrt(y / ya), -P); - - return sigma_0 * ((x - 1.) * (x - 1.) + yw * yw) * temp1 * temp2; -} - -/** - * @brief compute the chosen spectrum for a given frequency nu. - * This function is indended to be used to integrate the spectra - * over a frequency range in order to obtain averaged ionization - * cross sections. - * - * @param nu frequency at which to evaluate spectrum - * @param params spectrum integration params. Needs to be of type - * void* for GSL integrators. - */ -static double rt_interaction_rates_get_spectrum(const double nu, void *params) { - - struct rt_spectrum_integration_params *pars = - (struct rt_spectrum_integration_params *)params; - - if (pars->spectrum_type == 0) { - /* Constant spectrum */ - if (nu <= pars->const_stellar_spectrum_frequency_max) { - return 1.; - } else { - return 0.; - } - } else if (pars->spectrum_type == 1) { - /* Blackbody spectrum */ - const double T = pars->T; - const double kB = pars->kB; - const double h_planck = pars->h_planck; - const double c = pars->c; - return blackbody_spectrum_intensity(nu, T, kB, h_planck, c); - } else { - error("Unknown stellar spectrum type selected: %d", pars->spectrum_type); - return 0.; - } -} - -/** - * Spectrum function to be integrated. - * This function is called by the GSL integrator. - * - * @param nu frequency at which to evaluate spectrum - * @param params spectrum integration params. Needs to be of type - * void* for GSL integrators. - */ -static double spectrum_integrand(double nu, void *params) { - return rt_interaction_rates_get_spectrum(nu, params); -} - -/** - * Spectrum times cross section function to be integrated. - * This function is called by the GSL integrator. - * - * @param nu frequency at which to evaluate spectrum - * @param params spectrum integration params. Needs to be of type - * void* for GSL integrators. - */ -static double spectrum_times_sigma_integrand(double nu, void *params) { - struct rt_spectrum_integration_params *p = - (struct rt_spectrum_integration_params *)params; - const double E = nu * p->h_planck; - const double sigma = - photoionization_cross_section(E, p->species, p->cs_params); - const double J = rt_interaction_rates_get_spectrum(nu, params); - return J * sigma; -} - -/** - * Spectrum times cross section divided by h*nu function to be integrated. - * This function is called by the GSL integrator. - * - * @param nu frequency at which to evaluate spectrum - * @param params spectrum integration params. Needs to be of type - * void* for GSL integrators. - */ -static double spectrum_times_sigma_over_hnu_integrand(double nu, void *params) { - struct rt_spectrum_integration_params *p = - (struct rt_spectrum_integration_params *)params; - const double E = nu * p->h_planck; - const double sigma = - photoionization_cross_section(E, p->species, p->cs_params); - const double J = rt_interaction_rates_get_spectrum(nu, params); - return J * sigma / E; -} +#include "rt_species.h" +#include "rt_thermochemistry_utils.h" /** - * Integrate a function from nu_start to nu_stop with GSL routines - * - * @param function function to integrate - * @param nu_start lower boundary of the integral - * @param nu_stop upper boundary of the integral - * @param params spectrum integration params. - * */ -static double rt_interaction_rates_integrate_gsl( - double (*function)(double, void *), double nu_start, double nu_stop, - int npoints, struct rt_spectrum_integration_params *params) { - - gsl_function F; - gsl_integration_workspace *w = gsl_integration_workspace_alloc(npoints); - double result, error; - - F.function = function; - F.params = (void *)params; - gsl_integration_qags(&F, nu_start, nu_stop, /*espabs=*/0., /*epsrel=*/1e-7, - npoints, w, &result, &error); - - return result; -} + * @file src/rt/GEAR/rt_interaction_rates.h + * @brief header file concerning photoionization and photoheating rates + **/ /** - * @brief allocate and pre-compute the averaged cross sections - * for each photon group and ionizing species. + * @brief compute the heating, ionization, and dissassociation rates + * for the particle radiation field as needed by grackle. * - * @param rt_props RT properties struct + * @param rates (return) Interaction rates for grackle. [0]: heating rate. + * [1]: HI ionization. [2]: HeI ionization. [3]: HeII ionization. + * [4]: H2 dissociation. + * @param energy_density energy densities of each photon group [internal units] + * @param species_densities physical densities of all species [internal units] + * @param average_photon_energy mean photon energy in group, in erg + * @param cse energy weighted photon interaction cross sections, in cm^2 + * @param csn number weighted photon interaction cross sections, in cm^2 * @param phys_const physical constants struct * @param us internal units struct **/ -static void rt_interaction_rates_init(struct rt_props *restrict rt_props, - const struct phys_const *restrict - phys_const, - const struct unit_system *restrict us) { - - /* Allocate the space to store the (cross section) integrals */ - /* --------------------------------------------------------- */ - double **cse = malloc(RT_NGROUPS * sizeof(double *)); - double **csn = malloc(RT_NGROUPS * sizeof(double *)); - for (int group = 0; group < RT_NGROUPS; group++) { - cse[group] = malloc(RT_NIONIZING_SPECIES * sizeof(double)); - csn[group] = malloc(RT_NIONIZING_SPECIES * sizeof(double)); - } - - double integral_E[RT_NGROUPS]; - double integral_sigma_E[RT_NGROUPS][RT_NIONIZING_SPECIES]; - double integral_sigma_E_over_hnu[RT_NGROUPS][RT_NIONIZING_SPECIES]; - - /* Prepare parameter struct for integration functions */ - /* -------------------------------------------------- */ - struct rt_spectrum_integration_params integration_params = {0, 0, 0., 0., - 0., 0., 0., NULL}; - integration_params.spectrum_type = rt_props->stellar_spectrum_type; - integration_params.const_stellar_spectrum_frequency_max = - rt_props->const_stellar_spectrum_max_frequency; - - /* Grab constants and conversions in cgs */ - const double T_cgs = rt_props->stellar_spectrum_blackbody_T * - units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); - integration_params.T = T_cgs; - - const float dimension_kB[5] = {1, 2, -2, 0, -1}; /* [g cm^2 s^-2 K^-1] */ - const double kB_to_cgs = - units_general_cgs_conversion_factor(us, dimension_kB); - const double kB_cgs = phys_const->const_boltzmann_k * kB_to_cgs; - integration_params.kB = kB_cgs; - - const float dimension_h[5] = {1, 2, -1, 0, 0}; /* [g cm^2 s^-1] */ - const double h_to_cgs = units_general_cgs_conversion_factor(us, dimension_h); - const double h_planck_cgs = phys_const->const_planck_h * h_to_cgs; - integration_params.h_planck = h_planck_cgs; - - const double c_cgs = phys_const->const_speed_light_c * +__attribute__((always_inline)) INLINE static void +rt_get_interaction_rates_for_grackle( + gr_float rates[5], float energy_density[RT_NGROUPS], + gr_float species_densities[6], + const double average_photon_energy[RT_NGROUPS], double **cse, double **csn, + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us) { + + rates[0] = 0.; /* Needs to be in [erg / s / cm^3 / nHI] for grackle. */ + rates[1] = 0.; /* [1 / time_units] */ + rates[2] = 0.; /* [1 / time_units] */ + rates[3] = 0.; /* [1 / time_units] */ + rates[4] = 0.; /* [1 / time_units] */ + + double E_ion_cgs[rt_ionizing_species_count]; + rt_species_get_ionizing_energy(E_ion_cgs); + + /* Get some conversions and constants first. */ + const double c_cgs = rt_params.reduced_speed_of_light * units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); - integration_params.c = c_cgs; - - struct rt_photoion_cs_parameters cs_params = rt_init_photoion_cs_params_cgs(); - integration_params.cs_params = &cs_params; + const double to_energy_density_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_DENSITY); + const double inv_time_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME); - /* Compute integrals */ - /* ----------------- */ + /* get species number densities in cgs */ + double ns_cgs[rt_ionizing_species_count]; /* in cm^-3 */ + rt_tchem_get_ionizing_species_number_densities(ns_cgs, species_densities, + phys_const, us); - /* Get start and end points of the integrals */ - double nu_stop_final; - if (rt_props->stellar_spectrum_type == 0) { - nu_stop_final = rt_props->const_stellar_spectrum_max_frequency; - } else if (rt_props->stellar_spectrum_type == 1) { - nu_stop_final = 10. * blackbody_peak_frequency(integration_params.T, - integration_params.kB, - integration_params.h_planck); - } else { - error("Unknown stellar spectrum type %d", rt_props->stellar_spectrum_type); + /* store photoionization rate for each species here */ + double ionization_rates[rt_ionizing_species_count]; + for (int s = 0; s < rt_ionizing_species_count; s++) { + ionization_rates[s] = 0.; } - double nu_start[RT_NGROUPS]; - double nu_stop[RT_NGROUPS]; - for (int group = 0; group < RT_NGROUPS; group++) - nu_start[group] = rt_props->photon_groups[group]; - for (int group = 0; group < RT_NGROUPS - 1; group++) - nu_stop[group] = rt_props->photon_groups[group + 1]; - - nu_start[0] = 1e-20; /* don't start at exactly 0 to avoid unlucky divisions */ - nu_stop[RT_NGROUPS - 1] = nu_stop_final; + for (int g = 0; g < RT_NGROUPS; g++) { - for (int group = 0; group < RT_NGROUPS; group++) { - /* This is independent of species. */ - integral_E[group] = rt_interaction_rates_integrate_gsl( - spectrum_integrand, nu_start[group], nu_stop[group], - RT_INTEGRAL_NPOINTS, &integration_params); + /* Sum results for this group over all species */ + double heating_rate_group_cgs = 0.; + const double Eg = energy_density[g] * to_energy_density_cgs; + const double Emean_g = average_photon_energy[g]; + const double Ng = (Emean_g > 0.) ? Eg / Emean_g : 0.; - for (int species = 0; species < RT_NIONIZING_SPECIES; species++) { - integration_params.species = species; - integral_sigma_E[group][species] = rt_interaction_rates_integrate_gsl( - spectrum_times_sigma_integrand, nu_start[group], nu_stop[group], - RT_INTEGRAL_NPOINTS, &integration_params); - integral_sigma_E_over_hnu[group][species] = - rt_interaction_rates_integrate_gsl( - spectrum_times_sigma_over_hnu_integrand, nu_start[group], - nu_stop[group], RT_INTEGRAL_NPOINTS, &integration_params); + for (int s = 0; s < rt_ionizing_species_count; s++) { + /* All quantities here are in cgs. */ + heating_rate_group_cgs += + (cse[g][s] * Emean_g - E_ion_cgs[s] * csn[g][s]) * ns_cgs[s]; + ionization_rates[s] += csn[g][s] * Ng * c_cgs; } + rates[0] += heating_rate_group_cgs * Ng * c_cgs; } - /* Now compute the actual average cross sections */ - /* --------------------------------------------- */ - for (int group = 0; group < RT_NGROUPS; group++) { - for (int spec = 0; spec < RT_NIONIZING_SPECIES; spec++) { - if (integral_E[group] > 0.) { - cse[group][spec] = integral_sigma_E[group][spec] / integral_E[group]; - csn[group][spec] = - integral_sigma_E_over_hnu[group][spec] / integral_E[group]; - } else { - /* No radiation = no interaction */ - cse[group][spec] = 0.; - csn[group][spec] = 0.; - } - } + /* Convert into correct units. */ + const double nHI = ns_cgs[rt_ionizing_species_HI]; + if (nHI > 0.) + rates[0] /= nHI; + else + rates[0] = 0.; + + for (int s = 0; s < rt_ionizing_species_count; s++) { + ionization_rates[s] /= inv_time_cgs; /* internal units T^-1 */ +#ifdef SWIFT_RT_DEBUG_CHECKS + if (ionization_rates[s] < 0.) + error("unphysical ion rate spec %d - %.4g", s, ionization_rates[s]); +#endif } - /* Store the results */ - /* ----------------- */ - rt_props->energy_weighted_cross_sections = cse; - rt_props->number_weighted_cross_sections = csn; + /* We're done. Write the results in correct place */ + rates[1] = ionization_rates[0]; + rates[2] = ionization_rates[1]; + rates[3] = ionization_rates[2]; + /* rates[4] = skipped for now */ + +#ifdef SWIFT_RT_DEBUG_CHECKS + for (int i = 0; i < 5; i++) { + if (rates[i] < 0.) error("unphysical rate %d %.4g", i, rates[i]); + } +#endif } /** - * @brief compute the heating, ionization, and dissassociation rates - * for the particle radiation field as needed by grackle, and the - * net absorption/emission rates for each photon group + * @brief compute the rates at which the photons get absorbed/destroyed + * during interactions with gas. * - * @param rates (return) Interaction rates for grackle. [0]: heating rate. - * [1]: HI ionization. [2]: HeI ionization. [3]: HeII ionization. - * [4]: H2 dissociation. - * @param heating_rates_by_group (return) net absorption/emission rates of each - * photon frequency group. - * @param p particle to work on + * @param absorption_rates (return) the energy absorption rates in + * internal units for each photon group. * @param species_densities the physical densities of all traced species - * @param rt_props RT properties struct + *[internal units] + * @param average_photon_energy mean photon energy in group, in erg + * @param csn number weighted photon interaction cross sections, in cm^2 * @param phys_const physical constants struct * @param us internal units struct - * @param cosmo cosmology struct **/ -__attribute__((always_inline)) INLINE static void -rt_tchem_get_interaction_rates(gr_float rates[5], - float heating_rates_by_group[RT_NGROUPS], - struct part *restrict p, - gr_float species_densities[6], - const struct rt_props *restrict rt_props, - const struct phys_const *restrict phys_const, - const struct unit_system *restrict us, - const struct cosmology *restrict cosmo) { +__attribute__((always_inline)) INLINE static void rt_get_absorption_rates( + double absorption_rates[RT_NGROUPS], gr_float species_densities[6], + const double average_photon_energy[RT_NGROUPS], double **csn, + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us) { - rates[0] = 0.; - rates[1] = 0.; - rates[2] = 0.; - rates[3] = 0.; - rates[4] = 0.; - for (int group = 0; group < RT_NGROUPS; group++) { - heating_rates_by_group[group] = 0.; - } + for (int g = 0; g < RT_NGROUPS; g++) absorption_rates[g] = 0.; - /* "copy" ionization energies from cross section parameters */ - struct rt_photoion_cs_parameters cs_params = rt_init_photoion_cs_params_cgs(); - const double *E_ion = cs_params.E_ion; + double E_ion_cgs[rt_ionizing_species_count]; + rt_species_get_ionizing_energy(E_ion_cgs); - /* Integrate energy spectra and cross sections assuming blackbody spectra - * to obtain estimate for effective cross sections, then use the actual - * energies present to get the rates */ - /* TODO: check whether we shouldn't be using actual speed of light here */ + /* Get some conversions and constants first. */ const double c_cgs = rt_params.reduced_speed_of_light * units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); - const double to_erg = units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); - - /* First, get species number densities and number densities - * in units of neutral hydrogen number density. */ - double m_p = phys_const->const_proton_mass; - double species_number_densities_cgs[RT_NIONIZING_SPECIES]; /* in cm^-3 */ - double species_number_densities_nHI[RT_NIONIZING_SPECIES]; /* in nHI^-1 */ - const double to_inv_volume = - units_cgs_conversion_factor(us, UNIT_CONV_INV_VOLUME); - const double to_number_density_cgs = to_inv_volume / m_p; - /* neutral hydrogen */ - species_number_densities_cgs[0] = - species_densities[0] * to_number_density_cgs; - species_number_densities_nHI[0] = 1.; - /* neutral helium */ - species_number_densities_cgs[1] = - 0.25 * species_densities[2] * to_number_density_cgs; - species_number_densities_nHI[1] = - 0.25 * species_densities[2] / species_densities[0]; - /* singly ionized helium */ - species_number_densities_cgs[2] = - 0.25 * species_densities[3] * to_number_density_cgs; - species_number_densities_nHI[2] = - 0.25 * species_densities[3] / species_densities[0]; - const double inv_time_cgs = units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME); - /* For the grackle photoionization, we need to - * keep track of the rates for each species. - * For the heating rate, we need to sum up all species. - * To remove the correct amount of energy from the - * radiation fields, we additionally need to keep track - * rates from each photon group. */ + double ns_cgs[rt_ionizing_species_count]; /* in cm^-3 */ + rt_tchem_get_ionizing_species_number_densities(ns_cgs, species_densities, + phys_const, us); - /* store photoionization rate for each species here */ - double ionization_rates_by_species[RT_NIONIZING_SPECIES]; - for (int spec = 0; spec < RT_NIONIZING_SPECIES; spec++) - ionization_rates_by_species[spec] = 0.; - - for (int group = 0; group < RT_NGROUPS; group++) { - - /* Sum results for this group over all species */ - double heating_rate_group_nHI = 0.; - double heating_rate_group_cgs = 0.; - /* NOTE: we need energy density here, not energy. - * But energy *density* hasn't been updated at this point. */ - float energy_density_i_cgs = p->rt_data.conserved[group].energy / - p->geometry.volume * to_erg * to_inv_volume; - - for (int spec = 0; spec < RT_NIONIZING_SPECIES; spec++) { - const double cse = rt_props->energy_weighted_cross_sections[group][spec]; - const double csn = rt_props->number_weighted_cross_sections[group][spec]; - - heating_rate_group_nHI += - (cse - E_ion[spec] * csn) * species_number_densities_nHI[spec]; - heating_rate_group_cgs += - (cse - E_ion[spec] * csn) * species_number_densities_cgs[spec]; - ionization_rates_by_species[spec] += - energy_density_i_cgs * cse * species_number_densities_cgs[spec] * - c_cgs / inv_time_cgs; /* internal units T^-1 */ + for (int g = 0; g < RT_NGROUPS; g++) { + for (int s = 0; s < rt_ionizing_species_count; s++) { + absorption_rates[g] += c_cgs * csn[g][s] * ns_cgs[s]; } - - /* Store total heating rate for grackle */ - rates[0] += heating_rate_group_nHI * c_cgs * energy_density_i_cgs; - /* Store rates for each group in internal units WITHOUT THE ENERGY DENSITY - * TERM */ - heating_rates_by_group[group] += - heating_rate_group_cgs * c_cgs / inv_time_cgs; } - /* We're done. Write the results in correct place */ - rates[1] = ionization_rates_by_species[0]; - rates[2] = ionization_rates_by_species[1]; - rates[3] = ionization_rates_by_species[2]; + for (int g = 0; g < RT_NGROUPS; g++) { + absorption_rates[g] /= inv_time_cgs; +#ifdef SWIFT_RT_DEBUG_CHECKS + if (absorption_rates[g] < 0.) + error("unphysical rate %d - %.4g", g, absorption_rates[g]); +#endif + } } #endif /* SWIFT_RT_GEAR_INTERACION_RATES_H */ diff --git a/src/rt/GEAR/rt_io.h b/src/rt/GEAR/rt_io.h index 7e5a962162e6ef93e6843a68c6f98f2a5f8cbd12..49d1096b61d29549612e345655aa4483de81b4a5 100644 --- a/src/rt/GEAR/rt_io.h +++ b/src/rt/GEAR/rt_io.h @@ -46,12 +46,13 @@ INLINE static int rt_read_particles(const struct part* parts, sprintf(fieldname, "PhotonEnergiesGroup%d", phg + 1); list[count++] = io_make_input_field(fieldname, FLOAT, 1, OPTIONAL, UNIT_CONV_ENERGY, - parts, rt_data.conserved[phg].energy); + parts, rt_data.radiation[phg].energy_density); sprintf(fieldname, "PhotonFluxesGroup%d", phg + 1); list[count++] = io_make_input_field(fieldname, FLOAT, 3, OPTIONAL, - UNIT_CONV_RADIATION_FLUX, parts, - rt_data.conserved[phg].flux); + UNIT_CONV_ENERGY_VELOCITY, parts, + rt_data.radiation[phg].flux); } + list[count++] = io_make_input_field("MassFractionHI", FLOAT, 1, OPTIONAL, UNIT_CONV_NO_UNITS, parts, rt_data.tchem.mass_fraction_HI); @@ -85,37 +86,54 @@ INLINE static int rt_read_stars(const struct spart* sparts, } /** - * @brief Extract photon energies of conserved struct for all photon groups + * @brief Extract radiation energies of radiation struct for all photon groups * Note: "allocation" of `float* ret` happens in io_copy_temp_buffer() + * + * @param engine the engine + * @param part the particle to extract data from + * @param xpart the according xpart to extract data from + * @param ret (return) the extracted data */ -INLINE static void rt_convert_conserved_photon_energies( - const struct engine* engine, const struct part* part, - const struct xpart* xpart, float* ret) { +INLINE static void rt_convert_radiation_energies(const struct engine* engine, + const struct part* part, + const struct xpart* xpart, + float* ret) { for (int g = 0; g < RT_NGROUPS; g++) { - ret[g] = part->rt_data.conserved[g].energy; + ret[g] = part->rt_data.radiation[g].energy_density * part->geometry.volume; } } /** - * @brief Extract photon energies of conserved struct for all photon groups + * @brief Extract radiation fluxes of radiation struct for all photon groups * Note: "allocation" of `float* ret` happens in io_copy_temp_buffer() + * + * @param engine the engine + * @param part the particle to extract data from + * @param xpart the according xpart to extract data from + * @param ret (return) the extracted data */ -INLINE static void rt_convert_conserved_photon_fluxes( - const struct engine* engine, const struct part* part, - const struct xpart* xpart, float* ret) { +INLINE static void rt_convert_radiation_fluxes(const struct engine* engine, + const struct part* part, + const struct xpart* xpart, + float* ret) { int i = 0; for (int g = 0; g < RT_NGROUPS; g++) { - ret[i++] = part->rt_data.conserved[g].flux[0]; - ret[i++] = part->rt_data.conserved[g].flux[1]; - ret[i++] = part->rt_data.conserved[g].flux[2]; + ret[i++] = part->rt_data.radiation[g].flux[0]; + ret[i++] = part->rt_data.radiation[g].flux[1]; + ret[i++] = part->rt_data.radiation[g].flux[2]; } } /** * @brief Extract mass fractions of constituent species from tchem struct. * Note: "allocation" of `float* ret` happens in io_copy_temp_buffer() + * + * @param engine the engine + * @param part the particle to extract data from + * @param xpart the according xpart to extract data from + * @param ret (return) the extracted data */ INLINE static void rt_convert_mass_fractions(const struct engine* engine, const struct part* part, @@ -132,6 +150,11 @@ INLINE static void rt_convert_mass_fractions(const struct engine* engine, /** * @brief Creates additional output fields for the radiative * transfer data of hydro particles. + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_particles(const struct part* parts, struct io_props* list) { @@ -140,11 +163,11 @@ INLINE static int rt_write_particles(const struct part* parts, list[0] = io_make_output_field_convert_part( "PhotonEnergies", FLOAT, RT_NGROUPS, UNIT_CONV_ENERGY, 0, parts, - /*xparts=*/NULL, rt_convert_conserved_photon_energies, + /*xparts=*/NULL, rt_convert_radiation_energies, "Photon Energies (all groups)"); list[1] = io_make_output_field_convert_part( "PhotonFluxes", FLOAT, 3 * RT_NGROUPS, UNIT_CONV_RADIATION_FLUX, 0, parts, - /*xparts=*/NULL, rt_convert_conserved_photon_fluxes, + /*xparts=*/NULL, rt_convert_radiation_fluxes, "Photon Fluxes (all groups; x, y, and z coordinates)"); list[2] = io_make_output_field_convert_part( "IonMassFractions", FLOAT, 5, UNIT_CONV_NO_UNITS, 0, parts, @@ -184,11 +207,9 @@ INLINE static int rt_write_particles(const struct part* parts, "RTDebugRadAbsorbedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, parts, rt_data.debug_radiation_absorbed_tot, "Radiation absorbed by this part during its lifetime"); - list[10] = io_make_output_field("RTDebugStarsInjectPrepTotCounts", ULONGLONG, - 1, UNIT_CONV_NO_UNITS, 0, parts, - rt_data.debug_iact_stars_inject_prep_tot, - "Total interactions with stars during " - "injection prep during its lifetime"); + list[10] = io_make_output_field( + "RTDebugSubcycles", INT, 1, UNIT_CONV_NO_UNITS, 0, parts, + rt_data.debug_nsubcycles, "How many times this part was subcycled"); #endif return num_elements; @@ -197,6 +218,11 @@ INLINE static int rt_write_particles(const struct part* parts, /** * @brief Creates additional output fields for the radiative * transfer data of star particles. + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_stars(const struct spart* sparts, struct io_props* list) { @@ -204,22 +230,21 @@ INLINE static int rt_write_stars(const struct spart* sparts, #ifdef SWIFT_RT_DEBUG_CHECKS num_elements += 4; - list[0] = io_make_output_field( + list[0] = io_make_output_field("RTDebugHydroIact", INT, 1, UNIT_CONV_NO_UNITS, + 0, sparts, rt_data.debug_iact_hydro_inject, + "number of interactions between this star " + "particle and any particle during injection"); + list[1] = io_make_output_field( "RTDebugEmissionRateSet", INT, 1, UNIT_CONV_NO_UNITS, 0, sparts, rt_data.debug_emission_rate_set, "Stellar photon emission rates set?"); - list[1] = io_make_output_field( + list[2] = io_make_output_field( "RTDebugRadEmittedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, sparts, rt_data.debug_radiation_emitted_tot, "Total radiation emitted during the lifetime of this star"); - list[2] = io_make_output_field("RTDebugInjectedPhotonEnergy", FLOAT, + list[3] = io_make_output_field("RTDebugInjectedPhotonEnergy", FLOAT, RT_NGROUPS, UNIT_CONV_ENERGY, 0, sparts, rt_data.debug_injected_energy_tot, "Total radiation actually injected into gas"); - list[3] = io_make_output_field("RTDebugHydroInjectPrepCountsTot", ULONGLONG, - 1, UNIT_CONV_NO_UNITS, 0, sparts, - rt_data.debug_iact_hydro_inject_prep_tot, - "Total interactions with particles during " - "injection prep during its lifetime"); #endif return num_elements; @@ -245,12 +270,8 @@ INLINE static void rt_write_flavour(hid_t h_grp, hid_t h_grp_columns, /* Write scheme name */ /* ----------------- */ - if (rtp->hydro_controlled_injection) { - io_write_attribute_s(h_grp, "RT Scheme", - RT_IMPLEMENTATION ", hydro controlled injection"); - } else { - io_write_attribute_s(h_grp, "RT Scheme", RT_IMPLEMENTATION); - } + io_write_attribute_s(h_grp, "RT Scheme", RT_IMPLEMENTATION); + io_write_attribute_s(h_grp, "RT Riemann Solver", RT_RIEMANN_SOLVER_NAME); /* Write photon group counts */ /* ------------------------- */ diff --git a/src/rt/GEAR/rt_ionization_equilibrium.h b/src/rt/GEAR/rt_ionization_equilibrium.h index 376be5e5013f07d90f13028fa86f296615247272..21a51844b143e7584a8d5900cf9f2f6b5c215e41 100644 --- a/src/rt/GEAR/rt_ionization_equilibrium.h +++ b/src/rt/GEAR/rt_ionization_equilibrium.h @@ -49,19 +49,22 @@ rt_ion_equil_mass_fractions_from_T(double T, float X, float Y, float* XHI, float* XHII, float* XHeI, float* XHeII, float* XHeIII) { + if (fabsf(X + Y - 1.f) > 1e-4) + error("mass fractions don't add up to one: X=%.3e, Y=%.3e", X, Y); + /* Total number densities for all H and He species, respectively. * We don't really care about the actual number densities of the * species, but only want the mass fractions. So all quantities * will be per unit volume, specifically per rho_gas / m_proton. */ - float nH, nHe; + double nH, nHe; /* TODO: this assumes no metals. */ - if (X == 0.f) { - nH = 0.f; - nHe = 1.f; + if (X <= RT_GEAR_TINY_MASS_FRACTION) { + nH = RT_GEAR_TINY_MASS_FRACTION; + nHe = 1.; } else { nH = X; - nHe = 0.25f * Y; + nHe = 0.25 * Y; } /* Number densities of the actual species */ @@ -117,20 +120,25 @@ rt_ion_equil_mass_fractions_from_T(double T, float X, float Y, float* XHI, /* With the number densities per unit volume given, * let's compute the mass fractions now. */ - const float mHI = nHI; - const float mHII = nHII; - const float mHeI = 4.0f * nHeI; - const float mHeII = 4.0f * nHeII; - const float mHeIII = 4.0f * nHeIII; + const double mHI = nHI; + const double mHII = nHII; + const double mHeI = 4.0f * nHeI; + const double mHeII = 4.0f * nHeII; + const double mHeIII = 4.0f * nHeIII; /* we ignore the electron mass fraction. */ - const float mtot_inv = 1.f / (mHI + mHII + mHeI + mHeII + mHeIII); - - *XHI = mHI * mtot_inv; - *XHII = mHII * mtot_inv; - *XHeI = mHeI * mtot_inv; - *XHeII = mHeII * mtot_inv; - *XHeIII = mHeIII * mtot_inv; + const double mtot_inv = 1. / (mHI + mHII + mHeI + mHeII + mHeIII); + + *XHI = (float)(mHI * mtot_inv); + *XHI = max(*XHI, RT_GEAR_TINY_MASS_FRACTION); + *XHII = (float)(mHII * mtot_inv); + *XHII = max(*XHII, RT_GEAR_TINY_MASS_FRACTION); + *XHeI = (float)(mHeI * mtot_inv); + *XHeI = max(*XHeI, RT_GEAR_TINY_MASS_FRACTION); + *XHeII = (float)(mHeII * mtot_inv); + *XHeII = max(*XHeII, RT_GEAR_TINY_MASS_FRACTION); + *XHeIII = (float)(mHeIII * mtot_inv); + *XHeIII = max(*XHeIII, RT_GEAR_TINY_MASS_FRACTION); } /** @@ -143,6 +151,7 @@ rt_ion_equil_mass_fractions_from_T(double T, float X, float Y, float* XHI, * @param XHeIII (return) mass fraction of HeII * @param p part to work with * @param rt_props rt_properties struct + * @param hydro_props hydro properties struct * @param phys_const physical constants struct * @param us unit system struct * @param cosmo cosmology struct @@ -152,6 +161,7 @@ rt_ion_equil_get_mass_fractions(float* XHI, float* XHII, float* XHeI, float* XHeII, float* XHeIII, struct part* restrict p, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const struct cosmology* restrict cosmo) { @@ -167,8 +177,12 @@ rt_ion_equil_get_mass_fractions(float* XHI, float* XHII, float* XHeI, const double mp_cgs = phys_const->const_proton_mass * mp_to_cgs; /* Get the specific internal energy of the gas */ - const double u_expect = hydro_get_drifted_physical_internal_energy(p, cosmo) * - internal_energy_to_cgs; + const float u_minimal = hydro_props->minimal_internal_energy; + /* Using 'drifted' version here because I'm lazy and don't want to pass + * the xpart down to use in this function. */ + const float u_part = hydro_get_drifted_physical_internal_energy(p, cosmo); + const double u_expect = + ((double)max(u_minimal, u_part)) * internal_energy_to_cgs; double mu_guess, T_guess; /* Get a first estimate for gas temperature. */ diff --git a/src/rt/GEAR/rt_parameters.h b/src/rt/GEAR/rt_parameters.h index b448e78eec934a622e7a653f195668f0a0ee06db..a25f0011049a65ef19093f1cbc9084a28e0db141 100644 --- a/src/rt/GEAR/rt_parameters.h +++ b/src/rt/GEAR/rt_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 diff --git a/src/rt/GEAR/rt_properties.h b/src/rt/GEAR/rt_properties.h index daf447c0430170e0a50efbaf60ece9cdbc1883d3..a9226cc7568a0458d8cbe8998c9c2a216c333d13 100644 --- a/src/rt/GEAR/rt_properties.h +++ b/src/rt/GEAR/rt_properties.h @@ -19,9 +19,12 @@ #ifndef SWIFT_RT_PROPERTIES_GEAR_H #define SWIFT_RT_PROPERTIES_GEAR_H +#include "rt_grackle_utils.h" +#include "rt_interaction_rates.h" #include "rt_parameters.h" +#include "rt_stellar_emission_model.h" -#include <grackle.h> +#include <string.h> /** * @file src/rt/GEAR/rt_properties.h @@ -29,40 +32,25 @@ * properties. */ -/** - * @brief allocate and pre-compute the averaged cross sections - * for each photon group and ionizing species. - * Declare this here to avoid cyclical inclusions. - * - * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param us internal units struct - **/ -static void rt_interaction_rates_init(struct rt_props* restrict rt_props, - const struct phys_const* restrict - phys_const, - const struct unit_system* restrict us); +#define RT_IMPLEMENTATION "GEAR M1closure" + +#if defined(RT_RIEMANN_SOLVER_GLF) +#define RT_RIEMANN_SOLVER_NAME "GLF Riemann Solver" +#elif defined(RT_RIEMANN_SOLVER_HLL) +#define RT_RIEMANN_SOLVER_NAME "HLL Riemann Solver" +#else +#error "No valid choice of RT Riemann solver has been selected" +#endif /** * @brief Properties of the 'GEAR' radiative transfer model */ struct rt_props { - /* Are we running with hydro or star controlled injection? - * This is added to avoid #ifdef macros as far as possible */ - int hydro_controlled_injection; - - /* Do we need to run a conversion after the zeroth - * step, but before the first step? */ - int convert_stars_after_zeroth_step; - int convert_parts_after_zeroth_step; + /* Which stellar emission model to use */ + enum rt_stellar_emission_models stellar_emission_model; - /* Are we using constant stellar emission rates? */ - int use_const_emission_rates; - - /* Frequency bin edges for photon groups - * Includes 0 as leftmost edge, doesn't include infinity as - * rightmost bin edge*/ + /* (Lower) frequency bin edges for photon groups */ float photon_groups[RT_NGROUPS]; /* Global constant stellar emission rates */ @@ -71,6 +59,9 @@ struct rt_props { /* CFL condition */ float CFL_condition; + /* Factor to limit cooling time by */ + float f_limit_cooling_time; + /* do we set initial ionization mass fractions manually? */ int set_initial_ionization_mass_fractions; int set_equilibrium_initial_ionization_mass_fractions; @@ -91,6 +82,10 @@ struct rt_props { /* Skip thermochemistry? For testing/debugging only! */ int skip_thermochemistry; + /* Re-do thermochemistry recursively if difference in internal energy is too + * big? */ + int max_tchem_recursion; + /* Optionally restrict maximal timestep for stars */ float stars_max_timestep; @@ -102,8 +97,13 @@ struct rt_props { double stellar_spectrum_blackbody_T; /* Storage for integrated photoionization cross sections */ + /* Note: they are always in cgs. */ double** energy_weighted_cross_sections; double** number_weighted_cross_sections; + /* Mean photon energy in frequency bin for user provided spectrum. In erg.*/ + double average_photon_energy[RT_NGROUPS]; + /* Integral over photon numbers of user provided spectrum. */ + double photon_number_integral[RT_NGROUPS]; /* Grackle Stuff */ /* ------------- */ @@ -114,20 +114,22 @@ struct rt_props { /*! grackle chemistry data */ chemistry_data grackle_chemistry_data; + /* use case B recombination? */ + int case_B_recombination; + + /* make grackle talkative? */ + int grackle_verbose; + + /* TODO: cleanup later with all other grackle stuff */ /*! grackle chemistry data storage * (needed for local function calls) */ - chemistry_data_storage* grackle_chemistry_rates; + /* chemistry_data_storage* grackle_chemistry_rates; */ #ifdef SWIFT_RT_DEBUG_CHECKS - /* Do extended tests where we assume that all parts - * have spart neighbours? */ - /* skip this for GEAR */ - /* int debug_do_all_parts_have_stars_checks; */ - /* radiation emitted by stars this step. This is not really a property, * but a placeholder to sum up a global variable. It's being reset * every timestep. */ - int debug_radiation_emitted_this_step; + unsigned long long debug_radiation_emitted_this_step; /* total radiation emitted by stars. This is not really a property, * but a placeholder to sum up a global variable */ @@ -135,107 +137,36 @@ struct rt_props { /* radiation absorbed by gas this step. This is not really a property, * but a placeholder to sum up a global variable */ - int debug_radiation_absorbed_this_step; + unsigned long long debug_radiation_absorbed_this_step; /* total radiation absorbed by gas. This is not really a property, * but a placeholder to sum up a global variable */ unsigned long long debug_radiation_absorbed_tot; - /* Interactions of a star with gas during injection prep this step. This is - * not really a property, but a placeholder to sum up a global variable */ - int debug_star_injection_prep_iacts_with_parts_this_step; - - /* Interactions of a star with gas during injection prep. This is not - * really a property, but a placeholder to sum up a global variable */ - unsigned long long debug_star_injection_prep_iacts_with_parts_tot; - - /* Interactions of a star with gas during injection prep this step. This is - * not really a property, but a placeholder to sum up a global variable */ - int debug_part_injection_prep_iacts_with_stars_this_step; - - /* Interactions of a star with gas during injection prep. This is not - * really a property, but a placeholder to sum up a global variable */ - unsigned long long debug_part_injection_prep_iacts_with_stars_tot; - - /* Total radiation energy in the gas. It's being reset every step. */ - float debug_total_radiation_conserved_energy[RT_NGROUPS]; - float debug_total_radiation_energy_density[RT_NGROUPS]; - float debug_total_star_emitted_energy[RT_NGROUPS]; - - /* Files to write energy budget to after every step */ - FILE* conserved_energy_filep; - FILE* energy_density_filep; - FILE* star_emitted_energy_filep; + /* Max number of subcycles per hydro step */ + int debug_max_nr_subcycles; #endif }; +/* Some declarations to avoid cyclical inclusions. */ +/* ----------------------------------------------- */ +/* Keep the declarations for *after* the definition of rt_props struct */ + /** - * @brief initialize grackle during rt_props_init + * @brief allocate and pre-compute the averaged cross sections + * for each photon group and ionizing species. + * Declare this here to avoid cyclical inclusions. * - * @param rtp #rt_props struct - * @param us #unit_system struct + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param us internal units struct **/ -__attribute__((always_inline)) INLINE static void rt_props_init_grackle( - struct rt_props* rtp, const struct unit_system* us) { - - /* Initialize units */ - /* ---------------- */ - /* we assume all quantities to be physical, not comoving */ - rtp->grackle_units.a_units = 1.0; - rtp->grackle_units.a_value = 1.0; - rtp->grackle_units.comoving_coordinates = 0; - rtp->grackle_units.density_units = - units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); - rtp->grackle_units.length_units = - units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); - rtp->grackle_units.time_units = - units_cgs_conversion_factor(us, UNIT_CONV_TIME); - rtp->grackle_units.velocity_units = - units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); - - /* Chemistry Parameters */ - /* -------------------- */ - /* More details on https://grackle.readthedocs.io/en/latest/Parameters.html */ - - rtp->grackle_chemistry_data = _set_default_chemistry_parameters(); - /* chemistry on */ - rtp->grackle_chemistry_data.use_grackle = 1; - /* cooling on */ - /* NOTE: without cooling on, it also won't heat... */ - rtp->grackle_chemistry_data.with_radiative_cooling = 1; - /* 6 species atomic H and He. */ - rtp->grackle_chemistry_data.primordial_chemistry = 1; - /* dust processes */ - rtp->grackle_chemistry_data.dust_chemistry = 0; - /* H2 formation on dust */ - rtp->grackle_chemistry_data.h2_on_dust = 0; - /* metal cooling (uses Cloudy) off (for now) */ - rtp->grackle_chemistry_data.metal_cooling = 0; - /* no cooling below CMB temperature */ - rtp->grackle_chemistry_data.cmb_temperature_floor = 1; - /* UV background off */ - rtp->grackle_chemistry_data.UVbackground = 0; - /* data file - currently not used. */ - rtp->grackle_chemistry_data.grackle_data_file = ""; - /* adiabatic index */ - rtp->grackle_chemistry_data.Gamma = hydro_gamma; - /* we'll provide grackle with ionization and heating rates from RT */ - rtp->grackle_chemistry_data.use_radiative_transfer = 1; - /* fraction by mass of Hydrogen in the metal-free portion of the gas */ - rtp->grackle_chemistry_data.HydrogenFractionByMass = - rtp->hydrogen_mass_fraction; - - /* Initialize the chemistry_data_storage object to be - * able to use local functions */ - chemistry_data_storage* chem_data_storage = - malloc(sizeof(chemistry_data_storage)); - rtp->grackle_chemistry_rates = chem_data_storage; - if (!_initialize_chemistry_data(&rtp->grackle_chemistry_data, - rtp->grackle_chemistry_rates, - &rtp->grackle_units)) { - error("Error in _initialize_chemistry_data"); - } -} +void rt_cross_sections_init(struct rt_props* restrict rt_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us); + +/* Now for the good stuff */ +/* ------------------------------------- */ /** * @brief Print the RT model. @@ -249,16 +180,17 @@ __attribute__((always_inline)) INLINE static void rt_props_print( if (engine_rank != 0) return; message("Radiative transfer scheme: '%s'", RT_IMPLEMENTATION); - char messagestring[200] = "Using photon frequency bins: [0."; + message("RT Riemann Solver used: '%s'", RT_RIEMANN_SOLVER_NAME); + char messagestring[200] = "Using photon frequency bins: [ "; char freqstring[20]; - for (int g = 1; g < RT_NGROUPS; g++) { - sprintf(freqstring, ", %.3g", rtp->photon_groups[g]); + for (int g = 0; g < RT_NGROUPS; g++) { + sprintf(freqstring, "%.3g ", rtp->photon_groups[g]); strcat(messagestring, freqstring); } strcat(messagestring, "]"); message("%s", messagestring); - if (rtp->use_const_emission_rates) { + if (rtp->stellar_emission_model == rt_stellar_emission_model_const) { strcpy(messagestring, "Using constant stellar emission rates: [ "); for (int g = 0; g < RT_NGROUPS; g++) { sprintf(freqstring, "%.3g ", rtp->stellar_const_emission_rates[g]); @@ -266,6 +198,11 @@ __attribute__((always_inline)) INLINE static void rt_props_print( } strcat(messagestring, "]"); message("%s", messagestring); + } else if (rtp->stellar_emission_model == + rt_stellar_emission_model_IlievTest) { + message("Using Iliev+06 Test 4 stellar emission model."); + } else { + error("Unknown stellar emission model %d", rtp->stellar_emission_model); } if (rtp->set_equilibrium_initial_ionization_mass_fractions) @@ -293,21 +230,8 @@ __attribute__((always_inline)) INLINE static void rt_props_init( const struct unit_system* us, struct swift_params* params, struct cosmology* cosmo) { -#ifdef RT_HYDRO_CONTROLLED_INJECTION - rtp->hydro_controlled_injection = 1; -#else - rtp->hydro_controlled_injection = 0; -#endif - /* Make sure we reset debugging counters correctly after * zeroth step. */ -#ifdef SWIFT_RT_DEBUG_CHECKS - rtp->convert_parts_after_zeroth_step = 1; - rtp->convert_stars_after_zeroth_step = 1; -#else - rtp->convert_parts_after_zeroth_step = 0; - rtp->convert_stars_after_zeroth_step = rtp->hydro_controlled_injection; -#endif /* Read in photon frequency group properties */ /* ----------------------------------------- */ @@ -316,41 +240,66 @@ __attribute__((always_inline)) INLINE static void rt_props_init( "You need to run GEAR-RT with at least 1 photon group, " "you have %d", RT_NGROUPS); - } else if (RT_NGROUPS == 1) { - rtp->photon_groups[0] = 0.f; } else { - float frequencies[RT_NGROUPS - 1]; /* !! Keep the frequencies in Hz for now. !! */ - parser_get_param_float_array(params, "GEARRT:photon_groups_Hz", - RT_NGROUPS - 1, frequencies); - for (int g = 0; g < RT_NGROUPS - 1; g++) { - rtp->photon_groups[g + 1] = frequencies[g]; - } - rtp->photon_groups[0] = 0.f; + parser_get_param_float_array(params, "GEARRT:photon_groups_Hz", RT_NGROUPS, + rtp->photon_groups); } - /* Are we using constant emission rates? */ - /* ------------------------------------- */ - rtp->use_const_emission_rates = parser_get_opt_param_int( - params, "GEARRT:use_const_emission_rates", /* default = */ 0); + /* Sanity check: photon group edges must be in increasing order. */ + for (int g = 0; g < RT_NGROUPS - 1; g++) { + if (rtp->photon_groups[g + 1] <= rtp->photon_groups[g]) + error( + "Photon frequency bin edges need to be in increasing order. " + "Found index %d %.3g <= %.3g", + g + 1, rtp->photon_groups[g + 1], rtp->photon_groups[g]); + } + + /* Get stellar emission rate model related parameters */ + /* -------------------------------------------------- */ + + /* First initialize everything */ + for (int g = 0; g < RT_NGROUPS; g++) { + rtp->stellar_const_emission_rates[g] = 0.; + } + + rtp->stellar_emission_model = rt_stellar_emission_model_none; - if (rtp->use_const_emission_rates) { + char stellar_model_str[80]; + parser_get_param_string(params, "GEARRT:stellar_luminosity_model", + stellar_model_str); + + if (strcmp(stellar_model_str, "const") == 0) { + rtp->stellar_emission_model = rt_stellar_emission_model_const; + } else if (strcmp(stellar_model_str, "IlievTest4") == 0) { + rtp->stellar_emission_model = rt_stellar_emission_model_IlievTest; + } else { + error("Unknown stellar luminosity model '%s'", stellar_model_str); + } + + if (rtp->stellar_emission_model == rt_stellar_emission_model_const) { + /* Read the luminosities from the parameter file */ double emission_rates[RT_NGROUPS]; - parser_get_param_double_array(params, "GEARRT:star_emission_rates_LSol", + parser_get_param_double_array(params, + "GEARRT:const_stellar_luminosities_LSol", RT_NGROUPS, emission_rates); const double unit_power = units_cgs_conversion_factor(us, UNIT_CONV_POWER); const double unit_power_inv = 1. / unit_power; for (int g = 0; g < RT_NGROUPS; g++) { rtp->stellar_const_emission_rates[g] = emission_rates[g] * unit_power_inv; } + } else if (rtp->stellar_emission_model == + rt_stellar_emission_model_IlievTest) { + /* Nothing to do here */ } else { - /* kill the run for now */ - error("GEAR-RT can't run without constant stellar emission rates for now."); + error("Unknown stellar emission model %d", rtp->stellar_emission_model); } /* get reduced speed of light factor */ /* --------------------------------- */ const float f_r = parser_get_param_float(params, "GEARRT:f_reduce_c"); + if (f_r <= 0.f) + error("Invalid speed of light reduction factor: %.3e <= 0.", f_r); rt_params.reduced_speed_of_light = phys_const->const_speed_light_c * f_r; rt_params.reduced_speed_of_light_inverse = 1.f / rt_params.reduced_speed_of_light; @@ -358,13 +307,25 @@ __attribute__((always_inline)) INLINE static void rt_props_init( /* get CFL condition */ /* ----------------- */ const float CFL = parser_get_param_float(params, "GEARRT:CFL_condition"); + if (CFL <= 0.f) error("Invalid CFL number: %.3e <= 0.", CFL); rtp->CFL_condition = CFL; + const float f_limit_cooling_time = parser_get_opt_param_float( + params, "GEARRT:f_limit_cooling_time", /*default=*/0.6); + if (f_limit_cooling_time < 0.f) + error("Invalid cooling time reduction factor: %.3e < 0.", + f_limit_cooling_time); + else if (f_limit_cooling_time == 0.f) + message("Warning: Computation of cooling time will be skipped"); + rtp->f_limit_cooling_time = f_limit_cooling_time; + /* Get thermochemistry set-up */ /* -------------------------- */ rtp->hydrogen_mass_fraction = parser_get_param_float(params, "GEARRT:hydrogen_mass_fraction"); rtp->helium_mass_fraction = 1.f - rtp->hydrogen_mass_fraction; + if (rtp->hydrogen_mass_fraction <= 0.f || rtp->hydrogen_mass_fraction > 1.f) + error("Invalid hydrogen mass fraction: %g", rtp->hydrogen_mass_fraction); /* Are we manually overwriting initial mass fractions of H and He? */ rtp->set_initial_ionization_mass_fractions = parser_get_opt_param_int( @@ -421,11 +382,6 @@ __attribute__((always_inline)) INLINE static void rt_props_init( params, "GEARRT:set_equilibrium_initial_ionization_mass_fractions", /* default = */ 0); - /* Mark that we need some conversion after the first step now */ - if (rtp->set_equilibrium_initial_ionization_mass_fractions || - rtp->set_initial_ionization_mass_fractions) - rtp->convert_parts_after_zeroth_step = 1; - if (rtp->set_equilibrium_initial_ionization_mass_fractions && rtp->set_initial_ionization_mass_fractions) error( @@ -436,6 +392,10 @@ __attribute__((always_inline)) INLINE static void rt_props_init( rtp->skip_thermochemistry = parser_get_opt_param_int( params, "GEARRT:skip_thermochemistry", /* default = */ 0); + /* Are we re-doing thermochemistry? */ + rtp->max_tchem_recursion = parser_get_opt_param_int( + params, "GEARRT:max_tchem_recursion", /* default = */ 0); + /* Stellar Spectra */ /* --------------- */ @@ -474,55 +434,36 @@ __attribute__((always_inline)) INLINE static void rt_props_init( #ifdef SWIFT_RT_DEBUG_CHECKS rtp->debug_radiation_emitted_tot = 0ULL; + rtp->debug_radiation_emitted_this_step = 0ULL; + rtp->debug_radiation_absorbed_tot = 0ULL; - rtp->debug_star_injection_prep_iacts_with_parts_tot = 0LL; - rtp->debug_part_injection_prep_iacts_with_stars_tot = 0LL; - for (int g = 0; g < RT_NGROUPS; g++) - rtp->debug_total_star_emitted_energy[g] = 0.f; - - /* Open up files for energy budgets */ - rtp->conserved_energy_filep = fopen("RT_conserved_energy_budget.txt", "w"); - if (rtp->conserved_energy_filep == NULL) - error("Couldn't open RT conserved energy budget file to write in"); - rtp->energy_density_filep = fopen("RT_energy_density_budget.txt", "w"); - if (rtp->energy_density_filep == NULL) - error("Couldn't open RT energy density budget file to write in"); - rtp->star_emitted_energy_filep = fopen("RT_star_injected_energy.txt", "w"); - if (rtp->star_emitted_energy_filep == NULL) - error("Couldn't open RT star energy budget file to write in"); - - if (rtp->use_const_emission_rates) { - FILE* files[3] = {rtp->conserved_energy_filep, rtp->energy_density_filep, - rtp->star_emitted_energy_filep}; - for (int f = 0; f < 3; f++) { - fprintf(files[f], "# Emission rates: "); - const double solar_luminosity = 3.826e33; /* erg/s */ - for (int g = 0; g < RT_NGROUPS; g++) - fprintf(files[f], "%12.6e ", - rtp->stellar_const_emission_rates[g] * solar_luminosity); - fprintf(files[f], "\n"); - } - } + rtp->debug_radiation_absorbed_this_step = 0ULL; + + /* Don't make it an optional parameter here so we crash + * if I forgot to provide it */ + rtp->debug_max_nr_subcycles = + parser_get_param_int(params, "TimeIntegration:max_nr_rt_subcycles"); #endif /* Grackle setup */ /* ------------- */ - rt_props_init_grackle(rtp, us); + rtp->grackle_verbose = + parser_get_opt_param_int(params, "GEARRT:grackle_verbose", /*default=*/0); + rtp->case_B_recombination = parser_get_opt_param_int( + params, "GEARRT:case_B_recombination", /*default=*/1); + rt_init_grackle(&rtp->grackle_units, &rtp->grackle_chemistry_data, + rtp->hydrogen_mass_fraction, rtp->grackle_verbose, + rtp->case_B_recombination, us); /* Pre-compute interaction rates/cross sections */ /* -------------------------------------------- */ - rt_interaction_rates_init(rtp, phys_const, us); + + rtp->energy_weighted_cross_sections = NULL; + rtp->number_weighted_cross_sections = NULL; + rt_cross_sections_init(rtp, phys_const, us); /* Finishers */ /* --------- */ - - /* After initialisation, print params to screen */ - rt_props_print(rtp); - - /* Print a final message. */ - if (engine_rank == 0) { - message("Radiative transfer initialized"); - } } /** @@ -537,6 +478,10 @@ __attribute__((always_inline)) INLINE static void rt_struct_dump( restart_write_blocks((void*)props, sizeof(struct rt_props), 1, stream, "RT props", "RT properties struct"); + /* The RT parameters, in particular the reduced speed of light, are + * not defined at compile time. So we need to read them in again. */ + restart_write_blocks(&rt_params, sizeof(struct rt_parameters), 1, stream, + "RT global parameters", "RT global parameters struct"); } /** @@ -545,12 +490,28 @@ __attribute__((always_inline)) INLINE static void rt_struct_dump( * * @param props the struct * @param stream the file stream + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. */ __attribute__((always_inline)) INLINE static void rt_struct_restore( - struct rt_props* props, FILE* stream) { + struct rt_props* props, FILE* stream, const struct phys_const* phys_const, + const struct unit_system* us) { restart_read_blocks((void*)props, sizeof(struct rt_props), 1, stream, NULL, "RT properties struct"); + /* Set up stuff that needs array allocation */ + rt_init_grackle(&props->grackle_units, &props->grackle_chemistry_data, + props->hydrogen_mass_fraction, props->grackle_verbose, + props->case_B_recombination, us); + + props->energy_weighted_cross_sections = NULL; + props->number_weighted_cross_sections = NULL; + rt_cross_sections_init(props, phys_const, us); + + /* The RT parameters, in particular the reduced speed of light, are + * not defined at compile time. So we need to write them down. */ + restart_read_blocks(&rt_params, sizeof(struct rt_parameters), 1, stream, NULL, + "RT global parameters struct"); } #endif /* SWIFT_RT_PROPERTIES_GEAR_H */ diff --git a/src/rt/GEAR/rt_riemann_GLF.h b/src/rt/GEAR/rt_riemann_GLF.h index 48883f0236ee98a31d70aaaf28b041a7d5f3fead..3915c65d750d89d89e499806766b971b7f5a3542 100644 --- a/src/rt/GEAR/rt_riemann_GLF.h +++ b/src/rt/GEAR/rt_riemann_GLF.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 @@ -34,21 +34,21 @@ * @brief Solve the Riemann problem for the RT equations and return the * flux at the interface. * - * @param UL left state - * @param UR right state + * @param UL left state (radiation energy density, flux) + * @param UR right state (radiation energy density, flux) * @param FLnorm the norm of the radiation flux of the left state - * @param FRnorm the norm of the radiation flux of the left state + * @param FRnorm the norm of the radiation flux of the right state * @param hyperFluxL the flux of the hyperbolic conservation law of the left * state * @param hyperFluxR the flux of the hyperbolic conservation law of the right * state - * @param flux_half the resulting flux at the interface * @param n_unit the unit vector perpendicular to the "intercell" surface. + * @param flux_half (return) the resulting flux at the interface */ __attribute__((always_inline)) INLINE static void rt_riemann_solve_for_flux( const float UL[4], const float UR[4], const float FLnorm, const float FRnorm, float hyperFluxL[4][3], float hyperFluxR[4][3], - float flux_half[4], const float n_unit[3]) { + const float n_unit[3], float flux_half[4]) { float fluxL[4]; fluxL[0] = hyperFluxL[0][0] * n_unit[0] + hyperFluxL[0][1] * n_unit[1] + hyperFluxL[0][2] * n_unit[2]; diff --git a/src/rt/GEAR/rt_riemann_HLL.h b/src/rt/GEAR/rt_riemann_HLL.h index 4873ccdef6ff7678b7e9ff2699e07a5a013ad8e0..73ce92971f546cbe572248b42914afede0143b68 100644 --- a/src/rt/GEAR/rt_riemann_HLL.h +++ b/src/rt/GEAR/rt_riemann_HLL.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 @@ -104,21 +104,21 @@ rt_riemann_interpolate_eigenvals(float f, float theta, float *lambda_min, * @brief Solve the Riemann problem for the RT equations and return the * flux at the interface. * - * @param UL left state - * @param UR right state + * @param UL left state (radiation energy density, flux) + * @param UR right state (radiation energy density, flux) * @param FLnorm the norm of the radiation flux of the left state - * @param FRnorm the norm of the radiation flux of the left state + * @param FRnorm the norm of the radiation flux of the right state * @param hyperFluxL the flux of the hyperbolic conservation law of the left * state * @param hyperFluxR the flux of the hyperbolic conservation law of the right * state - * @param flux_half the resulting flux at the interface * @param n_unit the unit vector perpendicular to the "intercell" surface. + * @param flux_half (return) the resulting flux at the interface */ __attribute__((always_inline)) INLINE static void rt_riemann_solve_for_flux( const float UL[4], const float UR[4], const float FLnorm, const float FRnorm, float hyperFluxL[4][3], float hyperFluxR[4][3], - float flux_half[4], const float n_unit[3]) { + const float n_unit[3], float flux_half[4]) { /* Compute reduced fluxes and angles between surface and flux. * These are based on physical fluxes, not hyperbolic fluxes. */ diff --git a/src/rt/GEAR/rt_riemann_HLL_eigenvalues.h b/src/rt/GEAR/rt_riemann_HLL_eigenvalues.h index 94910d4042e21081f66a282cf28c27ee2c4cb559..d53cbdf57f2bd97c02bee0303a2b0777f3e967c0 100644 --- a/src/rt/GEAR/rt_riemann_HLL_eigenvalues.h +++ b/src/rt/GEAR/rt_riemann_HLL_eigenvalues.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 diff --git a/src/rt/GEAR/rt_slope_limiters_cell.h b/src/rt/GEAR/rt_slope_limiters_cell.h index c6a3216524b991b6d8a4b880c74145a650e51c42..93ab187f7c57a4258c3f7fbf5e6814ed60e8f1ed 100644 --- a/src/rt/GEAR/rt_slope_limiters_cell.h +++ b/src/rt/GEAR/rt_slope_limiters_cell.h @@ -40,8 +40,8 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_cell_init( struct part* p) { for (int g = 0; g < RT_NGROUPS; g++) { - p->rt_data.limiter[g].energy[0] = FLT_MAX; - p->rt_data.limiter[g].energy[1] = -FLT_MAX; + p->rt_data.limiter[g].energy_density[0] = FLT_MAX; + p->rt_data.limiter[g].energy_density[1] = -FLT_MAX; p->rt_data.limiter[g].flux[0][0] = FLT_MAX; p->rt_data.limiter[g].flux[0][1] = -FLT_MAX; p->rt_data.limiter[g].flux[1][0] = FLT_MAX; @@ -71,23 +71,23 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_cell_collect( /* basic slope limiter: collect the maximal and the minimal value for the * primitive variables among the ngbs */ - rtdi->limiter[g].energy[0] = - min(rtdj->density[g].energy, rtdi->limiter[g].energy[0]); - rtdi->limiter[g].energy[1] = - max(rtdj->density[g].energy, rtdi->limiter[g].energy[1]); + rtdi->limiter[g].energy_density[0] = min(rtdj->radiation[g].energy_density, + rtdi->limiter[g].energy_density[0]); + rtdi->limiter[g].energy_density[1] = max(rtdj->radiation[g].energy_density, + rtdi->limiter[g].energy_density[1]); rtdi->limiter[g].flux[0][0] = - min(rtdj->density[g].flux[0], rtdi->limiter[g].flux[0][0]); + min(rtdj->radiation[g].flux[0], rtdi->limiter[g].flux[0][0]); rtdi->limiter[g].flux[0][1] = - max(rtdj->density[g].flux[0], rtdi->limiter[g].flux[0][1]); + max(rtdj->radiation[g].flux[0], rtdi->limiter[g].flux[0][1]); rtdi->limiter[g].flux[1][0] = - min(rtdj->density[g].flux[1], rtdi->limiter[g].flux[1][0]); + min(rtdj->radiation[g].flux[1], rtdi->limiter[g].flux[1][0]); rtdi->limiter[g].flux[1][1] = - max(rtdj->density[g].flux[1], rtdi->limiter[g].flux[1][1]); + max(rtdj->radiation[g].flux[1], rtdi->limiter[g].flux[1][1]); rtdi->limiter[g].flux[2][0] = - min(rtdj->density[g].flux[2], rtdi->limiter[g].flux[2][0]); + min(rtdj->radiation[g].flux[2], rtdi->limiter[g].flux[2][0]); rtdi->limiter[g].flux[2][1] = - max(rtdj->density[g].flux[2], rtdi->limiter[g].flux[2][1]); + max(rtdj->radiation[g].flux[2], rtdi->limiter[g].flux[2][1]); /* just use the hydro one */ /* pi->limiter.maxr = max(r, pi->limiter.maxr); */ } @@ -113,7 +113,7 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_quantity( const float gradtrue_inv = 1.0f / gradtrue; const float gradmax = valmax - value; const float gradmin = valmin - value; - const float beta = 1.0f; /* TODO: test for best value here. For now, take + const float beta = 1.f; /* TODO: test for best value here. For now, take stability over diffusivity. */ const float min_temp = min(gradmax * gradtrue_inv, gradmin * gradtrue_inv) * beta; @@ -137,24 +137,24 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_cell( struct rt_part_data* rtd = &p->rt_data; for (int g = 0; g < RT_NGROUPS; g++) { - rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].energy, + rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].energy_density, /*maxr= */ maxr, - /*value= */ rtd->density[g].energy, - /*valmin= */ rtd->limiter[g].energy[0], - /*valmax= */ rtd->limiter[g].energy[1]); + /*value= */ rtd->radiation[g].energy_density, + /*valmin= */ rtd->limiter[g].energy_density[0], + /*valmax= */ rtd->limiter[g].energy_density[1]); rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[0], /*maxr= */ maxr, - /*value= */ rtd->density[g].flux[0], + /*value= */ rtd->radiation[g].flux[0], /*valmin= */ rtd->limiter[g].flux[0][0], /*valmax= */ rtd->limiter[g].flux[0][1]); rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[1], /*maxr= */ maxr, - /*value= */ rtd->density[g].flux[1], + /*value= */ rtd->radiation[g].flux[1], /*valmin= */ rtd->limiter[g].flux[1][0], /*valmax= */ rtd->limiter[g].flux[1][1]); rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[2], /*maxr= */ maxr, - /*value= */ rtd->density[g].flux[2], + /*value= */ rtd->radiation[g].flux[2], /*valmin= */ rtd->limiter[g].flux[2][0], /*valmax= */ rtd->limiter[g].flux[2][1]); } diff --git a/src/rt/GEAR/rt_slope_limiters_face.h b/src/rt/GEAR/rt_slope_limiters_face.h index cfde3ea8cbcdc7ddca53cb674aa7541ec4258a65..5d0e9c59e673e418aaa39d994ce260a2f455eb20 100644 --- a/src/rt/GEAR/rt_slope_limiters_face.h +++ b/src/rt/GEAR/rt_slope_limiters_face.h @@ -126,7 +126,7 @@ __attribute__((always_inline)) INLINE static float rt_limiter_mc( __attribute__((always_inline)) INLINE static float rt_limiter_vanLeer( const float dQi, const float dQj) { const float r = dQj == 0.f ? dQi * 1e6 : dQi / dQj; - const float absr = fabs(r); + const float absr = fabsf(r); return (r + absr) / (1.f + absr); } @@ -148,8 +148,8 @@ __attribute__((always_inline)) INLINE static float rt_limiter_superbee( /** * @brief Slope limit the slopes at the interface between two particles * - * @param Qi RT quantities of particle i (energy + fluxes in 3 dim) - * @param Qj RT quantities of particle j (energy + fluxes in 3 dim) + * @param Qi RT quantities of particle i (energy density + fluxes in 3 dim) + * @param Qj RT quantities of particle j (energy density + fluxes in 3 dim) * @param dQi Difference between the RT quantities of particle i at the * position of particle i and at the interface position. * @param dQj Difference between the RT quantities of particle j at the diff --git a/src/rt/GEAR/rt_species.h b/src/rt/GEAR/rt_species.h new file mode 100644 index 0000000000000000000000000000000000000000..6d03ce678528565ea5224fec87fb35f6027d8c83 --- /dev/null +++ b/src/rt/GEAR/rt_species.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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_RT_GEAR_SPECIES_H +#define SWIFT_RT_GEAR_SPECIES_H + +/** + * @file src/rt/GEAR/rt_species.h + * @brief header file concerning (ionizing) species. + **/ + +enum rt_ionizing_species { + rt_ionizing_species_HI = 0, + rt_ionizing_species_HeI, + rt_ionizing_species_HeII, + rt_ionizing_species_count +}; + +/** + * Get the ionizing energy in erg for all ionizing species. + * + * @param E_ion (return) the ionizing energies. + **/ +__attribute__((always_inline)) INLINE static void +rt_species_get_ionizing_energy(double E_ion[rt_ionizing_species_count]) { + + E_ion[rt_ionizing_species_HI] = 2.179e-11; /* 13.60 eV in erg */ + E_ion[rt_ionizing_species_HeI] = 3.940e-11; /* 24.59 eV in erg */ + E_ion[rt_ionizing_species_HeII] = 8.719e-11; /* 54.42 eV in erg */ +} + +#endif /* SWIFT_RT_GEAR_SPECIES_H */ diff --git a/src/rt/GEAR/rt_stellar_emission_model.h b/src/rt/GEAR/rt_stellar_emission_model.h new file mode 100644 index 0000000000000000000000000000000000000000..295db0d83c45502f90cb80036861835068d90ec0 --- /dev/null +++ b/src/rt/GEAR/rt_stellar_emission_model.h @@ -0,0 +1,114 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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_RT_STELLAR_EMISSION_MODEL_GEAR_H +#define SWIFT_RT_STELLAR_EMISSION_MODEL_GEAR_H + +/** + * @file src/rt/GEAR/rt_stellar_emission_model.h + * @brief Main header file for the GEAR M1 closure radiative transfer scheme + * stellar radiation emission models. + */ + +enum rt_stellar_emission_models { + rt_stellar_emission_model_none = 0, + rt_stellar_emission_model_const, + rt_stellar_emission_model_IlievTest, + rt_stellar_emission_model_count +}; + +/** + * @brief Compute the energy emitted from a star during the time step dt. + * This is for the constant emission rate model. + * + * @param emission_this_step (return) the emitted radiation energy of a star + * during the time interval dt + * @param const_stellar_emission_rates the constant emission rates used in this + * run + * @param dt time step size (in internal units) + */ +__attribute__((always_inline)) INLINE static void +rt_get_emission_this_step_const( + double emission_this_step[RT_NGROUPS], + const double const_stellar_emission_rates[RT_NGROUPS], double dt) { + + /* The read-in constant stellar emisison rates are in units of L_sol. + * But they have been read in assuming they are in cgs. Convert this + * only now to proper internal units to avoid float overflows. We only + * store the energy that is to be distributed from this spart to its + * neighbours in this step in internal units.*/ + const double solar_luminosity = 3.828e33; /* erg/s */ + for (int g = 0; g < RT_NGROUPS; g++) { + const double emission_rate_internal_units = + const_stellar_emission_rates[g] * solar_luminosity; + emission_this_step[g] = emission_rate_internal_units * dt; + } +} + +/** + * @brief Compute the energy emitted from a star during the time step dt. + * This is for the Iliev+2006 Test 4. + * + * @param emission_this_step (return) the emitted radiation energy of a star + * during the time interval dt + * @param M the star mass (in internal units) + * @param dt time step size (in internal units) + * @param photon_number_integral Integrated photon numbers over frequency + * interval + * @param average_photon_energy average photon energy in each frequency bin, in + * erg + * @param phys_const struct holding physical constants + * @param internal_units units struct containing internal units + */ +__attribute__((always_inline)) INLINE static void +rt_get_emission_this_step_IlievTest( + double emission_this_step[RT_NGROUPS], float M, const double dt, + const double photon_number_integral[RT_NGROUPS], + const double average_photon_energy[RT_NGROUPS], + const struct phys_const* phys_const, + const struct unit_system* internal_units) { + + /* Note that this model uses the halo mass to determine the luminosity + * of a source. I'm cheating the system here by storing the required halo + * mass as the star mass. This is only ok because the test is supposed to + * run with all dynamics and gravity turned off. */ + + const double Omega_b = 0.043; + const double Omega_0 = 0.27; + const double m_p = phys_const->const_proton_mass; + const double t_s = 3e6 * phys_const->const_year; + const double f_gamma = 250.; + const double Ndot_gamma = (f_gamma * M * Omega_b) / (Omega_0 * m_p * t_s); + + double Nsum = 0.; + for (int g = 0; g < RT_NGROUPS; g++) Nsum += photon_number_integral[g]; + + if (Nsum <= 0.) error("No photons in spectrum...???"); + + const double energy_units = + units_cgs_conversion_factor(internal_units, UNIT_CONV_ENERGY); + for (int g = 0; g < RT_NGROUPS; g++) { + const double fi = photon_number_integral[g] / Nsum; + const double Ndot_i = fi * Ndot_gamma; + /* average photon densities are in cgs! */ + const double Edot_i = average_photon_energy[g] * Ndot_i / energy_units; + emission_this_step[g] = Edot_i * dt; + } +} + +#endif /* SWIFT_RT_STELLAR_EMISSION_MODEL_GEAR_H */ diff --git a/src/rt/GEAR/rt_stellar_emission_rate.h b/src/rt/GEAR/rt_stellar_emission_rate.h deleted file mode 100644 index a6febb1df0fead661d08bb15a6e6961baee484f8..0000000000000000000000000000000000000000 --- a/src/rt/GEAR/rt_stellar_emission_rate.h +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) - * - * 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_RT_STELLAR_EMISSION_RATE_GEAR_H -#define SWIFT_RT_STELLAR_EMISSION_RATE_GEAR_H - -/** - * @file src/rt/GEAR/rt_stellar_emission_rate.h - * @brief Main header file for the GEAR M1 closure radiative transfer scheme - * stellar radiation emission rates related functions. - */ - -/** - * @brief Main function for getting the stellar emission rate and updating it - * in the spart. - * - * @param sp Star particle to work on. - * @param age_beg Age of the stars at the beginning of the step - * @param age_end Age of the stars at the end of the step - * @param rt_props RT properties struct - * @param phys_const struct holding physical constants - * @param internal_units units struct containing internal units - */ - -__attribute__((always_inline)) INLINE static void rt_set_stellar_emission_rate( - struct spart* restrict sp, double age_beg, double age_end, - const struct rt_props* rt_props, const struct phys_const* phys_const, - const struct unit_system* internal_units) { - - if (rt_props->use_const_emission_rates) { - /* The read-in constant stellar emisison rates are in units of L_sol. - * But they have been read in assuming they are in cgs. Convert this - * only now to proper internal units to avoid float overflows. We only - * store the energy that is to be distributed from this spart to its - * neighbours in this step in internal units.*/ - const double solar_luminosity = 3.826e33; /* erg/s */ - const double dt = (age_end - age_beg); - for (int g = 0; g < RT_NGROUPS; g++) { - double emission_rate_internal_units = - rt_props->stellar_const_emission_rates[g] * solar_luminosity; - sp->rt_data.emission_this_step[g] = emission_rate_internal_units * dt; - } - } - -#ifdef SWIFT_RT_DEBUG_CHECKS - sp->rt_data.debug_emission_rate_set += 1; -#endif -} - -#endif /* SWIFT_RT_STELLAR_EMISSION_RATE_GEAR_H */ diff --git a/src/rt/GEAR/rt_struct.h b/src/rt/GEAR/rt_struct.h index c681aefd1b7fc6f0ad7ddc907d8ff4b0682ef6b7..63cfebbb6a2e3337e317edea206d7dd183f7ac6e 100644 --- a/src/rt/GEAR/rt_struct.h +++ b/src/rt/GEAR/rt_struct.h @@ -27,17 +27,11 @@ /* Additional RT data in hydro particle struct */ struct rt_part_data { - /* conserved state vector */ + /* Radiation state vector. */ struct { - float energy; - float flux[3]; - } conserved[RT_NGROUPS]; - - /* density state vector */ - struct { - float energy; + float energy_density; float flux[3]; - } density[RT_NGROUPS]; + } radiation[RT_NGROUPS]; /* Fluxes in the conservation law sense */ struct { @@ -45,12 +39,15 @@ struct rt_part_data { float flux[3]; } flux[RT_NGROUPS]; - /* gradients of densities */ + /* Particle RT time step. */ + float flux_dt; + + /* gradients of the radiation state. */ /* for the flux[3][3] quantity: * first index: x, y, z coordinate of the flux. * Second index: gradient along x, y, z direction. */ struct { - float energy[3]; + float energy_density[3]; float flux[3][3]; } gradient[RT_NGROUPS]; @@ -61,7 +58,7 @@ struct rt_part_data { /* the Gizmo-style slope limiting doesn't help for RT as is, * so we're skipping it for now. */ /* struct { */ - /* float energy[2]; */ + /* float energy_density[2]; */ /* float flux[3][2]; */ /* [> float maxr; [> just use the hydro one <] <] */ /* } limiter[RT_NGROUPS]; */ @@ -91,32 +88,24 @@ struct rt_part_data { /*! how much radiation this part received from stars during total lifetime */ unsigned long long debug_radiation_absorbed_tot; - /*! how many interactions this part had with stars in injection prep over - * total lifetime */ - unsigned long long debug_iact_stars_inject_prep_tot; - /* data to store during one time step */ - /*! how many stars this part interacted with during preparation*/ - /* Note: It's useless to write this in outputs, as it gets reset - * at the end of every step. */ - int debug_iact_stars_inject_prep; - /*! how many stars this part interacted with during injection*/ /* Note: It's useless to write this in outputs, as it gets reset * at the end of every step. */ int debug_iact_stars_inject; - /* skip this for GEAR */ - /* called in a self/rt_injection task? */ - /* int debug_injection_check; */ - /*! calls from gradient interaction loop in actual function */ int debug_calls_iact_gradient_interaction; /*! calls from transport interaction loop in actual function */ int debug_calls_iact_transport_interaction; + /* Task completion flags */ + + /*! part got kicked? */ + int debug_kicked; + /*! calls from ghost1 tasks */ int debug_injection_done; @@ -129,6 +118,11 @@ struct rt_part_data { /*! thermochemistry done? */ int debug_thermochem_done; + /* Subcycling flags */ + + /*! Current subcycle wrt (last) hydro step */ + int debug_nsubcycles; + #endif }; @@ -137,8 +131,6 @@ struct rt_spart_data { /* Stellar energy emission that will be injected in to gas. * Total energy, not density, not rate! */ - /* TODO: keep this also for RT_HYDRO_CONTROLLED_INJECTION and - * store results with each hydro-star interaction in here */ float emission_this_step[RT_NGROUPS]; /*! Neighbour weigths in each octant surrounding the star */ @@ -150,36 +142,28 @@ struct rt_spart_data { /*! how much radiation this star emitted during total lifetime */ unsigned long long debug_radiation_emitted_tot; - /*! how many interactions this star had with parts during - * injection prep over total lifetime */ - unsigned long long debug_iact_hydro_inject_prep_tot; - /* data to store during one time step */ - /*! how many hydro particles this particle interacted with */ - /* Note: It's useless to write this in outputs, as it gets reset - * at the end of every step. */ + /*! how many hydro particles this particle interacted with + * during injection */ int debug_iact_hydro_inject; /*! how many hydro particles this particle interacted with * during injection prep*/ - /* Note: It's useless to write this in outputs, as it gets reset - * at the end of every step. */ int debug_iact_hydro_inject_prep; /*! stellar photon emisison rate computed? */ int debug_emission_rate_set; - /* skip this for GEAR */ - /* !called in a self/rt_injection task? */ - /* int debug_injection_check; */ - /*! how much energy this star particle actually has injected into the gas */ float debug_injected_energy[RT_NGROUPS]; /*! how much energy this star particle actually has injected into the gas over * the entire run*/ float debug_injected_energy_tot[RT_NGROUPS]; + + /*! sum up total weights used during injection to compare consistency */ + float debug_psi_sum; #endif }; diff --git a/src/rt/GEAR/rt_thermochemistry.h b/src/rt/GEAR/rt_thermochemistry.h index 427a64ad94287f8dcfeb554cfe05bba13f492252..917016a48adca7a29e254549daa03697721b6e4e 100644 --- a/src/rt/GEAR/rt_thermochemistry.h +++ b/src/rt/GEAR/rt_thermochemistry.h @@ -19,10 +19,8 @@ #ifndef SWIFT_RT_GEAR_THERMOCHEMISTRY_H #define SWIFT_RT_GEAR_THERMOCHEMISTRY_H -/* need to rework (and check) code if changed */ -#define GRACKLE_NPART 1 -#define GRACKLE_RANK 3 - +#include "rt_grackle_utils.h" +#include "rt_interaction_cross_sections.h" #include "rt_interaction_rates.h" #include "rt_ionization_equilibrium.h" @@ -37,12 +35,14 @@ * * @param p part to work with * @param rt_props rt_properties struct + * @param hydro_props hydro properties struct * @param phys_const physical constants struct * @param us unit system struct * @param cosmo cosmology struct */ __attribute__((always_inline)) INLINE static void rt_tchem_first_init_part( struct part* restrict p, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const struct cosmology* restrict cosmo) { @@ -50,7 +50,8 @@ __attribute__((always_inline)) INLINE static void rt_tchem_first_init_part( if (rt_props->set_equilibrium_initial_ionization_mass_fractions) { float XHI, XHII, XHeI, XHeII, XHeIII; rt_ion_equil_get_mass_fractions(&XHI, &XHII, &XHeI, &XHeII, &XHeIII, p, - rt_props, phys_const, us, cosmo); + rt_props, hydro_props, phys_const, us, + cosmo); p->rt_data.tchem.mass_fraction_HI = XHI; p->rt_data.tchem.mass_fraction_HII = XHII; p->rt_data.tchem.mass_fraction_HeI = XHeI; @@ -66,6 +67,22 @@ __attribute__((always_inline)) INLINE static void rt_tchem_first_init_part( /* Check that we didn't do something stupid */ rt_check_unphysical_mass_fractions(p); + + /* Check that the Hydrogen and Helium mass fractions correspond to those + * provided by the user in the parameter file. This mass fraction is also + * passed down to grackle internally, so it is error-prone if left + * unchecked. */ + const float mH = + p->rt_data.tchem.mass_fraction_HI + p->rt_data.tchem.mass_fraction_HII; + if (fabsf(mH - rt_props->hydrogen_mass_fraction) > 1e-4) + error("Got wrong Hydrogen mass fraction: Got =%.6f provided in yml =%.6f", + mH, rt_props->hydrogen_mass_fraction); + const float mHe = p->rt_data.tchem.mass_fraction_HeI + + p->rt_data.tchem.mass_fraction_HeII + + p->rt_data.tchem.mass_fraction_HeIII; + if (fabsf(mHe - rt_props->helium_mass_fraction) > 1e-4) + error("Got wrong Helium mass fraction: Got =%.6f provided in yml =%.6f", + mHe, rt_props->helium_mass_fraction); } /** @@ -79,29 +96,17 @@ __attribute__((always_inline)) INLINE static void rt_tchem_first_init_part( * @param phys_const The physical constants in internal units. * @param us The internal system of units. * @param dt The time-step of this particle. + * @depth recursion depth */ -static void rt_do_thermochemistry(struct part* restrict p, - struct xpart* restrict xp, - struct rt_props* rt_props, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const double dt) { +INLINE static void rt_do_thermochemistry( + struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, const double dt, int depth) { /* Note: Can't pass rt_props as const struct because of grackle * accessinging its properties there */ -#ifdef SWIFT_RT_DEBUG_CHECKS - if (!p->rt_data.debug_injection_done) - error("Trying to do thermochemistry when injection step hasn't been done"); - if (!p->rt_data.debug_gradients_done) - error("Trying to do thermochemistry when gradient step hasn't been done"); - if (!p->rt_data.debug_transport_done) - error("Trying to do thermochemistry when transport step hasn't been done"); - - p->rt_data.debug_thermochem_done += 1; -#endif - /* Nothing to do here? */ if (rt_props->skip_thermochemistry) return; if (dt == 0.) return; @@ -109,96 +114,71 @@ static void rt_do_thermochemistry(struct part* restrict p, /* This is where the fun begins */ /* ---------------------------- */ - /* initialize data */ + /* initialize data so it'll be in scope */ grackle_field_data particle_grackle_data; - int grid_dimension[GRACKLE_RANK] = {GRACKLE_NPART, 1, 1}; - int grid_start[GRACKLE_RANK] = {0, 0, 0}; - int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0}; + gr_float density = hydro_get_physical_density(p, cosmo); - particle_grackle_data.grid_dx = 0.; - particle_grackle_data.grid_rank = GRACKLE_RANK; - particle_grackle_data.grid_dimension = grid_dimension; - particle_grackle_data.grid_start = grid_start; - particle_grackle_data.grid_end = grid_end; + /* In rare cases, unphysical solutions can arise with negative densities + * which won't be fixed in the hydro part until further down the dependency + * graph. Also, we can have vacuum, in which case we have nothing to do here. + * So exit early if that is the case. */ + if (density <= 0.) return; - /* general particle data */ - gr_float density = hydro_get_physical_density(p, cosmo); const float u_minimal = hydro_props->minimal_internal_energy; gr_float internal_energy = max(hydro_get_physical_internal_energy(p, xp, cosmo), u_minimal); - const float u_old = internal_energy; - /* initialize density */ - particle_grackle_data.density = &density; - particle_grackle_data.internal_energy = &internal_energy; - /* grackle 3.0 doc: "Currently not used" */ - particle_grackle_data.x_velocity = NULL; - particle_grackle_data.y_velocity = NULL; - particle_grackle_data.z_velocity = NULL; - gr_float species_densities[6]; rt_tchem_get_species_densities(p, density, species_densities); - particle_grackle_data.HI_density = &species_densities[0]; - particle_grackle_data.HII_density = &species_densities[1]; - particle_grackle_data.HeI_density = &species_densities[2]; - particle_grackle_data.HeII_density = &species_densities[3]; - particle_grackle_data.HeIII_density = &species_densities[4]; - particle_grackle_data.e_density = &species_densities[5]; - - particle_grackle_data.volumetric_heating_rate = NULL; - particle_grackle_data.specific_heating_rate = NULL; - - gr_float rates[5]; - float rates_by_frequency_bin[RT_NGROUPS]; - rt_tchem_get_interaction_rates(rates, rates_by_frequency_bin, p, - species_densities, rt_props, phys_const, us, - cosmo); - particle_grackle_data.RT_heating_rate = &rates[0]; - particle_grackle_data.RT_HI_ionization_rate = &rates[1]; - particle_grackle_data.RT_HeI_ionization_rate = &rates[2]; - particle_grackle_data.RT_HeII_ionization_rate = &rates[3]; - particle_grackle_data.RT_H2_dissociation_rate = &rates[4]; - - particle_grackle_data.metal_density = NULL; + float radiation_energy_density[RT_NGROUPS]; + rt_part_get_radiation_energy_density(p, radiation_energy_density); + + gr_float iact_rates[5]; + rt_get_interaction_rates_for_grackle( + iact_rates, radiation_energy_density, species_densities, + rt_props->average_photon_energy, rt_props->energy_weighted_cross_sections, + rt_props->number_weighted_cross_sections, phys_const, us); + + /* Put all the data into a grackle field struct */ + rt_get_grackle_particle_fields(&particle_grackle_data, density, + internal_energy, species_densities, + iact_rates); /* solve chemistry */ - if (!local_solve_chemistry( - &rt_props->grackle_chemistry_data, rt_props->grackle_chemistry_rates, - &rt_props->grackle_units, &particle_grackle_data, dt)) + /* Note: `grackle_rates` is a global variable defined by grackle itself. + * Using a manually allocd and initialized variable here fails with MPI + * for some reason. */ + if (local_solve_chemistry(&rt_props->grackle_chemistry_data, &grackle_rates, + &rt_props->grackle_units, &particle_grackle_data, + dt) == 0) error("Error in solve_chemistry."); + /* copy updated grackle data to particle */ /* update particle internal energy. Grackle had access by reference * to internal_energy */ + internal_energy = particle_grackle_data.internal_energy[0]; const float u_new = max(internal_energy, u_minimal); - /* Redo if we changed by more than 10% ? */ - if (fabsf(u_new - u_old) > 0.1f * u_old) { - /* Redo because we changed by more than 10% ! */ + /* Re-do thermochemistry? */ + if ((rt_props->max_tchem_recursion > depth) && + (fabsf(u_old - u_new) > 0.1 * u_old)) { + /* Note that grackle already has internal "10% rules". But sometimes, they + * may not suffice. */ + rt_clean_grackle_fields(&particle_grackle_data); rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, us, - 0.5 * dt); + 0.5 * dt, depth + 1); rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, us, - 0.5 * dt); + 0.5 * dt, depth + 1); return; } /* If we're good, update the particle data from grackle results */ hydro_set_internal_energy(p, u_new); - /* update radiation fields */ - for (int g = 0; g < RT_NGROUPS; g++) { - const float factor_new = (1.f - dt * rates_by_frequency_bin[g]); - p->rt_data.conserved[g].energy *= factor_new; - for (int i = 0; i < 3; i++) { - p->rt_data.conserved[g].flux[i] *= factor_new; - } - rt_check_unphysical_conserved(&p->rt_data.conserved[g].energy, - p->rt_data.conserved[g].flux, 2); - } - - /* copy updated grackle data to particle */ + /* Update mass fractions */ const gr_float one_over_rho = 1. / density; p->rt_data.tchem.mass_fraction_HI = particle_grackle_data.HI_density[0] * one_over_rho; @@ -212,6 +192,105 @@ static void rt_do_thermochemistry(struct part* restrict p, particle_grackle_data.HeIII_density[0] * one_over_rho; rt_check_unphysical_mass_fractions(p); + + /* Update radiation fields */ + /* First get absorption rates at the start and the end of the step */ + double absorption_rates[RT_NGROUPS]; + rt_get_absorption_rates( + absorption_rates, species_densities, rt_props->average_photon_energy, + rt_props->number_weighted_cross_sections, phys_const, us); + + gr_float species_densities_new[6]; + species_densities_new[0] = particle_grackle_data.HI_density[0]; + species_densities_new[1] = particle_grackle_data.HII_density[0]; + species_densities_new[2] = particle_grackle_data.HeI_density[0]; + species_densities_new[3] = particle_grackle_data.HeII_density[0]; + species_densities_new[4] = particle_grackle_data.HeIII_density[0]; + species_densities_new[5] = particle_grackle_data.e_density[0]; + double absorption_rates_new[RT_NGROUPS]; + rt_get_absorption_rates(absorption_rates_new, species_densities_new, + rt_props->average_photon_energy, + rt_props->number_weighted_cross_sections, phys_const, + us); + + /* Now remove absorbed radiation */ + for (int g = 0; g < RT_NGROUPS; g++) { + const float E_old = p->rt_data.radiation[g].energy_density; + double f = dt * 0.5 * (absorption_rates[g] + absorption_rates_new[g]); + f = min(1., f); + f = max(0., f); + p->rt_data.radiation[g].energy_density *= (1. - f); + for (int i = 0; i < 3; i++) { + p->rt_data.radiation[g].flux[i] *= (1. - f); + } + + rt_check_unphysical_state(&p->rt_data.radiation[g].energy_density, + p->rt_data.radiation[g].flux, E_old, + /*callloc=*/2); + } + + /* Clean up after yourself. */ + rt_clean_grackle_fields(&particle_grackle_data); +} + +/** + * @brief Main function for the thermochemistry step. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + */ +__attribute__((always_inline)) INLINE static float rt_tchem_get_tchem_time( + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us) { + /* Note: Can't pass rt_props as const struct because of grackle + * accessinging its properties there */ + + /* initialize data so it'll be in scope */ + grackle_field_data particle_grackle_data; + + gr_float density = hydro_get_physical_density(p, cosmo); + const float u_minimal = hydro_props->minimal_internal_energy; + gr_float internal_energy = + max(hydro_get_physical_internal_energy(p, xp, cosmo), u_minimal); + + gr_float species_densities[6]; + rt_tchem_get_species_densities(p, density, species_densities); + + float radiation_energy_density[RT_NGROUPS]; + rt_part_get_radiation_energy_density(p, radiation_energy_density); + + gr_float iact_rates[5]; + rt_get_interaction_rates_for_grackle( + iact_rates, radiation_energy_density, species_densities, + rt_props->average_photon_energy, rt_props->energy_weighted_cross_sections, + rt_props->number_weighted_cross_sections, phys_const, us); + + rt_get_grackle_particle_fields(&particle_grackle_data, density, + internal_energy, species_densities, + iact_rates); + + /* Compute 'cooling' time */ + /* Note: grackle_rates is a global variable defined by grackle itself. + * Using a manually allocd and initialized variable here fails with MPI + * for some reason. */ + gr_float tchem_time; + if (local_calculate_cooling_time(&rt_props->grackle_chemistry_data, + &grackle_rates, &rt_props->grackle_units, + &particle_grackle_data, &tchem_time) == 0) + error("Error in calculate_cooling_time."); + + /* Clean up after yourself. */ + rt_clean_grackle_fields(&particle_grackle_data); + + return (float)tchem_time; } #endif /* SWIFT_RT_GEAR_THERMOCHEMISTRY_H */ diff --git a/src/rt/GEAR/rt_thermochemistry_utils.h b/src/rt/GEAR/rt_thermochemistry_utils.h index 41404738faf22ddb6f58f20dd5d650585382b66f..580813471307f99884e2c9a03fbdf9d6bf177935 100644 --- a/src/rt/GEAR/rt_thermochemistry_utils.h +++ b/src/rt/GEAR/rt_thermochemistry_utils.h @@ -24,6 +24,8 @@ * @brief thermochemistry utilities and misc functions. * */ +#include "rt_species.h" + /** * @brief compute the mean molecular weight mu for given * hydrogen and helium mass fractions @@ -79,7 +81,7 @@ __attribute__((always_inline)) INLINE static float rt_tchem_internal_energy_from_T(const double T, const double mu, const double kB, const double mp) { - return kB * T * hydro_one_over_gamma_minus_one / mu / mp; + return kB * T * hydro_one_over_gamma_minus_one / (mu * mp); } /** @@ -95,17 +97,17 @@ rt_tchem_internal_energy_from_T(const double T, const double mu, __attribute__((always_inline)) INLINE static float rt_tchem_internal_energy_dT( double mu, const double kB, const double mp) { - const double dudT = kB * hydro_one_over_gamma_minus_one / mu / mp; + const double dudT = kB * hydro_one_over_gamma_minus_one / (mu * mp); return dudT; } /** - * @brief get the number densities of all species and electrons. + * @brief get the densities of all species and electrons. * * @param p particle to use * @param rho particle physical density - * @param number_densities array to write number densities in + * @param species_densities array to write densities in **/ __attribute__((always_inline)) INLINE static void rt_tchem_get_species_densities(const struct part* restrict p, gr_float rho, @@ -127,11 +129,38 @@ rt_tchem_get_species_densities(const struct part* restrict p, gr_float rho, species_densities[5] = rho_e; } +/** + * @brief get the number densities of the ionizing species in cgs. + * + * @param ns_cgs (return) number densities in cgs of ionizing species + * @param species_densities densities of all species as returned by + * rt_tchem_get_species_densities() + * @param phys_const physical constants struct + * @param us internal units struct + * + **/ +__attribute__((always_inline)) INLINE static void +rt_tchem_get_ionizing_species_number_densities( + double ns_cgs[rt_ionizing_species_count], gr_float species_densities[6], + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us) { + + const double m_p = phys_const->const_proton_mass; + const double to_inv_volume_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_INV_VOLUME); + const double rho_to_n_cgs = to_inv_volume_cgs / m_p; + + ns_cgs[rt_ionizing_species_HI] = species_densities[0] * rho_to_n_cgs; + ns_cgs[rt_ionizing_species_HeI] = 0.25 * species_densities[2] * rho_to_n_cgs; + ns_cgs[rt_ionizing_species_HeII] = 0.25 * species_densities[3] * rho_to_n_cgs; +} + /** * @brief get the (physical) temperature of the gas * * @param p particle to use * @param phys_const physical constants struct + * @param cosmo cosmology struct **/ __attribute__((always_inline)) INLINE static double rt_tchem_get_gas_temperature(const struct part* restrict p, @@ -155,4 +184,103 @@ rt_tchem_get_gas_temperature(const struct part* restrict p, return T; } + +/** + * @brief Set a particle's density and internal energy. + * + * This function is only intended for use in very special case idealized + * tests, like the Iliev+06 tests, where we require fixed densities and + * temperatures. + **/ +__attribute__((always_inline)) INLINE static void +rt_tchem_set_particle_quantities_for_test(struct part* restrict p) { + + /* Set the values that you actually want. Needs to be in internal units.*/ + /* 1 hydrogen_atom_mass / cm^3 / (1.98848e18 g/IMU * 3.0857e15cm/ILU^3) */ + /* float density = 2.471e+04; */ + + /* Set the values that you actually want. Needs to be in internal units.*/ + /* 10^-3 hydrogen_atom_mass / cm^3 / (1.98848e18 g/IMU * 3.0857e15cm/ILU^3) */ + float density = 2.471e+01; + + /* 100 K */ + float internal_energy = 1.23816f; + + /* 10000 K with xHII = 1e-3 for Iliev Test 1 */ + /* float internal_energy = 124.8416491f; */ + + /* Be vocal, just in case somebody forgets you exist. */ + if (p->id == 1) message("Setting density from %.3e to %.3e", p->rho, density); + + float mass_corr = density / p->rho; + p->rho = density; + p->conserved.mass *= mass_corr; + /* This assumes zero velocity */ + p->conserved.energy = p->conserved.mass * internal_energy; + hydro_set_internal_energy(p, internal_energy); +} + +/** + * @brief Set a particle's radiation field given a photon flux. + * + * This function is only intended for use in very special case idealized + * tests, like the Iliev+06 tests, where we require fixed densities and + * temperatures. + * + * @param p particle to modify + * @param time current simulation time + * @param us unity system. + **/ +__attribute__((always_inline)) INLINE static void +rt_tchem_set_particle_radiation_field_for_test( + struct part* restrict p, const double time, + const struct unit_system* restrict us) { + + const double time_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + const double t_Myr = time * time_to_cgs / (3600. * 24. * 365. * 1e6); + + /* NOTE: this assumes that the test is set up with 3 photon groups. */ + double fixed_fluxes[3]; + for (int g = 0; g < 3; g++) fixed_fluxes[g] = 0.; + + if (t_Myr < 0.5) { + /* Be vocal, just in case somebody forgets you exist. */ + if (p->id == 1) message("Setting fixed radiation field."); + /* Set fixed radiation fields, in cgs*/ + fixed_fluxes[0] = 1.350e01; + fixed_fluxes[1] = 2.779e01; + fixed_fluxes[2] = 6.152e00; + } + + const double flux_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE); + const double cf = rt_params.reduced_speed_of_light_inverse / flux_to_cgs; + + /* Note that we inject energy / time / surface, not identical to what */ + /* is in Iliev06 paper */ + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.radiation[g].energy_density = fixed_fluxes[g] * cf; + } +} + +/** + * @brief Modify a boundary particle. + * + * This function is only intended for use in very special case idealized + * tests, like the Iliev+06 tests, to deal with boundary conditions in + * a simple manner. + * */ +__attribute__((always_inline)) INLINE static void +rt_tchem_set_boundary_particles_for_test(struct part* restrict p) { + + if (p->id >= 1000000000) { + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.radiation[g].energy_density = 0.f; + p->rt_data.radiation[g].flux[0] = 0.f; + p->rt_data.radiation[g].flux[1] = 0.f; + p->rt_data.radiation[g].flux[2] = 0.f; + } + } +} + #endif /* SWIFT_RT_GEAR_THERMOCHEMISTRY_UTILS_H */ diff --git a/src/rt/GEAR/rt_unphysical.h b/src/rt/GEAR/rt_unphysical.h index dd1ae1ed33f451db12217e1a31834f91e73a14b0..93728a3a86c004d0706c650f10b2b47050d24715 100644 --- a/src/rt/GEAR/rt_unphysical.h +++ b/src/rt/GEAR/rt_unphysical.h @@ -26,91 +26,104 @@ /** * @brief check for and correct if needed unphysical - * values for a photon density state + * values for a radiation state. * - * @param energy pointer to the photon energy density - * @param flux pointer to the photon flux density - * @param c integer identifier where this function was called from + * @param energy_density pointer to the radiation energy density + * @param flux pointer to radiation flux (3 dimensional) + * @param e_old energy density before change to check. Set = 0 if not available + * @param callloc integer indentifier where this function was called from */ -__attribute__((always_inline)) INLINE static void rt_check_unphysical_density( - float* energy, float* flux, int c) { +__attribute__((always_inline)) INLINE static void rt_check_unphysical_state( + float* energy_density, float* flux, const float e_old, int callloc) { /* Check for negative energies */ + /* Note to self for printouts: Maximal allowable F = E * c. + * In some cases, e.g. while cooling, we don't modify the fluxes, + * so you can get an estimate of what the photon energy used to be + * by dividing the printed out fluxes by the speed of light in + * code units */ #ifdef SWIFT_DEBUG_CHECKS - /* Only print something if it might be significant. Also skip - * message for case c=1, which is gradients predicting energies. */ - if (*energy < 0.f && fabs(*energy) > 1.e-1 && c != 1) - message("Fixing unphysical energy case%d %.6e | %.6e %.6e %.6e", c, *energy, - flux[0], flux[1], flux[2]); + float ratio = -1.f; + char print = 0; + if (e_old == 0.) { + if (*energy_density < -1.e-4f) print = 1; + } else { + if (fabsf(*energy_density) > 1.e-30) { + if (*energy_density < -1.e-4f * fabsf(e_old)) print = 1; + ratio = fabsf(*energy_density / e_old); + } + } + /* callloc = 1 is gradient extrapolation. Don't print out those. */ + if (callloc == 1) print = 0; + if (print) + message("Fixing unphysical energy case %d | %.6e | %.6e %.6e %.6e | %.6e", + callloc, *energy_density, flux[0], flux[1], flux[2], ratio); #endif - if (isnan(*energy) || isinf(*energy)) - error("Got inf/nan radiation energy case%d %.6e | %.6e %.6e %.6e", c, - *energy, flux[0], flux[1], flux[2]); - if (*energy <= 0.f) { - *energy = 0.f; + if (isinf(*energy_density) || isnan(*energy_density)) + error("Got inf/nan radiation energy case %d | %.6e | %.6e %.6e %.6e", + callloc, *energy_density, flux[0], flux[1], flux[2]); + + if (*energy_density <= 0.f) { + *energy_density = 0.f; flux[0] = 0.f; flux[1] = 0.f; flux[2] = 0.f; return; } - /* [> Check for too high fluxes <] */ - /* const float flux2 = flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * */ - /* flux[2]; */ - /* const float flux_norm = sqrtf(flux2); */ - /* const float flux_max = rt_params.reduced_speed_of_light * *energy; */ - /* if (flux_norm > flux_max) { */ - /* const float correct = flux_max / flux_norm; */ - /* #ifdef SWIFT_DEBUG_CHECKS */ - /* if (correct < 0.99 && c != 1) */ - /* message("Correcting max fluxes case%d %.6e | %.6e %.6e %.6e | %.6e", - */ - /* c, *energy, flux[0], flux[1], flux[2], correct); */ - /* #endif */ - /* flux[0] *= correct; */ - /* flux[1] *= correct; */ - /* flux[2] *= correct; */ - /* } */ + /* Check for too high fluxes */ + const float flux2 = flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * flux[2]; + const float flux_norm = sqrtf(flux2); + const float flux_max = rt_params.reduced_speed_of_light * *energy_density; + if (flux_norm > flux_max) { + const float correct = flux_max / flux_norm; + flux[0] *= correct; + flux[1] *= correct; + flux[2] *= correct; + } } /** - * @brief check for and correct if needed unphysical - * values for a photon conserved state + * @brief Do additional checks after reading in initial conditions, and exit on + * error. * - * @param energy pointer to the photon energy - * @param flux pointer to photon fluxes (3 dimensional) - * @param c integer indentifier where this function was called from + * @param p particle we're checking + * @param group current photon group we're checking + * @param energy_density pointer to the radiation energy density + * @param flux pointer to radiation flux (3 dimensional) + * @param c the speed of light (in internal units). NOT the reduced speed of + * light. */ -__attribute__((always_inline)) INLINE static void rt_check_unphysical_conserved( - float* energy, float* flux, int c) { +__attribute__((always_inline)) INLINE static void rt_check_unphysical_state_ICs( + const struct part* restrict p, int group, float* energy_density, + float* flux, const double c) { - /* Check for negative energies */ -#ifdef SWIFT_DEBUG_CHECKS - if (*energy < 0.f && fabs(*energy) > 1.e-1) - message("Fixing unphysical energy case %d | %.6e | %.6e %.6e %.6e", c, - *energy, flux[0], flux[1], flux[2]); -#endif - if (isinf(*energy) || isnan(*energy)) - error("Got inf/nan radiation energy case %d | %.6e | %.6e %.6e %.6e", c, - *energy, flux[0], flux[1], flux[2]); + /* Nothing to do here. The other unphysical check will catch other problems. + */ + if (*energy_density == 0.f) return; - if (*energy <= 0.f) { - *energy = 0.f; - flux[0] = 0.f; - flux[1] = 0.f; - flux[2] = 0.f; - return; - } + /* Check for negative energies */ + if (*energy_density < 0.f) + error( + "Found particle with negative energy density after reading in ICs: " + "pid= %lld group=%d E=%.6g", + p->id, group, *energy_density); + if (*energy_density > FLT_MAX || isnan(*energy_density)) + error("Got inf/nan energy_density: %g", *energy_density); /* Check for too high fluxes */ const float flux2 = flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * flux[2]; const float flux_norm = sqrtf(flux2); - const float flux_max = rt_params.reduced_speed_of_light * *energy; - if (flux_norm > flux_max) { - const float correct = flux_max / flux_norm; - flux[0] *= correct; - flux[1] *= correct; - flux[2] *= correct; + const float flux_max = c * *energy_density; + if (flux_max > FLT_MAX || isnan(flux_max)) + error("Got inf/nan flux_max: %g", flux_max); + if (flux_norm > FLT_MAX || isnan(flux_norm)) + error("Got inf/nan flux_norm: %g", flux_norm); + if (flux_norm > flux_max * 1.0001) { + error( + "Found too high radiation flux for a particle: pid=%lld, group=%d, " + "have=%.6g, max=%.6g", + p->id, group, flux_norm, flux_max); } } @@ -157,34 +170,46 @@ rt_check_unphysical_hyperbolic_flux(float flux[4][3]) { /** * @brief check whether gas species mass fractions have physical * values and correct small errors if necessary. + * + * @param p particle to work on */ __attribute__((always_inline)) INLINE static void rt_check_unphysical_mass_fractions(struct part* restrict p) { - if (p->rt_data.tchem.mass_fraction_HI < 0.f) { + if (p->conserved.mass <= 0.f) { + /* Deal with unphysical situations and vacuum. */ + p->rt_data.tchem.mass_fraction_HI = 0.f; + p->rt_data.tchem.mass_fraction_HII = 0.f; + p->rt_data.tchem.mass_fraction_HeI = 0.f; + p->rt_data.tchem.mass_fraction_HeII = 0.f; + p->rt_data.tchem.mass_fraction_HeIII = 0.f; + return; + } + + if (p->rt_data.tchem.mass_fraction_HI <= 0.f) { if (p->rt_data.tchem.mass_fraction_HI < -1e4) message("WARNING: Got negative HI mass fraction?"); - p->rt_data.tchem.mass_fraction_HI = 0.f; + p->rt_data.tchem.mass_fraction_HI = RT_GEAR_TINY_MASS_FRACTION; } - if (p->rt_data.tchem.mass_fraction_HII < 0.f) { + if (p->rt_data.tchem.mass_fraction_HII <= 0.f) { if (p->rt_data.tchem.mass_fraction_HII < -1e4) message("WARNING: Got negative HII mass fraction?"); - p->rt_data.tchem.mass_fraction_HII = 0.f; + p->rt_data.tchem.mass_fraction_HII = RT_GEAR_TINY_MASS_FRACTION; } - if (p->rt_data.tchem.mass_fraction_HeI < 0.f) { + if (p->rt_data.tchem.mass_fraction_HeI <= 0.f) { if (p->rt_data.tchem.mass_fraction_HeI < -1e4) message("WARNING: Got negative HeI mass fraction?"); - p->rt_data.tchem.mass_fraction_HeI = 0.f; + p->rt_data.tchem.mass_fraction_HeI = RT_GEAR_TINY_MASS_FRACTION; } - if (p->rt_data.tchem.mass_fraction_HeII < 0.f) { + if (p->rt_data.tchem.mass_fraction_HeII <= 0.f) { if (p->rt_data.tchem.mass_fraction_HeII < -1e4) message("WARNING: Got negative HeII mass fraction?"); - p->rt_data.tchem.mass_fraction_HeII = 0.f; + p->rt_data.tchem.mass_fraction_HeII = RT_GEAR_TINY_MASS_FRACTION; } - if (p->rt_data.tchem.mass_fraction_HeIII < 0.f) { + if (p->rt_data.tchem.mass_fraction_HeIII <= 0.f) { if (p->rt_data.tchem.mass_fraction_HeIII < -1e4) message("WARNING: Got negative HeIII mass fraction?"); - p->rt_data.tchem.mass_fraction_HeIII = 0.f; + p->rt_data.tchem.mass_fraction_HeIII = RT_GEAR_TINY_MASS_FRACTION; } const float XHI = p->rt_data.tchem.mass_fraction_HI; diff --git a/src/rt/SPHM1RT/rt.h b/src/rt/SPHM1RT/rt.h index 2469047ea9816dd52907dfce06c2773f14155dbc..64ad8eaa263d20c609352ec48ca293f2e3be3bf1 100644 --- a/src/rt/SPHM1RT/rt.h +++ b/src/rt/SPHM1RT/rt.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) - * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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,13 +20,19 @@ #ifndef SWIFT_RT_SPHM1RT_H #define SWIFT_RT_SPHM1RT_H +#include "rt_cooling.h" +#include "rt_getters.h" #include "rt_properties.h" +#include "rt_setters.h" +#include "rt_stellar_emission_rate.h" +#include "rt_struct.h" +#include "rt_unphysical.h" #include <float.h> /** * @file src/rt/SPHM1RT/rt.h - * @brief Main header file for no radiative transfer scheme. + * @brief Main header file for SPHM1RT radiative transfer scheme. * SPHM1RT method described in Chan+21: 2102.08404 */ @@ -34,68 +40,142 @@ * @brief Initialisation of the RT density loop related particle data. * Note: during initalisation (space_init), rt_reset_part and rt_init_part * are both called individually. + * @param p particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_part( struct part* restrict p) {} /** - * @brief Reset of the RT hydro particle data not related to the density. + * @brief Reset the RT hydro particle data not related to the hydro density. * Note: during initalisation (space_init), rt_reset_part and rt_init_part - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_part is made in space_convert_rt_quantities() after - * the zeroth time step is finished. + * are both called individually. To reset RT data needed in each RT sub-cycle, + * use rt_reset_part_each_subcycle(). + * + * @param p particle to work on + * @param cosmo Cosmology. */ __attribute__((always_inline)) INLINE static void rt_reset_part( - struct part* restrict p) {} + struct part* restrict p, const struct cosmology* cosmo) {} /** - * @brief First initialisation of the RT hydro particle data. - * @param p particle to work on + * @brief Reset RT particle data which needs to be reset each sub-cycle. + * + * @param p the particle to work on + * @param cosmo Cosmology. + * @param dt the current particle RT time step */ -__attribute__((always_inline)) INLINE static void rt_first_init_part( - struct part* restrict p) {} +__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle( + struct part* restrict p, const struct cosmology* cosmo, double dt) { + + struct rt_part_data* rpd = &p->rt_data; + + for (int g = 0; g < RT_NGROUPS; g++) { + rpd->dconserved_dt[g].urad = 0.0f; + rpd->dconserved_dt[g].frad[0] = 0.0f; + rpd->dconserved_dt[g].frad[1] = 0.0f; + rpd->dconserved_dt[g].frad[2] = 0.0f; + } + + for (int g = 0; g < RT_NGROUPS; g++) { + rpd->viscosity[g].divf = 0.0f; + rpd->diffusion[g].graduradc[0] = 0.0f; + rpd->diffusion[g].graduradc[1] = 0.0f; + rpd->diffusion[g].graduradc[2] = 0.0f; + } + + /* To avoid radiation reaching other dimension and violating conservation */ + for (int g = 0; g < RT_NGROUPS; g++) { +#if defined(HYDRO_DIMENSION_1D) + rpd->conserved[g].frad[1] = 0.0f; + rpd->conserved[g].frad[2] = 0.0f; +#endif +#if defined(HYDRO_DIMENSION_2D) + rpd->conserved[g].frad[2] = 0.0f; +#endif + } + + float urad_old; + const float cred = rt_get_comoving_cred(p, cosmo->a); + for (int g = 0; g < RT_NGROUPS; g++) { + /* TK: avoid the radiation flux to violate causality. Impose a limit: F<Ec + */ + urad_old = rpd->conserved[g].urad; + rt_check_unphysical_state(&rpd->conserved[g].urad, rpd->conserved[g].frad, + urad_old, cred); + } +}; /** - * @brief Initialises particle quantities that can't be set - * otherwise before the zeroth step is finished. E.g. because - * they require the particle density to be known. + * @brief First initialisation of the RT hydro particle data. * * @param p particle to work on + * @param cosmo #cosmology data structure. * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param us unit_system struct - * @param cosmo cosmology struct */ -__attribute__((always_inline)) INLINE static void -rt_init_part_after_zeroth_step(struct part* restrict p, - const struct rt_props* rt_props, - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo) {} +__attribute__((always_inline)) INLINE static void rt_first_init_part( + struct part* restrict p, const struct cosmology* cosmo, + const struct rt_props* restrict rt_props) { + + struct rt_part_data* rpd = &p->rt_data; + + for (int g = 0; g < RT_NGROUPS; g++) { + rpd->viscosity[g].alpha = 1.0f; + rpd->diffusion[g].alpha = 1.0f; + rpd->params.chi[g] = rt_props->initialchi[g]; + rpd->viscosity[g].divf_previous_step = 0.0f; + } + + /* We can get parameters for diffusion (force loop) */ + rpd->params.cred_phys = rt_props->cred_phys; + + rpd->force.f = 1.0f; + + rpd->dt = 1.0f; + + rt_init_part(p); + rt_reset_part(p, cosmo); + rt_reset_part_each_subcycle(p, cosmo, 0.); +} /** * @brief Initialisation of the RT density loop related star particle data. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart * are both called individually. + * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_spart( - struct spart* restrict sp) {} + struct spart* restrict sp) { + + sp->rt_data.injection_weight = 0.f; + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_reinject[g] = 0.f; + } +} /** * @brief Reset of the RT star particle data not related to the density. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_spart is made in space_convert_rt_quantities() after - * the zeroth time step is finished. + * are both called individually. + * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_reset_spart( - struct spart* restrict sp) {} + struct spart* restrict sp) { + + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] = 0.f; + } +} /** * @brief First initialisation of the RT star particle data. + * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_first_init_spart( - struct spart* restrict sp) {} + struct spart* restrict sp) { + + rt_init_spart(sp); + rt_reset_spart(sp); +} /** * @brief Split the RT data of a particle into n pieces @@ -104,7 +184,9 @@ __attribute__((always_inline)) INLINE static void rt_first_init_spart( * @param n The number of pieces to split into. */ __attribute__((always_inline)) INLINE static void rt_split_part(struct part* p, - double n) {} + double n) { + error("RT can't run with split particles for now."); +} /** * @brief Exception handle a hydro part not having any neighbours in ghost task @@ -112,38 +194,80 @@ __attribute__((always_inline)) INLINE static void rt_split_part(struct part* p, * @param p The #part. */ __attribute__((always_inline)) INLINE static void rt_part_has_no_neighbours( - struct part* p){}; + struct part* p) { + message("WARNING: found particle without neighbours"); +}; /** * @brief Exception handle a star part not having any neighbours in ghost task * - * @param p The #part. + * @param sp The #spart. */ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( - struct spart* sp){}; + struct spart* sp) { + message("WARNING: found star without neighbours"); +}; /** * @brief Do checks/conversions on particles on startup. * * @param p The particle to work on - * @param rtp The RT properties struct + * @param rt_props The RT properties struct + * @param hydro_props The hydro properties struct + * @param phys_const physical constants struct + * @param us unit_system struct + * @param cosmo cosmology struct */ __attribute__((always_inline)) INLINE static void rt_convert_quantities( - struct part* p, const struct rt_props* rtp){}; + struct part* restrict p, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo) { + + struct rt_part_data* rpd = &p->rt_data; + /* Note that in the input, we read radiation energy and flux + * then we convert these quantities to radiation energy per mass and flux per + * mass + */ + for (int g = 0; g < RT_NGROUPS; g++) { + rpd->conserved[g].urad = rpd->conserved[g].urad / p->mass; + rpd->conserved[g].frad[0] = rpd->conserved[g].frad[0] / p->mass; + rpd->conserved[g].frad[1] = rpd->conserved[g].frad[1] / p->mass; + rpd->conserved[g].frad[2] = rpd->conserved[g].frad[2] / p->mass; + } + + /* rpd->cred_phys and rt_props->cred_phys are in physical unit */ + rpd->params.cred_phys = rt_props->cred_phys; + + /* Initialize element mass fractions accoridng to parameter files. */ + rt_tchem_first_init_part(p, rt_props, phys_const, us, cosmo); +} /** * @brief Computes the next radiative transfer time step size * of a given particle (during timestep tasks) * - * @param p particle to work on - * @param rt_props the RT properties struct - * @param cosmo the cosmology + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @return dt The time-step of this particle. */ __attribute__((always_inline)) INLINE static float rt_compute_timestep( - const struct part* restrict p, const struct rt_props* restrict rt_props, - const struct cosmology* restrict cosmo) { + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us) { - return FLT_MAX; + float cred_phys = rt_get_physical_cred(p, cosmo->a); + float dt = p->h * cosmo->a / cred_phys * rt_props->CFL_condition; + + return dt; } /** @@ -153,17 +277,23 @@ __attribute__((always_inline)) INLINE static float rt_compute_timestep( * @param sp spart to work on * @param rt_props the RT properties struct * @param cosmo the cosmology + * + * @return star time step */ __attribute__((always_inline)) INLINE static float rt_compute_spart_timestep( const struct spart* restrict sp, const struct rt_props* restrict rt_props, const struct cosmology* restrict cosmo) { - return FLT_MAX; + /* For now, the only thing we care about is the upper threshold for stars. */ + return rt_props->stars_max_timestep; } /** * @brief Compute the time-step length for an RT step of a particle from given - * integer times ti_beg and ti_end + * integer times ti_beg and ti_end. This time-step length is then used to + * compute the actual time integration of the transport/force step and the + * thermochemistry. This is not used to determine the time-step length during + * the time-step tasks. * * @param ti_beg Start of the time-step (on the integer time-line). * @param ti_end End of the time-step (on the integer time-line). @@ -177,20 +307,22 @@ __attribute__((always_inline)) INLINE static double rt_part_dt( const integertime_t ti_beg, const integertime_t ti_end, const double time_base, const int with_cosmology, const struct cosmology* cosmo) { - return 0.0; + if (with_cosmology) { + error("SPHM1RT with cosmology not implemented yet! :("); + return 0.f; + } else { + return (ti_end - ti_beg) * time_base; + } } /** - * @brief Update the photon number of a particle, i.e. compute - * E^{n+1} = E^n + dt * dE_* / dt. This function finalises - * the injection step. + * @brief This function finalises the injection step. * * @param p particle to work on * @param props struct #rt_props that contains global RT properties */ -__attribute__((always_inline)) INLINE static void -rt_injection_update_photon_density(struct part* restrict p, - struct rt_props* props) {} +__attribute__((always_inline)) INLINE static void rt_finalise_injection( + struct part* restrict p, struct rt_props* props) {} /** * @brief Compute the photon emission rates for this stellar particle @@ -212,24 +344,160 @@ rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, double star_age, double dt, const struct rt_props* rt_props, const struct phys_const* phys_const, - const struct unit_system* internal_units) {} + const struct unit_system* internal_units) { + + /* Skip initial fake time-step */ + if (dt == 0.0l) return; + + if (time == 0.l) { + /* if function is called before the first actual step, time is still + * at zero unless specified otherwise in parameter file.*/ + star_age = dt; + } + + /* now get the emission rates */ + double star_age_begin_of_step = star_age - dt; + star_age_begin_of_step = max(0.l, star_age_begin_of_step); + rt_set_stellar_emission_rate(sp, star_age_begin_of_step, star_age, rt_props, + phys_const, internal_units); +} /** * @brief finishes up the gradient computation * * @param p particle to work on + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_end_gradient( - struct part* restrict p) {} + struct part* restrict p, const struct cosmology* cosmo) { + struct rt_part_data* rpd = &p->rt_data; + /* artificial diffusion for shock capturing */ + const float vsig_diss = rt_get_comoving_cred(p, cosmo->a); + /* similar to Cullen & Dehnen 2010 switch */ + float divf, divf_previous_step, urad, viscosity_alpha, diffusion_alpha; + float divf_dt, shockest, alphaflim, alpha_f_diss, alpha_f_diss_loc; + float alpha_diss_loc, alpha_diss; + if (rpd->dt == 0) return; + + for (int g = 0; g < RT_NGROUPS; g++) { + divf = rpd->viscosity[g].divf; + divf_previous_step = rpd->viscosity[g].divf_previous_step; + urad = rpd->conserved[g].urad; + viscosity_alpha = rpd->viscosity[g].alpha; + diffusion_alpha = rpd->diffusion[g].alpha; + divf_dt = (divf - divf_previous_step) / (rpd->dt); + + if (urad == 0.f) { + shockest = FLT_MAX; + } else { + shockest = -p->h * p->h / (vsig_diss) / (vsig_diss)*divf_dt * 200.f; + shockest /= urad; + } + alphaflim = max(shockest, 0.0f); /* should be positive or 0 */ + alpha_f_diss = viscosity_alpha; + alpha_f_diss_loc = 0.0f; + + /* f diffusion only operates in compression */ + if (divf < 0.0f) { + /* limit the diffusivity to Courant time step */ + alpha_f_diss_loc = min(alphaflim, 1.0f); + } + + if (alpha_f_diss_loc > alpha_f_diss) { + /* Reset the value of alpha to the appropriate value */ + alpha_f_diss = alpha_f_diss_loc; + } else { + /* Integrate the alpha forward in time to decay back to alpha = alpha_loc + */ + alpha_f_diss = alpha_f_diss_loc + + (alpha_f_diss - alpha_f_diss_loc) * + expf(-rpd->dt * vsig_diss * + (1.f / p->h + rpd->params.chi[g] * p->rho)); + } + + /* alpha inspired by Price 2010: it should vanish where radiation energy + * difference is small */ + alpha_diss_loc = 1.0f; + alpha_diss = diffusion_alpha; + if (alpha_diss_loc > alpha_diss) { + /* Reset the value of alpha to the appropriate value */ + alpha_diss = alpha_diss_loc; + } else { + /* Integrate the alpha forward in time to decay back to alpha = alpha_loc + */ + alpha_diss = alpha_diss_loc + + (alpha_diss - alpha_diss_loc) * + expf(-rpd->dt * vsig_diss * + (0.01f / p->h + rpd->params.chi[g] * p->rho)); + } + + /* Cap the dissipation to avoid instabilities */ + alpha_diss = min(alpha_diss, 1.0f); + alpha_diss = max(alpha_diss, 0.0f); + + alpha_f_diss = min(alpha_f_diss, 1.0f); + alpha_f_diss = max(alpha_f_diss, 0.0f); + + rpd->diffusion[g].alpha = alpha_diss; + rpd->viscosity[g].alpha = alpha_f_diss; + } +} /** * @brief finishes up the transport step * * @param p particle to work on * @param dt the current time step of the particle + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_finalise_transport( - struct part* restrict p, const double dt) {} + struct part* restrict p, const double dt, + const struct cosmology* restrict cosmo) { + struct rt_part_data* rpd = &p->rt_data; + + for (int g = 0; g < RT_NGROUPS; g++) { + rpd->conserved[g].urad += rpd->dconserved_dt[g].urad * dt; + rpd->conserved[g].frad[0] += rpd->dconserved_dt[g].frad[0] * dt; + rpd->conserved[g].frad[1] += rpd->dconserved_dt[g].frad[1] * dt; + rpd->conserved[g].frad[2] += rpd->dconserved_dt[g].frad[2] * dt; + } + + /* add frad source term implicitly */ + float dfrac, cred; + cred = rt_get_comoving_cred(p, cosmo->a); + for (int g = 0; g < RT_NGROUPS; g++) { + dfrac = -rpd->params.chi[g] * p->rho * cred; + rpd->conserved[g].frad[0] *= expf(dfrac * dt); + rpd->conserved[g].frad[1] *= expf(dfrac * dt); + rpd->conserved[g].frad[2] *= expf(dfrac * dt); + + /* update urad */ + /* limiter to avoid negative urad */ + /* negative urad will make the dissipation (diffusion) unstable) */ + if (rpd->conserved[g].urad < 0.0f) { + rpd->conserved[g].urad = 0.0f; + rpd->conserved[g].frad[0] = 0.0f; + rpd->conserved[g].frad[1] = 0.0f; + rpd->conserved[g].frad[2] = 0.0f; + } + + /* save next time step */ + rpd->viscosity[g].divf_previous_step = rpd->viscosity[g].divf; + } + + rpd->dt = dt; + + /* To avoid radiation reaching other dimension and violating conservation */ + for (int g = 0; g < RT_NGROUPS; g++) { +#if defined(HYDRO_DIMENSION_1D) + rpd->conserved[g].frad[1] = 0.0f; + rpd->conserved[g].frad[2] = 0.0f; +#endif +#if defined(HYDRO_DIMENSION_2D) + rpd->conserved[g].frad[2] = 0.0f; +#endif + } +} /** * @brief Do the thermochemistry on a particle. @@ -243,12 +511,11 @@ __attribute__((always_inline)) INLINE static void rt_finalise_transport( * @param us The internal system of units. * @param dt The time-step of this particle. */ -__attribute__((always_inline)) INLINE static void rt_tchem( - struct part* restrict p, struct xpart* restrict xp, - struct rt_props* rt_props, const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, const double dt) {} +void rt_tchem(struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, const double dt); /** * @brief Extra operations done during the kick. @@ -277,14 +544,41 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra( * @param p particle to work on **/ __attribute__((always_inline)) INLINE static void rt_prepare_force( - struct part* p) {} + struct part* p) { + + struct rt_part_data* rpd = &p->rt_data; + + /* Some smoothing length multiples. */ + const float rho = hydro_get_comoving_density(p); + const float rho_inv = 1.0f / rho; /* 1 / rho */ + + /* Compute the "grad h" term */ + float rho_dh = p->density.rho_dh; + + const float omega_inv = + 1.f / (1.f + hydro_dimension_inv * p->h * rho_dh * rho_inv); + + /* Update variables. */ + rpd->force.f = omega_inv; +} + +/** + * @brief Extra operations to be done during the drift + * + * @param p Particle to act upon. + * @param xp The extended particle data to act upon. + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void rt_predict_extra( + struct part* p, struct xpart* xp, float dt_drift) {} /** * @brief Clean the allocated memory inside the RT properties struct. * * @param props the #rt_props. + * @param restart did we restart? */ __attribute__((always_inline)) INLINE static void rt_clean( - struct rt_props* props) {} + struct rt_props* props, int restart) {} #endif /* SWIFT_RT_SPHM1RT_H */ diff --git a/src/rt/SPHM1RT/rt_additions.h b/src/rt/SPHM1RT/rt_additions.h index 39f0c8a4646474108d3c75e157b178d761a85b2a..9ef0549ca9bc02962ee980bb42337d52f1e286fa 100644 --- a/src/rt/SPHM1RT/rt_additions.h +++ b/src/rt/SPHM1RT/rt_additions.h @@ -33,7 +33,7 @@ * @param pj second interacting particle * @param mass_flux the mass flux between these two particles * @param mode 0: non-symmetric interaction, update i only. 1: symmetric - *interaction. + * interaction. **/ __attribute__((always_inline)) INLINE static void rt_part_update_mass_fluxes( struct part* restrict pi, struct part* restrict pj, float mass_flux, diff --git a/src/rt/SPHM1RT/rt_cooling.c b/src/rt/SPHM1RT/rt_cooling.c new file mode 100644 index 0000000000000000000000000000000000000000..d97f14e74b6b778d8f0e0c49249e0ed57d8d947d --- /dev/null +++ b/src/rt/SPHM1RT/rt_cooling.c @@ -0,0 +1,551 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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 src/rt/SPHM1RT/rt_cooling.c + * @brief SPHM1RT cooling functions + */ + +/* Some standard headers. */ +#include <cvode/cvode.h> +#include <cvode/cvode_direct.h> /* access to CVDls interface */ +#include <string.h> +#include <sunlinsol/sunlinsol_dense.h> +#include <sunmatrix/sunmatrix_dense.h> + +/* Local includes. */ +#include "rt_cooling.h" +#include "rt_cooling_rates.h" +#include "rt_getters.h" +#include "rt_setters.h" + +/** + * @brief Main function for the thermochemistry step. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + */ +void rt_do_thermochemistry(struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const double dt) { + + /* Nothing to do here? */ + if (rt_props->skip_thermochemistry == 1) return; + if (dt == 0.0) return; + + rt_check_unphysical_elem_spec(p, rt_props); + + struct rt_part_data* rpd = &p->rt_data; + + struct RTUserData data; /* data for CVODE */ + + const double dt_cgs = dt * units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + const double conv_factor_internal_energy_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + const double conv_factor_opacity_from_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_MASS) / + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH) / + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + + // TK remark: here we only consider the on-the-spot approximation + + /**************************/ + /* INITIALIZATION */ + /**************************/ + + int useparams = rt_props->useparams; + + /* adopt on the spot approximation (OTSA) by default */ + /* TODO: currently there is no non-OTSA implementation; to do in the future */ + int onthespot = rt_props->onthespot; + data.onthespot = onthespot; + + int coolingon = rt_props->coolingon; + data.coolingon = coolingon; + + int fixphotondensity = rt_props->fixphotondensity; + data.fixphotondensity = fixphotondensity; + + double metal_mass_fraction[rt_chemistry_element_count]; + + for (int elem = 0; elem < rt_chemistry_element_count; elem++) { + metal_mass_fraction[elem] = (double)(rpd->tchem.metal_mass_fraction[elem]); + data.metal_mass_fraction[elem] = metal_mass_fraction[elem]; + } + + const double X_H = metal_mass_fraction[rt_chemistry_element_H]; + + const double m_H_cgs = phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_MASS); + const double proton_mass_cgs_inv = 1.0 / m_H_cgs; + data.m_H_cgs = m_H_cgs; + + const double k_B_cgs = phys_const->const_boltzmann_k * + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) / + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + data.k_B_cgs = k_B_cgs; + + const double cred_phys = rt_get_physical_cred(p, cosmo->a); + const double cred_cgs = + cred_phys * units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + data.cred_cgs = cred_cgs; + + /* Get particle density [ and convert to g * cm^-3] */ + const double rho = hydro_get_physical_density(p, cosmo); + double rho_cgs = rho * units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + data.rho_cgs = rho_cgs; + + /* Hydrogen number density (X_H * rho / m_p) [cm^-3] */ + const double n_H_cgs = X_H * rho_cgs * proton_mass_cgs_inv; + data.n_H_cgs = n_H_cgs; + + /* Current energy (in internal units) */ + float urad[RT_NGROUPS]; + rt_get_physical_urad_multifrequency(p, cosmo, urad); + + /* need to convert to cgs */ + double ngamma_cgs[3]; + /* for now, the 0th bin for urad is 0-HI, so we ignore it */ + for (int g = 0; g < 3; g++) { + ngamma_cgs[g] = + (double)(rho_cgs * urad[g + 1] * conv_factor_internal_energy_to_cgs / + rt_props->ionizing_photon_energy_cgs[g]); + data.ngamma_cgs[g] = ngamma_cgs[g]; + } + + /* overwrite the photon density if we choose to fix it */ + for (int i = 0; i < 3; i++) { + if ((rt_props->Fgamma_fixed_cgs[i] > 0.0) && + (rt_props->fixphotondensity == 1)) { + ngamma_cgs[i] = rt_props->Fgamma_fixed_cgs[i] / cred_cgs; + data.ngamma_cgs[i] = ngamma_cgs[i]; + urad[i + 1] = (float)(data.ngamma_cgs[i] / rho_cgs / + conv_factor_internal_energy_to_cgs * + rt_props->ionizing_photon_energy_cgs[i]); + } + } + + double abundances[rt_species_count]; + + for (int spec = 0; spec < rt_species_count; spec++) { + abundances[spec] = (double)(rpd->tchem.abundances[spec]); + data.abundances[spec] = abundances[spec]; + } + + const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + + double u_cgs = u * conv_factor_internal_energy_to_cgs; + + double T_cgs = rt_convert_u_to_temp(k_B_cgs, m_H_cgs, X_H, u_cgs, abundances); + + double T_min_cgs = hydro_props->minimal_temperature; + + double u_min_cgs = + rt_convert_temp_to_u(k_B_cgs, m_H_cgs, T_min_cgs, X_H, abundances); + + u_cgs = fmax(u_cgs, u_min_cgs); + + data.u_cgs = u_cgs; + + data.u_min_cgs = u_min_cgs; + + /**************************/ + /* GET RATE COEFFICIENTS */ + /**************************/ + int aindex[3]; + + rt_get_index_to_species(aindex); + for (int i = 0; i < 3; i++) { + data.aindex[i] = aindex[i]; + } + + double alphalist[rt_species_count], betalist[rt_species_count], + Gammalist[rt_species_count], sigmalist[3][3], epsilonlist[3][3]; + + if (useparams == 1) { + betalist[rt_sp_elec] = 0.0; + betalist[rt_sp_HI] = rt_props->beta_cgs_H; + betalist[rt_sp_HII] = 0.0; + betalist[rt_sp_HeI] = 0.0; + betalist[rt_sp_HeII] = 0.0; + betalist[rt_sp_HeIII] = 0.0; + alphalist[rt_sp_elec] = 0.0; + alphalist[rt_sp_HI] = 0.0; + alphalist[rt_sp_HeI] = 0.0; + if (onthespot == 1) { + alphalist[rt_sp_HII] = rt_props->alphaB_cgs_H; + alphalist[rt_sp_HeII] = 0.0; + alphalist[rt_sp_HeIII] = 0.0; + } else { + alphalist[rt_sp_HII] = rt_props->alphaA_cgs_H; + alphalist[rt_sp_HeII] = 0.0; + alphalist[rt_sp_HeIII] = 0.0; + } + sigmalist[0][0] = rt_props->sigma_cross_cgs_H[0]; + sigmalist[1][0] = rt_props->sigma_cross_cgs_H[1]; + sigmalist[2][0] = rt_props->sigma_cross_cgs_H[2]; + sigmalist[0][1] = 0.0; + sigmalist[1][1] = 0.0; + sigmalist[2][1] = 0.0; + sigmalist[0][2] = 0.0; + sigmalist[1][2] = 0.0; + sigmalist[2][2] = 0.0; + data.alphaA_cgs_H = rt_props->alphaA_cgs_H; + data.alphaB_cgs_H = rt_props->alphaB_cgs_H; + data.beta_cgs_H = rt_props->beta_cgs_H; + data.sigma_cross_cgs_H[0] = rt_props->sigma_cross_cgs_H[0]; + data.sigma_cross_cgs_H[1] = rt_props->sigma_cross_cgs_H[1]; + data.sigma_cross_cgs_H[2] = rt_props->sigma_cross_cgs_H[2]; + for (int spec = 0; spec < rt_species_count; spec++) { + Gammalist[spec] = 0.0; + } + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + epsilonlist[i][j] = 0.0; + } + } + } else { + rt_compute_rate_coefficients(T_cgs, onthespot, alphalist, betalist, + Gammalist, sigmalist, epsilonlist); + } + + data.useparams = rt_props->useparams; + + /**************************/ + /* SOLVING RATE EQUTAION */ + /**************************/ + + /* Try explicit solution */ + + double new_abundances[rt_species_count], finish_abundances[rt_species_count], + max_relative_change, new_ngamma_cgs[3], u_new_cgs; + + max_relative_change = 0.0; + /* compute net changes and cooling and heating for explicit solution */ + rt_compute_explicit_thermochemistry_solution( + n_H_cgs, cred_cgs, dt_cgs, rho_cgs, u_cgs, u_min_cgs, abundances, + ngamma_cgs, alphalist, betalist, Gammalist, sigmalist, epsilonlist, + aindex, &u_new_cgs, new_abundances, new_ngamma_cgs, &max_relative_change); + + /* check whether xHI bigger than one */ + int errorHI = 0; + if (new_abundances[rt_sp_HI] > 1.01) { + errorHI = 1; + } else { + rt_enforce_constraint_equations(new_abundances, metal_mass_fraction, + finish_abundances); + } + + if ((max_relative_change < rt_props->explicitRelTolerance) && + (errorHI == 0)) { + for (int spec = 0; spec < rt_species_count; spec++) { + if (finish_abundances[spec] > 0.f) { + if (finish_abundances[spec] < FLT_MAX) { + rpd->tchem.abundances[spec] = (float)(finish_abundances[spec]); + } else { + error("finish_abundances larger than FLT_MAX"); + } + } else { + rpd->tchem.abundances[spec] = 0.f; + } + } + if (coolingon == 1) { + float u_new = 0.0f; + if (u_new_cgs / conv_factor_internal_energy_to_cgs > 0.f) { + if (u_new_cgs / conv_factor_internal_energy_to_cgs < FLT_MAX) { + u_new = (float)(u_new_cgs / conv_factor_internal_energy_to_cgs); + } + } + hydro_set_physical_internal_energy(p, xp, cosmo, u_new); + } + + /* set radiation energy */ + float urad_new[RT_NGROUPS]; + urad_new[0] = 0.f; + if (fixphotondensity == 0) { + for (int i = 0; i < 3; i++) { + urad_new[i + 1] = 0.f; + if (new_ngamma_cgs[i] / rho_cgs / conv_factor_internal_energy_to_cgs * + rt_props->ionizing_photon_energy_cgs[i] > + 0.f) { + if (new_ngamma_cgs[i] / rho_cgs / conv_factor_internal_energy_to_cgs * + rt_props->ionizing_photon_energy_cgs[i] < + FLT_MAX) { + urad_new[i + 1] = (float)(new_ngamma_cgs[i] / rho_cgs / + conv_factor_internal_energy_to_cgs * + rt_props->ionizing_photon_energy_cgs[i]); + } + } + } + } else { + for (int i = 0; i < 3; i++) { + urad_new[i + 1] = urad[i + 1]; + } + } + rt_set_physical_urad_multifrequency(p, cosmo, urad_new); + + /* chi is in physical unit (L^2/M) */ + float chi_new[RT_NGROUPS]; + for (int i = 0; i < RT_NGROUPS; i++) { + chi_new[i] = 0.0f; + } + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (finish_abundances[aindex[j]] * n_H_cgs / rho_cgs * sigmalist[i][j] * + conv_factor_opacity_from_cgs > + 0.f) { + if (finish_abundances[aindex[j]] * n_H_cgs / rho_cgs * + sigmalist[i][j] * conv_factor_opacity_from_cgs < + FLT_MAX) { + chi_new[i + 1] += + (float)(finish_abundances[aindex[j]] * n_H_cgs / rho_cgs * + sigmalist[i][j] * conv_factor_opacity_from_cgs); + } + } + } + } + rt_set_physical_radiation_opacity(p, cosmo, chi_new); + + rt_check_unphysical_elem_spec(p, rt_props); + + return; + + } else { + + /************************************** + * Explicit solution is insufficient. * + * Use implicit solver. * + **************************************/ + realtype reltol, t; + N_Vector abstol_vector, y; + + int maxsteps = 100000; + int network_size, icount = 0; + /* 3 for species; */ + network_size = 3; + /* 1 for thermal energy; */ + if (coolingon == 1) { + network_size += 1; + } + /* 3 for radiation bins */ + if (fixphotondensity == 0) { + network_size += 3; + } + + y = N_VNew_Serial(network_size); + abstol_vector = N_VNew_Serial(network_size); + for (int i = 0; i < 3; i++) { + NV_Ith_S(y, icount) = (realtype)data.abundances[aindex[i]]; + NV_Ith_S(abstol_vector, icount) = (realtype)(rt_props->absoluteTolerance); + icount += 1; + } + if (coolingon == 1) { + NV_Ith_S(y, icount) = (realtype)u_cgs; + NV_Ith_S(abstol_vector, icount) = (realtype)rt_props->absoluteTolerance; + icount += 1; + } + if (fixphotondensity == 0) { + for (int i = 0; i < 3; i++) { + NV_Ith_S(y, icount) = (realtype)data.ngamma_cgs[i]; + NV_Ith_S(abstol_vector, icount) = (realtype)rt_props->absoluteTolerance; + icount += 1; + } + } + /* Set up the solver */ + /* Set the tolerances*/ + reltol = (realtype)rt_props->relativeTolerance; + + /* Use CVodeCreate to create the solver + * memory and specify the Backward Differentiation + * Formula. Note that CVODE now uses Newton iteration + * iteration by default, so no need to specify this. */ + void* cvode_mem; + cvode_mem = CVodeCreate(CV_BDF); + + /* Set the user data for CVode */ + CVodeSetUserData(cvode_mem, &data); + + /* Use CVodeSetMaxNumSteps to set the maximum number + * of steps CVode takes. */ + CVodeSetMaxNumSteps(cvode_mem, maxsteps); + + /* Use CVodeInit to initialise the integrator + * memory and specify the right hand side + * function in y' = f(t,y) (i.e. the rate + * equations), the initial time 0.0 and the + * initial conditions, in y. */ + CVodeInit(cvode_mem, rt_frateeq, 0.0f, y); + + /* Use CVodeSVtolerances to specify the scalar + * relative and absolute tolerances. */ + CVodeSVtolerances(cvode_mem, reltol, abstol_vector); + + /* Create a dense SUNMatrix to use in the + * linear solver. */ + SUNMatrix A_sun; + + A_sun = SUNDenseMatrix(network_size, network_size); + + /* Create a denst SUNLinearSolver object + * to use in CVode. */ + SUNLinearSolver LS_sun; + LS_sun = SUNDenseLinearSolver(y, A_sun); + + /* Attach the matrix and linear + * solver to CVode. */ + CVDlsSetLinearSolver(cvode_mem, LS_sun, A_sun); + + /* Specify the maximum number of convergence + * test failures. */ + CVodeSetMaxConvFails(cvode_mem, 5000); + + /* Call CVode() to integrate the chemistry. */ + CVode(cvode_mem, (realtype)dt_cgs, y, &t, CV_NORMAL); + + /* Write the output abundances to the gas cell + * Note that species not included in the reduced + * network are kept constant in the GasVars struct. */ + icount = 0; + for (int i = 0; i < 3; i++) { + new_abundances[aindex[i]] = (double)NV_Ith_S(y, icount); + icount += 1; + } + if (coolingon == 1) { + u_cgs = (double)NV_Ith_S(y, icount); + icount += 1; + } + + if (fixphotondensity == 0) { + for (int i = 0; i < 3; i++) { + new_ngamma_cgs[i] = (double)NV_Ith_S(y, icount); + icount += 1; + } + } + + if (new_abundances[rt_sp_HI] > 1.01) + error("HI fraction bigger than one after the CVODE solver"); + rt_enforce_constraint_equations(new_abundances, metal_mass_fraction, + finish_abundances); + for (int spec = 0; spec < rt_species_count; spec++) { + if (finish_abundances[spec] > 0.f) { + if (finish_abundances[spec] < FLT_MAX) { + rpd->tchem.abundances[spec] = (float)(finish_abundances[spec]); + } else { + error("finish_abundances larger than FLT_MAX"); + } + } else { + rpd->tchem.abundances[spec] = 0.f; + } + } + if (coolingon == 1) { + float u_new = 0.0f; + if (u_new_cgs / conv_factor_internal_energy_to_cgs > 0.f) { + if (u_new_cgs / conv_factor_internal_energy_to_cgs < FLT_MAX) { + u_new = (float)(u_new_cgs / conv_factor_internal_energy_to_cgs); + } + } + hydro_set_physical_internal_energy(p, xp, cosmo, u_new); + } + /* set radiation energy */ + float urad_new[RT_NGROUPS]; + urad_new[0] = 0.f; + if (fixphotondensity == 0) { + for (int i = 0; i < 3; i++) { + urad_new[i + 1] = 0.f; + if (new_ngamma_cgs[i] / rho_cgs / conv_factor_internal_energy_to_cgs * + rt_props->ionizing_photon_energy_cgs[i] > + 0.f) { + if (new_ngamma_cgs[i] / rho_cgs / conv_factor_internal_energy_to_cgs * + rt_props->ionizing_photon_energy_cgs[i] < + FLT_MAX) { + urad_new[i + 1] = (float)(new_ngamma_cgs[i] / rho_cgs / + conv_factor_internal_energy_to_cgs * + rt_props->ionizing_photon_energy_cgs[i]); + } + } + } + } else { + for (int i = 0; i < 3; i++) { + urad_new[i + 1] = urad[i + 1]; + } + } + rt_set_physical_urad_multifrequency(p, cosmo, urad_new); + + /* chi is in physical unit (L^2/M) */ + float chi_new[RT_NGROUPS]; + for (int i = 0; i < RT_NGROUPS; i++) { + chi_new[i] = 0.0f; + } + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (finish_abundances[aindex[j]] * n_H_cgs / rho_cgs * sigmalist[i][j] * + conv_factor_opacity_from_cgs > + 0.f) { + if (finish_abundances[aindex[j]] * n_H_cgs / rho_cgs * + sigmalist[i][j] * conv_factor_opacity_from_cgs < + FLT_MAX) { + chi_new[i + 1] += + (float)(finish_abundances[aindex[j]] * n_H_cgs / rho_cgs * + sigmalist[i][j] * conv_factor_opacity_from_cgs); + } + } + } + } + rt_set_physical_radiation_opacity(p, cosmo, chi_new); + SUNLinSolFree(LS_sun); + SUNMatDestroy(A_sun); + N_VDestroy_Serial(y); + N_VDestroy_Serial(abstol_vector); + CVodeFree(&cvode_mem); + + rt_check_unphysical_elem_spec(p, rt_props); + } +} + +/** + * @brief Do the thermochemistry on a particle. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + */ +void rt_tchem(struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, const double dt) { + rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, us, + dt); +} diff --git a/src/rt/SPHM1RT/rt_cooling.h b/src/rt/SPHM1RT/rt_cooling.h new file mode 100644 index 0000000000000000000000000000000000000000..4d1118c686aabee50d4c603c5e2642157260f6a6 --- /dev/null +++ b/src/rt/SPHM1RT/rt_cooling.h @@ -0,0 +1,88 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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_RT_SPHM1RT_COOLING_H +#define SWIFT_RT_SPHM1RT_COOLING_H +/** + * @file src/rt/SPHM1RT/rt_cooling.h + * @brief Main header file for the SPHM1RT radiative transfer scheme + * thermochemistry related functions. + */ + +#include "rt_properties.h" +#include "rt_unphysical.h" + +/** + * @brief initialize particle quantities relevant for the thermochemistry. + * + * @param p part to work with + * @param rt_props rt_properties struct + * @param phys_const physical constants struct + * @param us unit system struct + * @param cosmo cosmology struct + */ +__attribute__((always_inline)) INLINE static void rt_tchem_first_init_part( + struct part* restrict p, const struct rt_props* rt_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo) { + + struct rt_part_data* rpd = &p->rt_data; + + /* Initialize mass fractions for total metals and each metal individually */ + if (rt_props->initial_metal_mass_fraction_total != -1.f) { + for (int elem = 0; elem < rt_chemistry_element_count; ++elem) { + rpd->tchem.metal_mass_fraction[elem] = + rt_props->initial_metal_mass_fraction[elem]; + } + } + + /* Initialize species from parameter files */ + if (rt_props->useabundances != 0) { + for (int spec = 0; spec < rt_species_count; ++spec) { + rpd->tchem.abundances[spec] = rt_props->initial_species_abundance[spec]; + } + } + + if (rt_props->skip_thermochemistry != 1) { + /* Check that we didn't do something stupid */ + rt_check_unphysical_elem_spec(p, rt_props); + } +} + +/** + * @brief Main function for the thermochemistry step. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + */ +void rt_do_thermochemistry(struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const double dt); + +#endif /* SWIFT_RT_SPHM1RT_COOLING_H */ diff --git a/src/rt/SPHM1RT/rt_cooling_rates.h b/src/rt/SPHM1RT/rt_cooling_rates.h new file mode 100644 index 0000000000000000000000000000000000000000..ab53317c027ddbff036588d7ec6ccdb216413ec3 --- /dev/null +++ b/src/rt/SPHM1RT/rt_cooling_rates.h @@ -0,0 +1,791 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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_RT_SPHM1RT_COOLING_RATES_H +#define SWIFT_RT_SPHM1RT_COOLING_RATES_H + +/* Local includes. */ +#include "error.h" +#include "rt_species_and_elements.h" + +#include <math.h> +#include <nvector/nvector_serial.h> +#include <sundials/sundials_types.h> + +struct RTUserData { + + // void *cvode_mem; /*!< Pointer to the CVODE memory. */ + + /* switch for on the spot approximation */ + int onthespot; + + /* switch for gas cooling */ + int coolingon; + + /* switch for not changing photon density */ + int fixphotondensity; + + /* 1: to use the input parameters; 0: calculate with temperature. */ + /* (H coefficient only; no heating or cooling) */ + int useparams; + + /*! Fraction of the particle mass in a given element */ + double metal_mass_fraction[rt_chemistry_element_count]; + + double m_H_cgs; + + double k_B_cgs; + + double cred_cgs; + + double rho_cgs; + + double n_H_cgs; + + double ngamma_cgs[3]; + + double u_cgs; + + /*! abundances of species i, i.e. n_i/nH */ + /* note that we use hydrogen density in the denominator */ + double abundances[rt_species_count]; + + double u_min_cgs; + + int aindex[3]; + + /* only use when useparam = 1 */ + /*! The case A recombination coefficient for hydrogen (cgs) */ + double alphaA_cgs_H; + + /*! The case B recombination coefficient for hydrogen (cgs) */ + double alphaB_cgs_H; + + /*! The collisional ionization coefficient for hydrogen (cgs) */ + double beta_cgs_H; + + /*! The cross section of ionizing photons for hydrogen (cgs) */ + double sigma_cross_cgs_H[3]; +}; + +/** + * @brief Computes the temperature from internal energy u + * + * @param k_B_cgs boltzman constant in cgs + * @param m_H_cgs hydrogen atom mass in cgs + * @param X_H hydrogen mass fraction + * @param u_cgs internal energy in cgs. + * @param abundances abundances of species in n_i/nH. + * + * @return T_cgs temperature in K. + * + */ +__attribute__((always_inline)) INLINE static double rt_convert_u_to_temp( + const double k_B_cgs, const double m_H_cgs, const double X_H, + const double u_cgs, const double abundances[rt_species_count]) { + + double sumabundances = 0.0; + for (int spec = 0; spec < rt_species_count; spec++) { + sumabundances += abundances[spec]; + } + double T_cgs = m_H_cgs / X_H / sumabundances * u_cgs * 2.0 / 3.0 / k_B_cgs; + return T_cgs; +} + +/** + * @brief Computes the internal energy corresponding to a given + * temperature, hydrogen neutral fraction + * + * @param k_B_cgs boltzman constant in cgs + * @param m_H_cgs hydrogen atom mass in cgs + * @param T_cgs temperature in K + * @param X_H hydrogen mass fraction + * @param abundances abundances of species in n_i/nH. + * + * @return u_cgs the internal energy in cgs + * + */ +__attribute__((always_inline)) INLINE static double rt_convert_temp_to_u( + const double k_B_cgs, const double m_H_cgs, const double T_cgs, + const double X_H, const double abundances[rt_species_count]) { + double sumabundances = 0.0; + for (int spec = 0; spec < rt_species_count; spec++) { + sumabundances += abundances[spec]; + } + const double u_cgs = 1.5 * k_B_cgs * T_cgs * sumabundances * X_H / m_H_cgs; + return u_cgs; +} + +/**************************************/ +/* PHOTO-IONIZATION COEFFICIENTS */ +/**************************************/ + +/** + * @brief Output the array translating to species + * @return aindex use to translate index to species + */ +INLINE static void rt_get_index_to_species(int aindex[3]) { + /* the first index denotes frequency bins; the second index denotes species */ + /* HI: index 0 */ + aindex[0] = rt_sp_HI; /* use to translate index to species */ + /* HeI: index 1 */ + aindex[1] = rt_sp_HeI; /* use to translate index to species */ + /* HeII: index 2 */ + aindex[2] = rt_sp_HeII; /* use to translate index to species */ +} + +/**************************************/ +/* RECOMBINATION COEFFICIENTS */ +/**************************************/ + +/** + * @brief Computes the chemistry coefficient (Hui and Gnedin 1997) + * https://ui.adsabs.harvard.edu/abs/1997MNRAS.292...27H/abstract + * @param T_cgs temperature in K + * @param onthespot use on the spot approximation? + * @return alphalist coefficients of recomination + * @return betalist coefficients of collisional ionization + */ +INLINE static void rt_compute_alphabeta_cgs(double T_cgs, int onthespot, + double alphalist[rt_species_count], + double betalist[rt_species_count]) { + + if (T_cgs == 0.0) error("Temperature is absolute zero."); + const double lambdaT = 315614.0 / T_cgs; + + /* Hydrogen coefficient */ + + /* Computes the case A recombination coefficient for HII (Hui and Gnedin 1997) + */ + double alphaAHII_cgs = 1.269e-13 * pow(lambdaT, 1.503) * + pow(1.0 + pow(lambdaT / 0.522, 0.470), -1.923); + + /* Computes the case B recombination coefficient for HII (Hui and Gnedin 1997) + */ + double alphaBHII_cgs = 2.753e-14 * pow(lambdaT, 1.5) * + pow(1.0 + pow(lambdaT / 2.740, 0.407), -2.242); + + /* Computes the collisional ionization rate for HI (Theuns et al. 1998) */ + double betaHI_cgs = 1.17e-10 * sqrt(T_cgs) * exp(-157809.1 / T_cgs) / + (1.0 + sqrt(T_cgs / 1e5)); + + /* Helium coefficient */ + + const double lambdaTI = 2.0 * 285335.0 / T_cgs; + + const double lambdaTII = 2.0 * 631515.0 / T_cgs; + + /* Computes the case A recombination coefficient for HeII (Hui and Gnedin + * 1997) */ + double alphaAHeII_cgs = 3.0e-14 * pow(lambdaTI, 0.654); + + /* Computes the case B recombination coefficient for HeII (Hui and Gnedin + * 1997) */ + double alphaBHeII_cgs = 1.26e-14 * pow(lambdaTI, 0.750); + + /* Computes the Dielectronic recombination coefficient for HeII (Aldrovandi + * and Pequignot 1973) */ + double alphaDiHeII_cgs = 1.9e-3 * pow(T_cgs, -1.5) * exp(-4.7e5 / T_cgs) * + (1.0 + 0.3 * exp(-9.4e4 / T_cgs)); + + /* Computes the case A recombination coefficient for HeIII (Hui and Gnedin + * 1997) */ + double alphaAHeIII_cgs = 2.538e-13 * pow(lambdaTII, 1.503) * + pow(1.0 + pow(lambdaTII / 0.522, 0.470), -1.923); + + /* Computes the case B recombination coefficient for HeIII (Hui and Gnedin + * 1997) */ + double alphaBHeIII_cgs = 5.506e-14 * pow(lambdaTII, 1.5) * + pow(1.0 + pow(lambdaTII / 2.740, 0.407), -2.242); + + /* Computes the collisional ionization rate for HeI (Theuns et al. 1998) */ + double betaHeI_cgs = 4.76e-11 * sqrt(T_cgs) * exp(-285335.4 / T_cgs) / + (1.0 + sqrt(T_cgs / 1e5)); + + /* Computes the collisional ionization rate for HeII (Theuns et al. 1998) */ + double betaHeII_cgs = 1.14e-11 * sqrt(T_cgs) * exp(-631515.0 / T_cgs) / + (1.0 + sqrt(T_cgs / 1e5)); + + betalist[rt_sp_elec] = 0.0; + betalist[rt_sp_HI] = betaHI_cgs; + betalist[rt_sp_HII] = 0.0; + betalist[rt_sp_HeI] = betaHeI_cgs; + betalist[rt_sp_HeII] = betaHeII_cgs; + betalist[rt_sp_HeIII] = 0.0; + alphalist[rt_sp_elec] = 0.0; + alphalist[rt_sp_HI] = 0.0; + alphalist[rt_sp_HeI] = 0.0; + if (onthespot == 1) { + alphalist[rt_sp_HII] = alphaBHII_cgs; + alphalist[rt_sp_HeII] = alphaBHeII_cgs + alphaDiHeII_cgs; + alphalist[rt_sp_HeIII] = alphaBHeIII_cgs; + } else { + alphalist[rt_sp_HII] = alphaAHII_cgs; + alphalist[rt_sp_HeII] = alphaAHeII_cgs + alphaDiHeII_cgs; + alphalist[rt_sp_HeIII] = alphaAHeIII_cgs; + } +} + +/**************************************/ +/* COOLING COEFFICIENTS */ +/**************************************/ + +/** + * @brief Computes the cooling coefficient (Hui and Gnedin 1997) + * https://ui.adsabs.harvard.edu/abs/1997MNRAS.292...27H/abstract + * @param T_cgs temperature in K + * @param onthespot use on the spot approximation? + * @return Gammalist cooling coefficients of recomination and collisional + * ionization (recombination positive) + */ +INLINE static void rt_compute_cooling_gamma_cgs( + const double T_cgs, const int onthespot, + double Gammalist[rt_species_count]) { + + if (T_cgs == 0.0) error("Temperature is absolute zero."); + const double log_T_cgs = log10(T_cgs); + const double lambdaT = 315614.0 / T_cgs; + + /* Hydrogen coefficient */ + /* Computes the collisional ionization cooling rate (Theuns+98) */ + double Gamma_colion_HI_cgs = 2.54e-21 * pow(T_cgs, 0.5) * + exp(-157809.1 / T_cgs) / + (1.0 + pow(T_cgs / 1.0e5, 0.5)); + + /* Computes the collisional excitation cooling rate (Theuns+98) */ + double Gamma_line_HI_cgs = + 7.5e-19 * exp(-118348.0 / T_cgs) / (1.0 + pow(T_cgs / 1.0e5, 0.5)); + + /* Computes the case A? recombination cooling rate (Hui & Gnedin 1997) */ + double Gamma_recomA_HII_cgs = 1.778e-29 * T_cgs * pow(lambdaT, 1.965) * + pow(1.0 + pow(lambdaT / 0.541, 0.502), -2.697); + + /* Computes the case B? recombination cooling rate (Hui & Gnedin 1997) */ + double Gamma_recomB_HII_cgs = 3.435e-30 * T_cgs * pow(lambdaT, 1.970) * + pow(1.0 + pow(lambdaT / 2.250, 0.376), -3.720); + + /* Computes the Bremsstrahlung cooling rate (Theuns+98) */ + double Gamma_ff_HII_cgs = + 1.42e-27 * pow(T_cgs, 0.5) * + (1.1 + 0.34 * exp(-pow(5.5 - log_T_cgs, 2.0) / 3.0)); + + /* Helium coefficient */ + + const double lambdaTI = 2.0 * 285335.0 / T_cgs; + + const double lambdaTII = 2.0 * 631515.0 / T_cgs; + + /* Computes the collisional ionization cooling rate for HeI (Theuns+98) */ + double Gamma_colion_HeI_cgs = 1.88e-21 * sqrt(T_cgs) * + exp(-285335.4 / T_cgs) / + (1.0 + pow(T_cgs / 1.0e5, 0.5)); + + /* Computes the collisional ionization cooling rate for HeI (Theuns+98) */ + double Gamma_colion_HeII_cgs = 9.90e-22 * sqrt(T_cgs) * + exp(-631515.0 / T_cgs) / + (1.0 + pow(T_cgs / 1.0e5, 0.5)); + + /* Computes the collisional excitation cooling rate (Theuns+98) */ + double Gamma_line_HeII_cgs = 5.54e-17 * pow(T_cgs, -0.397) * + exp(-473638.0 / T_cgs) / + (1.0 + pow(T_cgs / 1.0e5, 0.5)); + + /* Computes the case A? recombination cooling rate (Hui & Gnedin 1997) */ + double Gamma_recomA_HeII_cgs = + 1.38e-16 * T_cgs * 3.0e-14 * pow(lambdaTI, 0.654); + + /* Computes the case B? recombination cooling rate (Hui & Gnedin 1997) */ + double Gamma_recomB_HeII_cgs = + 1.38e-16 * T_cgs * 1.26e-14 * pow(lambdaTI, 0.750); + + /* Computes the dielectric recombination cooling rate (Hui & Gnedin 1997) */ + double Gamma_recomDi_HeII_cgs = 1.24e-13 * pow(T_cgs, -1.5) * + exp(-4.7e5 / T_cgs) * + (1.0 + 0.3 * exp(-9.4e4 / T_cgs)); + ; + + /* Computes the case A? recombination cooling rate (Hui & Gnedin 1997) */ + double Gamma_recomA_HeIII_cgs = + 1.4224e-28 * T_cgs * pow(lambdaTII, 1.965) * + pow(1.0 + pow(lambdaTII / 0.541, 0.502), -2.697); + + /* Computes the case B? recombination cooling rate (Hui & Gnedin 1997) */ + double Gamma_recomB_HeIII_cgs = + 2.748e-29 * T_cgs * pow(lambdaTII, 1.970) * + pow(1.0 + pow(lambdaTII / 2.250, 0.376), -3.720); + + /* Computes the Bremsstrahlung cooling rate (Theuns+98) */ + double Gamma_ff_HeII_cgs = + 1.42e-27 * pow(T_cgs, 0.5) * + (1.1 + 0.34 * exp(-pow(5.5 - log_T_cgs, 2.0) / 3.0)); + + /* Computes the Bremsstrahlung cooling rate (Theuns+98) */ + double Gamma_ff_HeIII_cgs = + 5.68e-27 * pow(T_cgs, 0.5) * + (1.1 + 0.34 * exp(-pow(5.5 - log_T_cgs, 2.0) / 3.0)); + + Gammalist[rt_sp_elec] = 0.0; + Gammalist[rt_sp_HI] = Gamma_colion_HI_cgs + Gamma_line_HI_cgs; + Gammalist[rt_sp_HII] = Gamma_ff_HII_cgs; + Gammalist[rt_sp_HeI] = Gamma_colion_HeI_cgs; + Gammalist[rt_sp_HeII] = + Gamma_colion_HeII_cgs + Gamma_line_HeII_cgs + Gamma_ff_HeII_cgs; + Gammalist[rt_sp_HeIII] = Gamma_ff_HeIII_cgs; + if (onthespot == 1) { + Gammalist[rt_sp_HII] += Gamma_recomB_HII_cgs; + Gammalist[rt_sp_HeII] += Gamma_recomB_HeII_cgs + Gamma_recomDi_HeII_cgs; + Gammalist[rt_sp_HeIII] += Gamma_recomB_HeIII_cgs; + } else { + Gammalist[rt_sp_HII] += Gamma_recomA_HII_cgs; + Gammalist[rt_sp_HeII] += Gamma_recomA_HeII_cgs + Gamma_recomDi_HeII_cgs; + Gammalist[rt_sp_HeIII] += Gamma_recomA_HeIII_cgs; + } +} + +/**************************************/ +/* PHOTO-IONIZATION COEFFICIENTS */ +/**************************************/ + +/** + * @brief Output the photo-ionization coefficient: assume BB1e5 and Verner+1996 + * cross-section Note!!!: numbers of frequency bins: has to be three from + * HI-HeI, HeI-HeII, HeII-infty + * @return sigmalist photo-ionization cross section in cm^2 + * @return epsilonlist averaged thermal energy per ionization in erg + */ +INLINE static void rt_compute_photoionization_rate_cgs( + double sigmalist[3][3], double epsilonlist[3][3]) { + /* the first index denotes frequency bins; the second index denotes species */ + /* HI: index 0 */ + sigmalist[0][0] = 2.99e-18; + sigmalist[1][0] = 5.66e-19; + sigmalist[2][0] = 7.84e-20; + epsilonlist[0][0] = 6.17e-12; + epsilonlist[1][0] = 2.81e-11; + epsilonlist[2][0] = 7.77e-11; + /* HeI: index 1 */ + sigmalist[0][1] = 0.0; + sigmalist[1][1] = 4.46e-18; + sigmalist[2][1] = 1.19e-18; + epsilonlist[0][1] = 0.0; + epsilonlist[1][1] = 1.25e-11; + epsilonlist[2][1] = 6.11e-11; + + /* HeII: index 2 */ + sigmalist[0][2] = 0.0; + sigmalist[1][2] = 0.0; + sigmalist[2][2] = 1.05e-18; + epsilonlist[0][2] = 0.0; + epsilonlist[1][2] = 0.0; + epsilonlist[2][2] = 1.27e-11; +} + +/** + * @brief Computes the chemistry and cooling coefficient + * @param T_cgs temperature in K + * @param onthespot use on the spot approximation? + * @param alphalist combined coefficients of recomination and collisional + * ionization + * @param betalist coefficients of collisional ionization + * @param Gammalist cooling coefficients of recomination and collisional + * ionization + * @param sigmalist photo-ionization cross section in cm^2 + * @param epsilonlist averaged thermal energy per ionization in erg + * @param aindex use to translate index to species + */ +INLINE static void rt_compute_rate_coefficients( + const double T_cgs, const int onthespot, double alphalist[rt_species_count], + double betalist[rt_species_count], double Gammalist[rt_species_count], + double sigmalist[3][3], double epsilonlist[3][3]) { + rt_compute_alphabeta_cgs(T_cgs, onthespot, alphalist, betalist); + rt_compute_cooling_gamma_cgs(T_cgs, onthespot, Gammalist); + rt_compute_photoionization_rate_cgs(sigmalist, epsilonlist); +} + +/** + * @brief function used to calculate chemistry changes. + * Table indices and offsets for redshift, hydrogen number density and + * helium fraction are passed it so as to compute them only once per particle. + * + * @param n_H_cgs Hydrogen number density in CGS units. + * @param cred_cgs (reduced) speed of light in cm/s + * @param abundances species abundance in n_i/nH. + * @param ngamma_cgs photon density in cm^-3 + * @param alphalist combined coefficients of recomination and collisional + * ionization + * @param betalist coefficients of collisional ionization + * @param sigmalist photo-ionization cross section in cm^2 + * @param aindex use to translate index to species + * + * @return chemistry_rates The chemistry rate (d n_i / d t in cgs) + */ +INLINE static void rt_compute_chemistry_rate( + const double n_H_cgs, const double cred_cgs, + const double abundances[rt_species_count], const double ngamma_cgs[3], + const double alphalist[rt_species_count], + const double betalist[rt_species_count], double sigmalist[3][3], + const int aindex[3], double chemistry_rates[rt_species_count]) { + + for (int spec = 0; spec < rt_species_count; spec++) { + chemistry_rates[spec] = 0.0; + } + + for (int i = 0; i < 3; i++) { + /* photo-ionization */ + /* HI */ + chemistry_rates[aindex[0]] += -sigmalist[i][0] * cred_cgs * ngamma_cgs[i] * + abundances[aindex[0]] * n_H_cgs; + /* HeI */ + chemistry_rates[aindex[1]] += -sigmalist[i][1] * cred_cgs * ngamma_cgs[i] * + abundances[aindex[1]] * n_H_cgs; + /* HeII */ + chemistry_rates[aindex[2]] += -sigmalist[i][2] * cred_cgs * ngamma_cgs[i] * + abundances[aindex[2]] * n_H_cgs; + /* addition from photo-ionization of a lower level species, i.e. from HeI to + * HeII */ + chemistry_rates[aindex[2]] += sigmalist[i][1] * cred_cgs * ngamma_cgs[i] * + abundances[aindex[1]] * n_H_cgs; + } + + /* collisional ionization */ + chemistry_rates[rt_sp_HI] += -betalist[rt_sp_HI] * abundances[rt_sp_elec] * + abundances[rt_sp_HI] * n_H_cgs * n_H_cgs; + chemistry_rates[rt_sp_HeI] += -betalist[rt_sp_HeI] * abundances[rt_sp_elec] * + abundances[rt_sp_HeI] * n_H_cgs * n_H_cgs; + chemistry_rates[rt_sp_HeII] += -betalist[rt_sp_HeII] * + abundances[rt_sp_elec] * + abundances[rt_sp_HeII] * n_H_cgs * n_H_cgs; + + /* collisional ionization from HeI -> HeII */ + chemistry_rates[rt_sp_HeII] += betalist[rt_sp_HeI] * abundances[rt_sp_elec] * + abundances[rt_sp_HeI] * n_H_cgs * n_H_cgs; + + /* recombination */ + chemistry_rates[rt_sp_HI] += alphalist[rt_sp_HII] * abundances[rt_sp_elec] * + abundances[rt_sp_HII] * n_H_cgs * n_H_cgs; + chemistry_rates[rt_sp_HeI] += alphalist[rt_sp_HeII] * abundances[rt_sp_elec] * + abundances[rt_sp_HeII] * n_H_cgs * n_H_cgs; + chemistry_rates[rt_sp_HeII] += alphalist[rt_sp_HeIII] * + abundances[rt_sp_elec] * + abundances[rt_sp_HeIII] * n_H_cgs * n_H_cgs; + + /* recombination from HeII to HeI */ + chemistry_rates[rt_sp_HeII] += -alphalist[rt_sp_HeII] * + abundances[rt_sp_elec] * + abundances[rt_sp_HeII] * n_H_cgs * n_H_cgs; +} + +/** + * @brief function used to calculate radiation absorption rate. + * Table indices and offsets for redshift, hydrogen number density and + * helium fraction are passed it so as to compute them only once per particle. + * + * @param n_H_cgs Hydrogen number density in CGS units. + * @param cred_cgs (reduced) speed of light in cm/s in cm/s + * @param abundances species abundance in n_i/nH. + * @param ngamma_cgs photon density in cm^-3 + * @param sigmalist photo-ionization cross section in cm^2 + * @param aindex use to translate index to species + * + * @return absorption_rate The radiation absorption rate (d n_gamma / d t in + * cgs) excluded diffuse emission + */ +INLINE static void rt_compute_radiation_rate( + const double n_H_cgs, const double cred_cgs, + const double abundances[rt_species_count], const double ngamma_cgs[3], + double sigmalist[3][3], const int aindex[3], double absorption_rate[3]) { + + absorption_rate[0] = 0.0; + absorption_rate[1] = 0.0; + absorption_rate[2] = 0.0; + + for (int i = 0; i < 3; i++) { + absorption_rate[0] += sigmalist[0][i] * cred_cgs * ngamma_cgs[0] * + abundances[aindex[i]] * n_H_cgs; + absorption_rate[1] += sigmalist[1][i] * cred_cgs * ngamma_cgs[1] * + abundances[aindex[i]] * n_H_cgs; + absorption_rate[2] += sigmalist[2][i] * cred_cgs * ngamma_cgs[2] * + abundances[aindex[i]] * n_H_cgs; + } +} + +/** + * @brief function used to calculate cooling rate. + * + * @param n_H_cgs Hydrogen number density in CGS units. + * @param cred_cgs (reduced) speed of light in cm/s + * @param abundances species abundance in n_i/nH. + * @param ngamma_cgs photon density in cm^-3 + * @param Gammalist cooling coefficient + * @param sigmalist photo-ionization cross section in cm^2 + * @param epsilonlist averaged thermal energy per ionization in erg + * @param aindex use to translate index to species + + * + * @return The net cooling rate of gas (d energy density / d t in cgs) + */ +INLINE static double rt_compute_cooling_rate( + const double n_H_cgs, const double cred_cgs, + const double abundances[rt_species_count], const double ngamma_cgs[3], + const double Gammalist[rt_species_count], double sigmalist[3][3], + double epsilonlist[3][3], int aindex[3]) { + /* The cooling rate of gas */ + double cooling_rate_cgs = 0.0; + + for (int spec = 0; spec < rt_species_count; spec++) { + cooling_rate_cgs += + Gammalist[spec] * abundances[rt_sp_elec] * abundances[spec]; + } + + cooling_rate_cgs *= n_H_cgs * n_H_cgs; + + /* The photo-heating rate */ + double photoheating_rate_cgs = 0.0; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + photoheating_rate_cgs += epsilonlist[i][j] * sigmalist[i][j] * cred_cgs * + ngamma_cgs[i] * abundances[aindex[j]] * n_H_cgs; + } + } + + double Lambda_net_cgs = photoheating_rate_cgs - cooling_rate_cgs; + + return Lambda_net_cgs; +} + +/** + * @brief function used to enforce constraint equation. + * If any of these + * constraints are not met to within 1 per cent, the + * abundances of all species involved in that constraint + * are re-scaled accordingly. This routine also enforces + * that all abundances are non-negative. + * @param abundances species abundance in n_i/nH. + * @param metal_mass_fraction metal mass + * + * @return finish_abundances species abundance in n_i/nH. + */ +INLINE static void rt_enforce_constraint_equations( + const double abundances[rt_species_count], + const double metal_mass_fraction[rt_chemistry_element_count], + double finish_abundances[rt_species_count]) { + + double metal_atomic_mass[rt_chemistry_element_count]; /* in unit of hydrogen + mass */ + + metal_atomic_mass[rt_chemistry_element_H] = 1.0; + + metal_atomic_mass[rt_chemistry_element_He] = 4.0; + + /* Initialization */ + for (int spec = 0; spec < rt_species_count; spec++) { + finish_abundances[spec] = fmax(abundances[spec], 0.0); + } + + /* enforce hydrogen species constraint */ + finish_abundances[rt_sp_HI] = fmax(finish_abundances[rt_sp_HI], 0.0); + finish_abundances[rt_sp_HI] = fmin(finish_abundances[rt_sp_HI], 1.0); + finish_abundances[rt_sp_HII] = 1.0 - finish_abundances[rt_sp_HI]; + + /* enforce helium species constraint */ + if (metal_mass_fraction[rt_chemistry_element_He] == 0.0) { + finish_abundances[rt_sp_HeI] = 0.0; + finish_abundances[rt_sp_HeII] = 0.0; + finish_abundances[rt_sp_HeIII] = 0.0; + } else { + double aHe = metal_mass_fraction[rt_chemistry_element_He] / + metal_mass_fraction[rt_chemistry_element_H] * + metal_atomic_mass[rt_chemistry_element_H] / + metal_atomic_mass[rt_chemistry_element_He]; + + finish_abundances[rt_sp_HeI] = fmax(finish_abundances[rt_sp_HeI], 0.0); + finish_abundances[rt_sp_HeII] = fmax(finish_abundances[rt_sp_HeII], 0.0); + finish_abundances[rt_sp_HeIII] = + fmax(aHe - finish_abundances[rt_sp_HeI] - finish_abundances[rt_sp_HeII], + 0.0); + double sumHe = finish_abundances[rt_sp_HeI] + + finish_abundances[rt_sp_HeII] + + finish_abundances[rt_sp_HeIII]; + if (sumHe > 1.01 * aHe) { + finish_abundances[rt_sp_HeI] *= aHe / sumHe; + finish_abundances[rt_sp_HeII] *= aHe / sumHe; + finish_abundances[rt_sp_HeIII] *= aHe / sumHe; + } + } + + /* enforce electron constraint */ + finish_abundances[rt_sp_elec] = finish_abundances[rt_sp_HII] + + finish_abundances[rt_sp_HeII] + + 2.0 * finish_abundances[rt_sp_HeIII]; +} + +/** + * @brief function to calculate ionization, heating, and cooling explicitly. + * + * @param abundances species abundance in n_i/nH. + * @param metal_mass_fraction metal mass + * @param rho_cgs gas density + * @param u_cgs gas internal energy per mass + * @param u_min_cgs minimum (floor) gas internal energy per mass + * @param dt_cgs this timestep + * @param n_H_cgs Hydrogen number density in CGS units. + * @param cred_cgs (reduced) speed of light in cm/s + * @param ngamma_cgs photon density in cm^-3 + * @param onthespot use on the spot approximation? + * @param alphalist combined coefficients of recomination and collisional + * ionization + * @param betalist coefficients of collisional ionization + * @param Gammalist cooling coefficients of recomination and collisional + * ionization + * @param sigmalist photo-ionization cross section in cm^2 + * @param epsilonlist averaged thermal energy per ionization in erg + * @param aindex use to translate index (photon-group) to species + * @return u_new_cgs new internal energy per mass + * @return new_abundances new species abundances + * @return new_ngamma_cgs new photon density in cm^-3 + * @return max_relative_change maximum relative change in all variables + */ +INLINE static void rt_compute_explicit_thermochemistry_solution( + const double n_H_cgs, const double cred_cgs, const double dt_cgs, + const double rho_cgs, const double u_cgs, const double u_min_cgs, + const double abundances[rt_species_count], const double ngamma_cgs[3], + const double alphalist[rt_species_count], + const double betalist[rt_species_count], + const double Gammalist[rt_species_count], double sigmalist[3][3], + double epsilonlist[3][3], int aindex[3], double *u_new_cgs, + double new_abundances[rt_species_count], double new_ngamma_cgs[3], + double *max_relative_change) { + + double absorption_rate[3], chemistry_rates[rt_species_count]; + + if (dt_cgs == 0.0) error("dt_cgs==%e", dt_cgs); + if (n_H_cgs == 0.0) error("n_H_cgs==%e", n_H_cgs); + + rt_compute_radiation_rate(n_H_cgs, cred_cgs, abundances, ngamma_cgs, + sigmalist, aindex, absorption_rate); + + rt_compute_chemistry_rate(n_H_cgs, cred_cgs, abundances, ngamma_cgs, + alphalist, betalist, sigmalist, aindex, + chemistry_rates); + + double Lambda_net_cgs; + Lambda_net_cgs = + rt_compute_cooling_rate(n_H_cgs, cred_cgs, abundances, ngamma_cgs, + Gammalist, sigmalist, epsilonlist, aindex); + + /* record for maximum relative change */ + double max_relative_change_value = 0.0; + double relative_change = 0.0; + double abundances_inv; + + for (int spec = 0; spec < rt_species_count; spec++) { + new_abundances[spec] = + fmax(abundances[spec] + chemistry_rates[spec] / n_H_cgs * dt_cgs, 0.0); + if (new_abundances[spec] > 1e-15) { + if (abundances[spec] > 1e-15) { + abundances_inv = 1.0 / abundances[spec]; + relative_change = + fabs(new_abundances[spec] - abundances[spec]) * abundances_inv; + max_relative_change_value = + fmax(max_relative_change_value, relative_change); + } + } + } + + double u_new_cgs_value; + u_new_cgs_value = fmax(u_cgs + Lambda_net_cgs * dt_cgs / rho_cgs, u_min_cgs); + relative_change = fabs(u_new_cgs_value - u_cgs) / u_cgs; + max_relative_change_value = fmax(max_relative_change_value, relative_change); + + for (int i = 0; i < 3; i++) { + new_ngamma_cgs[i] = fmax(ngamma_cgs[i] - absorption_rate[i] * dt_cgs, 0.0); + if ((new_ngamma_cgs[i] > 1e-8 * n_H_cgs) && + (ngamma_cgs[i] > 1e-8 * n_H_cgs)) { + relative_change = fabs(new_ngamma_cgs[i] - ngamma_cgs[i]) / ngamma_cgs[i]; + max_relative_change_value = + fmax(max_relative_change_value, relative_change); + } + } + + *u_new_cgs = u_new_cgs_value; + *max_relative_change = max_relative_change_value; +} + +/** + * @brief function used to initialize species abundance in n_i/nH, assuming + * collisional ionization equilibrium. + * + * @param alphalist combined coefficients of recomination and collisional + * ionization + * @param betalist coefficients of collisional ionization + * @param metal_mass_fraction metal mass fraction + * @param init_abundances species abundances for initial conditions + * + * @return The net cooling rate of gas (d energy density / d t in cgs) + */ +INLINE static void rt_initialize_abundances( + const double alphalist[rt_species_count], + const double betalist[rt_species_count], + const double metal_mass_fraction[rt_chemistry_element_count], + double init_abundances[rt_species_count]) { + + double metal_atomic_mass[rt_chemistry_element_count]; /* in unit of hydrogen + mass */ + + metal_atomic_mass[rt_chemistry_element_H] = 1.0; + + metal_atomic_mass[rt_chemistry_element_He] = 4.0; + + init_abundances[rt_sp_HI] = + alphalist[rt_sp_HII] / (betalist[rt_sp_HI] + alphalist[rt_sp_HII]); + init_abundances[rt_sp_HII] = 1.0 - init_abundances[rt_sp_HI]; + + double nHe_nH = metal_mass_fraction[rt_chemistry_element_He] / + metal_mass_fraction[rt_chemistry_element_H] * + metal_atomic_mass[rt_chemistry_element_H] / + metal_atomic_mass[rt_chemistry_element_He]; + double denoHe = (alphalist[rt_sp_HeIII] * betalist[rt_sp_HeI] + + betalist[rt_sp_HeII] * betalist[rt_sp_HeI] + + alphalist[rt_sp_HeII] * alphalist[rt_sp_HeIII]); + init_abundances[rt_sp_HeI] = + alphalist[rt_sp_HeII] * alphalist[rt_sp_HeIII] * nHe_nH / denoHe; + init_abundances[rt_sp_HeII] = + alphalist[rt_sp_HeIII] * betalist[rt_sp_HeI] * nHe_nH / denoHe; + init_abundances[rt_sp_HeIII] = + betalist[rt_sp_HeI] * betalist[rt_sp_HeII] * nHe_nH / denoHe; + init_abundances[rt_sp_elec] = init_abundances[rt_sp_HII] + + init_abundances[rt_sp_HeII] + + 2.0 * init_abundances[rt_sp_HeIII]; +} + +/** + * @brief Defines the right-hand side of the system of differential equations + * (dy/dt = ydot). + * + * Defines the system of differential equations that make + * up the right-hand side function, which will be integrated + * by CVode. + * + * @param t Current time. + * @param y Vector containing the variables to be integrated. + * @param ydot Vector containing the time derivatives of the variables. + * @param user_data The #RTUserData struct containing the input data. + */ +int rt_frateeq(realtype t, N_Vector y, N_Vector ydot, void *user_data); + +#endif /* SWIFT_RT_SPHM1RT_COOLING_RATES_H */ diff --git a/src/rt/SPHM1RT/rt_getters.h b/src/rt/SPHM1RT/rt_getters.h new file mode 100644 index 0000000000000000000000000000000000000000..2b2513c3affaa0061047c80400d65342d71e7816 --- /dev/null +++ b/src/rt/SPHM1RT/rt_getters.h @@ -0,0 +1,127 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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_SPHM1RT_RT_GETTERS_H +#define SWIFT_SPHM1RT_RT_GETTERS_H + +/** + * @file src/rt/SPHM1RT/rt_getters.h + * @brief Independent getter functions for the SPHM1RT scheme + */ + +/** + * @brief Returns the comoving radiation speed of a particle + * @param p Pointer to the particle data. + * @param a Current scale factor. + * @return comoving reduced speed of light + */ +__attribute__((always_inline)) INLINE static float rt_get_comoving_cred( + const struct part* restrict p, float a) { + return p->rt_data.params.cred_phys / a; +} + +/** + * @brief Returns the physical radiation speed of a particle + * + * @param p Pointer to the particle data. + * @param a Current scale factor. + * @return physical reduced speed of light + * + */ +__attribute__((always_inline)) INLINE static float rt_get_physical_cred( + const struct part* restrict p, float a) { + return p->rt_data.params.cred_phys; +} + +/** + * @brief Returns the comoving radiation energy per mass of a particle + * (note that the comoving and physical energy per mass are the same in our + * convention) + * + * @param p Pointer to the particle data. + * @param urad The comoving radiation energy per mass. + * + */ +__attribute__((always_inline)) INLINE static void +rt_get_comoving_urad_multifrequency(const struct part* restrict p, + float urad[RT_NGROUPS]) { + for (int g = 0; g < RT_NGROUPS; g++) { + urad[g] = p->rt_data.conserved[g].urad; + } +} + +/** + * @brief Returns the physical radiation energy per mass of a particle + * (note that the comoving and physical energy per mass are the same in our + * convention) + * + * @param p Pointer to the particle data. + * @param cosmo Cosmology data structure. + * @param urad The physical radiation energy. + * + */ +__attribute__((always_inline)) INLINE static void +rt_get_physical_urad_multifrequency(const struct part* restrict p, + const struct cosmology* cosmo, + float urad[RT_NGROUPS]) { + for (int g = 0; g < RT_NGROUPS; g++) { + urad[g] = p->rt_data.conserved[g].urad; + } +} + +/** + * @brief Returns the comoving radiation flux per gas density of a particle + * (note that the comoving and physical flux per gas density are the same in our + * convention) + * + * @param p Pointer to the particle data. + * @param fradtemp The comoving radiation flux per gas density + */ +__attribute__((always_inline)) INLINE static void +rt_get_comoving_frad_multifrequency(const struct part* restrict p, + float fradtemp[RT_NGROUPS][3]) { + + for (int g = 0; g < RT_NGROUPS; g++) { + fradtemp[g][0] = p->rt_data.conserved[g].frad[0]; + fradtemp[g][1] = p->rt_data.conserved[g].frad[1]; + fradtemp[g][2] = p->rt_data.conserved[g].frad[2]; + } +} + +/** + * @brief Returns the physical radiation flux per gas density of a particle + * (note that the comoving and physical flux per gas density are the same in our + * convention) + * + * @param p Pointer to the particle data. + * @param cosmo Cosmology data structure + * @param fradtemp The comoving radiation flux per gas density + */ +__attribute__((always_inline)) INLINE static void +rt_get_physical_frad_multifrequency(const struct part* restrict p, + const struct cosmology* cosmo, + float fradtemp[RT_NGROUPS][3]) { + + for (int g = 0; g < RT_NGROUPS; g++) { + fradtemp[g][0] = p->rt_data.conserved[g].frad[0] * cosmo->a; + fradtemp[g][1] = p->rt_data.conserved[g].frad[1] * cosmo->a; + fradtemp[g][2] = p->rt_data.conserved[g].frad[2] * cosmo->a; + } +} + +#endif /* SWIFT_SPHM1RT_RT_GETTERS_H */ diff --git a/src/rt/SPHM1RT/rt_gradients.h b/src/rt/SPHM1RT/rt_gradients.h new file mode 100644 index 0000000000000000000000000000000000000000..e23b8fba6a794396845efd772270125665de1ad3 --- /dev/null +++ b/src/rt/SPHM1RT/rt_gradients.h @@ -0,0 +1,476 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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_RT_GRAD_SPHM1RT_H +#define SWIFT_RT_GRAD_SPHM1RT_H + +/** + * @file SPHM1RT/rt_gradients.h + * @brief Compute the gradient or divergence according to SPH + */ + +/** + * @brief Compute the gradient according to SPH + * Note that the differential is: 1/rho * grad(rho * uin) + * @param uini quantity for particle i to be differentiated + * @param uinj quantity for particle j to be differentiated + * @param mi mass of particle i + * @param mj mass of particle j + * @param forcefi a correction factor for spatial variations of hi + * @param forcefj a correction factor for spatial variations of hj + * @param rhoi density of particle i + * @param rhoj density of particle j + * @param wi_dr derivative of kernel i + * @param wj_dr derivative of kernel j + * @param dx Comoving vector separating both particles (pi - pj). + * @param r Comoving distance between the two particles. + * @param diffmode mode=0 for ``difference''; mode=1 for ``symmetric''; mode=2 + * for ``difference'' but ignore h correction + * @param gradi gradient of uin for particle i + * @param gradj gradient of uin for particle j + */ +__attribute__((always_inline)) INLINE static void radiation_gradient_SPH( + const float uini, const float uinj, const float mi, const float mj, + const float forcefi, const float forcefj, const float rhoi, + const float rhoj, const float wi_dr, const float wj_dr, const float dx[3], + const float r, int diffmode, float gradi[3], float gradj[3]) { + const float rho_i_inv = 1.0f / rhoi; + const float rho_j_inv = 1.0f / rhoj; + const float r_inv = 1.0f / r; + if (diffmode == 0) { + const float fradpre_common = (rhoi * uini - rhoj * uinj); + const float fradprei = + mj * forcefi * rho_i_inv * rho_i_inv * fradpre_common * wi_dr; + const float fradprej = + mi * forcefj * rho_j_inv * rho_j_inv * fradpre_common * wj_dr; + gradi[0] = -fradprei * dx[0] * r_inv; + gradi[1] = -fradprei * dx[1] * r_inv; + gradi[2] = -fradprei * dx[2] * r_inv; + gradj[0] = -fradprej * dx[0] * r_inv; + gradj[1] = -fradprej * dx[1] * r_inv; + gradj[2] = -fradprej * dx[2] * r_inv; + } else if (diffmode == 1) { + const float fradpre_common = + uinj * rho_j_inv * wj_dr * forcefj + uini * rho_i_inv * wi_dr * forcefi; + const float fradprei = mj * fradpre_common; + const float fradprej = mi * fradpre_common; + gradi[0] = fradprei * dx[0] * r_inv; + gradi[1] = fradprei * dx[1] * r_inv; + gradi[2] = fradprei * dx[2] * r_inv; + gradj[0] = -fradprej * dx[0] * r_inv; + gradj[1] = -fradprej * dx[1] * r_inv; + gradj[2] = -fradprej * dx[2] * r_inv; + } else if (diffmode == 2) { + const float fradpre_common = (rhoi * uini - rhoj * uinj); + const float fradprei = + mj * rho_i_inv * rho_i_inv * fradpre_common * (wi_dr + wj_dr) * 0.5f; + const float fradprej = + mi * rho_j_inv * rho_j_inv * fradpre_common * (wi_dr + wj_dr) * 0.5f; + gradi[0] = -fradprei * dx[0] * r_inv; + gradi[1] = -fradprei * dx[1] * r_inv; + gradi[2] = -fradprei * dx[2] * r_inv; + gradj[0] = -fradprej * dx[0] * r_inv; + gradj[1] = -fradprej * dx[1] * r_inv; + gradj[2] = -fradprej * dx[2] * r_inv; + } else { + error("diffmode should be 0 or 1 or 2"); + } +} + +/** + * @brief Compute the anistropic gradient according to SPH + * Note that the differential is: 1/rho * grad(Ftensor * rho * uin) + * @param uini quantity for particle i to be differentiated + * @param uinj quantity for particle j to be differentiated + * @param mi mass of particle i + * @param mj mass of particle j + * @param forcefi a correction factor for spatial variations of hi + * @param forcefj a correction factor for spatial variations of hj + * @param rhoi density of particle i + * @param rhoj density of particle j + * @param wi_dr derivative of kernel i + * @param wj_dr derivative of kernel j + * @param Fanisoi Ftensor i + * @param Fanisoj Ftensor j + * @param dx Comoving vector separating both particles (pi - pj). + * @param r Comoving distance between the two particles. + * @param diffmode mode=0 for ``difference''; mode=1 for ``symmetric''; mode=2 + * for ``difference'' but ignore h correction; mode=3 + * for ``symmetric'' but ignore h correction; + * @param gradi gradient uin for particle i + * @param gradj gradient uin for particle j + */ +__attribute__((always_inline)) INLINE static void radiation_gradient_aniso_SPH( + const float uini, const float uinj, const float mi, const float mj, + const float forcefi, const float forcefj, const float rhoi, + const float rhoj, const float wi_dr, const float wj_dr, float Fanisoi[3][3], + float Fanisoj[3][3], const float dx[3], const float r, int diffmode, + float gradi[3], float gradj[3]) { + const float rho_i_inv = 1.0f / rhoi; + const float rho_j_inv = 1.0f / rhoj; + const float r_inv = 1.0f / r; + if (diffmode == 0) { + float tempi[3], tempj[3]; + tempi[0] = + Fanisoi[0][0] * dx[0] + Fanisoi[0][1] * dx[1] + Fanisoi[0][2] * dx[2]; + tempi[0] *= rhoi * uini * r_inv; + tempi[1] = + Fanisoi[1][0] * dx[0] + Fanisoi[1][1] * dx[1] + Fanisoi[1][2] * dx[2]; + tempi[1] *= rhoi * uini * r_inv; + tempi[2] = + Fanisoi[2][0] * dx[0] + Fanisoi[2][1] * dx[1] + Fanisoi[2][2] * dx[2]; + tempi[2] *= rhoi * uini * r_inv; + tempj[0] = + Fanisoj[0][0] * dx[0] + Fanisoj[0][1] * dx[1] + Fanisoj[0][2] * dx[2]; + tempj[0] *= rhoj * uinj * r_inv; + tempj[1] = + Fanisoj[1][0] * dx[0] + Fanisoj[1][1] * dx[1] + Fanisoj[1][2] * dx[2]; + tempj[1] *= rhoj * uinj * r_inv; + tempj[2] = + Fanisoj[2][0] * dx[0] + Fanisoj[2][1] * dx[1] + Fanisoj[2][2] * dx[2]; + tempj[2] *= rhoj * uinj * r_inv; + gradi[0] = + -(tempi[0] - tempj[0]) * mj * rho_i_inv * rho_i_inv * forcefi * wi_dr; + gradi[1] = + -(tempi[1] - tempj[1]) * mj * rho_i_inv * rho_i_inv * forcefi * wi_dr; + gradi[2] = + -(tempi[2] - tempj[2]) * mj * rho_i_inv * rho_i_inv * forcefi * wi_dr; + gradj[0] = + -(tempi[0] - tempj[0]) * mi * rho_j_inv * rho_j_inv * forcefj * wj_dr; + gradj[1] = + -(tempi[1] - tempj[1]) * mi * rho_j_inv * rho_j_inv * forcefj * wj_dr; + gradj[2] = + -(tempi[2] - tempj[2]) * mi * rho_j_inv * rho_j_inv * forcefj * wj_dr; + } else if (diffmode == 1) { + float tempi[3], tempj[3]; + tempi[0] = + Fanisoi[0][0] * dx[0] + Fanisoi[0][1] * dx[1] + Fanisoi[0][2] * dx[2]; + tempi[0] *= uini * rho_i_inv * forcefi * r_inv * wi_dr; + tempi[1] = + Fanisoi[1][0] * dx[0] + Fanisoi[1][1] * dx[1] + Fanisoi[1][2] * dx[2]; + tempi[1] *= uini * rho_i_inv * forcefi * r_inv * wi_dr; + tempi[2] = + Fanisoi[2][0] * dx[0] + Fanisoi[2][1] * dx[1] + Fanisoi[2][2] * dx[2]; + tempi[2] *= uini * rho_i_inv * forcefi * r_inv * wi_dr; + tempj[0] = + Fanisoj[0][0] * dx[0] + Fanisoj[0][1] * dx[1] + Fanisoj[0][2] * dx[2]; + tempj[0] *= uinj * rho_j_inv * forcefj * r_inv * wj_dr; + tempj[1] = + Fanisoj[1][0] * dx[0] + Fanisoj[1][1] * dx[1] + Fanisoj[1][2] * dx[2]; + tempj[1] *= uinj * rho_j_inv * forcefj * r_inv * wj_dr; + tempj[2] = + Fanisoj[2][0] * dx[0] + Fanisoj[2][1] * dx[1] + Fanisoj[2][2] * dx[2]; + tempj[2] *= uinj * rho_j_inv * forcefj * r_inv * wj_dr; + gradi[0] = (tempi[0] + tempj[0]) * mj; + gradi[1] = (tempi[1] + tempj[1]) * mj; + gradi[2] = (tempi[2] + tempj[2]) * mj; + gradj[0] = -(tempi[0] + tempj[0]) * mi; + gradj[1] = -(tempi[1] + tempj[1]) * mi; + gradj[2] = -(tempi[2] + tempj[2]) * mi; + } else if (diffmode == 2) { + float tempi[3], tempj[3]; + tempi[0] = + Fanisoi[0][0] * dx[0] + Fanisoi[0][1] * dx[1] + Fanisoi[0][2] * dx[2]; + tempi[0] *= rhoi * uini * r_inv; + tempi[1] = + Fanisoi[1][0] * dx[0] + Fanisoi[1][1] * dx[1] + Fanisoi[1][2] * dx[2]; + tempi[1] *= rhoi * uini * r_inv; + tempi[2] = + Fanisoi[2][0] * dx[0] + Fanisoi[2][1] * dx[1] + Fanisoi[2][2] * dx[2]; + tempi[2] *= rhoi * uini * r_inv; + tempj[0] = + Fanisoj[0][0] * dx[0] + Fanisoj[0][1] * dx[1] + Fanisoj[0][2] * dx[2]; + tempj[0] *= rhoj * uinj * r_inv; + tempj[1] = + Fanisoj[1][0] * dx[0] + Fanisoj[1][1] * dx[1] + Fanisoj[1][2] * dx[2]; + tempj[1] *= rhoj * uinj * r_inv; + tempj[2] = + Fanisoj[2][0] * dx[0] + Fanisoj[2][1] * dx[1] + Fanisoj[2][2] * dx[2]; + tempj[2] *= rhoj * uinj * r_inv; + gradi[0] = -(tempi[0] - tempj[0]) * mj * rho_i_inv * rho_i_inv * + (wi_dr + wj_dr) * 0.5f; + gradi[1] = -(tempi[1] - tempj[1]) * mj * rho_i_inv * rho_i_inv * + (wi_dr + wj_dr) * 0.5f; + gradi[2] = -(tempi[2] - tempj[2]) * mj * rho_i_inv * rho_i_inv * + (wi_dr + wj_dr) * 0.5f; + gradj[0] = -(tempi[0] - tempj[0]) * mi * rho_j_inv * rho_j_inv * + (wi_dr + wj_dr) * 0.5f; + gradj[1] = -(tempi[1] - tempj[1]) * mi * rho_j_inv * rho_j_inv * + (wi_dr + wj_dr) * 0.5f; + gradj[2] = -(tempi[2] - tempj[2]) * mi * rho_j_inv * rho_j_inv * + (wi_dr + wj_dr) * 0.5f; + } else if (diffmode == 3) { + float tempi[3], tempj[3]; + tempi[0] = + Fanisoi[0][0] * dx[0] + Fanisoi[0][1] * dx[1] + Fanisoi[0][2] * dx[2]; + tempi[0] *= uini * rho_i_inv * r_inv * (wi_dr + wj_dr) * 0.5f; + tempi[1] = + Fanisoi[1][0] * dx[0] + Fanisoi[1][1] * dx[1] + Fanisoi[1][2] * dx[2]; + tempi[1] *= uini * rho_i_inv * r_inv * (wi_dr + wj_dr) * 0.5f; + tempi[2] = + Fanisoi[2][0] * dx[0] + Fanisoi[2][1] * dx[1] + Fanisoi[2][2] * dx[2]; + tempi[2] *= uini * rho_i_inv * r_inv * (wi_dr + wj_dr) * 0.5f; + tempj[0] = + Fanisoj[0][0] * dx[0] + Fanisoj[0][1] * dx[1] + Fanisoj[0][2] * dx[2]; + tempj[0] *= uinj * rho_j_inv * r_inv * (wi_dr + wj_dr) * 0.5f; + tempj[1] = + Fanisoj[1][0] * dx[0] + Fanisoj[1][1] * dx[1] + Fanisoj[1][2] * dx[2]; + tempj[1] *= uinj * rho_j_inv * r_inv * (wi_dr + wj_dr) * 0.5f; + tempj[2] = + Fanisoj[2][0] * dx[0] + Fanisoj[2][1] * dx[1] + Fanisoj[2][2] * dx[2]; + tempj[2] *= uinj * rho_j_inv * r_inv * (wi_dr + wj_dr) * 0.5f; + gradi[0] = (tempi[0] + tempj[0]) * mj; + gradi[1] = (tempi[1] + tempj[1]) * mj; + gradi[2] = (tempi[2] + tempj[2]) * mj; + gradj[0] = -(tempi[0] + tempj[0]) * mi; + gradj[1] = -(tempi[1] + tempj[1]) * mi; + gradj[2] = -(tempi[2] + tempj[2]) * mi; + } else { + error("diffmode should be 0 or 1 or 2 or 3"); + } +} + +/** + * @brief Compute the divergence according to SPH + * Note that the differential is: 1/rho * div(rho * fin) + * @param fini quantity for particle i to be differentiated + * @param finj quantity for particle j to be differentiated + * @param mi mass of particle i + * @param mj mass of particle j + * @param forcefi a correction factor for spatial variations of hi + * @param forcefj a correction factor for spatial variations of hj + * @param rhoi density of particle i + * @param rhoj density of particle j + * @param wi_dr derivative of kernel i + * @param wj_dr derivative of kernel j + * @param dx Comoving vector separating both particles (pi - pj). + * @param r Comoving distance between the two particles. + * @param diffmode mode=0 for ``difference''; mode=1 for ``symmetric''; mode=2 + * for ``difference'' but ignore h correction + * @param divfi divergence of f for particle i + * @param divfj divergence of f for particle j + */ +__attribute__((always_inline)) INLINE static void radiation_divergence_SPH( + const float *fini, const float *finj, const float mi, const float mj, + const float forcefi, const float forcefj, const float rhoi, + const float rhoj, const float wi_dr, const float wj_dr, const float dx[3], + const float r, int diffmode, float *divfi, float *divfj) { + const float rho_i_inv = 1.0f / rhoi; + const float rho_j_inv = 1.0f / rhoj; + const float r_inv = 1.0f / r; + if (diffmode == 0) { + float drhof[3]; + const float faci = mj * forcefi * rho_i_inv * rho_i_inv * wi_dr; + const float facj = mi * forcefj * rho_j_inv * rho_j_inv * wj_dr; + /* Compute drhof dot r */ + drhof[0] = rhoi * fini[0] - rhoj * finj[0]; + drhof[1] = rhoi * fini[1] - rhoj * finj[1]; + drhof[2] = rhoi * fini[2] - rhoj * finj[2]; + const float drhofdr = + drhof[0] * dx[0] + drhof[1] * dx[1] + drhof[2] * dx[2]; + *divfi = -faci * drhofdr * r_inv; + *divfj = -facj * drhofdr * r_inv; + } else if (diffmode == 1) { + float uradpre_common = 0.0f; + uradpre_common += fini[0] * rho_i_inv * forcefi * dx[0] * r_inv * wi_dr; + uradpre_common += fini[1] * rho_i_inv * forcefi * dx[1] * r_inv * wi_dr; + uradpre_common += fini[2] * rho_i_inv * forcefi * dx[2] * r_inv * wi_dr; + uradpre_common += finj[0] * rho_j_inv * forcefj * dx[0] * r_inv * wj_dr; + uradpre_common += finj[1] * rho_j_inv * forcefj * dx[1] * r_inv * wj_dr; + uradpre_common += finj[2] * rho_j_inv * forcefj * dx[2] * r_inv * wj_dr; + *divfi = mj * uradpre_common; + *divfj = -mi * uradpre_common; + } else if (diffmode == 2) { + float drhof[3]; + const float faci = mj * rho_i_inv * rho_i_inv * (wi_dr + wj_dr) * 0.5f; + const float facj = mi * rho_j_inv * rho_j_inv * (wi_dr + wj_dr) * 0.5f; + /* Compute drhof dot r */ + drhof[0] = rhoi * fini[0] - rhoj * finj[0]; + drhof[1] = rhoi * fini[1] - rhoj * finj[1]; + drhof[2] = rhoi * fini[2] - rhoj * finj[2]; + const float drhofdr = + drhof[0] * dx[0] + drhof[1] * dx[1] + drhof[2] * dx[2]; + *divfi = -faci * drhofdr * r_inv; + *divfj = -facj * drhofdr * r_inv; + } else { + error("diffmode should be 0 or 1 or 2"); + } +} + +/** + * @brief Compute the anistropic divergence according to SPH + * Note that the differential is: 1/rho * div(Ftensor * rho * fin) + * @param fini quantity for particle i to be differentiated + * @param finj quantity for particle j to be differentiated + * @param mi mass of particle i + * @param mj mass of particle j + * @param forcefi a correction factor for spatial variations of hi + * @param forcefj a correction factor for spatial variations of hj + * @param rhoi density of particle i + * @param rhoj density of particle j + * @param wi_dr derivative of kernel i + * @param wj_dr derivative of kernel j + * @param Fanisoi Ftensor i + * @param Fanisoj Ftensor j + * @param dx Comoving vector separating both particles (pi - pj). + * @param r Comoving distance between the two particles. + * @param diffmode mode=0 for ``difference''; mode=1 for ``symmetric''; mode=2 + * for ``difference'' but ignore h correction + * @param divfi divergence of f for particle i + * @param divfj divergence of f for particle j + */ +__attribute__((always_inline)) INLINE static void +radiation_divergence_aniso_SPH(const float *fini, const float *finj, + const float mi, const float mj, + const float forcefi, const float forcefj, + const float rhoi, const float rhoj, + const float wi_dr, const float wj_dr, + float Fanisoi[3][3], float Fanisoj[3][3], + const float dx[3], const float r, int diffmode, + float *divfi, float *divfj) { + const float rho_i_inv = 1.0f / rhoi; + const float rho_j_inv = 1.0f / rhoj; + const float r_inv = 1.0f / r; + if (diffmode == 0) { + float tempi, tempj; + tempi = (Fanisoi[0][0] * dx[0] + Fanisoi[0][1] * dx[1] + + Fanisoi[0][2] * dx[2]) * + fini[0]; + tempi += (Fanisoi[1][0] * dx[0] + Fanisoi[1][1] * dx[1] + + Fanisoi[1][2] * dx[2]) * + fini[1]; + tempi += (Fanisoi[2][0] * dx[0] + Fanisoi[2][1] * dx[1] + + Fanisoi[2][2] * dx[2]) * + fini[2]; + tempi *= rhoi * r_inv; + tempj = (Fanisoj[0][0] * dx[0] + Fanisoj[0][1] * dx[1] + + Fanisoj[0][2] * dx[2]) * + finj[0]; + tempj += (Fanisoj[1][0] * dx[0] + Fanisoj[1][1] * dx[1] + + Fanisoj[1][2] * dx[2]) * + finj[1]; + tempj += (Fanisoj[2][0] * dx[0] + Fanisoj[2][1] * dx[1] + + Fanisoj[2][2] * dx[2]) * + finj[2]; + tempj *= rhoj * r_inv; + *divfi = -(tempi - tempj) * mj * forcefi * rho_i_inv * rho_i_inv * wi_dr; + *divfj = -(tempi - tempj) * mi * forcefj * rho_j_inv * rho_j_inv * wj_dr; + } else if (diffmode == 1) { + float tempi, tempj; + tempi = (Fanisoi[0][0] * dx[0] + Fanisoi[0][1] * dx[1] + + Fanisoi[0][2] * dx[2]) * + fini[0]; + tempi += (Fanisoi[1][0] * dx[0] + Fanisoi[1][1] * dx[1] + + Fanisoi[1][2] * dx[2]) * + fini[1]; + tempi += (Fanisoi[2][0] * dx[0] + Fanisoi[2][1] * dx[1] + + Fanisoi[2][2] * dx[2]) * + fini[2]; + tempi *= rho_i_inv * forcefi * r_inv * wi_dr; + tempj = (Fanisoj[0][0] * dx[0] + Fanisoj[0][1] * dx[1] + + Fanisoj[0][2] * dx[2]) * + finj[0]; + tempj += (Fanisoj[1][0] * dx[0] + Fanisoj[1][1] * dx[1] + + Fanisoj[1][2] * dx[2]) * + finj[1]; + tempj += (Fanisoj[2][0] * dx[0] + Fanisoj[2][1] * dx[1] + + Fanisoj[2][2] * dx[2]) * + finj[2]; + tempj *= rho_j_inv * forcefj * r_inv * wj_dr; + *divfi = (tempi + tempj) * mj; + *divfj = -(tempi + tempj) * mi; + } else if (diffmode == 2) { + float tempi, tempj; + tempi = (Fanisoi[0][0] * dx[0] + Fanisoi[0][1] * dx[1] + + Fanisoi[0][2] * dx[2]) * + fini[0]; + tempi += (Fanisoi[1][0] * dx[0] + Fanisoi[1][1] * dx[1] + + Fanisoi[1][2] * dx[2]) * + fini[1]; + tempi += (Fanisoi[2][0] * dx[0] + Fanisoi[2][1] * dx[1] + + Fanisoi[2][2] * dx[2]) * + fini[2]; + tempi *= rhoi * r_inv; + tempj = (Fanisoj[0][0] * dx[0] + Fanisoj[0][1] * dx[1] + + Fanisoj[0][2] * dx[2]) * + finj[0]; + tempj += (Fanisoj[1][0] * dx[0] + Fanisoj[1][1] * dx[1] + + Fanisoj[1][2] * dx[2]) * + finj[1]; + tempj += (Fanisoj[2][0] * dx[0] + Fanisoj[2][1] * dx[1] + + Fanisoj[2][2] * dx[2]) * + finj[2]; + tempj *= rhoj * r_inv; + *divfi = + -(tempi - tempj) * mj * rho_i_inv * rho_i_inv * (wi_dr + wj_dr) * 0.5f; + *divfj = + -(tempi - tempj) * mi * rho_j_inv * rho_j_inv * (wi_dr + wj_dr) * 0.5f; + } else { + error("diffmode should be 0 or 1 or 2"); + } +} + +/** + * @brief Compute the divergence according to SPH + * Note that the differential is: 1/rho * div(rho * fin) + * @param fini quantity for particle i to be differentiated + * @param finj quantity for particle j to be differentiated + * @param mi mass of particle i + * @param mj mass of particle j + * @param forcefi a correction factor for spatial variations of hi + * @param forcefj a correction factor for spatial variations of hj + * @param rhoi density of particle i + * @param rhoj density of particle j + * @param wi_dr derivative of kernel i + * @param wj_dr derivative of kernel j + * @param dx Comoving vector separating both particles (pi - pj). + * @param r Comoving distance between the two particles. + * @param diffmode mode=0 for ``difference'' + * @param shearfi divergence of f for particle i + * @param shearfj divergence of f for particle j + */ +__attribute__((always_inline)) INLINE static void radiation_gradient_vec_SPH( + const float *fini, const float *finj, const float mi, const float mj, + const float forcefi, const float forcefj, const float rhoi, + const float rhoj, const float wi_dr, const float wj_dr, const float dx[3], + const float r, int diffmode, float shearfi[3][3], float shearfj[3][3]) { + const float rho_i_inv = 1.0f / rhoi; + const float rho_j_inv = 1.0f / rhoj; + const float r_inv = 1.0f / r; + if (diffmode == 0) { + float drhof[3]; + const float faci = mj * forcefi * rho_i_inv * rho_i_inv * wi_dr; + const float facj = mi * forcefj * rho_j_inv * rho_j_inv * wj_dr; + /* Compute drhof dot r */ + drhof[0] = rhoi * fini[0] - rhoj * finj[0]; + drhof[1] = rhoi * fini[1] - rhoj * finj[1]; + drhof[2] = rhoi * fini[2] - rhoj * finj[2]; + for (int k = 0; k < 3; k++) { + shearfi[k][0] = -faci * r_inv * drhof[0] * dx[k]; + shearfi[k][1] = -faci * r_inv * drhof[1] * dx[k]; + shearfi[k][2] = -faci * r_inv * drhof[2] * dx[k]; + shearfj[k][0] = -facj * r_inv * drhof[0] * dx[k]; + shearfj[k][1] = -facj * r_inv * drhof[1] * dx[k]; + shearfj[k][2] = -facj * r_inv * drhof[2] * dx[k]; + } + } else { + error("diffmode should be 0"); + } +} + +#endif /* SWIFT_RT_GRAD_SPHM1RT_H */ diff --git a/src/rt/SPHM1RT/rt_iact.h b/src/rt/SPHM1RT/rt_iact.h index 4bae9c88cf963df82223d33c3562a65ce8f5fb97..23242980cc7cf653c225f36108ac86606d3b7977 100644 --- a/src/rt/SPHM1RT/rt_iact.h +++ b/src/rt/SPHM1RT/rt_iact.h @@ -20,6 +20,8 @@ #ifndef SWIFT_RT_IACT_SPHM1RT_H #define SWIFT_RT_IACT_SPHM1RT_H +#include "rt_gradients.h" + /** * @file src/rt/SPHM1RT/rt_iact.h * @brief Main header file for no radiative transfer scheme particle @@ -29,7 +31,6 @@ /** * @brief Preparation step for injection to gather necessary data. - * This function gets called during the feedback force loop. * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (si - pj). @@ -46,7 +47,44 @@ runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, const float hi, const float hj, struct spart *si, const struct part *pj, const struct cosmology *cosmo, - const struct rt_props *rt_props) {} + const struct rt_props *rt_props) { + + /* If the star doesn't have any neighbours, we + * have nothing to do here. */ + if (si->density.wcount == 0.f) return; + + /* Compute the weight of the neighbouring particle */ + const float mj = hydro_get_mass(pj); + const float r = sqrtf(r2); + /* Get the gas density. */ + const float rhoj = hydro_get_comoving_density(pj); + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + float wi; + kernel_eval(ui, &wi); + + /* This is actually the inverse of the enrichment weight */ + /* we abuse the variable here */ + if (r2 != 0.f) { +#if defined(HYDRO_DIMENSION_3D) + si->rt_data.injection_weight += mj / rhoj / r2; +#elif defined(HYDRO_DIMENSION_2D) + si->rt_data.injection_weight += mj / rhoj / r; +#elif defined(HYDRO_DIMENSION_1D) + si->rt_data.injection_weight += mj / rhoj; +#endif + } + /* get the radiation energy within injection radius */ + /* we need it only when we need to redistribute the radiation energy */ + if (rt_props->reinject) { + float urad[RT_NGROUPS]; + rt_get_physical_urad_multifrequency(pj, cosmo, urad); + for (int g = 0; g < RT_NGROUPS; g++) { + si->rt_data.emission_reinject[g] += mj * urad[g]; + } + } +} /** * @brief Injection step interaction between star and hydro particles. @@ -59,13 +97,208 @@ runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, * @param pj Hydro particle. * @param a Current scale factor. * @param H Current Hubble parameter. + * @param rt_props Properties of the RT scheme. */ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( const float r2, float *dx, const float hi, const float hj, - struct spart *restrict si, struct part *restrict pj, float a, float H) {} + struct spart *restrict si, struct part *restrict pj, float a, float H, + const struct rt_props *rt_props) { + + /* If the star doesn't have any neighbours, we + * have nothing to do here. */ + if (si->density.wcount == 0.f) return; + if (si->rt_data.injection_weight == 0.f) return; + + /* the direction of the radiation injected */ + const float r = sqrtf(r2); + const float minus_r_inv = -1.f / r; + const float n_unit[3] = {dx[0] * minus_r_inv, dx[1] * minus_r_inv, + dx[2] * minus_r_inv}; + + /* Get particle mass */ + const float mj = hydro_get_mass(pj); + const float mj_inv = 1.f / mj; + /* Get the gas density. */ + /* TK comment: we need to be careful in the cosmological case here: */ + const float rhoj = hydro_get_comoving_density(pj); + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + float wi; + kernel_eval(ui, &wi); + + /* collect the enrichment weights from the neighborhood */ + float tot_weight_inv; + tot_weight_inv = 1.f / si->rt_data.injection_weight; + + float injection_weight = 0.f; + /* the enrichment weight of individual gas particle */ + if (r2 != 0.f) { +#if defined(HYDRO_DIMENSION_3D) + injection_weight = mj / rhoj / r2; +#elif defined(HYDRO_DIMENSION_2D) + injection_weight = mj / rhoj / r; +#elif defined(HYDRO_DIMENSION_1D) + injection_weight = mj / rhoj; +#endif + } + + float new_urad, new_frad; + float urad[RT_NGROUPS]; + float frad[RT_NGROUPS][3]; + const float cred = rt_get_comoving_cred(pj, a); + for (int g = 0; g < RT_NGROUPS; g++) { + /* Inject energy. */ + if (rt_props->reinject) { + new_urad = (si->rt_data.emission_this_step[g] + + si->rt_data.emission_reinject[g]) * + injection_weight * tot_weight_inv * mj_inv; + } else { + new_urad = si->rt_data.emission_this_step[g] * injection_weight * + tot_weight_inv * mj_inv + + pj->rt_data.conserved[g].urad; + } + + urad[g] = new_urad; + + /* Inject flux. */ + /* We assume the path from the star to the gas is optically thin */ + new_frad = new_urad * cred; + frad[g][0] = new_frad * n_unit[0]; + frad[g][1] = new_frad * n_unit[1]; + frad[g][2] = new_frad * n_unit[2]; + } + + rt_set_comoving_urad_multifrequency(pj, urad); + rt_set_comoving_frad_multifrequency(pj, frad); +} /** - * @brief Flux calculation between particle i and particle j + * @brief do radiation gradient computation + * + * @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. + * @param mode: mode=1 symmetric, mode=0 non symmetric + * + * + */ +__attribute__((always_inline)) INLINE static void +radiation_gradient_loop_function(float r2, const float *dx, float hi, float hj, + struct part *restrict pi, + struct part *restrict pj, float a, float H, + int mode) { + + struct rt_part_data *rpi = &pi->rt_data; + struct rt_part_data *rpj = &pj->rt_data; + + float wi, wj, wi_dx, wj_dx; + /* Get r */ + const float r = sqrtf(r2); + + /* part j */ + /* Get the kernel for hj */ + const float hj_inv = 1.0f / hj; + + /* Compute the kernel function for pj */ + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + + /* part i */ + /* Get the kernel for hi */ + const float hi_inv = 1.0f / hi; + + /* Compute the kernel function for pi */ + const float xi = r * hi_inv; + kernel_deval(xi, &wi, &wi_dx); + + /* Get mass */ + const float mj = hydro_get_mass(pj); + const float mi = hydro_get_mass(pi); + const float rhoj = hydro_get_comoving_density(pj); + const float rhoi = hydro_get_comoving_density(pi); + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float wj_dr = hjd_inv * wj_dx; + const float wi_dr = hid_inv * wi_dx; + + float uradmfi[RT_NGROUPS]; + float uradmfj[RT_NGROUPS]; + + const float credi = rt_get_comoving_cred(pi, a); + const float credj = rt_get_comoving_cred(pj, a); + + /* use urad * c instead */ + float uradci; + float uradcj; + + float fradmfi[RT_NGROUPS][3]; + float fradmfj[RT_NGROUPS][3]; + + float fradi[3]; + float fradj[3]; + + /*******************************/ + /* Computer gradient of radiation field times c */ + /*******************************/ + float gradi[3], gradj[3]; + int diffmode = 2; + float divfi, divfj; + + /* gas density should not be zero */ + if ((rhoi == 0.f) || (rhoi == 0.f)) return; + + rt_get_comoving_urad_multifrequency(pi, uradmfi); + rt_get_comoving_urad_multifrequency(pj, uradmfj); + + rt_get_comoving_frad_multifrequency(pi, fradmfi); + rt_get_comoving_frad_multifrequency(pj, fradmfj); + + for (int g = 0; g < RT_NGROUPS; g++) { + + uradci = uradmfi[g] * credi; + uradcj = uradmfj[g] * credj; + radiation_gradient_SPH(uradci, uradcj, mi, mj, rpi->force.f, rpj->force.f, + rhoi, rhoj, wi_dr, wj_dr, dx, r, diffmode, gradi, + gradj); + + rpi->diffusion[g].graduradc[0] += gradi[0]; + rpi->diffusion[g].graduradc[1] += gradi[1]; + rpi->diffusion[g].graduradc[2] += gradi[2]; + if (mode == 1) { + rpj->diffusion[g].graduradc[0] += gradj[0]; + rpj->diffusion[g].graduradc[1] += gradj[1]; + rpj->diffusion[g].graduradc[2] += gradj[2]; + } + + /*******************************/ + /* Now we need to compute the div of f terms */ + /*******************************/ + divfi = 0.0f; + divfj = 0.0f; + fradi[0] = fradmfi[g][0]; + fradi[1] = fradmfi[g][1]; + fradi[2] = fradmfi[g][2]; + fradj[0] = fradmfj[g][0]; + fradj[1] = fradmfj[g][1]; + fradj[2] = fradmfj[g][2]; + radiation_divergence_SPH(fradi, fradj, mi, mj, rpi->force.f, rpj->force.f, + rhoi, rhoj, wi_dr, wj_dr, dx, r, diffmode, &divfi, + &divfj); + rpi->viscosity[g].divf += divfi; + if (mode == 1) { + rpj->viscosity[g].divf += divfj; + } + } +} + +/** + * @brief Calculate the gradient interaction between particle i and particle j * * @param r2 Comoving squared distance between particle i and particle j. * @param dx Comoving distance vector between the particles (dx = pi->x - @@ -77,13 +310,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( * @param a Current scale factor. * @param H Current Hubble parameter. */ -__attribute__((always_inline)) INLINE static void runner_iact_rt_transport( +__attribute__((always_inline)) INLINE static void runner_iact_rt_gradient( float r2, const float *dx, float hi, float hj, struct part *restrict pi, - struct part *restrict pj, float a, float H) {} + struct part *restrict pj, float a, float H) { + radiation_gradient_loop_function(r2, dx, hi, hj, pi, pj, a, H, 1); +} /** - * @brief Flux calculation between particle i and particle j: non-symmetric - * version + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version * * @param r2 Comoving squared distance between particle i and particle j. * @param dx Comoving distance vector between the particles (dx = pi->x - @@ -96,12 +331,399 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_transport( * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_rt_transport(float r2, const float *dx, float hi, float hj, - struct part *restrict pi, - struct part *restrict pj, float a, float H) {} +runner_iact_nonsym_rt_gradient(float r2, const float *dx, float hi, float hj, + struct part *restrict pi, + struct part *restrict pj, float a, float H) { + radiation_gradient_loop_function(r2, dx, hi, hj, pi, pj, a, H, 0); +} /** - * @brief Calculate the gradient interaction between particle i and particle j + * @brief do radiation force computation + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @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. + * @param mode: mode=1 symmetric, mode=0 non symmetric + * + */ +__attribute__((always_inline)) INLINE static void radiation_force_loop_function( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H, int mode) { + + struct rt_part_data *rpi = &pi->rt_data; + struct rt_part_data *rpj = &pj->rt_data; + + float wi, wj, wi_dx, wj_dx; + /* Get r */ + const float r = sqrtf(r2); + + /* part j */ + /* Get the kernel for hj */ + const float hj_inv = 1.0f / hj; + + /* Compute the kernel function for pj */ + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + + /* part i */ + /* Get the kernel for hi */ + const float hi_inv = 1.0f / hi; + + /* Compute the kernel function for pi */ + const float xi = r * hi_inv; + kernel_deval(xi, &wi, &wi_dx); + + /* Get mass */ + const float mj = hydro_get_mass(pj); + const float mi = hydro_get_mass(pi); + const float rhoj = hydro_get_comoving_density(pj); + const float rhoi = hydro_get_comoving_density(pi); + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float wj_dr = hjd_inv * wj_dx; + const float wi_dr = hid_inv * wi_dx; + + float uradmfi[RT_NGROUPS]; + float uradmfj[RT_NGROUPS]; + + const float credi = rt_get_comoving_cred(pi, a); + const float credj = rt_get_comoving_cred(pj, a); + + float fradmfi[RT_NGROUPS][3]; + float fradmfj[RT_NGROUPS][3]; + + rt_get_comoving_urad_multifrequency(pi, uradmfi); + rt_get_comoving_urad_multifrequency(pj, uradmfj); + + rt_get_comoving_frad_multifrequency(pi, fradmfi); + rt_get_comoving_frad_multifrequency(pj, fradmfj); + + /*******************************/ + /* CALCULATIONS OF TWO MOMENT EQUATIONS */ + /*******************************/ + + /* Calculate the radiation energy term from the divergence of f */ + int diffmode; + + float diss_durad_term_i, diss_durad_term_j; + float fradmagi, fradmagj; + double fradmagidouble, fradmagjdouble; + + float hid_inv_temp, wi_dr_temp, hjd_inv_temp, wj_dr_temp; + float drhou_low, diss_durad_term; + + float ddfi, ddfj; + float diss_dfrad_term_i[3], diss_dfrad_term_j[3]; + float durad_dt_i, durad_dt_j; + float dfrad_dt_i[3], dfrad_dt_j[3]; + float fraduniti[3], fradunitj[3]; + float vsig_diss_i, vsig_diss_j; + + float J_tensori[3][3]; + float J_tensorj[3][3]; + float ddi, ddj; + float foxi, foxj; + float sqi, sqj; + float flimi, flimj; + float F_tensori[3][3]; + float F_tensorj[3][3]; + float funiti[3], funitj[3]; + + int diffmodeaniso; + float diff_dfrad_term_i[3], diff_dfrad_term_j[3]; + const float r_inv = 1.f / r; + + float uradi, uradj; + float uradi0, uradj0; + float cred0 = fmaxf(credi, credj); + float rhomean2; + float fradi[3], fradj[3]; + double fradidouble[3], fradjdouble[3]; + float divfipar, divfjpar; /* divfipar is inside the loop, and divfi is summed + over particle */ + float divfi, divfj; + float alpha_diss_i, alpha_f_diss_i, alpha_diss_j, alpha_f_diss_j; + + /* gas density should not be zero */ + if ((rhoi == 0.f) || (rhoi == 0.f)) return; + + for (int g = 0; g < RT_NGROUPS; g++) { + uradi = uradmfi[g]; + uradj = uradmfj[g]; + uradi0 = uradi * credi / cred0; + uradj0 = uradj * credj / cred0; + fradi[0] = fradmfi[g][0]; + fradi[1] = fradmfi[g][1]; + fradi[2] = fradmfi[g][2]; + fradj[0] = fradmfj[g][0]; + fradj[1] = fradmfj[g][1]; + fradj[2] = fradmfj[g][2]; + + alpha_diss_i = rpi->diffusion[g].alpha; + alpha_f_diss_i = rpi->viscosity[g].alpha; + alpha_diss_j = rpj->diffusion[g].alpha; + alpha_f_diss_j = rpj->viscosity[g].alpha; + + divfi = rpi->viscosity[g].divf; + divfj = rpj->viscosity[g].divf; + + /* do nothing if there is no radiation */ + if ((uradi == 0.f) && (uradj == 0.f)) continue; + +#if defined(HYDRO_DIMENSION_1D) + fradi[1] = 0.0f; + fradi[2] = 0.0f; + fradj[1] = 0.0f; + fradj[2] = 0.0f; +#endif +#if defined(HYDRO_DIMENSION_2D) + fradi[2] = 0.0f; + fradj[2] = 0.0f; +#endif + + if ((fradi[0] == 0.f) && (fradi[1] == 0.f) && (fradi[2] == 0.f)) { + fradmagi = 0.f; + } else { + fradidouble[0] = (double)(fradi[0]); + fradidouble[1] = (double)(fradi[1]); + fradidouble[2] = (double)(fradi[2]); + fradmagidouble = sqrt(fradidouble[0] * fradidouble[0] + + fradidouble[1] * fradidouble[1] + + fradidouble[2] * fradidouble[2]); + if (isinf(fradmagidouble) || isnan(fradmagidouble)) + error("Got inf/nan in fradmagi | %.6e| %.6e %.6e %.6e", fradmagidouble, + fradi[0], fradi[1], fradi[2]); + fradmagi = (float)(fradmagidouble); + } + + if ((fradj[0] == 0.f) && (fradj[1] == 0.f) && (fradj[2] == 0.f)) { + fradmagj = 0.f; + } else { + fradjdouble[0] = (double)(fradj[0]); + fradjdouble[1] = (double)(fradj[1]); + fradjdouble[2] = (double)(fradj[2]); + fradmagjdouble = sqrt(fradjdouble[0] * fradjdouble[0] + + fradjdouble[1] * fradjdouble[1] + + fradjdouble[2] * fradjdouble[2]); + if (isinf(fradmagjdouble) || isnan(fradmagjdouble)) + error("Got inf/nan in fradmagj | %.6e| %.6e %.6e %.6e", fradmagjdouble, + fradj[0], fradj[1], fradj[2]); + fradmagj = (float)(fradmagjdouble); + } + + /*******************************/ + /* CALCULATIONS OF TWO MOMENT EQUATIONS */ + /*******************************/ + + /* Calculate the radiation energy term from the divergence of f */ + diffmode = 2; + divfipar = 0.0f; + divfjpar = 0.0f; + radiation_divergence_SPH(fradi, fradj, mi, mj, rpi->force.f, rpj->force.f, + rhoi, rhoj, wi_dr, wj_dr, dx, r, diffmode, + &divfipar, &divfjpar); + + /* Calculate the radiation flux term */ + /* funit is for Eddington tensor */ + if (fradmagi != 0.f) { + funiti[0] = fradi[0] / fradmagi; + funiti[1] = fradi[1] / fradmagi; + funiti[2] = fradi[2] / fradmagi; + funitj[0] = funiti[0]; + funitj[1] = funiti[1]; + funitj[2] = funiti[2]; + } else if (fradmagj != 0.f) { + funitj[0] = fradj[0] / fradmagj; + funitj[1] = fradj[1] / fradmagj; + funitj[2] = fradj[2] / fradmagj; + funiti[0] = funitj[0]; + funiti[1] = funitj[1]; + funiti[2] = funitj[2]; + } else { + /* Nothing we can do */ + return; + } + + /* Eddington factor (or optical thickness estimator?) */ + if (credi * uradi == 0.f) { + foxi = expf(-rpi->params.chi[g] * rhoi * hi); + } else { + foxi = fmaxf(expf(-rpi->params.chi[g] * rhoi * hi), + fradmagi / (credi * uradi)); + } + if (credj * uradj == 0.f) { + foxj = expf(-rpj->params.chi[g] * rhoj * hj); + } else { + foxj = fmaxf(expf(-rpj->params.chi[g] * rhoj * hj), + fradmagj / (credj * uradj)); + } + + foxi = fminf(foxi, 1.0f); + foxj = fminf(foxj, 1.0f); + + /* M1 closure with (modified) Eddington factor */ + /* protect against negative in the square root */ + sqi = 4.f - 3.f * foxi * foxi; + sqj = 4.f - 3.f * foxj * foxj; + flimi = fminf(1.f, (3.f + 4.f * foxi * foxi) / (5.f + 2.f * sqrtf(sqi))); + flimj = fminf(1.f, (3.f + 4.f * foxj * foxj) / (5.f + 2.f * sqrtf(sqj))); + + /* compute the Eddington tensor (without radiation energy density yet) */ + + for (int k = 0; k < 3; k++) { + for (int j = 0; j < 3; j++) { + F_tensori[k][j] = 0.5f * (3.0f * flimi - 1.0f) * funiti[k] * funiti[j]; + F_tensorj[k][j] = 0.5f * (3.0f * flimj - 1.0f) * funitj[k] * funitj[j]; + } + } + + F_tensori[0][0] += 0.5f * (1.0f - flimi); + F_tensori[1][1] += 0.5f * (1.0f - flimi); + F_tensori[2][2] += 0.5f * (1.0f - flimi); + F_tensorj[0][0] += 0.5f * (1.0f - flimj); + F_tensorj[1][1] += 0.5f * (1.0f - flimj); + F_tensorj[2][2] += 0.5f * (1.0f - flimj); + +#if defined(HYDRO_DIMENSION_1D) + /* the eddington tensor is different in 1D */ + for (int k = 0; k < 3; k++) { + for (int j = 0; j < 3; j++) { + F_tensori[k][j] = 0.0f; + F_tensorj[k][j] = 0.0f; + } + } + F_tensori[0][0] += 1.0f; + F_tensorj[0][0] += 1.0f; +#endif + + /* compute the contribution from the Eddington tensor to df/dt */ + diffmodeaniso = 2; + diff_dfrad_term_i[0] = 0.0f; + diff_dfrad_term_i[1] = 0.0f; + diff_dfrad_term_i[2] = 0.0f; + diff_dfrad_term_j[0] = 0.0f; + diff_dfrad_term_j[1] = 0.0f; + diff_dfrad_term_j[2] = 0.0f; + radiation_gradient_aniso_SPH(uradi0, uradj0, mi, mj, rpi->force.f, + rpj->force.f, rhoi, rhoj, wi_dr, wj_dr, + F_tensori, F_tensorj, dx, r, diffmodeaniso, + diff_dfrad_term_i, diff_dfrad_term_j); + diff_dfrad_term_i[0] *= -cred0 * cred0 / mj; + diff_dfrad_term_i[1] *= -cred0 * cred0 / mj; + diff_dfrad_term_i[2] *= -cred0 * cred0 / mj; + diff_dfrad_term_j[0] *= -cred0 * cred0 / mi; + diff_dfrad_term_j[1] *= -cred0 * cred0 / mi; + diff_dfrad_term_j[2] *= -cred0 * cred0 / mi; + + /*******************************/ + /* HERE COME THE CALCULATIONS OF ARTIFICIAL DISSIPATION */ + /*******************************/ + + /* fradunit is for anisotropic artificial viscosity */ + + if (fradmagi != 0.f) { + fraduniti[0] = fradi[0] / fradmagi; + fraduniti[1] = fradi[1] / fradmagi; + fraduniti[2] = fradi[2] / fradmagi; + } else { + fraduniti[0] = 0.0f; + fraduniti[1] = 0.0f; + fraduniti[2] = 0.0f; + } + + if (fradmagj != 0.f) { + fradunitj[0] = fradj[0] / fradmagj; + fradunitj[1] = fradj[1] / fradmagj; + fradunitj[2] = fradj[2] / fradmagj; + } else { + fradunitj[0] = 0.0f; + fradunitj[1] = 0.0f; + fradunitj[2] = 0.0f; + } + + vsig_diss_i = credi; + vsig_diss_j = credj; + + /* compute the artificial diffusion tensor */ + ddi = alpha_diss_i * vsig_diss_i * hi; + ddj = alpha_diss_j * vsig_diss_j * hj; + + for (int k = 0; k < 3; k++) { + for (int j = 0; j < 3; j++) { + J_tensori[k][j] = fraduniti[k] * fraduniti[j]; + J_tensorj[k][j] = fradunitj[k] * fradunitj[j]; + } + } + + /* compute the anisotropic diffusion */ + hid_inv_temp = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + wi_dr_temp = hid_inv_temp * wi_dx; + hjd_inv_temp = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + wj_dr_temp = hjd_inv_temp * wj_dx; + drhou_low = rhoi * uradi0 - rhoj * uradj0; + if ((uradi == 0.f) && (uradj == 0.f)) { + diss_durad_term = 0.0f; + } else { + rhomean2 = fminf(rhoi, rhoj) * fminf(rhoi, rhoj); + diss_durad_term = (drhou_low / rhomean2) * (wi_dr_temp + wj_dr_temp); + /* TK test: the interpolation is broken: need to fix later. */ + diss_durad_term *= (ddi + ddj); + diss_durad_term *= 0.5f * r_inv; + } + diss_durad_term_i = mj * diss_durad_term; + diss_durad_term_j = -mi * diss_durad_term; + + /* compute the anisotropic diffusion for radiation flux */ + ddfi = alpha_f_diss_i * vsig_diss_i * hi; + ddfj = alpha_f_diss_j * vsig_diss_j * hj; + + diffmodeaniso = 2; + diss_dfrad_term_i[0] = 0.0f; + diss_dfrad_term_i[1] = 0.0f; + diss_dfrad_term_i[2] = 0.0f; + diss_dfrad_term_j[0] = 0.0f; + diss_dfrad_term_j[1] = 0.0f; + diss_dfrad_term_j[2] = 0.0f; + radiation_gradient_aniso_SPH( + ddfi * divfi, ddfj * divfj, mi, mj, rpi->force.f, rpj->force.f, rhoi, + rhoj, wi_dr, wj_dr, J_tensori, J_tensorj, dx, r, diffmodeaniso, + diss_dfrad_term_i, diss_dfrad_term_j); + + /* Assemble the radiation energy equation term */ + durad_dt_i = -divfipar / mj + diss_durad_term_i / mj; + /* Assemble the radiation flux equation term */ + dfrad_dt_i[0] = diff_dfrad_term_i[0] + diss_dfrad_term_i[0] / mj; + dfrad_dt_i[1] = diff_dfrad_term_i[1] + diss_dfrad_term_i[1] / mj; + dfrad_dt_i[2] = diff_dfrad_term_i[2] + diss_dfrad_term_i[2] / mj; + + if (mode == 1) { + durad_dt_j = -divfjpar / mi + diss_durad_term_j / mi; + dfrad_dt_j[0] = diff_dfrad_term_j[0] + diss_dfrad_term_j[0] / mi; + dfrad_dt_j[1] = diff_dfrad_term_j[1] + diss_dfrad_term_j[1] / mi; + dfrad_dt_j[2] = diff_dfrad_term_j[2] + diss_dfrad_term_j[2] / mi; + } + + rpi->dconserved_dt[g].urad += mj * durad_dt_i * cred0 / credi; + rpi->dconserved_dt[g].frad[0] += mj * dfrad_dt_i[0]; + rpi->dconserved_dt[g].frad[1] += mj * dfrad_dt_i[1]; + rpi->dconserved_dt[g].frad[2] += mj * dfrad_dt_i[2]; + if (mode == 1) { + rpj->dconserved_dt[g].urad += mi * durad_dt_j * cred0 / credj; + rpj->dconserved_dt[g].frad[0] += mi * dfrad_dt_j[0]; + rpj->dconserved_dt[g].frad[1] += mi * dfrad_dt_j[1]; + rpj->dconserved_dt[g].frad[2] += mi * dfrad_dt_j[2]; + } + } +} + +/** + * @brief Flux calculation between particle i and particle j * * @param r2 Comoving squared distance between particle i and particle j. * @param dx Comoving distance vector between the particles (dx = pi->x - @@ -113,13 +735,16 @@ runner_iact_nonsym_rt_transport(float r2, const float *dx, float hi, float hj, * @param a Current scale factor. * @param H Current Hubble parameter. */ -__attribute__((always_inline)) INLINE static void runner_iact_rt_gradient( +__attribute__((always_inline)) INLINE static void runner_iact_rt_transport( float r2, const float *dx, float hi, float hj, struct part *restrict pi, - struct part *restrict pj, float a, float H) {} + struct part *restrict pj, float a, float H) { + + radiation_force_loop_function(r2, dx, hi, hj, pi, pj, a, H, 1); +} /** - * @brief Calculate the gradient interaction between particle i and particle j: - * non-symmetric version + * @brief Flux calculation between particle i and particle j: non-symmetric + * version * * @param r2 Comoving squared distance between particle i and particle j. * @param dx Comoving distance vector between the particles (dx = pi->x - @@ -132,8 +757,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_gradient( * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_rt_gradient(float r2, const float *dx, float hi, float hj, - struct part *restrict pi, - struct part *restrict pj, float a, float H) {} +runner_iact_nonsym_rt_transport(float r2, const float *dx, float hi, float hj, + struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + radiation_force_loop_function(r2, dx, hi, hj, pi, pj, a, H, 0); +} #endif /* SWIFT_RT_IACT_SPHM1RT_H */ diff --git a/src/rt/SPHM1RT/rt_io.h b/src/rt/SPHM1RT/rt_io.h index 61312036a20dc5bcb198907a641f8fb76a7505b5..c1cf3c41f521cfd29b6e8bc70c8167251aea0f1d 100644 --- a/src/rt/SPHM1RT/rt_io.h +++ b/src/rt/SPHM1RT/rt_io.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) - * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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,7 +20,9 @@ #ifndef SWIFT_RT_IO_SPHM1RT_H #define SWIFT_RT_IO_SPHM1RT_H -#include "io_properties.h" +#define RT_LABELS_SIZE 10 + +#include "rt.h" /** * @file src/rt/SPHM1RT/rt_io.h @@ -38,7 +40,36 @@ */ INLINE static int rt_read_particles(const struct part* parts, struct io_props* list) { - return 0; + + /* List what we want to read */ + + /* Note that in the input, we read radiation energy and flux + * then we convert these quantities to radiation energy per mass and flux per + * mass in rt_convert_quantities */ + + char fieldname[30]; + int count = 0; + for (int phg = 0; phg < RT_NGROUPS; phg++) { + sprintf(fieldname, "PhotonEnergiesGroup%d", phg + 1); + list[count++] = + io_make_input_field(fieldname, FLOAT, 1, OPTIONAL, UNIT_CONV_ENERGY, + parts, rt_data.conserved[phg].urad); + sprintf(fieldname, "PhotonFluxesGroup%d", phg + 1); + list[count++] = io_make_input_field(fieldname, FLOAT, 3, OPTIONAL, + UNIT_CONV_ENERGY_VELOCITY, parts, + rt_data.conserved[phg].frad); + } + + /* Read quantities for thermo-chemistry */ + list[count++] = io_make_input_field( + "RtElementMassFractions", FLOAT, rt_chemistry_element_count, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, rt_data.tchem.metal_mass_fraction); + + list[count++] = io_make_input_field( + "RtSpeciesAbundances", FLOAT, rt_species_count, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, rt_data.tchem.abundances); + + return count; } /** @@ -54,18 +85,85 @@ INLINE static int rt_read_stars(const struct spart* sparts, return 0; } +/** + * @brief Extract photon energies of conserved struct for all photon groups + * we convert radiation energy per mass to radiation energy + */ +INLINE static void rt_convert_conserved_photon_energies( + const struct engine* engine, const struct part* part, + const struct xpart* xpart, float* ret) { + + for (int g = 0; g < RT_NGROUPS; g++) { + ret[g] = part->rt_data.conserved[g].urad * part->mass; + } +} + +/** + * @brief Extract photon energies of conserved struct for all photon groups + * we convert radiation flux per mass to radiation flux + */ +INLINE static void rt_convert_conserved_photon_fluxes( + const struct engine* engine, const struct part* part, + const struct xpart* xpart, float* ret) { + + int i = 0; + for (int g = 0; g < RT_NGROUPS; g++) { + ret[i++] = part->rt_data.conserved[g].frad[0] * part->mass; + ret[i++] = part->rt_data.conserved[g].frad[1] * part->mass; + ret[i++] = part->rt_data.conserved[g].frad[2] * part->mass; + } +} + /** * @brief Creates additional output fields for the radiative * transfer data of hydro particles. + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_particles(const struct part* parts, struct io_props* list) { - return 0; + + /* Note that in the output, we write radiation energy and flux + * then we convert these quantities from radiation energy per mass and flux + * per mass + * */ + int num_elements = 4; + + list[0] = io_make_output_field_convert_part( + "PhotonEnergies", FLOAT, RT_NGROUPS, UNIT_CONV_ENERGY, 0, parts, + /*xparts=*/NULL, rt_convert_conserved_photon_energies, + "Photon Energies (all groups)"); + + list[1] = io_make_output_field_convert_part( + "PhotonFluxes", FLOAT, 3 * RT_NGROUPS, UNIT_CONV_ENERGY_VELOCITY, 0, + parts, + /*xparts=*/NULL, rt_convert_conserved_photon_fluxes, + "Photon Fluxes (all groups; x, y, and z coordinates)"); + + list[2] = io_make_output_field( + "RtElementMassFractions", FLOAT, rt_chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, parts, rt_data.tchem.metal_mass_fraction, + "Fractions of the particles' masses that are in the given element"); + + list[3] = io_make_output_field( + "RtSpeciesAbundances", FLOAT, rt_species_count, UNIT_CONV_NO_UNITS, 0.f, + parts, rt_data.tchem.abundances, + "Species Abundances in unit of hydrogen number density"); + + return num_elements; } /** * @brief Creates additional output fields for the radiative * transfer data of star particles. + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_stars(const struct spart* sparts, struct io_props* list) { @@ -86,6 +184,225 @@ INLINE static void rt_write_flavour(hid_t h_grp, hid_t h_grp_columns, const struct engine* e, const struct unit_system* internal_units, const struct unit_system* snapshot_units, - const struct rt_props* rtp) {} + const struct rt_props* rtp) { + +#if defined(HAVE_HDF5) + + /* Write scheme name */ + /* ----------------- */ + io_write_attribute_s(h_grp, "RT Scheme", RT_IMPLEMENTATION); + + /* Write photon group counts */ + /* ------------------------- */ + io_write_attribute_i(h_grp, "PhotonGroupNumber", RT_NGROUPS); + + /* Write photon group bin edges */ + /* ---------------------------- */ + + /* Note: photon frequency bin edges are kept in cgs. Convert them here to + * internal units so we're still compatible with swiftsimio. */ + /* TK comment: I think rtp->photon_groups is already in internal unit */ + // const float Hz_internal = + // units_cgs_conversion_factor(internal_units, UNIT_CONV_INV_TIME); + // const float Hz_internal_inv = 1.f / Hz_internal; + float photon_groups_internal[RT_NGROUPS]; + for (int g = 0; g < RT_NGROUPS; g++) + photon_groups_internal[g] = rtp->photon_groups[g]; + // photon_groups_internal[g] = rtp->photon_groups[g] * Hz_internal_inv; + + hid_t type_float = H5Tcopy(io_hdf5_type(FLOAT)); + + hsize_t dims[1] = {RT_NGROUPS}; + hid_t space = H5Screate_simple(1, dims, NULL); + hid_t dset = H5Dcreate(h_grp, "PhotonGroupEdges", type_float, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, type_float, H5S_ALL, H5S_ALL, H5P_DEFAULT, + photon_groups_internal); + + /* Write unit conversion factors for this data set */ + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, snapshot_units, UNIT_CONV_INV_TIME, + /*scale_factor_exponent=*/0); + float baseUnitsExp[5]; + units_get_base_unit_exponents_array(baseUnitsExp, UNIT_CONV_INV_TIME); + io_write_attribute_f(dset, "U_M exponent", baseUnitsExp[UNIT_MASS]); + io_write_attribute_f(dset, "U_L exponent", baseUnitsExp[UNIT_LENGTH]); + io_write_attribute_f(dset, "U_t exponent", baseUnitsExp[UNIT_TIME]); + io_write_attribute_f(dset, "U_I exponent", baseUnitsExp[UNIT_CURRENT]); + io_write_attribute_f(dset, "U_T exponent", baseUnitsExp[UNIT_TEMPERATURE]); + io_write_attribute_f(dset, "h-scale exponent", 0.f); + io_write_attribute_f(dset, "a-scale exponent", 0.f); + io_write_attribute_s(dset, "Expression for physical CGS units", buffer); + + /* Write the actual number this conversion factor corresponds to */ + const double factor = + units_cgs_conversion_factor(snapshot_units, UNIT_CONV_INV_TIME); + io_write_attribute_d( + dset, "Conversion factor to CGS (not including cosmological corrections)", + factor); + io_write_attribute_d( + dset, + "Conversion factor to physical CGS (including cosmological corrections)", + factor * pow(e->cosmology->a, 0.f)); + + H5Dclose(dset); + /* H5Tclose(type_float); [> close this later <] */ + + /* If without RT, we have nothing more to do. */ + const int with_rt = e->policy & engine_policy_rt; + if (!with_rt) return; + + /* Write photon group names */ + /* -------------------------*/ + + /* Generate Energy Group names */ + char names_energy[RT_NGROUPS * RT_LABELS_SIZE]; + for (int g = 0; g < RT_NGROUPS; g++) { + char newEname[RT_LABELS_SIZE]; + sprintf(newEname, "Group%d", g + 1); + strcpy(names_energy + g * RT_LABELS_SIZE, newEname); + } + + /* Now write them down */ + hid_t type_string_label = H5Tcopy(H5T_C_S1); + H5Tset_size(type_string_label, RT_LABELS_SIZE); + + hsize_t dimsE[1] = {RT_NGROUPS}; + hid_t spaceE = H5Screate_simple(1, dimsE, NULL); + hid_t dsetE = H5Dcreate(h_grp_columns, "PhotonEnergies", type_string_label, + spaceE, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dsetE, type_string_label, H5S_ALL, H5S_ALL, H5P_DEFAULT, + names_energy); + H5Dclose(dsetE); + + /* Generate Fluxes Group Names */ + char names_fluxes[3 * RT_NGROUPS * RT_LABELS_SIZE]; + int i = 0; + for (int g = 0; g < RT_NGROUPS; g++) { + char newFnameX[RT_LABELS_SIZE]; + sprintf(newFnameX, "Group%dX", g + 1); + strcpy(names_fluxes + i * RT_LABELS_SIZE, newFnameX); + i++; + char newFnameY[RT_LABELS_SIZE]; + sprintf(newFnameY, "Group%dY", g + 1); + strcpy(names_fluxes + i * RT_LABELS_SIZE, newFnameY); + i++; + char newFnameZ[RT_LABELS_SIZE]; + sprintf(newFnameZ, "Group%dZ", g + 1); + strcpy(names_fluxes + i * RT_LABELS_SIZE, newFnameZ); + i++; + } + + /* Now write them down */ + hsize_t dimsF[1] = {3 * RT_NGROUPS}; + hid_t spaceF = H5Screate_simple(1, dimsF, NULL); + hid_t dsetF = H5Dcreate(h_grp_columns, "PhotonFluxes", type_string_label, + spaceF, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dsetF, type_string_label, H5S_ALL, H5S_ALL, H5P_DEFAULT, + names_fluxes); + H5Dclose(dsetF); + + /* H5Tclose(type_string_label); [> close this later <] */ + + /* Write reduced speed of light */ + /* ---------------------------- */ + /* hid_t type2 = H5Tcopy(io_hdf5_type(FLOAT)); */ + + hsize_t dims_cred[1] = {1}; + hid_t space_cred = H5Screate_simple(1, dims_cred, NULL); + hid_t dset_cred = + H5Dcreate(h_grp, "ReducedLightspeed", type_float, space_cred, H5P_DEFAULT, + H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset_cred, type_float, H5S_ALL, H5S_ALL, H5P_DEFAULT, + &rtp->cred_phys); + + /* Write unit conversion factors for this data set */ + char buffer_cred[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer_cred, snapshot_units, UNIT_CONV_VELOCITY, + /*scale_factor_exponent=*/0); + float baseUnitsExp_cred[5]; + units_get_base_unit_exponents_array(baseUnitsExp_cred, UNIT_CONV_VELOCITY); + io_write_attribute_f(dset_cred, "U_M exponent", baseUnitsExp_cred[UNIT_MASS]); + io_write_attribute_f(dset_cred, "U_L exponent", + baseUnitsExp_cred[UNIT_LENGTH]); + io_write_attribute_f(dset_cred, "U_t exponent", baseUnitsExp_cred[UNIT_TIME]); + io_write_attribute_f(dset_cred, "U_I exponent", + baseUnitsExp_cred[UNIT_CURRENT]); + io_write_attribute_f(dset_cred, "U_T exponent", + baseUnitsExp_cred[UNIT_TEMPERATURE]); + io_write_attribute_f(dset_cred, "h-scale exponent", 0.f); + io_write_attribute_f(dset_cred, "a-scale exponent", 0.f); + io_write_attribute_s(dset_cred, "Expression for physical CGS units", + buffer_cred); + + /* Write the actual number this conversion factor corresponds to */ + /* TODO Mladen: check cosmology. reduced_speed_of_light is physical only for + * now. */ + const double factor_cred = + units_cgs_conversion_factor(snapshot_units, UNIT_CONV_VELOCITY); + io_write_attribute_d( + dset_cred, + "Conversion factor to CGS (not including cosmological corrections)", + factor_cred); + io_write_attribute_d( + dset_cred, + "Conversion factor to physical CGS (including cosmological corrections)", + factor_cred * pow(e->cosmology->a, 0.f)); + + H5Dclose(dset_cred); + + /* Clean up after yourself */ + /* ----------------------- */ + + /* Close up the types */ + H5Tclose(type_float); + H5Tclose(type_string_label); + + /* Create an array of element names */ + const int rt_element_name_length = 32; + char rt_element_names[rt_chemistry_element_count][rt_element_name_length]; + for (int elem = 0; elem < rt_chemistry_element_count; ++elem) { + sprintf(rt_element_names[elem], "%s", + rt_chemistry_get_element_name((enum rt_chemistry_element)elem)); + } + + /* Add to the named columns */ + hsize_t rt_dims[1] = {rt_chemistry_element_count}; + hid_t rt_type = H5Tcopy(H5T_C_S1); + H5Tset_size(rt_type, rt_element_name_length); + hid_t rt_space = H5Screate_simple(1, rt_dims, NULL); + hid_t rt_dset = H5Dcreate(h_grp_columns, "RtElementMassFractions", rt_type, + rt_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(rt_dset, rt_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, + rt_element_names[0]); + H5Dclose(rt_dset); + + H5Tclose(rt_type); + H5Sclose(rt_space); + + /* Add the species names to the named columns */ + const int rt_species_name_length = 32; + char rt_species_names[rt_species_count][rt_species_name_length]; + for (int spec = 0; spec < rt_species_count; ++spec) { + sprintf(rt_species_names[spec], "%s", + rt_get_species_name((enum rt_cooling_species)spec)); + } + + /* Add to the named columns */ + hsize_t rts_dims[1] = {rt_species_count}; + hid_t rts_type = H5Tcopy(H5T_C_S1); + H5Tset_size(rts_type, rt_species_name_length); + hid_t rts_space = H5Screate_simple(1, rts_dims, NULL); + hid_t rts_dset = H5Dcreate(h_grp_columns, "RtSpeciesAbundances", rts_type, + rts_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(rts_dset, rts_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, + rt_species_names[0]); + H5Dclose(rts_dset); + + H5Tclose(rts_type); + H5Sclose(rts_space); + +#endif /* HAVE_HDF5 */ +} #endif /* SWIFT_RT_IO_SPHM1RT_H */ diff --git a/src/rt/SPHM1RT/rt_parameters.h b/src/rt/SPHM1RT/rt_parameters.h index cf775387e258a8b8e47cc7075c3a57dd45dc75d9..33fcbd033d193f5097a636bb0771ebd6585f1879 100644 --- a/src/rt/SPHM1RT/rt_parameters.h +++ b/src/rt/SPHM1RT/rt_parameters.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 @@ -29,6 +29,9 @@ extern struct rt_parameters rt_params; +/** + * Some global RT related parameters. + */ struct rt_parameters {}; #endif /* SWIFT_SPHM1RT_RT_PARAMETERS_H */ diff --git a/src/rt/SPHM1RT/rt_properties.h b/src/rt/SPHM1RT/rt_properties.h index 5e0ca6fe246167fb5599716c2397550e6336ea58..f2eb53d2030fb103296dd691fa3e207ac9fd2423 100644 --- a/src/rt/SPHM1RT/rt_properties.h +++ b/src/rt/SPHM1RT/rt_properties.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) - * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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,25 +20,117 @@ #ifndef SWIFT_RT_PROPERTIES_SPHM1RT_H #define SWIFT_RT_PROPERTIES_SPHM1RT_H +#include "hydro.h" +#include "rt_species_and_elements.h" + /** * @file src/rt/SPHM1RT/rt_properties.h - * @brief Main header file for the 'none' radiative transfer scheme properties. - * SPHM1RT method described in Chan+21: 2102.08404 + * @brief Main header file for the 'SPHM1RT' radiative transfer scheme + * properties. SPHM1RT method described in Chan+21: 2102.08404 */ +#define RT_IMPLEMENTATION "SPH M1closure" + /** - * @brief Properties of the 'none' radiative transfer model + * @brief Properties of the 'SPHM1RT' radiative transfer model */ struct rt_props { - /* Are we running with hydro or star controlled injection? - * This is added to avoid #ifdef macros as far as possible */ - int hydro_controlled_injection; + /* CFL condition */ + float CFL_condition; + + /* reduced speed of light in code unit (physical) */ + float cred_phys; + + /*! initial opacity */ + float initialchi[RT_NGROUPS]; + + /* Frequency bin edges for photon groups + * Includes 0 as leftmost edge, doesn't include infinity as + * rightmost bin edge*/ + float photon_groups[RT_NGROUPS]; + + /* Are we using constant stellar emission rates? */ + int use_const_emission_rates; + + /* Global constant stellar emission rates */ + double stellar_const_emission_rates[RT_NGROUPS]; + + /* Optionally restrict maximal timestep for stars */ + float stars_max_timestep; + + /* Which stellar spectrum type to use? */ + int stellar_spectrum_type; + /* If constant: get max frequency */ + double const_stellar_spectrum_max_frequency; + /* If blackbody: get temperature */ + double stellar_spectrum_blackbody_T; + + /* Skip thermochemistry? For testing/debugging only! */ + int skip_thermochemistry; + + /*! Fraction of the particle mass in given elements at the start of the run */ + float initial_metal_mass_fraction[rt_chemistry_element_count]; + + /*! Atomic mass for the corresponding metal_mass_fraction */ + float atomicmass[rt_chemistry_element_count]; + + /*! The inverse of atomic mass for the corresponding metal_mass_fraction */ + float atomicmass_inv[rt_chemistry_element_count]; + + /*! Fraction of the particle mass in *all* metals at the start of the run */ + float initial_metal_mass_fraction_total; + + int useabundances; + + float initial_species_abundance[rt_species_count]; - /* Do we need to run a conversion after the zeroth - * step, but before the first step? */ - int convert_stars_after_zeroth_step; - int convert_parts_after_zeroth_step; + /* switch for the on the spot approximation? */ + int onthespot; + + /*! The energy of an ionizing photon in cgs units */ + /*! it is three bins for now: from HI-HeI, HeI-HeII, HeII-Inf */ + double ionizing_photon_energy_cgs[3]; + + /* switch for cooling (and photoheating) */ + /* however, there is still photo-ionization even if the switch is off */ + int coolingon; + + /* switch for fixing the photo-density */ + int fixphotondensity; + + /* the photo-density values if the photo density is fixed */ + float Fgamma_fixed_cgs[3]; + + /* gather energy around injection radius and re-inject the energy */ + int reinject; + + /*! switch to use thermo-chemistry parameters from the parameter file */ + int useparams; + + /* the following only use when useparam = 1 */ + /*! The case A recombination coefficient for hydrogen (cgs) */ + double alphaA_cgs_H; + + /*! The case B recombination coefficient for hydrogen (cgs) */ + double alphaB_cgs_H; + + /*! The collisional ionization coefficient for hydrogen (cgs) */ + double beta_cgs_H; + + /*! The cross section of ionizing photons for hydrogen (cgs) */ + double sigma_cross_cgs_H[3]; + + /*** end of useparams = 1 ***/ + + /*! tolerance of relative change to shift from explicit solver to CVODE */ + double explicitRelTolerance; + + /*! CVODE absolute tolerance */ + double absoluteTolerance; + + /*! CVODE relative tolerance */ + double relativeTolerance; }; /** @@ -53,6 +145,25 @@ __attribute__((always_inline)) INLINE static void rt_props_print( if (engine_rank != 0) return; message("Radiative transfer scheme: '%s'", RT_IMPLEMENTATION); + + char messagestring[200] = "Using photon frequency bins: [0."; + char freqstring[20]; + for (int g = 1; g < RT_NGROUPS; g++) { + sprintf(freqstring, ", %.3g", rtp->photon_groups[g]); + strcat(messagestring, freqstring); + } + strcat(messagestring, "]"); + message("%s", messagestring); + + if (rtp->use_const_emission_rates) { + strcpy(messagestring, "Using constant stellar emission rates: [ "); + for (int g = 0; g < RT_NGROUPS; g++) { + sprintf(freqstring, "%.3g ", rtp->stellar_const_emission_rates[g]); + strcat(messagestring, freqstring); + } + strcat(messagestring, "]"); + message("%s", messagestring); + } } /** @@ -69,17 +180,227 @@ __attribute__((always_inline)) INLINE static void rt_props_init( const struct unit_system* us, struct swift_params* params, struct cosmology* cosmo) { -#ifdef RT_HYDRO_CONTROLLED_INJECTION - rtp->hydro_controlled_injection = 1; -#else - rtp->hydro_controlled_injection = 0; -#endif + if (RT_NGROUPS <= 0) { + error( + "You need to run SPHM1RT with at least 1 photon group, " + "you have %d", + RT_NGROUPS); + } else if (RT_NGROUPS == 1) { + rtp->photon_groups[0] = 0.f; + } else { + /* Read in parameters */ + float frequencies[RT_NGROUPS - 1]; + parser_get_param_float_array(params, "SPHM1RT:photon_groups_Hz", + RT_NGROUPS - 1, frequencies); + float Hz_internal = units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME); + float Hz_internal_inv = 1.f / Hz_internal; + for (int g = 0; g < RT_NGROUPS - 1; g++) { + rtp->photon_groups[g + 1] = frequencies[g] * Hz_internal_inv; + } + rtp->photon_groups[0] = 0.f; + } + + /* Are we using constant emission rates? */ + /* ------------------------------------- */ + rtp->use_const_emission_rates = parser_get_opt_param_int( + params, "SPHM1RT:use_const_emission_rates", /* default = */ 0); + + if (rtp->use_const_emission_rates) { + double emission_rates[RT_NGROUPS]; + parser_get_param_double_array(params, "SPHM1RT:star_emission_rates", + RT_NGROUPS, emission_rates); + for (int g = 0; g < RT_NGROUPS; g++) { + rtp->stellar_const_emission_rates[g] = emission_rates[g]; + } + } else { + /* kill the run for now */ + error("SPHM1RT can't run without constant stellar emission rates for now."); + } + + /* get reduced speed of light in code unit (physical) */ + const float cred_phys = parser_get_param_float(params, "SPHM1RT:cred"); + + /* TK reminder: rtp->cred_phys is physical */ + rtp->cred_phys = cred_phys; + + /* get initial opacity in code unit */ + int errorint = parser_get_opt_param_float_array(params, "SPHM1RT:chi", + RT_NGROUPS, rtp->initialchi); + + if (errorint == 0) { + message("SPHM1RT:chi not found in params, using default values"); + for (int g = 0; g < RT_NGROUPS; g++) { + rtp->initialchi[g] = 0.0f; + } + } + + /* get CFL condition */ + const float CFL = parser_get_param_float(params, "SPHM1RT:CFL_condition"); + rtp->CFL_condition = CFL; + + /* Initialize conditional parameters to bogus values */ + rtp->const_stellar_spectrum_max_frequency = -1.; + rtp->stellar_spectrum_blackbody_T = -1.; + + rtp->stellar_spectrum_type = + parser_get_param_int(params, "SPHM1RT:stellar_spectrum_type"); + if (rtp->stellar_spectrum_type == 0) { + /* Constant spectrum: Read additional parameter */ + /* TODO: also translate back to internal units at later. For now, keep it in + * Hz */ + rtp->const_stellar_spectrum_max_frequency = parser_get_param_float( + params, "SPHM1RT:stellar_spectrum_const_max_frequency_Hz"); + } else if (rtp->stellar_spectrum_type == 1) { + /* Blackbody spectrum: Read additional parameter */ + rtp->stellar_spectrum_blackbody_T = parser_get_param_float( + params, "SPHM1RT:stellar_spectrum_blackbody_temperature_K"); + rtp->stellar_spectrum_blackbody_T /= + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + } else { + error("Selected unknown stellar spectrum type %d", + rtp->stellar_spectrum_type); + } + + /* Maximal Star Timestep? */ + /* ---------------------- */ + rtp->stars_max_timestep = parser_get_opt_param_float( + params, "SPHM1RT:stars_max_timestep", /*default=*/FLT_MAX); + /* Turn off if negative value given */ + if (rtp->stars_max_timestep < 0.f) rtp->stars_max_timestep = FLT_MAX; + /* Better safe than sorry */ + if (rtp->stars_max_timestep == 0.f) + error("You are restricting star time step to 0. That's a no-no."); + + /* thermo-chemistry parameters */ + + /* Are we skipping thermochemistry? */ + rtp->skip_thermochemistry = parser_get_opt_param_int( + params, "SPHM1RT:skip_thermochemistry", /* default = */ 0); + + if (rtp->skip_thermochemistry != 1) { + if (RT_NGROUPS != 4) + error( + "With thermo-chemistry, we can only use four frequency bins " + "(--with-rt=SPHM1RT_4): 0-HI, HI-HeI, HeI-HeII, HeII-Inf"); + } + + /* Read the total metallicity */ + rtp->initial_metal_mass_fraction_total = parser_get_opt_param_float( + params, "SPHM1RT:init_mass_fraction_metal", -1.f); - rtp->convert_parts_after_zeroth_step = 0; - rtp->convert_stars_after_zeroth_step = 0; + if (rtp->initial_metal_mass_fraction_total != -1.f) { + /* Read the individual mass fractions */ + for (int elem = 0; elem < rt_chemistry_element_count; ++elem) { + char buffer[50]; + sprintf(buffer, "SPHM1RT:init_mass_fraction_%s", + rt_chemistry_get_element_name((enum rt_chemistry_element)elem)); + rtp->initial_metal_mass_fraction[elem] = + parser_get_param_float(params, buffer); + } + } + + /* set up the atomic mass */ + rtp->atomicmass[rt_chemistry_element_H] = 1.0f; + rtp->atomicmass[rt_chemistry_element_He] = 4.0f; + rtp->atomicmass_inv[rt_chemistry_element_H] = + 1.0f / rtp->atomicmass[rt_chemistry_element_H]; + rtp->atomicmass_inv[rt_chemistry_element_He] = + 1.0f / rtp->atomicmass[rt_chemistry_element_He]; + + /* switch to use species abundances from the param file */ + rtp->useabundances = + parser_get_opt_param_float(params, "SPHM1RT:useabundances", 0); + + if (rtp->useabundances != 0) { + /* Read the individual species abundances */ + for (int spec = 0; spec < rt_species_count; ++spec) { + char buffer[50]; + sprintf(buffer, "SPHM1RT:init_species_abundance_%s", + rt_get_species_name((enum rt_cooling_species)spec)); + rtp->initial_species_abundance[spec] = + parser_get_param_float(params, buffer); + } + } + + rtp->explicitRelTolerance = + parser_get_opt_param_double(params, "SPHM1RT:explicitRelTolerance", 0.1); + rtp->absoluteTolerance = + parser_get_opt_param_double(params, "SPHM1RT:absoluteTolerance", 1e-8); + rtp->relativeTolerance = + parser_get_opt_param_double(params, "SPHM1RT:relativeTolerance", 1e-3); + + errorint = parser_get_opt_param_double_array( + params, "SPHM1RT:ionizing_photon_energy_erg", 3, + rtp->ionizing_photon_energy_cgs); + if (errorint == 0) { + message( + "SPHM1RT:ionizing_photon_energy_erg not found in params, using default " + "values"); + /* assume blackbody 1e5K spectrum */ + rtp->ionizing_photon_energy_cgs[0] = 3.0208e-11; + rtp->ionizing_photon_energy_cgs[1] = 5.61973e-11; + rtp->ionizing_photon_energy_cgs[2] = 1.05154e-10; + } + + /* options */ + /* gather energy around injection radius and re-inject the energy */ + rtp->reinject = parser_get_opt_param_int(params, "SPHM1RT:reinject", 0); + + /*! switch to use thermo-chemistry parameters from the parameter file */ + rtp->useparams = parser_get_opt_param_int(params, "SPHM1RT:useparams", 0); + /* 1: turn on cooling on gas; 0: turn off. */ + rtp->coolingon = parser_get_opt_param_int(params, "SPHM1RT:coolingon", 1); + + /* 1: apply on the spot approixmation; 0: turn it off. */ + rtp->onthespot = parser_get_opt_param_int(params, "SPHM1RT:onthespot", 1); + + /* 1: not changing photon density in thermochemistry; 0: evolve photon + * density. */ + rtp->fixphotondensity = + parser_get_opt_param_int(params, "SPHM1RT:fixphotondensity", 0); + errorint = parser_get_opt_param_float_array( + params, "SPHM1RT:Fgamma_fixed_cgs", 3, rtp->Fgamma_fixed_cgs); + if (errorint == 0) { + message( + "SPHM1RT:Fgamma_fixed_cgs not found in params, using default values"); + for (int ibin = 0; ibin < 3; ibin++) { + rtp->Fgamma_fixed_cgs[ibin] = -1.0; + } + } else { + if (rtp->fixphotondensity == 0) + error("Fgamma_fixed_cgs has to be used with fixphotondensity"); + } + + /*! The cross section of ionizing photons for hydrogen (cgs) */ + /*! current assume three frequency bins */ + errorint = parser_get_opt_param_double_array(params, "SPHM1RT:sigma_cross", 3, + rtp->sigma_cross_cgs_H); + if (errorint == 0) { + message("SPHM1RT:sigma_cross not found in params, using default values"); + /* assume blackbody 1e5K spectrum */ + rtp->sigma_cross_cgs_H[0] = 2.99e-18; + rtp->sigma_cross_cgs_H[1] = 5.66e-19; + rtp->sigma_cross_cgs_H[2] = 7.84e-20; + } + + rtp->alphaA_cgs_H = + parser_get_opt_param_double(params, "SPHM1RT:alphaA", 4.29e-13); + rtp->alphaB_cgs_H = + parser_get_opt_param_double(params, "SPHM1RT:alphaB", 2.59e-13); + rtp->beta_cgs_H = + parser_get_opt_param_double(params, "SPHM1RT:beta", 1.245e-15); + + if ((rtp->useparams == 1) && (rtp->coolingon == 1)) { + error("Unphysical: SPHM1RT:useparams=1 and SPHM1RT:coolingon=1"); + } /* After initialisation, print params to screen */ rt_props_print(rtp); + + /* Print a final message. */ + if (engine_rank == 0) { + message("Radiative transfer initialized"); + } } /** @@ -102,9 +423,12 @@ __attribute__((always_inline)) INLINE static void rt_struct_dump( * * @param props the struct * @param stream the file stream + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. */ __attribute__((always_inline)) INLINE static void rt_struct_restore( - struct rt_props* props, FILE* stream) { + struct rt_props* props, FILE* stream, const struct phys_const* phys_const, + const struct unit_system* us) { restart_read_blocks((void*)props, sizeof(struct rt_props), 1, stream, NULL, "RT properties struct"); diff --git a/src/rt/SPHM1RT/rt_rate_equations.c b/src/rt/SPHM1RT/rt_rate_equations.c new file mode 100644 index 0000000000000000000000000000000000000000..ef6bd07414cc38d451917c1a21e0d571fc430697 --- /dev/null +++ b/src/rt/SPHM1RT/rt_rate_equations.c @@ -0,0 +1,205 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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 src/rt/SPHM1RT/rt_rate_equations.c + * @brief Thermo-chemistry rate equation for + * SPHM1RT method described in Chan+21: 2102.08404 + */ + +/* Local includes. */ +#include "rt_cooling_rates.h" +#include "rt_species_and_elements.h" + +/* Local includes. */ +#include <cvode/cvode.h> +#include <cvode/cvode_direct.h> /* access to CVDls interface */ +#include <nvector/nvector_serial.h> +#include <sundials/sundials_types.h> +#include <sunlinsol/sunlinsol_dense.h> +#include <sunmatrix/sunmatrix_dense.h> + +/** + * @brief Defines the right-hand side of the system of differential equations + * (dy/dt = ydot). + * + * Defines the system of differential equations that make + * up the right-hand side function, which will be integrated + * by CVode. + * + * @param t Current time. + * @param y Vector containing the variables to be integrated. + * @param ydot Vector containing the time derivatives of the variables. + * @param user_data The #RTUserData struct containing the input data. + */ +int rt_frateeq(realtype t, N_Vector y, N_Vector ydot, void *user_data) { + struct RTUserData *data; + + data = (struct RTUserData *)user_data; + + /* First, loop through the enum types of all + * non-eq species. If they are included in + * the network then their abundance is in + * the vector y. */ + int icount = + 0; /* We use this to keep track of where we are in the vector y */ + int aindex[3]; + for (int i = 0; i < 3; i++) { + aindex[i] = data->aindex[i]; + } + for (int i = 0; i < 3; i++) { + data->abundances[aindex[i]] = (double)NV_Ith_S(y, icount); + icount += 1; + } + + /* Update the species not in the network */ + double finish_abundances[rt_species_count]; + rt_enforce_constraint_equations(data->abundances, data->metal_mass_fraction, + finish_abundances); + for (int spec = 0; spec < rt_species_count; spec++) { + data->abundances[spec] = finish_abundances[spec]; + } + + /* If Thermal Evolution is switched on, the element in the + * vector y is the internal energy (per unit volume). Use this + * to update the temperature, and also the rates that depend on T */ + double u_cgs; + if (data->coolingon == 1) { + u_cgs = (double)NV_Ith_S(y, icount); + if (data->u_min_cgs > u_cgs) { + u_cgs = data->u_min_cgs; + } + icount += 1; + } else { + u_cgs = data->u_cgs; + if (data->u_min_cgs > u_cgs) { + u_cgs = data->u_min_cgs; + } + } + + /* the final element in the + * vector y is the photon density. + * */ + double ngamma_cgs[3]; + if (data->fixphotondensity == 0) { + for (int i = 0; i < 3; i++) { + ngamma_cgs[i] = (double)NV_Ith_S(y, icount); + icount += 1; + } + } else { + for (int i = 0; i < 3; i++) { + ngamma_cgs[i] = data->ngamma_cgs[i]; + } + } + + double T_cgs = + rt_convert_u_to_temp(data->k_B_cgs, data->m_H_cgs, + data->metal_mass_fraction[rt_chemistry_element_H], + u_cgs, data->abundances); + const double T_min_cgs = + rt_convert_u_to_temp(data->k_B_cgs, data->m_H_cgs, + data->metal_mass_fraction[rt_chemistry_element_H], + data->u_min_cgs, data->abundances); + if (T_min_cgs > T_cgs) { + T_cgs = T_min_cgs; + } + + // Update rates + double alphalist[rt_species_count], betalist[rt_species_count], + Gammalist[rt_species_count], sigmalist[3][3], epsilonlist[3][3]; + + if (data->useparams == 1) { + betalist[rt_sp_elec] = 0.0; + betalist[rt_sp_HI] = data->beta_cgs_H; + betalist[rt_sp_HII] = 0.0; + betalist[rt_sp_HeI] = 0.0; + betalist[rt_sp_HeII] = 0.0; + betalist[rt_sp_HeIII] = 0.0; + alphalist[rt_sp_elec] = 0.0; + alphalist[rt_sp_HI] = 0.0; + alphalist[rt_sp_HeI] = 0.0; + if (data->onthespot == 1) { + alphalist[rt_sp_HII] = data->alphaB_cgs_H; + alphalist[rt_sp_HeII] = 0.0; + alphalist[rt_sp_HeIII] = 0.0; + } else { + alphalist[rt_sp_HII] = data->alphaA_cgs_H; + alphalist[rt_sp_HeII] = 0.0; + alphalist[rt_sp_HeIII] = 0.0; + } + + sigmalist[0][0] = data->sigma_cross_cgs_H[0]; + sigmalist[1][0] = data->sigma_cross_cgs_H[1]; + sigmalist[2][0] = data->sigma_cross_cgs_H[2]; + sigmalist[0][1] = 0.0; + sigmalist[1][1] = 0.0; + sigmalist[2][1] = 0.0; + sigmalist[0][2] = 0.0; + sigmalist[1][2] = 0.0; + sigmalist[2][2] = 0.0; + for (int spec = 0; spec < rt_species_count; spec++) { + Gammalist[spec] = 0.0; + } + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + epsilonlist[i][j] = 0.0; + } + } + } else { + rt_compute_rate_coefficients(T_cgs, data->onthespot, alphalist, betalist, + Gammalist, sigmalist, epsilonlist); + } + + // Compute creation and destruction rates + double absorption_rate[3], chemistry_rates[rt_species_count]; + + rt_compute_radiation_rate(data->n_H_cgs, data->cred_cgs, data->abundances, + ngamma_cgs, sigmalist, aindex, absorption_rate); + + rt_compute_chemistry_rate(data->n_H_cgs, data->cred_cgs, data->abundances, + ngamma_cgs, alphalist, betalist, sigmalist, aindex, + chemistry_rates); + + double Lambda_net_cgs; + Lambda_net_cgs = rt_compute_cooling_rate( + data->n_H_cgs, data->cred_cgs, data->abundances, ngamma_cgs, Gammalist, + sigmalist, epsilonlist, aindex); + + int jcount = 0; + /* Now set the output ydot vector for the chemical abundances */ + for (int i = 0; i < 3; i++) { + NV_Ith_S(ydot, jcount) = + (realtype)(chemistry_rates[aindex[i]] / data->n_H_cgs); + jcount += 1; + } + /* Now set the output ydot vector for the internal energy */ + if (data->coolingon == 1) { + NV_Ith_S(ydot, jcount) = (realtype)(Lambda_net_cgs / data->rho_cgs); + jcount += 1; + } + + /* Now set the output ydot vector for the radiation density */ + if (data->fixphotondensity == 0) { + for (int i = 0; i < 3; i++) { + NV_Ith_S(ydot, jcount) = (realtype)(-absorption_rate[i]); + jcount += 1; + } + } + return (0); +} diff --git a/src/rt/SPHM1RT/rt_setters.h b/src/rt/SPHM1RT/rt_setters.h new file mode 100644 index 0000000000000000000000000000000000000000..9e4b38e872363e54c81a2f03287af89366287236 --- /dev/null +++ b/src/rt/SPHM1RT/rt_setters.h @@ -0,0 +1,115 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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_SPHM1RT_RT_SETTERS_H +#define SWIFT_SPHM1RT_RT_SETTERS_H + +/** + * @file src/rt/SPHM1RT/rt_setters.h + * @brief Independent setter functions for the SPHM1RT scheme + */ + +/** + * @brief Sets the comoving radiation energy per mass of a particle + * (note that the comoving and physical energy per mass are the same in our + * convention) + * + * @param p The particle of interest. + * @param urad The comoving radiation energy per mass + * + */ +__attribute__((always_inline)) INLINE static void +rt_set_comoving_urad_multifrequency(struct part* p, + const float urad[RT_NGROUPS]) { + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.conserved[g].urad = urad[g]; + } +} + +/** + * @brief Sets the physical radiation energy per mass of a particle + * (note that the comoving and physical energy per mass are the same in our + * convention) + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param urad The physical radiation energy per mass + */ +__attribute__((always_inline)) INLINE static void +rt_set_physical_urad_multifrequency(struct part* p, + const struct cosmology* cosmo, + const float urad[RT_NGROUPS]) { + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.conserved[g].urad = urad[g]; + } +} + +/** + * @brief Sets the comoving radiation flux per density of a particle + * (note that the comoving and physical flux per density are the same in our + * convention) + * + * @param p The particle of interest. + * @param frad The comoving radiation flux + */ +__attribute__((always_inline)) INLINE static void +rt_set_comoving_frad_multifrequency(struct part* p, float frad[RT_NGROUPS][3]) { + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.conserved[g].frad[0] = frad[g][0]; + p->rt_data.conserved[g].frad[1] = frad[g][1]; + p->rt_data.conserved[g].frad[2] = frad[g][2]; + } +} + +/** + * @brief Sets the physical radiation flux of a particle + * (note that the comoving and physical flux are the same in our convention) + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param frad The comoving radiation flux + */ +__attribute__((always_inline)) INLINE static void +rt_set_physical_radiation_flux_multifrequency(struct part* p, + const struct cosmology* cosmo, + float frad[RT_NGROUPS][3]) { + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.conserved[g].frad[0] = frad[g][0] * cosmo->a_inv; + p->rt_data.conserved[g].frad[1] = frad[g][1] * cosmo->a_inv; + p->rt_data.conserved[g].frad[2] = frad[g][2] * cosmo->a_inv; + } +} + +/** + * @brief Sets the physical opacity (chi) of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param chi The physical opacity + */ +__attribute__((always_inline)) INLINE static void +rt_set_physical_radiation_opacity(struct part* p, const struct cosmology* cosmo, + const float chi[RT_NGROUPS]) { + + /* avoid getting negative opacity */ + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.params.chi[g] = max(chi[g], 0.f) * cosmo->a_inv * cosmo->a_inv; + } +} + +#endif /* SWIFT_SPHM1RT_RT_SETTERS_H */ diff --git a/src/rt/SPHM1RT/rt_species_and_elements.h b/src/rt/SPHM1RT/rt_species_and_elements.h new file mode 100644 index 0000000000000000000000000000000000000000..428216774486539bbc943ca0d7686b106f323059 --- /dev/null +++ b/src/rt/SPHM1RT/rt_species_and_elements.h @@ -0,0 +1,75 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) + * + * 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_SPHM1RT_RT_SPECIES_AND_ELEMENTS_H +#define SWIFT_SPHM1RT_RT_SPECIES_AND_ELEMENTS_H + +#include "inline.h" + +/** + * @file src/rt/SPHM1RT/rt_species_and_elements.h + * @brief Main header file for species and elements definitions in SPHM1RT. + */ + +/** + * @brief The individual elements traced in the SPHM1RT model. + */ +enum rt_chemistry_element { + rt_chemistry_element_H = 0, + rt_chemistry_element_He, + rt_chemistry_element_count +}; + +/** + * @brief The individual species traced. + */ +enum rt_cooling_species { + rt_sp_elec = 0, /* 0 */ + rt_sp_HI, /* 1 */ + rt_sp_HII, /* 2 */ + rt_sp_HeI, /* 3 */ + rt_sp_HeII, /* 4 */ + rt_sp_HeIII, /* 5 */ + rt_species_count +}; + +/** + * @brief Return a string containing the name of a given #rt_cooling_species. + */ +__attribute__((always_inline)) INLINE static const char* rt_get_species_name( + enum rt_cooling_species spec) { + + static const char* rt_cooling_species_names[rt_species_count] = { + "e", "HI", "HII", "HeI", "HeII", "HeIII"}; + + return rt_cooling_species_names[spec]; +} + +/** + * @brief Return a string containing the name of a given #rt_chemistry_element. + */ +__attribute__((always_inline)) INLINE static const char* +rt_chemistry_get_element_name(enum rt_chemistry_element elem) { + + static const char* rt_chemistry_element_names[rt_chemistry_element_count] = { + "Hydrogen", "Helium"}; + + return rt_chemistry_element_names[elem]; +} + +#endif /* SWIFT_SPHM1RT_RT_SPECIES_AND_ELEMENTS_H */ diff --git a/src/rt/debug/rt_stellar_emission_rate.h b/src/rt/SPHM1RT/rt_stellar_emission_rate.h similarity index 69% rename from src/rt/debug/rt_stellar_emission_rate.h rename to src/rt/SPHM1RT/rt_stellar_emission_rate.h index 348ae6435ab02a307e75a281d5d8cbd29531acff..8b87ce1b57896d1cd60268af6ff40a9d76f1f4f0 100644 --- a/src/rt/debug/rt_stellar_emission_rate.h +++ b/src/rt/SPHM1RT/rt_stellar_emission_rate.h @@ -1,6 +1,8 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * Copyright (c) 2022 Tsang Keung Chan (chantsangkeung@gmail.com) + * 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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,12 +18,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_RT_STELLAR_EMISSION_RATE_DEBUG_H -#define SWIFT_RT_STELLAR_EMISSION_RATE_DEBUG_H +#ifndef SWIFT_RT_STELLAR_EMISSION_RATE_SPHM1RT_H +#define SWIFT_RT_STELLAR_EMISSION_RATE_SPHM1RT_H /** - * @file src/rt/debug/rt_stellar_emission_rate.h - * @brief Main header file for the debug radiative transfer scheme + * @file src/rt/SPHM1RT/rt_stellar_emission_rate.h + * @brief Main header file for the SPHM1RT closure radiative transfer scheme * stellar radiation emission rates related functions. */ @@ -42,7 +44,15 @@ __attribute__((always_inline)) INLINE static void rt_set_stellar_emission_rate( const struct rt_props* rt_props, const struct phys_const* phys_const, const struct unit_system* internal_units) { - sp->rt_data.debug_emission_rate_set += 1; + if (rt_props->use_const_emission_rates) { + const double dt = (age_end - age_beg); + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] += + rt_props->stellar_const_emission_rates[g] * dt; + } + } else { + error("Unknown stellar emission rate model"); + } } -#endif /* SWIFT_RT_STELLAR_EMISSION_RATE_DEBUG_H */ +#endif /* SWIFT_RT_STELLAR_EMISSION_RATE_SPHM1RT_H */ diff --git a/src/rt/SPHM1RT/rt_struct.h b/src/rt/SPHM1RT/rt_struct.h index b00ac3545873008b379f01e3398254272ff2df00..4a2ed3d811a288eeb4b5cfbe5b3499f2e4964d4e 100644 --- a/src/rt/SPHM1RT/rt_struct.h +++ b/src/rt/SPHM1RT/rt_struct.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) - * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 @@ -26,10 +26,102 @@ * SPHM1RT method described in Chan+21: 2102.08404 */ +#include "rt_species_and_elements.h" + /* Additional RT data in hydro particle struct */ -struct rt_part_data {}; +struct rt_part_data { + + /*! time step of the gas particle */ + float dt; + + /* conserved state vector in comoving units */ + /* but comoving and physical urad and frad are the same in our convention here + */ + /* urad: radiation energy per mass */ + /* frad: radiation flux per gas density */ + /* (they are conserved in the sense of energy/mass; assuming mass is equal) */ + struct { + float urad; + float frad[3]; + } conserved[RT_NGROUPS]; + + /* rate of change of the conserved state vector */ + struct { + float urad; + float frad[3]; + } dconserved_dt[RT_NGROUPS]; + + /* Store viscosity information in a separate struct. */ + struct { + + /*! Particle radiation flux divergence */ + float divf; + + /*! Particle radiation flux divergence from previous step */ + float divf_previous_step; + + /* parameter to control dissipation */ + float alpha; + + } viscosity[RT_NGROUPS]; + + /* Store artificial diffusion information in a separate struct. */ + struct { + + /*! gradient of radiation energy density per gas density */ + float graduradc[3]; + + /* parameter to control dissipation */ + float alpha; + + } diffusion[RT_NGROUPS]; + + /* Store radiation parameter in a separate struct. */ + struct { + + /*! initial mean opacity */ + float chi[RT_NGROUPS]; + + /*! reduced speed of light (physical) */ + float cred_phys; + + } params; + + /* Store hydro information in a separate struct. */ + struct { + + /*! "Grad h" term */ + float f; + + } force; + + struct { + + /*! Fraction of the particle mass in a given element */ + float metal_mass_fraction[rt_chemistry_element_count]; + + /*! Fraction of the particle mass in *all* metals */ + float metal_mass_fraction_total; + + /*! abundances of species i, i.e. n_i/nH */ + /* note that we use hydrogen density in the denominator */ + float abundances[rt_species_count]; + + } tchem; +}; /* Additional RT data in star particle struct */ -struct rt_spart_data {}; +struct rt_spart_data { + + /* Stellar energy emission that will be injected in to gas. + * Total energy, not density, not rate! */ + float emission_this_step[RT_NGROUPS]; + + /*! normalisation factor used for the radiation injection */ + float injection_weight; + + /*! radiation energy within injection radius */ + float emission_reinject[RT_NGROUPS]; +}; #endif /* SWIFT_RT_STRUCT_SPHM1RT_H */ diff --git a/src/rt/SPHM1RT/rt_unphysical.h b/src/rt/SPHM1RT/rt_unphysical.h new file mode 100644 index 0000000000000000000000000000000000000000..c843a93d9adaa77a5fe4cc394476e20085b20c5c --- /dev/null +++ b/src/rt/SPHM1RT/rt_unphysical.h @@ -0,0 +1,184 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Tsang Keung Chan (chantsangkeung@gmail.com) + * 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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_RT_UNPHYSICAL_SPHM1RT_H +#define SWIFT_RT_UNPHYSICAL_SPHM1RT_H + +/** + * @file src/rt/SPHM1RT/rt_unphysical.h + * @brief Routines for checking for and correcting unphysical scenarios + */ + +#include "rt_properties.h" + +/** + * @brief check for and correct if needed unphysical + * values for a radiation state. + * + * @param energy_density pointer to the radiation energy density + * @param flux pointer to radiation flux (3 dimensional) + * @param e_old energy density before change to check. Set = 0 if not available + * @param cred reduced speed of light in (comoving) code unit + */ +__attribute__((always_inline)) INLINE static void rt_check_unphysical_state( + float* energy_density, float flux[3], const float e_old, const float cred) { + + /* Check for negative energies */ + /* Note to self for printouts: Maximal allowable F = E * c. + * In some cases, e.g. while cooling, we don't modify the fluxes, + * so you can get an estimate of what the photon energy used to be + * by dividing the printed out fluxes by the speed of light in + * code units */ + /* this check will never trigger unless you manually remove the + * -ffinite-math-only flag for gcc */ + if (isinf(*energy_density) || isnan(*energy_density)) + error("Got inf/nan radiation energy case | %.6e | %.6e %.6e %.6e", + *energy_density, flux[0], flux[1], flux[2]); + + if (*energy_density <= 0.f) { + *energy_density = 0.f; + flux[0] = 0.f; + flux[1] = 0.f; + flux[2] = 0.f; + return; + } + + /* Check for too high fluxes */ + double fluxdouble[3]; + fluxdouble[0] = (double)(flux[0]); + fluxdouble[1] = (double)(flux[1]); + fluxdouble[2] = (double)(flux[2]); + const double flux2double = fluxdouble[0] * fluxdouble[0] + + fluxdouble[1] * fluxdouble[1] + + fluxdouble[2] * fluxdouble[2]; + if (isinf(flux2double) || isnan(flux2double)) + error("Got inf/nan in flux2 | %.6e| %.6e %.6e %.6e", flux2double, flux[0], + flux[1], flux[2]); + + const double flux_normdouble = (flux2double == 0.0) ? 0.0 : sqrt(flux2double); + const float flux_norm = (float)(flux_normdouble); + + const float flux_norm_inv = (flux_norm == 0.f) ? 0.f : 1.f / flux_norm; + const float flux_max = cred * *energy_density; + float flux_diff = flux_norm - flux_max; + + if (flux_norm != 0.f) { + if (flux_diff > 0.f) { + const float correct = flux_max * flux_norm_inv; + flux[0] *= correct; + flux[1] *= correct; + flux[2] *= correct; + } + } +} + +/** + * @brief check whether gas species abundances have physical + * values and correct small errors if necessary. + * + * @param p particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_check_unphysical_elem_spec( + struct part* restrict p, const struct rt_props* rt_props) { + + /*************************/ + /* check mass fraction */ + /*************************/ + + /* check individual mass fraction */ + char name[10]; + double test_mass_fraction; + for (int elem = 0; elem < rt_chemistry_element_count; elem++) { + test_mass_fraction = p->rt_data.tchem.metal_mass_fraction[elem]; + if (isinf(test_mass_fraction) || isnan(test_mass_fraction)) + error("Got inf/nan test_mass_fraction %d | with value %.6e ", elem, + test_mass_fraction); + if (test_mass_fraction < 0.f) { + sprintf(name, "%s", + rt_chemistry_get_element_name((enum rt_chemistry_element)elem)); + error("Error: Got negative mass fraction in %s", name); + } + } + + /* check total mass fraction */ + float mass_fraction_tot = 0.f; + for (int elem = 0; elem < rt_chemistry_element_count; elem++) { + mass_fraction_tot += p->rt_data.tchem.metal_mass_fraction[elem]; + } + /* Make sure we sum up to 1. */ + if (fabsf(mass_fraction_tot - 1.f) > 1e-3) + error("Got total mass fraction = %.6g", mass_fraction_tot); + + /*********************/ + /* check abundaces */ + /*********************/ + + double test_abundance; + for (int spec = 0; spec < rt_species_count; spec++) { + test_abundance = p->rt_data.tchem.abundances[spec]; + if (isinf(test_abundance) || isnan(test_abundance)) + error("Got inf/nan test_abundance %d | with value %.6e ", spec, + test_abundance); + if (test_abundance < 0.f) { + if (test_abundance < -1e4) { + sprintf(name, "%s", rt_get_species_name((enum rt_cooling_species)spec)); + message("WARNING: Got negative abundance in %s", name); + } + p->rt_data.tchem.abundances[spec] = 0.f; + } + } + + /* normalized total abundances summed over many species */ + float abundance_tot_nor = 0.f; + /* first check Hydrogen */ + abundance_tot_nor = p->rt_data.tchem.abundances[rt_sp_HI] + + p->rt_data.tchem.abundances[rt_sp_HII]; + /* Make sure we sum up to 1. */ + if (fabsf(abundance_tot_nor - 1.f) > 1e-3) + error("Got total abundances of hydrogen gas = %.6g", abundance_tot_nor); + + /* second check Helium */ + abundance_tot_nor = p->rt_data.tchem.abundances[rt_sp_HeI] + + p->rt_data.tchem.abundances[rt_sp_HeII] + + p->rt_data.tchem.abundances[rt_sp_HeIII]; + /* Helium is more tricky. We need to check AHe=nHe/nH. */ + /* expected total abundance = MHe/MH / am_He * am_H */ + float abundance_tot_exp; + abundance_tot_exp = + p->rt_data.tchem.metal_mass_fraction[rt_chemistry_element_He] / + p->rt_data.tchem.metal_mass_fraction[rt_chemistry_element_H] * + rt_props->atomicmass[rt_chemistry_element_H] * + rt_props->atomicmass_inv[rt_chemistry_element_He]; + /* Make sure we sum up to expected. */ + if (fabsf(abundance_tot_nor - abundance_tot_exp) > 1e-4) + error("Got total abundances of helium gas = %.6g, expect = %.6g", + abundance_tot_nor, abundance_tot_exp); + + /* third check electron density */ + abundance_tot_exp = p->rt_data.tchem.abundances[rt_sp_HII] + + p->rt_data.tchem.abundances[rt_sp_HeII] + + 2.0f * p->rt_data.tchem.abundances[rt_sp_HeIII]; + /* Make sure we sum up to the expected. */ + if (fabsf(p->rt_data.tchem.abundances[rt_sp_elec] - abundance_tot_exp) > + 1e-3 * abundance_tot_exp) + error("Got total abundances of electron = %.6g; expected = %.6g", + p->rt_data.tchem.abundances[rt_sp_elec], abundance_tot_exp); +} + +#endif /* SWIFT_RT_UNPHYSICAL_SPHM1RT_H */ diff --git a/src/rt/debug/rt.h b/src/rt/debug/rt.h index da2a952429c1bc5e0cdb8307078a8f8433ea8dc2..3742ec1129dc348dfe27160e28c06e3d84236ebf 100644 --- a/src/rt/debug/rt.h +++ b/src/rt/debug/rt.h @@ -20,80 +20,99 @@ #define SWIFT_RT_DEBUG_H #include "rt_debugging.h" -#include "rt_stellar_emission_rate.h" -#include "rt_thermochemistry.h" /** * @file src/rt/debug/rt.h * @brief Main header file for the debug radiative transfer scheme. */ +/** + * @brief Compute the photon emission rates for this stellar particle. + * This function is called every time the spart is being reset + * (during start-up and during stars ghost if spart is active) + * and assumes that the photon emission rate is an intrinsic + * stellar property, i.e. doesn't depend on the environment. + * + * @param sp star particle to work on + * @param time current system time + * @param star_age age of the star *at the end of the step* + * @param dt star time step + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param internal_units struct holding internal units + */ +__attribute__((always_inline)) INLINE static void +rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, + double star_age, double dt, + const struct rt_props* rt_props, + const struct phys_const* phys_const, + const struct unit_system* internal_units) { + + sp->rt_data.debug_emission_rate_set += 1; + + /* rt_set_stellar_emission_rate(sp, star_age_begin_of_step, star_age, + * rt_props, phys_const, internal_units); */ +} + /** * @brief Initialisation of the RT density loop related particle data. * Note: during initalisation (space_init), rt_reset_part and rt_init_part * are both called individually. + * + * @param p Particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_part( struct part* restrict p) {} /** - * @brief Reset of the RT hydro particle data not related to the density. + * @brief Reset the RT hydro particle data not related to the hydro density. * Note: during initalisation (space_init), rt_reset_part and rt_init_part - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_part is made in space_convert_rt_quantities() after - * the zeroth time step is finished. + * are both called individually. To reset RT data needed in each RT sub-cycle, + * use rt_reset_part_each_subcycle(). * * @param p the particle to work on + * @param cosmo Cosmology. */ __attribute__((always_inline)) INLINE static void rt_reset_part( - struct part* restrict p) { + struct part* restrict p, const struct cosmology* cosmo) { /* reset this here as well as in the rt_debugging_checks_end_of_step() * routine to test task dependencies are done right */ p->rt_data.debug_iact_stars_inject = 0; - p->rt_data.debug_iact_stars_inject_prep = 0; + p->rt_data.debug_nsubcycles = 0; + p->rt_data.debug_kicked = 0; +} - p->rt_data.debug_injection_check = 0; - p->rt_data.debug_calls_iact_gradient_interaction = 0; - p->rt_data.debug_calls_iact_transport_interaction = 0; +/** + * @brief Reset RT particle data which needs to be reset each sub-cycle. + * + * @param p the particle to work on + * @param cosmo Cosmology. + * @param dt the current particle RT time step + */ +__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle( + struct part* restrict p, const struct cosmology* cosmo, double dt) { - p->rt_data.debug_injection_done = 0; - p->rt_data.debug_gradients_done = 0; - p->rt_data.debug_transport_done = 0; - p->rt_data.debug_thermochem_done = 0; + rt_debugging_reset_each_subcycle(p); } /** * @brief First initialisation of the RT hydro particle data. * * @param p particle to work on + * @param cosmo #cosmology data structure. + * @param rt_props RT properties struct */ __attribute__((always_inline)) INLINE static void rt_first_init_part( - struct part* restrict p) { + struct part* restrict p, const struct cosmology* cosmo, + const struct rt_props* restrict rt_props) { rt_init_part(p); - rt_reset_part(p); + rt_reset_part(p, cosmo); + rt_reset_part_each_subcycle(p, cosmo, 0.); p->rt_data.debug_radiation_absorbed_tot = 0ULL; - p->rt_data.debug_iact_stars_inject_prep_tot = 0ULL; } -/** - * @brief Initialises particle quantities that can't be set - * otherwise before the zeroth step is finished. E.g. because - * they require the particle density to be known. - * - * @param p particle to work on - * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param us unit_system struct - * @param cosmo cosmology struct - */ -__attribute__((always_inline)) INLINE static void -rt_init_part_after_zeroth_step(struct part* restrict p, - const struct rt_props* rt_props, - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo) {} /** * @brief Initialisation of the RT density loop related star particle data. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart @@ -102,30 +121,24 @@ rt_init_part_after_zeroth_step(struct part* restrict p, * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_spart( - struct spart* restrict sp) {} + struct spart* restrict sp) { + + /* reset this here as well as in the rt_debugging_checks_end_of_step() + * routine to test task dependencies are done right */ + sp->rt_data.debug_iact_hydro_inject_prep = 0; + sp->rt_data.debug_iact_hydro_inject = 0; + sp->rt_data.debug_emission_rate_set = 0; +} /** * @brief Reset of the RT star particle data not related to the density. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_spart is made in space_convert_rt_quantities() after - * the zeroth time step is finished. + * are both called individually. * * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_reset_spart( - struct spart* restrict sp) { - - /* reset everything */ - - /* reset this here as well as in the rt_debugging_checks_end_of_step() - * routine to test task dependencies are done right */ - sp->rt_data.debug_iact_hydro_inject = 0; - sp->rt_data.debug_iact_hydro_inject_prep = 0; - - sp->rt_data.debug_emission_rate_set = 0; - sp->rt_data.debug_injection_check = 0; -} + struct spart* restrict sp) {} /** * @brief First initialisation of the RT star particle data. @@ -147,7 +160,9 @@ __attribute__((always_inline)) INLINE static void rt_first_init_spart( * @param n The number of pieces to split into. */ __attribute__((always_inline)) INLINE static void rt_split_part(struct part* p, - double n) {} + double n) { + error("RT can't run with split particles for now."); +} /** * @brief Exception handle a hydro part not having any neighbours in ghost task @@ -162,7 +177,7 @@ __attribute__((always_inline)) INLINE static void rt_part_has_no_neighbours( /** * @brief Exception handle a star part not having any neighbours in ghost task * - * @param p The #part. + * @param sp The #spart. */ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( struct spart* sp){}; @@ -172,21 +187,36 @@ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( * * @param p The particle to work on * @param rtp The RT properties struct + * @param hydro_props The hydro properties struct + * @param phys_const physical constants struct + * @param us unit_system struct + * @param cosmo cosmology struct */ __attribute__((always_inline)) INLINE static void rt_convert_quantities( - struct part* p, const struct rt_props* rtp){}; + struct part* restrict p, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo) {} /** * @brief Computes the next radiative transfer time step size * of a given particle (during timestep tasks) * - * @param p particle to work on - * @param rt_props the RT properties struct - * @param cosmo the cosmology + * @param p Particle to work on. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. */ __attribute__((always_inline)) INLINE static float rt_compute_timestep( - const struct part* restrict p, const struct rt_props* restrict rt_props, - const struct cosmology* restrict cosmo) { + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us) { return FLT_MAX; } @@ -208,7 +238,10 @@ __attribute__((always_inline)) INLINE static float rt_compute_spart_timestep( /** * @brief Compute the time-step length for an RT step of a particle from given - * integer times ti_beg and ti_end + * integer times ti_beg and ti_end. This time-step length is then used to + * compute the actual time integration of the transport/force step and the + * thermochemistry. This is not used to determine the time-step length during + * the time-step tasks. * * @param ti_beg Start of the time-step (on the integer time-line). * @param ti_end End of the time-step (on the integer time-line). @@ -231,74 +264,30 @@ __attribute__((always_inline)) INLINE static double rt_part_dt( * @param p particle to work on * @param props struct #rt_props that contains global RT properties */ -__attribute__((always_inline)) INLINE static void -rt_injection_update_photon_density(struct part* restrict p, - struct rt_props* props) { - - if (props->debug_do_all_parts_have_stars_checks && - p->rt_data.debug_injection_check != 1) - error("called ghost1 when injection check count is %d; ID=%lld", - p->rt_data.debug_injection_check, p->id); - p->rt_data.debug_injection_done += 1; -} - -/** - * @brief Compute the photon emission rates for this stellar particle - * This function is called every time the spart is being reset - * (during start-up and during stars ghost if spart is active) - * and assumes that the photon emission rate is an intrinsic - * stellar property, i.e. doesn't depend on the environment. - * - * @param sp star particle to work on - * @param time current system time - * @param star_age age of the star *at the end of the step* - * @param dt star time step - * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param internal_units struct holding internal units - */ -__attribute__((always_inline)) INLINE static void -rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, - double star_age, double dt, - const struct rt_props* rt_props, - const struct phys_const* phys_const, - const struct unit_system* internal_units) { +__attribute__((always_inline)) INLINE static void rt_finalise_injection( + struct part* restrict p, struct rt_props* props) { - /* Skip initial fake time-step */ - if (dt == 0.0l) return; + rt_debug_sequence_check(p, 1, "rt_ghost1/rt_finalise_injection"); - if (time == 0.l) { - /* if function is called before the first actual step, time is still - * at zero unless specified otherwise in parameter file.*/ - star_age = dt; - } - - /* now get the emission rates */ - double star_age_begin_of_step = star_age - dt; - star_age_begin_of_step = max(0.l, star_age_begin_of_step); - rt_set_stellar_emission_rate(sp, star_age_begin_of_step, star_age, rt_props, - phys_const, internal_units); + p->rt_data.debug_injection_done += 1; } /** * @brief finishes up the gradient computation * * @param p particle to work on + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_end_gradient( - struct part* restrict p) { + struct part* restrict p, const struct cosmology* cosmo) { - if (p->rt_data.debug_injection_done != 1) - error( - "Called finalise gradient on particle " - "where injection count = %d", - p->rt_data.debug_injection_done); + rt_debug_sequence_check(p, 2, __func__); if (p->rt_data.debug_calls_iact_gradient_interaction == 0) message( - "WARNING: Called finalise gradient on particle " + "WARNING: Called finalise gradient on particle %lld" "with iact gradient count from rt_iact = %d", - p->rt_data.debug_calls_iact_gradient_interaction); + p->id, p->rt_data.debug_calls_iact_gradient_interaction); p->rt_data.debug_gradients_done += 1; } @@ -308,27 +297,19 @@ __attribute__((always_inline)) INLINE static void rt_end_gradient( * * @param p particle to work on * @param dt the current time step of the particle + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_finalise_transport( - struct part* restrict p, const double dt) { - - if (p->rt_data.debug_injection_done != 1) - error( - "Trying to do finalise_transport when " - "injection count is %d", - p->rt_data.debug_injection_done); + struct part* restrict p, const double dt, + const struct cosmology* restrict cosmo) { - if (p->rt_data.debug_gradients_done != 1) - error( - "Trying to do finalise_transport when " - "rt_finalise_gradient count is %d", - p->rt_data.debug_gradients_done); + rt_debug_sequence_check(p, 3, __func__); if (p->rt_data.debug_calls_iact_transport_interaction == 0) message( - "WARNING: Called finalise transport on particle " + "WARNING: Called finalise transport on particle %lld" "with iact transport count from rt_iact = %d", - p->rt_data.debug_calls_iact_transport_interaction); + p->id, p->rt_data.debug_calls_iact_transport_interaction); p->rt_data.debug_transport_done += 1; } @@ -354,11 +335,15 @@ __attribute__((always_inline)) INLINE static void rt_tchem( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const double dt) { - rt_do_thermochemistry(p); + rt_debug_sequence_check(p, 4, __func__); + p->rt_data.debug_thermochem_done += 1; + + /* rt_do_thermochemistry(p); */ } /** - * @brief Extra operations done during the kick. + * @brief Extra operations done during the kick. This needs to be + * done before the particle mass is updated in the hydro_kick_extra. * * @param p Particle to act upon. * @param dt_therm Thermal energy time-step @f$\frac{dt}{a^2}@f$. @@ -372,12 +357,21 @@ __attribute__((always_inline)) INLINE static void rt_tchem( __attribute__((always_inline)) INLINE static void rt_kick_extra( struct part* p, float dt_therm, float dt_grav, float dt_hydro, float dt_kick_corr, const struct cosmology* cosmo, - const struct hydro_props* hydro_props) {} + const struct hydro_props* hydro_props) { + + /* Don't account for timestep_sync backward kicks */ + if (dt_therm >= 0.f && dt_grav >= 0.f && dt_hydro >= 0.f && + dt_kick_corr >= 0.f) { + + rt_debug_sequence_check(p, 0, __func__); + p->rt_data.debug_kicked += 1; + } +} /** * @brief Prepare a particle for the !HYDRO! force calculation. * E.g. for the meshless schemes, we need to take into account the - * mass fluxes of the ionizing species between particles. + * mass fluxes of the constituent species between particles. * NOTE: don't call this during rt_init_part or rt_reset_part, * follow the hydro_prepare_force logic. * @@ -386,12 +380,23 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra( __attribute__((always_inline)) INLINE static void rt_prepare_force( struct part* p) {} +/** + * @brief Extra operations to be done during the drift + * + * @param p Particle to act upon. + * @param xp The extended particle data to act upon. + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void rt_predict_extra( + struct part* p, struct xpart* xp, float dt_drift) {} + /** * @brief Clean the allocated memory inside the RT properties struct. * * @param props the #rt_props. + * @param restart did we restart? */ __attribute__((always_inline)) INLINE static void rt_clean( - struct rt_props* props) {} + struct rt_props* props, int restart) {} #endif /* SWIFT_RT_DEBUG_H */ diff --git a/src/rt/debug/rt_additions.h b/src/rt/debug/rt_additions.h index f0d649e6db2e7bd68aad2bf16d1fc719904b7da5..93990804650939f937b467b59906e5945e634597 100644 --- a/src/rt/debug/rt_additions.h +++ b/src/rt/debug/rt_additions.h @@ -33,7 +33,7 @@ * @param pj second interacting particle * @param mass_flux the mass flux between these two particles * @param mode 0: non-symmetric interaction, update i only. 1: symmetric - *interaction. + * interaction. **/ __attribute__((always_inline)) INLINE static void rt_part_update_mass_fluxes( struct part* restrict pi, struct part* restrict pj, float mass_flux, diff --git a/src/rt/debug/rt_debugging.h b/src/rt/debug/rt_debugging.h index bf8e7d1f71a1d6b8f978f0535320b37962c1d0a8..32816e498e34b4e8c54b6c69e4a75bf89ca24eb3 100644 --- a/src/rt/debug/rt_debugging.h +++ b/src/rt/debug/rt_debugging.h @@ -19,7 +19,9 @@ #ifndef SWIFT_RT_DEBUGGING_DEBUG_H #define SWIFT_RT_DEBUGGING_DEBUG_H +#include "active.h" #include "rt_properties.h" +#include "timeline.h" /** * @file src/rt/debug/rt_debugging.h @@ -27,6 +29,122 @@ * extra debugging functions. */ +/** + * @brief Check whether RT time step size is valid compared to the hydro one. + * Here I abuse this function to set the number of RT sub-cycles to be exactly + * max_nr_rt_subcycles always, since the 'debug' RT scheme has no other + * meaningful way of defining the number of RT sub-cycles. + * + * @param p particle to work on + * @param dti_rt current RT integer time step + * @param dti_hydro current hydro integer time step + * @param max_nr_rt_subcycles max number of RT sub-cycles. + * @param time_base minimal time step in this run + */ +__attribute__((always_inline)) INLINE static void rt_debugging_check_timestep( + const struct part *restrict p, integertime_t *dti_rt, + const integertime_t *dti_hydro, int max_nr_rt_subcycles, double time_base) { + + const integertime_t f = max(max_nr_rt_subcycles, 1); + *dti_rt = *dti_hydro / f; + + if (*dti_rt * f != *dti_hydro) error("Caught a live one"); +} + +/** + * @brief This resets particle carried quantities after each subcycling + * step such that the internal checks are still consistent. + * @param p the particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_debugging_count_subcycle( + struct part *restrict p) { + p->rt_data.debug_nsubcycles += 1; +} + +/** + * @brief Check that the particle completed the correct number of subcycles. + * This is checked in every rt_reset_part, before the subcycling count is reset. + * @param p the particle to work on + * @param rt_props RT properties struct + */ +__attribute__((always_inline)) INLINE static void +rt_debugging_check_nr_subcycles(struct part *restrict p, + const struct rt_props *rt_props) { + + /* TODO: this check may fail when running with limiter/sync. */ + + /* NOTE: we need to do this check somewhere in the hydro tasks. + * (1) it needs to be done when all tasks are active and before the + * particle hydro time step is changed. + * (2) If you do it during RT tasks, it won't properly check how + * many sub-cycles you did during a single hydro task. + * (3) You can't do it during the timestep task, since between + * the hydro and the timestep we already do an RT step. */ + + /* skip initialization */ + if (p->time_bin == 0) return; + if (p->rt_time_data.time_bin == 0) + error("Got part %lld with RT time bin 0", p->id); + + timebin_t bindiff = p->time_bin - p->rt_time_data.time_bin; + + if (rt_props->debug_max_nr_subcycles <= 1) { + /* Running without subcycling. */ + if (bindiff != 0) + error("Running without subcycling but got bindiff=%d for part=%lld", + bindiff, p->id); + if (p->rt_data.debug_nsubcycles != 1) + error("Running without subcycling but got part=%lld subcycle count=%d", + p->id, p->rt_data.debug_nsubcycles); + return; + } + + /* This assumes that max_nr_subcycles is not an upper limit, + * but a fixed number of sub-cycles. */ + timebin_t bindiff_expect = 0; + + while (!(rt_props->debug_max_nr_subcycles & (1 << bindiff_expect)) && + bindiff_expect != num_time_bins) + ++bindiff_expect; + + if (bindiff_expect == num_time_bins) + error( + "Couldn't determine expected time bin difference. Max nr subcycles %d " + "bindiff %d", + rt_props->debug_max_nr_subcycles, bindiff); + + if (bindiff != bindiff_expect) + error("Particle %lld Got bindiff=%d expect=%d; timebins=%d rt=%d", p->id, + bindiff, bindiff_expect, p->time_bin, p->rt_time_data.time_bin); + + int subcycles_expect = (1 << bindiff); + if (p->rt_data.debug_nsubcycles != subcycles_expect) + + if (p->rt_data.debug_nsubcycles != rt_props->debug_max_nr_subcycles) + error( + "Particle %lld didn't do the expected amount of subcycles: Expected " + "%d, done %d; time bins %d RT: %d", + p->id, subcycles_expect, p->rt_data.debug_nsubcycles, p->time_bin, + p->rt_time_data.time_bin); +} + +/** + * @brief This resets particle carried quantities after each subcycling + * step such that the internal checks are still consistent. + * @param p the particle to work on + */ +__attribute__((always_inline)) INLINE static void +rt_debugging_reset_each_subcycle(struct part *restrict p) { + + p->rt_data.debug_calls_iact_gradient_interaction = 0; + p->rt_data.debug_calls_iact_transport_interaction = 0; + + p->rt_data.debug_injection_done = 0; + p->rt_data.debug_gradients_done = 0; + p->rt_data.debug_transport_done = 0; + p->rt_data.debug_thermochem_done = 0; +} + /** * @brief Debugging checks loop over all star particles after each time step */ @@ -35,33 +153,24 @@ static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data, void *restrict extra_data) { struct spart *restrict sparts = (struct spart *)map_data; - const struct engine *restrict e = (struct engine *)extra_data; + const struct engine *e = (struct engine *)extra_data; - int emission_sum = 0; - int iacts_with_parts_sum = 0; + unsigned long long emission_sum_this_step = 0ULL; unsigned long long emission_sum_tot = 0ULL; - unsigned long long iacts_with_parts_sum_tot = 0ULL; for (int k = 0; k < scount; k++) { struct spart *restrict sp = &sparts[k]; - emission_sum += sp->rt_data.debug_iact_hydro_inject; + emission_sum_this_step += sp->rt_data.debug_iact_hydro_inject; emission_sum_tot += sp->rt_data.debug_radiation_emitted_tot; /* Reset all values here in case stars won't be active next step */ sp->rt_data.debug_iact_hydro_inject = 0; - - iacts_with_parts_sum += sp->rt_data.debug_iact_hydro_inject_prep; - iacts_with_parts_sum_tot += sp->rt_data.debug_iact_hydro_inject_prep_tot; - /* Reset all values here in case stars won't be active next step */ sp->rt_data.debug_iact_hydro_inject_prep = 0; } - atomic_add(&e->rt_props->debug_radiation_emitted_this_step, emission_sum); + atomic_add(&e->rt_props->debug_radiation_emitted_this_step, + emission_sum_this_step); atomic_add(&e->rt_props->debug_radiation_emitted_tot, emission_sum_tot); - atomic_add(&e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step, - iacts_with_parts_sum); - atomic_add(&e->rt_props->debug_star_injection_prep_iacts_with_parts_tot, - iacts_with_parts_sum_tot); } /** @@ -72,33 +181,24 @@ static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data, void *restrict extra_data) { struct part *restrict parts = (struct part *)map_data; - const struct engine *restrict e = (struct engine *)extra_data; + const struct engine *e = (struct engine *)extra_data; - int absorption_sum = 0; - int iacts_with_stars_sum = 0; + unsigned long long absorption_sum_this_step = 0ULL; unsigned long long absorption_sum_tot = 0ULL; - unsigned long long iacts_with_stars_sum_tot = 0ULL; for (int k = 0; k < count; k++) { struct part *restrict p = &parts[k]; - absorption_sum += p->rt_data.debug_iact_stars_inject; + absorption_sum_this_step += p->rt_data.debug_iact_stars_inject; absorption_sum_tot += p->rt_data.debug_radiation_absorbed_tot; - /* Reset all values here in case particles won't be active next step */ - p->rt_data.debug_iact_stars_inject = 0; - iacts_with_stars_sum += p->rt_data.debug_iact_stars_inject_prep; - iacts_with_stars_sum_tot += p->rt_data.debug_iact_stars_inject_prep_tot; /* Reset all values here in case particles won't be active next step */ - p->rt_data.debug_iact_stars_inject_prep = 0; + p->rt_data.debug_iact_stars_inject = 0; } - atomic_add(&e->rt_props->debug_radiation_absorbed_this_step, absorption_sum); + atomic_add(&e->rt_props->debug_radiation_absorbed_this_step, + absorption_sum_this_step); atomic_add(&e->rt_props->debug_radiation_absorbed_tot, absorption_sum_tot); - atomic_add(&e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step, - iacts_with_stars_sum); - atomic_add(&e->rt_props->debug_part_injection_prep_iacts_with_stars_tot, - iacts_with_stars_sum_tot); } /** @@ -106,7 +206,7 @@ static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data, * particles and do whatever checks for this particular time step you * want done. * - * @param s The #space. + * @param e The #engine. * @param verbose Are we talkative? */ __attribute__((always_inline)) INLINE static void @@ -117,17 +217,13 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) { const ticks tic = getticks(); - /* reset values before the particle loops */ - e->rt_props->debug_radiation_emitted_this_step = 0; - e->rt_props->debug_radiation_absorbed_this_step = 0; - e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step = 0; - e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step = 0; - /* reset total counts as well. We track the totals since the beginning + /* reset values before the particle loops. + * reset total counts as well. We track the totals since the beginning * of time in particles individually. */ - e->rt_props->debug_radiation_emitted_tot = 0LL; - e->rt_props->debug_radiation_absorbed_tot = 0LL; - e->rt_props->debug_star_injection_prep_iacts_with_parts_tot = 0LL; - e->rt_props->debug_part_injection_prep_iacts_with_stars_tot = 0LL; + e->rt_props->debug_radiation_emitted_this_step = 0ULL; + e->rt_props->debug_radiation_absorbed_this_step = 0ULL; + e->rt_props->debug_radiation_emitted_tot = 0ULL; + e->rt_props->debug_radiation_absorbed_tot = 0ULL; /* hydro particle loop */ if (s->nr_parts > 0) @@ -141,97 +237,104 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) { s->sparts, s->nr_sparts, sizeof(struct spart), threadpool_auto_chunk_size, /*extra_data=*/e); - /* message("This step: %12d %12d %12d %12d", */ - /* e->rt_props->debug_radiation_emitted_this_step, */ - /* e->rt_props->debug_radiation_absorbed_this_step, */ - /* e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step, - */ - /* e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step - */ - /* ); */ - /* message("Over lifetime: %12lld %12lld %12lld %12lld", */ - /* e->rt_props->debug_radiation_emitted_tot, */ - /* e->rt_props->debug_radiation_absorbed_tot, */ - /* e->rt_props->debug_star_injection_prep_iacts_with_parts_tot, */ - /* e->rt_props->debug_part_injection_prep_iacts_with_stars_tot */ - /* ); */ +#ifdef WITH_MPI + /* Since we aren't sending data back, none of these checks will + * pass a run over MPI. Make sure you run the threadpool functions + * first though so certain variables can get reset properly. */ + return; +#endif /* Have we accidentally invented or deleted some radiation somewhere? */ + if ((e->rt_props->debug_radiation_emitted_this_step != e->rt_props->debug_radiation_absorbed_this_step) || (e->rt_props->debug_radiation_emitted_tot != e->rt_props->debug_radiation_absorbed_tot)) error( "Emitted and absorbed radiation vary.\n" - " This step: star emission %12d; gas absorption %12d\n" + " This step: star emission %12lld; gas absorption %12lld\n" "Since start: star emission %12lld; gas absorption %12lld", e->rt_props->debug_radiation_emitted_this_step, e->rt_props->debug_radiation_absorbed_this_step, e->rt_props->debug_radiation_emitted_tot, e->rt_props->debug_radiation_absorbed_tot); - if ((e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step != - e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step) || - (e->rt_props->debug_part_injection_prep_iacts_with_stars_tot != - e->rt_props->debug_star_injection_prep_iacts_with_parts_tot)) - error( - "Injection prep counts parts vs stars disagree.\n" - " This step: star iacts: %12d; gas iacts: %12d\n" - "Since start: star iacts: %12lld; gas iacts: %12lld", - e->rt_props->debug_star_injection_prep_iacts_with_parts_this_step, - e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step, - e->rt_props->debug_star_injection_prep_iacts_with_parts_tot, - e->rt_props->debug_part_injection_prep_iacts_with_stars_tot); - - if ((e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step != - e->rt_props->debug_radiation_emitted_this_step) || - (e->rt_props->debug_part_injection_prep_iacts_with_stars_tot != - e->rt_props->debug_radiation_emitted_tot)) - error( - "Injection prep iact counts vs actual iact counts disagree.\n" - " This step: prep iacts: %12d; inject iacts: %12d\n" - "Since start: prep iacts: %12lld; inject iacts: %12lld", - e->rt_props->debug_part_injection_prep_iacts_with_stars_this_step, - e->rt_props->debug_radiation_emitted_this_step, - e->rt_props->debug_part_injection_prep_iacts_with_stars_tot, - e->rt_props->debug_radiation_emitted_tot); - if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); } /** - * @brief This function is intended for debugging purposes only. It is called - * during the self injection tasks, (regardless whether the particle actually - * has neighbours to interact with) and intended to mark star or gas particles - * to have been called during the step so further checks can be performed - * further down the task system. + * @brief Perform a series of consistency and sanity checks. + * + * @param p particle to check + * @param loc location where this is called from. This determines which checks + * will be done: + * + * 0: during kicks/after drifts. + * 1: during rt_ghost1/finalise_injection / after kicks. + * 2: during gradients / after injection. + * 3: during transport / after gradients. + * 4: during thermochem / after transport. + * 5: after thermochem. * - * @param p Hydro particle. + * @param function_name: Function name (or message) you want printed on error. */ -__attribute__((always_inline)) INLINE static void -rt_debugging_check_injection_part(struct part *restrict p, - struct rt_props *props) { +__attribute__((always_inline)) INLINE static void rt_debug_sequence_check( + struct part *restrict p, int loc, const char *function_name) { - if (props->debug_do_all_parts_have_stars_checks) - p->rt_data.debug_injection_check += 1; -} + /* Note: Checking whether a particle has been drifted at all is not + * compatible with subcycling. There is no reliable point where to + * reset the counters and have sensible results. */ -/** - * @brief This function is intended for debugging purposes only. It is called - * during the self injection tasks, (regardless whether the particle actually - * has neighbours to interact with) and intended to mark star or gas particles - * to have been called during the step so further checks can be performed - * further down the task system. - * - * @param s Star particle. - */ -__attribute__((always_inline)) INLINE static void -rt_debugging_check_injection_spart(struct spart *restrict s, - struct rt_props *props) { + if (loc > 0) { + /* Are kicks done? */ + if (p->rt_data.debug_nsubcycles == 0) { + if (p->rt_data.debug_kicked != 1) + error( + "called %s on particle %lld with wrong kick count=%d (expected " + "1) cycle=%d | rt %d hydro%d", + function_name, p->id, p->rt_data.debug_kicked, + p->rt_data.debug_nsubcycles, p->rt_time_data.time_bin, p->time_bin); + } else if (p->rt_data.debug_nsubcycles > 0) { + if (p->rt_data.debug_kicked != 2) + error( + "called %s on particle %lld with wrong kick count=%d (expected 2) " + "cycle=%d", + function_name, p->id, p->rt_data.debug_kicked, + p->rt_data.debug_nsubcycles); + } else { + error("Got negative subcycle???"); + } + } - if (props->debug_do_all_parts_have_stars_checks) - s->rt_data.debug_injection_check += 1; + if (loc > 1) { + /* is injection done? */ + if (p->rt_data.debug_injection_done != 1) + error("called %s on part %lld when finalise injection count is %d ID", + function_name, p->id, p->rt_data.debug_injection_done); + } + + if (loc > 2) { + /* are gradients done? */ + if (p->rt_data.debug_gradients_done != 1) + error("called %s on part %lld when gradients_done count is %d", + function_name, p->id, p->rt_data.debug_gradients_done); + } + + if (loc > 3) { + /* is transport done? */ + if (p->rt_data.debug_transport_done != 1) + error("called %s on part %lld when transport_done != 1: %d", + function_name, p->id, p->rt_data.debug_transport_done); + } + + if (loc > 4) { + /* is thermochemistry done? */ + if (p->rt_data.debug_thermochem_done != 1) + error("called %s on part %lld with thermochem_done count=%d", + function_name, p->id, p->rt_data.debug_thermochem_done); + } } + #endif /* SWIFT_RT_DEBUGGING_DEBUG_H */ diff --git a/src/rt/debug/rt_gradients.h b/src/rt/debug/rt_gradients.h index cc5c7844e9f0034272566fdc65c9e042269314a1..3d2dfc8b0cd238e100872df5d1827e928cd3034c 100644 --- a/src/rt/debug/rt_gradients.h +++ b/src/rt/debug/rt_gradients.h @@ -19,6 +19,8 @@ #ifndef SWIFT_RT_GRADIENTS_DEBUG_H #define SWIFT_RT_GRADIENTS_DEBUG_H +#include "rt_debugging.h" + /** * @file src/rt/debug/rt_gradients.h * @brief Main header file for the debug radiative transfer scheme gradients @@ -35,23 +37,13 @@ * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void rt_gradients_collect( - float r2, const float *dx, float hi, float hj, struct part *restrict pi, + float r2, const float dx[3], float hi, float hj, struct part *restrict pi, struct part *restrict pj) { - if (pi->rt_data.debug_injection_done != 1) - error( - "Trying to do symmetric iact gradient when finalise injection count is " - "%d ID %lld", - pi->rt_data.debug_injection_done, pi->id); - - if (pj->rt_data.debug_injection_done != 1) - error( - "Trying to do symmetric iact gradient when finalise injection count is " - "%d ID %lld", - pj->rt_data.debug_injection_done, pj->id); + rt_debug_sequence_check(pi, 2, __func__); + rt_debug_sequence_check(pj, 2, __func__); pi->rt_data.debug_calls_iact_gradient_interaction += 1; - pj->rt_data.debug_calls_iact_gradient_interaction += 1; } @@ -66,15 +58,10 @@ __attribute__((always_inline)) INLINE static void rt_gradients_collect( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void rt_gradients_nonsym_collect( - float r2, const float *dx, float hi, float hj, struct part *restrict pi, + float r2, const float dx[3], float hi, float hj, struct part *restrict pi, struct part *restrict pj) { - if (pi->rt_data.debug_injection_done != 1) - error( - "Trying to do nonsym iact gradients when finalise injection count is " - "%d ID %lld", - pi->rt_data.debug_injection_done, pi->id); - + rt_debug_sequence_check(pi, 2, __func__); pi->rt_data.debug_calls_iact_gradient_interaction += 1; } diff --git a/src/rt/debug/rt_iact.h b/src/rt/debug/rt_iact.h index 61dd79ce94ad7bcc03e818b07eebd5882871d827..2eb590dd43281affb0c3aece562a7d5f71948376 100644 --- a/src/rt/debug/rt_iact.h +++ b/src/rt/debug/rt_iact.h @@ -19,6 +19,7 @@ #ifndef SWIFT_RT_IACT_DEBUG_H #define SWIFT_RT_IACT_DEBUG_H +#include "rt_debugging.h" #include "rt_gradients.h" /** @@ -44,14 +45,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, const float hi, const float hj, - struct spart *si, struct part *pj, + struct spart *si, const struct part *pj, const struct cosmology *cosmo, const struct rt_props *rt_props) { si->rt_data.debug_iact_hydro_inject_prep += 1; - si->rt_data.debug_iact_hydro_inject_prep_tot += 1ULL; - pj->rt_data.debug_iact_stars_inject_prep += 1; - pj->rt_data.debug_iact_stars_inject_prep_tot += 1ULL; } /** @@ -65,43 +63,31 @@ runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, * @param pj Hydro particle. * @param a Current scale factor. * @param H Current Hubble parameter. + * @param rt_props Properties of the RT scheme. */ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( const float r2, float *dx, const float hi, const float hj, - struct spart *restrict si, struct part *restrict pj, float a, float H) { + struct spart *restrict si, struct part *restrict pj, float a, float H, + const struct rt_props *rt_props) { + + /* If the star doesn't have any neighbours, we + * have nothing to do here. */ + if (si->density.wcount == 0.f) return; + /* Do some checks and increase neighbour counts + * before other potential early exits */ if (si->rt_data.debug_iact_hydro_inject_prep == 0) error( - "Injecting energy from star that wasn't called" - " during injection prep"); - if (pj->rt_data.debug_iact_stars_inject_prep == 0) { + "Injecting energy from star that wasn't called during injection prep"); - const float hig2 = hi * hi * kernel_gamma2; - const float res = sqrtf(r2 / hig2); - error( - "Injecting energy into part that wasn't called" - " during injection prep: sID %lld pID %lld r/H_s %.6f", - si->id, pj->id, res); - } + if (!si->rt_data.debug_emission_rate_set) + error("Injecting energy from star without setting emission rate"); si->rt_data.debug_iact_hydro_inject += 1; si->rt_data.debug_radiation_emitted_tot += 1ULL; pj->rt_data.debug_iact_stars_inject += 1; pj->rt_data.debug_radiation_absorbed_tot += 1ULL; - - /* Attempt to catch race condition/dependency error */ - if (si->rt_data.debug_iact_hydro_inject_prep < - si->rt_data.debug_iact_hydro_inject) - error( - "Star interacts with more particles during" - " injection than during injection prep"); - - if (pj->rt_data.debug_iact_stars_inject_prep < - pj->rt_data.debug_iact_stars_inject) - error( - "Part interacts with more stars during" - " injection than during injection prep"); } /** @@ -124,34 +110,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common( float r2, const float *dx, float hi, float hj, struct part *restrict pi, struct part *restrict pj, float a, float H, int mode) { - if (pi->rt_data.debug_injection_done != 1) - error( - "Trying to do iact transport when " - "finalise injection count is %d", - pi->rt_data.debug_injection_done); - - if (pi->rt_data.debug_gradients_done != 1) - error( - "Trying to do iact transport when " - "rt_finalise_gradient count is %d", - pi->rt_data.debug_gradients_done); - + const char *func_name = (mode == 1) ? "sym flux iact" : "nonsym flux iact"; + rt_debug_sequence_check(pi, 3, func_name); pi->rt_data.debug_calls_iact_transport_interaction += 1; if (mode == 1) { - - if (pj->rt_data.debug_injection_done != 1) - error( - "Trying to do iact transport when " - "finalise injection count is %d", - pj->rt_data.debug_injection_done); - - if (pj->rt_data.debug_gradients_done != 1) - error( - "Trying to do iact transport when " - "rt_finalise_gradient count is %d", - pj->rt_data.debug_gradients_done); - + rt_debug_sequence_check(pj, 3, func_name); pj->rt_data.debug_calls_iact_transport_interaction += 1; } } diff --git a/src/rt/debug/rt_io.h b/src/rt/debug/rt_io.h index 9e3b0645cc6f38de16e55b96adec8415f85517de..779724d2576dd9a1d26ec25a3e78005894d584f4 100644 --- a/src/rt/debug/rt_io.h +++ b/src/rt/debug/rt_io.h @@ -55,6 +55,11 @@ INLINE static int rt_read_stars(const struct spart* sparts, /** * @brief Creates additional output fields for the radiative * transfer data of hydro particles. + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_particles(const struct part* parts, struct io_props* list) { @@ -90,11 +95,9 @@ INLINE static int rt_write_particles(const struct part* parts, "RTDebugRadAbsorbedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, parts, rt_data.debug_radiation_absorbed_tot, "Radiation absorbed by this part during its lifetime"); - list[7] = io_make_output_field("RTDebugStarsInjectPrepTotCounts", ULONGLONG, - 1, UNIT_CONV_NO_UNITS, 0, parts, - rt_data.debug_iact_stars_inject_prep_tot, - "Total interactions with stars during " - "injection prep during its lifetime"); + list[7] = io_make_output_field("RTDebugSubcycles", INT, 1, UNIT_CONV_NO_UNITS, + 0, parts, rt_data.debug_nsubcycles, + "How many times this part was subcycled"); return 8; } @@ -102,14 +105,19 @@ INLINE static int rt_write_particles(const struct part* parts, /** * @brief Creates additional output fields for the radiative * transfer data of star particles. + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_stars(const struct spart* sparts, struct io_props* list) { list[0] = io_make_output_field("RTDebugHydroIact", INT, 1, UNIT_CONV_NO_UNITS, 0, sparts, rt_data.debug_iact_hydro_inject, - "number of interactions between this hydro " - "particle and any star particle"); + "number of interactions between this star " + "particle and any particle during injection"); list[1] = io_make_output_field( "RTDebugEmissionRateSet", INT, 1, UNIT_CONV_NO_UNITS, 0, sparts, rt_data.debug_emission_rate_set, "Stellar photon emission rates set?"); @@ -117,13 +125,7 @@ INLINE static int rt_write_stars(const struct spart* sparts, "RTDebugRadEmittedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, sparts, rt_data.debug_radiation_emitted_tot, "Total radiation emitted during the lifetime of this star"); - list[3] = io_make_output_field("RTDebugHydroInjectPrepCountsTot", ULONGLONG, - 1, UNIT_CONV_NO_UNITS, 0, sparts, - rt_data.debug_iact_hydro_inject_prep_tot, - "Total interactions with particles during " - "injection prep during its lifetime"); - - return 4; + return 3; } /** @@ -143,12 +145,7 @@ INLINE static void rt_write_flavour(hid_t h_grp, hid_t h_grp_columns, const struct rt_props* rtp) { #if defined(HAVE_HDF5) - if (rtp->hydro_controlled_injection) { - io_write_attribute_s(h_grp, "RT Scheme", - RT_IMPLEMENTATION ", hydro controlled injection"); - } else { - io_write_attribute_s(h_grp, "RT Scheme", RT_IMPLEMENTATION); - } + io_write_attribute_s(h_grp, "RT Scheme", RT_IMPLEMENTATION); #endif } diff --git a/src/rt/debug/rt_parameters.h b/src/rt/debug/rt_parameters.h index 07419abd04dffa6d660e3925b9de92491366805b..f68e22948df365193fe8ddfca306af9c6d9d4e10 100644 --- a/src/rt/debug/rt_parameters.h +++ b/src/rt/debug/rt_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 diff --git a/src/rt/debug/rt_properties.h b/src/rt/debug/rt_properties.h index f26ee4bdfe96252279fefc3c770f6143bf3e549c..9fba0a49b274eb4499c31a6072032d92a15f45c8 100644 --- a/src/rt/debug/rt_properties.h +++ b/src/rt/debug/rt_properties.h @@ -24,26 +24,17 @@ * @brief Main header file for the debug radiative transfer scheme properties. */ +#define RT_IMPLEMENTATION "debug" + /** * @brief Properties of the debug radiative transfer model */ struct rt_props { - /* Are we running with hydro or star controlled injection? - * This is added to avoid #ifdef macros as far as possible */ - int hydro_controlled_injection; - - /* Do we need to run a conversion after the zeroth - * step, but before the first step? */ - int convert_stars_after_zeroth_step; - int convert_parts_after_zeroth_step; - - /* Do extended tests where we assume that all parts - * have spart neighbours? */ - int debug_do_all_parts_have_stars_checks; /* radiation emitted by stars this step. This is not really a property, - * but a placeholder to sum up a global variable */ - int debug_radiation_emitted_this_step; + * but a placeholder to sum up a global variable. It's being reset + * every timestep. */ + unsigned long long debug_radiation_emitted_this_step; /* total radiation emitted by stars. This is not really a property, * but a placeholder to sum up a global variable */ @@ -51,27 +42,14 @@ struct rt_props { /* radiation absorbed by gas this step. This is not really a property, * but a placeholder to sum up a global variable */ - int debug_radiation_absorbed_this_step; + unsigned long long debug_radiation_absorbed_this_step; /* total radiation absorbed by gas. This is not really a property, * but a placeholder to sum up a global variable */ unsigned long long debug_radiation_absorbed_tot; - /* Interactions of a star with gas during injection prep this step. This is - * not really a property, but a placeholder to sum up a global variable */ - int debug_star_injection_prep_iacts_with_parts_this_step; - - /* Interactions of a star with gas during injection prep. This is not - * really a property, but a placeholder to sum up a global variable */ - unsigned long long debug_star_injection_prep_iacts_with_parts_tot; - - /* Interactions of a star with gas during injection prep this step. This is - * not really a property, but a placeholder to sum up a global variable */ - int debug_part_injection_prep_iacts_with_stars_this_step; - - /* Interactions of a star with gas during injection prep. This is not - * really a property, but a placeholder to sum up a global variable */ - unsigned long long debug_part_injection_prep_iacts_with_stars_tot; + /* Max number of subcycles per hydro step */ + int debug_max_nr_subcycles; }; /** @@ -86,12 +64,6 @@ __attribute__((always_inline)) INLINE static void rt_props_print( if (engine_rank != 0) return; message("Radiative transfer scheme: '%s'", RT_IMPLEMENTATION); - - /* Print the RT properties */ - if (rtp->debug_do_all_parts_have_stars_checks) - message("Doing extra checks assuming all parts have spart neighbours"); - else - message("Skipping extra checks assuming all parts have spart neighbours"); } /** @@ -108,31 +80,16 @@ __attribute__((always_inline)) INLINE static void rt_props_init( const struct unit_system* us, struct swift_params* params, struct cosmology* cosmo) { - rtp->debug_do_all_parts_have_stars_checks = - parser_get_opt_param_int(params, "DebugRT:all_parts_have_stars", 0); - -#ifdef RT_HYDRO_CONTROLLED_INJECTION - rtp->hydro_controlled_injection = 1; -#else - rtp->hydro_controlled_injection = 0; -#endif - - /* Make sure we reset debugging counters correctly. */ - rtp->convert_parts_after_zeroth_step = 1; - rtp->convert_stars_after_zeroth_step = 1; - rtp->debug_radiation_emitted_tot = 0ULL; - rtp->debug_radiation_absorbed_tot = 0ULL; - rtp->debug_star_injection_prep_iacts_with_parts_tot = 0LL; - rtp->debug_part_injection_prep_iacts_with_stars_tot = 0LL; + rtp->debug_radiation_emitted_this_step = 0ULL; - /* After initialisation, print params to screen */ - rt_props_print(rtp); + rtp->debug_radiation_absorbed_tot = 0ULL; + rtp->debug_radiation_absorbed_this_step = 0ULL; - /* Print a final message. */ - if (engine_rank == 0) { - message("Radiative transfer initialized"); - } + /* Don't make it an optional parameter here so we crash + * if I forgot to provide it */ + rtp->debug_max_nr_subcycles = + parser_get_param_int(params, "TimeIntegration:max_nr_rt_subcycles"); } /** @@ -155,9 +112,12 @@ __attribute__((always_inline)) INLINE static void rt_struct_dump( * * @param props the struct * @param stream the file stream + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. */ __attribute__((always_inline)) INLINE static void rt_struct_restore( - struct rt_props* props, FILE* stream) { + struct rt_props* props, FILE* stream, const struct phys_const* phys_const, + const struct unit_system* us) { restart_read_blocks((void*)props, sizeof(struct rt_props), 1, stream, NULL, "RT properties struct"); diff --git a/src/rt/debug/rt_struct.h b/src/rt/debug/rt_struct.h index a71e639ca516a9cd461f15eb102aea0ca59f15b6..bb3742f98c0256b76fa8b33231abaa16fce1d72d 100644 --- a/src/rt/debug/rt_struct.h +++ b/src/rt/debug/rt_struct.h @@ -19,6 +19,8 @@ #ifndef SWIFT_RT_STRUCT_DEBUG_H #define SWIFT_RT_STRUCT_DEBUG_H +#include "timeline.h" + /** * @file src/rt/debug/rt_struct.h * @brief Main header file for the debug radiative transfer struct. @@ -32,25 +34,13 @@ struct rt_part_data { /*! how much radiation this part received from stars during total lifetime */ unsigned long long debug_radiation_absorbed_tot; - /*! how many interactions this part had with stars in injection prep over - * total lifetime */ - unsigned long long debug_iact_stars_inject_prep_tot; - /* data to store during one time step */ - /*! how many stars this part interacted with during preparation*/ - /* Note: It's useless to write this in outputs, as it gets reset - * at the end of every step. */ - int debug_iact_stars_inject_prep; - /*! how many stars this part interacted with during injection*/ /* Note: It's useless to write this in outputs, as it gets reset * at the end of every step. */ int debug_iact_stars_inject; - /*! called in a self/rt_injection task? */ - int debug_injection_check; - /*! calls from gradient interaction loop in actual function */ int debug_calls_iact_gradient_interaction; @@ -59,6 +49,9 @@ struct rt_part_data { /* Task completion flags */ + /*! part got kicked? */ + int debug_kicked; + /*! calls from ghost1 tasks */ int debug_injection_done; @@ -70,6 +63,11 @@ struct rt_part_data { /*! thermochemistry done? */ int debug_thermochem_done; + + /* Subcycling flags */ + + /*! Current subcycle wrt (last) hydro step */ + int debug_nsubcycles; }; /* Additional RT data in star particle struct */ @@ -80,10 +78,6 @@ struct rt_spart_data { /*! how much radiation this star emitted during total lifetime */ unsigned long long debug_radiation_emitted_tot; - /*! how many interactions this star had with parts during - * injection prep over total lifetime */ - unsigned long long debug_iact_hydro_inject_prep_tot; - /* data to store during one time step */ /*! how many hydro particles this particle interacted with @@ -96,9 +90,6 @@ struct rt_spart_data { /*! stellar photon emisison rate computed? */ int debug_emission_rate_set; - - /*! called in a self/rt_injection task? */ - int debug_injection_check; }; #endif /* SWIFT_RT_STRUCT_DEBUG_H */ diff --git a/src/rt/debug/rt_thermochemistry.h b/src/rt/debug/rt_thermochemistry.h deleted file mode 100644 index 82aaf9cfe3ef267cf7a87feff84748ffd26af0ef..0000000000000000000000000000000000000000 --- a/src/rt/debug/rt_thermochemistry.h +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) - * - * 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_RT_THERMOCHEMISTRY_DEBUG_H -#define SWIFT_RT_THERMOCHEMISTRY_DEBUG_H - -/** - * @file src/rt/debug/rt_thermochemistry.h - * @brief Main header file for the debug radiative transfer scheme - * thermochemistry related functions. - */ - -/** - * @brief Main function for the thermochemistry step. - * - * @param p Particle to work on. - */ -__attribute__((always_inline)) INLINE static void rt_do_thermochemistry( - struct part *restrict p) { - - if (!p->rt_data.debug_injection_done) - error("Trying to do thermochemistry when injection step hasn't been done"); - if (!p->rt_data.debug_gradients_done) - error("Trying to do thermochemistry when gradient step hasn't been done"); - if (!p->rt_data.debug_transport_done) - error("Trying to do thermochemistry when transport step hasn't been done"); - - p->rt_data.debug_thermochem_done += 1; -} - -#endif /* SWIFT_RT_THERMOCHEMISTRY_DEBUG_H */ diff --git a/src/rt/none/rt.h b/src/rt/none/rt.h index 8a6cb66db28f5d1074e1a8f1005116083bf8a0fb..9edc956e1885080ac98f526b1f6f315c8267694a 100644 --- a/src/rt/none/rt.h +++ b/src/rt/none/rt.h @@ -28,52 +28,77 @@ * @brief Main header file for no radiative transfer scheme. */ +/** + * @brief Compute the photon emission rates for this stellar particle + * This function is called every time the spart is being reset + * (during start-up and during stars ghost if spart is active) + * and assumes that the photon emission rate is an intrinsic + * stellar property, i.e. doesn't depend on the environment. + * + * @param sp star particle to work on + * @param time current system time + * @param star_age age of the star *at the end of the step* + * @param dt star time step + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param internal_units struct holding internal units + */ +__attribute__((always_inline)) INLINE static void +rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, + double star_age, double dt, + const struct rt_props* rt_props, + const struct phys_const* phys_const, + const struct unit_system* internal_units) {} + /** * @brief Initialisation of the RT density loop related particle data. * Note: during initalisation (space_init), rt_reset_part and rt_init_part * are both called individually. + * + * @param p Particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_part( struct part* restrict p) {} /** - * @brief Reset of the RT hydro particle data not related to the density. + * @brief Reset the RT hydro particle data not related to the hydro density. * Note: during initalisation (space_init), rt_reset_part and rt_init_part - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_part is made in space_convert_rt_quantities() after - * the zeroth time step is finished. + * are both called individually. To reset RT data needed in each RT sub-cycle, + * use rt_reset_part_each_subcycle(). + * + * @param p particle to work on + * @param cosmo Cosmology. */ __attribute__((always_inline)) INLINE static void rt_reset_part( - struct part* restrict p) {} + struct part* restrict p, const struct cosmology* cosmo) {} /** - * @brief First initialisation of the RT hydro particle data. - * @param p particle to work on + * @brief Reset RT particle data which needs to be reset each sub-cycle. + * + * @param p the particle to work on + * @param cosmo Cosmology. + * @param dt the current particle RT time step */ -__attribute__((always_inline)) INLINE static void rt_first_init_part( - struct part* restrict p) {} +__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle( + struct part* restrict p, const struct cosmology* cosmo, double dt) {} /** - * @brief Initialises particle quantities that can't be set - * otherwise before the zeroth step is finished. E.g. because - * they require the particle density to be known. + * @brief First initialisation of the RT hydro particle data. * * @param p particle to work on + * @param cosmo #cosmology data structure. * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param us unit_system struct - * @param cosmo cosmology struct */ -__attribute__((always_inline)) INLINE static void -rt_init_part_after_zeroth_step(struct part* restrict p, - const struct rt_props* rt_props, - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo) {} +__attribute__((always_inline)) INLINE static void rt_first_init_part( + struct part* restrict p, const struct cosmology* cosmo, + const struct rt_props* restrict rt_props) {} + /** * @brief Initialisation of the RT density loop related star particle data. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart * are both called individually. + * + * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_spart( struct spart* restrict sp) {} @@ -81,15 +106,17 @@ __attribute__((always_inline)) INLINE static void rt_init_spart( /** * @brief Reset of the RT star particle data not related to the density. * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart - * are both called individually. Also, if debugging checks are active, an - * extra call to rt_reset_spart is made in space_convert_rt_quantities() after - * the zeroth time step is finished. + * are both called individually. + * + * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_reset_spart( struct spart* restrict sp) {} /** * @brief First initialisation of the RT star particle data. + * + * @param sp star particle to work on */ __attribute__((always_inline)) INLINE static void rt_first_init_spart( struct spart* restrict sp) {} @@ -114,7 +141,7 @@ __attribute__((always_inline)) INLINE static void rt_part_has_no_neighbours( /** * @brief Exception handle a star part not having any neighbours in ghost task * - * @param p The #part. + * @param sp The #spart. */ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( struct spart* sp){}; @@ -124,21 +151,36 @@ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( * * @param p The particle to work on * @param rtp The RT properties struct + * @param hydro_props The hydro properties struct + * @param phys_const physical constants struct + * @param us unit_system struct + * @param cosmo cosmology struct */ __attribute__((always_inline)) INLINE static void rt_convert_quantities( - struct part* p, const struct rt_props* rtp){}; + struct part* restrict p, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo) {} /** * @brief Computes the next radiative transfer time step size * of a given particle (during timestep tasks) * - * @param p particle to work on - * @param rt_props the RT properties struct - * @param cosmo the cosmology + * @param p Particle to work on. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. */ __attribute__((always_inline)) INLINE static float rt_compute_timestep( - const struct part* restrict p, const struct rt_props* restrict rt_props, - const struct cosmology* restrict cosmo) { + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us) { return FLT_MAX; } @@ -160,7 +202,10 @@ __attribute__((always_inline)) INLINE static float rt_compute_spart_timestep( /** * @brief Compute the time-step length for an RT step of a particle from given - * integer times ti_beg and ti_end + * integer times ti_beg and ti_end. This time-step length is then used to + * compute the actual time integration of the transport/force step and the + * thermochemistry. This is not used to determine the time-step length during + * the time-step tasks. * * @param ti_beg Start of the time-step (on the integer time-line). * @param ti_end End of the time-step (on the integer time-line). @@ -178,55 +223,33 @@ __attribute__((always_inline)) INLINE static double rt_part_dt( } /** - * @brief Update the photon number of a particle, i.e. compute - * E^{n+1} = E^n + dt * dE_* / dt. This function finalises - * the injection step. + * @brief This function finalises the injection step. * * @param p particle to work on * @param props struct #rt_props that contains global RT properties */ -__attribute__((always_inline)) INLINE static void -rt_injection_update_photon_density(struct part* restrict p, - struct rt_props* props) {} - -/** - * @brief Compute the photon emission rates for this stellar particle - * This function is called every time the spart is being reset - * (during start-up and during stars ghost if spart is active) - * and assumes that the photon emission rate is an intrinsic - * stellar property, i.e. doesn't depend on the environment. - * - * @param sp star particle to work on - * @param time current system time - * @param star_age age of the star *at the end of the step* - * @param dt star time step - * @param rt_props RT properties struct - * @param phys_const physical constants struct - * @param internal_units struct holding internal units - */ -__attribute__((always_inline)) INLINE static void -rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, - double star_age, double dt, - const struct rt_props* rt_props, - const struct phys_const* phys_const, - const struct unit_system* internal_units) {} +__attribute__((always_inline)) INLINE static void rt_finalise_injection( + struct part* restrict p, struct rt_props* props) {} /** * @brief finishes up the gradient computation * * @param p particle to work on + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_end_gradient( - struct part* restrict p) {} + struct part* restrict p, const struct cosmology* cosmo) {} /** * @brief finishes up the transport step * * @param p particle to work on * @param dt the current time step of the particle + * @param cosmo #cosmology data structure. */ __attribute__((always_inline)) INLINE static void rt_finalise_transport( - struct part* restrict p, const double dt) {} + struct part* restrict p, const double dt, + const struct cosmology* restrict cosmo) {} /** * @brief Do the thermochemistry on a particle. @@ -276,12 +299,23 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra( __attribute__((always_inline)) INLINE static void rt_prepare_force( struct part* p) {} +/** + * @brief Extra operations to be done during the drift + * + * @param p Particle to act upon. + * @param xp The extended particle data to act upon. + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void rt_predict_extra( + struct part* p, struct xpart* xp, float dt_drift) {} + /** * @brief Clean the allocated memory inside the RT properties struct. * * @param props the #rt_props. + * @param restart did we restart? */ __attribute__((always_inline)) INLINE static void rt_clean( - struct rt_props* props) {} + struct rt_props* props, int restart) {} #endif /* SWIFT_RT_NONE_H */ diff --git a/src/rt/none/rt_additions.h b/src/rt/none/rt_additions.h index 2e8e2c5dbe87ed99926a218b56d1846a4227ffa2..0c0968f7069941a3a2b5fae3d6376bec997e06fe 100644 --- a/src/rt/none/rt_additions.h +++ b/src/rt/none/rt_additions.h @@ -33,7 +33,7 @@ * @param pj second interacting particle * @param mass_flux the mass flux between these two particles * @param mode 0: non-symmetric interaction, update i only. 1: symmetric - *interaction. + * interaction. **/ __attribute__((always_inline)) INLINE static void rt_part_update_mass_fluxes( struct part* restrict pi, struct part* restrict pj, float mass_flux, diff --git a/src/rt/none/rt_iact.h b/src/rt/none/rt_iact.h index f5d2ff788f4d178e1ed8109b62a90cf85dea2b89..f8a3d85447ae7b463e9f7382d797af1037bf2cb5 100644 --- a/src/rt/none/rt_iact.h +++ b/src/rt/none/rt_iact.h @@ -57,10 +57,12 @@ runner_iact_nonsym_rt_injection_prep(const float r2, const float *dx, * @param pj Hydro particle. * @param a Current scale factor. * @param H Current Hubble parameter. + * @param rt_props Properties of the RT scheme. */ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( const float r2, float *dx, const float hi, const float hj, - struct spart *restrict si, struct part *restrict pj, float a, float H) {} + struct spart *restrict si, struct part *restrict pj, float a, float H, + const struct rt_props *rt_props) {} /** * @brief Flux calculation between particle i and particle j diff --git a/src/rt/none/rt_io.h b/src/rt/none/rt_io.h index 3f2e6ed445669bf2d3062b0ceed0d3ecfc9e0c06..3abc46201422bdaefbd7878eba1fbd6f32eab0d1 100644 --- a/src/rt/none/rt_io.h +++ b/src/rt/none/rt_io.h @@ -55,6 +55,11 @@ INLINE static int rt_read_stars(const struct spart* sparts, /** * @brief Creates additional output fields for the radiative * transfer data of hydro particles. + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_particles(const struct part* parts, struct io_props* list) { @@ -64,6 +69,11 @@ INLINE static int rt_write_particles(const struct part* parts, /** * @brief Creates additional output fields for the radiative * transfer data of star particles. + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. */ INLINE static int rt_write_stars(const struct spart* sparts, struct io_props* list) { diff --git a/src/rt/none/rt_parameters.h b/src/rt/none/rt_parameters.h index 90a2413130ae77eea9561da044a91a00012a450d..37de557f19d0a17bb066e3b06114fd0a6a8f30f1 100644 --- a/src/rt/none/rt_parameters.h +++ b/src/rt/none/rt_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) * * 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 diff --git a/src/rt/none/rt_properties.h b/src/rt/none/rt_properties.h index 02826ce2b992c3dfe5cae48fac4a95645e2ebc13..16fd5fa5bf64987b7203e5437fa2da1ed1844aee 100644 --- a/src/rt/none/rt_properties.h +++ b/src/rt/none/rt_properties.h @@ -24,20 +24,12 @@ * @brief Main header file for the 'none' radiative transfer scheme properties. */ +#define RT_IMPLEMENTATION "none" + /** * @brief Properties of the 'none' radiative transfer model */ -struct rt_props { - - /* Are we running with hydro or star controlled injection? - * This is added to avoid #ifdef macros as far as possible */ - int hydro_controlled_injection; - - /* Do we need to run a conversion after the zeroth - * step, but before the first step? */ - int convert_stars_after_zeroth_step; - int convert_parts_after_zeroth_step; -}; +struct rt_props {}; /** * @brief Print the RT model. @@ -65,20 +57,7 @@ __attribute__((always_inline)) INLINE static void rt_props_print( __attribute__((always_inline)) INLINE static void rt_props_init( struct rt_props* rtp, const struct phys_const* phys_const, const struct unit_system* us, struct swift_params* params, - struct cosmology* cosmo) { - -#ifdef RT_HYDRO_CONTROLLED_INJECTION - rtp->hydro_controlled_injection = 1; -#else - rtp->hydro_controlled_injection = 0; -#endif - - rtp->convert_parts_after_zeroth_step = 0; - rtp->convert_stars_after_zeroth_step = 0; - - /* After initialisation, print params to screen */ - rt_props_print(rtp); -} + struct cosmology* cosmo) {} /** * @brief Write an RT properties struct to the given FILE as a @@ -100,9 +79,12 @@ __attribute__((always_inline)) INLINE static void rt_struct_dump( * * @param props the struct * @param stream the file stream + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. */ __attribute__((always_inline)) INLINE static void rt_struct_restore( - struct rt_props* props, FILE* stream) { + struct rt_props* props, FILE* stream, const struct phys_const* phys_const, + const struct unit_system* us) { restart_read_blocks((void*)props, sizeof(struct rt_props), 1, stream, NULL, "RT properties struct"); diff --git a/src/rt_active.h b/src/rt_active.h deleted file mode 100644 index 43733ca98feda16b905216d8c96c9dbd5a2e7344..0000000000000000000000000000000000000000 --- a/src/rt_active.h +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com) - * - * 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_RT_ACTIVE_H -#define SWIFT_RT_ACTIVE_H - -/* Local includes. */ -#include "active.h" - -/** - * @file rt_active.h - * @brief This file contains the checks for whether radiative transfer - * (injection) tasks should be activated for given cells, parts, or sparts. - * The functions differ from the checks in `src/active.h` in that not only - * time-step data is being checked, but more properties as well. So for - * consistency, they get their own file. Finally, the functions are gathered - * in one RT related file to concentrate all #ifdef macro shenanigans in a - * single place as far as possible. - */ - -/** - * @brief Pick whether we need to check whether an spart is active depending - * on which version of injection we are using. This is done here to minimize - * #ifdef macros throughout this file. - * - * Returns 1 if spart is active. - * - * @param sp star particle - * @param e the engine - */ -__attribute__((always_inline)) INLINE static int rt_is_spart_active_in_loop( - struct spart *restrict sp, const struct engine *e) { - -#ifdef RT_HYDRO_CONTROLLED_INJECTION - return 1; /* ignore stellar activity when gas pulls radiation from stars */ -#else - return spart_is_active(sp, e); -#endif -} - -/** - * @brief Pick whether we need to check whether a part is active depending - * on which version of injection we are using. This is done here to minimize - * #ifdef macros throughout this file. - * - * Returns 1 if spart is active. - * - * @param p the part - * @param e the engine - */ -__attribute__((always_inline)) INLINE static int rt_is_part_active_in_loop( - struct part *restrict p, const struct engine *e) { - -#ifdef RT_HYDRO_CONTROLLED_INJECTION - return part_is_active(p, e); -#else - return 1; /* ignore hydro activity when stars push radiation onto gas */ -#endif -} - -/** - * @brief Does a cell contain particles that should do RT this step? - * This function is for a self-type interaction, where we need a cell - * to have active hydro particles and star particles in any state. - * - * @param c The #cell. - * @param e The #engine containing information about the current time. - * @return 1 if the #cell contains at least an active particle, 0 otherwise. - */ -__attribute__((always_inline)) INLINE static int rt_should_iact_cell( - const struct cell *c, const struct engine *e) { - -#ifdef RT_HYDRO_CONTROLLED_INJECTION - return ((cell_is_active_hydro(c, e) && (c->hydro.count > 0)) && - (c->stars.count > 0)); -#else - return ((cell_is_active_stars(c, e) && (c->stars.count > 0)) && - (c->hydro.count > 0)); -#endif -} - -/** - * @brief Does a cell contain particles that should do RT this step? - * This function is for a pair-type interaction, where we take stars from - * cell ci and hydro particles from cell cj. - * - * @param ci First #cell. - * @param cj Second #cell. - * @param e The #engine containing information about the current time. - * @return 1 if the #cell contains at least an active particle, 0 otherwise. - */ -__attribute__((always_inline)) INLINE static int rt_should_iact_cell_pair( - const struct cell *ci, const struct cell *cj, const struct engine *e) { - - if (cj == NULL) return 0; - -#ifdef RT_HYDRO_CONTROLLED_INJECTION - return (cell_is_active_hydro(cj, e) && (cj->hydro.count > 0) && - (ci->stars.count > 0)); -#else - return (cell_is_active_stars(ci, e) && (ci->stars.count > 0) && - (cj->hydro.count > 0)); -#endif -} - -/** - * @brief Do we need to unskip this cell's RT (injection) related tasks? - * For unskipping (recursively), don't check about the star's count here: - * Pair-type interactions don't require stars in every cell. This way, we - * can include the check for star count > 0 there on a pair by pair basis. - * - * @param c The #cell. - * @param e The #engine containing information about the current time. - * @return 1 if the #cell needs to activate tasks - */ -__attribute__((always_inline)) INLINE static int rt_should_do_unskip_cell( - const struct cell *c, const struct engine *e) { - /* whether it's hydro controlled or not, we need to check for hydro - * activity at the top level. We also need to check for star activity - * so we can activate the rt_in implicit tasks to catch dependencies - * before the injection and not be overwritten by work in star density - * ghosts. */ - return ((cell_is_active_hydro(c, e) && (c->hydro.count > 0)) || - cell_is_active_stars(c, e)); -} - -#endif /* SWIFT_RT_ACTIVE_H */ diff --git a/src/rt_additions.h b/src/rt_additions.h index 3525f3e9e840578d17d9dfef9162f7ad1d155844..cb7595c238171c57a5653ef3ace74857b5b39b4b 100644 --- a/src/rt_additions.h +++ b/src/rt_additions.h @@ -26,7 +26,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right RT definition */ #if defined(RT_NONE) diff --git a/src/rt_io.h b/src/rt_io.h index eb9532ffecced9832e945ea4d61a16a5e767e304..655c13f3bdd1c7af3eebc6cd09b3d0c01456a9b6 100644 --- a/src/rt_io.h +++ b/src/rt_io.h @@ -25,8 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" -#include "rt.h" +#include <config.h> /* Import the right RT definition */ #if defined(RT_NONE) diff --git a/src/rt_parameters.h b/src/rt_parameters.h index 317e2df2ec3ad784296c57b12452bc32191e7088..fa3821005f81c8e0157c0e9883f4f1bf27e38c26 100644 --- a/src/rt_parameters.h +++ b/src/rt_parameters.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2016 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 @@ -27,7 +27,7 @@ * */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right functions */ #if defined(RT_DEBUG) diff --git a/src/rt_properties.h b/src/rt_properties.h index 1793c38eeb08d8afbfb1f5ac0a9edeb307247a02..e0454afe79786f16c8f0c0b75fe3112709e2498d 100644 --- a/src/rt_properties.h +++ b/src/rt_properties.h @@ -25,8 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" -#include "rt.h" +#include <config.h> /* Import the right RT definition */ #if defined(RT_NONE) diff --git a/src/rt_struct.h b/src/rt_struct.h index 3c73e8867a41b269a79f64e2e1061ddee1916188..78986ef1af5fc45e4dcece74041bb498863bb518 100644 --- a/src/rt_struct.h +++ b/src/rt_struct.h @@ -25,7 +25,9 @@ */ /* Config parameters. */ -#include "../config.h" +#include "timeline.h" + +#include <config.h> /* Import the right RT struct definition */ #if defined(RT_NONE) @@ -40,4 +42,35 @@ #error "Invalid choice of radiation scheme" #endif +/* Define a struct to contain all RT sub-cycling related + * timestepping variables here. These variables need to be + * identical for every scheme and users should never touch + * them anyway, so hide them here. */ + +#if defined(RT_NONE) + +/*! empty placeholder for RT timestepping data. */ +struct rt_timestepping_data { + union { + /*! Time-bin this particle uses for RT interactions */ + timebin_t time_bin; + + /*! Minimal time-bin across all neighbours */ + timebin_t min_ngb_time_bin; + }; +}; + +#else + +/*! data relevant to the sub-cycle timestepping of parts. */ +struct rt_timestepping_data { + + /*! Time-bin this particle uses for RT interactions */ + timebin_t time_bin; + + /*! Minimal time-bin across all neighbours */ + timebin_t min_ngb_time_bin; +}; +#endif + #endif /* SWIFT_RT_STRUCT_H */ diff --git a/src/runner.h b/src/runner.h index 6f9bbe582438704153b770dad6628a8d3283808f..4ccd3cec4ac7e6ef1edffb0e81ec16da54d15145 100644 --- a/src/runner.h +++ b/src/runner.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -24,7 +24,7 @@ #define SWIFT_RUNNER_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "cache.h" @@ -41,14 +41,13 @@ struct task; #define TASK_LOOP_LIMITER 3 #define TASK_LOOP_FEEDBACK 4 #define TASK_LOOP_SWALLOW 5 -#define TASK_LOOP_SINK_FORMATION 6 -#define TASK_LOOP_SINK_MERGER 7 -#define TASK_LOOP_SINK_ACCRETION 8 +#define TASK_LOOP_SINK_SWALLOW 6 +#define TASK_LOOP_SINK_DO_SINK_SWALLOW 7 +#define TASK_LOOP_SINK_DO_GAS_SWALLOW 8 #define TASK_LOOP_STARS_PREP1 9 #define TASK_LOOP_STARS_PREP2 10 #define TASK_LOOP_RT_GRADIENT 11 #define TASK_LOOP_RT_TRANSPORT 12 -#define TASK_LOOP_RT_INJECT 13 /** * @brief A struct representing a runner's thread and its data. @@ -101,7 +100,7 @@ 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); + int cleanup, int rt_requests_sort, int clock); void runner_do_stars_sort(struct runner *r, struct cell *c, int flag, int cleanup, int clock); void runner_do_all_hydro_sort(struct runner *r, struct cell *c); @@ -154,6 +153,10 @@ void runner_do_pack_limiter(struct runner *r, struct cell *c, void **buffer, void runner_do_unpack_limiter(struct runner *r, struct cell *c, void *buffer, const int timer); void runner_do_neutrino_weighting(struct runner *r, struct cell *c, int timer); +void runner_do_rt_advance_cell_time(struct runner *r, struct cell *c, + int timer); +void runner_do_collect_rt_times(struct runner *r, struct cell *c, + const int timer); void *runner_main(void *data); ticks runner_get_active_time(const struct runner *restrict r); diff --git a/src/runner_black_holes.c b/src/runner_black_holes.c index 7f59308673199f317d80436afd8fe6921643ed0d..aebef16591aa6feaff1d12c26f0733e4a7cad0d4 100644 --- a/src/runner_black_holes.c +++ b/src/runner_black_holes.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner.h" @@ -385,7 +385,8 @@ void runner_do_bh_swallow(struct runner *r, struct cell *c, int timer) { /* Swallow the BH particle (i.e. update the swallowing BH * properties with the properties of cell_bp) */ black_holes_swallow_bpart(bp, cell_bp, e->cosmology, e->time, - with_cosmology, props); + with_cosmology, props, + e->physical_constants); /* Release the space as we are done updating the bpart */ if (lock_unlock(&s->lock) != 0) diff --git a/src/runner_doiact_black_holes.c b/src/runner_doiact_black_holes.c index 5c139eada6cf7403076194c42261948db5e0f7f4..ac0414302407cd1c8cd436687dd408ce6432bce4 100644 --- a/src/runner_doiact_black_holes.c +++ b/src/runner_doiact_black_holes.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,11 +20,11 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "active.h" -#include "black_holes.h" +#include "black_holes_iact.h" #include "cell.h" #include "engine.h" #include "runner.h" @@ -35,19 +35,16 @@ #define FUNCTION density #define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY #include "runner_doiact_functions_black_holes.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" /* 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 +#include "runner_doiact_undef.h" /* 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 +#include "runner_doiact_undef.h" diff --git a/src/runner_doiact_black_holes.h b/src/runner_doiact_black_holes.h index 763e557babb9ca94a05a28d1ea5ed0f1141684ff..52fd2ba71a74c781ce6913648f4340ed02ced679 100644 --- a/src/runner_doiact_black_holes.h +++ b/src/runner_doiact_black_holes.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify diff --git a/src/runner_doiact_functions_black_holes.h b/src/runner_doiact_functions_black_holes.h index cd439300b8807a4a7a44ca4cd176d7762c0eeb6e..55e63e928b2a8f849061933700e2c3c17d4c5fcc 100644 --- a/src/runner_doiact_functions_black_holes.h +++ b/src/runner_doiact_functions_black_holes.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -45,6 +45,7 @@ void DOSELF1_BH(struct runner *r, struct cell *c, int timer) { const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; const int with_cosmology = e->policy & engine_policy_cosmology; + const int bi_is_local = 1; /* SELF tasks are always local */ /* Anything to do here? */ if (c->black_holes.count == 0) return; @@ -89,7 +90,7 @@ void DOSELF1_BH(struct runner *r, struct cell *c, int timer) { 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 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 @@ -104,6 +105,15 @@ void DOSELF1_BH(struct runner *r, struct cell *c, int timer) { IACT_BH_GAS(r2, dx, hi, hj, bi, pj, xpj, with_cosmology, cosmo, e->gravity_properties, e->black_holes_properties, e->entropy_floor, ti_current, e->time); + + if (bi_is_local) { +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + runner_iact_nonsym_bh_gas_repos( + r2, dx, hi, pj->h, bi, pj, xpj, with_cosmology, cosmo, + e->gravity_properties, e->black_holes_properties, + e->entropy_floor, ti_current, e->time); +#endif + } } } /* loop over the parts in ci. */ } /* loop over the bparts in ci. */ @@ -145,7 +155,7 @@ void DOSELF1_BH(struct runner *r, struct cell *c, int timer) { 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 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 @@ -159,6 +169,12 @@ void DOSELF1_BH(struct runner *r, struct cell *c, int timer) { if (r2 < hig2) { IACT_BH_BH(r2, dx, hi, hj, bi, bj, cosmo, e->gravity_properties, e->black_holes_properties, ti_current); + + if (bi_is_local) { + runner_iact_nonsym_bh_bh_repos(r2, dx, hi, hj, bi, bj, cosmo, + e->gravity_properties, + e->black_holes_properties, ti_current); + } } } /* loop over the bparts in ci. */ } /* loop over the bparts in ci. */ @@ -190,6 +206,7 @@ void DO_NONSYM_PAIR1_BH_NAIVE(struct runner *r, struct cell *restrict ci, const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; const int with_cosmology = e->policy & engine_policy_cosmology; + const int bi_is_local = ci->nodeID == e->nodeID; /* Anything to do here? */ if (ci->black_holes.count == 0) return; @@ -243,7 +260,7 @@ void DO_NONSYM_PAIR1_BH_NAIVE(struct runner *r, struct cell *restrict ci, 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 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 @@ -258,6 +275,15 @@ void DO_NONSYM_PAIR1_BH_NAIVE(struct runner *r, struct cell *restrict ci, IACT_BH_GAS(r2, dx, hi, hj, bi, pj, xpj, with_cosmology, cosmo, e->gravity_properties, e->black_holes_properties, e->entropy_floor, ti_current, e->time); + + if (bi_is_local) { +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + runner_iact_nonsym_bh_gas_repos( + r2, dx, hi, hj, bi, pj, xpj, with_cosmology, cosmo, + e->gravity_properties, e->black_holes_properties, + e->entropy_floor, ti_current, e->time); +#endif + } } } /* loop over the parts in cj. */ } /* loop over the bparts in ci. */ @@ -299,7 +325,7 @@ void DO_NONSYM_PAIR1_BH_NAIVE(struct runner *r, struct cell *restrict ci, 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 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 @@ -313,6 +339,12 @@ void DO_NONSYM_PAIR1_BH_NAIVE(struct runner *r, struct cell *restrict ci, if (r2 < hig2) { IACT_BH_BH(r2, dx, hi, hj, bi, bj, cosmo, e->gravity_properties, e->black_holes_properties, ti_current); + + if (bi_is_local) { + runner_iact_nonsym_bh_bh_repos(r2, dx, hi, hj, bi, bj, cosmo, + e->gravity_properties, + e->black_holes_properties, ti_current); + } } } /* loop over the bparts in cj. */ } /* loop over the bparts in ci. */ @@ -371,6 +403,7 @@ void DOPAIR1_SUBSET_BH_NAIVE(struct runner *r, struct cell *restrict ci, const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; const int with_cosmology = e->policy & engine_policy_cosmology; + const int bi_is_local = ci->nodeID == e->nodeID; const int count_j = cj->hydro.count; struct part *restrict parts_j = cj->hydro.parts; @@ -412,8 +445,8 @@ void DOPAIR1_SUBSET_BH_NAIVE(struct runner *r, struct cell *restrict ci, const float hj = pj->h; /* Compute the pairwise distance. */ - float dx[3] = {(float)(bix - pjx), (float)(biy - pjy), - (float)(biz - pjz)}; + const 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 @@ -426,6 +459,14 @@ void DOPAIR1_SUBSET_BH_NAIVE(struct runner *r, struct cell *restrict ci, IACT_BH_GAS(r2, dx, hi, hj, bi, pj, xpj, with_cosmology, cosmo, e->gravity_properties, e->black_holes_properties, e->entropy_floor, ti_current, e->time); + if (bi_is_local) { +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + runner_iact_nonsym_bh_gas_repos( + r2, dx, hi, hj, bi, pj, xpj, with_cosmology, cosmo, + e->gravity_properties, e->black_holes_properties, + e->entropy_floor, ti_current, e->time); +#endif + } } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -453,6 +494,7 @@ void DOSELF1_SUBSET_BH(struct runner *r, struct cell *restrict ci, const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; const int with_cosmology = e->policy & engine_policy_cosmology; + const int bi_is_local = 1; /* SELF tasks are always local */ const int count_i = ci->hydro.count; struct part *restrict parts_j = ci->hydro.parts; @@ -490,7 +532,7 @@ void DOSELF1_SUBSET_BH(struct runner *r, struct cell *restrict ci, 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 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 @@ -504,6 +546,15 @@ void DOSELF1_SUBSET_BH(struct runner *r, struct cell *restrict ci, IACT_BH_GAS(r2, dx, hi, pj->h, bi, pj, xpj, with_cosmology, cosmo, e->gravity_properties, e->black_holes_properties, e->entropy_floor, ti_current, e->time); + + if (bi_is_local) { +#if (FUNCTION_TASK_LOOP == TASK_LOOP_SWALLOW) + runner_iact_nonsym_bh_gas_repos( + r2, dx, hi, pj->h, bi, pj, xpj, with_cosmology, cosmo, + e->gravity_properties, e->black_holes_properties, + e->entropy_floor, ti_current, e->time); +#endif + } } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ diff --git a/src/runner_doiact_functions_hydro.h b/src/runner_doiact_functions_hydro.h index e79fe0457eac77fcecd176abd84decd90c6d1b70..189ac30181b5532c927f87bd1502516f0b59bc5a 100644 --- a/src/runner_doiact_functions_hydro.h +++ b/src/runner_doiact_functions_hydro.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -49,16 +49,17 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, TIMER_TIC; /* Anything to do here? */ - if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return; + if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return; const int count_i = ci->hydro.count; const int count_j = cj->hydro.count; struct part *restrict parts_i = ci->hydro.parts; struct part *restrict parts_j = cj->hydro.parts; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; @@ -78,7 +79,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; - const int pi_active = part_is_active(pi, e); + const int pi_active = PART_IS_ACTIVE(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])), @@ -96,7 +97,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; - const int pj_active = part_is_active(pj, e); + const int pj_active = PART_IS_ACTIVE(pj, e); /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]), @@ -105,7 +106,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -117,13 +118,17 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, if (r2 < hig2 && pi_active) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -135,13 +140,17 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, dx[2] = -dx[2]; IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -175,16 +184,17 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, TIMER_TIC; /* Anything to do here? */ - if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return; + if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return; const int count_i = ci->hydro.count; const int count_j = cj->hydro.count; struct part *restrict parts_i = ci->hydro.parts; struct part *restrict parts_j = cj->hydro.parts; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; @@ -204,7 +214,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; - const int pi_active = part_is_active(pi, e); + const int pi_active = PART_IS_ACTIVE(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])), @@ -220,7 +230,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; - const int pj_active = part_is_active(pj, e); + const int pj_active = PART_IS_ACTIVE(pj, e); const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; @@ -231,7 +241,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -245,6 +255,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, if (pi_active && pj_active) { IACT(r2, dx, hi, hj, pi, pj, a, H); + IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); @@ -252,19 +263,24 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif } else if (pi_active) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -275,13 +291,17 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, dx[2] = -dx[2]; IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -314,11 +334,12 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { TIMER_TIC; /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; + if (!CELL_IS_ACTIVE(c, e)) return; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); const int count = c->hydro.count; struct part *restrict parts = c->hydro.parts; @@ -332,7 +353,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; - const int pi_active = part_is_active(pi, e); + const int pi_active = PART_IS_ACTIVE(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - c->loc[0]), @@ -350,7 +371,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; - const int pj_active = part_is_active(pj, e); + const int pj_active = PART_IS_ACTIVE(pj, e); /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), @@ -362,7 +383,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { const int doi = pi_active && (r2 < hig2); const int doj = pj_active && (r2 < hjg2); -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -374,6 +395,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { if (doi && doj) { IACT(r2, dx, hi, hj, pi, pj, a, H); + IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); @@ -381,19 +403,24 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif } else if (doi) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -404,13 +431,17 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { dx[2] = -dx[2]; IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -442,11 +473,12 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { TIMER_TIC; /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; + if (!CELL_IS_ACTIVE(c, e)) return; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); const int count = c->hydro.count; struct part *restrict parts = c->hydro.parts; @@ -460,7 +492,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; - const int pi_active = part_is_active(pi, e); + const int pi_active = PART_IS_ACTIVE(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - c->loc[0]), @@ -478,7 +510,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; - const int pj_active = part_is_active(pj, e); + const int pj_active = PART_IS_ACTIVE(pj, e); /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), @@ -490,7 +522,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { const int doi = pi_active && ((r2 < hig2) || (r2 < hjg2)); const int doj = pj_active && ((r2 < hig2) || (r2 < hjg2)); -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -502,6 +534,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { if (doi && doj) { IACT(r2, dx, hi, hj, pi, pj, a, H); + IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); @@ -509,19 +542,24 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif } else if (doi) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -532,13 +570,17 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { dx[2] = -dx[2]; IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -581,9 +623,10 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, const int count_j = cj->hydro.count; struct part *restrict parts_j = cj->hydro.parts; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); /* Loop over the parts_i. */ for (int pid = 0; pid < count; pid++) { @@ -617,7 +660,7 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, r2 += dx[k] * dx[k]; } -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -629,13 +672,17 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, pj->h, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, pj->h, pi, pj, mu_0, 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_pressure_floor(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_sink(r2, dx, hi, pj->h, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, pj->h, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, pj->h, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, pj->h, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -678,9 +725,10 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, const int count_j = cj->hydro.count; struct part *restrict parts_j = cj->hydro.parts; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); /* Pick-out the sorted lists. */ const struct sort_entry *sort_j = cell_get_hydro_sorts(cj, sid); @@ -721,7 +769,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, (float)(piz - pjz)}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -733,13 +781,17 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -783,7 +835,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, (float)(piz - pjz)}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -795,13 +847,17 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -843,35 +899,41 @@ void DOPAIR_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci, shift[k] = -e->s->dim[k]; } -#if !defined(SWIFT_USE_NAIVE_INTERACTIONS) /* 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); + 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."); -#endif + /* Is it sorted, if not we use the naive interactions. */ + const int is_sorted = + (cj->hydro.sorted & (1 << sid)) && + (cj->hydro.dx_max_sort_old <= space_maxreldx * cj->dmin); #if defined(SWIFT_USE_NAIVE_INTERACTIONS) - DOPAIR_SUBSET_NAIVE(r, ci, parts_i, ind, count, cj, shift); -#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) - if (sort_is_face(sid)) - runner_dopair_subset_density_vec(r, ci, parts_i, ind, count, cj, sid, - flipped, shift); - else - DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift); + int force_naive = 1; #else - DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift); + int force_naive = 0; #endif + + if (force_naive || !is_sorted) { + DOPAIR_SUBSET_NAIVE(r, ci, parts_i, ind, count, cj, shift); + } else { +#if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) + if (sort_is_face(sid)) + runner_dopair_subset_density_vec(r, ci, parts_i, ind, count, cj, sid, + flipped, shift); + else + DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift); +#else + DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift); +#endif + } } /** @@ -897,9 +959,10 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, TIMER_TIC; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); const int count_i = ci->hydro.count; struct part *restrict parts_j = ci->hydro.parts; @@ -939,7 +1002,7 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -951,13 +1014,17 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -1021,15 +1088,16 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, #ifdef SWIFT_DEBUG_CHECKS /* Some constants used to checks that the parts are in the right frame */ + /* TODO MLADEN: coordinate 2. -> 2.02 with Matthieu */ const float shift_threshold_x = - 2. * ci->width[0] + - 2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + 2.02 * ci->width[0] + + 2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); const float shift_threshold_y = - 2. * ci->width[1] + - 2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + 2.02 * ci->width[1] + + 2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); const float shift_threshold_z = - 2. * ci->width[2] + - 2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + 2.02 * ci->width[2] + + 2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); #endif /* SWIFT_DEBUG_CHECKS */ /* Get some other useful values. */ @@ -1043,11 +1111,12 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, const double dj_min = sort_j[0].d; const float dx_max = (ci->hydro.dx_max_sort + cj->hydro.dx_max_sort); - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); - if (cell_is_active_hydro(ci, e)) { + if (CELL_IS_ACTIVE(ci, e)) { /* Loop over the parts in ci. */ for (int pid = count_i - 1; @@ -1058,7 +1127,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, const float hi = pi->h; /* Skip inactive particles */ - if (!part_is_active(pi, e)) continue; + if (!PART_IS_ACTIVE(pi, e)) continue; /* Is there anything we need to interact with ? */ const double di = sort_i[pid].d + hi * kernel_gamma + dx_max - rshift; @@ -1115,24 +1184,30 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", pjz, ci->width[2]); +#if defined(DO_DRIFT_DEBUG_CHECKS) /* 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 (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); +#endif #endif /* Hit or miss? */ if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -1141,7 +1216,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, } /* loop over the parts in ci. */ } /* Cell ci is active */ - if (cell_is_active_hydro(cj, e)) { + if (CELL_IS_ACTIVE(cj, e)) { /* Loop over the parts in cj. */ for (int pjd = 0; pjd < count_j && sort_j[pjd].d - hj_max - dx_max < di_max; @@ -1152,7 +1227,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, const float hj = pj->h; /* Skip inactive particles */ - if (!part_is_active(pj, e)) continue; + if (!PART_IS_ACTIVE(pj, e)) continue; /* Is there anything we need to interact with ? */ const double dj = sort_j[pjd].d - hj * kernel_gamma - dx_max + rshift; @@ -1209,24 +1284,30 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", pjz, ci->width[2]); +#if defined(DO_DRIFT_DEBUG_CHECKS) /* 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 (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); +#endif #endif /* Hit or miss? */ if (r2 < hjg2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -1255,10 +1336,10 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { if (ci->hydro.count == 0 || cj->hydro.count == 0) return; /* Anything to do here? */ - if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return; + if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return; /* Check that cells are drifted. */ - if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e)) + if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e)) error("Interacting undrifted cells."); /* Get the sort ID. */ @@ -1363,15 +1444,16 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, #ifdef SWIFT_DEBUG_CHECKS /* Some constants used to checks that the parts are in the right frame */ + /* TODO MLADEN: coordinate 2. -> 2.02 with Matthieu */ const float shift_threshold_x = - 2. * ci->width[0] + - 2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + 2.02 * ci->width[0] + + 2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); const float shift_threshold_y = - 2. * ci->width[1] + - 2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + 2.02 * ci->width[1] + + 2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); const float shift_threshold_z = - 2. * ci->width[2] + - 2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + 2.02 * ci->width[2] + + 2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); #endif /* SWIFT_DEBUG_CHECKS */ /* Get some other useful values. */ @@ -1382,9 +1464,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, struct part *restrict parts_i = ci->hydro.parts; struct part *restrict parts_j = cj->hydro.parts; - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); /* Maximal displacement since last rebuild */ const double dx_max = (ci->hydro.dx_max_sort + cj->hydro.dx_max_sort); @@ -1399,22 +1482,22 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, const double shift_j[3] = {cj->loc[0], cj->loc[1], cj->loc[2]}; int count_active_i = 0, count_active_j = 0; - struct sort_entry *restrict sort_active_i = NULL, - *restrict sort_active_j = NULL; + struct sort_entry *restrict sort_active_i = NULL; + struct sort_entry *restrict sort_active_j = NULL; // MATTHIEU: temporary disable this optimization if (0 /*&& cell_is_all_active_hydro(ci, e)*/) { /* If everybody is active don't bother copying */ sort_active_i = sort_i; count_active_i = count_i; - } else if (cell_is_active_hydro(ci, e)) { + } else if (CELL_IS_ACTIVE(ci, e)) { if (posix_memalign((void **)&sort_active_i, SWIFT_CACHE_ALIGNMENT, sizeof(struct sort_entry) * count_i) != 0) error("Failed to allocate active sortlists."); /* Collect the active particles in ci */ for (int k = 0; k < count_i; k++) { - if (part_is_active(&parts_i[sort_i[k].i], e)) { + if (PART_IS_ACTIVE(&parts_i[sort_i[k].i], e)) { sort_active_i[count_active_i] = sort_i[k]; count_active_i++; } @@ -1426,14 +1509,14 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* If everybody is active don't bother copying */ sort_active_j = sort_j; count_active_j = count_j; - } else if (cell_is_active_hydro(cj, e)) { + } else if (CELL_IS_ACTIVE(cj, e)) { if (posix_memalign((void **)&sort_active_j, SWIFT_CACHE_ALIGNMENT, sizeof(struct sort_entry) * count_j) != 0) error("Failed to allocate active sortlists."); /* Collect the active particles in cj */ for (int k = 0; k < count_j; k++) { - if (part_is_active(&parts_j[sort_j[k].i], e)) { + if (PART_IS_ACTIVE(&parts_j[sort_j[k].i], e)) { sort_active_j[count_active_j] = sort_j[k]; count_active_j++; } @@ -1467,7 +1550,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Do we need to only check active parts in cj (i.e. pi does not need updating) ? */ - if (!part_is_active(pi, e)) { + if (!PART_IS_ACTIVE(pi, e)) { /* Loop over the *active* parts in cj within range of pi */ for (int pjd = 0; pjd < count_active_j && sort_active_j[pjd].d < di; @@ -1521,25 +1604,31 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", pjz, ci->width[2]); +#if defined(DO_DRIFT_DEBUG_CHECKS) /* 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 (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); +#endif #endif /* Hit or miss? (note that we will do the other condition in the reverse loop) */ if (r2 < hig2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -1596,20 +1685,23 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", pjz, ci->width[2]); +#if defined(DO_DRIFT_DEBUG_CHECKS) /* 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 (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); +#endif #endif /* Hit or miss? (note that we will do the other condition in the reverse loop) */ if (r2 < hig2) { /* Does pj need to be updated too? */ - if (part_is_active(pj, e)) { + if (PART_IS_ACTIVE(pj, e)) { IACT(r2, dx, hi, hj, pi, pj, a, H); + IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); @@ -1617,18 +1709,23 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif } else { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); @@ -1666,7 +1763,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Do we need to only check active parts in ci (i.e. pj does not need updating) ? */ - if (!part_is_active(pj, e)) { + if (!PART_IS_ACTIVE(pj, e)) { /* Loop over the *active* parts in ci. */ for (int pid = count_active_i - 1; @@ -1721,24 +1818,30 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", pjz, ci->width[2]); +#if defined(DO_DRIFT_DEBUG_CHECKS) /* 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 (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); +#endif #endif /* Hit or miss? (note that we must avoid the r2 < hig2 cases we already processed) */ if (r2 < hjg2 && r2 >= hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -1797,11 +1900,13 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", pjz, ci->width[2]); +#if defined(DO_DRIFT_DEBUG_CHECKS) /* 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 (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); +#endif #endif /* Hit or miss? @@ -1809,8 +1914,9 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, if (r2 < hjg2 && r2 >= hig2) { /* Does pi need to be updated too? */ - if (part_is_active(pi, e)) { + if (PART_IS_ACTIVE(pi, e)) { IACT(r2, dx, hj, hi, pj, pi, a, H); + IACT_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); @@ -1818,18 +1924,23 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif } else { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); @@ -1841,9 +1952,9 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, } /* Loop over all cj */ /* Clean-up if necessary */ // MATTHIEU: temporary disable this optimization - if (cell_is_active_hydro(ci, e)) // && !cell_is_all_active_hydro(ci, e)) + if (CELL_IS_ACTIVE(ci, e)) // && !cell_is_all_active_hydro(ci, e)) free(sort_active_i); - if (cell_is_active_hydro(cj, e)) // && !cell_is_all_active_hydro(cj, e)) + if (CELL_IS_ACTIVE(cj, e)) // && !cell_is_all_active_hydro(cj, e)) free(sort_active_j); TIMER_TOC(TIMER_DOPAIR); @@ -1866,10 +1977,10 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { if (ci->hydro.count == 0 || cj->hydro.count == 0) return; /* Anything to do here? */ - if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return; + if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return; /* Check that cells are drifted. */ - if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e)) + if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e)) error("Interacting undrifted cells."); /* Get the sort ID. */ @@ -1970,14 +2081,15 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { count * sizeof(int)) != 0) error("Failed to allocate indt."); for (int k = 0; k < count; k++) - if (part_is_active(&parts[k], e)) { + if (PART_IS_ACTIVE(&parts[k], e)) { indt[countdt] = k; countdt += 1; } - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); /* Loop over the particles in the cell. */ for (int pid = 0; pid < count; pid++) { @@ -1995,7 +2107,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { const float hig2 = hi * hi * kernel_gamma2; /* Is the ith particle inactive? */ - if (!part_is_active(pi, e)) { + if (!PART_IS_ACTIVE(pi, e)) { /* Loop over the other particles .*/ for (int pjd = firstdt; pjd < countdt; pjd++) { @@ -2004,7 +2116,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { struct part *restrict pj = &parts[indt[pjd]]; const float hj = pj->h; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -2024,13 +2136,17 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { if (r2 < hj * hj * kernel_gamma2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -2063,11 +2179,11 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { r2 += dx[k] * dx[k]; } const int doj = - (part_is_active(pj, e)) && (r2 < hj * hj * kernel_gamma2); + (PART_IS_ACTIVE(pj, e)) && (r2 < hj * hj * kernel_gamma2); const int doi = (r2 < hig2); -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -2082,6 +2198,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { if (doi && doj) { IACT(r2, dx, hi, hj, pi, pj, a, H); + IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); @@ -2089,19 +2206,24 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif } else if (doi) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); @@ -2112,13 +2234,17 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { dx[1] = -dx[1]; dx[2] = -dx[2]; IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); @@ -2150,14 +2276,14 @@ void DOSELF1_BRANCH(struct runner *r, struct cell *c) { if (c->hydro.count == 0) return; /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; + if (!CELL_IS_ACTIVE(c, e)) return; /* Did we mess up the recursion? */ if (c->hydro.h_max_old * kernel_gamma > c->dmin) error("Cell smaller than smoothing length"); /* Check that cells are drifted. */ - if (!cell_are_part_drifted(c, e)) error("Interacting undrifted cell."); + if (!CELL_ARE_PART_DRIFTED(c, e)) error("Interacting undrifted cell."); #if defined(SWIFT_USE_NAIVE_INTERACTIONS) DOSELF1_NAIVE(r, c); @@ -2197,14 +2323,15 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { count * sizeof(int)) != 0) error("Failed to allocate indt."); for (int k = 0; k < count; k++) - if (part_is_active(&parts[k], e)) { + if (PART_IS_ACTIVE(&parts[k], e)) { indt[countdt] = k; countdt += 1; } - /* Cosmological terms */ + /* Cosmological terms and physical constants */ const float a = cosmo->a; const float H = cosmo->H; + GET_MU0(); /* Loop over the particles in the cell. */ for (int pid = 0; pid < count; pid++) { @@ -2222,7 +2349,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { const float hig2 = hi * hi * kernel_gamma2; /* Is the ith particle not active? */ - if (!part_is_active(pi, e)) { + if (!PART_IS_ACTIVE(pi, e)) { /* Loop over the other particles .*/ for (int pjd = firstdt; pjd < countdt; pjd++) { @@ -2239,7 +2366,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { r2 += dx[k] * dx[k]; } -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -2251,13 +2378,17 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); + IACT_NONSYM_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dx, hj, hi, pj, pi, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, t_current, cosmo, with_cosmology); #endif @@ -2290,7 +2421,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { r2 += dx[k] * dx[k]; } -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS) /* 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"); @@ -2302,8 +2433,9 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) { /* Does pj need to be updated too? */ - if (part_is_active(pj, e)) { + if (PART_IS_ACTIVE(pj, e)) { IACT(r2, dx, hi, hj, pi, pj, a, H); + IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); @@ -2311,18 +2443,23 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); #endif } else { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); + IACT_NONSYM_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dx, hi, hj, pi, pj, a, H, + e->sink_properties); #endif #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hi, hj, pi, pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_nonsym_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, t_current, cosmo, with_cosmology); @@ -2354,14 +2491,14 @@ void DOSELF2_BRANCH(struct runner *r, struct cell *c) { if (c->hydro.count == 0) return; /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; + if (!CELL_IS_ACTIVE(c, e)) return; /* Did we mess up the recursion? */ if (c->hydro.h_max_old * kernel_gamma > c->dmin) error("Cell smaller than smoothing length"); /* Check that cells are drifted. */ - if (!cell_are_part_drifted(c, e)) error("Interacting undrifted cell."); + if (!CELL_ARE_PART_DRIFTED(c, e)) error("Interacting undrifted cell."); #if defined(SWIFT_USE_NAIVE_INTERACTIONS) DOSELF2_NAIVE(r, c); @@ -2393,7 +2530,7 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, TIMER_TIC; /* Should we even bother? */ - if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return; + if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return; if (ci->hydro.count == 0 || cj->hydro.count == 0) return; /* Get the type of pair and flip ci/cj if needed. */ @@ -2413,10 +2550,10 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, } /* Otherwise, compute the pair directly. */ - else if (cell_is_active_hydro(ci, e) || cell_is_active_hydro(cj, e)) { + else if (CELL_IS_ACTIVE(ci, e) || CELL_IS_ACTIVE(cj, e)) { /* Make sure both cells are drifted to the current timestep. */ - if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e)) + if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e)) error("Interacting undrifted cells."); /* Do any of the cells need to be sorted first? */ @@ -2452,7 +2589,7 @@ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) { TIMER_TIC; /* Should we even bother? */ - if (ci->hydro.count == 0 || !cell_is_active_hydro(ci, r->e)) return; + if (ci->hydro.count == 0 || !CELL_IS_ACTIVE(ci, r->e)) return; /* Recurse? */ if (cell_can_recurse_in_self_hydro_task(ci)) { @@ -2471,7 +2608,7 @@ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) { else { /* Drift the cell to the current timestep if needed. */ - if (!cell_are_part_drifted(ci, r->e)) error("Interacting undrifted cell."); + if (!CELL_ARE_PART_DRIFTED(ci, r->e)) error("Interacting undrifted cell."); DOSELF1_BRANCH(r, ci); } @@ -2499,7 +2636,7 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, TIMER_TIC; /* Should we even bother? */ - if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return; + if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return; if (ci->hydro.count == 0 || cj->hydro.count == 0) return; /* Get the type of pair and flip ci/cj if needed. */ @@ -2519,10 +2656,10 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, } /* Otherwise, compute the pair directly. */ - else if (cell_is_active_hydro(ci, e) || cell_is_active_hydro(cj, e)) { + else if (CELL_IS_ACTIVE(ci, e) || CELL_IS_ACTIVE(cj, e)) { /* Make sure both cells are drifted to the current timestep. */ - if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e)) + if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e)) error("Interacting undrifted cells."); /* Do any of the cells need to be sorted first? */ @@ -2558,7 +2695,7 @@ void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer) { TIMER_TIC; /* Should we even bother? */ - if (ci->hydro.count == 0 || !cell_is_active_hydro(ci, r->e)) return; + if (ci->hydro.count == 0 || !CELL_IS_ACTIVE(ci, r->e)) return; /* Recurse? */ if (cell_can_recurse_in_self_hydro_task(ci)) { diff --git a/src/runner_doiact_functions_limiter.h b/src/runner_doiact_functions_limiter.h index 68e398a1878be294c781fa2350c53d9481f7ba5b..ee60c7d322457f0abe4044cf8bb2bdd5e115c0ea 100644 --- a/src/runner_doiact_functions_limiter.h +++ b/src/runner_doiact_functions_limiter.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 diff --git a/src/runner_doiact_functions_rt.h b/src/runner_doiact_functions_rt.h deleted file mode 100644 index 1e73acf20b5ea4511646f1c6979f7d9037e57914..0000000000000000000000000000000000000000 --- a/src/runner_doiact_functions_rt.h +++ /dev/null @@ -1,809 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) - * 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) - * - * 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 "rt.h" -#include "rt_active.h" -#include "runner_doiact_rt.h" - -/** - * @brief Function for self-type interaction between stars and hydro particles - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void DOSELF1_RT(struct runner *r, struct cell *c, int timer) { - - TIMER_TIC; - - const struct engine *e = r->e; - - /* Cosmological terms */ - const struct cosmology *cosmo = e->cosmology; - const float a = cosmo->a; - const float H = cosmo->H; - - /* Anything to do here? */ - if (c->hydro.count == 0 || c->stars.count == 0) return; - -#ifdef SWIFT_DEBUG_CHECKS - /* Drift the cell to the current timestep if needed. */ - if (!cell_are_spart_drifted(c, r->e)) - error("Interacting undrifted cell (spart): %lld", c->cellID); - if (!cell_are_part_drifted(c, r->e)) - error("Interacting undrifted cell (part): %lld", c->cellID); -#endif - - struct spart *restrict sparts = c->stars.parts; - struct part *restrict parts = c->hydro.parts; - - const int scount = c->stars.count; - const int count = c->hydro.count; - - /* Loop over the sparts in cell */ - for (int sid = 0; sid < scount; sid++) { - - /* Get a hold of the ith spart in c. */ - struct spart *restrict si = &sparts[sid]; - - /* Skip inhibited particles. */ - if (spart_is_inhibited(si, e)) continue; - - /* Skip inactive particles */ - if (!rt_is_spart_active_in_loop(si, e)) continue; - - const float hi = si->h; - const float hig2 = hi * hi * kernel_gamma2; - const float six[3] = {(float)(si->x[0] - c->loc[0]), - (float)(si->x[1] - c->loc[1]), - (float)(si->x[2] - c->loc[2])}; - - /* Loop over the parts in cell */ - for (int pid = 0; pid < count; pid++) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts[pid]; - const float hj = pj->h; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pj, e)) continue; - - /* Skip inactive particles. */ - if (!rt_is_part_active_in_loop(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"); - - if (r2 < hig2 && e->rt_props->hydro_controlled_injection) { - - const integertime_t ti_current = e->ti_current; - const integertime_t pti_end = - get_integer_time_end(ti_current, pj->time_bin); - const integertime_t sti_end = - get_integer_time_end(ti_current, si->time_bin); - - if (sti_end < ti_current) - error( - "s-particle in an impossible time-zone! sp->ti_end=%lld " - "e->ti_current=%lld", - sti_end, ti_current); - if (pti_end < ti_current) - error( - "particle in an impossible time-zone! p->ti_end=%lld " - "e->ti_current=%lld", - pti_end, ti_current); - if (pti_end > sti_end) - message( - "WARNING: Got star that whose time step ends before the " - "interacting " - "hydro particle's time step ends. This needs to be dealt with."); - } -#endif - - if (r2 < hig2) { - IACT_RT(r2, dx, hi, hj, si, pj, a, H); - } - } - } - - if (timer) TIMER_TOC(TIMER_DOSELF_RT); -} - -/** - * @brief Function for non-symmetric pair-type interaction between stars - * and hydro particles. Will interact star particles of cell i - * with hydro particles of cell j. - * - * - * @param r runner task - * @param ci the first cell, where we take star particles from - * @param cj the second cell, where we take hydro particles from - */ -void DOPAIR1_NONSYM_RT_NAIVE(struct runner *r, struct cell *ci, - struct cell *cj) { - - TIMER_TIC; - - const struct engine *e = r->e; - - /* Cosmological terms */ - const struct cosmology *cosmo = e->cosmology; - 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; - - /* 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 inhibited particles. */ - if (spart_is_inhibited(si, e)) continue; - - /* Skip inactive particles */ - if (!rt_is_spart_active_in_loop(si, e)) continue; - - const float hi = si->h; - const float hig2 = hi * hi * kernel_gamma2; - const float six[3] = {(float)(si->x[0] - (cj->loc[0] + shift[0])), - (float)(si->x[1] - (cj->loc[1] + shift[1])), - (float)(si->x[2] - (cj->loc[2] + shift[2]))}; - - /* Loop over the parts in cj. */ - for (int pjd = 0; pjd < count_j; pjd++) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts_j[pjd]; - const float hj = pj->h; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pj, e)) continue; - - /* Skip inactive particles. */ - if (!rt_is_part_active_in_loop(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 RT_DEBUG - if (r2 < hig2 && e->rt_props->hydro_controlled_injection) { - - const integertime_t ti_current = e->ti_current; - const integertime_t pti_end = - get_integer_time_end(ti_current, pj->time_bin); - const integertime_t sti_end = - get_integer_time_end(ti_current, si->time_bin); - - if (sti_end < ti_current) - error( - "s-particle in an impossible time-zone! sp->ti_end=%lld " - "e->ti_current=%lld", - sti_end, ti_current); - if (pti_end < ti_current) - error( - "particle in an impossible time-zone! p->ti_end=%lld " - "e->ti_current=%lld", - pti_end, ti_current); - if (pti_end > sti_end) - message( - "WARNING: Got star that whose time step ends before the " - "interacting " - "hydro particle's time step ends. This needs to be dealt with."); - } -#endif - - if (r2 < hig2) { - IACT_RT(r2, dx, hi, hj, si, pj, a, H); - } - - } /* loop over the parts in cj. */ - } /* loop over the parts in ci. */ -} - -/** - * @brief Function for pair-type interaction between stars - * and hydro particles. Will interact hydro particles of cell i - * with star particles of cell j. - * - * @param r #runner task - * @param ci the first #cell - * @param cj the second #cell - * @param timer 1 if the time is to be recorded. - */ -void DO_SYM_PAIR1_RT(struct runner *r, struct cell *ci, struct cell *cj, - const int sid, const double *shift) { - - TIMER_TIC; - - const struct engine *e = r->e; - - /* Cosmological terms */ - const struct cosmology *cosmo = e->cosmology; - 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]; - - const int do_ci_stars = - (ci->nodeID == e->nodeID) && rt_should_iact_cell_pair(ci, cj, e); - const int do_cj_stars = - (cj->nodeID == e->nodeID) && rt_should_iact_cell_pair(cj, ci, e); - - if (do_ci_stars) { - - /* Pick-out the sorted lists. */ - const struct sort_entry *restrict sort_j = cell_get_hydro_sorts(cj, sid); - const struct sort_entry *restrict sort_i = cell_get_stars_sorts(ci, sid); - - /* 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; - const double dj_min = sort_j[0].d; - const float dx_max = (ci->stars.dx_max_sort + cj->hydro.dx_max_sort); - const float hydro_dx_max_rshift = cj->hydro.dx_max_sort - rshift; - - /* TODO: maybe change the order of the loops for better performance? */ - /* 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 spart in ci. */ - struct spart *restrict spi = &sparts_i[sort_i[pid].i]; - const float hi = spi->h; - - /* Skip inhibited particles */ - if (spart_is_inhibited(spi, e)) continue; - - /* Skip inactive particles */ - if (!rt_is_spart_active_in_loop(spi, e)) 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 + hydro_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]; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pj, e)) continue; - - /* Skip inactive particles. */ - if (!rt_is_part_active_in_loop(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 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"); - - if (r2 < hig2 && e->rt_props->hydro_controlled_injection) { - - const integertime_t ti_current = e->ti_current; - const integertime_t pti_end = - get_integer_time_end(ti_current, pj->time_bin); - const integertime_t sti_end = - get_integer_time_end(ti_current, spi->time_bin); - - if (sti_end < ti_current) - error( - "s-particle in an impossible time-zone! sp->ti_end=%lld " - "e->ti_current=%lld", - sti_end, ti_current); - if (pti_end < ti_current) - error( - "particle in an impossible time-zone! p->ti_end=%lld " - "e->ti_current=%lld", - pti_end, ti_current); - if (pti_end > sti_end) - message( - "WARNING: Got star that whose time step ends before the " - "interacting " - "hydro particle's time step ends. This needs to be dealt " - "with."); - } - -#endif - - /* Hit or miss? */ - if (r2 < hig2) { - IACT_RT(r2, dx, hi, hj, spi, pj, a, H); - } - } /* 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 = cell_get_hydro_sorts(ci, sid); - const struct sort_entry *restrict sort_j = cell_get_stars_sorts(cj, sid); - - /* Get some other useful values. */ - const double hj_max = cj->stars.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 spart *restrict sparts_j = cj->stars.parts; - const double di_max = sort_i[count_i - 1].d - rshift; - const float dx_max = (ci->hydro.dx_max_sort + cj->stars.dx_max_sort); - const float hydro_dx_max_rshift = ci->hydro.dx_max_sort - rshift; - - /* TODO: maybe change the order of the loops for better performance? */ - /* Loop over the sparts 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 spart in cj. */ - struct spart *spj = &sparts_j[sort_j[pjd].i]; - const float hj = spj->h; - - /* Skip inhibited particles */ - if (spart_is_inhibited(spj, e)) continue; - - /* Skip inactive particles */ - if (!rt_is_spart_active_in_loop(spj, e)) 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 - hydro_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]; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pi, e)) continue; - - /* Skip inactive particles. */ - if (!rt_is_part_active_in_loop(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 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"); - - if (r2 < hjg2 && e->rt_props->hydro_controlled_injection) { - - const integertime_t ti_current = e->ti_current; - const integertime_t pti_end = - get_integer_time_end(ti_current, pi->time_bin); - const integertime_t sti_end = - get_integer_time_end(ti_current, spj->time_bin); - - if (sti_end < ti_current) - error( - "s-particle in an impossible time-zone! sp->ti_end=%lld " - "e->ti_current=%lld", - sti_end, ti_current); - if (pti_end < ti_current) - error( - "particle in an impossible time-zone! p->ti_end=%lld " - "e->ti_current=%lld", - pti_end, ti_current); - if (pti_end > sti_end) - message( - "WARNING: Got star that whose time step ends before the " - "interacting " - "hydro particle's time step ends. This needs to be dealt " - "with."); - } -#endif - - /* Hit or miss? */ - if (r2 < hjg2) { - IACT_RT(r2, dx, hj, hi, spj, pi, a, H); - } - } /* loop over the parts in ci. */ - } /* loop over the parts in cj. */ - } /* Cell cj is active */ - - TIMER_TOC(TIMER_DOPAIR_RT); -} - -/** - * @brief Function for pair-type interaction between stars - * and hydro particles. Will interact hydro particles of cell i - * with star particles of cell j. - * - * @param r #runner task - * @param ci the first #cell - * @param cj the second #cell - * @param timer 1 if the time is to be recorded. - */ -void DOPAIR1_RT_NAIVE(struct runner *r, struct cell *ci, struct cell *cj, - int timer) { - - TIMER_TIC; - const struct engine *restrict e = r->e; - - const int do_stars_in_ci = - (cj->nodeID == r->e->nodeID) && rt_should_iact_cell_pair(ci, cj, e); - if (do_stars_in_ci) DOPAIR1_NONSYM_RT_NAIVE(r, ci, cj); - - const int do_stars_in_cj = - (ci->nodeID == r->e->nodeID) && rt_should_iact_cell_pair(cj, ci, e); - if (do_stars_in_cj) DOPAIR1_NONSYM_RT_NAIVE(r, cj, ci); - - if (timer) TIMER_TOC(TIMER_DOPAIR_RT); -} - -/** - * @brief Determine which version of DOSELF1_RT needs to be called - * - * @param r #runner - * @param c #cell c - * @param timer 1 if the time is to be recorded. - */ -void DOSELF1_BRANCH_RT(struct runner *r, struct cell *c, int timer) { - -#ifdef RT_DEBUG - /* Before an early exit, loop over all parts and sparts in this cell - * and mark that we checked these particles */ - - const struct engine *e = r->e; - - if (c->hydro.count > 0) { - struct part *restrict parts = c->hydro.parts; - - /* Loop over the parts in cell */ - for (int pid = 0; pid < c->hydro.count; pid++) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts[pid]; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pj, e)) continue; - - /* Skip inactive particles. */ - if (!rt_is_part_active_in_loop(pj, e)) continue; - - rt_debugging_check_injection_part(pj, e->rt_props); - } - } - - if (c->stars.count > 0) { - struct spart *restrict sparts = c->stars.parts; - - /* Loop over the parts in cell */ - for (int sid = 0; sid < c->stars.count; sid++) { - - /* Get a pointer to the ith spart. */ - struct spart *restrict si = &sparts[sid]; - - /* Skip inhibited particles. */ - if (spart_is_inhibited(si, e)) continue; - - /* Skip inactive particles */ - if (!rt_is_spart_active_in_loop(si, e)) continue; - - rt_debugging_check_injection_spart(si, e->rt_props); - } - } -#endif - - /* early exit? */ - if (!rt_should_iact_cell(c, r->e)) return; - DOSELF1_RT(r, c, timer); -} - -/** - * @brief Determine which version of DOPAIR1_RT needs to be called - * - * @param r #runner - * @param ci The first #cell - * @param cj The second #cell - * @param timer 1 if the time is to be recorded. - */ -void DOPAIR1_BRANCH_RT(struct runner *r, struct cell *ci, struct cell *cj, - int timer) { - - 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 do_stars_ci = - (cj->nodeID == r->e->nodeID) && rt_should_iact_cell_pair(ci, cj, e); - const int do_stars_cj = - (ci->nodeID == r->e->nodeID) && rt_should_iact_cell_pair(cj, ci, e); - - /* Anything to do here? */ - if (!do_stars_ci && !do_stars_cj) return; - -#ifdef SWIFT_DEBUG_CHECKS - if (do_stars_ci) { - - /* Check that cells are drifted. */ - if (!cell_are_spart_drifted(ci, e)) - error("Interacting undrifted stars in cells i=%12lld, j=%12lld", - ci->cellID, cj->cellID); - if (!cell_are_part_drifted(cj, e)) - error("Interacting undrifted hydro in cells i=%12lld, j=%12lld", - ci->cellID, cj->cellID); - - /* Have the cells been sorted? */ - if (!(ci->stars.sorted & (1 << sid)) || - ci->stars.dx_max_sort_old > space_maxreldx * ci->dmin) - error("Interacting unsorted cells: %lld %d", ci->cellID, sid); - if (!(cj->hydro.sorted & (1 << sid)) || - cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin) - error("Interacting unsorted cells: %lld %lld", ci->cellID, cj->cellID); - } - - if (do_stars_cj) { - - /* Check that cells are drifted. */ - if (!cell_are_spart_drifted(cj, e)) { - error("Interacting undrifted stars in cells j=%12lld, i=%12lld", - cj->cellID, ci->cellID); - } - if (!cell_are_part_drifted(ci, e)) - error("Interacting undrifted hydro in cells j=%12lld, i=%12lld", - cj->cellID, ci->cellID); - - /* Have the cells been sorted? */ - if (!(ci->hydro.sorted & (1 << sid)) || - ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin) - error("Interacting unsorted cells: %lld %d", cj->cellID, sid); - - if ((!(cj->stars.sorted & (1 << sid)) || - cj->stars.dx_max_sort_old > space_maxreldx * cj->dmin)) - error("Interacting unsorted cells: %lld %lld", ci->cellID, cj->cellID); - } -#endif /* SWIFT_DEBUG_CHECKS */ - - if (do_stars_ci || do_stars_cj) { -#ifdef SWIFT_USE_NAIVE_INTERACTIONS_RT - DOPAIR1_RT_NAIVE(r, ci, cj, 1); -#else - DO_SYM_PAIR1_RT(r, ci, cj, sid, shift); -#endif - } -} - -/** - * @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_RT(struct runner *r, struct cell *c, int timer) { - - TIMER_TIC; - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != engine_rank) - error("This function should not be called on foreign cells"); -#endif - - /* Should we even bother? */ - if (!rt_should_iact_cell(c, r->e)) { - -#ifdef RT_DEBUG - /* Before an early exit, loop over all parts and sparts in this cell - * and mark that we checked these particles */ - - const struct engine *e = r->e; - - if (c->hydro.count > 0) { - struct part *restrict parts = c->hydro.parts; - const int count = c->hydro.count; - - /* Loop over the parts in cell */ - for (int pid = 0; pid < count; pid++) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts[pid]; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pj, e)) continue; - - /* Skip inactive particles. */ - if (!rt_is_part_active_in_loop(pj, e)) continue; - - rt_debugging_check_injection_part(pj, e->rt_props); - } - } - - if (c->stars.count > 0) { - struct spart *restrict sparts = c->stars.parts; - const int scount = c->stars.count; - - /* Loop over the parts in cell */ - for (int sid = 0; sid < scount; sid++) { - - /* Get a pointer to the ith spart. */ - struct spart *restrict si = &sparts[sid]; - - /* Skip inhibited particles. */ - if (spart_is_inhibited(si, e)) continue; - - /* Skip inactive particles */ - if (!rt_is_spart_active_in_loop(si, e)) continue; - - rt_debugging_check_injection_spart(si, e->rt_props); - } - } -#endif - - /* exit early if there is nothing to do */ - return; - } - - /* Recurse? */ - if (cell_can_recurse_in_self_stars_task(c)) { - - /* Loop over all progeny. */ - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) { - DOSUB_SELF1_RT(r, c->progeny[k], 0); - for (int j = k + 1; j < 8; j++) - if (c->progeny[j] != NULL) - DOSUB_PAIR1_RT(r, c->progeny[k], c->progeny[j], 0); - } - } - - /* Otherwise, compute self-interaction. */ - else { - DOSELF1_BRANCH_RT(r, c, 0); - } - - if (timer) TIMER_TOC(TIMER_DOSUB_SELF_RT); -} - -/** - * @brief Compute grouped sub-cell interactions for pair tasks - * - * @param r The #runner. - * @param ci The first #cell. - * @param cj The second #cell. - * @param gettimer Do we have a timer ? - */ -void DOSUB_PAIR1_RT(struct runner *r, struct cell *ci, struct cell *cj, - int timer) { - - TIMER_TIC; - - struct space *s = r->e->s; - const struct engine *e = r->e; - - /* Should we even bother? */ - const int should_do_ci = rt_should_iact_cell_pair(ci, cj, e); - const int should_do_cj = rt_should_iact_cell_pair(cj, ci, 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_RT(r, ci->progeny[pid], cj->progeny[pjd], 0); - } - } - - /* Otherwise, compute the pair directly. */ - else { - - /* do full checks again, space_getsid() might swap ci/cj pointers */ - const int do_ci_stars = - (cj->nodeID == e->nodeID) && rt_should_iact_cell_pair(ci, cj, e); - const int do_cj_stars = - (ci->nodeID == e->nodeID) && rt_should_iact_cell_pair(cj, ci, e); - - if (do_ci_stars || do_cj_stars) DOPAIR1_BRANCH_RT(r, ci, cj, 0); - } - - if (timer) TIMER_TOC(TIMER_DOSUB_PAIR_RT); -} diff --git a/src/runner_doiact_functions_sinks.h b/src/runner_doiact_functions_sinks.h deleted file mode 100644 index a0188a5b82473db18f67f392bac9b2628987deb9..0000000000000000000000000000000000000000 --- a/src/runner_doiact_functions_sinks.h +++ /dev/null @@ -1,751 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -/* Before including this file, define FUNCTION, which is the - name of the interaction function. This creates the interaction functions - runner_dopair_FUNCTION, runner_dopair_FUNCTION_naive, runner_doself_FUNCTION, - and runner_dosub_FUNCTION calling the pairwise interaction function - runner_iact_FUNCTION. */ - -#include "active.h" -#include "runner_doiact_sinks.h" - -/** - * @brief Calculate the number density of #part around the #sink - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void DOSELF1_SINKS(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 - - const struct engine *e = r->e; - const struct cosmology *cosmo = e->cosmology; - - /* Anything to do here? */ - if (c->hydro.count == 0 || c->sinks.count == 0) return; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - if (!cell_is_active_hydro(c, e)) return; -#else - if (!cell_is_active_sink(c, e)) return; -#endif - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - const int scount = c->sinks.count; - const int count = c->hydro.count; - struct sink *restrict sinks = c->sinks.parts; - struct part *restrict parts = c->hydro.parts; - - /* Loop over the particles in ci. */ - /* TODO Loic try to switch loops order for accretion */ - for (int pjd = 0; pjd < count; pjd++) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts[pjd]; - const float hj = pj->h; - - /* Early abort? */ - if (part_is_inhibited(pj, e)) continue; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - /* Skip inactive particles */ - if (!part_is_active(pj, e)) continue; -#endif - - 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])}; - - /* Loop over the sinks in cj. */ - for (int sid = 0; sid < scount; sid++) { - - /* Get a hold of the ith sink in ci. */ - struct sink *restrict si = &sinks[sid]; - - /* Early abort? */ - if (sink_is_inhibited(si, e)) continue; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_ACCRETION) - /* Skip inactive particles */ - if (!sink_is_active(si, e)) continue; -#endif - - const float ri = si->r_cut; - const float ri2 = ri * ri; - - 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])}; - - /* Compute the pairwise distance. */ - 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"); - - if (si->ti_drift != e->ti_current) - error("Particle si not drifted to current time"); -#endif - - if (r2 < ri2) { - IACT_SINK(r2, dx, ri, hj, si, pj, a, H); - } - } /* loop over the sinks in ci. */ - } /* loop over the parts in ci. */ -} - -/** - * @brief Calculate the number density of cj #part around the ci #sink - * - * @param r runner task - * @param ci The first #cell - * @param cj The second #cell - */ -void DO_NONSYM_PAIR1_SINKS_NAIVE(struct runner *r, struct cell *restrict ci, - struct cell *restrict cj) { - -#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 struct cosmology *cosmo = e->cosmology; - - /* Anything to do here? */ - if (cj->hydro.count == 0 || ci->sinks.count == 0) return; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - if (!cell_is_active_hydro(ci, e)) return; -#else - if (!cell_is_active_sink(ci, e)) return; -#endif - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - const int scount_i = ci->sinks.count; - const int count_j = cj->hydro.count; - struct sink *restrict sinks_i = ci->sinks.parts; - struct part *restrict parts_j = cj->hydro.parts; - - /* 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 parts in cj. */ - /* TODO loic try switch loop order for accretion */ - for (int pjd = 0; pjd < count_j; pjd++) { - - /* Get a pointer to the jth particle. */ - struct part *restrict pj = &parts_j[pjd]; - const float hj = pj->h; - - /* Skip inhibited particles. */ - if (part_is_inhibited(pj, e)) continue; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - /* Skip inactive particles */ - if (!part_is_active(pj, e)) continue; -#endif - - 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])}; - - /* Loop over the sinks in ci. */ - for (int sid = 0; sid < scount_i; sid++) { - - /* Get a hold of the ith sink in ci. */ - struct sink *restrict si = &sinks_i[sid]; - - /* Skip inhibited particles. */ - if (sink_is_inhibited(si, e)) continue; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_ACCRETION) - /* Skip inactive particles */ - if (!sink_is_active(si, e)) continue; -#endif - - /* Get the radius */ - const float ri = si->r_cut; - const float ri2 = ri * ri; - - /* Compute the pairwise distance. */ - 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]))}; - 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"); - - if (si->ti_drift != e->ti_current) - error("Particle si not drifted to current time"); -#endif - - if (r2 < ri2) { - IACT_SINK(r2, dx, ri, hj, si, pj, a, H); - } - } /* 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_SINKS(struct runner *r, struct cell *ci, struct cell *cj, - const int sid, const double *shift) { - - const struct engine *e = r->e; - 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_FORMATION) - const int do_ci_sinks = (ci->nodeID == e->nodeID) && (ci->sinks.count != 0) && - (cj->hydro.count != 0) && cell_is_active_hydro(ci, e); - const int do_cj_sinks = (cj->nodeID == e->nodeID) && (cj->sinks.count != 0) && - (ci->hydro.count != 0) && cell_is_active_hydro(cj, e); -#else - const int do_ci_sinks = (ci->nodeID == e->nodeID) && (ci->sinks.count != 0) && - (cj->hydro.count != 0) && cell_is_active_sink(ci, e); - const int do_cj_sinks = (cj->nodeID == e->nodeID) && (cj->sinks.count != 0) && - (ci->hydro.count != 0) && cell_is_active_sink(cj, e); -#endif - - if (do_ci_sinks) { - - /* Pick-out the sorted lists. */ - const struct sort_entry *restrict sort_j = cell_get_hydro_sorts(cj, 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->sinks.dx_max_part, cj->hydro.dx_max_part); - const float shift_threshold_y = - 2. * ci->width[1] + - 2. * max(ci->sinks.dx_max_part, cj->hydro.dx_max_part); - const float shift_threshold_z = - 2. * ci->width[2] + - 2. * max(ci->sinks.dx_max_part, cj->hydro.dx_max_part); -#endif /* SWIFT_DEBUG_CHECKS */ - - /* Get some other useful values. */ - const int count_i = ci->sinks.count; - const int count_j = cj->hydro.count; - struct sink *restrict sinks_i = ci->sinks.parts; - struct part *restrict parts_j = cj->hydro.parts; - const double dj_min = sort_j[0].d; - const float hydro_dx_max_rshift = cj->hydro.dx_max_sort - rshift; - - /* Loop over the sinks in ci. */ - for (int i = 0; i < count_i; i++) { - - /* Get a hold of the ith part in ci. */ - struct sink *restrict spi = &sinks_i[i]; - const float ri = spi->r_cut; - - /* Skip inhibited particles */ - if (sink_is_inhibited(spi, e)) continue; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_ACCRETION) - /* Skip inactive particles */ - if (!sink_is_active(spi, e)) continue; -#endif - - /* 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 + ri + hydro_dx_max_rshift; - if (di < dj_min) continue; - - /* Get some additional information about pi */ - const float ri2 = ri * ri; - 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]; - - /* Early abort? */ - if (part_is_inhibited(pj, e)) continue; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - /* Skip inactive particles. */ - if (!part_is_active(pj, e)) continue; -#endif - - 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 < ri2) { - IACT_SINK(r2, dx, ri, hj, spi, pj, a, H); - } - } /* loop over the parts in cj. */ - } /* loop over the parts in ci. */ - } /* do_ci_sinks */ - - if (do_cj_sinks) { - /* Pick-out the sorted lists. */ - const struct sort_entry *restrict sort_i = cell_get_hydro_sorts(ci, 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->sinks.dx_max_part); - const float shift_threshold_y = - 2. * ci->width[1] + - 2. * max(ci->hydro.dx_max_part, cj->sinks.dx_max_part); - const float shift_threshold_z = - 2. * ci->width[2] + - 2. * max(ci->hydro.dx_max_part, cj->sinks.dx_max_part); -#endif /* SWIFT_DEBUG_CHECKS */ - - /* Get some other useful values. */ - const int count_i = ci->hydro.count; - const int count_j = cj->sinks.count; - struct part *restrict parts_i = ci->hydro.parts; - struct sink *restrict sinks_j = cj->sinks.parts; - const double di_max = sort_i[count_i - 1].d - rshift; - const float hydro_dx_max_rshift = ci->hydro.dx_max_sort - rshift; - - /* Loop over the sinks in cj. */ - for (int j = 0; j < count_j; j++) { - - /* Get a hold of the jth part in cj. */ - struct sink *spj = &sinks_j[j]; - const float rj = spj->r_cut; - - /* Skip inhibited particles */ - if (sink_is_inhibited(spj, e)) continue; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_ACCRETION) - /* Skip inactive particles */ - if (!sink_is_active(spj, e)) continue; -#endif - - /* 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 - rj - hydro_dx_max_rshift; - if (dj - rshift > di_max) continue; - - /* Get some additional information about pj */ - const float rj2 = rj * rj; - 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]; - - /* Early abort? */ - if (part_is_inhibited(pi, e)) continue; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - /* Skip inactive particles. */ - if (!part_is_active(pi, e)) continue; -#endif - - 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 < rj2) { - - IACT_SINK(r2, dx, rj, hi, spj, pi, a, H); - } - } /* loop over the parts in ci. */ - } /* loop over the parts in cj. */ - } /* Cell cj is active */ -} - -void DOPAIR1_SINKS_NAIVE(struct runner *r, struct cell *restrict ci, - struct cell *restrict cj, int timer) { - - const int do_ci_sinks = ci->nodeID == r->e->nodeID; - const int do_cj_sinks = cj->nodeID == r->e->nodeID; - if (do_ci_sinks && ci->sinks.count != 0 && cj->hydro.count != 0) - DO_NONSYM_PAIR1_SINKS_NAIVE(r, ci, cj); - if (do_cj_sinks && cj->sinks.count != 0 && ci->hydro.count != 0) - DO_NONSYM_PAIR1_SINKS_NAIVE(r, cj, ci); -} - -/** - * @brief Determine which version of DOSELF1_SINKS needs to be called depending - * on the optimisation level. - * - * @param r #runner - * @param c #cell c - * - */ -void DOSELF1_BRANCH_SINKS(struct runner *r, struct cell *c) { - - const struct engine *restrict e = r->e; - - /* Anything to do here? */ - if (c->sinks.count == 0) return; - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; -#else - if (!cell_is_active_sink(c, e)) return; -#endif - - /* Did we mess up the recursion? */ - if (c->sinks.r_cut_max_old > c->dmin) - error("Cell smaller than the cut off radius"); - - DOSELF1_SINKS(r, c, 1); -} - -/** - * @brief Determine which version of DOPAIR1_SINKS needs to be called depending - * on the orientation of the cells or whether DOPAIR1_SINKS needs to be called - * at all. - * - * @param r #runner - * @param ci #cell ci - * @param cj #cell cj - * - */ -void DOPAIR1_BRANCH_SINKS(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); - -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - const int ci_active = cell_is_active_hydro(ci, e); - const int cj_active = cell_is_active_hydro(cj, e); -#else - const int ci_active = cell_is_active_sink(ci, e); - const int cj_active = cell_is_active_sink(cj, e); -#endif - const int do_ci_sinks = ci->nodeID == e->nodeID; - const int do_cj_sinks = cj->nodeID == e->nodeID; - const int do_ci = (ci->sinks.count != 0 && cj->hydro.count != 0 && - ci_active && do_ci_sinks); - const int do_cj = (cj->sinks.count != 0 && ci->hydro.count != 0 && - cj_active && do_cj_sinks); - - /* Anything to do here? */ - if (!do_ci && !do_cj) return; - - /* Check that cells are drifted. */ - if (do_ci && (!cell_are_sink_drifted(ci, e) || !cell_are_part_drifted(cj, e))) - error("Interacting undrifted cells."); - - /* Have the cells been sorted? */ - 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_sink_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."); - -#ifdef SWIFT_USE_NAIVE_INTERACTIONS_SINKS - DOPAIR1_SINKS_NAIVE(r, ci, cj, 1); -#else - DO_SYM_PAIR1_SINKS(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_SINKS(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? */ -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - const int should_do_ci = ci->sinks.count != 0 && cj->hydro.count != 0 && - cell_is_active_hydro(ci, e); - const int should_do_cj = cj->sinks.count != 0 && ci->hydro.count != 0 && - cell_is_active_hydro(cj, e); -#else - const int should_do_ci = ci->sinks.count != 0 && cj->hydro.count != 0 && - cell_is_active_sink(ci, e); - const int should_do_cj = cj->sinks.count != 0 && ci->hydro.count != 0 && - cell_is_active_sink(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_sinks_task(ci, cj) && - cell_can_recurse_in_pair_sinks_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_SINKS(r, ci->progeny[pid], cj->progeny[pjd], 0); - } - } - - /* Otherwise, compute the pair directly. */ - else { - - const int do_ci_sinks = ci->nodeID == e->nodeID; - const int do_cj_sinks = cj->nodeID == e->nodeID; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - const int do_ci = ci->sinks.count != 0 && cj->hydro.count != 0 && - cell_is_active_hydro(ci, e) && do_ci_sinks; - const int do_cj = cj->sinks.count != 0 && ci->hydro.count != 0 && - cell_is_active_hydro(cj, e) && do_cj_sinks; -#else - const int do_ci = ci->sinks.count != 0 && cj->hydro.count != 0 && - cell_is_active_sink(ci, e) && do_ci_sinks; - const int do_cj = cj->sinks.count != 0 && ci->hydro.count != 0 && - cell_is_active_sink(cj, e) && do_cj_sinks; -#endif - - if (do_ci) { - - /* Make sure both cells are drifted to the current timestep. */ - if (!cell_are_sink_drifted(ci, e)) - error("Interacting undrifted cells (sinks)."); - - if (!cell_are_part_drifted(cj, e)) - error("Interacting undrifted cells (parts)."); - - /* Do any of the cells need to be sorted first? */ - 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_sink_drifted(cj, e)) - error("Interacting undrifted cells (sinks)."); - - /* 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 (do_ci || do_cj) DOPAIR1_BRANCH_SINKS(r, ci, cj); - } -} - -/** - * @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_SINKS(struct runner *r, struct cell *ci, int gettimer) { - -#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 (FUNCTION_TASK_LOOP == TASK_LOOP_FORMATION) - if (ci->hydro.count == 0 || ci->sinks.count == 0 || - !cell_is_active_hydro(ci, r->e)) - return; -#else - if (ci->hydro.count == 0 || ci->sinks.count == 0 || - !cell_is_active_sink(ci, r->e)) - return; -#endif - - /* Recurse? */ - if (cell_can_recurse_in_self_sinks_task(ci)) { - - /* Loop over all progeny. */ - for (int k = 0; k < 8; k++) - if (ci->progeny[k] != NULL) { - DOSUB_SELF1_SINKS(r, ci->progeny[k], 0); - for (int j = k + 1; j < 8; j++) - if (ci->progeny[j] != NULL) - DOSUB_PAIR1_SINKS(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_sink_drifted(ci, r->e)) error("Interacting undrifted cell."); - - DOSELF1_BRANCH_SINKS(r, ci); - } -} diff --git a/src/runner_doiact_functions_sinks_merger.h b/src/runner_doiact_functions_sinks_merger.h deleted file mode 100644 index b7df7795495bf0d8ddc3c98b82636fc8c41040ea..0000000000000000000000000000000000000000 --- a/src/runner_doiact_functions_sinks_merger.h +++ /dev/null @@ -1,337 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -/* Before including this file, define FUNCTION, which is the - name of the interaction function. - This creates the required interaction functions. */ - -#include "active.h" -#include "runner_doiact_sinks_merger.h" - -/** - * @brief Merge the sink particles that are too close to each other. - * - * @param r runner task - * @param c cell - */ -void DOSELF1_SINKS_MERGER(struct runner *r, struct cell *c) { - -#ifdef SWIFT_DEBUG_CHECKS - if (c->nodeID != engine_rank) error("Should be run on a different node"); -#endif - - const struct engine *e = r->e; - const struct cosmology *cosmo = e->cosmology; - - /* Anything to do here? */ - if (c->sinks.count == 0) return; - if (!cell_is_active_sinks(c, e)) return; - - /* Did we mess up the recursion? */ - if (c->sinks.r_cut_max_old > c->dmin) - error("Cell smaller than the cut off radius"); - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - const int scount = c->sinks.count; - struct sink *restrict sinks = c->sinks.parts; - - /* Loop over the sinks in ci. */ - for (int sid = 0; sid < scount; sid++) { - - /* Get a hold of the ith sink in ci. */ - struct sink *restrict si = &sinks[sid]; - - /* Skip inhibited particles */ - if (sink_is_inhibited(si, e)) continue; - - const float ri = si->r_cut; - const float ri2 = ri * ri; - 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 sinks in cj. */ - for (int sjd = sid + 1; sjd < scount; sjd++) { - - /* Get a pointer to the jth particle. */ - struct sink *restrict sj = &sinks[sjd]; - - /* Early abort? */ - if (!sink_is_active(si, e) && !sink_is_active(sj, e)) continue; - if (sink_is_inhibited(sj, e)) continue; - - /* Get the cutoff radius */ - const float rj = sj->r_cut; - const float rj2 = rj * rj; - - /* Compute the pairwise distance. */ - const float sjx[3] = {(float)(sj->x[0] - c->loc[0]), - (float)(sj->x[1] - c->loc[1]), - (float)(sj->x[2] - c->loc[2])}; - float dx[3] = {six[0] - sjx[0], six[1] - sjx[1], six[2] - sjx[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 (sj->ti_drift != e->ti_current) - error("Particle sj not drifted to current time"); - if (si->ti_drift != e->ti_current) - error("Particle si not drifted to current time"); -#endif - if (r2 < ri2 || r2 < rj2) { - enum sink_merger_remove remove = - IACT_SINK_MERGER(r2, dx, ri, rj, si, sj, a, H); - - /* Remove the particle. */ - switch (remove) { - case sink_merger_remove_none: - break; - case sink_merger_remove_first: - cell_remove_sink(e, c, si); - break; - case sink_merger_remove_second: - cell_remove_sink(e, c, sj); - break; - default: - error("Unknown value, please check your iact function."); - } - - /* Can we continue the loop on the current particle? */ - if (remove == sink_merger_remove_first) { - break; - } - } - } /* loop over the sinks in ci. */ - } /* loop over the sinks in ci. */ -} - -/** - * @brief Merge the sink particles that are too close to each other. - * - * @param r runner task - * @param ci The first #cell - * @param cj The second #cell - */ -void DO_SYM_PAIR1_SINKS_MERGER(struct runner *r, struct cell *restrict ci, - struct cell *restrict cj) { - -#ifdef SWIFT_DEBUG_CHECKS - if (ci->nodeID != engine_rank && cj->nodeID != engine_rank) - error("Should be run on a different node"); -#endif - - const struct engine *e = r->e; - const struct cosmology *cosmo = e->cosmology; - - /* Anything to do here? */ - if (cj->sinks.count == 0 || ci->sinks.count == 0) return; - if (!cell_is_active_sinks(ci, e) && !cell_is_active_sinks(cj, e)) return; - - const int ci_local = ci->nodeID == e->nodeID; - const int cj_local = cj->nodeID == e->nodeID; - - if (!ci_local || !cj_local) error("TODO"); - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; - - const int scount_i = ci->sinks.count; - const int scount_j = cj->sinks.count; - struct sink *restrict sinks_i = ci->sinks.parts; - struct sink *restrict sinks_j = cj->sinks.parts; - - /* 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 sinks in ci. */ - for (int sid = 0; sid < scount_i; sid++) { - - /* Get a hold of the ith sink in ci. */ - struct sink *restrict si = &sinks_i[sid]; - - /* Skip inhibited particles */ - if (sink_is_inhibited(si, e)) continue; - - const float ri = si->r_cut; - const float ri2 = ri * ri; - 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 sinks in cj. */ - for (int sjd = 0; sjd < scount_j; sjd++) { - - /* Get a pointer to the jth particle. */ - struct sink *restrict sj = &sinks_j[sjd]; - - /* Early abort? */ - if (!sink_is_active(si, e) && !sink_is_active(sj, e)) continue; - if (sink_is_inhibited(sj, e)) continue; - - /* Get the cutoff radius */ - const float rj = sj->r_cut; - const float rj2 = rj * rj; - - /* Compute the pairwise distance. */ - const float pjx[3] = {(float)(sj->x[0] - cj->loc[0]), - (float)(sj->x[1] - cj->loc[1]), - (float)(sj->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 (sj->ti_drift != e->ti_current) - error("Particle sj not drifted to current time"); - if (si->ti_drift != e->ti_current) - error("Particle si not drifted to current time"); -#endif - - if (r2 < ri2 || r2 < rj2) { - enum sink_merger_remove remove = - IACT_SINK_MERGER(r2, dx, ri, rj, si, sj, a, H); - - /* Remove the particle. */ - switch (remove) { - case sink_merger_remove_none: - break; - case sink_merger_remove_first: - cell_remove_sink(e, ci, si); - break; - case sink_merger_remove_second: - cell_remove_sink(e, cj, sj); - break; - default: - error("Unknown value, please check your iact function."); - } - - /* Can we continue the loop on the current particle? */ - if (remove == sink_merger_remove_first) { - break; - } - } - } /* loop over the sinks in cj. */ - } /* loop over the sinks in ci. */ -} - -/** - * @brief Compute grouped sub-cell interactions for pairs - * - * @param r The #runner. - * @param ci The first #cell. - * @param cj The second #cell. - * - * @todo Hard-code the sid on the recursive calls to avoid the - * redundant computations to find the sid on-the-fly. - */ -void DOSUB_PAIR1_SINKS_MERGER(struct runner *r, struct cell *ci, - struct cell *cj) { - - struct space *s = r->e->s; - const struct engine *e = r->e; - -#ifdef WITH_MPI - if (ci->nodeID != e->nodeID && cj->nodeID != e->nodeID) - error("Should not be running on this node"); -#endif - - /* Should we even bother? */ - if (!cell_is_active_sinks(ci, e) && !cell_is_active_sinks(cj, e)) return; - if (ci->sinks.count == 0 || cj->sinks.count == 0) 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_sinks_task(ci, cj) && - cell_can_recurse_in_pair_sinks_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_SINKS_MERGER(r, ci->progeny[pid], cj->progeny[pjd]); - } - } - - /* Otherwise, compute the pair directly. */ - else { - - /* Make sure both cells are drifted to the current timestep. */ - if (!cell_are_sink_drifted(ci, e)) - error("Interacting undrifted cells (ci)."); - - if (!cell_are_sink_drifted(cj, e)) - error("Interacting undrifted cells (cj)."); - - DO_SYM_PAIR1_SINKS_MERGER(r, ci, cj); - } -} - -/** - * @brief Compute grouped sub-cell interactions for self tasks - * - * @param r The #runner. - * @param ci The first #cell. - */ -void DOSUB_SELF1_SINKS_MERGER(struct runner *r, struct cell *ci) { - -#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->sinks.count == 0 || !cell_is_active_sinks(ci, r->e)) return; - - /* Recurse? */ - if (cell_can_recurse_in_self_sinks_task(ci)) { - - /* Loop over all progeny. */ - for (int k = 0; k < 8; k++) - if (ci->progeny[k] != NULL) { - DOSUB_SELF1_SINKS_MERGER(r, ci->progeny[k]); - for (int j = k + 1; j < 8; j++) - if (ci->progeny[j] != NULL) - DOSUB_PAIR1_SINKS_MERGER(r, ci->progeny[k], ci->progeny[j]); - } - } - - /* Otherwise, compute self-interaction. */ - else { - - /* Drift the cell to the current timestep if needed. */ - if (!cell_are_sink_drifted(ci, r->e)) { - error("Interacting undrifted cell."); - } - - DOSELF1_SINKS_MERGER(r, ci); - } -} diff --git a/src/runner_doiact_functions_stars.h b/src/runner_doiact_functions_stars.h index 88ac45b4465021587158ca71e1b52774d04d0a2a..b163c6fc8a290c93238960e68dbb142283c49003 100644 --- a/src/runner_doiact_functions_stars.h +++ b/src/runner_doiact_functions_stars.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -26,6 +26,17 @@ #include "runner_doiact_stars.h" +#ifdef RT_NONE +#define WITH_RT 0 +#else +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) || \ + (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) +#define WITH_RT (e->policy & engine_policy_rt) +#else +#define WITH_RT 0 +#endif +#endif + /** * @brief Calculate the number density of #part around the #spart * @@ -60,13 +71,8 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { #if (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) struct xpart *restrict xparts = c->hydro.xparts; #endif -#if (!(defined RT_NONE) && (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK)) - /* Don't exit early if star isn't active for feedback - * when we're running with RT */ - const int with_rt = (e->policy & engine_policy_rt); -#else - const int with_rt = 0; -#endif + + const int with_rt = WITH_RT; /* Loop over the sparts in ci. */ for (int sid = 0; sid < scount; sid++) { @@ -131,10 +137,16 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, e->hydro_properties, e->feedback_props, ti_current); +#endif } if (r2 < hig2 && with_rt) { + /* If we're running RT, we don't care whether star is active for + * feedback, just that the star is active. */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_rt_injection_prep(r2, dx, hi, hj, si, pj, cosmo, e->rt_props); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_rt_inject(r2, dx, hi, hj, si, pj, a, H, e->rt_props); #endif } } /* loop over the parts in ci. */ @@ -180,13 +192,7 @@ void DO_NONSYM_PAIR1_STARS_NAIVE(struct runner *r, struct cell *restrict ci, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) struct xpart *restrict xparts_j = cj->hydro.xparts; #endif -#if (!(defined RT_NONE) && (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK)) - /* Don't exit early if star isn't active for feedback - * when we're running with RT */ - const int with_rt = (e->policy & engine_policy_rt); -#else - const int with_rt = 0; -#endif + const int with_rt = WITH_RT; /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; @@ -261,10 +267,16 @@ void DO_NONSYM_PAIR1_STARS_NAIVE(struct runner *r, struct cell *restrict ci, runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, e->hydro_properties, e->feedback_props, ti_current); +#endif } if (r2 < hig2 && with_rt) { + /* If we're running RT, we don't care whether star is active for + * feedback, just that the star is active. */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_rt_injection_prep(r2, dx, hi, hj, si, pj, cosmo, e->rt_props); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_rt_inject(r2, dx, hi, hj, si, pj, a, H, e->rt_props); #endif } } /* loop over the parts in cj. */ @@ -310,14 +322,7 @@ void DO_SYM_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, 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 (!(defined RT_NONE) && (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK)) - /* Don't exit early if star isn't active for feedback - * when we're running with RT */ - const int with_rt = (e->policy & engine_policy_rt); -#else - const int with_rt = 0; -#endif + const int with_rt = WITH_RT; if (do_ci_stars) { @@ -457,10 +462,16 @@ void DO_SYM_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, e->hydro_properties, e->feedback_props, ti_current); +#endif } if (r2 < hig2 && with_rt) { + /* If we're running RT, we don't care whether star is active for + * feedback, just that the star is active. */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_rt_injection_prep(r2, dx, hi, hj, spi, pj, cosmo, e->rt_props); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_rt_inject(r2, dx, hi, hj, spi, pj, a, H, e->rt_props); #endif } } /* loop over the parts in cj. */ @@ -605,10 +616,16 @@ void DO_SYM_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, runner_iact_nonsym_feedback_apply(r2, dx, hj, hi, spj, pi, xpi, cosmo, e->hydro_properties, e->feedback_props, ti_current); +#endif } if (r2 < hjg2 && with_rt) { + /* If we're running RT, we don't care whether star is active for + * feedback, just that the star is active. */ +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_rt_injection_prep(r2, dx, hj, hi, spj, pi, cosmo, e->rt_props); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_rt_inject(r2, dx, hj, hi, spj, pi, a, H, e->rt_props); #endif } } /* loop over the parts in ci. */ @@ -729,6 +746,8 @@ void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, NULL, cosmo, e->feedback_props, e->ti_current); + runner_iact_nonsym_rt_injection_prep(r2, dx, hi, hj, spi, pj, cosmo, + e->rt_props); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) error("No subset feedback iact functions do (or should) exist!"); /* runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, @@ -790,6 +809,8 @@ void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, NULL, cosmo, e->feedback_props, e->ti_current); + runner_iact_nonsym_rt_injection_prep(r2, dx, hi, hj, spi, pj, cosmo, + e->rt_props); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) error("No subset feedback iact functions do (or should) exist!"); /* runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, @@ -886,6 +907,8 @@ void DOPAIR1_SUBSET_STARS_NAIVE(struct runner *r, struct cell *restrict ci, runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, NULL, cosmo, e->feedback_props, e->ti_current); + runner_iact_nonsym_rt_injection_prep(r2, dx, hi, hj, spi, pj, cosmo, + e->rt_props); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) error("No subset feedback iact functions do (or should) exist! ."); /* runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, @@ -971,6 +994,8 @@ void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, runner_iact_nonsym_feedback_density(r2, dx, hi, pj->h, spi, pj, NULL, cosmo, e->feedback_props, e->ti_current); + runner_iact_nonsym_rt_injection_prep(r2, dx, hi, pj->h, spi, pj, cosmo, + e->rt_props); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) error("No subset feedback iact functions do (or should) exist!"); /* runner_iact_nonsym_feedback_apply(r2, dx, hi, pj->h, spi, pj, xpj, */ @@ -1035,9 +1060,9 @@ void DOPAIR1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, /* 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); + 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]; @@ -1427,3 +1452,5 @@ void DOSUB_SELF1_STARS(struct runner *r, struct cell *ci, int gettimer) { TIMER_TOC(TIMER_DOSUB_SELF_STARS); } + +#undef WITH_RT diff --git a/src/runner_doiact_grav.c b/src/runner_doiact_grav.c index 82b1534f01196137999cec3106d388894012c2de..e0cadc530da26506f248ef6cdc456276b57ba650 100644 --- a/src/runner_doiact_grav.c +++ b/src/runner_doiact_grav.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -17,7 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner_doiact_grav.h" @@ -170,7 +170,7 @@ void runner_do_grav_down(struct runner *r, struct cell *c, int timer) { } /** - * @brief Compute the fully Newtoning gravitational forces from particles + * @brief Compute the fully Newtonian gravitational forces from particles * one array onto the particles in another array * * This function *must* be called at the leaf level for particles i. @@ -818,7 +818,8 @@ static INLINE void runner_dopair_grav_pp_truncated( ci_cache->m[pid] != 0.) { error("Found an extra gpart in the gravity interaction"); } - if (gparts_j[pjd].time_bin == time_bin_not_created && mass_j != 0.) { + if (pjd < gcount_j && gparts_j[pjd].time_bin == time_bin_not_created && + mass_j != 0.) { error("Found an extra gpart in the gravity interaction"); } diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h index 1afa2e0f5df900aea498a859b9672625bde773e7..cfac73779604e2bab4b30041c1a1b10b99ec52d6 100644 --- a/src/runner_doiact_grav.h +++ b/src/runner_doiact_grav.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -20,7 +20,7 @@ #ifndef SWIFT_RUNNER_DOIACT_GRAV_H #define SWIFT_RUNNER_DOIACT_GRAV_H -#include "../config.h" +#include <config.h> struct runner; struct cell; diff --git a/src/runner_doiact_hydro.c b/src/runner_doiact_hydro.c index b66c09749d414bd81e1fe68cab6cb651593efbb3..3ec53e5077df9da65348b385abccaca9830255a8 100644 --- a/src/runner_doiact_hydro.c +++ b/src/runner_doiact_hydro.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,17 +20,19 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "active.h" #include "cell.h" #include "chemistry.h" #include "engine.h" +#include "mhd.h" #include "pressure_floor_iact.h" #include "rt.h" #include "runner.h" #include "runner_doiact_hydro_vec.h" +#include "sink.h" #include "space_getsid.h" #include "star_formation_iact.h" #include "timers.h" @@ -40,35 +42,30 @@ #define FUNCTION density #define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY #include "runner_doiact_functions_hydro.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +#include "runner_doiact_undef.h" /* 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 +#include "runner_doiact_undef.h" #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 +#include "runner_doiact_undef.h" /* Import the RT gradient loop functions */ #define FUNCTION rt_gradient #define FUNCTION_TASK_LOOP TASK_LOOP_RT_GRADIENT #include "runner_doiact_functions_hydro.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +#include "runner_doiact_undef.h" /* Import the RT transport (force) loop functions. */ #define FUNCTION rt_transport #define FUNCTION_TASK_LOOP TASK_LOOP_RT_TRANSPORT #include "runner_doiact_functions_hydro.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +#include "runner_doiact_undef.h" diff --git a/src/runner_doiact_hydro.h b/src/runner_doiact_hydro.h index 1fd54c1037e2d0b9c7a671311cfee4720ebe8d84..04824e87eecc32f91aa4352176212c853ceaf56b 100644 --- a/src/runner_doiact_hydro.h +++ b/src/runner_doiact_hydro.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 @@ -101,6 +101,46 @@ #define _IACT(f) PASTE(runner_iact, f) #define IACT _IACT(FUNCTION) +#if ((FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) || \ + (FUNCTION_TASK_LOOP == TASK_LOOP_GRADIENT) || \ + (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE)) + +#define _IACT_NONSYM_MHD(f) PASTE(runner_iact_nonsym_mhd, f) +#define IACT_NONSYM_MHD _IACT_NONSYM_MHD(FUNCTION) + +#define _IACT_MHD(f) PASTE(runner_iact_mhd, f) +#define IACT_MHD _IACT_MHD(FUNCTION) + +#define GET_MU0() \ + const double mu_0 = e->physical_constants->const_vacuum_permeability; + +#else + +#define IACT_NONSYM_MHD(x1, x2, x3, x4, x5, x6, x7, x8, x9) \ + {} +#define IACT_MHD(x1, x2, x3, x4, x5, x6, x7, x8, x9) \ + {} +#define GET_MU0() \ + {} +#endif + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_RT_GRADIENT) || \ + (FUNCTION_TASK_LOOP == TASK_LOOP_RT_TRANSPORT) +/* RT specific function calls */ +#define PART_IS_ACTIVE part_is_rt_active +#define CELL_IS_ACTIVE cell_is_rt_active +#define CELL_ARE_PART_DRIFTED cell_are_part_drifted_rt_sub_cycle +#else +/* default hydro behaviour. */ +#define PART_IS_ACTIVE part_is_active +#define CELL_IS_ACTIVE cell_is_active_hydro +#define CELL_ARE_PART_DRIFTED cell_are_part_drifted +/* when running with RT subcycling, we can have RT active + * particles in a normal swift step that aren't drifted to + * the current time, so we don't do those checks there. */ +#define DO_DRIFT_DEBUG_CHECKS 1 +#endif + #define _IACT_NONSYM_VEC(f) PASTE(runner_iact_nonsym_vec, f) #define IACT_NONSYM_VEC _IACT_NONSYM_VEC(FUNCTION) diff --git a/src/runner_doiact_hydro_vec.c b/src/runner_doiact_hydro_vec.c index f5fde51939d51e8fcd6ecbca07d3012609acb88d..dde0c98e256ce6aa1580aad2c667ae1cbb01b85c 100644 --- a/src/runner_doiact_hydro_vec.c +++ b/src/runner_doiact_hydro_vec.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner_doiact_hydro_vec.h" diff --git a/src/runner_doiact_hydro_vec.h b/src/runner_doiact_hydro_vec.h index 6b01987c15513343d7a8784322803d7f834dea2c..f283d9bc4c3ba5d3e9664e908aebcffecea23dac 100644 --- a/src/runner_doiact_hydro_vec.h +++ b/src/runner_doiact_hydro_vec.h @@ -21,7 +21,7 @@ #define SWIFT_RUNNER_VEC_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "active.h" diff --git a/src/runner_doiact_limiter.c b/src/runner_doiact_limiter.c index ac65ca063cffd89e26673b0a8d4df6fbfa263e56..6ceced81662ea8f1b6741053ef0bacaf81fa52d8 100644 --- a/src/runner_doiact_limiter.c +++ b/src/runner_doiact_limiter.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "active.h" diff --git a/src/runner_doiact_limiter.h b/src/runner_doiact_limiter.h index a0daaf426174c3653ba95ba7d0e36a07899b2a89..4671c39db4a569122e9a2de6b8087307f1445b99 100644 --- a/src/runner_doiact_limiter.h +++ b/src/runner_doiact_limiter.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2016 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 diff --git a/src/runner_doiact_rt.h b/src/runner_doiact_rt.h deleted file mode 100644 index 3762b1b2af7cd9a184741027be57e2912eaa5d5b..0000000000000000000000000000000000000000 --- a/src/runner_doiact_rt.h +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) - * 2018 Loic Hausammann (loic.hausammann@epfl.ch) - * 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -/* Before including this file, define FUNCTION, which is the - name of the interaction function. This creates the interaction functions - runner_dopair_FUNCTION, runner_doself_FUNCTION and runner_dosub_FUNCTION - calling the pairwise interaction function runner_iact_FUNCTION. */ - -#define PASTE(x, y) x##_##y - -#define _DOSELF1_RT(f) PASTE(runner_doself_rt, f) -#define DOSELF1_RT _DOSELF1_RT(FUNCTION) - -#define _DOPAIR1_RT(f) PASTE(runner_dopair_rt, f) -#define DOPAIR1_RT _DOPAIR1_RT(FUNCTION) - -#define _DOPAIR1_NONSYM_RT(f) PASTE(runner_dopair_nonsym_rt, f) -#define DOPAIR1_NONSYM_RT _DOPAIR1_NONSYM_RT(FUNCTION) - -#define _DOSELF1_BRANCH_RT(f) PASTE(runner_doself_branch_rt, f) -#define DOSELF1_BRANCH_RT _DOSELF1_BRANCH_RT(FUNCTION) - -#define _DOPAIR1_BRANCH_RT(f) PASTE(runner_dopair_branch_rt, f) -#define DOPAIR1_BRANCH_RT _DOPAIR1_BRANCH_RT(FUNCTION) - -#define _DOSUB_PAIR1_RT(f) PASTE(runner_dosub_pair_rt, f) -#define DOSUB_PAIR1_RT _DOSUB_PAIR1_RT(FUNCTION) - -#define _DOSUB_SELF1_RT(f) PASTE(runner_dosub_self_rt, f) -#define DOSUB_SELF1_RT _DOSUB_SELF1_RT(FUNCTION) - -#define _TIMER_DOSELF_RT(f) PASTE(timer_doself_rt, f) -#define TIMER_DOSELF_RT _TIMER_DOSELF_RT(FUNCTION) - -#define _TIMER_DOPAIR_RT(f) PASTE(timer_dopair_rt, f) -#define TIMER_DOPAIR_RT _TIMER_DOPAIR_RT(FUNCTION) - -#define _TIMER_DOSUB_SELF_RT(f) PASTE(timer_dosub_self_rt, f) -#define TIMER_DOSUB_SELF_RT _TIMER_DOSUB_SELF_RT(FUNCTION) - -#define _TIMER_DOSUB_PAIR_RT(f) PASTE(timer_dosub_pair_rt, f) -#define TIMER_DOSUB_PAIR_RT _TIMER_DOSUB_PAIR_RT(FUNCTION) - -#define _IACT_RT(f) PASTE(runner_iact_rt, f) -#define IACT_RT _IACT_RT(FUNCTION) - -void DOSELF1_BRANCH_RT(struct runner *r, struct cell *c, int timer); -void DOPAIR1_BRANCH_RT(struct runner *r, struct cell *ci, struct cell *cj, - int timer); - -void DOSUB_SELF1_RT(struct runner *r, struct cell *ci, int timer); -void DOSUB_PAIR1_RT(struct runner *r, struct cell *ci, struct cell *cj, - int timer); diff --git a/src/runner_doiact_sinks.h b/src/runner_doiact_sinks.h index 0b3ac214191153c9c7dfd5523d35dabb1d12d55e..28239aa64ca34f7abcae25797e00937d2044d99a 100644 --- a/src/runner_doiact_sinks.h +++ b/src/runner_doiact_sinks.h @@ -22,39 +22,20 @@ runner_dopair_FUNCTION, runner_doself_FUNCTION and runner_dosub_FUNCTION calling the pairwise interaction function runner_iact_FUNCTION. */ -#define PASTE(x, y) x##_##y - -#define _DOSELF1_SINKS(f) PASTE(runner_doself_sinks, f) -#define DOSELF1_SINKS _DOSELF1_SINKS(FUNCTION) - -#define _DO_SYM_PAIR1_SINKS(f) PASTE(runner_do_sym_pair_sinks, f) -#define DO_SYM_PAIR1_SINKS _DO_SYM_PAIR1_SINKS(FUNCTION) - -#define _DO_NONSYM_PAIR1_SINKS_NAIVE(f) \ - PASTE(runner_do_nonsym_pair_sinks_naive, f) -#define DO_NONSYM_PAIR1_SINKS_NAIVE _DO_NONSYM_PAIR1_SINKS_NAIVE(FUNCTION) - -#define _DOPAIR1_SINKS_NAIVE(f) PASTE(runner_dopair_sinks_naive, f) -#define DOPAIR1_SINKS_NAIVE _DOPAIR1_SINKS_NAIVE(FUNCTION) - -#define _DOSELF1_BRANCH_SINKS(f) PASTE(runner_doself_branch_sinks, f) -#define DOSELF1_BRANCH_SINKS _DOSELF1_BRANCH_SINKS(FUNCTION) - -#define _DOPAIR1_BRANCH_SINK(f) PASTE(runner_dopair_branch_sinks, f) -#define DOPAIR1_BRANCH_SINKS _DOPAIR1_BRANCH_SINK(FUNCTION) - -#define _DOSUB_PAIR1_SINKS(f) PASTE(runner_dosub_pair_sinks, f) -#define DOSUB_PAIR1_SINKS _DOSUB_PAIR1_SINKS(FUNCTION) - -#define _DOSUB_SELF1_SINKS(f) PASTE(runner_dosub_self_sinks, f) -#define DOSUB_SELF1_SINKS _DOSUB_SELF1_SINKS(FUNCTION) - -#define _IACT_SINK(f) PASTE(runner_iact_nonsym_sinks, f) -#define IACT_SINK _IACT_SINK(FUNCTION) - -void DOSELF1_BRANCH_SINKS(struct runner *r, struct cell *c); -void DOPAIR1_BRANCH_SINKS(struct runner *r, struct cell *ci, struct cell *cj); - -void DOSUB_SELF1_SINKS(struct runner *r, struct cell *ci, int gettimer); -void DOSUB_PAIR1_SINKS(struct runner *r, struct cell *ci, struct cell *cj, - int gettimer); +void runner_doself_branch_sinks_swallow(struct runner *r, struct cell *c); +void runner_dopair_branch_sinks_swallow(struct runner *r, struct cell *ci, + struct cell *cj); +void runner_dosub_self_sinks_swallow(struct runner *r, struct cell *ci, + int gettimer); +void runner_dosub_pair_sinks_swallow(struct runner *r, struct cell *ci, + struct cell *cj, int gettimer); + +void runner_do_sinks_gas_swallow_self(struct runner *r, struct cell *c, + int timer); +void runner_do_sinks_gas_swallow_pair(struct runner *r, struct cell *ci, + struct cell *cj, int timer); + +void runner_do_sinks_sink_swallow_self(struct runner *r, struct cell *c, + int timer); +void runner_do_sinks_sink_swallow_pair(struct runner *r, struct cell *ci, + struct cell *cj, int timer); diff --git a/src/runner_doiact_sinks_merger.h b/src/runner_doiact_sinks_merger.h deleted file mode 100644 index 3405f63941bb0f20b2079459f9b8a23ff94527f4..0000000000000000000000000000000000000000 --- a/src/runner_doiact_sinks_merger.h +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -/* Before including this file, define FUNCTION, which is the - name of the interaction function. This creates the interaction functions - runner_dopair_FUNCTION, runner_doself_FUNCTION and runner_dosub_FUNCTION - calling the pairwise interaction function runner_iact_FUNCTION. */ - -#define PASTE(x, y) x##_##y - -#define _DOSELF1_SINKS_MERGER(f) PASTE(runner_doself_sinks, f) -#define DOSELF1_SINKS_MERGER _DOSELF1_SINKS_MERGER(FUNCTION) - -#define _DO_SYM_PAIR1_SINKS_MERGER(f) PASTE(runner_do_sym_pair_sinks, f) -#define DO_SYM_PAIR1_SINKS_MERGER _DO_SYM_PAIR1_SINKS_MERGER(FUNCTION) - -#define _DOSUB_PAIR1_SINKS_MERGER(f) PASTE(runner_dosub_pair_sinks, f) -#define DOSUB_PAIR1_SINKS_MERGER _DOSUB_PAIR1_SINKS_MERGER(FUNCTION) - -#define _DOSUB_SELF1_SINKS_MERGER(f) PASTE(runner_dosub_self_sinks, f) -#define DOSUB_SELF1_SINKS_MERGER _DOSUB_SELF1_SINKS_MERGER(FUNCTION) - -#define _IACT_SINK_MERGER(f) PASTE(runner_iact_sym_sinks, f) -#define IACT_SINK_MERGER _IACT_SINK_MERGER(FUNCTION) - -void DOSELF1_SINKS_MERGER(struct runner *r, struct cell *c); -void DO_SYM_PAIR1_SINKS_MERGER(struct runner *r, struct cell *ci, - struct cell *cj); - -void DOSUB_SELF1_SINKS_MERGER(struct runner *r, struct cell *ci); -void DOSUB_PAIR1_SINKS_MERGER(struct runner *r, struct cell *ci, - struct cell *cj); diff --git a/src/runner_doiact_stars.c b/src/runner_doiact_stars.c index 56f4c0bbcc3e284a57e352d78c3bccfbfa659523..4720a96fbd02e9d93eb4ddb8b59c82157a3fdaf0 100644 --- a/src/runner_doiact_stars.c +++ b/src/runner_doiact_stars.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,13 +20,13 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "active.h" #include "cell.h" #include "engine.h" -#include "feedback.h" +#include "feedback_iact.h" #include "rt.h" #include "runner.h" #include "space_getsid.h" @@ -37,15 +37,13 @@ #define FUNCTION density #define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY #include "runner_doiact_functions_stars.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" /* 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 +#include "runner_doiact_undef.h" #ifdef EXTRA_STAR_LOOPS @@ -53,14 +51,12 @@ #define FUNCTION prep1 #define FUNCTION_TASK_LOOP TASK_LOOP_STARS_PREP1 #include "runner_doiact_functions_stars.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" /* Import the stars prepare2 loop functions. */ #define FUNCTION prep2 #define FUNCTION_TASK_LOOP TASK_LOOP_STARS_PREP2 #include "runner_doiact_functions_stars.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" #endif /* EXTRA_STAR_LOOPS */ diff --git a/src/runner_doiact_stars.h b/src/runner_doiact_stars.h index 2d41d5a0bd1b1003039e1795eec205889b46baf6..1bc31f5e92c3f95aa0c9c0ffef6fcf2276eeb12f 100644 --- a/src/runner_doiact_stars.h +++ b/src/runner_doiact_stars.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify diff --git a/src/runner_doiact_undef.h b/src/runner_doiact_undef.h new file mode 100644 index 0000000000000000000000000000000000000000..0a76c6d15df5a535a6cf7e93f6b54a9972b60bfa --- /dev/null +++ b/src/runner_doiact_undef.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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/>. + * + ******************************************************************************/ + +/* A large number of macros are defined in runner_doiact_hydro.h + * based on the values of some macros defined in runner_main.c + * For each loop definition (density, force, etc.) the values are different + * so we need to undefine everything before redefining them. + * This is done here in a single place to avoid code duplication and missing + * undefs if they were scattered all over the place */ +#undef IACT +#undef IACT_NONSYM +#undef IACT_MHD +#undef IACT_NONSYM_MHD +#undef IACT_STARS +#undef IACT_BH_GAS +#undef IACT_BH_BH +#undef GET_MU0 +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + +/* these are defined in runner_doiact_functions_hydro.h at every #include */ +#undef PART_IS_ACTIVE +#undef CELL_IS_ACTIVE +#undef CELL_ARE_PART_DRIFTED +#undef DO_DRIFT_DEBUG_CHECKS diff --git a/src/runner_drift.c b/src/runner_drift.c index db1918de8c7012de6780e5e2afa2fd44f1c2dc16..39bd0dcfa1f89e23e887a7f4d11d69e59ace4beb 100644 --- a/src/runner_drift.c +++ b/src/runner_drift.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner.h" @@ -42,7 +42,7 @@ void runner_do_drift_part(struct runner *r, struct cell *c, int timer) { TIMER_TIC; - cell_drift_part(c, r->e, 0); + cell_drift_part(c, r->e, 0, NULL); if (timer) TIMER_TOC(timer_drift_part); } @@ -58,7 +58,7 @@ void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer) { TIMER_TIC; - cell_drift_gpart(c, r->e, 0); + cell_drift_gpart(c, r->e, 0, NULL); if (timer) TIMER_TOC(timer_drift_gpart); } @@ -74,7 +74,7 @@ void runner_do_drift_spart(struct runner *r, struct cell *c, int timer) { TIMER_TIC; - cell_drift_spart(c, r->e, 0); + cell_drift_spart(c, r->e, 0, NULL); if (timer) TIMER_TOC(timer_drift_spart); } @@ -90,7 +90,7 @@ void runner_do_drift_bpart(struct runner *r, struct cell *c, int timer) { TIMER_TIC; - cell_drift_bpart(c, r->e, 0); + cell_drift_bpart(c, r->e, 0, NULL); if (timer) TIMER_TOC(timer_drift_bpart); } diff --git a/src/runner_ghost.c b/src/runner_ghost.c index 306f16f94e2c4c27cf14fcd53da28c9ed630445c..c5ba6ecc21fa2a1696c80ee38efd505568d58f0a 100644 --- a/src/runner_ghost.c +++ b/src/runner_ghost.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner.h" @@ -31,6 +31,7 @@ #include "cell.h" #include "engine.h" #include "feedback.h" +#include "mhd.h" #include "pressure_floor.h" #include "pressure_floor_iact.h" #include "rt.h" @@ -131,7 +132,8 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { 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)) { + if (spart_is_active(&sparts[k], e) && + (feedback_is_active(&sparts[k], e) || with_rt)) { sid[scount] = k; h_0[scount] = sparts[k].h; left[scount] = 0.f; @@ -143,6 +145,9 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { for (int num_reruns = 0; scount > 0 && num_reruns < max_smoothing_iter; num_reruns++) { + ghost_stats_account_for_stars(&c->ghost_statistics, num_reruns, scount, + sparts, sid); + /* Reset the redo-count. */ redo = 0; @@ -156,6 +161,8 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { /* Is this part within the timestep? */ if (!spart_is_active(sp, e)) error("Ghost applied to inactive particle"); + if (!feedback_is_active(sp, e) && !with_rt) + error("Ghost applied to particle inactive for feedback and RT"); #endif /* Get some useful values */ @@ -169,6 +176,8 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { if (sp->density.wcount < 1.e-5 * kernel_root) { /* No neighbours case */ + ghost_stats_no_ngb_star_iteration(&c->ghost_statistics, num_reruns); + /* Flag that there were no neighbours */ has_no_neighbours = 1; @@ -252,7 +261,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { feedback_reset_feedback(sp, feedback_props); } - if (with_rt && !rt_props->hydro_controlled_injection) { + if (with_rt) { rt_reset_spart(sp); @@ -360,6 +369,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { /* Do some damage control if no neighbours at all were found */ if (has_no_neighbours) { + ghost_stats_no_ngb_star_converged(&c->ghost_statistics); stars_spart_has_no_neighbours(sp, cosmo); rt_spart_has_no_neighbours(sp); } @@ -377,6 +387,8 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { h_max = max(h_max, sp->h); h_max_active = max(h_max_active, sp->h); + ghost_stats_converged_star(&c->ghost_statistics, sp); + stars_reset_feedback(sp); /* Only do feedback if stars have a reasonable birth time */ @@ -423,7 +435,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { feedback_reset_feedback(sp, feedback_props); } - if (with_rt && !rt_props->hydro_controlled_injection) { + if (with_rt) { rt_reset_spart(sp); @@ -628,6 +640,9 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c, for (int num_reruns = 0; bcount > 0 && num_reruns < max_smoothing_iter; num_reruns++) { + ghost_stats_account_for_black_holes(&c->ghost_statistics, num_reruns, + bcount, bparts, sid); + /* Reset the redo-count. */ redo = 0; @@ -654,6 +669,9 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c, if (bp->density.wcount < 1.e-5 * kernel_root) { /* No neighbours case */ + ghost_stats_no_ngb_black_hole_iteration(&c->ghost_statistics, + num_reruns); + /* Flag that there were no neighbours */ has_no_neighbours = 1; @@ -770,6 +788,7 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c, /* Do some damage control if no neighbours at all were found */ if (has_no_neighbours) { + ghost_stats_no_ngb_black_hole_converged(&c->ghost_statistics); black_holes_bpart_has_no_neighbours(bp, cosmo); } @@ -782,6 +801,8 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c, /* We now have a particle whose smoothing length has converged */ + ghost_stats_converged_black_hole(&c->ghost_statistics, bp); + black_holes_reset_feedback(bp); /* Check if h_max has increased */ @@ -1006,13 +1027,14 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) { /* Finish the gradient calculation */ hydro_end_gradient(p); + mhd_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; + double dt_alpha, dt_therm; if (with_cosmology) { const integertime_t ti_step = get_integer_timestep(p->time_bin); @@ -1021,20 +1043,26 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) { dt_alpha = cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + dt_therm = cosmology_get_therm_kick_factor(cosmo, ti_begin, + ti_begin + ti_step); } else { dt_alpha = get_timestep(p->time_bin, time_base); + dt_therm = get_timestep(p->time_bin, time_base); } /* Compute variables required for the force loop */ - hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); + hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha, dt_therm); + mhd_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); timestep_limiter_prepare_force(p, xp); rt_prepare_force(p); + rt_timestep_prepare_force(p); /* 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); + mhd_reset_acceleration(p); } } } @@ -1067,6 +1095,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { const struct hydro_props *hydro_props = e->hydro_properties; const int with_cosmology = (e->policy & engine_policy_cosmology); + const int with_rt = (e->policy & engine_policy_rt); const float hydro_h_max = e->hydro_properties->h_max; const float hydro_h_min = e->hydro_properties->h_min; @@ -1128,6 +1157,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { for (int num_reruns = 0; count > 0 && num_reruns < max_smoothing_iter; num_reruns++) { + ghost_stats_account_for_hydro(&c->ghost_statistics, num_reruns, count, + parts, pid); + /* Reset the redo-count. */ redo = 0; @@ -1154,6 +1186,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { if (p->density.wcount < 1.e-5 * kernel_root) { /* No neighbours case */ + ghost_stats_no_ngb_hydro_iteration(&c->ghost_statistics, num_reruns); + /* Flag that there were no neighbours */ has_no_neighbours = 1; @@ -1164,6 +1198,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Finish the density calculation */ hydro_end_density(p, cosmo); + mhd_end_density(p, cosmo); chemistry_end_density(p, chemistry, cosmo); pressure_floor_end_density(p, cosmo); star_formation_end_density(p, xp, star_formation, cosmo); @@ -1218,6 +1253,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Compute variables required for the gradient loop */ hydro_prepare_gradient(p, xp, cosmo, hydro_props); + mhd_prepare_gradient(p, xp, cosmo, hydro_props); /* The particle gradient values are now set. Do _NOT_ try to read any particle density variables! */ @@ -1225,6 +1261,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Prepare the particle for the gradient loop over neighbours */ hydro_reset_gradient(p); + mhd_reset_gradient(p); #else /* Calculate the time-step for passing to hydro_prepare_force, used @@ -1232,7 +1269,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { * artificial viscosity and thermal conduction terms) */ const double time_base = e->time_base; const integertime_t ti_current = e->ti_current; - double dt_alpha; + double dt_alpha, dt_therm; if (with_cosmology) { const integertime_t ti_step = get_integer_timestep(p->time_bin); @@ -1241,26 +1278,37 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { dt_alpha = cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + dt_therm = cosmology_get_therm_kick_factor(cosmo, ti_begin, + ti_begin + ti_step); } else { dt_alpha = get_timestep(p->time_bin, time_base); + dt_therm = 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); + hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha, dt_therm); + mhd_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); timestep_limiter_prepare_force(p, xp); rt_prepare_force(p); + rt_timestep_prepare_force(p); /* 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); + mhd_reset_acceleration(p); #endif /* EXTRA_HYDRO_LOOP */ - rt_reset_part(p); + if (with_rt) { +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debugging_check_nr_subcycles(p, e->rt_props); +#endif + rt_reset_part(p, cosmo); + } /* Ok, we are done with this particle */ continue; @@ -1330,6 +1378,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Re-initialise everything */ hydro_init_part(p, hs); + mhd_init_part(p); chemistry_init_part(p, chemistry); pressure_floor_init_part(p, xp); star_formation_init_part(p, star_formation); @@ -1353,7 +1402,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Do some damage control if no neighbours at all were found */ if (has_no_neighbours) { + ghost_stats_no_ngb_hydro_converged(&c->ghost_statistics); hydro_part_has_no_neighbours(p, xp, cosmo); + mhd_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); star_formation_part_has_no_neighbours(p, xp, star_formation, @@ -1374,6 +1425,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { h_max = max(h_max, p->h); h_max_active = max(h_max_active, p->h); + ghost_stats_converged_hydro(&c->ghost_statistics, p); + #ifdef EXTRA_HYDRO_LOOP /* As of here, particle gradient variables will be set. */ @@ -1381,12 +1434,14 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Compute variables required for the gradient loop */ hydro_prepare_gradient(p, xp, cosmo, hydro_props); + mhd_prepare_gradient(p, xp, cosmo, hydro_props); /* 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); + mhd_reset_gradient(p); #else @@ -1395,7 +1450,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { * artificial viscosity and thermal conduction terms) */ const double time_base = e->time_base; const integertime_t ti_current = e->ti_current; - double dt_alpha; + double dt_alpha, dt_therm; if (with_cosmology) { const integertime_t ti_step = get_integer_timestep(p->time_bin); @@ -1404,26 +1459,37 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { dt_alpha = cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + dt_therm = cosmology_get_therm_kick_factor(cosmo, ti_begin, + ti_begin + ti_step); } else { dt_alpha = get_timestep(p->time_bin, time_base); + dt_therm = 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); + hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha, dt_therm); + mhd_prepare_force(p, xp, cosmo, hydro_props, dt_alpha); timestep_limiter_prepare_force(p, xp); rt_prepare_force(p); + rt_timestep_prepare_force(p); /* 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); + mhd_reset_acceleration(p); #endif /* EXTRA_HYDRO_LOOP */ - rt_reset_part(p); + if (with_rt) { +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debugging_check_nr_subcycles(p, e->rt_props); +#endif + rt_reset_part(p, cosmo); + } } /* We now need to treat the particles whose smoothing length had not @@ -1540,11 +1606,13 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { void runner_do_rt_ghost1(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology *cosmo = e->cosmology; int count = c->hydro.count; /* Anything to do here? */ if (count == 0) return; - if (!cell_is_active_hydro(c, e)) return; + if (!cell_is_rt_active(c, e)) return; TIMER_TIC; @@ -1564,9 +1632,24 @@ void runner_do_rt_ghost1(struct runner *r, struct cell *c, int timer) { if (part_is_inhibited(p, e)) continue; /* Skip inactive parts */ - if (!part_is_active(p, e)) continue; + if (!part_is_rt_active(p, e)) continue; - rt_injection_update_photon_density(p, e->rt_props); + /* First reset everything that needs to be reset for the following + * subcycle */ + const integertime_t ti_current_subcycle = e->ti_current_subcycle; + const integertime_t ti_step = + get_integer_timestep(p->rt_time_data.time_bin); + const integertime_t ti_begin = get_integer_time_begin( + ti_current_subcycle + 1, p->rt_time_data.time_bin); + const integertime_t ti_end = ti_begin + ti_step; + + const float dt = + rt_part_dt(ti_begin, ti_end, e->time_base, with_cosmology, cosmo); + + rt_reset_part_each_subcycle(p, cosmo, dt); + + /* Now finish up injection */ + rt_finalise_injection(p, e->rt_props); } } @@ -1585,10 +1668,11 @@ void runner_do_rt_ghost2(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; int count = c->hydro.count; + const struct cosmology *cosmo = e->cosmology; /* Anything to do here? */ if (count == 0) return; - if (!cell_is_active_hydro(c, e)) return; + if (!cell_is_rt_active(c, e)) return; TIMER_TIC; @@ -1608,9 +1692,9 @@ void runner_do_rt_ghost2(struct runner *r, struct cell *c, int timer) { if (part_is_inhibited(p, e)) continue; /* Skip inactive parts */ - if (!part_is_active(p, e)) continue; + if (!part_is_rt_active(p, e)) continue; - rt_end_gradient(p); + rt_end_gradient(p, cosmo); } } diff --git a/src/runner_main.c b/src/runner_main.c index 52f5629c6bd9df8a6307174ede7666ad987c5cf9..d925a71e648436dfb0a5b358ce4827a4f4e28c4f 100644 --- a/src/runner_main.c +++ b/src/runner_main.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -33,6 +33,7 @@ /* Local headers. */ #include "engine.h" #include "feedback.h" +#include "runner_doiact_sinks.h" #include "scheduler.h" #include "space_getsid.h" #include "timers.h" @@ -44,38 +45,33 @@ #define FUNCTION density #define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY #include "runner_doiact_hydro.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +#include "runner_doiact_undef.h" /* 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 +#include "runner_doiact_undef.h" #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 +#include "runner_doiact_undef.h" /* Import the limiter loop functions. */ #define FUNCTION limiter #define FUNCTION_TASK_LOOP TASK_LOOP_LIMITER #include "runner_doiact_limiter.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +#include "runner_doiact_undef.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 +#include "runner_doiact_undef.h" #ifdef EXTRA_STAR_LOOPS @@ -83,15 +79,13 @@ #define FUNCTION prep1 #define FUNCTION_TASK_LOOP TASK_LOOP_STARS_PREP1 #include "runner_doiact_stars.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" /* Import the stars prepare2 loop functions. */ #define FUNCTION prep2 #define FUNCTION_TASK_LOOP TASK_LOOP_STARS_PREP2 #include "runner_doiact_stars.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" #endif /* EXTRA_STAR_LOOPS */ @@ -99,71 +93,37 @@ #define FUNCTION feedback #define FUNCTION_TASK_LOOP TASK_LOOP_FEEDBACK #include "runner_doiact_stars.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" /* 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 +#include "runner_doiact_undef.h" /* 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 +#include "runner_doiact_undef.h" /* 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 - -/* Import radiative transfer loop functions. */ -#define FUNCTION inject -#define FUNCTION_TASK_LOOP TASK_LOOP_RT_INJECT -#include "runner_doiact_rt.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +#include "runner_doiact_undef.h" /* Import the RT gradient loop functions */ #define FUNCTION rt_gradient #define FUNCTION_TASK_LOOP TASK_LOOP_RT_GRADIENT #include "runner_doiact_hydro.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP +#include "runner_doiact_undef.h" /* Import the RT transport (force) loop functions. */ #define FUNCTION rt_transport #define FUNCTION_TASK_LOOP TASK_LOOP_RT_TRANSPORT #include "runner_doiact_hydro.h" -#undef FUNCTION -#undef FUNCTION_TASK_LOOP - -/* Import the sink compute formation loop functions. */ -#define FUNCTION compute_formation -#define FUNCTION_TASK_LOOP TASK_LOOP_SINK_FORMATION -#include "runner_doiact_sinks.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION - -/* Import the sink compute formation loop functions. */ -#define FUNCTION accretion -#define FUNCTION_TASK_LOOP TASK_LOOP_SINK_ACCRETION -#include "runner_doiact_sinks.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION - -/* Import the sink merger loop functions. */ -#define FUNCTION merger -#define FUNCTION_TASK_LOOP TASK_LOOP_SINK_MERGER -#include "runner_doiact_sinks_merger.h" -#undef FUNCTION_TASK_LOOP -#undef FUNCTION +#include "runner_doiact_undef.h" /** * @brief The #runner main thread routine. @@ -175,8 +135,7 @@ 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) { @@ -269,18 +228,16 @@ void *runner_main(void *data) { runner_do_bh_swallow_self(r, ci, 1); else if (t->subtype == task_subtype_bh_feedback) runner_doself_branch_bh_feedback(r, ci); - else if (t->subtype == task_subtype_rt_inject) - runner_doself_branch_rt_inject(r, ci, 1); else if (t->subtype == task_subtype_rt_gradient) runner_doself1_branch_rt_gradient(r, ci); else if (t->subtype == task_subtype_rt_transport) runner_doself2_branch_rt_transport(r, ci); - else if (t->subtype == task_subtype_sink_compute_formation) - runner_doself_branch_sinks_compute_formation(r, ci); - else if (t->subtype == task_subtype_sink_accretion) - runner_doself_branch_sinks_accretion(r, ci); - else if (t->subtype == task_subtype_sink_merger) - runner_doself_sinks_merger(r, ci); + else if (t->subtype == task_subtype_sink_swallow) + runner_doself_branch_sinks_swallow(r, ci); + else if (t->subtype == task_subtype_sink_do_gas_swallow) + runner_do_sinks_gas_swallow_self(r, ci, 1); + else if (t->subtype == task_subtype_sink_do_sink_swallow) + runner_do_sinks_sink_swallow_self(r, ci, 1); else error("Unknown/invalid task subtype (%s).", subtaskID_names[t->subtype]); @@ -319,18 +276,16 @@ void *runner_main(void *data) { 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 if (t->subtype == task_subtype_rt_inject) - runner_dopair_branch_rt_inject(r, ci, cj, 1); else if (t->subtype == task_subtype_rt_gradient) runner_dopair1_branch_rt_gradient(r, ci, cj); else if (t->subtype == task_subtype_rt_transport) runner_dopair2_branch_rt_transport(r, ci, cj); - else if (t->subtype == task_subtype_sink_compute_formation) - runner_dopair_branch_sinks_compute_formation(r, ci, cj); - else if (t->subtype == task_subtype_sink_accretion) - runner_dopair_branch_sinks_accretion(r, ci, cj); - else if (t->subtype == task_subtype_sink_merger) - runner_do_sym_pair_sinks_merger(r, ci, cj); + else if (t->subtype == task_subtype_sink_swallow) + runner_dopair_branch_sinks_swallow(r, ci, cj); + else if (t->subtype == task_subtype_sink_do_gas_swallow) + runner_do_sinks_gas_swallow_pair(r, ci, cj, 1); + else if (t->subtype == task_subtype_sink_do_sink_swallow) + runner_do_sinks_sink_swallow_pair(r, ci, cj, 1); else error("Unknown/invalid task subtype (%s/%s).", taskID_names[t->type], subtaskID_names[t->subtype]); @@ -367,18 +322,16 @@ void *runner_main(void *data) { 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 if (t->subtype == task_subtype_rt_inject) - runner_dosub_self_rt_inject(r, ci, 1); else if (t->subtype == task_subtype_rt_gradient) runner_dosub_self1_rt_gradient(r, ci, 1); else if (t->subtype == task_subtype_rt_transport) runner_dosub_self2_rt_transport(r, ci, 1); - else if (t->subtype == task_subtype_sink_compute_formation) - runner_dosub_self_sinks_compute_formation(r, ci, 1); - else if (t->subtype == task_subtype_sink_accretion) - runner_dosub_self_sinks_accretion(r, ci, 1); - else if (t->subtype == task_subtype_sink_merger) - runner_dosub_self_sinks_merger(r, ci); + else if (t->subtype == task_subtype_sink_swallow) + runner_dosub_self_sinks_swallow(r, ci, 1); + else if (t->subtype == task_subtype_sink_do_gas_swallow) + runner_do_sinks_gas_swallow_self(r, ci, 1); + else if (t->subtype == task_subtype_sink_do_sink_swallow) + runner_do_sinks_sink_swallow_self(r, ci, 1); else error("Unknown/invalid task subtype (%s/%s).", taskID_names[t->type], subtaskID_names[t->subtype]); @@ -415,18 +368,16 @@ void *runner_main(void *data) { 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 if (t->subtype == task_subtype_rt_inject) - runner_dosub_pair_rt_inject(r, ci, cj, 1); else if (t->subtype == task_subtype_rt_gradient) runner_dosub_pair1_rt_gradient(r, ci, cj, 1); else if (t->subtype == task_subtype_rt_transport) runner_dosub_pair2_rt_transport(r, ci, cj, 1); - else if (t->subtype == task_subtype_sink_compute_formation) - runner_dosub_pair_sinks_compute_formation(r, ci, cj, 1); - else if (t->subtype == task_subtype_sink_accretion) - runner_dosub_pair_sinks_accretion(r, ci, cj, 1); - else if (t->subtype == task_subtype_sink_merger) - runner_dosub_pair_sinks_merger(r, ci, cj); + else if (t->subtype == task_subtype_sink_swallow) + runner_dosub_pair_sinks_swallow(r, ci, cj, 1); + else if (t->subtype == task_subtype_sink_do_gas_swallow) + runner_do_sinks_gas_swallow_pair(r, ci, cj, 1); + else if (t->subtype == task_subtype_sink_do_sink_swallow) + runner_do_sinks_sink_swallow_pair(r, ci, cj, 1); else error("Unknown/invalid task subtype (%s/%s).", taskID_names[t->type], subtaskID_names[t->subtype]); @@ -436,7 +387,19 @@ void *runner_main(void *data) { /* 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); + ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, + cell_get_flag(ci, cell_flag_rt_requests_sort), 1); + /* Reset the sort flags as our work here is done. */ + t->flags = 0; + break; + case task_type_rt_sort: + /* Cleanup only if any of the indices went stale. + * NOTE: we check whether we reset the sort flags when the + * recv tasks are running. Cells without an RT recv task + * don't have rt_sort tasks. */ + runner_do_hydro_sort( + r, ci, t->flags, + ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, 1, 1); /* Reset the sort flags as our work here is done. */ t->flags = 0; break; @@ -510,6 +473,9 @@ void *runner_main(void *data) { case task_type_collect: runner_do_timestep_collect(r, ci, 1); break; + case task_type_rt_collect_times: + runner_do_collect_rt_times(r, ci, 1); + break; #ifdef WITH_MPI case task_type_send: if (t->subtype == task_subtype_tend) { @@ -538,6 +504,10 @@ void *runner_main(void *data) { 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_rt_gradient) { + runner_do_recv_part(r, ci, 2, 1); + } else if (t->subtype == task_subtype_rt_transport) { + runner_do_recv_part(r, ci, -1, 1); } else if (t->subtype == task_subtype_part_swallow) { cell_unpack_part_swallow(ci, (struct black_holes_part_data *)t->buff); @@ -620,6 +590,9 @@ void *runner_main(void *data) { case task_type_rt_tchem: runner_do_rt_tchem(r, t->ci, 1); break; + case task_type_rt_advance_cell_time: + runner_do_rt_advance_cell_time(r, t->ci, 1); + break; default: error("Unknown/invalid task type (%d).", t->type); } diff --git a/src/runner_neutrino.c b/src/runner_neutrino.c index a771cfc115047536e23dbd99838fb83a77fd9168..22807a870ab3b7e3879a9d2f14398de390944294 100644 --- a/src/runner_neutrino.c +++ b/src/runner_neutrino.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner.h" diff --git a/src/runner_others.c b/src/runner_others.c index 6f7b776a0d2f4481bb702282fee644338efcd5c5..35aead47ba64e7f84f6faddc70e9f42987952498 100644 --- a/src/runner_others.c +++ b/src/runner_others.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -22,7 +22,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -50,6 +50,7 @@ #include "fof.h" #include "gravity.h" #include "hydro.h" +#include "potential.h" #include "pressure_floor.h" #include "rt.h" #include "space.h" @@ -233,7 +234,7 @@ void runner_do_star_formation_sink(struct runner *r, struct cell *c, } } else { - /* Loop over the gas particles in this cell. */ + /* Loop over the sink particles in this cell. */ for (int k = 0; k < count; k++) { /* Get a handle on the part. */ @@ -655,6 +656,7 @@ void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer) { /* Finish the force loop */ hydro_end_force(p, cosmo); + mhd_end_force(p, cosmo); timestep_limiter_end_force(p); chemistry_end_force(p, cosmo, with_cosmology, e->time, dt); @@ -668,12 +670,13 @@ void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer) { /* Don't move ! */ hydro_reset_acceleration(p); + mhd_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); + e->hydro_properties, 0, 0); rt_prepare_force(p); #endif } @@ -982,6 +985,10 @@ void runner_do_fof_pair(struct runner *r, struct cell *ci, struct cell *cj, TIMER_TIC; +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != cj->nodeID) error("Searching foreign cells!"); +#endif + const struct engine *e = r->e; struct space *s = e->s; const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; @@ -1019,7 +1026,7 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) { /* Anything to do here? */ if (count == 0) return; - if (!cell_is_active_hydro(c, e)) return; + if (!cell_is_rt_active(c, e)) return; TIMER_TIC; @@ -1029,7 +1036,6 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) { if (c->progeny[k] != NULL) runner_do_rt_tchem(r, c->progeny[k], 0); } else { - /* const struct cosmology *cosmo = e->cosmology; */ struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; @@ -1044,32 +1050,35 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) { if (part_is_inhibited(p, e)) continue; /* Skip inactive parts */ - if (!part_is_active(p, e)) continue; + if (!part_is_rt_active(p, e)) continue; /* Finish the force loop */ - const integertime_t ti_current = e->ti_current; - 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); + const integertime_t ti_current_subcycle = e->ti_current_subcycle; + const integertime_t ti_step = + get_integer_timestep(p->rt_time_data.time_bin); + const integertime_t ti_begin = get_integer_time_begin( + ti_current_subcycle + 1, p->rt_time_data.time_bin); const integertime_t ti_end = ti_begin + ti_step; + const double dt = + rt_part_dt(ti_begin, ti_end, e->time_base, with_cosmology, cosmo); #ifdef SWIFT_DEBUG_CHECKS - if (ti_begin != ti_current) + if (ti_begin != ti_current_subcycle) 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->limiter_data.wakeup, - ti_current); + ti_current_subcycle); + if (dt < 0.) + error("Got part with negative time-step: %lld, %.6g", p->id, dt); #endif - const double dt = rt_part_dt(ti_begin, ti_end, e->time_base, - with_cosmology, e->cosmology); - rt_finalise_transport(p, dt); + rt_finalise_transport(p, dt, cosmo); /* And finally do thermochemistry */ rt_tchem(p, xp, rt_props, cosmo, hydro_props, phys_const, us, dt); } } - if (timer) TIMER_TOC(timer_end_rt_tchem); + if (timer) TIMER_TOC(timer_do_rt_tchem); } diff --git a/src/runner_pack.c b/src/runner_pack.c index c337a5ad3cd942d7033113df70d238bcfb825fe8..d319290312e9aef2dbb36b73329bf3f4844e22e2 100644 --- a/src/runner_pack.c +++ b/src/runner_pack.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI diff --git a/src/runner_recv.c b/src/runner_recv.c index c37d9da771cb0ddd5c4a5cdd4e8353c5c4ef4036..c36f6d2349c29a6aa042f488ec9d49b524168153 100644 --- a/src/runner_recv.c +++ b/src/runner_recv.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -46,7 +46,7 @@ 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; + struct part *restrict parts = c->hydro.parts; const size_t nr_parts = c->hydro.count; const integertime_t ti_current = r->e->ti_current; const timebin_t max_active_bin = r->e->max_active_bin; @@ -63,6 +63,27 @@ void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts, if (c->nodeID == engine_rank) error("Updating a local cell!"); #endif + if (clear_sorts == 2) { + /* This is a call for the first RT recv task. Check whether + * we need to clear the sorts now. In the case of a foreign + * cell where no xv comms are done, but RT is active, we + * need to force a sort after the gradient recv. */ + clear_sorts = !cell_get_flag(c, cell_flag_skip_rt_sort); + cell_clear_flag(c, cell_flag_skip_rt_sort); + } else if (clear_sorts == -1) { + /* When running with RT and symmetric MPI exchanges, the first RT + * recv tasks (gradients) may not necessarily run, i.e. in cases + * where a local cell is inactive and needs to be updated by an + * active foreign cell. Should that occur, we need to make sure + * that the skip_rt_sort cell flag has been properly cleaned up + * first. */ + cell_clear_flag(c, cell_flag_skip_rt_sort); + /* clear_sorts == -1 is abused to mark the case where this + * function is called for a recv_rt_transport task. We're not + * actually sorting now, so don't clear the sorts. */ + clear_sorts = 0; + } + /* Clear this cell's sorted mask. */ if (clear_sorts) c->hydro.sorted = 0; @@ -75,6 +96,7 @@ void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts, 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); + parts[k].gpart = NULL; if (parts[k].time_bin <= max_active_bin) h_max_active = max(h_max_active, parts[k].h); } @@ -235,6 +257,7 @@ void runner_do_recv_spart(struct runner *r, struct cell *c, int clear_sorts, 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); + sparts[k].gpart = NULL; if (sparts[k].time_bin <= max_active_bin) h_max_active = max(h_max_active, sparts[k].h); } @@ -324,6 +347,7 @@ void runner_do_recv_bpart(struct runner *r, struct cell *c, int clear_sorts, 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); + bparts[k].gpart = NULL; if (bparts[k].time_bin <= max_active_bin) h_max_active = max(h_max_active, bparts[k].h); } diff --git a/src/runner_sinks.c b/src/runner_sinks.c new file mode 100644 index 0000000000000000000000000000000000000000..208a7e99471056dc660e4212078f551c56643ea0 --- /dev/null +++ b/src/runner_sinks.c @@ -0,0 +1,879 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Yves Revaz (yves.revaz@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include <config.h> + +/* This object's header. */ +#include "runner.h" + +/* Local headers. */ +#include "active.h" +#include "cell.h" +#include "engine.h" +#include "sink.h" +#include "space_getsid.h" +#include "timers.h" + +/** + * @brief Calculate gas and sink interaction around #sinks + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_doself_sinks_swallow(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; + + /* Anything to do here? */ + if (c->sinks.count == 0) return; + if (!cell_is_active_sinks(c, e)) return; + + const int scount = c->sinks.count; + const int count = c->hydro.count; + struct sink *restrict sinks = c->sinks.parts; + struct part *restrict parts = c->hydro.parts; + + /* Do we actually have any gas neighbours? */ + if (c->hydro.count != 0) { + + /* Loop over the sinks in ci. */ + for (int sid = 0; sid < scount; sid++) { + + /* Get a hold of the ith sinks in ci. */ + struct sink *restrict si = &sinks[sid]; + + /* Skip inactive particles */ + if (!sink_is_active(si, e)) continue; + + const float ri = si->r_cut; + const float ri2 = ri * ri; + 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 (gas) in cj. */ + for (int pjd = 0; pjd < count; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts[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])}; + const 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 (si->ti_drift != e->ti_current) + error("Particle si not drifted to current time"); + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + if (r2 < ri2) { + runner_iact_nonsym_sinks_gas_swallow(r2, dx, ri, hj, si, pj); + } + } /* loop over the parts in ci. */ + } /* loop over the bparts in ci. */ + } /* Do we have gas particles in the cell? */ + + /* When doing sink swallowing, we need a quick loop also over the sink + * neighbours */ + + /* Loop over the sinks in ci. */ + for (int sid = 0; sid < scount; sid++) { + + /* Get a hold of the ith sink in ci. */ + struct sink *restrict si = &sinks[sid]; + + /* Skip inactive particles */ + if (!sink_is_active(si, e)) continue; + + const float ri = si->r_cut; + const float ri2 = ri * ri; + 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 sinks in cj. */ + for (int sjd = 0; sjd < scount; sjd++) { + + /* Skip self interaction */ + if (sid == sjd) continue; + + /* Get a pointer to the jth particle. */ + struct sink *restrict sj = &sinks[sjd]; + const float rj = sj->r_cut; + const float rj2 = rj * rj; + + /* Early abort? */ + if (sink_is_inhibited(sj, e)) continue; + + /* Compute the pairwise distance. */ + const float sjx[3] = {(float)(sj->x[0] - c->loc[0]), + (float)(sj->x[1] - c->loc[1]), + (float)(sj->x[2] - c->loc[2])}; + const float dx[3] = {six[0] - sjx[0], six[1] - sjx[1], six[2] - sjx[2]}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + + if (r2 < ri2 || r2 < rj2) { + runner_iact_nonsym_sinks_sink_swallow(r2, dx, ri, rj, si, sj); + } + } /* loop over the sinks in ci. */ + } /* loop over the sinks in ci. */ + + if (timer) TIMER_TOC(timer_doself_sink_swallow); +} + +/** + * @brief Calculate gas and sink interaction around #sinks + * + * @param r runner task + * @param ci The first #cell + * @param cj The second #cell + */ +void runner_do_nonsym_pair_sinks_naive_swallow(struct runner *r, + struct cell *restrict ci, + struct cell *restrict cj) { + + const struct engine *e = r->e; + + /* Anything to do here? */ + if (ci->sinks.count == 0) return; + if (!cell_is_active_sinks(ci, e)) return; + + const int scount_i = ci->sinks.count; + const int count_j = cj->hydro.count; + struct sink *restrict sinks_i = ci->sinks.parts; + struct part *restrict parts_j = cj->hydro.parts; + + /* 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 sinks in ci. */ + for (int sid = 0; sid < scount_i; sid++) { + + /* Get a hold of the ith bpart in ci. */ + struct sink *restrict si = &sinks_i[sid]; + + /* Skip inactive particles */ + if (!sink_is_active(si, e)) continue; + + const float ri = si->r_cut; + const float ri2 = ri * ri; + const float six[3] = {(float)(si->x[0] - cj->loc[0]), + (float)(si->x[1] - cj->loc[1]), + (float)(si->x[2] - cj->loc[2])}; + + /* Loop over the parts (gas) in cj. */ + for (int pjd = 0; pjd < count_j; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_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])}; + const 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 (si->ti_drift != e->ti_current) + error("Particle si not drifted to current time"); + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + if (r2 < ri2) { + runner_iact_nonsym_sinks_gas_swallow(r2, dx, ri, hj, si, pj); + } + } /* loop over the parts in cj. */ + } /* loop over the sinks in ci. */ + } /* Do we have gas particles in the cell? */ + + /* When doing sink swallowing, we need a quick loop also over the sinks + * neighbours */ + + const int scount_j = cj->sinks.count; + struct sink *restrict sinks_j = cj->sinks.parts; + + /* Loop over the sinks in ci. */ + for (int sid = 0; sid < scount_i; sid++) { + + /* Get a hold of the ith bpart in ci. */ + struct sink *restrict si = &sinks_i[sid]; + + /* Skip inactive particles */ + if (!sink_is_active(si, e)) continue; + + const float ri = si->r_cut; + const float ri2 = ri * ri; + 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 sinks in cj. */ + for (int sjd = 0; sjd < scount_j; sjd++) { + + /* Get a pointer to the jth particle. */ + struct sink *restrict sj = &sinks_j[sjd]; + const float rj = sj->r_cut; + const float rj2 = rj * rj; + + /* Skip inhibited particles. */ + if (sink_is_inhibited(sj, e)) continue; + + /* Compute the pairwise distance. */ + const float sjx[3] = {(float)(sj->x[0] - cj->loc[0]), + (float)(sj->x[1] - cj->loc[1]), + (float)(sj->x[2] - cj->loc[2])}; + const float dx[3] = {six[0] - sjx[0], six[1] - sjx[1], six[2] - sjx[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 (si->ti_drift != e->ti_current) + error("Particle si not drifted to current time"); + if (sj->ti_drift != e->ti_current) + error("Particle sj not drifted to current time"); +#endif + + if (r2 < ri2 || r2 < rj2) { + runner_iact_nonsym_sinks_sink_swallow(r2, dx, ri, rj, si, sj); + } + } /* loop over the sinks in cj. */ + } /* loop over the sinks in ci. */ +} + +/** + * @brief Calculate swallow for ci #sinks part around the cj #gas and sinks and + * cj #sinks part around the ci #gas and sinks + * + * @param r runner task + * @param ci The first #cell + * @param cj The second #cell + */ +void runner_dopair_sinks_naive_swallow(struct runner *r, + struct cell *restrict ci, + struct cell *restrict cj, int timer) { + + TIMER_TIC; + + runner_do_nonsym_pair_sinks_naive_swallow(r, ci, cj); + runner_do_nonsym_pair_sinks_naive_swallow(r, cj, ci); + + if (timer) TIMER_TOC(timer_dopair_sink_swallow); +} + +/** + * @brief Wrapper to runner_doself_sinks_swallow + * + * @param r #runner + * @param c #cell c + * + */ +void runner_doself_branch_sinks_swallow(struct runner *r, struct cell *c) { + + const struct engine *restrict e = r->e; + + /* Anything to do here? */ + if (c->sinks.count == 0) return; + + /* Anything to do here? */ + if (!cell_is_active_sinks(c, e)) return; + + /* Did we mess up the recursion? */ + if (c->sinks.r_cut_max_old > c->dmin) + error("Cell smaller than the cut off radius"); + + runner_doself_sinks_swallow(r, c, 1); +} + +/** + * @brief Wrapper for runner_dopair_sinks_naive_swallow. + * + * @param r #runner + * @param ci #cell ci + * @param cj #cell cj + * + */ +void runner_dopair_branch_sinks_swallow(struct runner *r, struct cell *ci, + struct cell *cj) { + + const struct engine *restrict e = r->e; + + const int ci_active = cell_is_active_sinks(ci, e); + const int cj_active = cell_is_active_sinks(cj, e); + + const int do_ci = (ci->sinks.count != 0 && cj->hydro.count != 0 && ci_active); + const int do_cj = (cj->sinks.count != 0 && ci->hydro.count != 0 && cj_active); + + /* Anything to do here? */ + if (!do_ci && !do_cj) return; + + /* Check that cells are drifted. */ + if (do_ci && (!cell_are_sink_drifted(ci, e) || !cell_are_part_drifted(cj, e))) + error("Interacting undrifted cells."); + + if (do_cj && (!cell_are_part_drifted(ci, e) || !cell_are_sink_drifted(cj, e))) + error("Interacting undrifted cells."); + + /* No sorted interactions here -> use the naive ones */ + runner_dopair_sinks_naive_swallow(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 runner_dosub_pair_sinks_swallow(struct runner *r, struct cell *ci, + struct cell *cj, int timer) { + + 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 sink-sink and sink-gas + * interactions. */ + + const int should_do_ci = ci->sinks.count != 0 && cell_is_active_sinks(ci, e); + const int should_do_cj = cj->sinks.count != 0 && cell_is_active_sinks(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_sinks_task(ci, cj) && + cell_can_recurse_in_pair_sinks_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) + runner_dosub_pair_sinks_swallow(r, ci->progeny[pid], cj->progeny[pjd], + 0); + } + } + + /* Otherwise, compute the pair directly. */ + else { + + const int do_ci = ci->sinks.count != 0 && cell_is_active_sinks(ci, e); + const int do_cj = cj->sinks.count != 0 && cell_is_active_sinks(cj, e); + + if (do_ci) { + + /* Make sure both cells are drifted to the current timestep. */ + if (!cell_are_sink_drifted(ci, e)) + error("Interacting undrifted cells (sinks)."); + + 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_sink_drifted(cj, e)) + error("Interacting undrifted cells (sinks)."); + } + + if (do_ci || do_cj) runner_dopair_branch_sinks_swallow(r, ci, cj); + } + + if (timer) TIMER_TOC(timer_dosub_pair_sink_swallow); +} + +/** + * @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 runner_dosub_self_sinks_swallow(struct runner *r, struct cell *ci, + int timer) { + + 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 sink-sink and sink-gas + * interactions. */ + + const int should_do_ci = ci->sinks.count != 0 && cell_is_active_sinks(ci, e); + + if (!should_do_ci) return; + + /* Recurse? */ + if (cell_can_recurse_in_self_sinks_task(ci)) { + + /* Loop over all progeny. */ + for (int k = 0; k < 8; k++) + if (ci->progeny[k] != NULL) { + runner_dosub_self_sinks_swallow(r, ci->progeny[k], 0); + for (int j = k + 1; j < 8; j++) + if (ci->progeny[j] != NULL) + runner_dosub_pair_sinks_swallow(r, ci->progeny[k], ci->progeny[j], + 0); + } + } + + /* Otherwise, compute self-interaction. */ + else { + + /* Check we did drift to the current time */ + if (!cell_are_sink_drifted(ci, e)) error("Interacting undrifted cell."); + + if (ci->hydro.count != 0 && !cell_are_part_drifted(ci, e)) + error("Interacting undrifted cells (parts)."); + + runner_doself_branch_sinks_swallow(r, ci); + } + + if (timer) TIMER_TOC(timer_dosub_self_sink_swallow); +} + +/** + * @brief Process all the gas particles in a cell that have been flagged for + * swallowing by a sink. + * + * 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 sink for the particle with the corresponding + * ID. If found, the sink swallows the gas particle and the gas particle is + * removed. If the cell is local, we may be looking for a foreign sink, in which + * case, we do not update the sink (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_sinks_gas_swallow(struct runner *r, struct cell *c, int timer) { + + struct engine *e = r->e; + struct space *s = e->s; + + struct sink *sinks = s->sinks; + const size_t nr_sink = s->nr_sinks; +#ifdef WITH_MPI + error("MPI is not implemented yet for sink particles."); +#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_sinks_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 sink that will swallow this part */ + const long long swallow_id = sink_get_part_swallow_id(&p->sink_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 sink swallowing this particle */ + const long long sink_id = swallow_id; + + /* Have we found this particle's sink already? */ + int found = 0; + + /* Let's look for the hungry sink in the local list */ + for (size_t i = 0; i < nr_sink; ++i) { + + /* Get a handle on the bpart. */ + struct sink *sp = &sinks[i]; + + if (sp->id == sink_id) { + + /* Lock the space as we are going to work directly on the spart list + */ + lock_lock(&s->lock); + + /* Swallow the gas particle (i.e. update the sink properties) */ + sink_swallow_part(sp, p, xp, e->cosmology); + + /* Release the space as we are done updating the spart */ + if (lock_unlock(&s->lock) != 0) + error("Failed to unlock the space."); + + /* If the gas particle is local, remove it */ + if (c->nodeID == e->nodeID) { + + message("sink %lld removing gas particle %lld", sp->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 */ + sink_mark_part_as_swallowed(&p->sink_data); + + found = 1; + break; + } + + } /* Loop over local sinks */ + +#ifdef WITH_MPI + error("MPI is not implemented yet for sink particles."); +#endif + + /* If we have a local particle, we must have found the sink in one + * of our list of sinks. */ + if (c->nodeID == e->nodeID && !found) { + error("Gas particle %lld could not find sink %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_sinks_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_sinks(c, r->e)) + error("Running self task on inactive cell"); +#endif + + runner_do_sinks_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_sinks_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 sink */ + if (cell_is_active_sinks(cj, e)) runner_do_sinks_gas_swallow(r, ci, timer); + if (cell_is_active_sinks(ci, e)) runner_do_sinks_gas_swallow(r, cj, timer); +} + +/** + * @brief Process all the sink particles in a cell that have been flagged for + * swallowing by a sink. + * + * 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 sinks for the particle with the corresponding + * ID. If found, the sink swallows the sink particle and the sink particle is + * removed. If the cell is local, we may be looking for a foreign sink, in which + * case, we do not update the sink (that will be done on its node) but just + * remove the sink particle. + * + * @param r The thread #runner. + * @param c The #cell. + * @param timer Are we timing this? + */ +void runner_do_sinks_sink_swallow(struct runner *r, struct cell *c, int timer) { + + struct engine *e = r->e; + struct space *s = e->s; + + struct sink *sinks = s->sinks; + const size_t nr_sink = s->nr_sinks; +#ifdef WITH_MPI + error("MPI is not implemented yet for sink particles."); +#endif + + struct sink *cell_sinks = c->sinks.parts; + + /* Early abort? + * (We only want cells for which we drifted the sink as these are + * the only ones that could have sink particles that have been flagged + * for swallowing) */ + if (c->sinks.count == 0 || c->sinks.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_sinks_sink_swallow(r, cp, 0); + } + } + } else { + + /* Loop over all the sinks particles in the cell + * Note that the cell (and hence the sinks) may be local or foreign. */ + const size_t nr_cell_sinks = c->sinks.count; + + for (size_t k = 0; k < nr_cell_sinks; k++) { + + /* Get a handle on the part. */ + struct sink *const cell_sp = &cell_sinks[k]; + + /* Ignore inhibited particles (they have already been removed!) */ + if (sink_is_inhibited(cell_sp, e)) continue; + + /* Get the ID of the sink that will swallow this sink */ + const long long swallow_id = + sink_get_sink_swallow_id(&cell_sp->merger_data); + + /* Has this particle been flagged for swallowing? */ + if (swallow_id >= 0) { + +#ifdef SWIFT_DEBUG_CHECKS + if (cell_sp->ti_drift != e->ti_current) + error("Trying to swallow an un-drifted particle."); +#endif + + /* ID of the sink swallowing this particle */ + const long long sink_id = swallow_id; + + /* Have we found this particle's sink already? */ + int found = 0; + + /* Let's look for the hungry sink in the local list */ + for (size_t i = 0; i < nr_sink; ++i) { + + /* Get a handle on the bpart. */ + struct sink *sp = &sinks[i]; + + if (sp->id == sink_id) { + + /* Is the swallowing sink itself flagged for swallowing by + another sink? */ + if (sink_get_sink_swallow_id(&sp->merger_data) != -1) { + + /* Pretend it was found and abort */ + sink_mark_sink_as_not_swallowed(&cell_sp->merger_data); + found = 1; + break; + } + + /* Lock the space as we are going to work directly on the + * space's bpart list */ + lock_lock(&s->lock); + + /* Swallow the sink particle (i.e. update the swallowing sink + * properties with the properties of cell_sp) */ + sink_swallow_sink(sp, cell_sp, e->cosmology); + + /* Release the space as we are done updating the spart */ + if (lock_unlock(&s->lock) != 0) + error("Failed to unlock the space."); + + // message("sink %lld swallowing sink particle %lld", sp->id, + // cell_sp->id); + + /* If the sink particle is local, remove it */ + if (c->nodeID == e->nodeID) { + + message("sink %lld removing sink particle %lld", sp->id, + cell_sp->id); + + /* Finally, remove the sink particle from the system + * Recall that the gpart associated with it is also removed + * at the same time. */ + cell_remove_sink(e, c, cell_sp); + } + + /* In any case, prevent the particle from being re-swallowed */ + sink_mark_sink_as_merged(&cell_sp->merger_data); + + found = 1; + break; + } + + } /* Loop over local sinks */ + +#ifdef WITH_MPI + error("MPI is not implemented yet for sink particles."); +#endif + + /* If we have a local particle, we must have found the sink in one + * of our list of sinks. */ + if (c->nodeID == e->nodeID && !found) { + error("sink particle %lld could not find sink %lld to be swallowed", + cell_sp->id, swallow_id); + } + + } /* Part was flagged for swallowing */ + } /* Loop over the parts */ + } /* Cell is not split */ +} + +/** + * @brief Processing of sink particles to swallow - self task case. + * + * @param r The thread #runner. + * @param c The #cell. + * @param timer Are we timing this? + */ +void runner_do_sinks_sink_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_sinks(c, r->e)) + error("Running self task on inactive cell"); +#endif + + runner_do_sinks_sink_swallow(r, c, timer); +} + +/** + * @brief Processing of sink 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_sinks_sink_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 sink */ + if (cell_is_active_sinks(cj, e)) runner_do_sinks_sink_swallow(r, ci, timer); + if (cell_is_active_sinks(ci, e)) runner_do_sinks_sink_swallow(r, cj, timer); +} diff --git a/src/runner_sort.c b/src/runner_sort.c index 76a4922404f3d3a86198391ea4f91533a5f60779..550626517f861769bd38fa64db04a2b0270b81fe 100644 --- a/src/runner_sort.c +++ b/src/runner_sort.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner.h" @@ -193,11 +193,13 @@ RUNNER_CHECK_SORTS(stars) * @param flags Cell flag. * @param cleanup If true, re-build the sorts for the selected flags instead * of just adding them. + * @param rt_requests_sort whether this sort was requested for RT. If true, + * this cell is allowed to be undrifted. * @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) { + int cleanup, int rt_requests_sort, int clock) { struct sort_entry *fingers[8]; const int count = c->hydro.count; @@ -218,17 +220,22 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, } else { flags &= ~c->hydro.sorted; } - if (flags == 0 && !cell_get_flag(c, cell_flag_do_hydro_sub_sort)) return; + if (flags == 0 && !cell_get_flag(c, cell_flag_do_hydro_sub_sort) && + !cell_get_flag(c, cell_flag_do_rt_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); + if (flags && !cell_are_part_drifted(c, r->e)) { + /* If the sort was requested by RT, cell may be intentionally + * undrifted. */ + if (!rt_requests_sort) error("Sorting un-drifted cell"); + } #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). */ + /* 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) @@ -259,7 +266,7 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, r, c->progeny[k], flags, cleanup && (c->progeny[k]->hydro.dx_max_sort_old > space_maxreldx * c->progeny[k]->dmin), - 0); + rt_requests_sort, 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); @@ -416,6 +423,8 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, /* Clear the cell's sort flags. */ c->hydro.do_sort = 0; cell_clear_flag(c, cell_flag_do_hydro_sub_sort); + cell_clear_flag(c, cell_flag_do_rt_sub_sort); + cell_clear_flag(c, cell_flag_rt_requests_sort); c->hydro.requires_sorts = 0; if (clock) TIMER_TOC(timer_dosort); @@ -673,7 +682,8 @@ void runner_do_all_hydro_sort(struct runner *r, struct cell *c) { if (c->hydro.super == c) { /* Sort everything */ - runner_do_hydro_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); + runner_do_hydro_sort(r, c, 0x1FFF, /*cleanup=*/0, /*rt_requests_sort=*/0, + /*timer=*/0); } else { diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c index 02c601043fbdb1575b6ef1e62900a183b6e0a864..7b31072f34fa9407da2b2c282131354b96e9d1df 100644 --- a/src/runner_time_integration.c +++ b/src/runner_time_integration.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "runner.h" @@ -636,12 +636,14 @@ void runner_do_kick2(struct runner *r, struct cell *c, const int timer) { void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { const struct engine *e = r->e; const integertime_t ti_current = e->ti_current; + const integertime_t ti_current_subcycle = e->ti_current_subcycle; const struct cosmology *cosmo = e->cosmology; const struct feedback_props *feedback_props = e->feedback_props; const struct unit_system *us = e->internal_units; const struct phys_const *phys_const = e->physical_constants; const int with_cosmology = (e->policy & engine_policy_cosmology); const int with_feedback = (e->policy & engine_policy_feedback); + const int with_rt = (e->policy & engine_policy_rt); const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; @@ -660,11 +662,14 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && !cell_is_active_stars(c, e) && !cell_is_active_sinks(c, e) && !cell_is_active_black_holes(c, e)) { + /* Note: cell_is_rt_active is deliberately skipped. We only change + * the RT subcycling time steps when particles are hydro active. */ c->hydro.updated = 0; c->grav.updated = 0; c->stars.updated = 0; c->sinks.updated = 0; c->black_holes.updated = 0; + c->rt.updated = 0; return; } @@ -672,6 +677,8 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { 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_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0; + integertime_t ti_rt_min_step_size = max_nr_timesteps; 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, @@ -701,19 +708,48 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { if (ti_end != ti_current) error("Computing time-step of rogue particle."); + + if (with_rt) { + const integertime_t ti_rt_end = get_integer_time_end( + ti_current_subcycle, p->rt_time_data.time_bin); + if (ti_rt_end != ti_current_subcycle) + error("Computing RT time-step of rogue particle"); + } #endif + /* Old time-step length in physical units */ + const integertime_t ti_old_step = get_integer_timestep(p->time_bin); + double old_time_step_length; + if (with_cosmology) { + old_time_step_length = cosmology_get_delta_time( + e->cosmology, e->ti_current - ti_old_step, e->ti_current); + } else { + old_time_step_length = get_timestep(p->time_bin, e->time_base); + } /* Get new time-step */ - const integertime_t ti_new_step = get_part_timestep(p, xp, e); + integertime_t ti_rt_new_step = get_part_rt_timestep(p, xp, e); + const integertime_t ti_new_step = + get_part_timestep(p, xp, e, ti_rt_new_step); + /* Enforce RT time-step size <= hydro step size. */ + ti_rt_new_step = min(ti_new_step, ti_rt_new_step); + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* For the DEBUG RT scheme, this sets the RT time step to be + * (dt_hydro / max_nr_sub_cycles). For others, this does a proper + * debugging/consistency check. */ + rt_debugging_check_timestep(p, &ti_rt_new_step, &ti_new_step, + e->max_nr_rt_subcycles, e->time_base); +#endif /* 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); + tracers_after_timestep_part( + p, xp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, e->hydro_properties, e->cooling_func, e->time, + old_time_step_length, e->snapshot_recording_triggers_started_part); /* Number of updated particles */ updated++; @@ -737,6 +773,16 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); } + + /* Same for RT */ + if (with_rt) { + p->rt_time_data.time_bin = get_time_bin(ti_rt_new_step); + ti_rt_end_min = + min(ti_current_subcycle + ti_rt_new_step, ti_rt_end_min); + ti_rt_beg_max = + max(ti_current_subcycle + ti_rt_new_step, ti_rt_beg_max); + ti_rt_min_step_size = min(ti_rt_min_step_size, ti_rt_new_step); + } } else { /* part is inactive */ @@ -756,6 +802,27 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { /* What is the next starting point for this cell ? */ ti_hydro_beg_max = max(ti_beg, ti_hydro_beg_max); + /* Same for RT. */ + if (with_rt) { + /* Here we assume that the particle is inactive, which is true for + * hydro, but not necessarily for a RT subcycle. RT time steps are + * only changed while the particle is hydro active. This allows to + * end up with results ti_rt_end == ti_current_subcyle, so we need + * to pretend we're past ti_current_subcycle already. */ + integertime_t ti_rt_end = get_integer_time_end( + ti_current_subcycle + 1, p->rt_time_data.time_bin); + + const integertime_t ti_rt_beg = get_integer_time_begin( + ti_current_subcycle + 1, p->rt_time_data.time_bin); + + ti_rt_end_min = min(ti_rt_end, ti_rt_end_min); + ti_rt_beg_max = max(ti_rt_beg, ti_rt_beg_max); + + integertime_t ti_rt_step = + get_integer_timestep(p->rt_time_data.time_bin); + ti_rt_min_step_size = min(ti_rt_min_step_size, ti_rt_step); + } + if (p->gpart != NULL) { /* What is the next sync-point ? */ @@ -848,6 +915,16 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { if (ti_end != ti_current) error("Computing time-step of rogue particle."); #endif + /* Old time-step length in physical units */ + const integertime_t ti_old_step = get_integer_timestep(sp->time_bin); + double old_time_step_length; + if (with_cosmology) { + old_time_step_length = cosmology_get_delta_time( + e->cosmology, e->ti_current - ti_old_step, e->ti_current); + } else { + old_time_step_length = get_timestep(sp->time_bin, e->time_base); + } + /* Get new time-step */ const integertime_t ti_new_step = get_spart_timestep(sp, e); @@ -855,6 +932,12 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { sp->time_bin = get_time_bin(ti_new_step); sp->gpart->time_bin = get_time_bin(ti_new_step); + /* Update the tracers properties */ + tracers_after_timestep_spart( + sp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, old_time_step_length, + e->snapshot_recording_triggers_started_spart); + /* Update feedback related counters */ if (with_feedback) { @@ -976,6 +1059,16 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { if (ti_end != ti_current) error("Computing time-step of rogue particle."); #endif + /* Old time-step length in physical units */ + const integertime_t ti_old_step = get_integer_timestep(bp->time_bin); + double old_time_step_length; + if (with_cosmology) { + old_time_step_length = cosmology_get_delta_time( + e->cosmology, e->ti_current - ti_old_step, e->ti_current); + } else { + old_time_step_length = get_timestep(bp->time_bin, e->time_base); + } + /* Get new time-step */ const integertime_t ti_new_step = get_bpart_timestep(bp, e); @@ -983,6 +1076,12 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { bp->time_bin = get_time_bin(ti_new_step); bp->gpart->time_bin = get_time_bin(ti_new_step); + /* Update the tracers properties */ + tracers_after_timestep_bpart( + bp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, old_time_step_length, + e->snapshot_recording_triggers_started_bpart); + /* Number of updated s-particles */ b_updated++; g_updated++; @@ -1041,6 +1140,11 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); + ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min); + ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max); + ti_rt_min_step_size = + min(cp->rt.ti_rt_min_step_size, ti_rt_min_step_size); + ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); @@ -1064,9 +1168,19 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { c->stars.updated = s_updated; c->sinks.updated = sink_updated; c->black_holes.updated = b_updated; + /* We don't count the RT updates here because the + * timestep tasks aren't active during sub-cycles. + * We do that in rt_advanced_cell_time instead. */ c->hydro.ti_end_min = ti_hydro_end_min; c->hydro.ti_beg_max = ti_hydro_beg_max; + c->rt.ti_rt_end_min = ti_rt_end_min; + c->rt.ti_rt_beg_max = ti_rt_beg_max; + if (cell_is_starting_hydro(c, e)) { + /* We only change the RT time steps when the cell is also hydro active. + * Without this check here, ti_rt_min_step_size = max_nr_steps... */ + c->rt.ti_rt_min_step_size = ti_rt_min_step_size; + } c->grav.ti_end_min = ti_gravity_end_min; c->grav.ti_beg_max = ti_gravity_beg_max; c->stars.ti_end_min = ti_stars_end_min; @@ -1092,6 +1206,12 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { 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!"); + /* Contrary to sinks, stars, bhs etc, we may have "rt particles" + * without running with RT. So additional if (with_rt) check is + * needed here. */ + if (with_rt && (c->rt.ti_rt_end_min == e->ti_current && + c->rt.ti_rt_end_min < max_nr_timesteps)) + error("Cell %lld End of next RT step is current time!", c->cellID); #endif if (timer) TIMER_TOC(timer_timestep); @@ -1118,7 +1238,10 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c, size_t s_updated = 0; size_t b_updated = 0; size_t si_updated = 0; + size_t rt_updated = 0; + integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0; + integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0; integertime_t ti_grav_end_min = max_nr_timesteps, ti_grav_beg_max = 0; integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_beg_max = 0; integertime_t ti_black_holes_end_min = max_nr_timesteps, @@ -1136,6 +1259,8 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c, /* And update */ ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min); ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max); + ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min); + ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max); ti_grav_end_min = min(ti_grav_end_min, cp->grav.ti_end_min); ti_grav_beg_max = max(ti_grav_beg_max, cp->grav.ti_beg_max); ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); @@ -1152,6 +1277,7 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c, s_updated += cp->stars.updated; b_updated += cp->black_holes.updated; si_updated += cp->sinks.updated; + rt_updated += cp->rt.updated; /* Collected, so clear for next time. */ cp->hydro.updated = 0; @@ -1159,12 +1285,15 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c, cp->stars.updated = 0; cp->black_holes.updated = 0; cp->sinks.updated = 0; + cp->rt.updated = 0; } } /* Store the collected values in the cell. */ c->hydro.ti_end_min = ti_hydro_end_min; c->hydro.ti_beg_max = ti_hydro_beg_max; + c->rt.ti_rt_end_min = ti_rt_end_min; + c->rt.ti_rt_beg_max = ti_rt_beg_max; c->grav.ti_end_min = ti_grav_end_min; c->grav.ti_beg_max = ti_grav_beg_max; c->stars.ti_end_min = ti_stars_end_min; @@ -1179,6 +1308,7 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c, c->stars.updated = s_updated; c->black_holes.updated = b_updated; c->sinks.updated = si_updated; + c->rt.updated = rt_updated; } /** @@ -1275,6 +1405,14 @@ void runner_do_limiter(struct runner *r, struct cell *c, int force, /* Bip, bip, bip... wake-up time */ if (p->limiter_data.wakeup != time_bin_not_awake) { + if (!part_is_active(p, e) && p->limiter_data.to_be_synchronized) { + warning( + "Not limiting particle with id %lld because it needs to be " + "synced.", + p->id); + continue; + } + // message("Limiting particle %lld in cell %lld", p->id, c->cellID); /* Apply the limiter and get the new end of time-step */ @@ -1420,22 +1558,46 @@ void runner_do_sync(struct runner *r, struct cell *c, int force, /* Finish this particle's time-step */ timestep_process_sync_part(p, xp, e, cosmo); + /* Note that at this moment the new RT time step is only used to + * limit the hydro time step here. */ + integertime_t ti_rt_new_step = get_part_rt_timestep(p, xp, e); /* Get new time-step */ - integertime_t ti_new_step = get_part_timestep(p, xp, e); + integertime_t ti_new_step = get_part_timestep(p, xp, e, ti_rt_new_step); timebin_t new_time_bin = get_time_bin(ti_new_step); + /* Enforce RT time-step size <= hydro step size. */ + /* On the commented out line below: We should be doing this once we + * correctly add RT to this part of the code. */ + /* ti_rt_new_step = min(ti_new_step, ti_rt_new_step); */ + + /* Apply the limiter if necessary */ + if (p->limiter_data.wakeup != time_bin_not_awake) { + new_time_bin = min(new_time_bin, -p->limiter_data.wakeup + 2); + p->limiter_data.wakeup = time_bin_not_awake; + } /* Limit the time-bin to what is allowed in this step */ new_time_bin = min(new_time_bin, e->max_active_bin); ti_new_step = get_integer_timestep(new_time_bin); + /* Time-step length in physical units */ + // MATTHIEU: TODO: think about this one! + double time_step_length; + if (with_cosmology) { + time_step_length = cosmology_get_delta_time( + e->cosmology, e->ti_current, e->ti_current + ti_new_step); + } else { + time_step_length = get_timestep(new_time_bin, e->time_base); + } + /* Update particle */ p->time_bin = new_time_bin; if (p->gpart != NULL) p->gpart->time_bin = new_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); + tracers_after_timestep_part( + p, xp, e->internal_units, e->physical_constants, with_cosmology, + e->cosmology, e->hydro_properties, e->cooling_func, e->time, + 0 * time_step_length, e->snapshot_recording_triggers_started_part); #ifdef SWIFT_HYDRO_DENSITY_CHECKS p->limited_part = 1; @@ -1478,3 +1640,157 @@ void runner_do_sync(struct runner *r, struct cell *c, int force, if (timer) TIMER_TOC(timer_do_sync); } + +/** + * @brief Update the cell's t_rt_end_min so that the sub-cycling can proceed + * with correct cell times. (During sub-cycles, the regular timestep and + * timestep_collect tasks do not run. This replaces the collection of cell + * times of timestep tasks during sub-cycles. ) + * + * @param r The #runner thread. + * @param c The #cell. + * @param timer Are we timing this ? + */ +void runner_do_rt_advance_cell_time(struct runner *r, struct cell *c, + int timer) { + + struct engine *e = r->e; + const int count = c->hydro.count; + /* Reset update count regardless whether cell is active or not */ + c->rt.updated = 0; + +#ifdef SWIFT_RT_DEBUG_CHECKS + if (c->super == c) c->rt.advanced_time = 1; +#endif + + /* Anything to do here? */ + if (count == 0) return; + if (!cell_is_rt_active(c, e)) return; + + TIMER_TIC; + + int rt_updated = 0; + + if (c->split) { + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL) { + runner_do_rt_advance_cell_time(r, cp, 0); + rt_updated += cp->rt.updated; + } + } + } else { + /* Do some debugging stuff on active particles before setting the cell time. + * This test is not reliable on foreign cells. After a rebuild, we may end + * up with an active foreign cell which was not updated in this step because + * it had no active neighbouring cells, and its particle data may be random + * junk. */ + + if (c->nodeID == engine_rank) { + struct part *restrict parts = c->hydro.parts; + + /* Loop over the gas particles in this cell. */ + for (int i = 0; i < count; i++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[i]; + + /* Skip inhibited parts */ + if (part_is_inhibited(p, e)) continue; + + /* Skip inactive parts */ + if (!part_is_rt_active(p, e)) continue; + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* Run checks. */ + rt_debug_sequence_check(p, 5, __func__); + /* Mark that the subcycling has happened */ + rt_debugging_count_subcycle(p); +#endif + rt_updated++; + } + } + } + + c->rt.updated = rt_updated; + + /* Note: c->rt.ti_rt_min_step_size may be greater than + * c->super->rt.ti_rt_min_step_size. This is expected behaviour. + * We only update the cell's own time after it's been active. */ + c->rt.ti_rt_end_min += c->rt.ti_rt_min_step_size; + + if (timer) TIMER_TOC(timer_do_rt_advance_cell_time); +} + +/** + * @brief Recursively collect the end-of-timestep information from the top-level + * to the super level for the RT sub-cycling. (During sub-cycles, the regular + * timestep and timestep_collect tasks do not run. This replaces the + * timestep_collect task.) + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_collect_rt_times(struct runner *r, struct cell *c, + const int timer) { + + const struct engine *e = r->e; + size_t rt_updated = 0; + + if (e->ti_current == e->ti_current_subcycle) + error("called collect_rt_times during a main step"); + + /* Early stop if we are at the super level. + * The time-step/rt_advance_cell_time tasks would have set things at + * this level already. */ + + if (c->super == c) { +#ifdef SWIFT_RT_DEBUG_CHECKS + /* Do a check before the early exit. + * rt_advanced_cell_time should be called exactly once before + * collect times. Except on the first subcycle, because the + * collect_rt_times task shouldn't be called in the main steps. + * In that case, it should be exactly 2. */ + if (e->ti_current_subcycle - c->rt.ti_rt_end_min == e->ti_current) { + /* This is the first subcycle */ + if (c->rt.advanced_time != 2) + error("Called cell with wrong advanced_time counter. Expect=2, got=%d", + c->rt.advanced_time); + } else { + if (c->rt.advanced_time != 1) + error("Called cell with wrong advanced_time counter. Expect=1, got=%d", + c->rt.advanced_time); + } + c->rt.advanced_time = 0; +#endif + return; + } + + integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_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) { + + /* Recurse */ + runner_do_collect_rt_times(r, cp, 0); + + /* And update */ + ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min); + ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max); + + /* Collected, so clear for next time. */ + rt_updated += cp->rt.updated; + cp->rt.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->rt.ti_rt_end_min = ti_rt_end_min; + c->rt.ti_rt_beg_max = ti_rt_beg_max; + c->rt.updated = rt_updated; + + if (timer) TIMER_TOC(timer_do_rt_collect_times); +} diff --git a/src/scheduler.c b/src/scheduler.c index bb93cd40cece2541ece1d7102c47d4f5aa2e4437..c210b9f20705b94acd1d8d130ea166c48aa74787 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2016 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <limits.h> @@ -275,6 +275,7 @@ void task_dependency_sum(void *in_p, void *out_p, int *len, for (int i = 0; i < *len; i++) { /* loop over all the object set in invals */ for (int j = 0; j < MAX_NUMBER_DEP; j++) { + /* Have we reached the end of the links? */ if (in[i].number_link[j] == -1) { break; @@ -388,6 +389,14 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { * task_subtype */ struct task_dependency *task_dep = (struct task_dependency *)malloc( nber_tasks * sizeof(struct task_dependency)); + /* keep track of whether a task exists in this run */ + int *task_exists = (int *)malloc(nber_tasks * sizeof(int)); + /* keep track of whether a task has a dependency or an unlock, + * and hence will be drawn in the task graph */ + int *task_has_deps = (int *)malloc(nber_tasks * sizeof(int)); + + /* Special marker for tasks with no dependencies. */ + const int no_dependency = -3; if (task_dep == NULL) error("Error allocating memory for task-dependency graph (table)."); @@ -398,6 +407,10 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { task_dep[i].task_in_is_top = 1; task_dep[i].task_in_is_grav_super = 1; task_dep[i].task_in_is_hydro_super = 1; + const int tt = i / task_subtype_count; + const int tst = i % task_subtype_count; + task_dep[i].type_in = tt; + task_dep[i].subtype_in = tst; for (int j = 0; j < MAX_NUMBER_DEP; j++) { /* Use number_link as indicator of the existance of a relation */ @@ -408,6 +421,8 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { task_dep[i].task_out_is_grav_super[j] = 1; task_dep[i].task_out_is_hydro_super[j] = 1; } + task_exists[i] = 0; + task_has_deps[i] = 0; } /* loop over all tasks */ @@ -422,10 +437,17 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { const int ind = ta->type * task_subtype_count + ta->subtype; struct task_dependency *cur = &task_dep[ind]; + task_exists[ind]++; - /* Set ta */ - cur->type_in = ta->type; - cur->subtype_in = ta->subtype; +#ifdef SWIFT_DEBUG_CHECKS + if (cur->type_in != ta->type) + error("wrong indexing for task %d: Expect type %d got %d", i, + cur->type_in, ta->type); + if (cur->subtype_in != ta->subtype) + error("wrong indexing for task %d: Expect subtype %d got %d", i, + cur->subtype_in, ta->subtype); +#endif + /* Is ta implicit? */ cur->implicit_in = ta->implicit; /* Set the task level. */ @@ -453,7 +475,42 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { cur->task_in_is_grav_super = 0; } - /* and their dependencies */ + /* If this task unlocks nothing, make a note of it. */ + if (ta->nr_unlock_tasks == 0) { + int k = 0; + while (k < MAX_NUMBER_DEP) { + /* not written yet */ + if (cur->number_link[k] == -1) { + cur->type_out[k] = no_dependency; + cur->subtype_out[k] = no_dependency; + cur->implicit_out[k] = no_dependency; + + /* statistics */ + cur->number_link[k] = 0; + cur->number_rank[k] = 1; + + /* Are we dealing with a task at the top level? */ + cur->task_out_is_top[k] = no_dependency; + cur->task_out_is_hydro_super[k] = no_dependency; + cur->task_out_is_grav_super[k] = no_dependency; + + break; + } + + /* already written */ + if (cur->type_out[k] == no_dependency && + cur->subtype_out[k] == no_dependency) { + break; + } + k += 1; + } + + /* if this task unlocks nothing, we have nothing left to do. + * go to next. */ + continue; + } + + /* This task unlocks stuff, so check the dependencies */ for (int j = 0; j < ta->nr_unlock_tasks; j++) { const struct task *tb = ta->unlock_tasks[j]; @@ -461,6 +518,19 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { * For the 0-step, we wish to show all the tasks (even the inactive). */ if (step != 0 && tb->skip) continue; + int indj = tb->type * task_subtype_count + tb->subtype; + +#ifdef SWIFT_DEBUG_CHECKS + const struct task_dependency *target = &task_dep[indj]; + if (target->type_in != tb->type) + error("wrong indexing for task %d: Expect type %d got %d", i, + target->type_in, tb->type); + if (target->subtype_in != tb->subtype) + error("wrong indexing for task %d: Expect subtype %d got %d", i, + target->subtype_in, tb->subtype); +#endif + task_exists[indj]++; + const struct cell *ci_b = tb->ci; const struct cell *cj_b = tb->cj; const int is_ci_b_top = @@ -536,18 +606,20 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { #ifdef WITH_MPI /* create MPI operator */ - MPI_Datatype data_type; - task_dependency_define(&data_type); + MPI_Datatype dependency_data_type; + task_dependency_define(&dependency_data_type); - MPI_Op sum; - MPI_Op_create(task_dependency_sum, /* commute */ 1, &sum); + MPI_Op dependency_sum; + MPI_Op_create(task_dependency_sum, /* commute */ 1, &dependency_sum); /* create recv buffer */ struct task_dependency *recv = NULL; + int *recv_exists = NULL; if (s->nodeID == 0) { recv = (struct task_dependency *)malloc(nber_tasks * sizeof(struct task_dependency)); + recv_exists = (int *)malloc(nber_tasks * sizeof(int)); /* reset counter */ for (int i = 0; i < nber_tasks; i++) { @@ -555,18 +627,25 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { /* Use number_link as indicator of the existance of a relation */ recv[i].number_link[j] = -1; } + recv_exists[i] = 0; } } /* Do the reduction */ - int test = - MPI_Reduce(task_dep, recv, nber_tasks, data_type, sum, 0, MPI_COMM_WORLD); + int test = MPI_Reduce(task_dep, recv, nber_tasks, dependency_data_type, + dependency_sum, 0, MPI_COMM_WORLD); + if (test != MPI_SUCCESS) error("MPI reduce failed"); + + test = MPI_Reduce(task_exists, recv_exists, nber_tasks, MPI_INT, MPI_SUM, 0, + MPI_COMM_WORLD); if (test != MPI_SUCCESS) error("MPI reduce failed"); /* free some memory */ if (s->nodeID == 0) { free(task_dep); task_dep = recv; + free(task_exists); + task_exists = recv_exists; } #endif @@ -584,14 +663,14 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { "task_in,task_out,implicit_in,implicit_out,mpi_in,mpi_out,cluster_in," "cluster_out,number_link,number_rank,task_in_is_top,task_in_is_hydro_" "super,task_in_is_grav_super,task_out_is_top,task_out_is_hydro_super," - "task_out_is_grav_super\n"); + "task_out_is_grav_super,cell_has_active_task\n"); for (int i = 0; i < nber_tasks; i++) { for (int j = 0; j < MAX_NUMBER_DEP; j++) { /* Does this link exists */ - if (task_dep[i].number_link[j] == -1) { - continue; - } + if (task_dep[i].number_link[j] == -1) continue; + /* Don't write tasks without dependencies (yet) */ + if (task_dep[i].type_out[j] == no_dependency) continue; /* Define a few variables */ const int ta_type = task_dep[i].type_in; @@ -619,6 +698,11 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { char ta_name[200]; char tb_name[200]; + /* take note that these tasks have dependencies and unlocks */ + task_has_deps[i]++; + int indj = tb_type * task_subtype_count + tb_subtype; + task_has_deps[indj]++; + /* construct line */ task_get_full_name(ta_type, ta_subtype, ta_name); task_get_full_name(tb_type, tb_subtype, tb_name); @@ -636,13 +720,73 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { task_get_group_name(ta_type, ta_subtype, ta_cluster); task_get_group_name(tb_type, tb_subtype, tb_cluster); - fprintf(f, "%s,%s,%d,%d,%d,%d,%s,%s,%d,%d,%d,%d,%d,%d,%d,%d\n", ta_name, - tb_name, ta_implicit, tb_implicit, ta_mpi, tb_mpi, ta_cluster, - tb_cluster, count, number_rank, task_in_is_top, + fprintf(f, "%s,%s,%d,%d,%d,%d,%s,%s,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + ta_name, tb_name, ta_implicit, tb_implicit, ta_mpi, tb_mpi, + ta_cluster, tb_cluster, count, number_rank, task_in_is_top, + task_in_is_hydro_super, task_in_is_grav_super, task_out_is_top, + task_out_is_hydro_super, task_out_is_grav_super, + /*cell_has_active_task=*/1); + } + } + + /* Now write the tasks without dependencies */ + for (int i = 0; i < nber_tasks; i++) { + + /* There may be several tasks that don't unlock anything, + * e.g. timestep_collect or kick1 tasks. Those are covered + * in the graph by them being unlocked by some other task. + * If a task however doesn't unlock anything, nor is unlocked + * by any other task, it needs special treatment, which it + * receives now. The condition to be written down is a) the + * task must exist, i.e. we must have encountered it in the + * list of tasks, and b) it must not have been unlocked by + * anyting. */ + + if (task_exists[i] && !task_has_deps[i]) { + + /* Define a few variables */ + const int ta_type = task_dep[i].type_in; + const int ta_subtype = task_dep[i].subtype_in; + const int ta_implicit = task_dep[i].implicit_in; + + const int tb_implicit = 0; + + const int count = 0; + const int number_rank = 1; + + const int task_in_is_top = task_dep[i].task_in_is_top; + const int task_in_is_grav_super = task_dep[i].task_in_is_grav_super; + const int task_in_is_hydro_super = task_dep[i].task_in_is_hydro_super; + + const int task_out_is_top = -1; + const int task_out_is_grav_super = -1; + const int task_out_is_hydro_super = -1; + + /* text to write */ + char ta_name[200]; + task_get_full_name(ta_type, ta_subtype, ta_name); + char *tb_name = "task_unlocks_nothing\0"; + + /* Check if MPI */ + int ta_mpi = 0; + if (ta_type == task_type_send || ta_type == task_type_recv) ta_mpi = 1; + + int tb_mpi = 0; + + /* Get group name */ + char ta_cluster[20]; + task_get_group_name(ta_type, ta_subtype, ta_cluster); + char *tb_cluster = "None\0"; + + fprintf(f, "%s,%s,%d,%d,%d,%d,%s,%s,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + ta_name, tb_name, ta_implicit, tb_implicit, ta_mpi, tb_mpi, + ta_cluster, tb_cluster, count, number_rank, task_in_is_top, task_in_is_hydro_super, task_in_is_grav_super, task_out_is_top, - task_out_is_hydro_super, task_out_is_grav_super); + task_out_is_hydro_super, task_out_is_grav_super, + /*cell_has_active_task=*/1); } } + /* Close the file */ fclose(f); } @@ -674,14 +818,331 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) { /* Be clean */ free(task_dep); + free(task_exists); + free(task_has_deps); #ifdef WITH_MPI - MPI_Type_free(&data_type); - MPI_Op_free(&sum); + MPI_Type_free(&dependency_data_type); + MPI_Op_free(&dependency_sum); +#endif + + if (verbose) + message("Printing task graph took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); +} + +/** + * @brief Write a csv file with the task dependencies for a single cell. + * + * Run plot_task_dependencies.py for an example of how to use it + * to generate the figure. + * + * @param s The #scheduler we are working in. + * @param verbose Are we verbose about this? + * @param step The current step number. + */ +void scheduler_write_cell_dependencies(struct scheduler *s, int verbose, + int step) { + +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) + + const ticks tic = getticks(); + + const long long cellID = s->dependency_graph_cellID; + if (cellID == 0LL) return; + + /* Number of possible relations between tasks */ + const int nber_tasks = task_type_count * task_subtype_count; + + /* To get the table for a task: + * ind = (ta * task_subtype_count + sa) + * where ta is the value of task_type and sa is the value of + * task_subtype */ + struct task_dependency *task_dep = (struct task_dependency *)malloc( + nber_tasks * sizeof(struct task_dependency)); + + /* Keep track whether the requested cell is also involved in the + * dependency */ + int cell_involved[nber_tasks][MAX_NUMBER_DEP]; + + if (task_dep == NULL) + error("Error allocating memory for task-dependency graph (table)."); + + /* Reset counter */ + for (int i = 0; i < nber_tasks; i++) { + /* Assume that the tasks are at all levels and correct later. */ + task_dep[i].task_in_is_top = 1; + task_dep[i].task_in_is_grav_super = 1; + task_dep[i].task_in_is_hydro_super = 1; + const int tt = i / task_subtype_count; + const int tst = i % task_subtype_count; + task_dep[i].type_in = tt; + task_dep[i].subtype_in = tst; + + for (int j = 0; j < MAX_NUMBER_DEP; j++) { + /* Use number_link as indicator of the existance of a relation */ + task_dep[i].number_link[j] = -1; + + /* Assume that the tasks are at all levels and correct later. */ + task_dep[i].task_out_is_top[j] = 1; + task_dep[i].task_out_is_grav_super[j] = 1; + task_dep[i].task_out_is_hydro_super[j] = 1; + cell_involved[i][j] = 0; + } + } + + /* loop over all tasks */ + int local_count = 0; + for (int i = 0; i < s->nr_tasks; i++) { + const struct task *ta = &s->tasks[i]; + + /* Are we using this task? + * For the 0-step, we wish to show all the tasks (even the inactives). */ + if (step != 0 && ta->skip) continue; + /* Note: task_type_none may have t->ci==NULL too */ + if (!(((ta->ci != NULL) && ta->ci->cellID == cellID) || + ((ta->cj != NULL) && ta->cj->cellID == cellID))) + continue; + + /* Current index */ + const int ind = ta->type * task_subtype_count + ta->subtype; + struct task_dependency *cur = &task_dep[ind]; + +#ifdef SWIFT_DEBUG_CHECKS + if (cur->type_in != ta->type) + error("wrong indexing for task %d: Expect type %d got %d", i, + cur->type_in, ta->type); + if (cur->subtype_in != ta->subtype) + error("wrong indexing for task %d: Expect subtype %d got %d", i, + cur->subtype_in, ta->subtype); #endif + /* Is ta implicit? */ + cur->implicit_in = ta->implicit; + + /* Set the task level. */ + const struct cell *ci = ta->ci; + const struct cell *cj = ta->cj; + const int is_ci_top = ci == NULL || (ci != NULL && ci == ci->top); + const int is_cj_top = cj == NULL || (cj != NULL && cj == cj->top); + const int is_hydro_super = + (cj == NULL || (cj != NULL && cj == cj->hydro.super)) && + (ci == NULL || (ci != NULL && ci == ci->hydro.super)); + const int is_grav_super = + (cj == NULL || (cj != NULL && cj == cj->grav.super)) && + (ci == NULL || (ci != NULL && ci == ci->grav.super)); + + /* Are we dealing with a task at the top level? */ + if (!(is_ci_top && is_cj_top)) { + cur->task_in_is_top = 0; + } + /* At the hydro level? */ + if (!is_hydro_super) { + cur->task_in_is_hydro_super = 0; + } + /* At the gravity level? */ + if (!is_grav_super) { + cur->task_in_is_grav_super = 0; + } + + /* and their dependencies */ + for (int j = 0; j < ta->nr_unlock_tasks; j++) { + const struct task *tb = ta->unlock_tasks[j]; + + /* Are we using this task? + * For the 0-step, we wish to show all the tasks (even the inactive). */ + if (step != 0 && tb->skip) continue; + + /* Found a task with a dependency. */ + local_count++; + + const struct cell *ci_b = tb->ci; + const struct cell *cj_b = tb->cj; + const int is_ci_b_top = + ci_b == NULL || (ci_b != NULL && ci_b == ci_b->top); + const int is_cj_b_top = + cj_b == NULL || (cj_b != NULL && cj_b == cj_b->top); + const int is_b_hydro_super = + (cj_b == NULL || (cj_b != NULL && cj_b == cj_b->hydro.super)) && + (ci_b == NULL || (ci_b != NULL && ci_b == ci_b->hydro.super)); + const int is_b_grav_super = + (cj_b == NULL || (cj_b != NULL && cj_b == cj_b->grav.super)) && + (ci_b == NULL || (ci_b != NULL && ci_b == ci_b->grav.super)); + + int k = 0; + while (k < MAX_NUMBER_DEP) { + /* not written yet */ + if (cur->number_link[k] == -1) { + /* set tb */ + cur->type_out[k] = tb->type; + cur->subtype_out[k] = tb->subtype; + cur->implicit_out[k] = tb->implicit; + + /* statistics */ + cur->number_link[k] = 1; + cur->number_rank[k] = 1; + + /* Are we dealing with a task at the top level? */ + if (!(is_ci_b_top && is_cj_b_top)) { + cur->task_out_is_top[k] = 0; + } + /* At the hydro level? */ + if (!is_b_hydro_super) { + cur->task_out_is_hydro_super[k] = 0; + } + /* At the gravity level? */ + if (!is_b_grav_super) { + cur->task_out_is_grav_super[k] = 0; + } + + if (ci_b->cellID == cellID) cell_involved[ind][k]++; + if (cj_b != NULL && cj_b->cellID == cellID) cell_involved[ind][k]++; + + break; + } + + /* already written */ + if (cur->type_out[k] == tb->type && + cur->subtype_out[k] == tb->subtype) { + + /* Increase the number of link. */ + cur->number_link[k] += 1; + + /* Are we dealing with a task at the top level? */ + if (!(is_ci_b_top && is_cj_b_top)) { + cur->task_out_is_top[k] = 0; + } + /* At the hydro level? */ + if (!is_b_hydro_super) { + cur->task_out_is_hydro_super[k] = 0; + } + /* At the gravity level? */ + if (!is_b_grav_super) { + cur->task_out_is_grav_super[k] = 0; + } + + if (ci_b->cellID == cellID) cell_involved[ind][k]++; + if (cj_b != NULL && cj_b->cellID == cellID) cell_involved[ind][k]++; + + break; + } + + k += 1; + } + + /* MAX_NUMBER_DEP is too small */ + if (k == MAX_NUMBER_DEP) + error("Not enough memory, please increase MAX_NUMBER_DEP"); + } + + /* Some tasks might not unlock anything, like the kick1 tasks. This is + * expected, and they should turn up in the task graph because they are + * being unlocked by some other task. However, if a dev missed a + * dependency and has tasks with no unlocks nor dependencies, they + * wouldn't show up in the graph. So we write these tasks with no + * unlocks down too, but as a special case. */ + if (ta->nr_unlock_tasks == 0) { + cur->number_link[0] = 0; + cur->type_out[0] = -1; + cur->subtype_out[0] = -1; + cur->implicit_out[0] = -1; + cur->number_rank[0] = 1; + cell_involved[ind][0] = 1; + } + } + + if (local_count > 0) { + /* We have tasks involving the requested cell on this node */ + + /* Create file */ + char filename[50]; + sprintf(filename, "dependency_graph_cell_%lld_step_%i_rank_%i.csv", cellID, + step, engine_rank); + FILE *f = fopen(filename, "w"); + if (f == NULL) error("Error opening dependency graph file."); + + /* Write header */ + fprintf(f, "# %s\n", git_revision()); + fprintf( + f, + "task_in,task_out,implicit_in,implicit_out,mpi_in,mpi_out,cluster_in," + "cluster_out,number_link,number_rank,task_in_is_top,task_in_is_hydro_" + "super,task_in_is_grav_super,task_out_is_top,task_out_is_hydro_super," + "task_out_is_grav_super,cell_has_active_task\n"); + + for (int i = 0; i < nber_tasks; i++) { + for (int j = 0; j < MAX_NUMBER_DEP; j++) { + /* Does this link exists */ + if (task_dep[i].number_link[j] == -1) { + continue; + } + + /* Define a few variables */ + const int ta_type = task_dep[i].type_in; + const int ta_subtype = task_dep[i].subtype_in; + const int ta_implicit = task_dep[i].implicit_in; + + const int tb_type = task_dep[i].type_out[j]; + const int tb_subtype = task_dep[i].subtype_out[j]; + const int tb_implicit = task_dep[i].implicit_out[j]; + + const int count = task_dep[i].number_link[j]; + const int number_rank = task_dep[i].number_rank[j]; + + const int task_in_is_top = task_dep[i].task_in_is_top; + const int task_in_is_grav_super = task_dep[i].task_in_is_grav_super; + const int task_in_is_hydro_super = task_dep[i].task_in_is_hydro_super; + + const int task_out_is_top = task_dep[i].task_out_is_top[j]; + const int task_out_is_grav_super = + task_dep[i].task_out_is_grav_super[j]; + const int task_out_is_hydro_super = + task_dep[i].task_out_is_hydro_super[j]; + + /* text to write */ + char ta_name[200]; + char tb_name[200]; + + /* construct line */ + task_get_full_name(ta_type, ta_subtype, ta_name); + if (tb_type == -1) { + /* special handling of tasks which have no unlocks */ + strcpy(tb_name, "task_unlocks_nothing"); + } else { + task_get_full_name(tb_type, tb_subtype, tb_name); + } + + /* Check if MPI */ + int ta_mpi = 0; + if (ta_type == task_type_send || ta_type == task_type_recv) ta_mpi = 1; + + int tb_mpi = 0; + if (tb_type == task_type_send || tb_type == task_type_recv) tb_mpi = 1; + + /* Get group name */ + char ta_cluster[20]; + char tb_cluster[20]; + task_get_group_name(ta_type, ta_subtype, ta_cluster); + task_get_group_name(tb_type, tb_subtype, tb_cluster); + + fprintf(f, "%s,%s,%d,%d,%d,%d,%s,%s,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + ta_name, tb_name, ta_implicit, tb_implicit, ta_mpi, tb_mpi, + ta_cluster, tb_cluster, count, number_rank, task_in_is_top, + task_in_is_hydro_super, task_in_is_grav_super, task_out_is_top, + task_out_is_hydro_super, task_out_is_grav_super, + cell_involved[i][j]); + } + } + /* Close the file */ + fclose(f); + } + + /* Clean up after yourself */ + free(task_dep); if (verbose) message("Printing task graph took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); + +#endif /* defined SWIFT_DEBUG_CHECKS || defined CELL_GRAPH */ } /** @@ -1500,8 +1961,7 @@ void scheduler_reweight(struct scheduler *s, int verbose) { t->weight = 0.f; for (int j = 0; j < t->nr_unlock_tasks; j++) - if (t->unlock_tasks[j]->weight > t->weight) - t->weight = t->unlock_tasks[j]->weight; + t->weight += t->unlock_tasks[j]->weight; const float count_i = (t->ci != NULL) ? t->ci->hydro.count : 0.f; const float count_j = (t->cj != NULL) ? t->cj->hydro.count : 0.f; @@ -1516,18 +1976,19 @@ void scheduler_reweight(struct scheduler *s, int verbose) { switch (t->type) { case task_type_sort: + case task_type_rt_sort: cost = wscale * intrinsics_popcount(t->flags) * count_i * - (sizeof(int) * 8 - intrinsics_clz(t->ci->hydro.count)); + (sizeof(int) * 8 - (count_i ? intrinsics_clz(count_i) : 0)); break; case task_type_stars_sort: cost = wscale * intrinsics_popcount(t->flags) * scount_i * - (sizeof(int) * 8 - intrinsics_clz(t->ci->stars.count)); + (sizeof(int) * 8 - (scount_i ? intrinsics_clz(scount_i) : 0)); break; case task_type_stars_resort: cost = wscale * intrinsics_popcount(t->flags) * scount_i * - (sizeof(int) * 8 - intrinsics_clz(t->ci->stars.count)); + (sizeof(int) * 8 - (scount_i ? intrinsics_clz(scount_i) : 0)); break; case task_type_self: @@ -1540,10 +2001,10 @@ void scheduler_reweight(struct scheduler *s, int verbose) { t->subtype == task_subtype_stars_prep2 || t->subtype == task_subtype_stars_feedback) cost = 1.f * wscale * scount_i * count_i; - else if (t->subtype == task_subtype_sink_compute_formation || - t->subtype == task_subtype_sink_accretion) + else if (t->subtype == task_subtype_sink_swallow || + t->subtype == task_subtype_sink_do_gas_swallow) cost = 1.f * wscale * count_i * sink_count_i; - else if (t->subtype == task_subtype_sink_merger) + else if (t->subtype == task_subtype_sink_do_sink_swallow) cost = 1.f * wscale * sink_count_i * sink_count_i; else if (t->subtype == task_subtype_bh_density || t->subtype == task_subtype_bh_swallow || @@ -1558,8 +2019,6 @@ void scheduler_reweight(struct scheduler *s, int verbose) { t->subtype == task_subtype_force || t->subtype == task_subtype_limiter) cost = 1.f * (wscale * count_i) * count_i; - else if (t->subtype == task_subtype_rt_inject) - cost = 1.f * wscale * scount_i * count_i; else if (t->subtype == task_subtype_rt_gradient) cost = 1.f * wscale * count_i * count_i; else if (t->subtype == task_subtype_rt_transport) @@ -1588,8 +2047,8 @@ void scheduler_reweight(struct scheduler *s, int verbose) { cost = 2.f * wscale * (scount_i * count_j + scount_j * count_i) * sid_scale[t->flags]; - } else if (t->subtype == task_subtype_sink_compute_formation || - t->subtype == task_subtype_sink_accretion) { + } else if (t->subtype == task_subtype_sink_swallow || + t->subtype == task_subtype_sink_do_gas_swallow) { if (t->ci->nodeID != nodeID) cost = 3.f * wscale * count_i * sink_count_j * sid_scale[t->flags]; else if (t->cj->nodeID != nodeID) @@ -1599,7 +2058,7 @@ void scheduler_reweight(struct scheduler *s, int verbose) { (sink_count_i * count_j + sink_count_j * count_i) * sid_scale[t->flags]; - } else if (t->subtype == task_subtype_sink_merger) { + } else if (t->subtype == task_subtype_sink_do_sink_swallow) { if (t->ci->nodeID != nodeID) cost = 3.f * wscale * sink_count_i * sink_count_j * sid_scale[t->flags]; @@ -1637,8 +2096,6 @@ void scheduler_reweight(struct scheduler *s, int verbose) { else cost = 2.f * (wscale * count_i) * count_j * sid_scale[t->flags]; - } else if (t->subtype == task_subtype_rt_inject) { - cost = 1.f * wscale * scount_i * count_j; } else if (t->subtype == task_subtype_rt_gradient) { cost = 1.f * wscale * count_i * count_j; } else if (t->subtype == task_subtype_rt_transport) { @@ -1666,8 +2123,8 @@ void scheduler_reweight(struct scheduler *s, int verbose) { sid_scale[t->flags]; } - } else if (t->subtype == task_subtype_sink_compute_formation || - t->subtype == task_subtype_sink_accretion) { + } else if (t->subtype == task_subtype_sink_swallow || + t->subtype == task_subtype_sink_do_gas_swallow) { if (t->ci->nodeID != nodeID) { cost = 3.f * (wscale * count_i) * sink_count_j * sid_scale[t->flags]; @@ -1680,7 +2137,7 @@ void scheduler_reweight(struct scheduler *s, int verbose) { sid_scale[t->flags]; } - } else if (t->subtype == task_subtype_sink_merger) { + } else if (t->subtype == task_subtype_sink_do_sink_swallow) { if (t->ci->nodeID != nodeID) { cost = 3.f * (wscale * sink_count_i) * sink_count_j * sid_scale[t->flags]; @@ -1719,8 +2176,6 @@ void scheduler_reweight(struct scheduler *s, int verbose) { } else { cost = 2.f * (wscale * count_i) * count_j * sid_scale[t->flags]; } - } else if (t->subtype == task_subtype_rt_inject) { - cost = 1.f * wscale * scount_i * count_j; } else if (t->subtype == task_subtype_rt_gradient) { cost = 1.f * wscale * count_i * count_j; } else if (t->subtype == task_subtype_rt_transport) { @@ -1737,10 +2192,10 @@ void scheduler_reweight(struct scheduler *s, int verbose) { t->subtype == task_subtype_stars_prep2 || t->subtype == task_subtype_stars_feedback) { cost = 1.f * (wscale * scount_i) * count_i; - } else if (t->subtype == task_subtype_sink_compute_formation || - t->subtype == task_subtype_sink_accretion) { + } else if (t->subtype == task_subtype_sink_swallow || + t->subtype == task_subtype_sink_do_gas_swallow) { cost = 1.f * (wscale * sink_count_i) * count_i; - } else if (t->subtype == task_subtype_sink_merger) { + } else if (t->subtype == task_subtype_sink_do_sink_swallow) { cost = 1.f * (wscale * sink_count_i) * sink_count_i; } else if (t->subtype == task_subtype_bh_density || t->subtype == task_subtype_bh_swallow || @@ -1755,8 +2210,6 @@ void scheduler_reweight(struct scheduler *s, int verbose) { t->subtype == task_subtype_force || t->subtype == task_subtype_limiter) { cost = 1.f * (wscale * count_i) * count_i; - } else if (t->subtype == task_subtype_rt_inject) { - cost = 1.f * wscale * scount_i * count_i; } else if (t->subtype == task_subtype_rt_gradient) { cost = 1.f * wscale * scount_i * count_i; } else if (t->subtype == task_subtype_rt_transport) { @@ -1835,6 +2288,10 @@ void scheduler_reweight(struct scheduler *s, int verbose) { case task_type_rt_tchem: cost = wscale * count_i; break; + case task_type_rt_advance_cell_time: + case task_type_rt_collect_times: + cost = wscale; + break; case task_type_csds: cost = wscale * (count_i + gcount_i + scount_i + sink_count_i + bcount_i); @@ -1976,8 +2433,6 @@ void scheduler_start(struct scheduler *s) { * @param t The #task. */ void scheduler_enqueue(struct scheduler *s, struct task *t) { - /* The target queue for this task. */ - int qid = -1; /* Ignore skipped tasks */ if (t->skip) return; @@ -1986,6 +2441,16 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { if (t->implicit) { #ifdef SWIFT_DEBUG_CHECKS t->ti_run = s->space->e->ti_current; + + /* Mark that we have run this task on these cells */ + if (t->ci != NULL) { + t->ci->tasks_executed[t->type]++; + t->ci->subtasks_executed[t->subtype]++; + } + if (t->cj != NULL) { + t->cj->tasks_executed[t->type]++; + t->cj->subtasks_executed[t->subtype]++; + } #endif t->skip = 1; for (int j = 0; j < t->nr_unlock_tasks; j++) { @@ -2001,23 +2466,30 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { #endif /* Find the previous owner for each task type, and do - any pre-processing needed. */ + * any pre-processing needed. */ + short int qid = -1; + short int *owner = NULL; switch (t->type) { case task_type_self: case task_type_sub_self: if (t->subtype == task_subtype_grav || - t->subtype == task_subtype_external_grav) + t->subtype == task_subtype_external_grav) { qid = t->ci->grav.super->owner; - else + owner = &t->ci->grav.super->owner; + } else { qid = t->ci->hydro.super->owner; + owner = &t->ci->hydro.super->owner; + } break; case task_type_sort: case task_type_ghost: case task_type_drift_part: qid = t->ci->hydro.super->owner; + owner = &t->ci->hydro.super->owner; break; case task_type_drift_gpart: qid = t->ci->grav.super->owner; + owner = &t->ci->grav.super->owner; break; case task_type_kick1: case task_type_kick2: @@ -2026,13 +2498,18 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { case task_type_stars_sort: case task_type_timestep: qid = t->ci->super->owner; + owner = &t->ci->super->owner; break; case task_type_pair: case task_type_sub_pair: qid = t->ci->super->owner; - if (qid < 0 || - s->queues[qid].count > s->queues[t->cj->super->owner].count) + owner = &t->ci->super->owner; + if ((qid < 0) || + ((t->cj->super->owner > -1) && + (s->queues[qid].count > s->queues[t->cj->super->owner].count))) { qid = t->cj->super->owner; + owner = &t->cj->super->owner; + } break; case task_type_recv: #ifdef WITH_MPI @@ -2061,6 +2538,8 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { } else if (t->subtype == task_subtype_xv || t->subtype == task_subtype_rho || t->subtype == task_subtype_gradient || + t->subtype == task_subtype_rt_gradient || + t->subtype == task_subtype_rt_transport || t->subtype == task_subtype_part_prep1) { count = t->ci->hydro.count; @@ -2166,6 +2645,8 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { } else if (t->subtype == task_subtype_xv || t->subtype == task_subtype_rho || t->subtype == task_subtype_gradient || + t->subtype == task_subtype_rt_gradient || + t->subtype == task_subtype_rt_transport || t->subtype == task_subtype_part_prep1) { count = t->ci->hydro.count; @@ -2249,9 +2730,11 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { if (qid >= s->nr_queues) error("Bad computed qid."); - /* If no previous owner, pick a random queue. */ - /* Note that getticks() is random enough */ - if (qid < 0) qid = getticks() % s->nr_queues; + /* If no qid, pick a random queue. */ + if (qid < 0) qid = rand() % s->nr_queues; + + /* Save qid as owner for next time a task accesses this cell. */ + if (owner != NULL) *owner = qid; /* Increase the waiting counter. */ atomic_inc(&s->waiting); @@ -2476,7 +2959,6 @@ void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks, s->size = 0; s->tasks = NULL; s->tasks_ind = NULL; - pthread_key_create(&s->local_seed_pointer, NULL); scheduler_reset(s, nr_tasks); } diff --git a/src/scheduler.h b/src/scheduler.h index c7c9b1412458f49950087f695705e22bfd1b9582..349cdc38a3c770d0418e43f00167a4e76edb8723 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -21,7 +21,7 @@ #define SWIFT_SCHEDULER_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI @@ -106,9 +106,6 @@ struct scheduler { * MPI. */ size_t mpi_message_limit; - /* 'Pointer' to the seed for the random number generator */ - pthread_key_t local_seed_pointer; - /* Total ticks spent running the tasks */ ticks total_ticks; @@ -123,6 +120,9 @@ struct scheduler { /* Frequency of the dependency graph dumping. */ int frequency_dependency; + /* Specific cell to dump dependency graph for */ + long long dependency_graph_cellID; + /* Frequency of the task levels dumping. */ int frequency_task_levels; }; @@ -285,6 +285,8 @@ void scheduler_print_tasks(const struct scheduler *s, const char *fileName); void scheduler_clean(struct scheduler *s); void scheduler_free_tasks(struct scheduler *s); void scheduler_write_dependencies(struct scheduler *s, int verbose, int step); +void scheduler_write_cell_dependencies(struct scheduler *s, int verbose, + int step); void scheduler_write_task_level(const struct scheduler *s, int step); void scheduler_dump_queues(struct engine *e); void scheduler_report_task_times(const struct scheduler *s, diff --git a/src/serial_io.c b/src/serial_io.c index df201dc5a8f353b408f0a931e03740d0aaeca8fc..5344cca05f92d77447cfb384a4523360be794871 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5) @@ -49,6 +49,7 @@ #include "hydro_properties.h" #include "io_properties.h" #include "memuse.h" +#include "mhd_io.h" #include "output_list.h" #include "output_options.h" #include "part.h" @@ -247,9 +248,9 @@ void read_array_serial(hid_t grp, const struct io_props props, size_t N, } void prepare_array_serial( - const struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, - char* partTypeGroupName, const struct io_props props, - unsigned long long N_total, + const struct engine* e, hid_t grp, const char* fileName, FILE* xmfFile, + const char* partTypeGroupName, const struct io_props props, + const unsigned long long N_total, const enum lossy_compression_schemes lossy_compression, const struct unit_system* internal_units, const struct unit_system* snapshot_units) { @@ -293,11 +294,13 @@ void prepare_array_serial( /* Dataset properties */ hid_t h_prop = H5Pcreate(H5P_DATASET_CREATE); - /* Set chunk size */ - h_err = H5Pset_chunk(h_prop, rank, chunk_shape); - if (h_err < 0) - error("Error while setting chunk size (%llu, %llu) for field '%s'.", - chunk_shape[0], chunk_shape[1], props.name); + /* Set chunk size if have some particles */ + if (N_total > 0) { + h_err = H5Pset_chunk(h_prop, rank, chunk_shape); + if (h_err < 0) + error("Error while setting chunk size (%llu, %llu) for field '%s'.", + chunk_shape[0], chunk_shape[1], props.name); + } /* Are we imposing some form of lossy compression filter? */ char comp_buffer[32] = "None"; @@ -305,8 +308,8 @@ void prepare_array_serial( set_hdf5_lossy_compression(&h_prop, &h_type, lossy_compression, props.name, comp_buffer); - /* Impose data compression */ - if (e->snapshot_compression > 0) { + /* Impose GZIP and shuffle data compression */ + if (e->snapshot_compression > 0 && N_total > 0) { h_err = H5Pset_shuffle(h_prop); if (h_err < 0) error("Error while setting shuffling options for field '%s'.", @@ -319,9 +322,11 @@ void prepare_array_serial( } /* Impose check-sum to verify data corruption */ - h_err = H5Pset_fletcher32(h_prop); - if (h_err < 0) - error("Error while setting checksum options for field '%s'.", props.name); + if (N_total > 0) { + h_err = H5Pset_fletcher32(h_prop); + if (h_err < 0) + error("Error while setting checksum options for field '%s'.", props.name); + } /* Create dataset */ const hid_t h_data = H5Dcreate(grp, props.name, h_type, h_space, H5P_DEFAULT, @@ -398,9 +403,10 @@ void prepare_array_serial( * the part array will be written once the structures have been stabilized. */ void write_array_serial(const struct engine* e, hid_t grp, char* fileName, - FILE* xmfFile, char* partTypeGroupName, - const struct io_props props, size_t N, - long long N_total, int mpi_rank, long long offset, + FILE* xmfFile, const char* partTypeGroupName, + const struct io_props props, const size_t N, + const long long N_total, const int mpi_rank, + const long long offset, const enum lossy_compression_schemes lossy_compression, const struct unit_system* internal_units, const struct unit_system* snapshot_units) { @@ -531,12 +537,14 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, struct bpart** bparts, size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, size_t* Nnuparts, size_t* Nsinks, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_sink, 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, - int remap_ids, struct ic_info* ics_metadata) { + int* flag_entropy, const int with_hydro, + const int with_gravity, const int with_sink, + const int with_stars, const int with_black_holes, + const int with_cosmology, const int cleanup_h, + const int cleanup_sqrt_a, double h, double a, + const int mpi_rank, int mpi_size, MPI_Comm comm, + MPI_Info info, const int n_threads, const int dry_run, + const int remap_ids, struct ic_info* ics_metadata) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -797,6 +805,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, if (with_hydro) { Nparticles = *Ngas; hydro_read_particles(*parts, list, &num_fields); + num_fields += mhd_read_particles(*parts, list + num_fields); num_fields += chemistry_read_particles(*parts, list + num_fields); num_fields += rt_read_particles(*parts, list + num_fields); } @@ -951,8 +960,9 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, */ void write_output_serial(struct engine* e, const struct unit_system* internal_units, - const struct unit_system* snapshot_units, int mpi_rank, - int mpi_size, MPI_Comm comm, MPI_Info info) { + const struct unit_system* snapshot_units, + const int mpi_rank, const int mpi_size, MPI_Comm comm, + MPI_Info info) { hid_t h_file = 0, h_grp = 0; int numFiles = 1; @@ -968,8 +978,13 @@ void write_output_serial(struct engine* e, 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 = e->s->with_DM; const int with_DM_background = e->s->with_DM_background; const int with_neutrinos = e->s->with_neutrinos; + const int with_hydro = (e->policy & engine_policy_hydro) ? 1 : 0; + const int with_stars = (e->policy & engine_policy_stars) ? 1 : 0; + const int with_black_hole = (e->policy & engine_policy_black_holes) ? 1 : 0; + const int with_sink = (e->policy & engine_policy_sinks) ? 1 : 0; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -1118,6 +1133,15 @@ void write_output_serial(struct engine* e, /* The last rank now has the correct N_total. Let's broadcast from there */ MPI_Bcast(N_total, swift_type_count, MPI_LONG_LONG_INT, mpi_size - 1, comm); + /* List what fields to write. + * Note that we want to want to write a 0-size dataset for some species + * in case future snapshots will contain them (e.g. star formation) */ + const int to_write[swift_type_count] = { + with_hydro, with_DM, with_DM_background, with_sink, + with_stars, with_black_hole, with_neutrinos + + }; + /* Now everybody knows its offset and the total number of particles of each * type */ @@ -1207,6 +1231,8 @@ void write_output_serial(struct engine* e, swift_type_count); io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, swift_type_count); + io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total, + swift_type_count); double MassTable[swift_type_count] = {0}; io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count); io_write_attribute(h_grp, "InitialMassTable", DOUBLE, @@ -1219,6 +1245,7 @@ void write_output_serial(struct engine* e, io_write_attribute_i(h_grp, "ThisFile", 0); io_write_attribute_s(h_grp, "SelectOutput", current_selection_name); io_write_attribute_i(h_grp, "Virtual", 0); + io_write_attribute(h_grp, "CanHaveTypes", INT, to_write, swift_type_count); if (subsample_any) { io_write_attribute_s(h_grp, "OutputType", "SubSampled"); @@ -1240,9 +1267,10 @@ void write_output_serial(struct engine* e, /* Loop over all particle types */ for (int ptype = 0; ptype < swift_type_count; ptype++) { - /* Don't do anything if there are (a) no particles of this kind, or (b) - * if we have disabled every field of this particle type. */ - if (N_total[ptype] == 0 || numFields[ptype] == 0) continue; + /* Don't do anything if there are + * (a) no particles of this kind in this run, or + * (b) if we have disabled every field of this particle type. */ + if (!to_write[ptype] || numFields[ptype] == 0) continue; /* Open the particle group in the file */ char partTypeGroupName[PARTICLE_GROUP_BUFFER_SIZE]; @@ -1261,7 +1289,8 @@ void write_output_serial(struct engine* e, if (h_err < 0) error("Error while creating alias for particle group.\n"); /* Write the number of particles as an attribute */ - io_write_attribute_l(h_grp, "NumberOfParticles", N_total[ptype]); + io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]); + io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]); /* Close particle group */ H5Gclose(h_grp); @@ -1290,8 +1319,8 @@ void write_output_serial(struct engine* e, io_write_cell_offsets(h_grp_cells, e->s->cdim, e->s->dim, e->s->cells_top, e->s->nr_cells, e->s->width, mpi_rank, /*distributed=*/0, subsample, subsample_fraction, - e->snapshot_output_count, N_total, offset, numFields, - internal_units, snapshot_units); + e->snapshot_output_count, N_total, offset, to_write, + numFields, internal_units, snapshot_units); /* Close everything */ if (mpi_rank == 0) { @@ -1312,9 +1341,10 @@ void write_output_serial(struct engine* e, /* Loop over all particle types */ for (int ptype = 0; ptype < swift_type_count; ptype++) { - /* Don't do anything if there are (a) no particles of this kind, or (b) - * if we have disabled every field of this particle type. */ - if (N_total[ptype] == 0 || numFields[ptype] == 0) continue; + /* Don't do anything if there are + * (a) no particles of this kind in this run, or + * (b) if we have disabled every field of this particle type. */ + if (!to_write[ptype] || numFields[ptype] == 0) continue; /* Add the global information for that particle type to the XMF * meta-file */ diff --git a/src/serial_io.h b/src/serial_io.h index 26a6a1cff1fdd7fcd7bfac1b4711c30a899d07c1..350da844aa034575c04f7efa432e84abb933c5ef 100644 --- a/src/serial_io.h +++ b/src/serial_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 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 @@ -20,7 +20,7 @@ #define SWIFT_SERIAL_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5) @@ -42,17 +42,20 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, struct bpart** bparts, size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, size_t* Nnuparts, size_t* Nsinks, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_sinks, 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, - int remap_ids, struct ic_info* ics_metadata); + int* flag_entropy, const int with_hydro, + const int with_gravity, const int with_sinks, + const int with_stars, const int with_black_holes, + const int with_cosmology, const int cleanup_h, + const int cleanup_sqrt_a, const double h, const double a, + const int mpi_rank, int mpi_size, MPI_Comm comm, + MPI_Info info, const int n_threads, const int dry_run, + const int remap_ids, struct ic_info* ics_metadata); void write_output_serial(struct engine* e, const struct unit_system* internal_units, - const struct unit_system* snapshot_units, int mpi_rank, - int mpi_size, MPI_Comm comm, MPI_Info info); + const struct unit_system* snapshot_units, + const int mpi_rank, const int mpi_size, MPI_Comm comm, + MPI_Info info); #endif /* HAVE_HDF5 && WITH_MPI && !HAVE_PARALLEL_HDF5 */ diff --git a/src/sign.h b/src/sign.h index a92875d5d6c5f319eb6019adb20aa34af86d620b..436e00c59d9300eb1d2cf440874ec703f4c23554 100644 --- a/src/sign.h +++ b/src/sign.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/signal_velocity.h b/src/signal_velocity.h new file mode 100644 index 0000000000000000000000000000000000000000..7a0cd182a7e632a11ffb5c00eb3be3b7e1504ea3 --- /dev/null +++ b/src/signal_velocity.h @@ -0,0 +1,77 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 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_SIGNAL_VELOCITY_H +#define SWIFT_SIGNAL_VELOCITY_H + +/* Config parameters. */ +#include <config.h> + +#ifndef NONE_MHD + +/* Include MHD definition of signal velocity */ +#include "mhd.h" + +/** + * @brief Compute the signal velocity between two gas particles, + * MHD case. + * + * Warning ONLY to be called just after preparation of the force loop. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + return mhd_signal_velocity(dx, pi, pj, mu_ij, beta); +} + +#else + +/* Include hydro definition of signal velocity */ +#include "hydro.h" + +/** + * @brief Compute the signal velocity between two gas particles, + * Non-MHD case. + * + * Warning: Can ONLY to be called just after preparation of the force loop. + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + return hydro_signal_velocity(dx, pi, pj, mu_ij, beta); +} + +#endif + +#endif /* SWIFT_SIGNAL_VELOCITY_H */ diff --git a/src/sincos.h b/src/sincos.h index fbbee10fe54ce5ac48d1eaaca083cdc0eb51f52c..b43d88d5afedcb173d7d743cac49a5206baf50ab 100644 --- a/src/sincos.h +++ b/src/sincos.h @@ -20,7 +20,7 @@ #define SWIFT_SINCOS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> @@ -37,15 +37,15 @@ * not implement GNU extensions to the C language. * * @param x The input value. - * @param sin (return) The sine of x. - * @param cos (return) The cosine of x. + * @param s (return) The sine of x. + * @param c (return) The cosine of x. */ __attribute__((always_inline)) INLINE static void sincos(const double x, - double *sin, - double *cos) { + double *restrict s, + double *restrict c) { - *sin = sin(x); - *cos = cos(x); + *s = sin(x); + *c = cos(x); } #endif @@ -59,15 +59,15 @@ __attribute__((always_inline)) INLINE static void sincos(const double x, * not implement GNU extensions to the C language. * * @param x The input value. - * @param sin (return) The sine of x. - * @param cos (return) The cosine of x. + * @param s (return) The sine of x. + * @param c (return) The cosine of x. */ __attribute__((always_inline)) INLINE static void sincosf(const float x, - float *sin, - float *cos) { + float *restrict s, + float *restrict c) { - *sin = sinf(x); - *cos = cosf(x); + *s = sinf(x); + *c = cosf(x); } #endif diff --git a/src/single_io.c b/src/single_io.c index de438e81a786abb465550a087612dd5f3251fae3..3a8e17a938249c04345826db23062c2296222d28 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && !defined(WITH_MPI) @@ -49,6 +49,7 @@ #include "io_compression.h" #include "io_properties.h" #include "memuse.h" +#include "mhd_io.h" #include "output_list.h" #include "output_options.h" #include "part.h" @@ -235,9 +236,9 @@ void read_array_single(hid_t h_grp, const struct io_props props, size_t N, * @todo A better version using HDF5 hyper-slabs to write the file directly from * the part array will be written once the structures have been stabilized. */ -void write_array_single(const struct engine* e, hid_t grp, char* fileName, - FILE* xmfFile, char* partTypeGroupName, - const struct io_props props, size_t N, +void write_array_single(const struct engine* e, hid_t grp, const char* fileName, + FILE* xmfFile, const char* partTypeGroupName, + const struct io_props props, const size_t N, const enum lossy_compression_schemes lossy_compression, const struct unit_system* internal_units, const struct unit_system* snapshot_units) { @@ -296,11 +297,14 @@ void write_array_single(const struct engine* e, hid_t grp, char* fileName, /* Dataset properties */ hid_t h_prop = H5Pcreate(H5P_DATASET_CREATE); - /* Set chunk size */ - h_err = H5Pset_chunk(h_prop, rank, chunk_shape); - if (h_err < 0) - error("Error while setting chunk size (%llu, %llu) for field '%s'.", - chunk_shape[0], chunk_shape[1], props.name); + /* Set chunk size if have some particles */ + if (N > 0) { + h_err = H5Pset_chunk(h_prop, rank, chunk_shape); + if (h_err < 0) + error("Error while setting chunk size (%llu, %llu) for field '%s'.", + (unsigned long long)chunk_shape[0], + (unsigned long long)chunk_shape[1], props.name); + } /* Are we imposing some form of lossy compression filter? */ char comp_buffer[32] = "None"; @@ -308,8 +312,8 @@ void write_array_single(const struct engine* e, hid_t grp, char* fileName, set_hdf5_lossy_compression(&h_prop, &h_type, lossy_compression, props.name, comp_buffer); - /* Impose GZIP data compression */ - if (e->snapshot_compression > 0) { + /* Impose GZIP and shuffle data compression */ + if (e->snapshot_compression > 0 && N > 0) { h_err = H5Pset_shuffle(h_prop); if (h_err < 0) error("Error while setting shuffling options for field '%s'.", @@ -322,9 +326,11 @@ void write_array_single(const struct engine* e, hid_t grp, char* fileName, } /* Impose check-sum to verify data corruption */ - h_err = H5Pset_fletcher32(h_prop); - if (h_err < 0) - error("Error while setting checksum options for field '%s'.", props.name); + if (N > 0) { + h_err = H5Pset_fletcher32(h_prop); + if (h_err < 0) + error("Error while setting checksum options for field '%s'.", props.name); + } /* Create dataset */ const hid_t h_data = H5Dcreate(grp, props.name, h_type, h_space, H5P_DEFAULT, @@ -428,18 +434,17 @@ void write_array_single(const struct engine* e, hid_t grp, char* fileName, * @warning Can not read snapshot distributed over more than 1 file !!! * @todo Read snapshots distributed in more than one file. */ -void read_ic_single(const char* fileName, - const struct unit_system* internal_units, double dim[3], - struct part** parts, struct gpart** gparts, - struct sink** sinks, struct spart** sparts, - struct bpart** bparts, size_t* Ngas, size_t* Ngparts, - size_t* Ngparts_background, size_t* Nnuparts, - size_t* Nsinks, size_t* Nstars, size_t* Nblackholes, - int* flag_entropy, int with_hydro, int with_gravity, - int with_sink, 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, - int remap_ids, struct ic_info* ics_metadata) { +void read_ic_single( + const char* fileName, const struct unit_system* internal_units, + double dim[3], struct part** parts, struct gpart** gparts, + struct sink** sinks, struct spart** sparts, struct bpart** bparts, + size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, size_t* Nnuparts, + size_t* Nsinks, size_t* Nstars, size_t* Nblackholes, int* flag_entropy, + const int with_hydro, const int with_gravity, const int with_sink, + const int with_stars, const int with_black_holes, const int with_cosmology, + const int cleanup_h, const int cleanup_sqrt_a, const double h, + const double a, const int n_threads, const int dry_run, const int remap_ids, + struct ic_info* ics_metadata) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -663,6 +668,7 @@ void read_ic_single(const char* fileName, if (with_hydro) { Nparticles = *Ngas; hydro_read_particles(*parts, list, &num_fields); + num_fields += mhd_read_particles(*parts, list + num_fields); num_fields += chemistry_read_particles(*parts, list + num_fields); num_fields += rt_read_particles(*parts, list + num_fields); } @@ -821,7 +827,12 @@ void write_output_single(struct engine* e, 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; + const int with_DM = e->s->with_DM; const int with_neutrinos = e->s->with_neutrinos; + const int with_hydro = (e->policy & engine_policy_hydro) ? 1 : 0; + const int with_stars = (e->policy & engine_policy_stars) ? 1 : 0; + const int with_black_hole = (e->policy & engine_policy_black_holes) ? 1 : 0; + const int with_sink = (e->policy & engine_policy_sinks) ? 1 : 0; #ifdef HAVE_VELOCIRAPTOR const int with_stf = (e->policy & engine_policy_structure_finding) && (e->s->gpart_group_data != NULL); @@ -956,12 +967,21 @@ void write_output_single(struct engine* e, } /* Format things in a Gadget-friendly array */ - long long N_total[swift_type_count] = { + const long long N_total[swift_type_count] = { (long long)Ngas_written, (long long)Ndm_written, (long long)Ndm_background, (long long)Nsinks_written, (long long)Nstars_written, (long long)Nblackholes_written, (long long)Ndm_neutrino}; + /* List what fields to write. + * Note that we want to want to write a 0-size dataset for some species + * in case future snapshots will contain them (e.g. star formation) */ + const int to_write[swift_type_count] = { + with_hydro, with_DM, with_DM_background, with_sink, + with_stars, with_black_hole, with_neutrinos + + }; + /* Open file */ /* message("Opening file '%s'.", fileName); */ h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); @@ -1041,6 +1061,8 @@ void write_output_single(struct engine* e, swift_type_count); io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, swift_type_count); + io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total, + swift_type_count); double MassTable[swift_type_count] = {0}; io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count); io_write_attribute(h_grp, "InitialMassTable", DOUBLE, @@ -1053,6 +1075,7 @@ void write_output_single(struct engine* e, io_write_attribute_i(h_grp, "ThisFile", 0); io_write_attribute_s(h_grp, "SelectOutput", current_selection_name); io_write_attribute_i(h_grp, "Virtual", 0); + io_write_attribute(h_grp, "CanHaveTypes", INT, to_write, swift_type_count); if (subsample_any) { io_write_attribute_s(h_grp, "OutputType", "SubSampled"); @@ -1081,15 +1104,16 @@ void write_output_single(struct engine* e, e->s->nr_cells, e->s->width, e->nodeID, /*distributed=*/0, subsample, subsample_fraction, e->snapshot_output_count, N_total, global_offsets, - numFields, internal_units, snapshot_units); + to_write, numFields, internal_units, snapshot_units); H5Gclose(h_grp); /* Loop over all particle types */ for (int ptype = 0; ptype < swift_type_count; ptype++) { - /* Don't do anything if there are (a) no particles of this kind, or (b) - * if we have disabled every field of this particle type. */ - if (numParticles[ptype] == 0 || numFields[ptype] == 0) continue; + /* Don't do anything if there are + * (a) no particles of this kind in this run, or + * (b) if we have disabled every field of this particle type. */ + if (!to_write[ptype] || numFields[ptype] == 0) continue; /* Add the global information for that particle type to the XMF meta-file */ xmf_write_groupheader(xmfFile, fileName, /*distributed=*/0, @@ -1112,7 +1136,8 @@ void write_output_single(struct engine* e, if (h_err < 0) error("Error while creating alias for particle group.\n"); /* Write the number of particles and fields as an attribute */ - io_write_attribute_l(h_grp, "NumberOfParticles", numParticles[ptype]); + io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]); + io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]); int num_fields = 0; struct io_props list[100]; diff --git a/src/single_io.h b/src/single_io.h index 9a372fbb046c1a3d8a563fe4706ba9e287ef9c6f..13a1e1be9c292aafc3fd8c0c5754fa32b7e53512 100644 --- a/src/single_io.h +++ b/src/single_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 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 @@ -20,7 +20,7 @@ #define SWIFT_SINGLE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_HDF5) && !defined(WITH_MPI) @@ -31,18 +31,17 @@ struct engine; struct unit_system; -void read_ic_single(const char* fileName, - const struct unit_system* internal_units, double dim[3], - struct part** parts, struct gpart** gparts, - struct sink** sinks, struct spart** sparts, - struct bpart** bparts, size_t* Ngas, size_t* Ndm, - size_t* Ndm_background, size_t* Nnuparts, size_t* Nsinks, - size_t* Nstars, size_t* Nblackholes, int* flag_entropy, - int with_hydro, int with_gravity, int with_sinks, - 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, int remap_ids, - struct ic_info* ics_metadata); +void read_ic_single( + const char* fileName, const struct unit_system* internal_units, + double dim[3], struct part** parts, struct gpart** gparts, + struct sink** sinks, struct spart** sparts, struct bpart** bparts, + size_t* Ngas, size_t* Ndm, size_t* Ndm_background, size_t* Nnuparts, + size_t* Nsinks, size_t* Nstars, size_t* Nblackholes, int* flag_entropy, + const int with_hydro, const int with_gravity, const int with_sinks, + const int with_stars, const int with_black_holes, const int with_cosmology, + const int cleanup_h, const int cleanup_sqrt_a, const double h, + const double a, const int nr_threads, const int dry_run, + const int remap_ids, struct ic_info* ics_metadata); void write_output_single(struct engine* e, const struct unit_system* internal_units, diff --git a/src/sink.h b/src/sink.h index f263a5f4f83aa8105fd0763b713bd9c76980fb06..e5d3a78aa2a35e6604136194f68bc496cd088f05 100644 --- a/src/sink.h +++ b/src/sink.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -20,20 +20,7 @@ #define SWIFT_SINK_H /* Config parameters. */ -#include "../config.h" - -/** - * @brief Defines which sink particle should be removed. - * - * In the iact function of the merger, we return this enum in order - * to define if the none, the first or the second sink particle should be - * removed (in the order of the arguments) - */ -enum sink_merger_remove { - sink_merger_remove_none, - sink_merger_remove_first, - sink_merger_remove_second -}; +#include <config.h> /* Select the correct sink model */ #if defined(SINK_NONE) diff --git a/src/sink/Default/sink.h b/src/sink/Default/sink.h index 5803b1ae681c19acb95e5e5f73758395abfb0771..7ae8017350083d177502c29c905ab987ffa56090 100644 --- a/src/sink/Default/sink.h +++ b/src/sink/Default/sink.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -48,11 +48,15 @@ __attribute__((always_inline)) INLINE static float sink_compute_timestep( * @param sink_props The properties of the sink particles scheme. */ __attribute__((always_inline)) INLINE static void sink_first_init_sink( - struct sink* sp, const struct sink_props* sink_props) { + struct sink* sp, const struct sink_props* sink_props) {} - sp->r_cut = sink_props->cut_off_radius; - sp->time_bin = 0; -} +/** + * @brief Prepares a particle for the sink calculation. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void sink_init_part( + struct part* restrict p) {} /** * @brief Prepares a sink-particle for its interactions @@ -60,21 +64,7 @@ __attribute__((always_inline)) INLINE static void sink_first_init_sink( * @param sp The particle to act upon */ __attribute__((always_inline)) INLINE static void sink_init_sink( - struct sink* sp) { -#ifdef DEBUG_INTERACTIONS_SINKS - for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_SINKS; ++i) - sp->ids_ngbs_accretion[i] = -1; - sp->num_ngb_accretion = 0; - - for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_SINKS; ++i) - sp->ids_ngbs_merger[i] = -1; - sp->num_ngb_merger = 0; - - for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_SINKS; ++i) - sp->ids_ngbs_formation[i] = -1; - sp->num_ngb_formation = 0; -#endif -} + struct sink* sp) {} /** * @brief Predict additional particle fields forward in time when drifting @@ -107,7 +97,8 @@ __attribute__((always_inline)) INLINE static void sink_kick_extra( * @brief Calculate if the gas has the potential of becoming * a sink. * - * No sink formation should occur, so return 0. + * Return 0 if no sink formation should occur. + * Note: called in runner_do_sink_formation * * @param sink_props the sink properties to use. * @param p the gas particles. @@ -136,6 +127,7 @@ INLINE static int sink_is_forming( * sink or not. * * No SF should occur, so return 0. + * Note: called in runner_do_sink_formation * * @param p The #part. * @param xp The #xpart. @@ -148,6 +140,7 @@ INLINE static int sink_should_convert_to_sink( const struct part* p, const struct xpart* xp, const struct sink_props* sink_props, const struct engine* e, const double dt_sink) { + return 0; } @@ -175,6 +168,33 @@ INLINE static void sink_copy_properties( const struct unit_system* restrict us, const struct cooling_function_data* restrict cooling) {} +/** + * @brief Update the properties of a sink particles by swallowing + * a gas particle. + * + * @param sp The #sink to update. + * @param p The #part that is swallowed. + * @param xp The #xpart that is swallowed. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void sink_swallow_part( + struct sink* sp, const struct part* p, const struct xpart* xp, + const struct cosmology* cosmo) {} + +/** + * @brief Update the properties of a sink particles by swallowing + * a sink particle. + * + * @param spi The #sink to update. + * @param spj The #sink that is swallowed. + * @param cosmo The current cosmological model. + * @param time Time since the start of the simulation (non-cosmo mode). + * @param with_cosmology Are we running with cosmology? + * @param props The properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static void sink_swallow_sink( + struct sink* spi, const struct sink* spj, const struct cosmology* cosmo) {} + /** * @brief Should the sink spawn a star particle? * diff --git a/src/sink/Default/sink_debug.h b/src/sink/Default/sink_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..407b6dfc1ba33f373cb2f39cddef29866181a18a --- /dev/null +++ b/src/sink/Default/sink_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_SINK_DEFAULT_DEBUG_H +#define SWIFT_SINK_DEFAULT_DEBUG_H + +__attribute__((always_inline)) INLINE static void sink_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_SINK_DEFAULT_DEBUG_H */ diff --git a/src/sink/Default/sink_iact.h b/src/sink/Default/sink_iact.h index 7c7e5ebf786e2c6fed49d712157f9dd658d672e7..5b74c60e08de4817c2ea1d071298420953e3e2ab 100644 --- a/src/sink/Default/sink_iact.h +++ b/src/sink/Default/sink_iact.h @@ -20,36 +20,43 @@ #define SWIFT_DEFAULT_SINKS_IACT_H /** - * @brief Compute formation interaction between two particles (non-symmetric). + * @brief do sink 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 ri Comoving cut off radius of particle i. + * @param hi Comoving smoothing-length of particle i. * @param hj Comoving smoothing-length of particle j. - * @param si First sink particle. + * @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_nonsym_sinks_compute_formation(const float r2, const float *dx, - const float hi, const float hj, - struct sink *restrict si, - const struct part *restrict pj, - const float a, const float H) { - -#ifdef DEBUG_INTERACTIONS_SINKS - /* Update ngb counters */ - if (si->num_ngb_formation < MAX_NUM_OF_NEIGHBOURS_SINKS) - si->ids_ngbs_formation[si->num_ngb_formation] = pj->id; +__attribute__((always_inline)) INLINE static void runner_iact_sink( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H, const struct sink_props *sink_props) {} - /* Update ngb counters */ - ++si->num_ngb_formation; -#endif -} +/** + * @brief do sink 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_sink( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, const struct part *restrict pj, const float a, + const float H, const struct sink_props *sink_props) {} /** - * @brief Compute the sink merger interaction. + * @brief Compute sink-sink swallow interaction (non-symmetric). * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -57,32 +64,15 @@ runner_iact_nonsym_sinks_compute_formation(const float r2, const float *dx, * @param rj Comoving cut off radius of particle j. * @param si First sink particle. * @param sj Second sink particle. - * @param a Current scale factor. - * @param H Current Hubble parameter. - * - * @param Which particle should be removed? - * Possible value: (sink_merger_remove_none/first/second) */ -__attribute__((always_inline)) INLINE static enum sink_merger_remove -runner_iact_sym_sinks_merger(const float r2, const float *dx, const float hi, - const float hj, struct sink *restrict si, - struct sink *restrict sj, const float a, - const float H) { - -#ifdef DEBUG_INTERACTIONS_SINKS - /* Update ngb counters */ - if (si->num_ngb_merger < MAX_NUM_OF_NEIGHBOURS_SINKS) - si->ids_ngbs_merger[si->num_ngb_merger] = sj->id; - - /* Update ngb counters */ - ++si->num_ngb_merger; -#endif - - return sink_merger_remove_none; -} +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_sinks_sink_swallow(const float r2, const float *dx, + const float ri, const float rj, + struct sink *restrict si, + struct sink *restrict sj) {} /** - * @brief Accretion interaction between two particles (non-symmetric). + * @brief Compute sink-gas swallow interaction (non-symmetric). * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -90,24 +80,11 @@ runner_iact_sym_sinks_merger(const float r2, const float *dx, const float hi, * @param hj Comoving smoothing-length of particle j. * @param si First sink particle. * @param pj Second particle. - * @param a Current scale factor. - * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_sinks_accretion(const float r2, const float *dx, - const float hi, const float hj, - struct sink *restrict si, - const struct part *restrict pj, - const float a, const float H) { - -#ifdef DEBUG_INTERACTIONS_SINKS - /* Update ngb counters */ - if (si->num_ngb_accretion < MAX_NUM_OF_NEIGHBOURS_SINKS) - si->ids_ngbs_accretion[si->num_ngb_accretion] = pj->id; - - /* Update ngb counters */ - ++si->num_ngb_accretion; -#endif -} +runner_iact_nonsym_sinks_gas_swallow(const float r2, const float *dx, + const float ri, const float hj, + struct sink *restrict si, + struct part *restrict pj) {} #endif diff --git a/src/sink/Default/sink_io.h b/src/sink/Default/sink_io.h index d4deb5911fde5fb37107cba78335e23f7a558386..fd47e65d8b94f573835f0f44ceb293c8d4f4c6c5 100644 --- a/src/sink/Default/sink_io.h +++ b/src/sink/Default/sink_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/sink/Default/sink_part.h b/src/sink/Default/sink_part.h index 56bf9f783c8b2bec6a40193629c1ecf871f6690a..112248fc81dfce1f8e25714c820e5fa4ed5f6c36 100644 --- a/src/sink/Default/sink_part.h +++ b/src/sink/Default/sink_part.h @@ -64,6 +64,9 @@ struct sink { #endif + /*! sink merger information (e.g. merging ID) */ + struct sink_sink_data merger_data; + #ifdef DEBUG_INTERACTIONS_SINKS /*! Number of interactions in merger SELF and PAIR */ int num_ngb_merger; diff --git a/src/sink/Default/sink_properties.h b/src/sink/Default/sink_properties.h index 6ae2438c94f12ad2b2c9eda6dd866100855f59fc..6691840dc98e5c5897b881539e1c3979c17e21f0 100644 --- a/src/sink/Default/sink_properties.h +++ b/src/sink/Default/sink_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/sink/Default/sink_struct.h b/src/sink/Default/sink_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..2e6aca2ff7d10bd97dcc44814020dc797362ffcd --- /dev/null +++ b/src/sink/Default/sink_struct.h @@ -0,0 +1,92 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Yves Revaz (yves.revaz@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_SINK_STRUCT_DEFAULT_H +#define SWIFT_SINK_STRUCT_DEFAULT_H + +/** + * @brief Sink-related fields carried by each *gas* particle. + */ +struct sink_part_data {}; + +/** + * @brief Sink-related fields carried by each *sink* particle. + */ +struct sink_sink_data {}; + +/** + * @brief Return the ID of the sink that should swallow this #part. + * + * @param s_data The #part's #sink_part_data structure. + */ +__attribute__((always_inline)) INLINE static long long sink_get_part_swallow_id( + struct sink_part_data* s_data) { + + /* Return a non-existing ID */ + return -1; +} + +/** + * @brief Update a given #part's sink data field to mark the particle has + * not yet been swallowed. + * + * @param s_data The #part's #sink_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +sink_mark_part_as_not_swallowed(struct sink_part_data* s_data) {} + +/** + * @brief Update a given #part's sink data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #part's #sink_part_data structure. + */ +__attribute__((always_inline)) INLINE static void sink_mark_part_as_swallowed( + struct sink_part_data* s_data) {} + +/** + * @brief Update a given #sink's sink data field to mark the particle has + * not yet been swallowed. + * + * @param s_data The #sink's #sink_sink_data structure. + */ +__attribute__((always_inline)) INLINE static void +sink_mark_sink_as_not_swallowed(struct sink_sink_data* s_data) {} + +/** + * @brief Update a given #sink's sink data field to mark the particle has + * having been been swallowed. + * + * @param s_data The #sink's #bsink_sink_data structure. + */ +__attribute__((always_inline)) INLINE static void sink_mark_sink_as_merged( + struct sink_sink_data* s_data) {} + +/** + * @brief Return the ID of the sink that should swallow this #sink. + * + * @param s_data The #sink's #sink_sink_data structure. + */ +__attribute__((always_inline)) INLINE static long long sink_get_sink_swallow_id( + struct sink_sink_data* s_data) { + + /* Return a non-existing ID */ + return -1; +} + +#endif /* SWIFT_SINK_STRUCT_DEFAULT_H */ diff --git a/src/sink/GEAR/sink.h b/src/sink/GEAR/sink.h index d077bbe675d4771af82e9b6bd476ec95d8c0f76c..554df5b03f2cddcf0ee3d210aa03d4159197719a 100644 --- a/src/sink/GEAR/sink.h +++ b/src/sink/GEAR/sink.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2021 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 @@ -22,6 +22,7 @@ #include <float.h> /* Local includes */ +#include "cooling.h" #include "minmax.h" #include "random.h" #include "sink_part.h" @@ -52,10 +53,35 @@ __attribute__((always_inline)) INLINE static void sink_first_init_sink( sp->r_cut = sink_props->cut_off_radius; sp->time_bin = 0; + + sp->number_of_gas_swallows = 0; + sp->number_of_direct_gas_swallows = 0; + sp->number_of_sink_swallows = 0; + sp->number_of_direct_sink_swallows = 0; + sp->swallowed_angular_momentum[0] = 0.f; + sp->swallowed_angular_momentum[1] = 0.f; + sp->swallowed_angular_momentum[2] = 0.f; + + sink_mark_sink_as_not_swallowed(&sp->merger_data); +} + +/** + * @brief Initialisation of particle data before the hydro density loop. + * Note: during initalisation (space_init) + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void sink_init_part( + struct part* restrict p) { + + struct sink_part_data* cpd = &p->sink_data; + + cpd->can_form_sink = 1; } /** - * @brief Prepares a sink-particle for its interactions + * @brief Initialisation of sink particle data before sink loops. + * Note: during initalisation (space_init_sinks) * * @param sp The particle to act upon */ @@ -107,7 +133,8 @@ __attribute__((always_inline)) INLINE static void sink_kick_extra( * @brief Calculate if the gas has the potential of becoming * a sink. * - * No sink formation should occur, so return 0. + * Return 0 if no sink formation should occur. + * Note: called in runner_do_sink_formation * * @param sink_props the sink properties to use. * @param p the gas particles. @@ -128,7 +155,22 @@ INLINE static int sink_is_forming( const struct cooling_function_data* restrict cooling, const struct entropy_floor_properties* restrict entropy_floor) { - return 1; + /* the particle is not elligible */ + if (!p->sink_data.can_form_sink) return 0; + + const float temperature_max = sink_props->maximal_temperature; + const float temperature = cooling_get_temperature(phys_const, hydro_props, us, + cosmo, cooling, p, xp); + + const float density_threashold = sink_props->density_threashold; + const float density = hydro_get_physical_density(p, cosmo); + + if (density > density_threashold && temperature < temperature_max) { + message("forming a sink particle ! %lld", p->id); + return 1; + } + + return 0; } /** @@ -136,6 +178,7 @@ INLINE static int sink_is_forming( * sink or not. * * No SF should occur, so return 0. + * Note: called in runner_do_sink_formation * * @param p The #part. * @param xp The #xpart. @@ -148,10 +191,11 @@ INLINE static int sink_should_convert_to_sink( const struct part* p, const struct xpart* xp, const struct sink_props* sink_props, const struct engine* e, const double dt_sink) { - const float random_number = - random_unit_interval(p->id, e->ti_current, random_number_star_formation); - return random_number < 5e-4; + /* We do not use a stockastic approach. + * Once elligible (sink_is_forming), the gas particle form a sink */ + + return 1; } /** @@ -176,7 +220,133 @@ INLINE static void sink_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) { + + /* First initialisation */ + sink_init_sink(sink); + + /* Flag it as not swallowed */ + sink_mark_sink_as_not_swallowed(&sink->merger_data); +} + +/** + * @brief Update the properties of a sink particles by swallowing + * a gas particle. + * + * @param sp The #sink to update. + * @param p The #part that is swallowed. + * @param xp The #xpart that is swallowed. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void sink_swallow_part( + struct sink* sp, const struct part* p, const struct xpart* xp, + const struct cosmology* cosmo) { + + /* Get the current dynamical masses */ + const float gas_mass = hydro_get_mass(p); + const float sink_mass = sp->mass; + + /* Increase the dynamical mass of the sink. */ + sp->mass += gas_mass; + sp->gpart->mass += gas_mass; + + /* Physical velocity difference between the particles */ + const float dv[3] = {(sp->v[0] - p->v[0]) * cosmo->a_inv, + (sp->v[1] - p->v[1]) * cosmo->a_inv, + (sp->v[2] - p->v[2]) * cosmo->a_inv}; + + /* Physical distance between the particles */ + const float dx[3] = {(sp->x[0] - p->x[0]) * cosmo->a, + (sp->x[1] - p->x[1]) * cosmo->a, + (sp->x[2] - p->x[2]) * cosmo->a}; + + /* Collect the swallowed angular momentum */ + sp->swallowed_angular_momentum[0] += + gas_mass * (dx[1] * dv[2] - dx[2] * dv[1]); + sp->swallowed_angular_momentum[1] += + gas_mass * (dx[2] * dv[0] - dx[0] * dv[2]); + sp->swallowed_angular_momentum[2] += + gas_mass * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Update the sink momentum */ + const float sink_mom[3] = {sink_mass * sp->v[0] + gas_mass * p->v[0], + sink_mass * sp->v[1] + gas_mass * p->v[1], + sink_mass * sp->v[2] + gas_mass * p->v[2]}; + + sp->v[0] = sink_mom[0] / sp->mass; + sp->v[1] = sink_mom[1] / sp->mass; + sp->v[2] = sink_mom[2] / sp->mass; + sp->gpart->v_full[0] = sp->v[0]; + sp->gpart->v_full[1] = sp->v[1]; + sp->gpart->v_full[2] = sp->v[2]; + + /* + const float dr = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]); + message( + "sink %lld swallowing gas particle %lld " + "(Delta_v = [%f, %f, %f] U_V, " + "Delta_x = [%f, %f, %f] U_L, " + "Delta_v_rad = %f)", + sp->id, p->id, -dv[0], -dv[1], -dv[2], -dx[0], -dx[1], -dx[2], + (dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]) / dr); + */ + + /* Update the sink metal masses */ + struct chemistry_sink_data* sp_chem = &sp->chemistry_data; + const struct chemistry_part_data* p_chem = &p->chemistry_data; + chemistry_add_part_to_sink(sp_chem, p_chem, gas_mass); + + /* This sink swallowed a gas particle */ + sp->number_of_gas_swallows++; + sp->number_of_direct_gas_swallows++; +} + +/** + * @brief Update the properties of a sink particles by swallowing + * a sink particle. + * + * @param spi The #sink to update. + * @param spj The #sink that is swallowed. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void sink_swallow_sink( + struct sink* spi, const struct sink* spj, const struct cosmology* cosmo) { + + /* Get the current dynamical masses */ + const float spi_dyn_mass = spi->mass; + const float spj_dyn_mass = spj->mass; + + /* Increase the masses of the sink. */ + spi->mass += spj->mass; + spi->gpart->mass += spj->mass; + + /* Collect the swallowed angular momentum */ + spi->swallowed_angular_momentum[0] += spj->swallowed_angular_momentum[0]; + spi->swallowed_angular_momentum[1] += spj->swallowed_angular_momentum[1]; + spi->swallowed_angular_momentum[2] += spj->swallowed_angular_momentum[2]; + + /* Update the sink momentum */ + const float sink_mom[3] = { + spi_dyn_mass * spi->v[0] + spj_dyn_mass * spj->v[0], + spi_dyn_mass * spi->v[1] + spj_dyn_mass * spj->v[1], + spi_dyn_mass * spi->v[2] + spj_dyn_mass * spj->v[2]}; + + spi->v[0] = sink_mom[0] / spi->mass; + spi->v[1] = sink_mom[1] / spi->mass; + spi->v[2] = sink_mom[2] / spi->mass; + spi->gpart->v_full[0] = spi->v[0]; + spi->gpart->v_full[1] = spi->v[1]; + spi->gpart->v_full[2] = spi->v[2]; + + /* Update the sink metal masses */ + struct chemistry_sink_data* spi_chem = &spi->chemistry_data; + const struct chemistry_sink_data* spj_chem = &spj->chemistry_data; + chemistry_add_sink_to_sink(spi_chem, spj_chem); + + /* This sink swallowed a sink particle */ + spi->number_of_sink_swallows++; + spi->number_of_direct_sink_swallows++; +} /** * @brief Should the sink spawn a star particle? @@ -197,10 +367,20 @@ INLINE static int sink_spawn_star(struct sink* sink, const struct engine* e, const int with_cosmology, const struct phys_const* phys_const, const struct unit_system* restrict us) { + const float random_number = random_unit_interval( sink->id, e->ti_current, random_number_star_formation); + // return random_number < 1; //1e-3; - return random_number < 1e-3; + if (sink->n_stars > 0) { + if (random_number < 1e-2) { + // sink->n_stars--; + // message("%lld spawn a star : n_star is now %d",sink->id,sink->n_stars); + return 0; + } else + return 0; + } else + return 0; } /** diff --git a/src/sink/GEAR/sink_debug.h b/src/sink/GEAR/sink_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..71cb85a60c2a7e818fe7df7548bff9a4cb0a4c3c --- /dev/null +++ b/src/sink/GEAR/sink_debug.h @@ -0,0 +1,30 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_SINK_GEAR_DEBUG_H +#define SWIFT_SINK_GEAR_DEBUG_H + +__attribute__((always_inline)) INLINE static void sink_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld] sink_part_data:", p->id); + warning("[PID%lld] swallow_id = %lld, can_form_sink = %d", p->id, + p->sink_data.swallow_id, p->sink_data.can_form_sink); +} + +#endif /* SWIFT_SINK_GEAR_DEBUG_H */ diff --git a/src/sink/GEAR/sink_iact.h b/src/sink/GEAR/sink_iact.h index ebd4343c44ca41bb5d97f2f21392fb22cb080e99..137ab008e8bcd8dff2f02a41212c6c6632c0c918 100644 --- a/src/sink/GEAR/sink_iact.h +++ b/src/sink/GEAR/sink_iact.h @@ -20,36 +20,60 @@ #define SWIFT_GEAR_SINKS_IACT_H /** - * @brief Compute formation interaction between two particles (non-symmetric). + * @brief do sink 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 ri Comoving cut off radius of particle i. + * @param hi Comoving smoothing-length of particle i. * @param hj Comoving smoothing-length of particle j. - * @param si First sink particle. + * @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_nonsym_sinks_compute_formation(const float r2, const float *dx, - const float hi, const float hj, - struct sink *restrict si, - const struct part *restrict pj, - const float a, const float H) { +__attribute__((always_inline)) INLINE static void runner_iact_sink( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H, const struct sink_props *sink_props) {} -#ifdef DEBUG_INTERACTIONS_SINKS - /* Update ngb counters */ - if (si->num_ngb_formation < MAX_NUM_OF_NEIGHBOURS_SINKS) - si->ids_ngbs_formation[si->num_ngb_formation] = pj->id; +/** + * @brief do sink 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_sink( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, const struct part *restrict pj, const float a, + const float H, const struct sink_props *sink_props) { - /* Update ngb counters */ - ++si->num_ngb_formation; -#endif + /* In order to prevent the formation of two sink particles at a distance + * smaller than the sink cutoff radius, we keep only gas particles with + * the smallest potential. */ + + const float r = sqrtf(r2); + + if (r < sink_props->cut_off_radius) { + + float potential_i = pi->gpart->potential; + float potential_j = pj->gpart->potential; + + /* if the potential is larger + * prevent the particle to form a sink */ + if (potential_i > potential_j) pi->sink_data.can_form_sink = 0; + } } /** - * @brief Compute the sink merger interaction. + * @brief Compute sink-sink swallow interaction (non-symmetric). * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -57,31 +81,49 @@ runner_iact_nonsym_sinks_compute_formation(const float r2, const float *dx, * @param rj Comoving cut off radius of particle j. * @param si First sink particle. * @param sj Second sink particle. - * @param a Current scale factor. - * @param H Current Hubble parameter. - * - * @param Which particle should be removed? - * Possible value: (sink_merger_remove_none/first/second) */ -__attribute__((always_inline)) INLINE static int runner_iact_sym_sinks_merger( - const float r2, const float *dx, const float hi, const float hj, - struct sink *restrict si, struct sink *restrict sj, const float a, - const float H) { +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_sinks_sink_swallow(const float r2, const float *dx, + const float ri, const float rj, + struct sink *restrict si, + struct sink *restrict sj) { + + /* See runner_iact_nonsym_bh_bh_swallow. + * The sink 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 sink + * have the exact same mass. */ + + /* We should check the relative energy */ - return sink_merger_remove_none; + if ((sj->mass < si->mass) || (sj->mass == si->mass && sj->id < si->id)) { + + /* This particle is swallowed by the sink with the largest mass of all the + * candidates wanting to swallow it (we use IDs to break ties)*/ + if ((sj->merger_data.swallow_mass < si->mass) || + (sj->merger_data.swallow_mass == si->mass && + sj->merger_data.swallow_id < si->id)) { + + // message("sink %lld wants to swallow sink particle %lld", si->id, + // sj->id); + + sj->merger_data.swallow_id = si->id; + sj->merger_data.swallow_mass = si->mass; + } + } #ifdef DEBUG_INTERACTIONS_SINKS /* Update ngb counters */ - if (si->num_ngb_merger < MAX_NUM_OF_NEIGHBOURS_SINKS) - si->ids_ngbs_merger[si->num_ngb_merger] = sj->id; + if (si->num_ngb_formation < MAX_NUM_OF_NEIGHBOURS_SINKS) + si->ids_ngbs_formation[si->num_ngb_formation] = pj->id; /* Update ngb counters */ - ++si->num_ngb_merger; + ++si->num_ngb_formation; #endif } /** - * @brief Accretion interaction between two particles (non-symmetric). + * @brief Compute sink-gas swallow interaction (non-symmetric). * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -89,23 +131,32 @@ __attribute__((always_inline)) INLINE static int runner_iact_sym_sinks_merger( * @param hj Comoving smoothing-length of particle j. * @param si First sink particle. * @param pj Second particle. - * @param a Current scale factor. - * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_sinks_accretion(const float r2, const float *dx, - const float hi, const float hj, - struct sink *restrict si, - const struct part *restrict pj, - const float a, const float H) { +runner_iact_nonsym_sinks_gas_swallow(const float r2, const float *dx, + const float ri, const float hj, + struct sink *restrict si, + struct part *restrict pj) { + + /* See see runner_iact_nonsym_bh_gas_swallow. + * We first check if a gas particle has not been already marked to + * be swallowed by another sink particle. */ + + /* We should check the relative energy */ + + // message("sink %lld wants to swallow gas particle %lld", si->id, pj->id); + + if (pj->sink_data.swallow_id < si->id) { + pj->sink_data.swallow_id = si->id; + } #ifdef DEBUG_INTERACTIONS_SINKS /* Update ngb counters */ - if (si->num_ngb_accretion < MAX_NUM_OF_NEIGHBOURS_SINKS) - si->ids_ngbs_accretion[si->num_ngb_accretion] = pj->id; + if (si->num_ngb_formation < MAX_NUM_OF_NEIGHBOURS_SINKS) + si->ids_ngbs_formation[si->num_ngb_formation] = pj->id; /* Update ngb counters */ - ++si->num_ngb_accretion; + ++si->num_ngb_formation; #endif } diff --git a/src/sink/GEAR/sink_io.h b/src/sink/GEAR/sink_io.h index 41a79d94d9da6f82d7859473ad21a922e96cfb1a..ac23fc3ab898284b1009c25c366a357c8ef8538e 100644 --- a/src/sink/GEAR/sink_io.h +++ b/src/sink/GEAR/sink_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2021 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2021 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 diff --git a/src/sink/GEAR/sink_part.h b/src/sink/GEAR/sink_part.h index f66402f5a5160f36c7f1a196e025373bc1b6dfaf..b07457516f26188bbd9c1c11f19ffc1dec349e42 100644 --- a/src/sink/GEAR/sink_part.h +++ b/src/sink/GEAR/sink_part.h @@ -54,6 +54,35 @@ struct sink { /*! Particle time bin */ timebin_t time_bin; + /*! number of stars contained in the sink */ + int n_stars; + + /*! Total (physical) angular momentum accumulated by swallowing particles */ + float swallowed_angular_momentum[3]; + + /*! Total number of sink merger events (including sink swallowed + * by merged-in sinks) */ + int number_of_sink_swallows; + + /*! Total number of sink merger events (excluding sink swallowed + * by merged-in sinks) */ + int number_of_direct_sink_swallows; + + /*! Total number of gas particles swallowed (including particles swallowed + * by merged-in sinks) */ + int number_of_gas_swallows; + + /*! Total number of gas particles swallowed (excluding particles swallowed + * by merged-in sinks) */ + int number_of_direct_gas_swallows; + + /*! Chemistry information (e.g. metal content at birth, swallowed metal + * content, etc.) */ + struct chemistry_sink_data chemistry_data; + + /*! sink merger information (e.g. merging ID) */ + struct sink_sink_data merger_data; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/sink/GEAR/sink_properties.h b/src/sink/GEAR/sink_properties.h index c8273b5da648d83fb4c2d30dc8b7ea3f319b4347..082443ae99fafa3381c9708761b245c725541dcc 100644 --- a/src/sink/GEAR/sink_properties.h +++ b/src/sink/GEAR/sink_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -26,6 +26,12 @@ struct sink_props { /*! Cut off radius */ float cut_off_radius; + + /*! Maximal gas temperature for forming a star. */ + float maximal_temperature; + + /*! Minimal gas density for forming a star. */ + float density_threashold; }; /** @@ -45,6 +51,21 @@ INLINE static void sink_props_init(struct sink_props *sp, sp->cut_off_radius = parser_get_param_float(params, "GEARSink:cut_off_radius"); + + sp->maximal_temperature = + parser_get_param_float(params, "GEARSink:maximal_temperature"); + + sp->density_threashold = + parser_get_param_float(params, "GEARSink:density_threashold"); + + /* Apply unit change */ + sp->maximal_temperature /= + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + sp->density_threashold /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + + message("maximal_temperature = %g", sp->maximal_temperature); + message("density_threashold = %g", sp->density_threashold); } /** diff --git a/src/sink/GEAR/sink_struct.h b/src/sink/GEAR/sink_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..c5427f2f970530ca873a437b7a39a48c19d0639d --- /dev/null +++ b/src/sink/GEAR/sink_struct.h @@ -0,0 +1,118 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Yves Revaz (yves.revaz@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_SINK_STRUCT_DEFAULT_H +#define SWIFT_SINK_STRUCT_DEFAULT_H + +/** + * @brief Sink-related fields carried by each *gas* particle. + */ +struct sink_part_data { + + /*! ID of the sink that will swallow this #part. */ + long long swallow_id; + + /*! Gravitational potential of the particle */ + uint8_t can_form_sink; +}; + +/** + * @brief Sink-related fields carried by each *sink* particle. + */ +struct sink_sink_data { + + /*! ID of the sink that will swallow this #sink. */ + long long swallow_id; + + /*! Mass of the sink that will swallow this #sink. */ + float swallow_mass; +}; + +/** + * @brief Return the ID of the sink that should swallow this #part. + * + * @param s_data The #part's #sink_part_data structure. + */ +__attribute__((always_inline)) INLINE static long long sink_get_part_swallow_id( + struct sink_part_data* s_data) { + + return s_data->swallow_id; +} + +/** + * @brief Update a given #part's sink data field to mark the particle has + * not yet been swallowed. + * + * @param s_data The #part's #sink_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +sink_mark_part_as_not_swallowed(struct sink_part_data* s_data) { + + s_data->swallow_id = -1; +} + +/** + * @brief Update a given #part's sink data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #part's #sink_part_data structure. + */ +__attribute__((always_inline)) INLINE static void sink_mark_part_as_swallowed( + struct sink_part_data* s_data) { + + s_data->swallow_id = -2; +} + +/** + * @brief Update a given #sink's sink data field to mark the particle has + * not yet been swallowed. + * + * @param s_data The #sink's #sink_sink_data structure. + */ +__attribute__((always_inline)) INLINE static void +sink_mark_sink_as_not_swallowed(struct sink_sink_data* s_data) { + + s_data->swallow_id = -1; + s_data->swallow_mass = 0.f; +} + +/** + * @brief Update a given #sink's sink data field to mark the particle has + * having been been swallowed. + * + * @param s_data The #sink's #bsink_sink_data structure. + */ +__attribute__((always_inline)) INLINE static void sink_mark_sink_as_merged( + struct sink_sink_data* s_data) { + + s_data->swallow_id = -2; + s_data->swallow_mass = -1.f; +} + +/** + * @brief Return the ID of the sink that should swallow this #sink. + * + * @param s_data The #sink's #sink_sink_data structure. + */ +__attribute__((always_inline)) INLINE static long long sink_get_sink_swallow_id( + struct sink_sink_data* s_data) { + + return s_data->swallow_id; +} + +#endif /* SWIFT_SINK_STRUCT_DEFAULT_H */ diff --git a/src/sink_debug.h b/src/sink_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..2346711e7387ebe3e480c759e0322e3c1f407d8d --- /dev/null +++ b/src/sink_debug.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_SINK_DEBUG_H +#define SWIFT_SINK_DEBUG_H + +/* Config parameters. */ +#include <config.h> + +/* Import the debug routines of the right sink definition */ +#if defined(SINK_NONE) +#include "./sink/Default/sink_debug.h" +#elif defined(SINK_GEAR) +#include "./sink/GEAR/sink_debug.h" +#else +#error "Invalid choice of sink model" +#endif + +#endif /* SWIFT_SINK_DEBUG_H */ diff --git a/src/sink_io.h b/src/sink_io.h index 8089f5e44097782fd5713fa71aa7f2d179984359..f5b0be176ca4dadf75bc825a9f2f001e1eef2883 100644 --- a/src/sink_io.h +++ b/src/sink_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -19,7 +19,7 @@ #ifndef SWIFT_SINK_IO_H #define SWIFT_SINK_IO_H -#include "../config.h" +#include <config.h> /* Load the correct sink type */ #if defined(SINK_NONE) diff --git a/src/sink_properties.h b/src/sink_properties.h index bb0f8d4fb8039d9cdb05e870926a25698141f272..2ec9bd9d00fa09ef01792a42e3117096e65244ff 100644 --- a/src/sink_properties.h +++ b/src/sink_properties.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -20,7 +20,7 @@ #define SWIFT_SINK_PROPERTIES_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct sink model */ #if defined(SINK_NONE) @@ -28,7 +28,7 @@ #elif defined(SINK_GEAR) #include "./sink/GEAR/sink_properties.h" #else -#error "Invalid choice of black hole model" +#error "Invalid choice of sink model" #endif #endif /* SWIFT_SINK_PROPERTIES_H */ diff --git a/src/sink_struct.h b/src/sink_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..00e6380298182149d8666663c977c5be665ac11d --- /dev/null +++ b/src/sink_struct.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Yves Revaz (yves.revaz@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_SINK_STRUCT_H +#define SWIFT_SINK_STRUCT_H + +/** + * @file src/sink_struct.h + * @brief Branches between the different sink functions. + */ + +/* Config parameters. */ +#include <config.h> + +/* Local includes. */ +#include "inline.h" + +/* Import the right black holes definition */ +#if defined(SINK_NONE) +#include "./sink/Default/sink_struct.h" +#elif defined(SINK_GEAR) +#include "./sink/GEAR/sink_struct.h" +#else +#error "Invalid choice of sink model." +#endif + +#endif /* SWIFT_SINK_STRUCT_H */ diff --git a/src/sort_part.h b/src/sort_part.h index 09816e1e36d8d0c9b3562e1f40126d62ece0e395..a22b7c1dd034cfa3ef4a479067a16b4cd93f6b5a 100644 --- a/src/sort_part.h +++ b/src/sort_part.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2017 James S. Wills (james.s.willis@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 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 @@ -21,7 +21,7 @@ #define SWIFT_SORT_PART_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "inline.h" diff --git a/src/space.c b/src/space.c index 8f2a945a17bc4184804e1f5083f5b94e240675c6..d955654f9afbe212d532f3076d5a00e5cc8104d2 100644 --- a/src/space.c +++ b/src/space.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -22,7 +22,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -42,12 +42,14 @@ /* Local headers. */ #include "active.h" #include "atomic.h" +#include "black_holes.h" #include "const.h" #include "cooling.h" #include "engine.h" #include "error.h" #include "kernel_hydro.h" #include "lock.h" +#include "mhd.h" #include "minmax.h" #include "proxy.h" #include "restart.h" @@ -58,6 +60,7 @@ #include "stars.h" #include "threadpool.h" #include "tools.h" +#include "tracers.h" /* Split size. */ int space_splitsize = space_splitsize_default; @@ -90,6 +93,11 @@ int engine_max_parts_per_ghost = engine_max_parts_per_ghost_default; int engine_max_sparts_per_ghost = engine_max_sparts_per_ghost_default; int engine_max_parts_per_cooling = engine_max_parts_per_cooling_default; +/*! Allocation margins */ +double engine_redistribute_alloc_margin = + engine_redistribute_alloc_margin_default; +double engine_foreign_alloc_margin = engine_foreign_alloc_margin_default; + /*! Maximal depth at which the stars resort task can be pushed */ int engine_star_resort_task_depth = engine_star_resort_task_depth_default; @@ -433,57 +441,56 @@ void space_map_cells_pre(struct space *s, int full, * @param nr_cells Number of #cell to pick up. * @param cells Array of @c nr_cells #cell pointers in which to store the * new cells. + * @param tpid ID of threadpool threadpool associated with cells_sub. */ -void space_getcells(struct space *s, int nr_cells, struct cell **cells) { - - /* Lock the space. */ - lock_lock(&s->lock); +void space_getcells(struct space *s, int nr_cells, struct cell **cells, + const short int tpid) { /* For each requested cell... */ for (int j = 0; j < nr_cells; j++) { /* Is the cell buffer empty? */ - if (s->cells_sub == NULL) { - if (swift_memalign("cells_sub", (void **)&s->cells_sub, cell_align, + if (s->cells_sub[tpid] == NULL) { + if (swift_memalign("cells_sub", (void **)&s->cells_sub[tpid], cell_align, space_cellallocchunk * sizeof(struct cell)) != 0) error("Failed to allocate more cells."); /* Clear the newly-allocated cells. */ - bzero(s->cells_sub, sizeof(struct cell) * space_cellallocchunk); + bzero(s->cells_sub[tpid], sizeof(struct cell) * space_cellallocchunk); /* Constructed a linked list */ for (int k = 0; k < space_cellallocchunk - 1; k++) - s->cells_sub[k].next = &s->cells_sub[k + 1]; - s->cells_sub[space_cellallocchunk - 1].next = NULL; + s->cells_sub[tpid][k].next = &s->cells_sub[tpid][k + 1]; + s->cells_sub[tpid][space_cellallocchunk - 1].next = NULL; } /* Is the multipole buffer empty? */ - if (s->with_self_gravity && s->multipoles_sub == NULL) { + if (s->with_self_gravity && s->multipoles_sub[tpid] == NULL) { if (swift_memalign( - "multipoles_sub", (void **)&s->multipoles_sub, multipole_align, + "multipoles_sub", (void **)&s->multipoles_sub[tpid], + multipole_align, space_cellallocchunk * sizeof(struct gravity_tensors)) != 0) error("Failed to allocate more multipoles."); /* Constructed a linked list */ for (int k = 0; k < space_cellallocchunk - 1; k++) - s->multipoles_sub[k].next = &s->multipoles_sub[k + 1]; - s->multipoles_sub[space_cellallocchunk - 1].next = NULL; + s->multipoles_sub[tpid][k].next = &s->multipoles_sub[tpid][k + 1]; + s->multipoles_sub[tpid][space_cellallocchunk - 1].next = NULL; } /* Pick off the next cell. */ - cells[j] = s->cells_sub; - s->cells_sub = cells[j]->next; - s->tot_cells += 1; + cells[j] = s->cells_sub[tpid]; + s->cells_sub[tpid] = cells[j]->next; /* Hook the multipole */ if (s->with_self_gravity) { - cells[j]->grav.multipole = s->multipoles_sub; - s->multipoles_sub = cells[j]->grav.multipole->next; + cells[j]->grav.multipole = s->multipoles_sub[tpid]; + s->multipoles_sub[tpid] = cells[j]->grav.multipole->next; } } /* Unlock the space. */ - lock_unlock_blind(&s->lock); + atomic_add(&s->tot_cells, nr_cells); /* Init some things in the cell we just got. */ for (int j = 0; j < nr_cells; j++) { @@ -494,6 +501,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { bzero(cells[j], sizeof(struct cell)); cells[j]->grav.multipole = temp; cells[j]->nodeID = -1; + cells[j]->tpid = tpid; if (lock_init(&cells[j]->hydro.lock) != 0 || lock_init(&cells[j]->grav.plock) != 0 || lock_init(&cells[j]->grav.mlock) != 0 || @@ -513,10 +521,12 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { * @param s The #space. */ void space_free_buff_sort_indices(struct space *s) { - for (struct cell *finger = s->cells_sub; finger != NULL; - finger = finger->next) { - cell_free_hydro_sorts(finger); - cell_free_stars_sorts(finger); + for (short int tpid = 0; tpid < s->e->nr_pool_threads; ++tpid) { + for (struct cell *finger = s->cells_sub[tpid]; finger != NULL; + finger = finger->next) { + cell_free_hydro_sorts(finger); + cell_free_stars_sorts(finger); + } } } @@ -747,149 +757,85 @@ void space_synchronize_particle_positions(struct space *s) { clocks_getunit()); } -void space_convert_rt_star_quantities_mapper(void *restrict map_data, - int scount, - void *restrict extra_data) { - - struct spart *restrict sparts = (struct spart *)map_data; - const struct engine *restrict e = (struct engine *)extra_data; - const int with_cosmology = (e->policy & engine_policy_cosmology); - - for (int k = 0; k < scount; k++) { - - struct spart *restrict sp = &sparts[k]; - rt_reset_spart(sp); - - /* If we're running with star controlled injection, we don't - * need to compute the stellar emission rates here now. */ - if (!e->rt_props->hydro_controlled_injection) continue; - - /* get star's age and time step for stellar emission rates */ - const integertime_t ti_begin = - get_integer_time_begin(e->ti_current - 1, sp->time_bin); - const integertime_t ti_step = get_integer_timestep(sp->time_bin); - - /* Get particle time-step */ - double dt_star; - if (with_cosmology) { - dt_star = - cosmology_get_delta_time(e->cosmology, ti_begin, ti_begin + ti_step); - } else { - dt_star = get_timestep(sp->time_bin, e->time_base); - } - - /* Calculate age of the star at current time */ - const double star_age_end_of_step = - stars_compute_age(sp, e->cosmology, e->time, with_cosmology); - - rt_compute_stellar_emission_rate(sp, e->time, star_age_end_of_step, dt_star, - e->rt_props, e->physical_constants, - e->internal_units); - } -} - -void space_convert_rt_hydro_quantities_mapper(void *restrict map_data, - int count, - void *restrict extra_data) { - +void space_convert_quantities_mapper(void *restrict map_data, int count, + void *restrict extra_data) { + struct space *s = (struct space *)extra_data; + const struct cosmology *cosmo = s->e->cosmology; + const struct hydro_props *hydro_props = s->e->hydro_properties; struct part *restrict parts = (struct part *)map_data; - const struct engine *restrict e = (struct engine *)extra_data; - const struct rt_props *restrict rt_props = e->rt_props; - const struct phys_const *restrict phys_const = e->physical_constants; - const struct unit_system *restrict iu = e->internal_units; - const struct cosmology *restrict cosmo = e->cosmology; + const ptrdiff_t index = parts - s->parts; + struct xpart *restrict xparts = s->xparts + index; + /* Loop over all the particles ignoring the extra buffer ones for on-the-fly + * creation */ for (int k = 0; k < count; k++) { - struct part *restrict p = &parts[k]; - rt_reset_part(p); - rt_init_part_after_zeroth_step(p, rt_props, phys_const, iu, cosmo); + if (parts[k].time_bin <= num_time_bins) { + hydro_convert_quantities(&parts[k], &xparts[k], cosmo, hydro_props); + mhd_convert_quantities(&parts[k], &xparts[k], cosmo, hydro_props); + } } } /** - * @brief Initializes values of radiative transfer data for particles - * that needs to be set before the first actual step is done, but will - * be reset in forthcoming steps when the corresponding particle is - * active. - * In hydro controlled injection, in particular we need the stellar - * emisison rates to be set from the start, not only after the stellar - * particle has been active. This function requires that the time bins - * for star particles have been set already and is called after the - * zeroth time step. - * In either star controlled injection or hydro controlled injection, - * for the debug RT scheme some data fields need to be reset after the - * zeroth step. In particular the interaction count between stars and - * hydro particles needs to be reset to zero; Otherwise only the active - * particles/stars will be reset when called in their respective ghosts, - * while the others remain nonzero, such that the respective sums over - * all hydro particles and the sum over all star particles won't be - * equal any longer. - * TODO MLADEN: Clean this up once you finish with the hydro/star - * controlled injection and debugging mode. + * @brief Calls the #part quantities conversion function on all particles in the + * space. * * @param s The #space. * @param verbose Are we talkative? */ -void space_convert_rt_quantities_after_zeroth_step(struct space *s, - int verbose) { +void space_convert_quantities(struct space *s, int verbose) { - const struct rt_props *rt_props = s->e->rt_props; const ticks tic = getticks(); - if (s->nr_parts > 0 && rt_props->convert_parts_after_zeroth_step) - /* Particle loop. Reset hydro particle values so we don't inject too much - * radiation into the gas, and other initialisations after zeroth step. */ - threadpool_map(&s->e->threadpool, space_convert_rt_hydro_quantities_mapper, - s->parts, s->nr_parts, sizeof(struct part), - threadpool_auto_chunk_size, /*extra_data=*/s->e); - - if (s->nr_sparts > 0 && rt_props->convert_stars_after_zeroth_step) - /* Star particle loop. Hydro controlled injection requires star particles - * to have their emission rates computed and ready for interactions. */ - threadpool_map(&s->e->threadpool, space_convert_rt_star_quantities_mapper, - s->sparts, s->nr_sparts, sizeof(struct spart), - threadpool_auto_chunk_size, /*extra_data=*/s->e); + if (s->nr_parts > 0) + threadpool_map(&s->e->threadpool, space_convert_quantities_mapper, s->parts, + s->nr_parts, sizeof(struct part), threadpool_auto_chunk_size, + s); if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); } -void space_convert_quantities_mapper(void *restrict map_data, int count, - void *restrict extra_data) { +void space_convert_rt_quantities_mapper(void *restrict map_data, int count, + void *restrict extra_data) { struct space *s = (struct space *)extra_data; - const struct cosmology *cosmo = s->e->cosmology; - const struct hydro_props *hydro_props = s->e->hydro_properties; + const struct engine *restrict e = s->e; + const int with_rt = (e->policy & engine_policy_rt); + if (!with_rt) return; + + const struct rt_props *restrict rt_props = e->rt_props; + const struct hydro_props *restrict hydro_props = e->hydro_properties; + const struct phys_const *restrict phys_const = e->physical_constants; + const struct unit_system *restrict iu = e->internal_units; + const struct cosmology *restrict cosmo = e->cosmology; + struct part *restrict parts = (struct part *)map_data; - const ptrdiff_t index = parts - s->parts; - struct xpart *restrict xparts = s->xparts + index; - const int with_rt = (s->e->policy & engine_policy_rt); - const struct rt_props *rt_props = s->e->rt_props; /* Loop over all the particles ignoring the extra buffer ones for on-the-fly * creation */ for (int k = 0; k < count; k++) { if (parts[k].time_bin <= num_time_bins) - hydro_convert_quantities(&parts[k], &xparts[k], cosmo, hydro_props); - if (with_rt) rt_convert_quantities(&parts[k], rt_props); + rt_convert_quantities(&parts[k], rt_props, hydro_props, phys_const, iu, + cosmo); } } /** - * @brief Calls the #part quantities conversion function on all particles in the - * space. + * @brief Calls the #part RT quantities conversion function on all particles in + * the space. * * @param s The #space. * @param verbose Are we talkative? */ -void space_convert_quantities(struct space *s, int verbose) { +void space_convert_rt_quantities(struct space *s, int verbose) { const ticks tic = getticks(); if (s->nr_parts > 0) - threadpool_map(&s->e->threadpool, space_convert_quantities_mapper, s->parts, - s->nr_parts, sizeof(struct part), threadpool_auto_chunk_size, - s); + threadpool_map(&s->e->threadpool, space_convert_rt_quantities_mapper, + s->parts, s->nr_parts, sizeof(struct part), + threadpool_auto_chunk_size, s); if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -1032,6 +978,8 @@ void space_collect_mean_masses(struct space *s, int verbose) { * * Note: the Intel compiler vectorizes this loop and creates FPEs from * the masked bit of the vector... Silly ICC... */ + /* TK comment: the following also has problems with gnu_7.3.0 and optimization + */ #if defined(__ICC) #pragma novector #endif @@ -1085,8 +1033,8 @@ void space_init(struct space *s, struct swift_params *params, size_t Nspart, size_t Nbpart, size_t Nnupart, int periodic, int replicate, int remap_ids, int generate_gas_in_ics, int hydro, int self_gravity, int star_formation, int with_sink, - int DM_background, int neutrinos, int verbose, int dry_run, - int nr_nodes) { + int with_DM, int with_DM_background, int neutrinos, int verbose, + int dry_run, int nr_nodes) { /* Clean-up everything */ bzero(s, sizeof(struct space)); @@ -1100,7 +1048,8 @@ void space_init(struct space *s, struct swift_params *params, s->with_hydro = hydro; s->with_star_formation = star_formation; s->with_sink = with_sink; - s->with_DM_background = DM_background; + s->with_DM = with_DM; + s->with_DM_background = with_DM_background; s->with_neutrinos = neutrinos; s->nr_parts = Npart; s->nr_gparts = Ngpart; @@ -1161,7 +1110,7 @@ void space_init(struct space *s, struct swift_params *params, /* Are we generating gas from the DM-only ICs? */ if (generate_gas_in_ics) { - space_generate_gas(s, cosmo, hydro_properties, periodic, DM_background, + space_generate_gas(s, cosmo, hydro_properties, periodic, with_DM_background, neutrinos, dim, verbose); parts = s->parts; gparts = s->gparts; @@ -1180,10 +1129,8 @@ 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) + if (with_DM_background) error("Can't replicate the space if background DM particles are in use."); - if (neutrinos) - error("Can't replicate the space if neutrino DM particles are in use."); space_replicate(s, replicate, verbose); parts = s->parts; @@ -1267,6 +1214,14 @@ void space_init(struct space *s, struct swift_params *params, parser_get_opt_param_int(params, "Scheduler:engine_max_parts_per_cooling", engine_max_parts_per_cooling_default); + engine_redistribute_alloc_margin = parser_get_opt_param_double( + params, "Scheduler:engine_redist_alloc_margin", + engine_redistribute_alloc_margin_default); + + engine_foreign_alloc_margin = parser_get_opt_param_double( + params, "Scheduler:engine_foreign_alloc_margin", + engine_foreign_alloc_margin_default); + if (verbose) { message("max_size set to %d split_size set to %d", space_maxsize, space_splitsize); @@ -1484,6 +1439,7 @@ void space_replicate(struct space *s, int replicate, int verbose) { const size_t nr_sparts = s->nr_sparts; const size_t nr_bparts = s->nr_bparts; const size_t nr_sinks = s->nr_sinks; + const size_t nr_nuparts = s->nr_nuparts; const size_t nr_dm = nr_gparts - nr_parts - nr_sparts - nr_bparts; s->size_parts = s->nr_parts = nr_parts * factor; @@ -1491,6 +1447,7 @@ void space_replicate(struct space *s, int replicate, int verbose) { s->size_sparts = s->nr_sparts = nr_sparts * factor; s->size_bparts = s->nr_bparts = nr_bparts * factor; s->size_sinks = s->nr_sinks = nr_sinks * factor; + s->nr_nuparts = nr_nuparts * factor; /* Allocate space for new particles */ struct part *parts = NULL; @@ -1649,6 +1606,15 @@ void space_remap_ids(struct space *s, int nr_nodes, int verbose) { if (verbose) message("Remapping all the IDs"); + size_t local_nr_dm_background = 0; + size_t local_nr_nuparts = 0; + for (size_t i = 0; i < s->nr_gparts; ++i) { + if (s->gparts[i].type == swift_type_neutrino) + local_nr_nuparts++; + else if (s->gparts[i].type == swift_type_dark_matter_background) + local_nr_dm_background++; + } + /* Get the current local number of particles */ const size_t local_nr_parts = s->nr_parts; const size_t local_nr_sinks = s->nr_sinks; @@ -1657,8 +1623,10 @@ void space_remap_ids(struct space *s, int nr_nodes, int verbose) { const size_t local_nr_bparts = s->nr_bparts; const size_t local_nr_baryons = local_nr_parts + local_nr_sinks + local_nr_sparts + local_nr_bparts; - const size_t local_nr_dm = - local_nr_gparts > 0 ? local_nr_gparts - local_nr_baryons : 0; + const size_t local_nr_dm = local_nr_gparts > 0 + ? local_nr_gparts - local_nr_baryons - + local_nr_nuparts - local_nr_dm_background + : 0; /* Get the global offsets */ long long offset_parts = 0; @@ -1666,6 +1634,8 @@ void space_remap_ids(struct space *s, int nr_nodes, int verbose) { long long offset_sparts = 0; long long offset_bparts = 0; long long offset_dm = 0; + long long offset_dm_background = 0; + long long offset_nuparts = 0; #ifdef WITH_MPI MPI_Exscan(&local_nr_parts, &offset_parts, 1, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); @@ -1677,6 +1647,10 @@ void space_remap_ids(struct space *s, int nr_nodes, int verbose) { MPI_COMM_WORLD); MPI_Exscan(&local_nr_dm, &offset_dm, 1, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); + MPI_Exscan(&local_nr_dm_background, &offset_dm_background, 1, + MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); + MPI_Exscan(&local_nr_nuparts, &offset_nuparts, 1, MPI_LONG_LONG_INT, MPI_SUM, + MPI_COMM_WORLD); #endif /* Total number of particles of each kind */ @@ -1684,7 +1658,10 @@ void space_remap_ids(struct space *s, int nr_nodes, int verbose) { long long total_parts = offset_parts + local_nr_parts; long long total_sinks = offset_sinks + local_nr_sinks; long long total_sparts = offset_sparts + local_nr_sparts; - // long long total_bparts = offset_bparts + local_nr_bparts; + long long total_bparts = offset_bparts + local_nr_bparts; + long long total_nuparts = offset_nuparts + local_nr_nuparts; + // long long total_dm_backgroud = offset_dm_background + + // local_nr_dm_background; #ifdef WITH_MPI /* The last rank now has the correct total, let's broadcast this back */ @@ -1692,17 +1669,27 @@ void space_remap_ids(struct space *s, int nr_nodes, int verbose) { MPI_Bcast(&total_parts, 1, MPI_LONG_LONG_INT, nr_nodes - 1, MPI_COMM_WORLD); MPI_Bcast(&total_sinks, 1, MPI_LONG_LONG_INT, nr_nodes - 1, MPI_COMM_WORLD); MPI_Bcast(&total_sparts, 1, MPI_LONG_LONG_INT, nr_nodes - 1, MPI_COMM_WORLD); - // MPI_Bcast(&total_bparts, 1, MPI_LONG_LONG_INT, nr_nodes - 1, + MPI_Bcast(&total_bparts, 1, MPI_LONG_LONG_INT, nr_nodes - 1, MPI_COMM_WORLD); + MPI_Bcast(&total_nuparts, 1, MPI_LONG_LONG_INT, nr_nodes - 1, MPI_COMM_WORLD); + // MPI_Bcast(&total_dm_background, 1, MPI_LONG_LONG_INT, nr_nodes - 1, // MPI_COMM_WORLD); #endif /* Let's order the particles - * IDs will be DM then gas then sinks than stars then BHs */ + * IDs will be DM then gas then sinks than stars then BHs then nus then + * DM background. Note that we leave a large gap (10x the number of particles) + * in-between the regular particles and the background ones. This allow for + * particle splitting to keep a compact set of ids. */ offset_dm += 1; offset_parts += 1 + total_dm; offset_sinks += 1 + total_dm + total_parts; offset_sparts += 1 + total_dm + total_parts + total_sinks; offset_bparts += 1 + total_dm + total_parts + total_sinks + total_sparts; + offset_nuparts += + 1 + total_dm + total_parts + total_sinks + total_sparts + total_bparts; + offset_dm_background += + 1 + 10 * (total_dm * total_parts + total_sinks + total_sparts + + total_bparts + total_nuparts); /* We can now remap the IDs in the range [offset offset + local_nr] */ for (size_t i = 0; i < local_nr_parts; ++i) { @@ -1717,11 +1704,21 @@ void space_remap_ids(struct space *s, int nr_nodes, int verbose) { for (size_t i = 0; i < local_nr_bparts; ++i) { s->bparts[i].id = offset_bparts + i; } - for (size_t i = 0; i < local_nr_dm; ++i) { - if (s->gparts[i].type == swift_type_dark_matter || - s->gparts[i].type == swift_type_dark_matter_background || - s->gparts[i].type == swift_type_neutrino) - s->gparts[i].id_or_neg_offset = offset_dm + i; + size_t count_dm = 0; + size_t count_dm_background = 0; + size_t count_nu = 0; + for (size_t i = 0; i < s->nr_gparts; ++i) { + if (s->gparts[i].type == swift_type_dark_matter) { + s->gparts[i].id_or_neg_offset = offset_dm + count_dm; + count_dm++; + } else if (s->gparts[i].type == swift_type_neutrino) { + s->gparts[i].id_or_neg_offset = offset_nuparts + count_nu; + count_nu++; + } else if (s->gparts[i].type == swift_type_dark_matter_background) { + s->gparts[i].id_or_neg_offset = + offset_dm_background + count_dm_background; + count_dm_background++; + } } } @@ -1771,17 +1768,18 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo, const size_t current_nr_parts = s->nr_parts; const size_t current_nr_gparts = s->nr_gparts; + /* Basic checks for unwanted modes */ if (current_nr_parts != 0) - error("Generating gas particles from DM but gas already exists!"); + error("Generating gas particles from DM but gas already exist!"); if (s->nr_sparts != 0) - error("Generating gas particles from DM but stars already exists!"); + error("Generating gas particles from DM but stars already exist!"); if (s->nr_bparts != 0) - error("Generating gas particles from DM but BHs already exists!"); + error("Generating gas particles from DM but BHs already exist!"); if (s->nr_sinks != 0) - error("Generating gas particles from DM but sink already exists!"); + error("Generating gas particles from DM but sinks already exist!"); /* Pull out information about particle splitting */ const int particle_splitting = hydro_properties->particle_splitting; @@ -1839,12 +1837,19 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo, size_t j = 0; for (size_t i = 0; i < current_nr_gparts; ++i) { - /* For the background & neutrino DM particles, just copy the data */ - if (s->gparts[i].type == swift_type_dark_matter_background || - s->gparts[i].type == swift_type_neutrino) { + /* For the neutrino DM particles, just copy the data */ + if (s->gparts[i].type == swift_type_neutrino) { memcpy(&gparts[i], &s->gparts[i], sizeof(struct gpart)); + /* For the background DM particles, copy the data and give a better ID */ + } else if (s->gparts[i].type == swift_type_dark_matter_background) { + + memcpy(&gparts[i], &s->gparts[i], sizeof(struct gpart)); + + /* Multiply the ID by two to match the convention of even IDs for DM. */ + gparts[i].id_or_neg_offset *= 2; + } else { /* For the zoom DM particles, there is a lot of work to do */ @@ -2058,6 +2063,7 @@ void space_check_cosmology(struct space *s, const struct cosmology *cosmo, * @brief Compute the max id of any #part in this space. * * This function is inefficient. Don't call often. + * Background particles are ignored. * * @param s The #space. */ @@ -2070,9 +2076,10 @@ long long space_get_max_parts_id(struct space *s) { max_id = max(max_id, s->sparts[i].id); for (size_t i = 0; i < s->nr_bparts; ++i) max_id = max(max_id, s->bparts[i].id); + + /* Note: We Explicitly do *NOT* consider background particles */ for (size_t i = 0; i < s->nr_gparts; ++i) if (s->gparts[i].type == swift_type_dark_matter || - s->gparts[i].type == swift_type_dark_matter_background || s->gparts[i].type == swift_type_neutrino) max_id = max(max_id, s->gparts[i].id_or_neg_offset); return max_id; @@ -2328,6 +2335,23 @@ void space_reset_task_counters(struct space *s) { #endif } +/** + * @brief Call the post-snapshot tracer on all the particles. + * + * @param s The #space. + */ +void space_after_snap_tracer(struct space *s, int verbose) { + for (size_t i = 0; i < s->nr_parts; ++i) { + tracers_after_snapshot_part(&s->parts[i], &s->xparts[i]); + } + for (size_t i = 0; i < s->nr_sparts; ++i) { + tracers_after_snapshot_spart(&s->sparts[i]); + } + for (size_t i = 0; i < s->nr_bparts; ++i) { + tracers_after_snapshot_bpart(&s->bparts[i]); + } +} + /** * @brief Frees up the memory allocated for this #space */ @@ -2353,6 +2377,8 @@ void space_clean(struct space *s) { swift_free("gparts_foreign", s->gparts_foreign); swift_free("bparts_foreign", s->bparts_foreign); #endif + free(s->cells_sub); + free(s->multipoles_sub); if (lock_destroy(&s->unique_id.lock) != 0) error("Failed to destroy spinlocks."); @@ -2414,6 +2440,12 @@ void space_struct_dump(struct space *s, FILE *stream) { restart_write_blocks(&engine_star_resort_task_depth, sizeof(int), 1, stream, "engine_star_resort_task_depth", "engine_star_resort_task_depth"); + restart_write_blocks(&engine_redistribute_alloc_margin, sizeof(double), 1, + stream, "engine_redistribute_alloc_margin", + "engine_redistribute_alloc_margin"); + restart_write_blocks(&engine_foreign_alloc_margin, sizeof(double), 1, stream, + "engine_foreign_alloc_margin", + "engine_foreign_alloc_margin"); /* More things to write. */ if (s->nr_parts > 0) { @@ -2488,6 +2520,10 @@ void space_struct_restore(struct space *s, FILE *stream) { NULL, "engine_max_parts_per_cooling"); restart_read_blocks(&engine_star_resort_task_depth, sizeof(int), 1, stream, NULL, "engine_star_resort_task_depth"); + restart_read_blocks(&engine_redistribute_alloc_margin, sizeof(double), 1, + stream, NULL, "engine_redistribute_alloc_margin"); + restart_read_blocks(&engine_foreign_alloc_margin, sizeof(double), 1, stream, + NULL, "engine_foreign_alloc_margin"); /* Things that should be reconstructed in a rebuild. */ s->cells_top = NULL; diff --git a/src/space.h b/src/space.h index 9d7cc10236d604b389ba71710c1deb23853ed877..371c3427d5903fb33318e28bf7a2250751329351 100644 --- a/src/space.h +++ b/src/space.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -24,7 +24,7 @@ #define SWIFT_SPACE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stddef.h> @@ -84,6 +84,8 @@ extern int space_extra_gparts; extern int space_extra_sparts; extern int space_extra_bparts; extern int space_extra_sinks; +extern double engine_redistribute_alloc_margin; +extern double engine_foreign_alloc_margin; /** * @brief The space in which the cells and particles reside. @@ -111,6 +113,9 @@ struct space { /*! Are we doing star formation through sink particles? */ int with_sink; + /*! Are we running with some regular DM particles? */ + int with_DM; + /*! Are we running with some DM background particles? */ int with_DM_background; @@ -153,14 +158,14 @@ struct space { /*! The (level 0) cells themselves. */ struct cell *cells_top; - /*! Buffer of unused cells for the sub-cells. */ - struct cell *cells_sub; + /*! Buffer of unused cells for the sub-cells. One chunk per thread. */ + struct cell **cells_sub; /*! The multipoles associated with the top-level (level 0) cells */ struct gravity_tensors *multipoles_top; - /*! Buffer of unused multipoles for the sub-cells. */ - struct gravity_tensors *multipoles_sub; + /*! Buffer of unused multipoles for the sub-cells. One chunk per thread. */ + struct gravity_tensors **multipoles_sub; /*! The indices of the *local* top-level cells */ int *local_cells_top; @@ -285,6 +290,15 @@ struct space { /*! Sum of the norm of the velocity of all the #bpart */ float sum_bpart_vel_norm; + /*! Minimal gravity acceleration accross all particles */ + float min_a_grav; + + /*! Max gravity softening accross all particles */ + float max_softening; + + /*! Max multipole power accross all top-level cells */ + float max_mpole_power[SELF_GRAVITY_MULTIPOLE_ORDER + 1]; + /* Initial mean mass of each particle type in the system. */ double initial_mean_mass_particles[swift_type_count]; @@ -347,7 +361,8 @@ void space_bparts_sort(struct bpart *bparts, int *ind, int *counts, int num_bins, ptrdiff_t bparts_offset); void space_sinks_sort(struct sink *sinks, int *ind, int *counts, int num_bins, ptrdiff_t sinks_offset); -void space_getcells(struct space *s, int nr_cells, struct cell **cells); +void space_getcells(struct space *s, int nr_cells, struct cell **cells, + const short int tid); void space_init(struct space *s, struct swift_params *params, const struct cosmology *cosmo, double dim[3], const struct hydro_props *hydro_properties, struct part *parts, @@ -356,8 +371,8 @@ void space_init(struct space *s, struct swift_params *params, size_t Nspart, size_t Nbpart, size_t Nnupart, int periodic, int replicate, int remap_ids, int generate_gas_in_ics, int hydro, int gravity, int star_formation, int with_sink, - int DM_background, int neutrinos, int verbose, int dry_run, - int nr_nodes); + int with_DM, int with_DM_background, int neutrinos, int verbose, + int dry_run, int nr_nodes); 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); @@ -407,9 +422,9 @@ void space_init_gparts(struct space *s, int verbose); void space_init_sparts(struct space *s, int verbose); void space_init_bparts(struct space *s, int verbose); void space_init_sinks(struct space *s, int verbose); -void space_convert_rt_quantities_after_zeroth_step(struct space *s, - int verbose); +void space_after_snap_tracer(struct space *s, int verbose); void space_convert_quantities(struct space *s, int verbose); +void space_convert_rt_quantities(struct space *s, int verbose); void space_link_cleanup(struct space *s); void space_check_drift_point(struct space *s, integertime_t ti_drift, int multipole); diff --git a/src/space_cell_index.c b/src/space_cell_index.c index d83cb2db780747f746fddd83dbff4274d167d271..7f4f617af2b620f67f9c526d4d77e155ed4d2084 100644 --- a/src/space_cell_index.c +++ b/src/space_cell_index.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" diff --git a/src/space_extras.c b/src/space_extras.c index 49de646258db337bdad4c80e91c3f1f362dece05..9d8a88952e5a3d2a670ba63f96001dc27eda26c7 100644 --- a/src/space_extras.c +++ b/src/space_extras.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" @@ -184,7 +184,9 @@ void space_allocate_extras(struct space *s, int verbose) { size_t local_cell_id = 0; int current_cell = local_cells[local_cell_id]; int count_in_cell = 0; +#ifdef SWIFT_DEBUG_CHECKS size_t count_extra_gparts = 0; +#endif for (size_t i = 0; i < nr_actual_gparts + expected_num_extra_gparts; ++i) { #ifdef SWIFT_DEBUG_CHECKS @@ -199,7 +201,9 @@ void space_allocate_extras(struct space *s, int verbose) { s->gparts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; s->gparts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; ++count_in_cell; +#ifdef SWIFT_DEBUG_CHECKS count_extra_gparts++; +#endif } /* Once we have reached the number of extra gpart per cell, we move to the @@ -276,7 +280,9 @@ void space_allocate_extras(struct space *s, int verbose) { size_t local_cell_id = 0; int current_cell = local_cells[local_cell_id]; int count_in_cell = 0; +#ifdef SWIFT_DEBUG_CHECKS size_t count_extra_parts = 0; +#endif for (size_t i = 0; i < nr_actual_parts + expected_num_extra_parts; ++i) { #ifdef SWIFT_DEBUG_CHECKS @@ -291,7 +297,9 @@ void space_allocate_extras(struct space *s, int verbose) { s->parts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; s->parts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; ++count_in_cell; +#ifdef SWIFT_DEBUG_CHECKS count_extra_parts++; +#endif } /* Once we have reached the number of extra part per cell, we move to the @@ -357,7 +365,9 @@ void space_allocate_extras(struct space *s, int verbose) { size_t local_cell_id = 0; int current_cell = local_cells[local_cell_id]; int count_in_cell = 0; +#ifdef SWIFT_DEBUG_CHECKS size_t count_extra_sinks = 0; +#endif for (size_t i = 0; i < nr_actual_sinks + expected_num_extra_sinks; ++i) { #ifdef SWIFT_DEBUG_CHECKS @@ -372,7 +382,9 @@ void space_allocate_extras(struct space *s, int verbose) { s->sinks[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; s->sinks[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; ++count_in_cell; +#ifdef SWIFT_DEBUG_CHECKS count_extra_sinks++; +#endif } /* Once we have reached the number of extra sink per cell, we move to the @@ -439,7 +451,9 @@ void space_allocate_extras(struct space *s, int verbose) { size_t local_cell_id = 0; int current_cell = local_cells[local_cell_id]; int count_in_cell = 0; +#ifdef SWIFT_DEBUG_CHECKS size_t count_extra_sparts = 0; +#endif for (size_t i = 0; i < nr_actual_sparts + expected_num_extra_sparts; ++i) { #ifdef SWIFT_DEBUG_CHECKS @@ -454,7 +468,9 @@ void space_allocate_extras(struct space *s, int verbose) { s->sparts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; s->sparts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; ++count_in_cell; +#ifdef SWIFT_DEBUG_CHECKS count_extra_sparts++; +#endif } /* Once we have reached the number of extra spart per cell, we move to the @@ -521,7 +537,9 @@ void space_allocate_extras(struct space *s, int verbose) { size_t local_cell_id = 0; int current_cell = local_cells[local_cell_id]; int count_in_cell = 0; +#ifdef SWIFT_DEBUG_CHECKS size_t count_extra_bparts = 0; +#endif for (size_t i = 0; i < nr_actual_bparts + expected_num_extra_bparts; ++i) { #ifdef SWIFT_DEBUG_CHECKS @@ -536,7 +554,9 @@ void space_allocate_extras(struct space *s, int verbose) { s->bparts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; s->bparts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; ++count_in_cell; +#ifdef SWIFT_DEBUG_CHECKS count_extra_bparts++; +#endif } /* Once we have reached the number of extra bpart per cell, we move to the diff --git a/src/space_first_init.c b/src/space_first_init.c index 27943be6ea82e3b7102a5dfb17044ab42ceb6d59..71dbd26b50d9e9c2e6a19603e6d19ea4f22d82b5 100644 --- a/src/space_first_init.c +++ b/src/space_first_init.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" @@ -29,7 +29,9 @@ #include "black_holes.h" #include "chemistry.h" #include "engine.h" +#include "feedback.h" #include "gravity.h" +#include "mhd.h" #include "neutrino.h" #include "particle_splitting.h" #include "pressure_floor.h" @@ -66,6 +68,7 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, 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; + const struct rt_props *rt_props = e->rt_props; /* Check that the smoothing lengths are non-zero */ for (int k = 0; k < count; k++) { @@ -108,6 +111,7 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, for (int k = 0; k < count; k++) { hydro_first_init_part(&p[k], &xp[k]); + mhd_first_init_part(&p[k], &xp[k], &hydro_props->mhd, s->dim[0]); p[k].limiter_data.min_ngb_time_bin = num_time_bins + 1; p[k].limiter_data.wakeup = time_bin_not_awake; p[k].limiter_data.to_be_synchronized = 0; @@ -137,11 +141,14 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, /* And the black hole markers */ black_holes_mark_part_as_not_swallowed(&p[k].black_holes_data); + /* And the sink markers */ + sink_mark_part_as_not_swallowed(&p[k].sink_data); + /* Also initialise the splitting data */ particle_splitting_mark_part_as_not_split(&xp[k].split_data, p[k].id); /* And the radiative transfer */ - rt_first_init_part(&p[k]); + rt_first_init_part(&p[k], cosmo, rt_props); #ifdef SWIFT_DEBUG_CHECKS /* Check part->gpart->part linkeage. */ @@ -261,6 +268,9 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count, const struct cosmology *cosmo = e->cosmology; const struct stars_props *stars_properties = e->stars_properties; + const struct feedback_props *feedback_properties = e->feedback_props; + const struct phys_const *phys_const = s->e->physical_constants; + const struct unit_system *us = s->e->internal_units; const float a_factor_vel = cosmo->a; /* Convert velocities to internal units */ @@ -303,9 +313,15 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count, csds_part_data_init(&sp[k].csds_data); #endif + /* And the tracers */ + tracers_first_init_spart(&sp[k], us, phys_const, cosmo); + /* Also initialise the chemistry */ chemistry_first_init_spart(chemistry, &sp[k]); + /* Also initialise the feedback */ + feedback_first_init_spart(&sp[k], feedback_properties); + /* Also initialise the splitting data */ particle_splitting_mark_part_as_not_split(&sp[k].split_data, sp[k].id); @@ -355,6 +371,8 @@ void space_first_init_bparts_mapper(void *restrict map_data, int count, const float initial_h = s->initial_bpart_h; const struct cosmology *cosmo = e->cosmology; + const struct phys_const *phys_const = s->e->physical_constants; + const struct unit_system *us = s->e->internal_units; const float a_factor_vel = cosmo->a; /* Convert velocities to internal units */ @@ -392,6 +410,9 @@ void space_first_init_bparts_mapper(void *restrict map_data, int count, black_holes_first_init_bpart(&bp[k], props); + /* And the tracers */ + tracers_first_init_bpart(&bp[k], us, phys_const, cosmo); + /* And the splitting data */ particle_splitting_mark_part_as_not_split(&bp[k].split_data, bp[k].id); diff --git a/src/space_getsid.h b/src/space_getsid.h index 752f34834e12fe3c8ff0ac0cd9cec5c8a07421bc..2124e2e1f027c54248d084c0c35da8a19d9efe66 100644 --- a/src/space_getsid.h +++ b/src/space_getsid.h @@ -20,7 +20,7 @@ #define SWIFT_SPACE_GETSID_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stddef.h> diff --git a/src/space_init.c b/src/space_init.c index 5a6fa674f2d3fd46da6d0c90e58572264a0438a5..c59ef1d2406deafa1284e8d090ad659e26a3ed80 100644 --- a/src/space_init.c +++ b/src/space_init.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" @@ -30,6 +30,7 @@ #include "chemistry.h" #include "engine.h" #include "gravity.h" +#include "mhd.h" #include "pressure_floor.h" #include "rt.h" #include "sink.h" @@ -51,15 +52,17 @@ void space_init_parts_mapper(void *restrict map_data, int count, for (int k = 0; k < count; k++) { hydro_init_part(&parts[k], hs); + mhd_init_part(&parts[k]); black_holes_init_potential(&parts[k].black_holes_data); chemistry_init_part(&parts[k], e->chemistry); pressure_floor_init_part(&parts[k], &xparts[k]); rt_init_part(&parts[k]); - rt_reset_part(&parts[k]); + rt_reset_part(&parts[k], e->cosmology); star_formation_init_part(&parts[k], e->star_formation); 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); + sink_init_part(&parts[k]); } } diff --git a/src/space_rebuild.c b/src/space_rebuild.c index 6bd5aef57d4d6923198f77615b22b3d5b65f7397..36d784c8b34c393d4260fbd54ae5fa02b5c4c771 100644 --- a/src/space_rebuild.c +++ b/src/space_rebuild.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" @@ -902,6 +902,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { s->nr_cells_with_particles = 0; s->nr_local_cells_with_particles = 0; s->nr_local_cells = 0; + for (int k = 0; k < s->nr_cells; k++) { struct cell *restrict c = &cells_top[k]; c->hydro.ti_old_part = ti_current; @@ -957,6 +958,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { s->nr_local_cells_with_particles++; } } + if (verbose) { message("Have %d local top-level cells with particles (total=%d)", s->nr_local_cells_with_particles, s->nr_cells); diff --git a/src/space_recycle.c b/src/space_recycle.c index 9a46a55a14e8e4002d297a9dd736d119beff5d9c..657d3e23bb1743e8bacc79031bc175174fbf4eb1 100644 --- a/src/space_recycle.c +++ b/src/space_recycle.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" @@ -127,9 +127,9 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->hydro.ghost_out = NULL; c->hydro.ghost = NULL; c->hydro.prep1_ghost = NULL; - c->hydro.sink_formation = NULL; c->hydro.star_formation = NULL; - c->hydro.star_formation_sink = NULL; + c->sinks.sink_formation = NULL; + c->sinks.star_formation_sink = NULL; c->hydro.stars_resort = NULL; c->stars.density_ghost = NULL; c->stars.prep1_ghost = NULL; @@ -138,9 +138,9 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->stars.feedback = NULL; c->stars.prepare1 = NULL; c->stars.prepare2 = NULL; - c->sinks.compute_formation = NULL; - c->sinks.merger = NULL; - c->sinks.accretion = NULL; + c->sinks.swallow = NULL; + c->sinks.do_sink_swallow = NULL; + c->sinks.do_gas_swallow = NULL; c->black_holes.density_ghost = NULL; c->black_holes.swallow_ghost_0 = NULL; c->black_holes.swallow_ghost_1 = NULL; @@ -169,7 +169,8 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->black_holes.black_holes_in = NULL; c->black_holes.black_holes_out = NULL; c->sinks.sink_in = NULL; - c->sinks.ghost = NULL; + c->sinks.sink_ghost1 = NULL; + c->sinks.sink_ghost2 = NULL; c->sinks.sink_out = NULL; c->grav.drift = NULL; c->grav.drift_out = NULL; @@ -199,15 +200,24 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->sinks.ti_end_min = -1; c->stars.ti_end_min = -1; c->black_holes.ti_end_min = -1; - c->hydro.rt_in = NULL; - c->hydro.rt_inject = NULL; - c->hydro.rt_ghost1 = NULL; - c->hydro.rt_gradient = NULL; - c->hydro.rt_ghost2 = NULL; - c->hydro.rt_transport = NULL; - c->hydro.rt_transport_out = NULL; - c->hydro.rt_tchem = NULL; - c->hydro.rt_out = NULL; + c->rt.rt_in = NULL; + c->rt.rt_ghost1 = NULL; + c->rt.rt_gradient = NULL; + c->rt.rt_ghost2 = NULL; + c->rt.rt_transport = NULL; + c->rt.rt_transport_out = NULL; + c->rt.rt_tchem = NULL; + c->rt.rt_advance_cell_time = NULL; + c->rt.rt_sorts = NULL; + c->rt.rt_out = NULL; + c->rt.rt_collect_times = NULL; + c->rt.ti_rt_end_min = -1; + c->rt.ti_rt_min_step_size = -1; + c->rt.updated = 0; +#ifdef SWIFT_RT_DEBUG_CHECKS + c->rt.advanced_time = 0; +#endif + star_formation_logger_init(&c->stars.sfh); #if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) c->cellID = 0; @@ -243,22 +253,16 @@ void space_recycle(struct space *s, struct cell *c) { lock_destroy(&c->stars.star_formation_lock) != 0) error("Failed to destroy spinlocks."); - /* Lock the space. */ - lock_lock(&s->lock); - /* Hook the multipole back in the buffer */ if (s->with_self_gravity) { - c->grav.multipole->next = s->multipoles_sub; - s->multipoles_sub = c->grav.multipole; + c->grav.multipole->next = s->multipoles_sub[c->tpid]; + s->multipoles_sub[c->tpid] = c->grav.multipole; } /* Hook this cell into the buffer. */ - c->next = s->cells_sub; - s->cells_sub = c; - s->tot_cells -= 1; - - /* Unlock the space. */ - lock_unlock_blind(&s->lock); + c->next = s->cells_sub[c->tpid]; + s->cells_sub[c->tpid] = c; + atomic_dec(&s->tot_cells); } /** @@ -306,15 +310,17 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin, /* Lock the space. */ lock_lock(&s->lock); - /* Hook the cells into the buffer. */ - cell_list_end->next = s->cells_sub; - s->cells_sub = cell_list_begin; - s->tot_cells -= count; + /* Hook the cells into the buffer keeping tpid if we can. */ + short int tpid = cell_list_begin->tpid; + if (tpid < 0) tpid = 0; + cell_list_end->next = s->cells_sub[tpid]; + s->cells_sub[tpid] = cell_list_begin; + atomic_sub(&s->tot_cells, count); /* Hook the multipoles into the buffer. */ if (s->with_self_gravity) { - multipole_list_end->next = s->multipoles_sub; - s->multipoles_sub = multipole_list_begin; + multipole_list_end->next = s->multipoles_sub[tpid]; + s->multipoles_sub[tpid] = multipole_list_begin; } /* Unlock the space. */ diff --git a/src/space_regrid.c b/src/space_regrid.c index 9c86af7108cf630cfcf395bb7e3f52218d8f8fee..95fa4d9cd96ea5ab1832ba2e01d6295a8320a45b 100644 --- a/src/space_regrid.c +++ b/src/space_regrid.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" @@ -126,6 +126,16 @@ void space_regrid(struct space *s, int verbose) { (int)floor(s->dim[2] / fmax(h_max * kernel_gamma * space_stretch, s->cell_min))}; + /* check that we have at least 1 cell in each dimension */ + if (cdim[0] == 0 || cdim[1] == 0 || cdim[2] == 0) { + error( + "Top level cell dimension of size 0 detected (cdim = [%i %i " + "%i])!\nThis usually indicates a problem with the initial smoothing " + "lengths of the particles, e.g. a smoothing length that is comparable " + "in size to the box size.", + cdim[0], cdim[1], cdim[2]); + } + /* Check if we have enough cells for periodicity. */ if (s->periodic && (cdim[0] < 3 || cdim[1] < 3 || cdim[2] < 3)) error( diff --git a/src/space_sort.c b/src/space_sort.c index 562f706182f306e48127f94cf134f4e0389ea5f1..75a09ad78f06a81beaf507df470d6426b660368c 100644 --- a/src/space_sort.c +++ b/src/space_sort.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "error.h" diff --git a/src/space_split.c b/src/space_split.c index aefb7bebec3d74c086119fcca27c9d43a3fa68d2..526ac16d12093f10fc936a8a97428c10dac3891c 100644 --- a/src/space_split.c +++ b/src/space_split.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "space.h" @@ -55,7 +55,8 @@ void space_split_recursive(struct space *s, struct cell *c, struct cell_buff *restrict sbuff, struct cell_buff *restrict bbuff, struct cell_buff *restrict gbuff, - struct cell_buff *restrict sink_buff) { + struct cell_buff *restrict sink_buff, + const short int tpid) { const int count = c->hydro.count; const int gcount = c->grav.count; @@ -75,6 +76,8 @@ void space_split_recursive(struct space *s, struct cell *c, float sinks_h_max_active = 0.f; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, ti_hydro_beg_max = 0; + integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0; + integertime_t ti_rt_min_step_size = max_nr_timesteps; 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, @@ -92,6 +95,10 @@ void space_split_recursive(struct space *s, struct cell *c, struct engine *e = s->e; const integertime_t ti_current = e->ti_current; + /* Set the top level cell tpid. Doing it here ensures top level cells + * have the same tpid as their progeny. */ + if (depth == 0) c->tpid = tpid; + /* If the buff is NULL, allocate it, and remember to free it. */ const int allocate_buffer = (buff == NULL && gbuff == NULL && sbuff == NULL && bbuff == NULL && sink_buff == NULL); @@ -197,7 +204,7 @@ void space_split_recursive(struct space *s, struct cell *c, c->split = 1; /* Create the cell's progeny. */ - space_getcells(s, 8, c->progeny); + space_getcells(s, 8, c->progeny, tpid); for (int k = 0; k < 8; k++) { struct cell *cp = c->progeny[k]; cp->hydro.count = 0; @@ -284,7 +291,7 @@ void space_split_recursive(struct space *s, struct cell *c, /* Recurse */ space_split_recursive(s, cp, progeny_buff, progeny_sbuff, progeny_bbuff, - progeny_gbuff, progeny_sink_buff); + progeny_gbuff, progeny_sink_buff, tpid); /* Update the pointers in the buffers */ progeny_buff += cp->hydro.count; @@ -307,6 +314,10 @@ void space_split_recursive(struct space *s, struct cell *c, ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min); ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max); + ti_rt_end_min = min(ti_rt_end_min, cp->rt.ti_rt_end_min); + ti_rt_beg_max = max(ti_rt_beg_max, cp->rt.ti_rt_beg_max); + ti_rt_min_step_size = + min(ti_rt_min_step_size, cp->rt.ti_rt_min_step_size); ti_gravity_end_min = min(ti_gravity_end_min, cp->grav.ti_end_min); ti_gravity_beg_max = max(ti_gravity_beg_max, cp->grav.ti_beg_max); ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); @@ -468,13 +479,23 @@ void space_split_recursive(struct space *s, struct cell *c, /* When does this particle's time-step start and end? */ const timebin_t time_bin = parts[k].time_bin; + const timebin_t time_bin_rt = parts[k].rt_time_data.time_bin; const integertime_t ti_end = get_integer_time_end(ti_current, time_bin); const integertime_t ti_beg = get_integer_time_begin(ti_current, time_bin); + const integertime_t ti_rt_end = + get_integer_time_end(ti_current, time_bin_rt); + const integertime_t ti_rt_beg = + get_integer_time_begin(ti_current, time_bin_rt); + const integertime_t ti_rt_step = get_integer_timestep(time_bin_rt); ti_hydro_end_min = min(ti_hydro_end_min, ti_end); ti_hydro_end_max = max(ti_hydro_end_max, ti_end); ti_hydro_beg_max = max(ti_hydro_beg_max, ti_beg); + ti_rt_end_min = min(ti_rt_end_min, ti_rt_end); + ti_rt_beg_max = max(ti_rt_beg_max, ti_rt_beg); + ti_rt_min_step_size = min(ti_rt_min_step_size, ti_rt_step); + h_max = max(h_max, parts[k].h); if (part_is_active(&parts[k], e)) @@ -635,6 +656,9 @@ void space_split_recursive(struct space *s, struct cell *c, c->hydro.h_max_active = h_max_active; c->hydro.ti_end_min = ti_hydro_end_min; c->hydro.ti_beg_max = ti_hydro_beg_max; + c->rt.ti_rt_end_min = ti_rt_end_min; + c->rt.ti_rt_beg_max = ti_rt_beg_max; + c->rt.ti_rt_min_step_size = ti_rt_min_step_size; c->grav.ti_end_min = ti_gravity_end_min; c->grav.ti_beg_max = ti_gravity_beg_max; c->stars.ti_end_min = ti_stars_end_min; @@ -651,24 +675,8 @@ void space_split_recursive(struct space *s, struct cell *c, c->black_holes.h_max_active = black_holes_h_max_active; c->maxdepth = maxdepth; - /* Set ownership according to the start of the parts array. */ - if (s->nr_parts > 0) - c->owner = ((c->hydro.parts - s->parts) % s->nr_parts) * s->nr_queues / - s->nr_parts; - else if (s->nr_sinks > 0) - c->owner = ((c->sinks.parts - s->sinks) % s->nr_sinks) * s->nr_queues / - s->nr_sinks; - else if (s->nr_sparts > 0) - c->owner = ((c->stars.parts - s->sparts) % s->nr_sparts) * s->nr_queues / - s->nr_sparts; - else if (s->nr_bparts > 0) - c->owner = ((c->black_holes.parts - s->bparts) % s->nr_bparts) * - s->nr_queues / s->nr_bparts; - else if (s->nr_gparts > 0) - c->owner = ((c->grav.parts - s->gparts) % s->nr_gparts) * s->nr_queues / - s->nr_gparts; - else - c->owner = 0; /* Ok, there is really nothing on this rank... */ + /* No runner owns this cell yet. We assign those during scheduling. */ + c->owner = -1; /* Store the global max depth */ if (c->depth == 0) atomic_max(&s->maxdepth, maxdepth); @@ -698,10 +706,29 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) { struct cell *cells_top = s->cells_top; int *local_cells_with_particles = (int *)map_data; + /* Collect some global information about the top-level m-poles */ + float min_a_grav = FLT_MAX; + float max_softening = 0.f; + float max_mpole_power[SELF_GRAVITY_MULTIPOLE_ORDER + 1] = {0.f}; + + /* Threadpool id of current thread. */ + short int tpid = threadpool_gettid(); + /* Loop over the non-empty cells */ for (int ind = 0; ind < num_cells; ind++) { struct cell *c = &cells_top[local_cells_with_particles[ind]]; - space_split_recursive(s, c, NULL, NULL, NULL, NULL, NULL); + space_split_recursive(s, c, NULL, NULL, NULL, NULL, NULL, tpid); + + if (s->with_self_gravity) { + min_a_grav = + min(min_a_grav, c->grav.multipole->m_pole.min_old_a_grav_norm); + max_softening = + max(max_softening, c->grav.multipole->m_pole.max_softening); + + for (int n = 0; n < SELF_GRAVITY_MULTIPOLE_ORDER + 1; ++n) + max_mpole_power[n] = + max(max_mpole_power[n], c->grav.multipole->m_pole.power[n]); + } } #ifdef SWIFT_DEBUG_CHECKS @@ -712,6 +739,11 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) { if (!checkCellhdxmax(c, &depth)) message(" at cell depth %d", depth); } #endif + + atomic_min_f(&s->min_a_grav, min_a_grav); + atomic_max_f(&s->max_softening, max_softening); + for (int n = 0; n < SELF_GRAVITY_MULTIPOLE_ORDER + 1; ++n) + atomic_max_f(&s->max_mpole_power[n], max_mpole_power[n]); } /** @@ -727,6 +759,10 @@ void space_split(struct space *s, int verbose) { const ticks tic = getticks(); + s->min_a_grav = FLT_MAX; + s->max_softening = 0.f; + bzero(s->max_mpole_power, (SELF_GRAVITY_MULTIPOLE_ORDER + 1) * sizeof(float)); + threadpool_map(&s->e->threadpool, space_split_mapper, s->local_cells_with_particles_top, s->nr_local_cells_with_particles, sizeof(int), diff --git a/src/space_unique_id.c b/src/space_unique_id.c index 11773cdf0876c284e4ad08a5030370731be0bf57..137eb7189bc50bc5054265441691e42503abe0b2 100644 --- a/src/space_unique_id.c +++ b/src/space_unique_id.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <limits.h> diff --git a/src/space_unique_id.h b/src/space_unique_id.h index 49ec49d2bdc09b46451a4decdadd6dce046d5a20..124c825d3d9d40206624e4aff600f6870e7af208 100644 --- a/src/space_unique_id.h +++ b/src/space_unique_id.h @@ -21,7 +21,7 @@ #define SWIFT_SPACE_UNIQUE_ID_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "lock.h" diff --git a/src/star_formation.c b/src/star_formation.c index c0e8dfc2049c62ff827c7caa945bb08ee46e92e8..22426215e698249c0c4b2cdc2783c158a00b4068 100644 --- a/src/star_formation.c +++ b/src/star_formation.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* This object's header. */ #include "part.h" diff --git a/src/star_formation.h b/src/star_formation.h index cd21c8aa43c5c6a0a6747c3bd1e7c3544e3cc4e9..2fb08d3e82ad5940d05d67057ff74eb421f4d2fb 100644 --- a/src/star_formation.h +++ b/src/star_formation.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "hydro.h" diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h index eaf77400c1e35f5ce76c295eaadb93d5b7422869..88fa46dfbef6a3f956bad2d85296a81126c0886c 100644 --- a/src/star_formation/EAGLE/star_formation.h +++ b/src/star_formation/EAGLE/star_formation.h @@ -208,8 +208,8 @@ INLINE static int star_formation_is_star_forming_Z_dep( /* Physical density of the particle */ const double physical_density = hydro_get_physical_density(p, cosmo); - /* Get the Hydrogen number density (assuming primordial H abundance) */ - const double n_H = physical_density * hydro_props->hydrogen_mass_fraction; + /* Get the Hydrogen mass density (assuming primordial H abundance) */ + const double rho_H = physical_density * hydro_props->hydrogen_mass_fraction; /* Get the density threshold for star formation */ const double Z = @@ -232,7 +232,7 @@ INLINE static int star_formation_is_star_forming_Z_dep( density_threshold *= phys_const->const_proton_mass; /* Check if it exceeded the minimum density */ - if (n_H < density_threshold) return 0; + if (rho_H < density_threshold) return 0; /* Calculate the entropy of the particle */ const double entropy = hydro_get_physical_entropy(p, xp, cosmo); @@ -826,6 +826,15 @@ INLINE static void starformation_init_backend( } else if (strcmp(temp_SF, "Subgrid") == 0) { +#ifdef COOLING_EAGLE + error( + "The 'Subgrid' SF threshold in the EAGLE star formation model cannot " + "be used in combination with EAGLE cooling. A cooling model with " + "subgrid quantities (such as 'COLIBRE' using the Ploeckinger tables) " + "must be used. Alternatively, the 'Zdep' threshold should be used as " + "it can be combined with any cooling model."); +#endif + /* Subgrid quantities based model */ starform->SF_threshold = eagle_star_formation_threshold_subgrid; diff --git a/src/star_formation/EAGLE/star_formation_debug.h b/src/star_formation/EAGLE/star_formation_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..04b2717e4d9053d178eaa1ca9ad4fc2e1dc32fb2 --- /dev/null +++ b/src/star_formation/EAGLE/star_formation_debug.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_STAR_FORMATION_EAGLE_DEBUG_H +#define SWIFT_STAR_FORMATION_EAGLE_DEBUG_H + +__attribute__((always_inline)) INLINE static void star_formation_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] sf_data:", p->id); + warning("[PID%lld] SFR = %.3e", p->id, xp->sf_data.SFR); + } +} + +#endif /* SWIFT_STAR_FORMATION_EAGLE_DEBUG_H */ diff --git a/src/star_formation/EAGLE/star_formation_iact.h b/src/star_formation/EAGLE/star_formation_iact.h index acc4c871d3883d0647f58ccc64ea945eb6f2a1d7..eb366c617b001ebfcfdb3f33a7163a8be67a14c6 100644 --- a/src/star_formation/EAGLE/star_formation_iact.h +++ b/src/star_formation/EAGLE/star_formation_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/star_formation/EAGLE/star_formation_io.h b/src/star_formation/EAGLE/star_formation_io.h index a40ec2c2f804b0a82e99049d309dbf465594af61..d7c1cd2e69bb28215fc8508bbcd15deb080ef00c 100644 --- a/src/star_formation/EAGLE/star_formation_io.h +++ b/src/star_formation/EAGLE/star_formation_io.h @@ -20,7 +20,7 @@ #define SWIFT_STAR_FORMATION_EAGLE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "io_properties.h" diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index af58d58a575385c332f1814cd11c4331bd9ef6b7..872f6a67cd0f0f4f35dfdcd0b4fd02bfb66fad77 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (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 @@ -72,15 +72,23 @@ INLINE static int star_formation_is_star_forming( const float temperature = cooling_get_temperature(phys_const, hydro_props, us, cosmo, cooling, p, xp); + const float density = hydro_get_physical_density(p, cosmo); + const float temperature_max = starform->maximal_temperature; + const float density_threashold = starform->density_threashold; + /* Check the temperature criterion */ if (temperature > temperature_max) { return 0; } + /* Check the density threashold */ + if (density < density_threashold) { + return 0; + } + /* Get the required variables */ - const float density = hydro_get_physical_density(p, cosmo); const float n_jeans_2_3 = starform->n_jeans_2_3; const float h = p->h * kernel_gamma * cosmo->a; diff --git a/src/star_formation/GEAR/star_formation_csds.h b/src/star_formation/GEAR/star_formation_csds.h index 144dbf113a58957667cb034e34be2d4cff74e4d6..f05eebc4a09f108ad0d259dbb5b90c5a757048e7 100644 --- a/src/star_formation/GEAR/star_formation_csds.h +++ b/src/star_formation/GEAR/star_formation_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/star_formation/GEAR/star_formation_debug.h b/src/star_formation/GEAR/star_formation_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..0b7d79427b3f57497ef526c1044bee72e8431209 --- /dev/null +++ b/src/star_formation/GEAR/star_formation_debug.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_STAR_FORMATION_GEAR_DEBUG_H +#define SWIFT_STAR_FORMATION_GEAR_DEBUG_H + +__attribute__((always_inline)) INLINE static void star_formation_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] sf_data:", p->id); + warning("[PID%lld] div_v = %.3e", p->id, xp->sf_data.div_v); + } +} + +#endif /* SWIFT_STAR_FORMATION_GEAR_DEBUG_H */ diff --git a/src/star_formation/GEAR/star_formation_io.h b/src/star_formation/GEAR/star_formation_io.h index e5a7231d9678e0b5b8396e87d85789551719231b..9412097c79b5662009364da8144eb3be4053bd1f 100644 --- a/src/star_formation/GEAR/star_formation_io.h +++ b/src/star_formation/GEAR/star_formation_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (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 @@ -21,7 +21,7 @@ #define SWIFT_STAR_FORMATION_GEAR_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "io_properties.h" @@ -120,10 +120,14 @@ INLINE static void starformation_init_backend( starform->star_formation_efficiency = parser_get_param_double( parameter_file, "GEARStarFormation:star_formation_efficiency"); - /* Maximum temperature for star formation */ + /* Maximum gas temperature for star formation */ starform->maximal_temperature = parser_get_param_double( parameter_file, "GEARStarFormation:maximal_temperature"); + /* Minimal gas density for star formation */ + starform->density_threashold = parser_get_param_double( + parameter_file, "GEARStarFormation:density_threashold"); + /* Number of stars per particles */ starform->n_stars_per_part = parser_get_param_double( parameter_file, "GEARStarFormation:n_stars_per_particle"); @@ -140,11 +144,17 @@ INLINE static void starformation_init_backend( starform->n_jeans_2_3 = pow(starform->n_jeans_2_3, 2. / 3.); /* Apply unit change */ - starform->maximal_temperature *= + starform->maximal_temperature /= units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + starform->density_threashold /= + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + /* Initialize the mass of the stars to 0 for the stats computation */ starform->mass_stars = 0; + + message("maximal_temperature = %g", starform->maximal_temperature); + message("density_threashold = %g", starform->density_threashold); } #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 b0295ed4f8792f149b6eeb33d84736c48ddbdcb6..0d8beddd419f75760c8a3f7ed83c477c9acef525 100644 --- a/src/star_formation/GEAR/star_formation_logger.h +++ b/src/star_formation/GEAR/star_formation_logger.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (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 diff --git a/src/star_formation/GEAR/star_formation_logger_struct.h b/src/star_formation/GEAR/star_formation_logger_struct.h index 63e3af06a7cd5af375662a1ba28dc5000a69dc3f..3bf377993f658231489f0ceb76415397ee3ff125 100644 --- a/src/star_formation/GEAR/star_formation_logger_struct.h +++ b/src/star_formation/GEAR/star_formation_logger_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (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 diff --git a/src/star_formation/GEAR/star_formation_struct.h b/src/star_formation/GEAR/star_formation_struct.h index 09a077d4bf9adccbb97c86d4067deb9a41550ddc..ac5c83610caef63d6a7b74b7a3036b4be278b3ca 100644 --- a/src/star_formation/GEAR/star_formation_struct.h +++ b/src/star_formation/GEAR/star_formation_struct.h @@ -60,9 +60,12 @@ struct star_formation { * Jeans criterion (at power 2/3). */ float n_jeans_2_3; - /*! Maximal temperature for forming a star. */ + /*! Maximal gas temperature for forming a star. */ float maximal_temperature; + /*! Minimal gas density for forming a star. */ + float density_threashold; + /*! Star formation efficiency. */ float star_formation_efficiency; diff --git a/src/star_formation/QLA/star_formation_debug.h b/src/star_formation/QLA/star_formation_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..a92e0714476705c61c82bc3f86b649cc5f81bf79 --- /dev/null +++ b/src/star_formation/QLA/star_formation_debug.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_STAR_FORMATION_QLA_DEBUG_H +#define SWIFT_STAR_FORMATION_QLA_DEBUG_H + +__attribute__((always_inline)) INLINE static void star_formation_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] sf_data:", p->id); + warning("[PID%lld] convert_to_star = %d", p->id, + xp->sf_data.convert_to_star); + } +} + +#endif /* SWIFT_STAR_FORMATION_QLA_DEBUG_H */ diff --git a/src/star_formation/QLA/star_formation_io.h b/src/star_formation/QLA/star_formation_io.h index d65a40bf70782dcaf104ea1afe2565df2b2b307e..ee8de2b778aaea0fa797599da35b0d2075b9f626 100644 --- a/src/star_formation/QLA/star_formation_io.h +++ b/src/star_formation/QLA/star_formation_io.h @@ -20,7 +20,7 @@ #define SWIFT_STAR_FORMATION_QLA_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "io_properties.h" diff --git a/src/star_formation/none/star_formation_csds.h b/src/star_formation/none/star_formation_csds.h index 91ef1c097e4501f9a3fe33bb4e454667d3531ecd..8cd25a4f543244c448e3ce87fa7cc912ec9cf179 100644 --- a/src/star_formation/none/star_formation_csds.h +++ b/src/star_formation/none/star_formation_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/star_formation/none/star_formation_debug.h b/src/star_formation/none/star_formation_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..4923d24d8caba7fb212c93921e84f84c00c85200 --- /dev/null +++ b/src/star_formation/none/star_formation_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_STAR_FORMATION_NONE_DEBUG_H +#define SWIFT_STAR_FORMATION_NONE_DEBUG_H + +__attribute__((always_inline)) INLINE static void star_formation_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_STAR_FORMATION_NONE_DEBUG_H */ diff --git a/src/star_formation/none/star_formation_iact.h b/src/star_formation/none/star_formation_iact.h index 91ee8a6726d68b3d697e6024904422d16b2bc136..6731f1a24a96fe6881e04e088673c2ec159bd82e 100644 --- a/src/star_formation/none/star_formation_iact.h +++ b/src/star_formation/none/star_formation_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/star_formation/none/star_formation_io.h b/src/star_formation/none/star_formation_io.h index 5c2329b3978ebf0c5ed64c3f978c479456a3efc0..d89060282c9ebe0d633d8940931b1fe8027df803 100644 --- a/src/star_formation/none/star_formation_io.h +++ b/src/star_formation/none/star_formation_io.h @@ -20,7 +20,7 @@ #define SWIFT_STAR_FORMATION_NONE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "io_properties.h" diff --git a/src/star_formation_csds.h b/src/star_formation_csds.h index e0db074748f895073be77d5248953b3deebb5363..d34471860158c2dba29232a6713bd61cce98214e 100644 --- a/src/star_formation_csds.h +++ b/src/star_formation_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -20,7 +20,7 @@ #define SWIFT_STAR_FORMATION_PARTICLE_CSDS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "align.h" diff --git a/src/star_formation_debug.h b/src/star_formation_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..81b15daa01b841e5260ac80faccf80124c0966b5 --- /dev/null +++ b/src/star_formation_debug.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_STAR_FORMATION_DEBUG_H +#define SWIFT_STAR_FORMATION_DEBUG_H + +/* Config parameters. */ +#include <config.h> + +/* Import the debug routines of the right star formation definition */ +#if defined(STAR_FORMATION_NONE) +#include "./star_formation/none/star_formation_debug.h" +#elif defined(STAR_FORMATION_QLA) +#include "./star_formation/QLA/star_formation_debug.h" +#elif defined(STAR_FORMATION_EAGLE) +#include "./star_formation/EAGLE/star_formation_debug.h" +#elif defined(STAR_FORMATION_GEAR) +#include "./star_formation/GEAR/star_formation_debug.h" +#else +#error "Invalid choice of star formation model." +#endif + +#endif /* SWIFT_STAR_FORMATION_DEBUG_H */ diff --git a/src/star_formation_iact.h b/src/star_formation_iact.h index 5baf914a2dd2e74d681edbf907a032109e39177d..9959c52d207f4cd81bdce0f39b9f0720b502257e 100644 --- a/src/star_formation_iact.h +++ b/src/star_formation_iact.h @@ -26,7 +26,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right star formation law definition */ #if defined(STAR_FORMATION_NONE) diff --git a/src/star_formation_io.h b/src/star_formation_io.h index 3837b5d3d85255b43eeb19371ca868517fa0d4a1..4edd084e66f6a4018701e3c19c4b19d5e5d4f43c 100644 --- a/src/star_formation_io.h +++ b/src/star_formation_io.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right cooling definition */ #if defined(STAR_FORMATION_NONE) diff --git a/src/star_formation_logger.h b/src/star_formation_logger.h index bddd1e68af2ec42ee4a9f00e32c2062909f47642..fe1bc7d3891045befbac3a7d7d5ff3da9b611150 100644 --- a/src/star_formation_logger.h +++ b/src/star_formation_logger.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right SFH logger definition */ #if defined(STAR_FORMATION_NONE) diff --git a/src/star_formation_logger_struct.h b/src/star_formation_logger_struct.h index 0c6b9533106f895cd102671ee2d712fd65a1c0f9..5ba6654fd850cb05d5f0c8903339360f7404e192 100644 --- a/src/star_formation_logger_struct.h +++ b/src/star_formation_logger_struct.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right SFH logger struct definition */ #if defined(STAR_FORMATION_NONE) diff --git a/src/star_formation_struct.h b/src/star_formation_struct.h index ae4cdace9c7cd0daf5e61ff2b87a3333e271dab7..2c8e72418e93f18e8daa5167d98d97ef8c9752cc 100644 --- a/src/star_formation_struct.h +++ b/src/star_formation_struct.h @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right star formation definition */ #if defined(STAR_FORMATION_NONE) diff --git a/src/stars.c b/src/stars.c index b0355947281dc9802ba92db29053c8a6389abc4b..e86f0a06922fc0df6ef779dfdbd619b1524e097f 100644 --- a/src/stars.c +++ b/src/stars.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> diff --git a/src/stars.h b/src/stars.h index 334c6aff844da0df6bf7cfc3f015c68c06f3465c..68b8d7fa52e27238db642ca8b7d1e1ea1923401a 100644 --- a/src/stars.h +++ b/src/stars.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_STARS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Select the correct star model */ #if defined(STARS_NONE) diff --git a/src/stars/Basic/stars.h b/src/stars/Basic/stars.h index 5e1441f004619ed8b2dde144f5177ddf520076d1..3af7af821d1bd0f9fb47f53e70aef746c605dfe3 100644 --- a/src/stars/Basic/stars.h +++ b/src/stars/Basic/stars.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/stars/Basic/stars_csds.h b/src/stars/Basic/stars_csds.h index fa89aab12aa355abba8447a690a11ae08d9e8b5b..7430bb947e8a7a1e4de46616fb961a61cf219f54 100644 --- a/src/stars/Basic/stars_csds.h +++ b/src/stars/Basic/stars_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/stars/Basic/stars_debug.h b/src/stars/Basic/stars_debug.h index ee6f571dacb8d859ebd6013ad9fad294ef95b6fc..a5f1a7a8d4b489213974810ecaea6d03715f23f3 100644 --- a/src/stars/Basic/stars_debug.h +++ b/src/stars/Basic/stars_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/stars/Basic/stars_iact.h b/src/stars/Basic/stars_iact.h index d24f6059082b630f2c2df9cabe7112a97bd75368..90e9a0549ac440ff8819340b14c9af0610215ad8 100644 --- a/src/stars/Basic/stars_iact.h +++ b/src/stars/Basic/stars_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify diff --git a/src/stars/Basic/stars_io.h b/src/stars/Basic/stars_io.h index 05a6717742d36575a2d45b883d5b0f140c7357a3..54ad764954bd42d199ae0d67550cbedb3f855b86 100644 --- a/src/stars/Basic/stars_io.h +++ b/src/stars/Basic/stars_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -105,6 +105,15 @@ INLINE static void convert_spart_vel(const struct engine *e, ret[2] *= cosmo->a_inv; } +INLINE static void convert_spart_potential(const struct engine *e, + const struct spart *sp, float *ret) { + + if (sp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(sp->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which s-particle fields to write to a dataset * @@ -141,6 +150,10 @@ INLINE static void stars_write_particles(const struct spart *sparts, "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_convert_spart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, sparts, + convert_spart_potential, "Gravitational potentials of the particles"); + #ifdef DEBUG_INTERACTIONS_STARS list += *num_fields; diff --git a/src/stars/Basic/stars_part.h b/src/stars/Basic/stars_part.h index b023d8205a5694bcc641211f088d5d7ae867a5a1..713fe0e281c65ad4e484e42682f83c16b88b1cda 100644 --- a/src/stars/Basic/stars_part.h +++ b/src/stars/Basic/stars_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -88,7 +88,7 @@ struct spart { struct feedback_spart_data feedback_data; /*! Tracer structure */ - struct tracers_xpart_data tracers_data; + struct tracers_spart_data tracers_data; /*! Chemistry structure */ struct chemistry_spart_data chemistry_data; diff --git a/src/stars/EAGLE/stars.h b/src/stars/EAGLE/stars.h index 75ee2a0d16f503bb05c89a35dec3b01f7727d19a..ac6005f8620d9636e6aa91881086a130ed724806 100644 --- a/src/stars/EAGLE/stars.h +++ b/src/stars/EAGLE/stars.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/stars/EAGLE/stars_debug.h b/src/stars/EAGLE/stars_debug.h index 6bdba45e3b090ccd2eb207bf92a374ce531ee3e0..6ab0e4c43ef75f27f8c991b19ee4f76a5793d1e1 100644 --- a/src/stars/EAGLE/stars_debug.h +++ b/src/stars/EAGLE/stars_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/stars/EAGLE/stars_iact.h b/src/stars/EAGLE/stars_iact.h index e540549484635fae2f7f28ce4d560ecb4c8366a4..c75d741693430cb83b8993049f2426e02f73095c 100644 --- a/src/stars/EAGLE/stars_iact.h +++ b/src/stars/EAGLE/stars_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/stars/EAGLE/stars_io.h b/src/stars/EAGLE/stars_io.h index 09880d072714506036a7187235c7cf051b95fb05..1c499624a67e995e106d4c89225a1e076d8ec21c 100644 --- a/src/stars/EAGLE/stars_io.h +++ b/src/stars/EAGLE/stars_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify @@ -122,6 +122,15 @@ INLINE static void convert_spart_luminosities(const struct engine *e, ret); } +INLINE static void convert_spart_potential(const struct engine *e, + const struct spart *sp, float *ret) { + + if (sp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(sp->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which s-particle fields to write to a dataset * @@ -134,7 +143,7 @@ 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 = 13; + *num_fields = 14; /* List what we want to write */ list[0] = io_make_output_field_convert_spart( @@ -212,6 +221,10 @@ INLINE static void stars_write_particles(const struct spart *sparts, "have been divided by 3631 Jy already, i.e. they can be turned into " "absolute AB-magnitudes (rest-frame absolute maggies) directly by " "applying -2.5 log10(L) without additional corrections."); + + list[13] = io_make_output_field_convert_spart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, sparts, + convert_spart_potential, "Gravitational potentials of the particles"); } /** diff --git a/src/stars/EAGLE/stars_part.h b/src/stars/EAGLE/stars_part.h index 75461dafa894ed3768d309f29c76e580c8ee1bbb..22091ecc4f6c43928993b6eb8ff43ed9d9e7cd1e 100644 --- a/src/stars/EAGLE/stars_part.h +++ b/src/stars/EAGLE/stars_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify @@ -110,7 +110,7 @@ struct spart { struct feedback_spart_data feedback_data; /*! Tracer structure */ - struct tracers_xpart_data tracers_data; + struct tracers_spart_data tracers_data; /*! Chemistry structure */ struct chemistry_spart_data chemistry_data; diff --git a/src/stars/GEAR/stars.h b/src/stars/GEAR/stars.h index 28180b7b894919a8507f630f527ac3047f77f692..0645f56e7d97ea277f23052dde9c2f634bb501f8 100644 --- a/src/stars/GEAR/stars.h +++ b/src/stars/GEAR/stars.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 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 diff --git a/src/stars/GEAR/stars_csds.h b/src/stars/GEAR/stars_csds.h index dd39ed970d6f60ecdb76aaea20c962803b34fb95..4d4f05ed929711212b42b4543e1e04621ccc0e26 100644 --- a/src/stars/GEAR/stars_csds.h +++ b/src/stars/GEAR/stars_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 diff --git a/src/stars/GEAR/stars_debug.h b/src/stars/GEAR/stars_debug.h index 41953367f6c8771ffd7460cee493c8330ecd874b..fa5d6c723c0dd6a8546cc2f133fb6b79c1598153 100644 --- a/src/stars/GEAR/stars_debug.h +++ b/src/stars/GEAR/stars_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 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 diff --git a/src/stars/GEAR/stars_iact.h b/src/stars/GEAR/stars_iact.h index ffc764444350e6ebfb85a6699ae0e912b709bd7a..8c4f4e1196a5bd12f0e6f2cb7dc2fc7e88f23c87 100644 --- a/src/stars/GEAR/stars_iact.h +++ b/src/stars/GEAR/stars_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 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 diff --git a/src/stars/GEAR/stars_io.h b/src/stars/GEAR/stars_io.h index bed046b1f6e4c7b4641226363debd31d2a63a2dd..041551d96c2c545ce8bb51eb2fd89e0e16e27362 100644 --- a/src/stars/GEAR/stars_io.h +++ b/src/stars/GEAR/stars_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 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 @@ -107,6 +107,15 @@ INLINE static void convert_spart_vel(const struct engine *e, ret[2] *= cosmo->a_inv; } +INLINE static void convert_spart_potential(const struct engine *e, + const struct spart *sp, float *ret) { + + if (sp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(sp->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which s-particle fields to write to a dataset * @@ -120,7 +129,7 @@ INLINE static void stars_write_particles(const struct spart *sparts, const int with_cosmology) { /* Say how much we want to write */ - *num_fields = 6; + *num_fields = 7; /* List what we want to write */ list[0] = io_make_output_field_convert_spart( @@ -153,6 +162,10 @@ INLINE static void stars_write_particles(const struct spart *sparts, "Times at which the stars were born"); } + list[6] = io_make_output_field_convert_spart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, sparts, + convert_spart_potential, "Gravitational potentials of the particles"); + #ifdef DEBUG_INTERACTIONS_STARS list += *num_fields; diff --git a/src/stars/GEAR/stars_part.h b/src/stars/GEAR/stars_part.h index cbca5a11c158c095738aaf3696cec197ec297c4a..d9d73c1f3e2da40aeef359e576a3d9b298b10a5b 100644 --- a/src/stars/GEAR/stars_part.h +++ b/src/stars/GEAR/stars_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * 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 @@ -88,7 +88,7 @@ struct spart { struct feedback_spart_data feedback_data; /*! Tracer structure */ - struct tracers_xpart_data tracers_data; + struct tracers_spart_data tracers_data; /*! Chemistry structure */ struct chemistry_spart_data chemistry_data; diff --git a/src/stars/None/stars.h b/src/stars/None/stars.h index 05f520510b5bb40ab0059c145ff1345b0627886f..70376a297bc132af056b167e3ad608070c75ef05 100644 --- a/src/stars/None/stars.h +++ b/src/stars/None/stars.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2020 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 diff --git a/src/stars/None/stars_debug.h b/src/stars/None/stars_debug.h index 827a457e5899b138d750cb642165fcf2af1ef04b..9d3215a509e8228e744c0c1c46ebda2754eb763d 100644 --- a/src/stars/None/stars_debug.h +++ b/src/stars/None/stars_debug.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2020 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 diff --git a/src/stars/None/stars_iact.h b/src/stars/None/stars_iact.h index d46261ea9a955b96c497babe0dd22c600f2e309e..266e7897db17ff7632972915de609b8436b6674d 100644 --- a/src/stars/None/stars_iact.h +++ b/src/stars/None/stars_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * Copyright (c) 2020 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 diff --git a/src/stars/None/stars_io.h b/src/stars/None/stars_io.h index d06e725af8d6f42b28a629547cc5d0759eec7710..2702a6e3c8f1a009e56c9a46e24d5ab3431edece 100644 --- a/src/stars/None/stars_io.h +++ b/src/stars/None/stars_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 diff --git a/src/stars/None/stars_part.h b/src/stars/None/stars_part.h index cdc16ec57c5f384eff2f162114fb6cbf83547d9c..2400407ce25a60b504c31bb782730a7dfe6521fc 100644 --- a/src/stars/None/stars_part.h +++ b/src/stars/None/stars_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -58,6 +58,9 @@ struct spart { /*! Particle time bin */ timebin_t time_bin; + /*! Tracer structure */ + struct tracers_spart_data tracers_data; + /*! Splitting structure */ struct particle_splitting_data split_data; diff --git a/src/stars_csds.h b/src/stars_csds.h index e3ff6219b4069596ea4e662587a92e4e68fb5197..fe88620f8a06b066dba8f1ab207535be228a40a7 100644 --- a/src/stars_csds.h +++ b/src/stars_csds.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) + * Copyright (c) 2020 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 @@ -20,7 +20,7 @@ #define SWIFT_STARS_CSDS_H /* Include config */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "./const.h" diff --git a/src/stars_io.h b/src/stars_io.h index 402e90ed703d925ef1fed55d861278a9c04775d8..a62923534662ff4c6962b1270b7cb18a108dcf1e 100644 --- a/src/stars_io.h +++ b/src/stars_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -19,7 +19,7 @@ #ifndef SWIFT_STARS_IO_H #define SWIFT_STARS_IO_H -#include "../config.h" +#include <config.h> /* Load the correct star type */ #if defined(STARS_NONE) diff --git a/src/statistics.c b/src/statistics.c index 6bc347cbf1786b8166b306787fba6cc115685c37..8987c95f49ea092a6561f00c0198c49594987a31 100644 --- a/src/statistics.c +++ b/src/statistics.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> @@ -41,6 +41,8 @@ #include "error.h" #include "gravity_io.h" #include "hydro_io.h" +#include "mhd_io.h" +#include "potential.h" #include "sink_io.h" #include "stars_io.h" #include "threadpool.h" @@ -84,6 +86,8 @@ void stats_add(struct statistics *a, const struct statistics *b) { a->bh_Z_mass += b->bh_Z_mass; a->bh_accretion_rate += b->bh_accretion_rate; a->bh_accreted_mass += b->bh_accreted_mass; + a->bh_bolometric_luminosity += b->bh_bolometric_luminosity; + a->bh_jet_power += b->bh_jet_power; a->mom[0] += b->mom[0]; a->mom[1] += b->mom[1]; a->mom[2] += b->mom[2]; @@ -97,6 +101,10 @@ void stats_add(struct statistics *a, const struct statistics *b) { a->gas_H2_mass += b->gas_H2_mass; a->gas_HI_mass += b->gas_HI_mass; a->gas_He_mass += b->gas_He_mass; + a->E_mag += b->E_mag; + a->divB_error += b->divB_error; + a->H_cross += b->H_cross; + a->H_mag += b->H_mag; } /** @@ -235,6 +243,16 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) { /* Collect entropy */ stats.entropy += m * entropy; + + /* Collect magnetic energy */ + stats.E_mag += mhd_get_magnetic_energy(p, xp); + + /* Collect helicity */ + stats.H_mag += mhd_get_magnetic_helicity(p, xp); + stats.H_cross += mhd_get_cross_helicity(p, xp); + + /* Collect div B error */ + stats.divB_error += mhd_get_divB_error(p, xp); } /* Now write back to memory */ @@ -505,6 +523,11 @@ void stats_collect_bpart_mapper(void *map_data, int nr_bparts, /* Collect accretion data. */ stats.bh_accretion_rate += black_holes_get_accretion_rate(bp); stats.bh_accreted_mass += black_holes_get_accreted_mass(bp); + + /* Collect bolometric luminosity and jet powers. */ + stats.bh_bolometric_luminosity += + black_holes_get_bolometric_luminosity(bp, phys_const); + stats.bh_jet_power += black_holes_get_jet_power(bp, phys_const); } /* Now write back to memory */ @@ -820,29 +843,56 @@ void stats_write_file_header(FILE *file, const struct unit_system *restrict us, "simulation. \n"); fprintf(file, "# Unit = %e gram\n", us->UnitMass_in_cgs); fprintf(file, "# Unit = %e Msun\n", 1. / phys_const->const_solar_mass); + fprintf(file, + "# (34) Total Magnetic Energy B2/(2*mu0) in the" + "simulation. \n"); + fprintf(file, "# Unit = %e erg\n", + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY)); + fprintf(file, + "# (35) Total DivB error in the" + "simulation. \n"); + fprintf(file, "# Unit = dimensionless\n"); + fprintf(file, + "# (36) Total Cross Helicity :: sum(V.B) in the" + "simulation. \n"); + fprintf(file, "# Unit = %e gram * cm * s**-3 * A**-1 \n", + units_cgs_conversion_factor(us, UNIT_CONV_MAGNETIC_CROSS_HELICITY)); + fprintf(file, + "# (37) Total Magnetic Helicity :: sum(A.B) in the" + "simulation. \n"); + fprintf(file, "# Unit = %e gram**2 * cm * s**-4 * A**-2\n", + 1. / units_cgs_conversion_factor(us, UNIT_CONV_MAGNETIC_HELICITY)); + fprintf(file, "# (38) Total bolometric luminosity of the BHs. \n"); + fprintf(file, "# Unit = %e erg * s**-1\n", + units_cgs_conversion_factor(us, UNIT_CONV_POWER)); + fprintf(file, "# (39) Total jet power of the BHs. \n"); + fprintf(file, "# Unit = %e erg * s**-1\n", + units_cgs_conversion_factor(us, UNIT_CONV_POWER)); fprintf(file, "#\n"); fprintf( file, "#%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s " "%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s " - "%14s %14s %14s %14s %14s %14s\n", + "%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s \n", "(0)", "(1)", "(2)", "(3)", "(4)", "(5)", "(6)", "(7)", "(8)", "(9)", "(10)", "(11)", "(12)", "(13)", "(14)", "(15)", "(16)", "(17)", "(18)", "(19)", "(20)", "(21)", "(22)", "(23)", "(24)", "(25)", "(26)", "(27)", - "(28)", "(29)", "(30)", "(31)", "(32)", "(33)"); + "(28)", "(29)", "(30)", "(31)", "(32)", "(33)", "(34)", "(35)", "(36)", + "(37)", "(38)", "(39)"); fprintf( file, "#%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s " "%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s " - "%14s %14s %14s %14s %14s %14s\n", + "%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s \n", "Step", "Time", "a", "z", "Total mass", "Gas mass", "DM mass", "Sink mass", "Star mass", "BH mass", "Gas Z mass", "Star Z mass", "BH Z mass", "Kin. Energy", "Int. Energy", "Pot. energy", "Rad. energy", "Gas Entropy", "CoM x", "CoM y", "CoM z", "Mom. x", "Mom. y", "Mom. z", "Ang. mom. x", "Ang. mom. y", "Ang. mom. z", "BH acc. rate", "BH acc. mass", "BH sub. mass", "Gas H mass", "Gas H2 mass", - "Gas HI mass", "Gas He mass"); + "Gas HI mass", "Gas He mass", "Mag. Energy", "DivB err", "Cr. Helicity", + "Mag. Helicity", "BH bol. lum.", "BH jet power"); fflush(file); } @@ -869,7 +919,7 @@ void stats_write_to_file(FILE *file, const struct statistics *stats, file, " %14d %14e %14.7f %14.7f %14e %14e %14e %14e %14e %14e %14e %14e %14e " "%14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e " - "%14e %14e %14e %14e %14e %14e %14e\n", + "%14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e\n", step, time, a, z, stats->total_mass, stats->gas_mass, stats->dm_mass, stats->sink_mass, stats->star_mass, stats->bh_mass, stats->gas_Z_mass, stats->star_Z_mass, stats->bh_Z_mass, stats->E_kin, stats->E_int, E_pot, @@ -878,7 +928,9 @@ void stats_write_to_file(FILE *file, const struct statistics *stats, stats->mom[1], stats->mom[2], stats->ang_mom[0], stats->ang_mom[1], stats->ang_mom[2], stats->bh_accretion_rate, stats->bh_accreted_mass, stats->bh_subgrid_mass, stats->gas_H_mass, stats->gas_H2_mass, - stats->gas_HI_mass, stats->gas_He_mass); + stats->gas_HI_mass, stats->gas_He_mass, stats->E_mag, stats->divB_error, + stats->H_cross, stats->H_mag, stats->bh_bolometric_luminosity, + stats->bh_jet_power); fflush(file); } diff --git a/src/statistics.h b/src/statistics.h index 0b82387efa7310016cccd8bc92d1d44edc2e2a47..0ab0f7d8ad3488f7fcada1d52b7ce1ce410b6498 100644 --- a/src/statistics.h +++ b/src/statistics.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_STATISTICS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "lock.h" @@ -92,6 +92,12 @@ struct statistics { /*! Total accreted mass of all BHs (internal units)*/ double bh_accreted_mass; + /* Total BH bolometric luminosity of all BHs (internal units) */ + double bh_bolometric_luminosity; + + /* Total jet power of all BHs (internal units) */ + double bh_jet_power; + /*! Momentum (internal units)*/ double mom[3]; @@ -113,6 +119,18 @@ struct statistics { /*! Total gas mass that is in Helium (all species) */ double gas_He_mass; + /*! Total Magnetic Energy */ + double E_mag; + + /*! Total divB error */ + double divB_error; + + /*! Total Cross Helicity */ + double H_cross; + + /*! Total Magnetic helicity */ + double H_mag; + /*! Lock for threaded access */ swift_lock_type lock; }; diff --git a/src/swift.h b/src/swift.h index b112766b5fbc09bd2ef6f0fdf47242e8eecd84f7..9cee769a6e9d4e92a5a13982795b3a98df5ec18c 100644 --- a/src/swift.h +++ b/src/swift.h @@ -20,7 +20,7 @@ #define SWIFT_SWIFT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "active.h" @@ -42,6 +42,7 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "extra_io.h" #include "feedback.h" #include "feedback_properties.h" #include "fof.h" @@ -52,6 +53,7 @@ #include "hydro.h" #include "hydro_properties.h" #include "ic_info.h" +#include "lightcone/lightcone_array.h" #include "line_of_sight.h" #include "lock.h" #include "map.h" @@ -70,6 +72,7 @@ #include "periodic.h" #include "physical_constants.h" #include "potential.h" +#include "power_spectrum.h" #include "pressure_floor.h" #include "pressure_floor_iact.h" #include "profiler.h" diff --git a/src/task.c b/src/task.c index 650a7f43a0a21869a7794fb867db4e764364d0bd..333f648769db702651f6438d2a8a3cf9c34de5b2 100644 --- a/src/task.c +++ b/src/task.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -22,7 +22,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> @@ -68,7 +68,7 @@ const char *taskID_names[task_type_count] = { "drift_bpart", "drift_gpart", "drift_gpart_out", - "end_hydro_force", + "hydro_end_force", "kick1", "kick2", "timestep", @@ -112,7 +112,8 @@ const char *taskID_names[task_type_count] = { "fof_pair", "neutrino_weight", "sink_in", - "sink_ghost", + "sink_ghost1", + "sink_ghost2", "sink_out", "rt_in", "rt_out", @@ -121,6 +122,9 @@ const char *taskID_names[task_type_count] = { "rt_ghost2", "rt_transport_out", "rt_tchem", + "rt_advance_cell_time", + "rt_sorts", + "rt_collect_times", }; /* Sub-task type names. */ @@ -146,7 +150,7 @@ const char *subtaskID_names[task_subtype_count] = { "stars_prep1", "stars_prep2", "stars_feedback", - "sf_count", + "sf_counts", "bpart_rho", "bpart_swallow", "bpart_feedback", @@ -155,10 +159,9 @@ const char *subtaskID_names[task_subtype_count] = { "do_gas_swallow", "do_bh_swallow", "bh_feedback", - "sink_merger", - "rt_inject", - "sink_compute_formation", - "sink_accretion", + "sink_do_sink_swallow", + "sink_swallow", + "sink_do_gas_swallow", "rt_gradient", "rt_transport", }; @@ -184,22 +187,22 @@ MPI_Comm subtaskMPI_comms[task_subtype_count]; * @param ARRAY is the array of this specific type. * @param COUNT is the number of elements in the array. */ -#define TASK_CELL_OVERLAP(TYPE, ARRAY, COUNT) \ - __attribute__((always_inline)) \ - INLINE static size_t task_cell_overlap_##TYPE( \ - const struct cell *restrict ci, const struct cell *restrict cj) { \ - \ - if (ci == NULL || cj == NULL) return 0; \ - \ - if (ci->ARRAY <= cj->ARRAY && \ - ci->ARRAY + ci->COUNT >= cj->ARRAY + cj->COUNT) { \ - return cj->COUNT; \ - } else if (cj->ARRAY <= ci->ARRAY && \ - cj->ARRAY + cj->COUNT >= ci->ARRAY + ci->COUNT) { \ - return ci->COUNT; \ - } \ - \ - return 0; \ +#define TASK_CELL_OVERLAP(TYPE, ARRAY, COUNT) \ + __attribute__((always_inline)) \ + INLINE static size_t task_cell_overlap_##TYPE( \ + const struct cell *restrict ci, const struct cell *restrict cj) { \ + \ + if (ci == NULL || cj == NULL) return 0; \ + \ + if (ci->ARRAY <= cj->ARRAY && \ + ci->ARRAY + ci->COUNT >= cj->ARRAY + cj->COUNT) { \ + return cj->COUNT; \ + } else if (cj->ARRAY <= ci->ARRAY && \ + cj->ARRAY + cj->COUNT >= ci->ARRAY + ci->COUNT) { \ + return ci->COUNT; \ + } \ + \ + return 0; \ } TASK_CELL_OVERLAP(part, hydro.parts, hydro.count); @@ -256,6 +259,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_type_rt_ghost1: case task_type_rt_ghost2: case task_type_rt_tchem: + case task_type_rt_sort: return task_action_part; break; @@ -288,15 +292,11 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( return task_action_bpart; break; - case task_subtype_sink_accretion: - case task_subtype_sink_merger: - case task_subtype_sink_compute_formation: + case task_subtype_sink_do_gas_swallow: + case task_subtype_sink_do_sink_swallow: + case task_subtype_sink_swallow: return task_action_all; - case task_subtype_rt_inject: - return task_action_all; - break; - case task_subtype_rt_transport: case task_subtype_rt_gradient: return task_action_part; @@ -546,6 +546,8 @@ void task_unlock(struct task *t) { case task_type_rt_ghost1: case task_type_rt_ghost2: case task_type_rt_tchem: + case task_type_rt_sort: + case task_type_rt_advance_cell_time: cell_unlocktree(ci); break; @@ -570,13 +572,13 @@ void task_unlock(struct task *t) { cell_gunlocktree(ci); cell_munlocktree(ci); #endif - } else if (subtype == task_subtype_sink_compute_formation) { + } else if (subtype == task_subtype_sink_swallow) { cell_sink_unlocktree(ci); cell_unlocktree(ci); - } else if (subtype == task_subtype_sink_merger) { + } else if (subtype == task_subtype_sink_do_sink_swallow) { cell_sink_unlocktree(ci); cell_gunlocktree(ci); - } else if (subtype == task_subtype_sink_accretion) { + } else if (subtype == task_subtype_sink_do_gas_swallow) { cell_unlocktree(ci); cell_sink_unlocktree(ci); cell_gunlocktree(ci); @@ -594,9 +596,6 @@ void task_unlock(struct task *t) { cell_unlocktree(ci); } else if (subtype == task_subtype_do_bh_swallow) { cell_bunlocktree(ci); - } else if (subtype == task_subtype_rt_inject) { - cell_unlocktree(ci); - cell_sunlocktree(ci); } else if (subtype == task_subtype_limiter) { #ifdef SWIFT_TASKS_WITHOUT_ATOMICS cell_unlocktree(ci); @@ -615,17 +614,17 @@ void task_unlock(struct task *t) { cell_munlocktree(ci); cell_munlocktree(cj); #endif - } else if (subtype == task_subtype_sink_compute_formation) { + } else if (subtype == task_subtype_sink_swallow) { cell_sink_unlocktree(ci); cell_sink_unlocktree(cj); cell_unlocktree(ci); cell_unlocktree(cj); - } else if (subtype == task_subtype_sink_merger) { + } else if (subtype == task_subtype_sink_do_sink_swallow) { cell_sink_unlocktree(ci); cell_sink_unlocktree(cj); cell_gunlocktree(ci); cell_gunlocktree(cj); - } else if (subtype == task_subtype_sink_accretion) { + } else if (subtype == task_subtype_sink_do_gas_swallow) { cell_sink_unlocktree(ci); cell_sink_unlocktree(cj); cell_unlocktree(ci); @@ -651,11 +650,6 @@ void task_unlock(struct task *t) { } else if (subtype == task_subtype_do_bh_swallow) { cell_bunlocktree(ci); cell_bunlocktree(cj); - } else if (subtype == task_subtype_rt_inject) { - cell_sunlocktree(ci); - cell_sunlocktree(cj); - cell_unlocktree(ci); - cell_unlocktree(cj); } else if (subtype == task_subtype_limiter) { #ifdef SWIFT_TASKS_WITHOUT_ATOMICS cell_unlocktree(ci); @@ -775,6 +769,8 @@ int task_lock(struct task *t) { case task_type_rt_ghost1: case task_type_rt_ghost2: case task_type_rt_tchem: + case task_type_rt_sort: + case task_type_rt_advance_cell_time: if (ci->hydro.hold) return 0; if (cell_locktree(ci) != 0) return 0; break; @@ -809,7 +805,7 @@ int task_lock(struct task *t) { return 0; } #endif - } else if (subtype == task_subtype_sink_merger) { + } else if (subtype == task_subtype_sink_do_sink_swallow) { if (ci->sinks.hold) return 0; if (ci->grav.phold) return 0; if (cell_sink_locktree(ci) != 0) return 0; @@ -817,7 +813,7 @@ int task_lock(struct task *t) { cell_sink_unlocktree(ci); return 0; } - } else if (subtype == task_subtype_sink_compute_formation) { + } else if (subtype == task_subtype_sink_swallow) { if (ci->sinks.hold) return 0; if (ci->hydro.hold) return 0; if (cell_sink_locktree(ci) != 0) return 0; @@ -825,7 +821,7 @@ int task_lock(struct task *t) { cell_sink_unlocktree(ci); return 0; } - } else if (subtype == task_subtype_sink_accretion) { + } else if (subtype == task_subtype_sink_do_gas_swallow) { if (ci->sinks.hold) return 0; if (ci->grav.phold) return 0; if (ci->hydro.hold) return 0; @@ -869,14 +865,6 @@ int task_lock(struct task *t) { if (ci->hydro.hold) return 0; if (cell_locktree(ci) != 0) return 0; #endif - } else if (subtype == task_subtype_rt_inject) { - if (ci->stars.hold) return 0; - if (ci->hydro.hold) return 0; - if (cell_slocktree(ci) != 0) return 0; - if (cell_locktree(ci) != 0) { - cell_sunlocktree(ci); - return 0; - } } else { /* subtype == hydro */ if (ci->hydro.hold) return 0; if (cell_locktree(ci) != 0) return 0; @@ -904,7 +892,7 @@ int task_lock(struct task *t) { return 0; } #endif - } else if (subtype == task_subtype_sink_compute_formation) { + } else if (subtype == task_subtype_sink_swallow) { /* Lock the sinks and the gas particles in both cells */ if (ci->sinks.hold || cj->sinks.hold) return 0; if (ci->hydro.hold || cj->hydro.hold) return 0; @@ -924,7 +912,7 @@ int task_lock(struct task *t) { cell_unlocktree(ci); return 0; } - } else if (subtype == task_subtype_sink_accretion) { + } else if (subtype == task_subtype_sink_do_gas_swallow) { /* Lock the sinks and the gas particles in both cells */ if (ci->sinks.hold || cj->sinks.hold) return 0; if (ci->hydro.hold || cj->hydro.hold) return 0; @@ -960,7 +948,7 @@ int task_lock(struct task *t) { cell_gunlocktree(ci); return 0; } - } else if (subtype == task_subtype_sink_merger) { + } else if (subtype == task_subtype_sink_do_sink_swallow) { /* Lock the sink and the dm particles in both cells */ if (ci->sinks.hold || cj->sinks.hold) return 0; if (ci->grav.phold || cj->grav.phold) return 0; @@ -1033,26 +1021,6 @@ int task_lock(struct task *t) { cell_bunlocktree(ci); return 0; } - } else if (subtype == task_subtype_rt_inject) { - /* Lock the stars and the gas particles in both cells */ - if (ci->stars.hold || cj->stars.hold) return 0; - if (ci->hydro.hold || cj->hydro.hold) return 0; - if (cell_slocktree(ci) != 0) return 0; - if (cell_slocktree(cj) != 0) { - cell_sunlocktree(ci); - return 0; - } - if (cell_locktree(ci) != 0) { - cell_sunlocktree(ci); - cell_sunlocktree(cj); - return 0; - } - if (cell_locktree(cj) != 0) { - cell_sunlocktree(ci); - cell_sunlocktree(cj); - cell_unlocktree(ci); - return 0; - } } else if (subtype == task_subtype_limiter) { #ifdef SWIFT_TASKS_WITHOUT_ATOMICS if (ci->hydro.hold || cj->hydro.hold) return 0; @@ -1257,22 +1225,27 @@ void task_get_group_name(int type, int subtype, char *cluster) { case task_subtype_bh_feedback: strcpy(cluster, "BHFeedback"); break; - case task_subtype_rt_inject: - strcpy(cluster, "RTinject"); - break; case task_subtype_rt_gradient: - strcpy(cluster, "RTgradient"); + if (type == task_type_send || type == task_type_recv) { + strcpy(cluster, "None"); + } else { + strcpy(cluster, "RTgradient"); + } break; case task_subtype_rt_transport: - strcpy(cluster, "RTtransport"); + if (type == task_type_send || type == task_type_recv) { + strcpy(cluster, "None"); + } else { + strcpy(cluster, "RTtransport"); + } break; - case task_subtype_sink_compute_formation: + case task_subtype_sink_swallow: strcpy(cluster, "SinkFormation"); break; - case task_subtype_sink_merger: + case task_subtype_sink_do_sink_swallow: strcpy(cluster, "SinkMerger"); break; - case task_subtype_sink_accretion: + case task_subtype_sink_do_gas_swallow: strcpy(cluster, "SinkAccretion"); break; default: @@ -1523,8 +1496,8 @@ void task_dump_stats(const char *dumpfile, struct engine *e, 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 this step. */ - if (!e->sched.tasks[l].implicit && e->sched.tasks[l].tic > e->tic_step) { + /* Skip implicit tasks and tasks that have not ran. */ + if (!e->sched.tasks[l].implicit && e->sched.tasks[l].tic > 0) { int subtype = e->sched.tasks[l].subtype; double dt = e->sched.tasks[l].toc - e->sched.tasks[l].tic; @@ -1697,7 +1670,6 @@ void task_dump_active(struct engine *e) { fprintf(file_thread, "%i 0 none none -1 0 %lld %lld %lld %lld %lld 0 %lld\n", engine_rank, (long long int)e->tic_step, (long long int)e->toc_step, e->updates, e->g_updates, e->s_updates, cpufreq); - int count = 0; for (int l = 0; l < e->sched.nr_tasks; l++) { struct task *t = &e->sched.tasks[l]; @@ -1718,7 +1690,6 @@ void task_dump_active(struct engine *e) { (t->ci != NULL) ? t->ci->grav.count : 0, (t->cj != NULL) ? t->cj->grav.count : 0, t->flags); } - count++; } fclose(file_thread); @@ -1815,6 +1786,8 @@ enum task_categories task_get_category(const struct task *t) { case task_type_rt_transport_out: case task_type_rt_tchem: case task_type_rt_out: + case task_type_rt_sort: + case task_type_rt_advance_cell_time: return task_category_rt; case task_type_neutrino_weight: @@ -1851,12 +1824,11 @@ enum task_categories task_get_category(const struct task *t) { case task_subtype_bh_feedback: return task_category_black_holes; - case task_subtype_sink_compute_formation: - case task_subtype_sink_merger: - case task_subtype_sink_accretion: + case task_subtype_sink_swallow: + case task_subtype_sink_do_sink_swallow: + case task_subtype_sink_do_gas_swallow: return task_category_sink; - case task_subtype_rt_inject: case task_subtype_rt_gradient: case task_subtype_rt_transport: return task_category_rt; diff --git a/src/task.h b/src/task.h index 2725696f71dea423db2c303297015f62767515df..60d51ba706404d8413edd22f9baddc9fbc5cf478 100644 --- a/src/task.h +++ b/src/task.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 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) @@ -23,7 +23,7 @@ #ifndef SWIFT_TASK_H #define SWIFT_TASK_H -#include "../config.h" +#include <config.h> /* Includes. */ #include "align.h" @@ -40,7 +40,7 @@ struct engine; * @brief The different task types. * * Be sure to update the taskID_names array in tasks.c if you modify this list! - * Also update the python task plotting scripts! + * Also update the python3 task plotting scripts! */ enum task_types { task_type_none = 0, @@ -104,16 +104,20 @@ enum task_types { task_type_fof_self, task_type_fof_pair, task_type_neutrino_weight, - task_type_sink_in, /* Implicit */ - task_type_sink_ghost, /* Implicit */ - task_type_sink_out, /* Implicit */ - task_type_rt_in, /* Implicit */ - task_type_rt_out, /* Implicit */ + task_type_sink_in, /* Implicit */ + task_type_sink_ghost1, /* Implicit */ + task_type_sink_ghost2, /* Implicit */ + task_type_sink_out, /* Implicit */ + task_type_rt_in, /* Implicit */ + task_type_rt_out, /* Implicit */ task_type_sink_formation, task_type_rt_ghost1, task_type_rt_ghost2, task_type_rt_transport_out, /* Implicit */ task_type_rt_tchem, + task_type_rt_advance_cell_time, + task_type_rt_sort, + task_type_rt_collect_times, task_type_count } __attribute__((packed)); @@ -151,10 +155,9 @@ enum task_subtypes { task_subtype_do_gas_swallow, task_subtype_do_bh_swallow, task_subtype_bh_feedback, - task_subtype_sink_merger, - task_subtype_rt_inject, - task_subtype_sink_compute_formation, - task_subtype_sink_accretion, + task_subtype_sink_do_sink_swallow, + task_subtype_sink_swallow, + task_subtype_sink_do_gas_swallow, task_subtype_rt_gradient, task_subtype_rt_transport, task_subtype_count diff --git a/src/threadpool.c b/src/threadpool.c index 932c74a271f8c27d7ba64271017c0adb304c2ab1..85092b5d6a9f60a1ef8dab5a192b96ee68305e00 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -18,12 +18,13 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <float.h> #include <limits.h> #include <math.h> +#include <sched.h> #include <stdlib.h> #include <string.h> #ifdef SWIFT_DEBUG_THREADPOOL @@ -39,6 +40,18 @@ #include "error.h" #include "minmax.h" +/* Keys for thread specific data. */ +static pthread_key_t threadpool_tid; + +/* Affinity mask shared by all threads, and if set. */ +#ifdef HAVE_SETAFFINITY +static cpu_set_t thread_affinity; +static int thread_affinity_set = 0; +#endif + +/* Local declarations. */ +static void threadpool_apply_affinity_mask(void); + #ifdef SWIFT_DEBUG_THREADPOOL /** * @brief Store a log entry of the given chunk. @@ -134,6 +147,10 @@ void threadpool_dump_log(struct threadpool *tp, const char *filename, */ static void threadpool_chomp(struct threadpool *tp, int tid) { + /* Store the thread ID as thread specific data. */ + int localtid = tid; + pthread_setspecific(threadpool_tid, &localtid); + /* Loop until we can't get a chunk. */ while (1) { /* Compute the desired chunk size. */ @@ -161,19 +178,29 @@ static void threadpool_chomp(struct threadpool *tp, int tid) { #ifdef SWIFT_DEBUG_THREADPOOL ticks tic = getticks(); #endif + tp->map_function((char *)tp->map_data + (tp->map_data_stride * task_ind), chunk_size, tp->map_extra_data); + #ifdef SWIFT_DEBUG_THREADPOOL threadpool_log(tp, tid, chunk_size, tic, getticks()); #endif } } +/** + * @brief The thread start routine. Loops until told to exit. + * + * @param data the threadpool we are part of. + */ static void *threadpool_runner(void *data) { /* Our threadpool. */ struct threadpool *tp = (struct threadpool *)data; + /* Our affinity, if set. */ + threadpool_apply_affinity_mask(); + /* Main loop. */ while (1) { @@ -203,6 +230,13 @@ void threadpool_init(struct threadpool *tp, int num_threads) { /* Initialize the thread counters. */ tp->num_threads = num_threads; + /* Create thread local data areas. Only do this once for all threads. */ + pthread_key_create(&threadpool_tid, NULL); + + /* Store the main thread ID as thread specific data. */ + static int localtid = 0; + pthread_setspecific(threadpool_tid, &localtid); + #ifdef SWIFT_DEBUG_THREADPOOL if ((tp->logs = (struct mapper_log *)malloc(sizeof(struct mapper_log) * num_threads)) == NULL) @@ -388,3 +422,35 @@ void threadpool_clean(struct threadpool *tp) { free(tp->logs); #endif } + +/** + * @brief return the threadpool id of the current thread. + */ +int threadpool_gettid(void) { + int *tid = (int *)pthread_getspecific(threadpool_tid); + return *tid; +} + +#ifdef HAVE_SETAFFINITY +/** + * @brief set an affinity mask to be used for all threads. + * + * @param affinity the mask to use. + */ +void threadpool_set_affinity_mask(cpu_set_t *affinity) { + memcpy(&thread_affinity, affinity, sizeof(cpu_set_t)); + thread_affinity_set = 1; +} +#endif + +/** + * @brief apply the affinity mask the current thread, if set. + * + */ +static void threadpool_apply_affinity_mask(void) { +#ifdef HAVE_SETAFFINITY + if (thread_affinity_set) { + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &thread_affinity); + } +#endif +} diff --git a/src/threadpool.h b/src/threadpool.h index aff3be6991b09a6d868c9d4d3bc613e0b8b0ab79..c8127f43f893d184a625ad441d4e0d5a00188487 100644 --- a/src/threadpool.h +++ b/src/threadpool.h @@ -20,7 +20,7 @@ #define SWIFT_THREADPOOL_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard headers */ #include <pthread.h> @@ -99,7 +99,11 @@ void threadpool_init(struct threadpool *tp, int num_threads); void threadpool_map(struct threadpool *tp, threadpool_map_function map_function, void *map_data, size_t N, int stride, int chunk, void *extra_data); +int threadpool_gettid(void); void threadpool_clean(struct threadpool *tp); +#ifdef HAVE_SETAFFINITY +void threadpool_set_affinity_mask(cpu_set_t *entry_affinity); +#endif #ifdef SWIFT_DEBUG_THREADPOOL void threadpool_reset_log(struct threadpool *tp); void threadpool_dump_log(struct threadpool *tp, const char *filename, diff --git a/src/timeline.h b/src/timeline.h index 25605614be2aa079e96176cb6f0a221c828f5e3d..63164d43203501b9e74824aeabd8df2868466c7f 100644 --- a/src/timeline.h +++ b/src/timeline.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_TIMELINE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" @@ -56,7 +56,7 @@ typedef int8_t timebin_t; * @param bin The time bin of interest. */ __attribute__((const)) static INLINE integertime_t -get_integer_timestep(timebin_t bin) { +get_integer_timestep(const timebin_t bin) { if (bin <= 0) return 0; return 1LL << (bin + 1); @@ -71,9 +71,11 @@ get_integer_timestep(timebin_t bin) { * We use a fast (but exact for any non-zero value) logarithm in base 2 * calculation based on the bit representation of the number: * log_2(x) = (number of bits in the type) - (number of leading 0-bits in x) - 1 + * + * @param time_step An integer time-step length. */ __attribute__((const)) static INLINE timebin_t -get_time_bin(integertime_t time_step) { +get_time_bin(const integertime_t time_step) { /* ((int) log_2(time_step)) - 1 */ return (timebin_t)((8 * sizeof(integertime_t) - 2) - @@ -86,8 +88,8 @@ get_time_bin(integertime_t time_step) { * @param bin The time bin of interest. * @param time_base the minimal time-step size of the simulation. */ -__attribute__((const)) static INLINE double get_timestep(timebin_t bin, - double time_base) { +__attribute__((const)) static INLINE double get_timestep( + const timebin_t bin, const double time_base) { return get_integer_timestep(bin) * time_base; } @@ -95,12 +97,14 @@ __attribute__((const)) static INLINE double get_timestep(timebin_t bin, /** * @brief Returns the integer time corresponding to the start of the time-step * given by a time-bin. + * If the current time is a possible beginning for the given time-bin, return + * the current time minus the time-step size. * * @param ti_current The current time on the integer time line. * @param bin The time bin of interest. */ __attribute__((const)) static INLINE integertime_t -get_integer_time_begin(integertime_t ti_current, timebin_t bin) { +get_integer_time_begin(const integertime_t ti_current, const timebin_t bin) { const integertime_t dti = get_integer_timestep(bin); if (dti == 0) @@ -110,20 +114,27 @@ get_integer_time_begin(integertime_t ti_current, timebin_t bin) { } /** - * @brief Returns the integer time corresponding to the start of the time-step + * @brief Returns the integer time corresponding to the end of the time-step * given by a time-bin. + * If the current time is a possible end for the given time-bin, return the + * current time. * * @param ti_current The current time on the integer time line. * @param bin The time bin of interest. */ __attribute__((const)) static INLINE integertime_t -get_integer_time_end(integertime_t ti_current, timebin_t bin) { +get_integer_time_end(const integertime_t ti_current, const timebin_t bin) { const integertime_t dti = get_integer_timestep(bin); if (dti == 0) return 0; - else - return dti * ceil((double)ti_current / (double)dti); + else { + const integertime_t mod = ti_current % dti; + if (mod == 0) + return ti_current; + else + return ti_current - mod + dti; + } } /** @@ -132,7 +143,7 @@ get_integer_time_end(integertime_t ti_current, timebin_t bin) { * @param time The current point on the time line. */ __attribute__((const)) static INLINE timebin_t -get_max_active_bin(integertime_t time) { +get_max_active_bin(const integertime_t time) { if (time == 0) return num_time_bins; @@ -149,7 +160,7 @@ get_max_active_bin(integertime_t time) { * @param ti_old The last synchronisation point on the time line. */ __attribute__((const)) static INLINE timebin_t -get_min_active_bin(integertime_t ti_current, integertime_t ti_old) { +get_min_active_bin(const integertime_t ti_current, const integertime_t ti_old) { const timebin_t min_bin = get_max_active_bin(ti_current - ti_old); return min_bin; diff --git a/src/timers.c b/src/timers.c index 4fc5c5ffd43924cd98830935804190ad9cfe3d8b..2f1e04bf83e5363c7786b379f7d446c9c745942e 100644 --- a/src/timers.c +++ b/src/timers.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2016 John A. Regan (john.a.regan@durham.ac.uk) * Tom Theuns (tom.theuns@durham.ac.uk) * @@ -61,6 +61,7 @@ const char* timers_names[timer_count] = { "doself_bh_swallow", "doself_bh_feedback", "doself_grav_pp", + "doself_sink_swallow", "dopair_density", "dopair_gradient", "dopair_force", @@ -72,6 +73,7 @@ const char* timers_names[timer_count] = { "dopair_bh_feedback", "dopair_grav_mm", "dopair_grav_pp", + "dopair_sink_swallow", "dograv_external", "dograv_down", "dograv_mesh", @@ -87,6 +89,7 @@ const char* timers_names[timer_count] = { "dosub_self_bh_swallow", "dosub_self_bh_feedback", "dosub_self_grav", + "dosub_self_sink_swallow", "dosub_pair_density", "dosub_pair_gradient", "dosub_pair_force", @@ -97,6 +100,7 @@ const char* timers_names[timer_count] = { "dosub_pair_bh_swallow", "dosub_pair_bh_feedback", "dosub_pair_grav", + "dosub_pair_sink_swallow", "doself_subset", "dopair_subset", "dopair_subset_naive", @@ -125,10 +129,6 @@ const char* timers_names[timer_count] = { "fof_self", "fof_pair", "drift_sink", - "doself_rt_inject", - "dopair_rt_inject", - "dosub_self_rt_inject", - "dosub_pair_rt_inject", "rt_ghost1", "rt_ghost2", "doself_rt_gradient", @@ -140,6 +140,8 @@ const char* timers_names[timer_count] = { "dosub_self_rt_transport", "dosub_pair_rt_transport", "rt_tchem", + "rt_advance_cell_time", + "rt_collect_times", }; /* File to store the timers */ diff --git a/src/timers.h b/src/timers.h index 4af7947279beca21109bc01745c7afe392c5e6de..ad20f6a414e0b36f8a2118ee28cc8768f6e4fc0a 100644 --- a/src/timers.h +++ b/src/timers.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2016 John A. Regan (john.a.regan@durham.ac.uk) * Tom Theuns (tom.theuns@durham.ac.uk) * @@ -23,7 +23,7 @@ #define SWIFT_TIMERS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "atomic.h" @@ -61,6 +61,7 @@ enum { timer_doself_bh_swallow, timer_doself_bh_feedback, timer_doself_grav_pp, + timer_doself_sink_swallow, timer_dopair_density, timer_dopair_gradient, timer_dopair_force, @@ -72,6 +73,7 @@ enum { timer_dopair_bh_feedback, timer_dopair_grav_mm, timer_dopair_grav_pp, + timer_dopair_sink_swallow, timer_dograv_external, timer_dograv_down, timer_dograv_mesh, @@ -87,6 +89,7 @@ enum { timer_dosub_self_bh_swallow, timer_dosub_self_bh_feedback, timer_dosub_self_grav, + timer_dosub_self_sink_swallow, timer_dosub_pair_density, timer_dosub_pair_gradient, timer_dosub_pair_force, @@ -97,6 +100,7 @@ enum { timer_dosub_pair_bh_swallow, timer_dosub_pair_bh_feedback, timer_dosub_pair_grav, + timer_dosub_pair_sink_swallow, timer_doself_subset, timer_dopair_subset, timer_dopair_subset_naive, @@ -125,10 +129,6 @@ enum { timer_fof_self, timer_fof_pair, timer_drift_sink, - timer_doself_rt_inject, - timer_dopair_rt_inject, - timer_dosub_self_rt_inject, - timer_dosub_pair_rt_inject, timer_do_rt_ghost1, timer_do_rt_ghost2, timer_doself_rt_gradient, @@ -140,6 +140,8 @@ enum { timer_dosub_self_rt_transport, timer_dosub_pair_rt_transport, timer_do_rt_tchem, + timer_do_rt_advance_cell_time, + timer_do_rt_collect_times, timer_count, }; diff --git a/src/timestep.h b/src/timestep.h index 3c6af2a59e5442d0e6a482b280eb20700b72d8b7..851befc3b5f6c072bbfbb4b326abf09e08e060db 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,11 +20,12 @@ #define SWIFT_TIMESTEP_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "cooling.h" #include "debug.h" +#include "potential.h" #include "rt.h" #include "timeline.h" @@ -135,15 +136,20 @@ __attribute__((always_inline)) INLINE static integertime_t get_gpart_timestep( * @param p The #part. * @param xp The #xpart partner of p. * @param e The #engine (used to get some constants). + * @param new_dti_rt The new radiation integer time step. */ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( const struct part *restrict p, const struct xpart *restrict xp, - const struct engine *restrict e) { + const struct engine *restrict e, const integertime_t new_dti_rt) { /* Compute the next timestep (hydro condition) */ const float new_dt_hydro = hydro_compute_timestep(p, xp, e->hydro_properties, e->cosmology); + /* Compute the next timestep (MHD condition) */ + const float new_dt_mhd = + mhd_compute_timestep(p, xp, e->hydro_properties, e->cosmology); + /* Compute the next timestep (cooling condition) */ float new_dt_cooling = FLT_MAX; if (e->policy & engine_policy_cooling) @@ -172,13 +178,9 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( chemistry_timestep(e->physical_constants, e->cosmology, e->internal_units, e->hydro_properties, e->chemistry, p); - /* Get the RT timestep */ - float new_dt_radiation = FLT_MAX; - if (e->policy & engine_policy_rt) - new_dt_radiation = rt_compute_timestep(p, e->rt_props, e->cosmology); - - float new_dt = min5(new_dt_hydro, new_dt_cooling, new_dt_grav, - new_dt_chemistry, new_dt_radiation); + /* Take the minimum of all */ + float new_dt = min5(new_dt_hydro, new_dt_cooling, new_dt_grav, new_dt_mhd, + new_dt_chemistry); /* Limit change in smoothing length */ const float dt_h_change = @@ -202,10 +204,67 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( new_dt, e->dt_min); /* Convert to integer time */ - const integertime_t new_dti = make_integer_timestep( + integertime_t new_dti = make_integer_timestep( new_dt, p->time_bin, p->limiter_data.min_ngb_time_bin, e->ti_current, e->time_base_inv); + if (e->policy & engine_policy_rt) { + if (new_dti_rt <= new_dti) { + /* enforce dt_hydro <= nsubcycles * dt_rt. The rare case where + * new_dti_rt > new_dti will be handled in the parent function + * that calls this one. */ + const integertime_t max_subcycles = max(e->max_nr_rt_subcycles, 1); + new_dti = min(new_dti, new_dti_rt * max_subcycles); + } + } + + return new_dti; +} + +/** + * @brief Compute the new (integer) time-step of a given #part + * + * @param p The #part. + * @param xp The #xpart partner of p. + * @param e The #engine (used to get some constants). + */ +__attribute__((always_inline)) INLINE static integertime_t get_part_rt_timestep( + const struct part *restrict p, const struct xpart *restrict xp, + const struct engine *restrict e) { + + if (!(e->policy & engine_policy_rt)) + return get_integer_timestep(num_time_bins); + + float new_dt = + rt_compute_timestep(p, xp, e->rt_props, e->cosmology, e->hydro_properties, + e->physical_constants, e->internal_units); + + if ((e->policy & engine_policy_cosmology)) + error("Cosmology factor in get_part_rt_timestep not implemented yet"); + /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/ + /* new_dt = min(new_dt, e->dt_max_RMS_displacement); */ + + /* Apply cosmology correction (This is 1 if non-cosmological) */ + /* new_dt *= e->cosmology->time_step_factor; */ + + /* Limit timestep within the allowed range */ + new_dt = min(new_dt, e->dt_max); + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* Proper error will be caught in get_part_timestep(), so keep this as + * debugging check only. */ + const float f = (float)max(e->max_nr_rt_subcycles, 1); + if (new_dt < e->dt_min / f) + error( + "part (id=%lld) wants an RT time-step (%e) below dt_min/nr_subcycles " + "(%e)", + p->id, new_dt, e->dt_min / f); +#endif + + const integertime_t new_dti = make_integer_timestep( + new_dt, p->rt_time_data.time_bin, p->rt_time_data.min_ngb_time_bin, + e->ti_current, e->time_base_inv); + return new_dti; } diff --git a/src/timestep_limiter.h b/src/timestep_limiter.h index eee7b3e6596c90f936ae432f16470129497be045..fc6362334e86dd34fae1da83b7fe6a6dbd08d0c1 100644 --- a/src/timestep_limiter.h +++ b/src/timestep_limiter.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -20,7 +20,7 @@ #define SWIFT_TIMESTEP_LIMITER_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "kick.h" diff --git a/src/timestep_limiter_iact.h b/src/timestep_limiter_iact.h index 8e528f0f93f78c2b6a2b2e5af79097c9d597a462..586d0170c7ec272cd242c8ba02d08d1e241dc60c 100644 --- a/src/timestep_limiter_iact.h +++ b/src/timestep_limiter_iact.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/src/timestep_limiter_struct.h b/src/timestep_limiter_struct.h index 61b3afe7dba5422ebed01ccfda1b3317e9133c26..90989080928461b4d4843ba1da0f147e6a01e11e 100644 --- a/src/timestep_limiter_struct.h +++ b/src/timestep_limiter_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "timeline.h" diff --git a/src/timestep_sync.h b/src/timestep_sync.h index 658e7ecae3d20480a74032b2ba908e984a336de8..179fc361b28f703b31660d923baed867dad773ce 100644 --- a/src/timestep_sync.h +++ b/src/timestep_sync.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_TIMESTEP_SYNC_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "engine.h" @@ -134,7 +134,8 @@ INLINE static void timestep_process_sync_part(struct part *p, struct xpart *xp, /* The particle is now ready to compute its new time-step size and for the * next kick */ p->time_bin = -min_active_bin; - p->limiter_data.wakeup = time_bin_not_awake; + /* do not touch the limiter flag, as we might still need to limit the time + step of this particle (if the new time step is still too large) */ } #endif /* SWIFT_TIMESTEP_SYNC_H */ diff --git a/src/timestep_sync_part.h b/src/timestep_sync_part.h index 09042158f85ee3c68ee9cfae9eeff000b9478bf3..b62fc99326a4be0f81cc870125e373f5bf9949dd 100644 --- a/src/timestep_sync_part.h +++ b/src/timestep_sync_part.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_TIMESTEP_SYNC_PART_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /** * @brief Flag a particle for synchronization on the time-line. diff --git a/src/tools.c b/src/tools.c index 605d796e7e21b9ff95fe01e24151ac2b5318f974..022c3fb3b64aa432332e3e137e2ae7a220b91d3b 100644 --- a/src/tools.c +++ b/src/tools.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * Copyright (c) 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <ctype.h> @@ -47,10 +47,12 @@ #include "feedback.h" #include "gravity.h" #include "hydro.h" +#include "mhd.h" #include "part.h" #include "periodic.h" #include "pressure_floor_iact.h" #include "runner.h" +#include "sink.h" #include "star_formation_iact.h" #include "stars.h" @@ -160,6 +162,7 @@ void pairs_single_density(double *dim, long long int pid, printf("pairs_single: part[%i].id == %lli.\n", k, pid); hydro_init_part(&p, NULL); + mhd_init_part(&p); /* Loop over all particle pairs. */ for (k = 0; k < N; k++) { @@ -232,6 +235,8 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { runner_iact_nonsym_chemistry(r2, dx, hi, pj->h, pi, pj, a, H); runner_iact_nonsym_pressure_floor(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_sink(r2, dx, hi, pj->h, pi, pj, a, H, + e->sink_properties); } } } @@ -266,6 +271,8 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { runner_iact_nonsym_chemistry(r2, dx, hj, pi->h, pj, pi, a, H); runner_iact_nonsym_pressure_floor(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_sink(r2, dx, hj, pi->h, pj, pi, a, H, + e->sink_properties); } } } @@ -544,6 +551,8 @@ void self_all_density(struct runner *r, struct cell *ci) { runner_iact_nonsym_chemistry(r2, dxi, hi, hj, pi, pj, a, H); runner_iact_nonsym_pressure_floor(r2, dxi, hi, hj, pi, pj, a, H); runner_iact_nonsym_star_formation(r2, dxi, hi, hj, pi, pj, a, H); + runner_iact_nonsym_sink(r2, dxi, hi, hj, pi, pj, a, H, + e->sink_properties); } /* Hit or miss? */ @@ -558,6 +567,8 @@ void self_all_density(struct runner *r, struct cell *ci) { runner_iact_nonsym_chemistry(r2, dxi, hj, hi, pj, pi, a, H); runner_iact_nonsym_pressure_floor(r2, dxi, hj, hi, pj, pi, a, H); runner_iact_nonsym_star_formation(r2, dxi, hj, hi, pj, pi, a, H); + runner_iact_nonsym_sink(r2, dxi, hj, hi, pj, pi, a, H, + e->sink_properties); } } } @@ -720,6 +731,7 @@ void engine_single_density(double *dim, long long int pid, /* Clear accumulators. */ hydro_init_part(&p, NULL); + mhd_init_part(&p); /* Loop over all particle pairs (force). */ for (k = 0; k < N; k++) { @@ -742,6 +754,7 @@ void engine_single_density(double *dim, long long int pid, /* Dump the result. */ hydro_end_density(&p, cosmo); + mhd_end_density(&p, cosmo); message("part %lli (h=%e) has wcount=%e, rho=%e.", p.id, p.h, p.density.wcount, hydro_get_comoving_density(&p)); fflush(stdout); @@ -763,6 +776,7 @@ void engine_single_force(double *dim, long long int pid, /* Clear accumulators. */ hydro_reset_acceleration(&p); + mhd_reset_acceleration(&p); /* Loop over all particle pairs (force). */ for (k = 0; k < N; k++) { @@ -782,6 +796,7 @@ void engine_single_force(double *dim, long long int pid, if (r2 < p.h * p.h * kernel_gamma2 || r2 < parts[k].h * parts[k].h * kernel_gamma2) { hydro_reset_acceleration(&p); + mhd_reset_acceleration(&p); runner_iact_nonsym_force(r2, fdx, p.h, parts[k].h, &p, &parts[k], a, H); } } @@ -793,10 +808,12 @@ void engine_single_force(double *dim, long long int pid, } /** - * Returns a random number (uniformly distributed) in [a,b[ + * Returns a random number (uniformly distributed) in [a,b) + * + * This function is *not* thread-safe. */ -double random_uniform(double a, double b) { - return (rand() / (double)RAND_MAX) * (b - a) + a; +double random_uniform(const double a, const double b) { + return (rand() / (((double)RAND_MAX) + 1.0)) * (b - a) + a; } /** @@ -1013,7 +1030,7 @@ int compare_particles(struct part *a, struct part *b, double threshold) { * * @result memory use in Kb. */ -long get_maxrss() { +long get_maxrss(void) { struct rusage usage; getrusage(RUSAGE_SELF, &usage); return usage.ru_maxrss; diff --git a/src/tools.h b/src/tools.h index 8f7a65b7aa9acd60fe3dc6d37fe2b1589e61b133..2c3d87c67d029ffd3abbe5e6332e47963cde93fd 100644 --- a/src/tools.h +++ b/src/tools.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * Copyright (c) 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -23,6 +23,7 @@ #define SWIFT_TOOL_H #include "cell.h" +#include "error.h" #include "gravity_properties.h" #include "part.h" #include "physical_constants.h" @@ -66,4 +67,11 @@ char *trim_both(char *s); void safe_checkdir(const char *dir, int create); +#define check_snprintf(s, n, format, ...) \ + do { \ + int _len = snprintf(s, n, format, __VA_ARGS__); \ + if ((_len < 0) || (_len >= n)) \ + error("truncation of string with format %s", format); \ + } while (0) + #endif /* SWIFT_TOOL_H */ diff --git a/src/tracers.h b/src/tracers.h index 888d30af1172e1b8b639f8826b68067874a4f63a..98ee03111561aeb8f559108c1d22c142425b9366 100644 --- a/src/tracers.h +++ b/src/tracers.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -25,9 +25,9 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> -/* Import the right cooling definition */ +/* Import the right tracers definition */ #if defined(TRACERS_NONE) #include "./tracers/none/tracers.h" #elif defined(TRACERS_EAGLE) diff --git a/src/tracers/EAGLE/tracers.h b/src/tracers/EAGLE/tracers.h index 8856c7c565d20862a33bb465940a6badcd61adc9..6f135882acdcc1077c645228d154e9085ffcec74 100644 --- a/src/tracers/EAGLE/tracers.h +++ b/src/tracers/EAGLE/tracers.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2021 Edo Altamura (edoardo.altamura@manchester.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -21,11 +21,14 @@ #define SWIFT_TRACERS_EAGLE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ +#include "black_holes.h" #include "cooling.h" +#include "engine.h" #include "part.h" +#include "star_formation.h" #include "tracers_struct.h" /** @@ -77,7 +80,7 @@ static INLINE void tracers_after_drift( * @brief Update the particle tracers just after its time-step has been * computed. * - * In EAGLE we record the highest temperature reached. + * In EAGLE we record the highest temperature reached and the average SFR. * * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data (containing the tracers @@ -89,12 +92,16 @@ static INLINE void tracers_after_drift( * @param hydro_props the hydro_props struct * @param cooling The #cooling_function_data used in the run. * @param time The current time. + * @param time_step_length The length of the step that just finished + * @param tracers_triggers_started Which triggers have started? (array of size + * num_snapshot_triggers_part) */ -static INLINE void tracers_after_timestep( +static INLINE void tracers_after_timestep_part( const struct part *p, struct xpart *xp, const struct unit_system *us, const struct phys_const *phys_const, const int with_cosmology, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const struct cooling_function_data *cooling, const double time) { + const struct cooling_function_data *cooling, const double time, + const double time_step_length, const int *const tracers_triggers_started) { /* Current temperature */ const float temperature = cooling_get_temperature(phys_const, hydro_props, us, @@ -111,6 +118,69 @@ static INLINE void tracers_after_timestep( xp->tracers_data.maximum_temperature_time = time; } } + + /* Accumulate average SFR */ + for (int i = 0; i < num_snapshot_triggers_part; ++i) { + if (tracers_triggers_started[i]) + xp->tracers_data.averaged_SFR[i] += + star_formation_get_SFR(p, xp) * time_step_length; + } +} + +/** + * @brief Update the star particle tracers just after its time-step has been + * computed. + * + * In EAGLE, nothing to do. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The current cosmological model. + * @param time_step_length The length of the step that just finished + * @param tracers_triggers_started Which triggers have started? (array of size + * num_snapshot_triggers_spart) + */ +static INLINE void tracers_after_timestep_spart( + struct spart *sp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const double time_step_length, + const int *const tracers_triggers_started) {} + +/** + * @brief Update the black hole particle tracers just after its time-step has + * been computed. + * + * In EAGLE, we record the average accr. rate. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The current cosmological model. + * @param time_step_length The length of the step that just finished + * @param tracers_triggers_started Which triggers have started? (array of size + * num_snapshot_triggers_bpart) + */ +static INLINE void tracers_after_timestep_bpart( + struct bpart *bp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const double time_step_length, + const int *const tracers_triggers_started) { + + const float accr_rate = black_holes_get_accretion_rate(bp); + + /* Accumulate average accretion rate */ + for (int i = 0; i < num_snapshot_triggers_part; ++i) { + if (tracers_triggers_started[i]) + bp->tracers_data.averaged_accretion_rate[i] += + accr_rate * time_step_length; + } } /** @@ -146,6 +216,41 @@ static INLINE void tracers_first_init_xpart( xp->tracers_data.density_at_last_AGN_feedback_event = -1.f; } +/** + * @brief Initialise the star tracer data at the start of a calculation. + * + * In EAGLE, nothing to do. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + */ +static INLINE void tracers_first_init_spart(struct spart *sp, + const struct unit_system *us, + const struct phys_const *phys_const, + const struct cosmology *cosmo) {} + +/** + * @brief Initialise the black hole tracer data at the start of a calculation. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + */ +static INLINE void tracers_first_init_bpart(struct bpart *bp, + const struct unit_system *us, + const struct phys_const *phys_const, + const struct cosmology *cosmo) { + for (int i = 0; i < num_snapshot_triggers_bpart; ++i) + bp->tracers_data.averaged_accretion_rate[i] = 0.f; +} + /** * @brief Update the particles' tracer data after a stellar feedback * event. @@ -210,6 +315,53 @@ static INLINE void tracers_after_black_holes_feedback( xp->tracers_data.AGN_feedback_energy += delta_energy; } +static INLINE void tracers_after_jet_feedback( + const struct part *p, struct xpart *xp, const int with_cosmology, + const float scale_factor, const double time, const double delta_energy) { + + if (with_cosmology) + xp->tracers_data.last_AGN_jet_feedback_scale_factor = scale_factor; + else + xp->tracers_data.last_AGN_jet_feedback_time = time; + xp->tracers_data.hit_by_jet_feedback++; + xp->tracers_data.jet_feedback_energy += delta_energy; +} + +/** + * @brief Tracer event called after a snapshot was written. + * + * @param p the #part. + * @param xp the #xpart. + */ +static INLINE void tracers_after_snapshot_part(const struct part *p, + struct xpart *xp) { + + for (int i = 0; i < num_snapshot_triggers_part; ++i) + xp->tracers_data.averaged_SFR[i] = 0.f; +} + +/** + * @brief Tracer event called after a snapshot was written. + * + * @param sp the #spart. + */ +static INLINE void tracers_after_snapshot_spart(struct spart *sp) { + + for (int i = 0; i < num_snapshot_triggers_part; ++i) + sp->tracers_data.averaged_SFR[i] = 0.f; +} + +/** + * @brief Tracer event called after a snapshot was written. + * + * @param bp the #bpart. + */ +static INLINE void tracers_after_snapshot_bpart(struct bpart *bp) { + + for (int i = 0; i < num_snapshot_triggers_bpart; ++i) + bp->tracers_data.averaged_accretion_rate[i] = 0.f; +} + /** * @brief Split the tracer content of a particle into n pieces * diff --git a/src/tracers/EAGLE/tracers_debug.h b/src/tracers/EAGLE/tracers_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..01bcc58e57222df5780f694aac964a48e25c9ae0 --- /dev/null +++ b/src/tracers/EAGLE/tracers_debug.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_TRACERS_EAGLE_DEBUG_H +#define SWIFT_TRACERS_EAGLE_DEBUG_H + +__attribute__((always_inline)) INLINE static void tracers_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] tracers_xpart_data:", p->id); + warning( + "[PID%lld] maximum_temperature = %.3e, " + "maximum_temperature_scale_factor/time = %.3e, " + "last_AGN_injection_scale_factor/time = %.3e, " + "density_before_last_AGN_feedback_event = %.3e, " + "entropy_before_last_AGN_feedback_event = %.3e, " + "density_at_last_AGN_feedback_event = %.3e, " + "entropy_at_last_AGN_feedback_event = %.3e, AGN_feedback_energy = " + "%.3e, " + "hit_by_SNII_feedback = %d, hit_by_AGN_feedback = %d", + p->id, xp->tracers_data.maximum_temperature, + xp->tracers_data.maximum_temperature_scale_factor, + xp->tracers_data.last_AGN_injection_scale_factor, + xp->tracers_data.density_before_last_AGN_feedback_event, + xp->tracers_data.entropy_before_last_AGN_feedback_event, + xp->tracers_data.density_at_last_AGN_feedback_event, + xp->tracers_data.entropy_at_last_AGN_feedback_event, + xp->tracers_data.AGN_feedback_energy, + xp->tracers_data.hit_by_SNII_feedback, + xp->tracers_data.hit_by_AGN_feedback); + } +} + +#endif /* SWIFT_TRACERS_EAGLE_DEBUG_H */ diff --git a/src/tracers/EAGLE/tracers_io.h b/src/tracers/EAGLE/tracers_io.h index 252645f21ad3aa7ae4fa41817f7ae09e049f4aa5..9997449151cba56ffd542c2c2d9f28a5e1586e60 100644 --- a/src/tracers/EAGLE/tracers_io.h +++ b/src/tracers/EAGLE/tracers_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2021 Edo Altamura (edoardo.altamura@manchester.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ #define SWIFT_TRACERS_EAGLE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "io_properties.h" @@ -41,6 +41,48 @@ __attribute__((always_inline)) INLINE static void tracers_write_flavour( } #endif +INLINE static void convert_part_averaged_SFR(const struct engine* e, + const struct part* p, + const struct xpart* xp, + float* ret) { + + for (int i = 0; i < num_snapshot_triggers_part; ++i) { + if (e->snapshot_recording_triggers_started_part[i]) + ret[i] = xp->tracers_data.averaged_SFR[i] / + e->snapshot_recording_triggers_part[i]; + else + ret[i] = 0.f; + } +} + +INLINE static void convert_spart_averaged_SFR(const struct engine* e, + const struct spart* sp, + float* ret) { + + /* Note: We use the 'part' trigger here as the SF would have started when the + * particle was still in gas form */ + for (int i = 0; i < num_snapshot_triggers_part; ++i) { + if (e->snapshot_recording_triggers_started_part[i]) + ret[i] = sp->tracers_data.averaged_SFR[i] / + e->snapshot_recording_triggers_part[i]; + else + ret[i] = 0.f; + } +} + +INLINE static void convert_bpart_averaged_accretion_rate(const struct engine* e, + const struct bpart* bp, + float* ret) { + + for (int i = 0; i < num_snapshot_triggers_bpart; ++i) { + if (e->snapshot_recording_triggers_started_bpart[i]) + ret[i] = bp->tracers_data.averaged_accretion_rate[i] / + e->snapshot_recording_triggers_bpart[i]; + else + ret[i] = 0.f; + } +} + /** * @brief Specifies which particle fields to write to a dataset * @@ -139,7 +181,13 @@ __attribute__((always_inline)) INLINE static int tracers_write_particles( "particle has never been hit by feedback"); } - return 10; + list[10] = io_make_output_field_convert_part( + "AveragedStarFormationRates", FLOAT, 2, UNIT_CONV_SFR, 0.f, parts, xparts, + convert_part_averaged_SFR, + "Star formation rates of the particles averaged over the period set by " + "the first two snapshot triggers"); + + return 11; } __attribute__((always_inline)) INLINE static int tracers_write_sparticles( @@ -237,7 +285,28 @@ __attribute__((always_inline)) INLINE static int tracers_write_sparticles( "-1 if a particle has never been hit by feedback"); } - return 10; + list[10] = io_make_output_field_convert_spart( + "AveragedStarFormationRates", FLOAT, num_snapshot_triggers_part, + UNIT_CONV_SFR, 0.f, sparts, convert_spart_averaged_SFR, + "Star formation rates of the particles averaged over the period set by " + "the first two snapshot triggers when the particle was still a gas " + "particle."); + + return 11; +} + +__attribute__((always_inline)) INLINE static int tracers_write_bparticles( + const struct bpart* bparts, struct io_props* list, + const int with_cosmology) { + + list[0] = io_make_output_field_convert_bpart( + "AveragedAccretionRates", FLOAT, num_snapshot_triggers_bpart, + UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, bparts, + convert_bpart_averaged_accretion_rate, + "Accretion rates of the black holes averaged over the period set by the " + "first two snapshot triggers"); + + return 1; } #endif /* SWIFT_TRACERS_EAGLE_IO_H */ diff --git a/src/tracers/EAGLE/tracers_struct.h b/src/tracers/EAGLE/tracers_struct.h index 0afe30dc321527bd5f405218e107a521866c6f15..d1203ddd9fc54dd8d050c966da119873b64ab649 100644 --- a/src/tracers/EAGLE/tracers_struct.h +++ b/src/tracers/EAGLE/tracers_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2021 Edo Altamura (edoardo.altamura@manchester.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,6 +20,9 @@ #ifndef SWIFT_TRACERS_STRUCT_EAGLE_H #define SWIFT_TRACERS_STRUCT_EAGLE_H +/* Local includes */ +#include "tracers_triggers.h" + /** * @brief Properties of the tracers stored in the extended particle data. */ @@ -47,6 +50,18 @@ struct tracers_xpart_data { float last_AGN_injection_time; }; + union { + + /* Last scale factor this particle was kicked as part of jet feedback */ + float last_AGN_jet_feedback_scale_factor; + + /* Last time this particle was kicked as part of jet feedback */ + float last_AGN_jet_feedback_time; + }; + + /*! Averaged SFR over two different time slices */ + float averaged_SFR[num_snapshot_triggers_part]; + /*! Density of the gas before the last AGN feedback event * (physical internal units) */ float density_before_last_AGN_feedback_event; @@ -67,6 +82,13 @@ struct tracers_xpart_data { * (physical units) */ float AGN_feedback_energy; + /*! Total jet feedback energy received by this particle */ + float jet_feedback_energy; + + /*! Counts how many times this particle has been kicked as part of + jet feedback */ + char hit_by_jet_feedback; + /*! Has this particle been hit by SNII feedback? */ char hit_by_SNII_feedback; @@ -74,4 +96,20 @@ struct tracers_xpart_data { char hit_by_AGN_feedback; }; +/** + * @brief Properties of the tracers stored in the star particle data. + * + * Note: In this model, they are identical to the xpart data. + */ +#define tracers_spart_data tracers_xpart_data + +/** + * @brief Properties of the tracers stored in the black hole particle data. + */ +struct tracers_bpart_data { + + /*! Averaged accretion rate over two different time slices */ + float averaged_accretion_rate[num_snapshot_triggers_bpart]; +}; + #endif /* SWIFT_TRACERS_STRUCT_EAGLE_H */ diff --git a/src/tracers/none/tracers.h b/src/tracers/none/tracers.h index 706fac7a359c6e8b84d19397a216d3e4ce83c864..29c0017def96d2565c3e47c2394fde5c771ccc19 100644 --- a/src/tracers/none/tracers.h +++ b/src/tracers/none/tracers.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_TRACERS_NONE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "cooling.h" @@ -72,22 +72,74 @@ static INLINE void tracers_after_drift( * @brief Update the particle tracers just after its time-step has been * computed. * - * Nothing to do here in the EAGLE model. + * Nothing to do here. * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). * @param us The internal system of units. * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? * @param cosmo The current cosmological model. * @param hydro_props the hydro_props struct * @param cooling The #cooling_function_data used in the run. + * @param time The current time. + * @param time_step_length The length of the step that just finished + * @param tracers_triggers_started Which triggers have started? (array of size + * num_snapshot_triggers_part) + */ +static INLINE void tracers_after_timestep_part( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling, const double time, + const double time_step_length, const int *const tracers_triggers_started) {} + +/** + * @brief Update the star particle tracers just after its time-step has been + * computed. + * + * Nothing to do here. + * * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data (containing the tracers * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The current cosmological model. + * @param time_step_length The length of the step that just finished + * @param tracers_triggers_started Which triggers have started? (array of size + * num_snapshot_triggers_spart) */ -static INLINE void tracers_after_timestep( - const struct part *p, struct xpart *xp, const struct unit_system *us, +static INLINE void tracers_after_timestep_spart( + struct spart *sp, const struct unit_system *us, const struct phys_const *phys_const, const int with_cosmology, - const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const struct cooling_function_data *cooling, const double time) {} + const struct cosmology *cosmo, const double time_step_length, + const int *const tracers_triggers_started) {} + +/** + * @brief Update the black hole particle tracers just after its time-step has + * been computed. + * + * Nothing to do here. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The current cosmological model. + * @param time_step_length The length of the step that just finished + * @param tracers_triggers_started Which triggers have started? (array of size + * num_snapshot_triggers_bpart) + */ +static INLINE void tracers_after_timestep_bpart( + struct bpart *bp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const double time_step_length, + const int *const tracers_triggers_started) {} /** * @brief Initialise the tracer data at the start of a calculation. @@ -107,6 +159,40 @@ static INLINE void tracers_first_init_xpart( const struct hydro_props *hydro_props, const struct cooling_function_data *cooling) {} +/** + * @brief Initialise the star tracer data at the start of a calculation. + * + * Nothing to do here. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + */ +static INLINE void tracers_first_init_spart(struct spart *sp, + const struct unit_system *us, + const struct phys_const *phys_const, + const struct cosmology *cosmo) {} + +/** + * @brief Initialise the black hole tracer data at the start of a calculation. + * + * Nothing to do here. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + */ +static INLINE void tracers_first_init_bpart(struct bpart *bp, + const struct unit_system *us, + const struct phys_const *phys_const, + const struct cosmology *cosmo) {} + /** * @brief Update the particles' tracer data after a stellar feedback * event. @@ -149,6 +235,35 @@ static INLINE void tracers_after_black_holes_feedback( const struct part *p, struct xpart *xp, const int with_cosmology, const float scale_factor, const double time, const double delta_energy) {} +/** + * @brief Tracer event called after a snapshot was written. + * + * Nothing to do here. + * + * @param p the #part. + * @param xp the #xpart. + */ +static INLINE void tracers_after_snapshot_part(const struct part *p, + struct xpart *xp) {} + +/** + * @brief Tracer event called after a snapshot was written. + * + * Nothing to do here. + * + * @param sp the #spart. + */ +static INLINE void tracers_after_snapshot_spart(struct spart *sp) {} + +/** + * @brief Tracer event called after a snapshot was written. + * + * Nothing to do here. + * + * @param sp the #spart. + */ +static INLINE void tracers_after_snapshot_bpart(struct bpart *bp) {} + /** * @brief Split the tracer content of a particle into n pieces * diff --git a/src/tracers/none/tracers_debug.h b/src/tracers/none/tracers_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..782ee944d3401afb474f7e09ac354d9da5325822 --- /dev/null +++ b/src/tracers/none/tracers_debug.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_TRACERS_NONE_DEBUG_H +#define SWIFT_TRACERS_NONE_DEBUG_H + +__attribute__((always_inline)) INLINE static void tracers_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_TRACERS_NONE_DEBUG_H */ diff --git a/src/tracers/none/tracers_io.h b/src/tracers/none/tracers_io.h index f81c142fa8f828dcaa2a29b6040a81c0eca3c740..a5fa95eaa7807b71af8f0333951dca8d562380d4 100644 --- a/src/tracers/none/tracers_io.h +++ b/src/tracers/none/tracers_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -20,7 +20,7 @@ #define SWIFT_TRACERS_NONE_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes */ #include "io_properties.h" @@ -63,4 +63,12 @@ __attribute__((always_inline)) INLINE static int tracers_write_sparticles( return 0; } + +__attribute__((always_inline)) INLINE static int tracers_write_bparticles( + const struct bpart* bparts, struct io_props* list, + const int with_cosmology) { + + return 0; +} + #endif /* SWIFT_TRACERS_NONE_IO_H */ diff --git a/src/tracers/none/tracers_struct.h b/src/tracers/none/tracers_struct.h index b13539917eef888ade3f9056e245ebb7f963f4d1..16c5d273a8062c3f31f6c35d1dba8f69fa3d1bcd 100644 --- a/src/tracers/none/tracers_struct.h +++ b/src/tracers/none/tracers_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -24,4 +24,14 @@ */ struct tracers_xpart_data {}; +/** + * @brief Properties of the tracers stored in the star particle data. + */ +struct tracers_spart_data {}; + +/** + * @brief Properties of the tracers stored in the black hole particle data. + */ +struct tracers_bpart_data {}; + #endif /* SWIFT_TRACERS_STRUCT_NONE_H */ diff --git a/src/tracers_debug.h b/src/tracers_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..3f99eaff3c9a1431282b10f1cca86fa5180e1bb0 --- /dev/null +++ b/src/tracers_debug.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * + * 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_TRACERS_DEBUG_H +#define SWIFT_TRACERS_DEBUG_H + +/* Config parameters. */ +#include <config.h> + +/* Import the debug routines of the right tracers definition */ +#if defined(TRACERS_NONE) +#include "./tracers/none/tracers_debug.h" +#elif defined(TRACERS_EAGLE) +#include "./tracers/EAGLE/tracers_debug.h" +#else +#error "Invalid choice of tracers." +#endif + +#endif /* SWIFT_TRACERS_DEBUG_H */ diff --git a/src/tracers_io.h b/src/tracers_io.h index 87ca8bb1ffa22017aa825c2760fe42eca4c2888b..6b92e75cf0c3d4b68f90ba2339352133c6bf033d 100644 --- a/src/tracers_io.h +++ b/src/tracers_io.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right cooling definition */ #if defined(TRACERS_NONE) diff --git a/src/tracers_struct.h b/src/tracers_struct.h index 48af73c2e577f7df3b0fed291b1517692f6437bd..12584e9efd36d0624adb752a542a3fbbdc5fd2b3 100644 --- a/src/tracers_struct.h +++ b/src/tracers_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -25,7 +25,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Import the right cooling definition */ #if defined(TRACERS_NONE) diff --git a/src/tracers_triggers.h b/src/tracers_triggers.h new file mode 100644 index 0000000000000000000000000000000000000000..6d171208f720f3f30a6f4546f83d1a82bf43479c --- /dev/null +++ b/src/tracers_triggers.h @@ -0,0 +1,43 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 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_TRACERS_TRIGGERS_H +#define SWIFT_TRACERS_TRIGGERS_H + +/** + * @file src/tracers_triggers.h + * @brief Branches between the different particle data tracers + */ + +/* Config parameters. */ +#include <config.h> + +/* Import the right tracers definition */ +#if defined(TRACERS_NONE) +#define num_snapshot_triggers_part 0 +#define num_snapshot_triggers_spart 0 +#define num_snapshot_triggers_bpart 0 +#elif defined(TRACERS_EAGLE) +#define num_snapshot_triggers_part 2 +#define num_snapshot_triggers_spart 0 +#define num_snapshot_triggers_bpart 2 +#else +#error "Invalid choice of tracers." +#endif + +#endif /* SWIFT_TRACERS_TRIGGERS_H */ diff --git a/src/units.c b/src/units.c index 3b5468f69de710d284707cfd415f6867cf624ed2..76914bbe532949692f9742dda115348c5fb0865d 100644 --- a/src/units.c +++ b/src/units.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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 @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <math.h> @@ -255,6 +255,7 @@ void units_get_base_unit_exponents_array(float baseUnitsExp[5], case UNIT_CONV_FREQUENCY: case UNIT_CONV_SSFR: + case UNIT_CONV_PHOTONS_PER_TIME: baseUnitsExp[UNIT_TIME] = -1.f; break; @@ -317,6 +318,12 @@ void units_get_base_unit_exponents_array(float baseUnitsExp[5], baseUnitsExp[UNIT_TIME] = -2.f; break; + case UNIT_CONV_ENERGY_VELOCITY: + baseUnitsExp[UNIT_MASS] = 1.f; + baseUnitsExp[UNIT_LENGTH] = 3.f; + baseUnitsExp[UNIT_TIME] = -3.f; + break; + case UNIT_CONV_ENTROPY: baseUnitsExp[UNIT_MASS] = 2.f - hydro_gamma; baseUnitsExp[UNIT_LENGTH] = 3.f * hydro_gamma - 1.f; @@ -400,6 +407,19 @@ void units_get_base_unit_exponents_array(float baseUnitsExp[5], baseUnitsExp[UNIT_CURRENT] = -2.f; break; + case UNIT_CONV_MAGNETIC_HELICITY: + baseUnitsExp[UNIT_MASS] = 2.f; + baseUnitsExp[UNIT_LENGTH] = 1.f; + baseUnitsExp[UNIT_TIME] = -4.f; + baseUnitsExp[UNIT_CURRENT] = -2.f; + break; + + case UNIT_CONV_MAGNETIC_CROSS_HELICITY: + baseUnitsExp[UNIT_MASS] = 2.f; + baseUnitsExp[UNIT_TIME] = -3.f; + baseUnitsExp[UNIT_CURRENT] = -1.f; + break; + case UNIT_CONV_TEMPERATURE: baseUnitsExp[UNIT_TEMPERATURE] = 1.f; break; @@ -440,21 +460,42 @@ void units_get_base_unit_exponents_array(float baseUnitsExp[5], baseUnitsExp[UNIT_TIME] = -3.f; break; + case UNIT_CONV_ENERGY_DENSITY: + baseUnitsExp[UNIT_MASS] = 1.f; + baseUnitsExp[UNIT_LENGTH] = -1.f; + baseUnitsExp[UNIT_TIME] = -2.f; + break; + case UNIT_CONV_INV_TIME: baseUnitsExp[UNIT_TIME] = -1.f; break; + case UNIT_CONV_INV_AREA: + baseUnitsExp[UNIT_LENGTH] = -2.f; + break; + case UNIT_CONV_POWER_DENSITY: baseUnitsExp[UNIT_MASS] = 1.f; baseUnitsExp[UNIT_LENGTH] = -1.f; baseUnitsExp[UNIT_TIME] = -3.f; break; + case UNIT_CONV_GASOLINE_DIFF_RATE: case UNIT_CONV_THERMAL_DIFFUSIVITY: baseUnitsExp[UNIT_LENGTH] = 2.f; baseUnitsExp[UNIT_TIME] = -1.f; break; + case UNIT_CONV_NUMBER_DENSITY_PER_TIME: + baseUnitsExp[UNIT_LENGTH] = -3.f; + baseUnitsExp[UNIT_TIME] = -1.f; + break; + + case UNIT_CONV_PHOTON_FLUX_PER_UNIT_SURFACE: + baseUnitsExp[UNIT_LENGTH] = -2.f; + baseUnitsExp[UNIT_TIME] = -1.f; + break; + default: error("Invalid choice of pre-defined units"); break; diff --git a/src/units.h b/src/units.h index 2a34d23896e76d144529b4ada1ac601f420986a7..84c28f578f59eda448d98b437a5e1129070ed72d 100644 --- a/src/units.h +++ b/src/units.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 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 @@ -20,7 +20,7 @@ #define SWIFT_UNITS_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "parser.h" @@ -82,6 +82,7 @@ enum unit_conversion_factor { UNIT_CONV_TIDAL_TENSOR, UNIT_CONV_ENERGY, UNIT_CONV_ENERGY_PER_UNIT_MASS, + UNIT_CONV_ENERGY_VELOCITY, UNIT_CONV_ENTROPY, UNIT_CONV_ENTROPY_PER_UNIT_MASS, UNIT_CONV_PHYSICAL_ENTROPY_PER_UNIT_MASS, @@ -97,6 +98,8 @@ enum unit_conversion_factor { UNIT_CONV_MAGNETIC_FLUX, UNIT_CONV_MAGNETIC_FIELD, UNIT_CONV_MAGNETIC_INDUCTANCE, + UNIT_CONV_MAGNETIC_HELICITY, + UNIT_CONV_MAGNETIC_CROSS_HELICITY, UNIT_CONV_TEMPERATURE, UNIT_CONV_AREA, UNIT_CONV_VOLUME, @@ -108,11 +111,17 @@ enum unit_conversion_factor { UNIT_CONV_MASS_PER_UNIT_TIME, UNIT_CONV_VELOCITY_SQUARED, UNIT_CONV_INV_TIME, + UNIT_CONV_INV_AREA, UNIT_CONV_RADIATION_FLUX, UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE, UNIT_CONV_ENERGY_FLUX_DENSITY, + UNIT_CONV_ENERGY_DENSITY, UNIT_CONV_POWER_DENSITY, + UNIT_CONV_GASOLINE_DIFF_RATE, UNIT_CONV_THERMAL_DIFFUSIVITY, + UNIT_CONV_NUMBER_DENSITY_PER_TIME, + UNIT_CONV_PHOTONS_PER_TIME, + UNIT_CONV_PHOTON_FLUX_PER_UNIT_SURFACE, }; void units_init_cgs(struct unit_system*); diff --git a/src/vector.h b/src/vector.h index a1ecddc6ed68ef659759665f15f25aa7e32dc908..0c8c2c7c798270342b55fb23b209d3f6bd079e75 100644 --- a/src/vector.h +++ b/src/vector.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 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 @@ -21,7 +21,7 @@ #define SWIFT_VECTOR_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers */ #include "inline.h" diff --git a/src/vector_power.h b/src/vector_power.h index d2adf2cbfa719bc0631b91692f45b33038abdf19..171d300a19eb7ed24723876f3dec4084f7f3ce43 100644 --- a/src/vector_power.h +++ b/src/vector_power.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2016 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 @@ -32,7 +32,7 @@ */ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "inline.h" diff --git a/src/velociraptor_dummy.c b/src/velociraptor_dummy.c index 36cb65bfbe6931464f33d7e4b641f8882fdf65d0..03f3469accc816684485134aedc5870e8c6439ab 100644 --- a/src/velociraptor_dummy.c +++ b/src/velociraptor_dummy.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local includes. */ #include "error.h" diff --git a/src/velociraptor_interface.c b/src/velociraptor_interface.c index afbd1fb2421575a9964b0b0c2796ad5723e6fa08..fab15248c16f0c775fb751fc6fbba99f8c98a1cb 100644 --- a/src/velociraptor_interface.c +++ b/src/velociraptor_interface.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <hdf5.h> diff --git a/src/velociraptor_interface.h b/src/velociraptor_interface.h index 2547fa56c1677e93b1c59a1435e9a6ab92c1f308..0adf8ca45dc65ead58da0499fb11030879dea6e3 100644 --- a/src/velociraptor_interface.h +++ b/src/velociraptor_interface.h @@ -20,7 +20,7 @@ #define SWIFT_VELOCIRAPTOR_INTERFACE_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Forward declaration */ struct engine; diff --git a/src/velociraptor_io.h b/src/velociraptor_io.h index 446fc3131bfc638a0b37307110a2e802c3dd01ea..dc912e81c4848d6340afcb3a8332857ef9c36e52 100644 --- a/src/velociraptor_io.h +++ b/src/velociraptor_io.h @@ -20,7 +20,7 @@ #define SWIFT_VELOCIRAPTOR_IO_H /* Config parameters. */ -#include "../config.h" +#include <config.h> INLINE static void velociraptor_convert_part_groupID(const struct engine* e, const struct part* p, diff --git a/src/velociraptor_struct.h b/src/velociraptor_struct.h index b998263a6ba2fe0aaa6552f274cb8f4ee85d3b1c..cad5f711510174724cb307e42785522654d3eaf5 100644 --- a/src/velociraptor_struct.h +++ b/src/velociraptor_struct.h @@ -20,7 +20,7 @@ #define SWIFT_VELOCIRAPTOR_STRUCT_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /** * @brief Data returned by VELOCIraptor for each #gpart. diff --git a/src/version.c b/src/version.c index edb227bbf4a8bd3f8d5a50f53f08411b415e341e..9d8642573d3225a98af0bf6db20e774125af0bb9 100644 --- a/src/version.c +++ b/src/version.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2012 Matthieu Schaller (schaller@strw.leidenuniv.nl). * Copyright (C) 2015 Peter W. Draper (p.w.draper@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* MPI headers. */ #ifdef WITH_MPI diff --git a/src/version.h b/src/version.h index b8f0a6de1b822b2e3f80d7696dd36e53d3db7dd9..379537a2617a44be75010a7b1e6b199ec3f01fde 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 Matthieu Schaller (schaller@strw.leidenuniv.nl). * Copyright (c) 2015 Peter W. Draper (p.w.draper@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify @@ -36,6 +36,7 @@ const char* parmetis_version(void); const char* hdf5_version(void); const char* fftw3_version(void); const char* libgsl_version(void); +const char* sundials_version(void); const char* thread_barrier_version(void); const char* allocator_version(void); void greetings(int fof); diff --git a/src/version_string.h.in b/src/version_string.h.in index cea9f189ea4c68a483b7715cc43acd9a8cc26037..c39eaff7ac8e32597e90b8b31beb2df63131d8ba 100644 --- a/src/version_string.h.in +++ b/src/version_string.h.in @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2012 Matthieu Schaller (schaller@strw.leidenuniv.nl). * Copyright (c) 2015 Peter W. Draper (p.w.draper@durham.ac.uk). * * This program is free software: you can redistribute it and/or modify diff --git a/src/xmf.c b/src/xmf.c index 3168743df46017af6d9fa05414012525633803b1..b212d92d2d2cc5bbeeab9bc2f3b423ebf7a7ea60 100644 --- a/src/xmf.c +++ b/src/xmf.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2017 Matthieu Schaller (schaller@strw.leidenuniv.nl). * Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <libgen.h> diff --git a/src/xmf.h b/src/xmf.h index ecf09ca370712bee0ab3a984751283903404c02d..7cc57ec67030dc8f2caf58dbc7d638b2d3518416 100644 --- a/src/xmf.h +++ b/src/xmf.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (c) 2017 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 @@ -20,7 +20,7 @@ #define SWIFT_XMF_H /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "common_io.h" diff --git a/examples/main.c b/swift.c similarity index 90% rename from examples/main.c rename to swift.c index 7ea08228e1527860ae6f058287cc21a721c57541..130cdc871d1865757c01ece10b5fd265061a3324 100644 --- a/examples/main.c +++ b/swift.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * Angus Lepper (angus.lepper@ed.ac.uk) * 2016 John A. Regan (john.a.regan@durham.ac.uk) @@ -23,7 +23,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <errno.h> @@ -90,8 +90,10 @@ int main(int argc, char *argv[]) { struct cooling_function_data cooling_func; struct cosmology cosmo; struct external_potential potential; + struct extra_io_properties extra_io_props; struct star_formation starform; struct pm_mesh mesh; + struct power_spectrum_data pow_data; struct gpart *gparts = NULL; struct gravity_props gravity_properties; struct hydro_props hydro_properties; @@ -102,8 +104,10 @@ int main(int argc, char *argv[]) { struct feedback_props feedback_properties; struct rt_props rt_properties; struct entropy_floor_properties entropy_floor; + struct pressure_floor_props pressure_floor_props; struct black_holes_props black_holes_properties; struct fof_props fof_properties; + struct lightcone_array_props lightcone_array_properties; struct part *parts = NULL; struct phys_const prog_const; struct space s; @@ -168,6 +172,7 @@ int main(int argc, char *argv[]) { int with_hydro = 0; int with_stars = 0; int with_fof = 0; + int with_lightcone = 0; int with_star_formation = 0; int with_feedback = 0; int with_black_holes = 0; @@ -178,12 +183,13 @@ int main(int argc, char *argv[]) { int with_mpole_reconstruction = 0; int with_structure_finding = 0; int with_csds = 0; - int with_sink = 0; + int with_sinks = 0; int with_qla = 0; int with_eagle = 0; int with_gear = 0; int with_line_of_sight = 0; int with_rt = 0; + int with_power = 0; int verbose = 0; int nr_threads = 1; int nr_pool_threads = -1; @@ -231,12 +237,15 @@ int main(int argc, char *argv[]) { OPT_BOOLEAN('S', "stars", &with_stars, "Run with stars.", NULL, 0, 0), OPT_BOOLEAN('B', "black-holes", &with_black_holes, "Run with black holes.", NULL, 0, 0), - OPT_BOOLEAN('k', "sinks", &with_sink, "Run with sink particles.", NULL, 0, - 0), + OPT_BOOLEAN('k', "sinks", &with_sinks, "Run with sink particles.", NULL, + 0, 0), OPT_BOOLEAN( 'u', "fof", &with_fof, "Run Friends-of-Friends algorithm to perform black hole seeding.", NULL, 0, 0), + + OPT_BOOLEAN(0, "lightcone", &with_lightcone, + "Generate lightcone outputs.", NULL, 0, 0), OPT_BOOLEAN('x', "velociraptor", &with_structure_finding, "Run with structure finding.", NULL, 0, 0), OPT_BOOLEAN(0, "line-of-sight", &with_line_of_sight, @@ -250,9 +259,9 @@ int main(int argc, char *argv[]) { OPT_BOOLEAN(0, "csds", &with_csds, "Run with the Continuous Simulation Data Stream (CSDS).", NULL, 0, 0), - OPT_BOOLEAN('R', "radiation", &with_rt, - "Run with radiative transfer. Work in progress, currently " - "has no effect.", + OPT_BOOLEAN('R', "radiation", &with_rt, "Run with radiative transfer.", + NULL, 0, 0), + OPT_BOOLEAN(0, "power", &with_power, "Run with power spectrum outputs.", NULL, 0, 0), OPT_GROUP(" Simulation meta-options:\n"), @@ -476,13 +485,13 @@ int main(int argc, char *argv[]) { #endif #ifdef WITH_MPI - if (with_sink) { + if (with_sinks) { printf("Error: sink particles are not available yet with MPI.\n"); return 1; } #endif - if (with_sink && with_star_formation) { + if (with_sinks && with_star_formation) { printf( "Error: The sink particles are not supposed to be run with star " "formation.\n"); @@ -539,6 +548,14 @@ int main(int argc, char *argv[]) { return 1; } + if (with_lightcone) { +#ifndef WITH_LIGHTCONE + error("Running with lightcone output but compiled without it!"); +#endif + if (!with_cosmology) + error("Error: cannot make lightcones without --cosmology."); + } + if (!with_stars && with_star_formation) { if (myrank == 0) { argparse_usage(&argparse); @@ -600,6 +617,8 @@ int main(int argc, char *argv[]) { "chosen\n"); } if (with_rt && !with_stars) { + /* In principle we can run without stars, but I don't trust the user to + * remember to add the flag every time they want to run RT. */ error( "Error: Cannot use radiative transfer without stars, --stars must be " "chosen\n"); @@ -612,19 +631,22 @@ int main(int argc, char *argv[]) { if (with_rt && with_cooling) { error("Error: Cannot use radiative transfer and cooling simultaneously"); } - -#ifdef WITH_MPI - /* Temporary, this will be removed in due time */ - error("Error: Cannot use radiative transfer with MPI\n"); -#endif + if (with_rt && with_cosmology) { + error("Error: Cannot use run radiative transfer with cosmology (yet)"); + } #endif /* idfef RT_NONE */ #ifdef SINK_NONE - if (with_sink) { + if (with_sinks) { error("Running with sink particles but compiled without them!"); } #endif +#ifdef TRACERS_EAGLE + if (!with_cooling && !with_temperature) + error("Error: Cannot use EAGLE tracers without --cooling or --temperature"); +#endif + /* Let's pin the main thread, now we know if affinity will be used. */ #if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE) if (with_aff && @@ -729,6 +751,9 @@ int main(int argc, char *argv[]) { /* Read the parameter file */ struct swift_params *params = (struct swift_params *)malloc(sizeof(struct swift_params)); + + /* In scope reference for a potential copy when restarting. */ + struct swift_params *refparams = NULL; if (params == NULL) error("Error allocating memory for the parameter file."); if (myrank == 0) { message("Reading runtime parameters from file '%s'", param_filename); @@ -859,25 +884,6 @@ int main(int argc, char *argv[]) { parser_get_opt_param_string(params, "Restarts:basename", restart_name, "swift"); - /* How often to check for the stop file and dump restarts and exit the - * application. */ - const int restart_stop_steps = - parser_get_opt_param_int(params, "Restarts:stop_steps", 100); - - /* Get the maximal wall-clock time of this run */ - const float restart_max_hours_runtime = - parser_get_opt_param_float(params, "Restarts:max_run_time", FLT_MAX); - - /* Do we want to resubmit when we hit the limit? */ - const int resubmit_after_max_hours = - parser_get_opt_param_int(params, "Restarts:resubmit_on_exit", 0); - - /* What command should we run to resubmit at the end? */ - char resubmit_command[PARSER_MAX_LINE_SIZE]; - if (resubmit_after_max_hours) - parser_get_param_string(params, "Restarts:resubmit_command", - resubmit_command); - /* If restarting, look for the restart files. */ if (restart) { @@ -961,14 +967,25 @@ int main(int argc, char *argv[]) { } #endif + /* Keep a copy of the params from the restart. */ + refparams = (struct swift_params *)malloc(sizeof(struct swift_params)); + memcpy(refparams, e.parameter_file, sizeof(struct swift_params)); + /* And initialize the engine with the space and policies. */ engine_config(/*restart=*/1, /*fof=*/0, &e, params, nr_nodes, myrank, - nr_threads, nr_pool_threads, with_aff, talking, restart_file); + nr_threads, nr_pool_threads, with_aff, talking, restart_dir, + restart_file, &reparttype); /* Check if we are already done when given steps on the command-line. */ if (e.step >= nsteps && nsteps > 0) error("Not restarting, already completed %d steps", e.step); + /* If we are restarting at the very end of a run, just build the tree and + * prepare to dump. + * The main simulation loop below (where rebuild normally happens) won't be + * executed. */ + if (engine_is_done(&e)) space_rebuild(e.s, /*repartitioned=*/0, e.verbose); + } else { /* Prepare and verify the selection of outputs */ @@ -1059,7 +1076,7 @@ int main(int argc, char *argv[]) { pressure_floor_init(&pressure_floor_props, &prog_const, &us, &hydro_properties, params); else - bzero(&pressure_floor_props, sizeof(struct pressure_floor_properties)); + bzero(&pressure_floor_props, sizeof(struct pressure_floor_props)); /* Initialise the stars properties */ if (with_stars) @@ -1098,7 +1115,7 @@ int main(int argc, char *argv[]) { bzero(&black_holes_properties, sizeof(struct black_holes_props)); /* Initialise the sink properties */ - if (with_sink) { + if (with_sinks) { sink_props_init(&sink_properties, &prog_const, &us, params, &cosmo); } else bzero(&sink_properties, sizeof(struct sink_props)); @@ -1133,6 +1150,10 @@ int main(int argc, char *argv[]) { chemistry_init(params, &us, &prog_const, &chemistry); if (myrank == 0) chemistry_print(&chemistry); + /* Initialise the extra i/o */ + bzero(&extra_io_props, sizeof(struct extra_io_properties)); + extra_io_init(params, &us, &prog_const, &cosmo, &extra_io_props); + /* Initialise the FOF properties */ bzero(&fof_properties, sizeof(struct fof_props)); #ifdef WITH_FOF @@ -1148,6 +1169,17 @@ int main(int argc, char *argv[]) { } #endif + /* Initialize power spectra calculation */ + if (with_power) { +#ifdef HAVE_FFTW + power_init(&pow_data, params, nr_threads); +#else + error("No FFTW library found. Cannot compute power spectra."); +#endif + } else { + bzero(&pow_data, sizeof(struct power_spectrum_data)); + } + /* Be verbose about what happens next */ if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); if (myrank == 0 && cleanup_h) @@ -1171,7 +1203,7 @@ int main(int argc, char *argv[]) { read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sinks, &sparts, &bparts, &Ngas, &Ngpart, &Ngpart_background, &Nnupart, &Nsink, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, - with_gravity, with_sink, with_stars, with_black_holes, + with_gravity, with_sinks, 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, remap_ids, &ics_metadata); @@ -1179,7 +1211,7 @@ int main(int argc, char *argv[]) { read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sinks, &sparts, &bparts, &Ngas, &Ngpart, &Ngpart_background, &Nnupart, &Nsink, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, - with_gravity, with_sink, with_stars, with_black_holes, + with_gravity, with_sinks, 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, remap_ids, &ics_metadata); @@ -1188,7 +1220,7 @@ int main(int argc, char *argv[]) { read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sinks, &sparts, &bparts, &Ngas, &Ngpart, &Ngpart_background, &Nnupart, &Nsink, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, - with_gravity, with_sink, with_stars, with_black_holes, + with_gravity, with_sinks, with_stars, with_black_holes, with_cosmology, cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, dry_run, remap_ids, &ics_metadata); #endif @@ -1226,7 +1258,7 @@ int main(int argc, char *argv[]) { for (size_t k = 0; k < Ngpart; ++k) if (gparts[k].type == swift_type_gas) error("Linking problem"); } - if (!with_sink && !dry_run) { + if (!with_sinks && !dry_run) { for (size_t k = 0; k < Ngpart; ++k) if (gparts[k].type == swift_type_sink) error("Linking problem"); } @@ -1301,13 +1333,23 @@ int main(int argc, char *argv[]) { space_init(&s, params, &cosmo, dim, &hydro_properties, parts, gparts, sinks, sparts, bparts, Ngas, Ngpart, Nsink, Nspart, Nbpart, Nnupart, periodic, replicate, remap_ids, generate_gas_in_ics, with_hydro, - with_self_gravity, with_star_formation, with_sink, - with_DM_background_particles, with_neutrinos, talking, dry_run, - nr_nodes); + with_self_gravity, with_star_formation, with_sinks, + with_DM_particles, with_DM_background_particles, with_neutrinos, + talking, dry_run, nr_nodes); /* Initialise the line of sight properties. */ if (with_line_of_sight) los_init(s.dim, &los_properties, params); + /* Initialise the lightcone properties */ + bzero(&lightcone_array_properties, sizeof(struct lightcone_array_props)); +#ifdef WITH_LIGHTCONE + if (with_lightcone) + lightcone_array_init(&lightcone_array_properties, &s, &cosmo, params, &us, + &prog_const, verbose); + else + lightcone_array_properties.nr_lightcones = 0; +#endif + if (myrank == 0) { clocks_gettime(&toc); message("space_init took %.3f %s.", clocks_diff(&tic, &toc), @@ -1318,10 +1360,11 @@ int main(int argc, char *argv[]) { /* 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_external_gravity, with_baryon_particles, with_DM_particles, - with_neutrinos, with_DM_background_particles, periodic, s.dim); + gravity_props_init(&gravity_properties, params, &prog_const, &cosmo, + with_cosmology, with_external_gravity, + with_baryon_particles, with_DM_particles, + with_neutrinos, with_DM_background_particles, periodic, + s.dim, s.cdim); /* Initialize the neutrino response if used */ bzero(&neutrino_response, sizeof(struct neutrino_response)); @@ -1440,23 +1483,26 @@ int main(int argc, char *argv[]) { if (with_fof) engine_policies |= engine_policy_fof; if (with_csds) engine_policies |= engine_policy_csds; if (with_line_of_sight) engine_policies |= engine_policy_line_of_sight; - if (with_sink) engine_policies |= engine_policy_sinks; + if (with_sinks) engine_policies |= engine_policy_sinks; if (with_rt) engine_policies |= engine_policy_rt; + if (with_power) engine_policies |= engine_policy_power_spectra; /* Initialize the engine with the space and policies. */ engine_init(&e, &s, params, output_options, N_total[swift_type_gas], N_total[swift_type_count], N_total[swift_type_sink], N_total[swift_type_stars], N_total[swift_type_black_hole], N_total[swift_type_dark_matter_background], - N_total[swift_type_neutrino], engine_policies, talking, - &reparttype, &us, &prog_const, &cosmo, &hydro_properties, - &entropy_floor, &gravity_properties, &stars_properties, - &black_holes_properties, &sink_properties, &neutrino_properties, - &neutrino_response, &feedback_properties, &rt_properties, &mesh, - &potential, &cooling_func, &starform, &chemistry, - &fof_properties, &los_properties, &ics_metadata); + N_total[swift_type_neutrino], engine_policies, talking, &us, + &prog_const, &cosmo, &hydro_properties, &entropy_floor, + &gravity_properties, &stars_properties, &black_holes_properties, + &sink_properties, &neutrino_properties, &neutrino_response, + &feedback_properties, &pressure_floor_props, &rt_properties, + &mesh, &pow_data, &potential, &cooling_func, &starform, + &chemistry, &extra_io_props, &fof_properties, &los_properties, + &lightcone_array_properties, &ics_metadata); engine_config(/*restart=*/0, /*fof=*/0, &e, params, nr_nodes, myrank, - nr_threads, nr_pool_threads, with_aff, talking, restart_file); + nr_threads, nr_pool_threads, with_aff, talking, restart_dir, + restart_file, &reparttype); /* Compute some stats for the star formation */ if (with_star_formation) { @@ -1538,6 +1584,10 @@ int main(int argc, char *argv[]) { /*seed_black_holes=*/0, /*buffers allocated=*/1); } + /* If we want power spectra, output them now as well */ + if (with_power) + calc_all_power_spectra(e.power_data, e.s, &e.threadpool, e.verbose); + engine_dump_snapshot(&e); } @@ -1545,14 +1595,14 @@ int main(int argc, char *argv[]) { if (!e.output_list_stats) engine_print_stats(&e); /* Is there a dump before the end of the first time-step? */ - engine_check_for_dumps(&e); + engine_io(&e); } /* Legend */ if (myrank == 0) { printf( "# %6s %14s %12s %12s %14s %9s %12s %12s %12s %12s %12s %16s [%s] " - "%6s %s [%s] \n", + "%6s %12s [%s] \n", "Step", "Time", "Scale-factor", "Redshift", "Time-step", "Time-bins", "Updates", "g-Updates", "s-Updates", "sink-Updates", "b-Updates", "Wall-clock time", clocks_getunit(), "Props", "Dead time", @@ -1569,12 +1619,28 @@ int main(int argc, char *argv[]) { error("Failed to generate restart filename"); /* dump the parameters as used. */ - if (!restart && myrank == 0) { + if (myrank == 0) { - /* used parameters */ - parser_write_params_to_file(params, "used_parameters.yml", /*used=*/1); - /* unused parameters */ - parser_write_params_to_file(params, "unused_parameters.yml", /*used=*/0); + const char *usedname = "used_parameters.yml"; + const char *unusedname = "unused_parameters.yml"; + if (restart) { + + /* The used parameters can change, so try to track that. */ + struct swift_params tmp; + memcpy(&tmp, params, sizeof(struct swift_params)); + parser_compare_params(refparams, &tmp); + + /* We write a file, even if nothing has changed. */ + char pname[64]; + sprintf(pname, "%s.%d", usedname, e.step); + parser_write_params_to_file(&tmp, pname, /*used=*/1); + + } else { + + /* Write the fully populated used and unused files. */ + parser_write_params_to_file(params, usedname, /*used=*/1); + parser_write_params_to_file(params, unusedname, /*used=*/0); + } } /* Dump memory use report if collected for the 0 step. */ @@ -1601,7 +1667,7 @@ int main(int argc, char *argv[]) { /* Main simulation loop */ /* ==================== */ - int force_stop = 0, resubmit = 0; + int force_stop = 0; for (int j = 0; !engine_is_done(&e) && e.step - 1 != nsteps && !force_stop; j++) { @@ -1609,30 +1675,15 @@ int main(int argc, char *argv[]) { timers_reset_all(); /* Take a step. */ - engine_step(&e); + force_stop = engine_step(&e); /* Print the timers. */ if (with_verbose_timers) timers_print(e.step); - /* Every so often allow the user to stop the application and dump the - * restart files. */ - if (j % restart_stop_steps == 0) { - force_stop = restart_stop_now(restart_dir, 0); - if (myrank == 0 && force_stop) - message("Forcing application exit, dumping restart files..."); - } - - /* Did we exceed the maximal runtime? */ - if (e.runtime > restart_max_hours_runtime) { - force_stop = 1; - message("Runtime limit reached, dumping restart files..."); - if (resubmit_after_max_hours) resubmit = 1; - } - - /* Also if using nsteps to exit, will not have saved any restarts on exit, - * make sure we do that (useful in testing only). */ - if (force_stop || (e.restart_onexit && e.step - 1 == nsteps)) - engine_dump_restarts(&e, 0, 1); + /* Shall we write some check-point files? + * Note that this was already done by engine_step() if force_stop is set */ + if (e.restart_onexit && e.step - 1 == nsteps && !force_stop) + engine_dump_restarts(&e, /*drifted=*/0, /*force=*/1); /* Dump the task data using the given frequency. */ if (dump_tasks && (dump_tasks == 1 || j % dump_tasks == 1)) { @@ -1654,6 +1705,8 @@ int main(int argc, char *argv[]) { } #endif + space_write_ghost_stats(e.s, j + 1); + /* Dump memory use report if collected. */ #ifdef SWIFT_MEMUSE_REPORTS { @@ -1676,7 +1729,7 @@ int main(int argc, char *argv[]) { j + 1); mpiuse_log_dump(dumpfile, e.tic_step); } -#endif // WITH_MPI +#endif #ifdef SWIFT_DEBUG_THREADPOOL /* Dump the task data using the given frequency. */ @@ -1692,7 +1745,7 @@ int main(int argc, char *argv[]) { } else { threadpool_reset_log(&e.threadpool); } -#endif // SWIFT_DEBUG_THREADPOOL +#endif } /* Write final time information */ @@ -1704,7 +1757,7 @@ int main(int argc, char *argv[]) { printf( " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %12lld " "%12lld" - " %21.3f %6d %21.3f\n", + " %21.3f %6d %17.3f\n", e.step, e.time, e.cosmology->a, e.cosmology->z, e.time_step, e.min_active_bin, e.max_active_bin, e.updates, e.g_updates, e.s_updates, e.sink_updates, e.b_updates, e.wallclock_time, e.step_props, dead_time); @@ -1712,7 +1765,7 @@ int main(int argc, char *argv[]) { fprintf(e.file_timesteps, " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %12lld" - " %12lld %21.3f %6d %21.3f\n", + " %12lld %21.3f %6d %17.3f\n", e.step, e.time, e.cosmology->a, e.cosmology->z, e.time_step, e.min_active_bin, e.max_active_bin, e.updates, e.g_updates, e.s_updates, e.sink_updates, e.b_updates, e.wallclock_time, @@ -1769,6 +1822,10 @@ int main(int argc, char *argv[]) { /*seed_black_holes=*/0, /*buffers allocated=*/1); } + if (with_power) { + calc_all_power_spectra(e.power_data, e.s, &e.threadpool, e.verbose); + } + #ifdef HAVE_VELOCIRAPTOR if (with_structure_finding && e.snapshot_invoke_stf && !e.stf_this_timestep) @@ -1789,6 +1846,16 @@ int main(int argc, char *argv[]) { velociraptor_invoke(&e, /*linked_with_snap=*/0); } #endif + + /* Write out any remaining lightcone data at the end of the run */ +#ifdef WITH_LIGHTCONE + lightcone_array_flush(e.lightcone_array_properties, &(e.threadpool), + e.cosmology, e.internal_units, e.snapshot_units, + /*flush_map_updates=*/1, /*flush_particles=*/1, + /*end_file=*/1, /*dump_all_shells=*/1); + lightcone_array_write_index(e.lightcone_array_properties, e.internal_units, + e.snapshot_units); +#endif } /* Remove the stop file if used. Do this anyway, we could have missed the @@ -1796,9 +1863,9 @@ int main(int argc, char *argv[]) { if (myrank == 0) force_stop = restart_stop_now(restart_dir, 1); /* Did we want to run a re-submission command just before dying? */ - if (myrank == 0 && resubmit) { + if (myrank == 0 && e.resubmit) { message("Running the resubmission command:"); - restart_resubmit(resubmit_command); + restart_resubmit(e.resubmit_command); fflush(stdout); fflush(stderr); message("resubmission command completed."); @@ -1809,13 +1876,17 @@ int main(int argc, char *argv[]) { if (with_cosmology) cosmology_clean(e.cosmology); if (e.neutrino_properties->use_linear_response) neutrino_response_clean(e.neutrino_response); - if (with_self_gravity) pm_mesh_clean(e.mesh); + if (with_self_gravity && s.periodic) pm_mesh_clean(e.mesh); if (with_stars) stars_props_clean(e.stars_properties); if (with_cooling || with_temperature) cooling_clean(e.cooling_func); if (with_feedback) feedback_clean(e.feedback_props); - if (with_rt) rt_clean(e.rt_props); + if (with_lightcone) lightcone_array_clean(e.lightcone_array_properties); + if (with_rt) rt_clean(e.rt_props, restart); + if (with_power) power_clean(e.power_data); + extra_io_clean(e.io_extra_props); engine_clean(&e, /*fof=*/0, restart); free(params); + if (restart) free(refparams); free(output_options); #ifdef WITH_MPI diff --git a/examples/main_fof.c b/swift_fof.c similarity index 95% rename from examples/main_fof.c rename to swift_fof.c index 039b464e2b4c6f98895b7274af41def79aca6441..dfab9527b44f074f9eb14bf0647781264e1d1232 100644 --- a/examples/main_fof.c +++ b/swift_fof.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * Angus Lepper (angus.lepper@ed.ac.uk) * 2016 John A. Regan (john.a.regan@durham.ac.uk) @@ -23,7 +23,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <errno.h> @@ -90,6 +90,7 @@ int main(int argc, char *argv[]) { struct pm_mesh mesh; struct gpart *gparts = NULL; struct gravity_props gravity_properties; + struct hydro_props hydro_properties; struct fof_props fof_properties; struct neutrino_props neutrino_properties; struct part *parts = NULL; @@ -333,7 +334,7 @@ int main(int argc, char *argv[]) { message("sizeof(cell) is %4zi bytes.", sizeof(struct cell)); } - /* Read the parameter file */ + /* Read the parameter file. */ struct swift_params *params = (struct swift_params *)malloc(sizeof(struct swift_params)); if (params == NULL) error("Error allocating memory for the parameter file."); @@ -435,6 +436,12 @@ int main(int argc, char *argv[]) { cosmology_init_no_cosmo(&cosmo); if (myrank == 0 && with_cosmology) cosmology_print(&cosmo); + /* Initialise the hydro properties */ + if (with_hydro) + hydro_props_init(&hydro_properties, &prog_const, &us, params); + else + bzero(&hydro_properties, sizeof(struct hydro_props)); + /* Initialise the equation of state */ if (with_hydro) eos_init(&eos, &prog_const, &us, params); @@ -565,8 +572,8 @@ int main(int argc, char *argv[]) { Nnupart, periodic, replicate, /*remap_ids=*/0, /*generate_gas_in_ics=*/0, /*hydro=*/N_total[0] > 0, /*gravity=*/1, /*with_star_formation=*/0, /*sink=*/N_total[swift_type_sink], - with_DM_background_particles, with_neutrinos, talking, - /*dry_run=*/0, nr_nodes); + with_DM_particles, with_DM_background_particles, with_neutrinos, + talking, /*dry_run=*/0, nr_nodes); if (myrank == 0) { clocks_gettime(&toc); @@ -580,7 +587,7 @@ int main(int argc, char *argv[]) { gravity_props_init(&gravity_properties, params, &prog_const, &cosmo, with_cosmology, /*with_external_gravity=*/0, with_baryon_particles, with_DM_particles, with_neutrinos, - with_DM_background_particles, periodic, s.dim); + with_DM_background_particles, periodic, s.dim, s.cdim); /* Initialise the long-range gravity mesh */ if (periodic) { @@ -646,16 +653,19 @@ int main(int argc, char *argv[]) { N_total[swift_type_count], N_total[swift_type_sink], N_total[swift_type_stars], N_total[swift_type_black_hole], N_total[swift_type_dark_matter_background], N_total[swift_type_neutrino], - engine_policies, talking, &reparttype, &us, &prog_const, &cosmo, - /*hydro_properties=*/NULL, /*entropy_floor=*/NULL, &gravity_properties, + engine_policies, talking, &us, &prog_const, &cosmo, &hydro_properties, + /*entropy_floor=*/NULL, &gravity_properties, /*stars_properties=*/NULL, /*black_holes_properties=*/NULL, /*sink_properties=*/NULL, &neutrino_properties, /*neutrino_response=*/NULL, /*feedback_properties=*/NULL, - /*rt_properties=*/NULL, &mesh, /*potential=*/NULL, + /*pressure_floor_properties=*/NULL, + /*rt_properties=*/NULL, &mesh, /*pow_data=*/NULL, /*potential=*/NULL, /*cooling_func=*/NULL, /*starform=*/NULL, /*chemistry=*/NULL, - &fof_properties, /*los_properties=*/NULL, &ics_metadata); + /*extra_io_props=*/NULL, &fof_properties, /*los_properties=*/NULL, + /*lightcone_properties=*/NULL, &ics_metadata); engine_config(/*restart=*/0, /*fof=*/1, &e, params, nr_nodes, myrank, - nr_threads, nr_threads, with_aff, talking, NULL); + nr_threads, nr_threads, with_aff, talking, NULL, NULL, + &reparttype); /* Get some info to the user. */ if (myrank == 0) { @@ -697,6 +707,13 @@ int main(int argc, char *argv[]) { engine_fof(&e, /*dump_results=*/1, /*dump_debug=*/0, /*seed_black_holes=*/0, /*buffers allocated=*/1); + /* Update the policies to make sure the particles are written + * if they exist */ + if (with_hydro) e.policy |= engine_policy_hydro; + if (with_stars) e.policy |= engine_policy_stars; + if (with_black_holes) e.policy |= engine_policy_black_holes; + if (with_sinks) e.policy |= engine_policy_sinks; + /* Write output. */ engine_dump_snapshot(&e); diff --git a/tests/Makefile.am b/tests/Makefile.am index aa6d956b8d12bab3f3117e0eac6bc88e57f48f71..53fce13c93487429a517e960e958b08ff6921c77 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,5 @@ # This file is part of SWIFT. -# Copyright (c) 2015 matthieu.schaller@durham.ac.uk. +# Copyright (c) 2015 schaller@strw.leidenuniv.nl. # # 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 @@ -15,9 +15,9 @@ # 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 $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(OPENMP_CFLAGS) +AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(OPENMP_CFLAGS) $(CHEALPIX_CFLAGS) -AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) +AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) $(CHEALPIX_LIBS) if HAVECSDS AM_LDFLAGS += ../csds/src/.libs/libcsds_writer.a @@ -27,29 +27,29 @@ endif TESTS = testGreetings testMaths testReading.sh testKernel testKernelLongGrav \ testActivePair.sh test27cells.sh test27cellsPerturbed.sh testExp \ testParser.sh test125cells.sh test125cellsPerturbed.sh testFFT \ - testAdiabaticIndex testRandom testRandomSpacing testErfc \ + testAdiabaticIndex testRandom testRandomSpacing testRandomPoisson testErfc \ testMatrixInversion testThreadpool testDump testCSDS testInteractions.sh \ testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \ testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \ testPotentialPair testEOS testUtilities testSelectOutput.sh \ - testCbrt testCosmology testOutputList testFormat.sh \ + testCbrt testCosmology testRandomCone testOutputList testFormat.sh \ test27cellsStars.sh test27cellsStarsPerturbed.sh testHydroMPIrules \ - testAtomic testGravitySpeed testNeutrinoCosmology testNeutrinoFermiDirac \ - testLog testDistance + testAtomic testGravitySpeed testNeutrinoCosmology.sh testNeutrinoFermiDirac \ + testLog testDistance testTimeline # List of test programs to compile check_PROGRAMS = testGreetings testReading testTimeIntegration testKernelLongGrav \ testActivePair test27cells test27cells_subset test125cells testParser \ testKernel testFFT testInteractions testMaths testRandom testExp \ testSymmetry testDistance testThreadpool testRandomSpacing testErfc \ - testAdiabaticIndex testRiemannExact testRiemannTRRS \ + testAdiabaticIndex testRiemannExact testRiemannTRRS testRandomPoisson testRandomCone \ testRiemannHLLC testMatrixInversion testDump testCSDS \ testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \ testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \ testSelectOutput testCbrt testCosmology testOutputList test27cellsStars \ test27cellsStars_subset testCooling testComovingCooling testFeedback testHashmap \ testAtomic testHydroMPIrules testGravitySpeed testNeutrinoCosmology \ - testNeutrinoFermiDirac testLog + testNeutrinoFermiDirac testLog testTimeline # Rebuild tests when SWIFT is updated. $(check_PROGRAMS): ../src/.libs/libswiftsim.a @@ -63,8 +63,12 @@ testAtomic_SOURCES = testAtomic.c testRandom_SOURCES = testRandom.c +testRandomPoisson_SOURCES = testRandomPoisson.c + testRandomSpacing_SOURCES = testRandomSpacing.c +testRandomCone_SOURCES = testRandomCone.c + testReading_SOURCES = testReading.c testSelectOutput_SOURCES = testSelectOutput.c @@ -162,6 +166,8 @@ testHashmap_SOURCES = testHashmap.c testLog_SOURCES = testLog.c +testTimeline_SOURCES = testTimeline.c + testHydroMPIrules = testHydroMPIrules.c # Files necessary for distribution @@ -177,4 +183,4 @@ EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ output_list_scale_factor.txt testEOS.sh testEOS_plot.sh \ test27cellsStars.sh test27cellsStarsPerturbed.sh star_tolerance_27_normal.dat \ star_tolerance_27_perturbed.dat star_tolerance_27_perturbed_h.dat star_tolerance_27_perturbed_h2.dat \ - testNeutrinoCosmology.dat + testNeutrinoCosmology.dat testNeutrinoCosmology.sh diff --git a/tests/difffloat.py b/tests/difffloat.py index 45aefbc1c41f349c85e3bceaa5398c80126cd411..da662113a86fda130fe7417ee5d634d48053cb4a 100644 --- a/tests/difffloat.py +++ b/tests/difffloat.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/tests/makeInput.py b/tests/makeInput.py index f1f4b9467c7f35ba44540c68897a4754c46cb2c9..c3544d7a25f93ec5c914754db2a93d8a6b30b7c4 100644 --- a/tests/makeInput.py +++ b/tests/makeInput.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2015 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 diff --git a/tests/output_list_params.yml b/tests/output_list_params.yml.in similarity index 68% rename from tests/output_list_params.yml rename to tests/output_list_params.yml.in index 79fbc70010d6b7d4c5cfe96ac66e489f89013ed0..673bf2b6512d072df825639f5314adf9f888f714 100644 --- a/tests/output_list_params.yml +++ b/tests/output_list_params.yml.in @@ -17,12 +17,12 @@ Units: Redshift: output_list_on: 1 # (Optional) Enable the output list - output_list: output_list_redshift.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) + output_list: @srcdir@/output_list_redshift.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) Time: output_list_on: 1 # (Optional) Enable the output list - output_list: output_list_time.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) + output_list: @srcdir@/output_list_time.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) ScaleFactor: output_list_on: 1 # (Optional) Enable the output list - output_list: output_list_scale_factor.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) + output_list: @srcdir@/output_list_scale_factor.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) diff --git a/tests/test125cells.c b/tests/test125cells.c index 33359d1d1c6b754ed08f23b32276ecfee1b4a446..5716625aab829b403b90755038267785697dcec7 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2016 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -117,7 +117,9 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(PLANETARY_SPH) part->u = pressure / (hydro_gamma_minus_one * density); -#elif defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + part->conserved.energy = pressure / (hydro_gamma_minus_one * density); +#elif defined(SHADOWFAX_SPH) part->primitives.P = pressure; #else error("Need to define pressure here !"); @@ -215,15 +217,16 @@ void reset_particles(struct cell *c, struct hydro_space *hs, set_velocity(p, vel, size); set_energy_state(p, press, size, density); +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + hydro_first_init_part(p, &c->hydro.xparts[i]); + p->time_bin = 1; +#endif + hydro_init_part(p, hs); -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(SHADOWFAX_SPH) float volume = p->conserved.mass / density; -#if defined(GIZMO_MFV_SPH) - p->geometry.volume = volume; -#else p->cell.volume = volume; -#endif p->primitives.rho = density; p->primitives.v[0] = p->v[0]; p->primitives.v[1] = p->v[1]; @@ -264,7 +267,10 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h, const size_t count = n * n * n; const double volume = size * size * size; - struct cell *cell = (struct cell *)malloc(sizeof(struct cell)); + struct cell *cell = NULL; + if (posix_memalign((void **)&cell, cell_align, sizeof(struct cell)) != 0) { + error("Couldn't allocate the cell"); + } bzero(cell, sizeof(struct cell)); if (posix_memalign((void **)&cell->hydro.parts, part_align, @@ -296,7 +302,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h, part->h = size * h / (float)n; h_max = fmax(h_max, part->h); -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) part->conserved.mass = density * volume / count; #else part->mass = density * volume / count; @@ -310,24 +316,6 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h, part->id = ++(*partId); part->time_bin = 1; -#if defined(GIZMO_MFV_SPH) - part->geometry.volume = part->conserved.mass / density; - part->primitives.rho = density; - part->primitives.v[0] = part->v[0]; - part->primitives.v[1] = part->v[1]; - part->primitives.v[2] = part->v[2]; - part->conserved.momentum[0] = part->conserved.mass * part->v[0]; - part->conserved.momentum[1] = part->conserved.mass * part->v[1]; - part->conserved.momentum[2] = part->conserved.mass * part->v[2]; - part->conserved.energy = - part->primitives.P / hydro_gamma_minus_one * volume + - 0.5f * - (part->conserved.momentum[0] * part->conserved.momentum[0] + - part->conserved.momentum[1] * part->conserved.momentum[1] + - part->conserved.momentum[2] * part->conserved.momentum[2]) / - part->conserved.mass; -#endif - #ifdef SWIFT_DEBUG_CHECKS part->ti_drift = 8; part->ti_kick = 8; @@ -354,6 +342,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h, cell->loc[1] = offset[1]; cell->loc[2] = offset[2]; + cell->hydro.super = cell; cell->hydro.ti_old_part = 8; cell->hydro.ti_end_min = 8; cell->nodeID = NODE_ID; @@ -401,10 +390,10 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1], main_cell->hydro.parts[pid].v[2], main_cell->hydro.parts[pid].h, hydro_get_comoving_density(&main_cell->hydro.parts[pid]), -#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || \ - defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) || \ - defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) || \ - defined(GASOLINE_SPH) +#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || \ + defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || \ + defined(SHADOWFAX_SPH) || defined(HOPKINS_PU_SPH) || \ + defined(HOPKINS_PU_SPH_MONAGHAN) || defined(GASOLINE_SPH) 0.f, #elif defined(ANARCHY_PU_SPH) || defined(SPHENIX_SPH) || defined(PHANTOM_SPH) main_cell->hydro.parts[pid].viscosity.div_v, @@ -593,6 +582,7 @@ int main(int argc, char *argv[]) { struct phys_const prog_const; prog_const.const_newton_G = 1.f; + prog_const.const_vacuum_permeability = 1.0; struct hydro_props hp; hydro_props_init_no_hydro(&hp); @@ -621,6 +611,10 @@ int main(int argc, char *argv[]) { struct runner runner; runner.e = &engine; + struct lightcone_array_props lightcone_array_properties; + lightcone_array_properties.nr_lightcones = 0; + engine.lightcone_array_properties = &lightcone_array_properties; + /* Construct some cells */ struct cell *cells[125]; struct cell *inner_cells[27]; @@ -675,7 +669,7 @@ int main(int argc, char *argv[]) { /* First, sort stuff */ for (int j = 0; j < 125; ++j) - runner_do_hydro_sort(&runner, cells[j], 0x1FFF, 0, 0); + runner_do_hydro_sort(&runner, cells[j], 0x1FFF, 0, 0, 0); /* Do the density calculation */ diff --git a/tests/test125cells.sh.in b/tests/test125cells.sh.in index d6d3ddc5b6b61bbd493c94005fd500a93ae7a01d..4af8e8869f1e3a435462866391922f92b83455d9 100755 --- a/tests/test125cells.sh.in +++ b/tests/test125cells.sh.in @@ -12,7 +12,7 @@ do if [ -e brute_force_125_standard.dat ] then - if python @srcdir@/difffloat.py brute_force_125_standard.dat swift_dopair_125_standard.dat @srcdir@/tolerance_125_normal.dat 6 + if python3 @srcdir@/difffloat.py brute_force_125_standard.dat swift_dopair_125_standard.dat @srcdir@/tolerance_125_normal.dat 6 then echo "Accuracy test passed" else diff --git a/tests/test125cellsPerturbed.sh.in b/tests/test125cellsPerturbed.sh.in index 9a5cfc07c978b0cfd5aa050aa117e887a1d40907..e6c26170c5bf4dd9f71ddb905699d69ee23b070a 100755 --- a/tests/test125cellsPerturbed.sh.in +++ b/tests/test125cellsPerturbed.sh.in @@ -12,7 +12,7 @@ do if [ -e brute_force_125_perturbed.dat ] then - if python @srcdir@/difffloat.py brute_force_125_perturbed.dat swift_dopair_125_perturbed.dat @srcdir@/tolerance_125_perturbed.dat 6 + if python3 @srcdir@/difffloat.py brute_force_125_perturbed.dat swift_dopair_125_perturbed.dat @srcdir@/tolerance_125_perturbed.dat 6 then echo "Accuracy test passed" else diff --git a/tests/test27cells.c b/tests/test27cells.c index 7aa1d0ee67136548342466f2850c70e653fafb4a..24f9143ae4055500bec5c18bac1bc054a49ae8a6 100644 --- a/tests/test27cells.c +++ b/tests/test27cells.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -98,7 +98,10 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, const size_t count = n * n * n; const double volume = size * size * size; float h_max = 0.f; - struct cell *cell = (struct cell *)malloc(sizeof(struct cell)); + struct cell *cell = NULL; + if (posix_memalign((void **)&cell, cell_align, sizeof(struct cell)) != 0) { + error("Couldn't allocate the cell"); + } bzero(cell, sizeof(struct cell)); if (posix_memalign((void **)&cell->hydro.parts, part_align, @@ -150,7 +153,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, h_max = fmaxf(h_max, part->h); part->id = ++(*partId); -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) part->conserved.mass = density * volume / count; #ifdef SHADOWFAX_SPH @@ -194,6 +197,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, cell->loc[1] = offset[1]; cell->loc[2] = offset[2]; + cell->hydro.super = cell; cell->hydro.ti_old_part = 8; cell->hydro.ti_end_min = 8; cell->nodeID = NODE_ID; @@ -272,7 +276,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1], main_cell->hydro.parts[pid].v[2], hydro_get_comoving_density(&main_cell->hydro.parts[pid]), -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) 0.f, #elif defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) || \ defined(ANARCHY_PU_SPH) @@ -321,7 +325,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, cj->hydro.parts[pjd].v[0], cj->hydro.parts[pjd].v[1], cj->hydro.parts[pjd].v[2], hydro_get_comoving_density(&cj->hydro.parts[pjd]), -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) 0.f, #else main_cell->hydro.parts[pjd].density.rho_dh, @@ -483,6 +487,10 @@ int main(int argc, char *argv[]) { engine.hydro_properties = &hp; engine.nodeID = NODE_ID; + struct phys_const prog_const; + prog_const.const_vacuum_permeability = 1.0; + engine.physical_constants = &prog_const; + struct cosmology cosmo; cosmology_init_no_cosmo(&cosmo); engine.cosmology = &cosmo; @@ -490,6 +498,10 @@ int main(int argc, char *argv[]) { struct runner runner; runner.e = &engine; + struct lightcone_array_props lightcone_array_properties; + lightcone_array_properties.nr_lightcones = 0; + engine.lightcone_array_properties = &lightcone_array_properties; + /* Construct some cells */ struct cell *cells[27]; struct cell *main_cell; @@ -504,7 +516,8 @@ int main(int argc, char *argv[]) { runner_do_drift_part(&runner, cells[i * 9 + j * 3 + k], 0); - runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); + runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0, + 0); } } } diff --git a/tests/test27cells.sh.in b/tests/test27cells.sh.in index cc8083221420e97a38fbaf2a3ecc0234997cfc61..6c9e205d60fa15fac6de591654d6178d12573b91 100755 --- a/tests/test27cells.sh.in +++ b/tests/test27cells.sh.in @@ -18,7 +18,7 @@ do if [ -e brute_force_27_standard.dat ] then - if python @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27_normal.dat 6 + if python3 @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27_normal.dat 6 then echo "Accuracy test passed" else @@ -46,7 +46,7 @@ do if [ -e brute_force_27_standard.dat ] then - if python @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27_perturbed_h.dat 6 + if python3 @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27_perturbed_h.dat 6 then echo "Accuracy test passed" else @@ -74,7 +74,7 @@ do if [ -e brute_force_27_standard.dat ] then - if python @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27_perturbed_h2.dat 6 + if python3 @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27_perturbed_h2.dat 6 then echo "Accuracy test passed" else diff --git a/tests/test27cellsPerturbed.sh.in b/tests/test27cellsPerturbed.sh.in index f875504e541588377ca6e40fe55681ebec3466f6..3177067881012b64e3b58acf7ef3b2b5fdbe5972 100755 --- a/tests/test27cellsPerturbed.sh.in +++ b/tests/test27cellsPerturbed.sh.in @@ -12,7 +12,7 @@ do if [ -e brute_force_27_perturbed.dat ] then - if python @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27_perturbed.dat 6 + if python3 @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27_perturbed.dat 6 then echo "Accuracy test passed" else @@ -40,7 +40,7 @@ do if [ -e brute_force_27_perturbed.dat ] then - if python @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27_perturbed_h.dat 6 + if python3 @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27_perturbed_h.dat 6 then echo "Accuracy test passed" else @@ -68,7 +68,7 @@ do if [ -e brute_force_27_perturbed.dat ] then - if python @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27_perturbed_h2.dat 6 + if python3 @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27_perturbed_h2.dat 6 then echo "Accuracy test passed" else diff --git a/tests/test27cellsStars.c b/tests/test27cellsStars.c index f836ba78a813207dce4b2bf88bbb3ab457898e0b..7785cdaed65e1415626a8f997c15bed1500ad80e 100644 --- a/tests/test27cellsStars.c +++ b/tests/test27cellsStars.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -71,7 +71,10 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size, const size_t scount = n_stars * n_stars * n_stars; float h_max = 0.f; float stars_h_max = 0.f; - struct cell *cell = (struct cell *)malloc(sizeof(struct cell)); + struct cell *cell = NULL; + if (posix_memalign((void **)&cell, cell_align, sizeof(struct cell)) != 0) { + error("Couldn't allocate the cell"); + } bzero(cell, sizeof(struct cell)); if (posix_memalign((void **)&cell->hydro.parts, part_align, @@ -175,6 +178,7 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size, cell->loc[1] = offset[1]; cell->loc[2] = offset[2]; + cell->hydro.super = cell; cell->stars.ti_old_part = 8; cell->stars.ti_end_min = 8; cell->hydro.ti_old_part = 8; @@ -405,6 +409,10 @@ int main(int argc, char *argv[]) { struct runner runner; runner.e = &engine; + struct lightcone_array_props lightcone_array_properties; + lightcone_array_properties.nr_lightcones = 0; + engine.lightcone_array_properties = &lightcone_array_properties; + /* Construct some cells */ struct cell *cells[27]; struct cell *main_cell; @@ -422,7 +430,8 @@ int main(int argc, char *argv[]) { runner_do_drift_part(&runner, cells[i * 9 + j * 3 + k], 0); runner_do_drift_spart(&runner, cells[i * 9 + j * 3 + k], 0); - runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); + runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0, + 0); runner_do_stars_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); } } diff --git a/tests/test27cellsStars.sh.in b/tests/test27cellsStars.sh.in index 5385b86fca6bcd24878f51567266eb81b7c21772..b8cc4ad558ed2a108c922e86d32f54fd3c09707a 100644 --- a/tests/test27cellsStars.sh.in +++ b/tests/test27cellsStars.sh.in @@ -16,7 +16,7 @@ do if [ -e star_brute_force_27_standard.dat ] then - if python @srcdir@/difffloat.py star_brute_force_27_standard.dat swift_star_dopair_27_standard.dat @srcdir@/star_tolerance_27_normal.dat 6 + if python3 @srcdir@/difffloat.py star_brute_force_27_standard.dat swift_star_dopair_27_standard.dat @srcdir@/star_tolerance_27_normal.dat 6 then echo "Accuracy test passed" else @@ -41,7 +41,7 @@ do if [ -e star_brute_force_27_standard.dat ] then - if python @srcdir@/difffloat.py star_brute_force_27_standard.dat swift_star_dopair_27_standard.dat @srcdir@/star_tolerance_27_perturbed_h.dat 6 + if python3 @srcdir@/difffloat.py star_brute_force_27_standard.dat swift_star_dopair_27_standard.dat @srcdir@/star_tolerance_27_perturbed_h.dat 6 then echo "Accuracy test passed" else @@ -66,7 +66,7 @@ do if [ -e star_brute_force_27_standard.dat ] then - if python @srcdir@/difffloat.py star_brute_force_27_standard.dat swift_star_dopair_27_standard.dat @srcdir@/star_tolerance_27_perturbed_h2.dat 6 + if python3 @srcdir@/difffloat.py star_brute_force_27_standard.dat swift_star_dopair_27_standard.dat @srcdir@/star_tolerance_27_perturbed_h2.dat 6 then echo "Accuracy test passed" else diff --git a/tests/test27cellsStarsPerturbed.sh.in b/tests/test27cellsStarsPerturbed.sh.in index ddf258fc17e6054d801ea9c73b4d0bd274cfad12..39c987ac1e5c5e3a293d57e17c38ab6b4a570b4a 100644 --- a/tests/test27cellsStarsPerturbed.sh.in +++ b/tests/test27cellsStarsPerturbed.sh.in @@ -10,7 +10,7 @@ if [ -e star_brute_force_27_perturbed.dat ] then - if python @srcdir@/difffloat.py star_brute_force_27_perturbed.dat swift_star_dopair_27_perturbed.dat @srcdir@/star_tolerance_27_perturbed.dat 6 + if python3 @srcdir@/difffloat.py star_brute_force_27_perturbed.dat swift_star_dopair_27_perturbed.dat @srcdir@/star_tolerance_27_perturbed.dat 6 then echo "Accuracy test passed" else @@ -34,7 +34,7 @@ if [ -e star_brute_force_27_perturbed.dat ] then - if python @srcdir@/difffloat.py star_brute_force_27_perturbed.dat swift_star_dopair_27_perturbed.dat @srcdir@/star_tolerance_27_perturbed_h.dat 6 + if python3 @srcdir@/difffloat.py star_brute_force_27_perturbed.dat swift_star_dopair_27_perturbed.dat @srcdir@/star_tolerance_27_perturbed_h.dat 6 then echo "Accuracy test passed" else @@ -59,7 +59,7 @@ if [ -e star_brute_force_27_perturbed.dat ] then - if python @srcdir@/difffloat.py star_brute_force_27_perturbed.dat swift_star_dopair_27_perturbed.dat @srcdir@/star_tolerance_27_perturbed_h2.dat 6 + if python3 @srcdir@/difffloat.py star_brute_force_27_perturbed.dat swift_star_dopair_27_perturbed.dat @srcdir@/star_tolerance_27_perturbed_h2.dat 6 then echo "Accuracy test passed" else diff --git a/tests/testActivePair.c b/tests/testActivePair.c index 3b70e097a6e77f800872cfe4d5b00c7834731304..4e27bbdacba0b1112ca9018109b718985cc5d051 100644 --- a/tests/testActivePair.c +++ b/tests/testActivePair.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -60,7 +60,10 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, const size_t count = n * n * n; const double volume = size * size * size; float h_max = 0.f; - struct cell *cell = (struct cell *)malloc(sizeof(struct cell)); + struct cell *cell = NULL; + if (posix_memalign((void **)&cell, cell_align, sizeof(struct cell)) != 0) { + error("Couldn't allocate the cell"); + } bzero(cell, sizeof(struct cell)); if (posix_memalign((void **)&cell->hydro.parts, part_align, @@ -95,7 +98,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, part->id = ++(*partId); /* Set the mass */ -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) part->conserved.mass = density * volume / count; #ifdef SHADOWFAX_SPH @@ -106,7 +109,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, #else part->mass = density * volume / count; -#endif /* GIZMO_MFV_SPH */ +#endif /* Set the thermodynamic variable */ #if defined(GADGET2_SPH) @@ -118,6 +121,13 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, #elif defined(HOPKINS_PE_SPH) part->entropy = 1.f; part->entropy_one_over_gamma = 1.f; +#elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + part->conserved.energy = 1.f; +#endif + +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + struct xpart dummy_xp; + hydro_first_init_part(part, &dummy_xp); #endif /* Set the time-bin */ @@ -149,6 +159,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, cell->loc[1] = offset[1]; cell->loc[2] = offset[2]; + cell->hydro.super = cell; cell->hydro.ti_old_part = 8; cell->hydro.ti_end_min = 8; cell->nodeID = NODE_ID; @@ -172,6 +183,10 @@ void clean_up(struct cell *ci) { void zero_particle_fields_density(struct cell *c, const struct cosmology *cosmo, const struct hydro_props *hydro_props) { for (int pid = 0; pid < c->hydro.count; pid++) { +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + c->hydro.parts[pid].geometry.wcorr = 1.0f; +#endif + hydro_init_part(&c->hydro.parts[pid], NULL); } } @@ -202,8 +217,12 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo, p->density.rho_dh = 0.f; p->density.wcount = 48.f / (kernel_norm * pow_dimension(p->h)); p->density.wcount_dh = 0.f; +#if defined(MINIMAL_SPH) + p->force.v_sig = hydro_get_comoving_soundspeed(p); +#else p->viscosity.v_sig = hydro_get_comoving_soundspeed(p); #endif /* MINIMAL */ +#endif /* MINIMAL, SPHENIX, PHANTOM, GASOLINE */ #ifdef HOPKINS_PE_SPH p->rho = 1.f; p->rho_bar = 1.f; @@ -223,15 +242,27 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo, #endif /* PRESSURE-ENERGY */ #if defined(ANARCHY_PU_SPH) || defined(SPHENIX_SPH) /* Initialise viscosity variables */ +#if defined(SPHENIX_SPH) p->force.pressure = hydro_get_comoving_pressure(p); +#endif p->viscosity.alpha = 0.8; p->viscosity.div_v = 0.f; p->viscosity.div_v_previous_step = 0.f; p->viscosity.v_sig = hydro_get_comoving_soundspeed(p); #endif /* ANARCHY_PU_SPH viscosity variables */ +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + const float E[3][3] = { + {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + p->geometry.matrix_E[i][j] = E[i][j]; + } + } + p->geometry.volume = 1.0f; +#endif /* And prepare for a round of force tasks. */ - hydro_prepare_force(p, xp, cosmo, hydro_props, 0.); + hydro_prepare_force(p, xp, cosmo, hydro_props, 0., 0.); hydro_reset_acceleration(p); } } @@ -243,6 +274,12 @@ void end_calculation_density(struct cell *c, const struct cosmology *cosmo) { for (int pid = 0; pid < c->hydro.count; pid++) { hydro_end_density(&c->hydro.parts[pid], cosmo); +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) + /* undo the artificial correction that was applied to wcount */ + c->hydro.parts[pid].density.wcount /= c->hydro.parts[pid].geometry.wcorr; + c->hydro.parts[pid].density.wcount_dh /= c->hydro.parts[pid].geometry.wcorr; +#endif + /* Recover the common "Neighbour number" definition */ c->hydro.parts[pid].density.wcount *= pow_dimension(c->hydro.parts[pid].h); c->hydro.parts[pid].density.wcount *= kernel_norm; @@ -308,8 +345,8 @@ void test_pair_interactions(struct runner *runner, struct cell **ci, interaction_func vec_interaction, init_func init, finalise_func finalise) { - runner_do_hydro_sort(runner, *ci, 0x1FFF, 0, 0); - runner_do_hydro_sort(runner, *cj, 0x1FFF, 0, 0); + runner_do_hydro_sort(runner, *ci, 0x1FFF, 0, 0, 0); + runner_do_hydro_sort(runner, *cj, 0x1FFF, 0, 0, 0); /* Zero the fields */ init(*ci, runner->e->cosmology, runner->e->hydro_properties); @@ -501,6 +538,7 @@ int main(int argc, char *argv[]) { struct engine engine; struct cosmology cosmo; struct hydro_props hydro_props; + struct phys_const prog_const; struct runner *runner; static long long partId = 0; char outputFileNameExtension[100] = ""; @@ -583,6 +621,9 @@ int main(int argc, char *argv[]) { engine.max_active_bin = num_time_bins; engine.nodeID = NODE_ID; + prog_const.const_vacuum_permeability = 1.0; + engine.physical_constants = &prog_const; + cosmology_init_no_cosmo(&cosmo); engine.cosmology = &cosmo; hydro_props_init_no_hydro(&hydro_props); diff --git a/tests/testActivePair.sh.in b/tests/testActivePair.sh.in index 87d09cad7118c45b41c5a41058c331cbda92b613..02fc1be749ac4fa8311541b7549be475f213b362 100755 --- a/tests/testActivePair.sh.in +++ b/tests/testActivePair.sh.in @@ -7,10 +7,25 @@ rm -f brute_force_pair_active.dat swift_dopair_active.dat echo "Running ./testActivePair -n 6 -r 1 -d 0 -f active" ./testActivePair -n 6 -r 1 -d 0 -f active - -python @srcdir@/difffloat.py brute_force_pair_active.dat swift_dopair_active.dat @srcdir@/tolerance_pair_active.dat - -python @srcdir@/difffloat.py brute_force_dopair2_active.dat swift_dopair2_force_active.dat @srcdir@/tolerance_pair_force_active.dat +if [ $? -ne 0 ] +then + echo "Test failed" + exit 1 +fi + +python3 @srcdir@/difffloat.py brute_force_pair_active.dat swift_dopair_active.dat @srcdir@/tolerance_pair_active.dat +if [ $? -ne 0 ] +then + echo "Accuracy test failed" + exit 1 +fi + +python3 @srcdir@/difffloat.py brute_force_dopair2_active.dat swift_dopair2_force_active.dat @srcdir@/tolerance_pair_force_active.dat +if [ $? -ne 0 ] +then + echo "Accuracy test failed" + exit 1 +fi rm -f brute_force_pair_active.dat swift_dopair_active.dat @@ -18,9 +33,24 @@ rm -f brute_force_pair_active.dat swift_dopair_active.dat echo "Running ./testActivePair -n 6 -r 1 -d 0 -f active -s 1506434777" ./testActivePair -n 6 -r 1 -d 0 -f active -s 1506434777 - -python @srcdir@/difffloat.py brute_force_pair_active.dat swift_dopair_active.dat @srcdir@/tolerance_pair_active.dat - -python @srcdir@/difffloat.py brute_force_dopair2_active.dat swift_dopair2_force_active.dat @srcdir@/tolerance_pair_force_active.dat +if [ $? -ne 0 ] +then + echo "Test failed" + exit 1 +fi + +python3 @srcdir@/difffloat.py brute_force_pair_active.dat swift_dopair_active.dat @srcdir@/tolerance_pair_active.dat +if [ $? -ne 0 ] +then + echo "Accuracy test failed" + exit 1 +fi + +python3 @srcdir@/difffloat.py brute_force_dopair2_active.dat swift_dopair2_force_active.dat @srcdir@/tolerance_pair_force_active.dat +if [ $? -ne 0 ] +then + echo "Accuracy test failed" + exit 1 +fi exit $? diff --git a/tests/testAdiabaticIndex.c b/tests/testAdiabaticIndex.c index 6d754a06feb4cc640d2819014dcb4b75673c8261..2255d11fea0ecc74a7a4d7c2c17ee0ed3cadb659 100644 --- a/tests/testAdiabaticIndex.c +++ b/tests/testAdiabaticIndex.c @@ -16,9 +16,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "swift.h" +/* System includes. */ #include <fenv.h> #include <stdio.h> #include <stdlib.h> diff --git a/tests/testAtomic.c b/tests/testAtomic.c index 462d71d04f92fb674619206daeea7c870bbd2fc6..5441aebdecbd120451b70daec08fbfde58596ba0 100644 --- a/tests/testAtomic.c +++ b/tests/testAtomic.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard includes. */ #include <fenv.h> diff --git a/tests/testCSDS.c b/tests/testCSDS.c index 452237ffd3c7d5c23f767688d9e5976571cebc59..a055a96f83aa23f089423e51ab684c2b612f13ce 100644 --- a/tests/testCSDS.c +++ b/tests/testCSDS.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #if defined(HAVE_POSIX_FALLOCATE) && \ defined(WITH_CSDS) /* Are we on a sensible platform? */ diff --git a/tests/testCbrt.c b/tests/testCbrt.c index fc89ea1bda7f68cb2b606eee1c80d6a8c1eee812..431753f2b6384d9b45aa7b501374ba24d8a245a2 100644 --- a/tests/testCbrt.c +++ b/tests/testCbrt.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Standard includes. */ #include <fenv.h> diff --git a/tests/testComovingCooling.c b/tests/testComovingCooling.c index ba8de08384ccb7984a55705e4cc6934c518b7b6a..9189e718844b89f731614f1e16ce59363c89e16d 100644 --- a/tests/testComovingCooling.c +++ b/tests/testComovingCooling.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "swift.h" diff --git a/tests/testCooling.c b/tests/testCooling.c index 227d12d02cab6fa1eb3fe2be7557036f823e4ea7..62a63e4ee07c0616f009994203ea94865c380425 100644 --- a/tests/testCooling.c +++ b/tests/testCooling.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "swift.h" diff --git a/tests/testCosmology.c b/tests/testCosmology.c index 4b1ae40f0436c8fd29cd49e415c611c573ba149c..12cfa20b0a52b18a3029a641f2376770b899dee0 100644 --- a/tests/testCosmology.c +++ b/tests/testCosmology.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Some standard headers. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include "swift.h" @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { struct cosmology cosmo; cosmology_init(¶ms, &us, &phys_const, &cosmo); - message("Start checking computation..."); + message("Start checking time since big bang computation..."); for (int i = 0; i < N_CHECK; i++) { double a = 0.1 + 0.9 * i / (N_CHECK - 1.); @@ -70,6 +70,20 @@ int main(int argc, char *argv[]) { assert(fabs(tmp) < TOLERANCE); } + message("Start checking comoving distance computation..."); + + for (int i = 0; i < N_CHECK; i++) { + double a = 0.1 + 0.9 * i / (N_CHECK - 1.); + /* Compute a(comoving_distance(a)) and check if same results */ + double tmp = cosmology_get_comoving_distance(&cosmo, a); + tmp = cosmology_scale_factor_at_comoving_distance(&cosmo, tmp); + + /* check accuracy */ + tmp = (tmp - a) / a; + message("Accuracy of %g at a=%g", tmp, a); + assert(fabs(tmp) < TOLERANCE); + } + message("Everything seems fine with cosmology."); cosmology_clean(&cosmo); diff --git a/tests/testDistance.c b/tests/testDistance.c index 27fd7a0d7ed8c0a966f9bc3a7021efaf0bc7aa1e..91b2985a0a8121538a6b92015366766ac5301f93 100644 --- a/tests/testDistance.c +++ b/tests/testDistance.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2016 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,15 +16,19 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "swift.h" +/* System includes. */ #include <fenv.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -void compute_interaction(struct part *pi, struct part *pj, float a, float H) { +void compute_interaction(struct part *pi, struct part *pj, float mu_0, float a, + float H) { /* Compute the distance between the two particles */ const float dx[3] = {pi->x[0] - pj->x[0], pi->x[1] - pj->x[1], @@ -35,6 +39,7 @@ void compute_interaction(struct part *pi, struct part *pj, float a, float H) { /* And interact them (density) */ runner_iact_density(r2, dx, pi->h, pj->h, pi, pj, a, H); + runner_iact_mhd_density(r2, dx, pi->h, pj->h, pi, pj, mu_0, a, H); runner_iact_chemistry(r2, dx, pi->h, pj->h, pi, pj, a, H); runner_iact_pressure_floor(r2, dx, pi->h, pj->h, pi, pj, a, H); runner_iact_star_formation(r2, dx, pi->h, pj->h, pi, pj, a, H); @@ -43,10 +48,12 @@ void compute_interaction(struct part *pi, struct part *pj, float a, float H) { /* And interact them (gradient) */ runner_iact_gradient(r2, dx, pi->h, pj->h, pi, pj, a, H); + runner_iact_mhd_gradient(r2, dx, pi->h, pj->h, pi, pj, mu_0, a, H); #endif /* And interact them (force) */ runner_iact_force(r2, dx, pi->h, pj->h, pi, pj, a, H); + runner_iact_mhd_force(r2, dx, pi->h, pj->h, pi, pj, mu_0, a, H); } } @@ -55,6 +62,7 @@ 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; + const float mu_0 = 4. * M_PI; /* Create two random particles (don't do this at home !) */ struct part pi, pj; @@ -83,7 +91,7 @@ void test(void) { pj.x[1] = pi.x[1] + random_uniform(0., dist * pi.h); pj.x[2] = pi.x[2] + random_uniform(0., dist * pi.h); - compute_interaction(&pi, &pj, a, H); + compute_interaction(&pi, &pj, mu_0, a, H); } /* Also test 0 distance */ @@ -91,7 +99,7 @@ void test(void) { pj.x[1] = pi.x[1]; pj.x[2] = pi.x[2]; - compute_interaction(&pi, &pj, a, H); + compute_interaction(&pi, &pj, mu_0, a, H); } int main(int argc, char *argv[]) { diff --git a/tests/testDump.c b/tests/testDump.c index bb5ec1a1d0ef329c3199568a16d3c5cab379bb37..67a31725c83e826bd9f4257a885ff24b9c839f96 100644 --- a/tests/testDump.c +++ b/tests/testDump.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> #ifdef HAVE_POSIX_FALLOCATE /* Are we on a sensible platform? */ #ifdef WITH_CSDS diff --git a/tests/testEOS.c b/tests/testEOS.c index 4a1e666b47acc55a5ed7f1800e7199a1abb5e821..db6fa4fbef8252245c0c38677dfe47a2ea130cca 100644 --- a/tests/testEOS.c +++ b/tests/testEOS.c @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> diff --git a/tests/testEOS.py b/tests/testEOS.py index c9be274b87c958272ad1b0d0efef66152256a864..84148ad8036539a817d9bcc1ba840994a37378f0 100644 --- a/tests/testEOS.py +++ b/tests/testEOS.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) # 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk) # # This program is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ Plot the output of testEOS to show how the equation of state pressure and sound speed varies with density and specific internal energy. Usage: - python testEOS.py (mat_id) + python3 testEOS.py (mat_id) Sys args (optional): mat_id | int | Material ID, see equation_of_state.h for the options. @@ -77,7 +77,7 @@ Di_material = { "SS08_water": Di_type["SESAME"] * type_factor + 3, # Senft & Stewart (2008) } # Invert so the mat_id are the keys -Di_mat_id = {mat_id: mat for mat, mat_id in Di_material.items()} +Di_mat_id = {mat_id: mat for mat, mat_id in list(Di_material.items())} # Unit conversion Ba_to_Mbar = 1e-12 diff --git a/tests/testEOS_plot.sh b/tests/testEOS_plot.sh index 5fd7f4976496223e467aae65b2846a8c4e1b7485..73c5ca4731272f9ed91029eccf0b6ef0c0b71a00 100755 --- a/tests/testEOS_plot.sh +++ b/tests/testEOS_plot.sh @@ -21,7 +21,7 @@ A1_mat_id=( for mat_id in "${A1_mat_id[@]}" do - python ./testEOS.py "$mat_id" + python3 ./testEOS.py "$mat_id" done exit $? diff --git a/tests/testErfc.c b/tests/testErfc.c index 8506e3afd561c39eceeb2fac7fb5858dd65dbdb7..548558eaa4fe480e6190d7dd772feaf9f590c95d 100644 --- a/tests/testErfc.c +++ b/tests/testErfc.c @@ -17,7 +17,9 @@ * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "swift.h" /* Standard includes */ diff --git a/tests/testExp.c b/tests/testExp.c index 3f48736dc7d4ff171458828ebfd42207ea6b865d..7362eff2c9fbb05bdf00fbc92ec553ecf80a20eb 100644 --- a/tests/testExp.c +++ b/tests/testExp.c @@ -17,7 +17,9 @@ * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "swift.h" /* Standard includes */ diff --git a/tests/testFFT.c b/tests/testFFT.c index 415802d0da276bf7a5b1a7adf219d2d54702796e..dbfb9cdc2e1d5a96f40c9d0b508d58db94a23899 100644 --- a/tests/testFFT.c +++ b/tests/testFFT.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2017 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Some standard headers. */ -#include "../config.h" +#include <config.h> // MATTHIEU fix this test #if 1 || !defined(HAVE_FFTW) diff --git a/tests/testFeedback.c b/tests/testFeedback.c index 1228a6fae056f41d9e4dd54a11065db336aad216..a1f37c781666cf8c34c1a133be608aba564ab4f8 100644 --- a/tests/testFeedback.c +++ b/tests/testFeedback.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 diff --git a/tests/testGravityDerivatives.c b/tests/testGravityDerivatives.c index 9dba2dc929715b8da8763cc3dcd87a53e72d6d87..e7776baf8ca02dd6d4a7df908195dff4367f3a70 100644 --- a/tests/testGravityDerivatives.c +++ b/tests/testGravityDerivatives.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (C) 2016 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> diff --git a/tests/testGravitySpeed.c b/tests/testGravitySpeed.c index 49a4f8aa94505a9922605dd348308f27c824493f..5212a12f1f81fdedcc80534cf891be314c9c769b 100644 --- a/tests/testGravitySpeed.c +++ b/tests/testGravitySpeed.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -55,11 +55,16 @@ void make_cell(struct cell *c, int N, const double loc[3], double width, c->grav.ti_beg_max = 1; c->grav.ti_old_part = 1; c->grav.ti_old_multipole = 1; + c->grav.ti_end_min = 8; /* Create the particles */ c->grav.count = N; c->grav.count_total = N; - c->grav.parts = (struct gpart *)malloc(N * sizeof(struct gpart)); + c->grav.parts = NULL; + if (posix_memalign((void **)&c->grav.parts, part_align, + N * sizeof(struct gpart)) != 0) { + error("couldn't allocate particles, no. of particles: %d", (int)N); + } bzero(c->grav.parts, N * sizeof(struct gpart)); for (int i = 0.; i < N; ++i) { @@ -124,6 +129,8 @@ int main(int argc, char *argv[]) { struct engine e; e.mesh = &mesh; e.max_active_bin = 56; + e.time = 0.1f; + e.ti_current = 1; e.gravity_properties = &grav_props; /* Construct a runner */ diff --git a/tests/testGreetings.c b/tests/testGreetings.c index 09565af157a6ae2620c17c89cedea7e1f42b1478..94e388126059af57fe5b7bb0a82d5c8709c4cf66 100644 --- a/tests/testGreetings.c +++ b/tests/testGreetings.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 diff --git a/tests/testHashmap.c b/tests/testHashmap.c index af299732318744f2924a364ce4947482a23d3034..4feff1e63121567d7faea70a4265f36e066f9a30 100644 --- a/tests/testHashmap.c +++ b/tests/testHashmap.c @@ -18,8 +18,9 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ #include <fenv.h> /* Local headers. */ diff --git a/tests/testHydroMPIrules.c b/tests/testHydroMPIrules.c index 1e635284d12fc999f30faffb7f837eb90adc2d1b..799d86adeec763af781c1316a1771224a31629ba 100644 --- a/tests/testHydroMPIrules.c +++ b/tests/testHydroMPIrules.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2019 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 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,9 +16,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "swift.h" +/* System includes. */ #include <fenv.h> #include <stdio.h> #include <stdlib.h> @@ -38,6 +41,7 @@ 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; + const float mu_0 = 4. * M_PI; /* Create two random particles (don't do this at home !) */ struct part pi, pj; @@ -81,6 +85,7 @@ void test(void) { /* --- Test the density loop --- */ runner_iact_nonsym_density(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_nonsym_mhd_density(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); runner_iact_nonsym_chemistry(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_nonsym_star_formation(r2, dx, pi.h, pj.h, &pi, &pj, a, H); @@ -100,6 +105,7 @@ void test(void) { #ifdef EXTRA_HYDRO_LOOP runner_iact_nonsym_gradient(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_nonsym_mhd_gradient(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); /* Check whether pj has been modified */ j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); @@ -115,6 +121,7 @@ void test(void) { /* --- Test the force loop --- */ runner_iact_nonsym_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_nonsym_mhd_force(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); /* Check that the particles are the same */ j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 68601df67a7374bc563e309a6b11e6de0e8bdcf0..a4d7ead66ad0ef94accb722d61a48795617182ea 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -440,6 +440,7 @@ void test_force_interactions(struct part test_part, struct part *parts, const float a = 1.f; const float H = 0.f; + const float mu_0 = 4. * M_PI; strcpy(serial_filename, filePrefix); strcpy(vec_filename, filePrefix); @@ -520,6 +521,9 @@ void test_force_interactions(struct part test_part, struct part *parts, for (size_t i = 0; i < count; i++) { runner_iact_nonsym_force(r2[i], &(dx[3 * i]), pi_serial.h, pj_serial[i].h, &pi_serial, &pj_serial[i], a, H); + runner_iact_nonsym_mhd_force(r2[i], &(dx[3 * i]), pi_serial.h, + pj_serial[i].h, &pi_serial, &pj_serial[i], + mu_0, a, H); } serial_time += getticks() - tic; } diff --git a/tests/testInteractions.sh.in b/tests/testInteractions.sh.in index 2a0bba267dd3ba8468177ec5f91af108aba0f1e5..5165577098c7467d3d2e8780aa08a1d52cd916f7 100644 --- a/tests/testInteractions.sh.in +++ b/tests/testInteractions.sh.in @@ -13,14 +13,14 @@ if [ $? != 0 ]; then else if [ -e test_nonsym_density_serial.dat ] then - if python @srcdir@/difffloat.py test_nonsym_density_serial.dat test_nonsym_density_1_vec.dat @srcdir@/tolerance_testInteractions.dat + if python3 @srcdir@/difffloat.py test_nonsym_density_serial.dat test_nonsym_density_1_vec.dat @srcdir@/tolerance_testInteractions.dat then echo "Calculating density using 1 vector accuracy test passed" else echo "Calculating density using 1 vector accuracy test failed" exit 1 fi - if python @srcdir@/difffloat.py test_nonsym_density_serial.dat test_nonsym_density_2_vec.dat @srcdir@/tolerance_testInteractions.dat + if python3 @srcdir@/difffloat.py test_nonsym_density_serial.dat test_nonsym_density_2_vec.dat @srcdir@/tolerance_testInteractions.dat then echo "Calculating density using 2 vectors accuracy test passed" else @@ -33,14 +33,14 @@ else fi if [ -e test_nonsym_force_serial.dat ] then - if python @srcdir@/difffloat.py test_nonsym_force_serial.dat test_nonsym_force_1_vec.dat @srcdir@/tolerance_testInteractions.dat + if python3 @srcdir@/difffloat.py test_nonsym_force_serial.dat test_nonsym_force_1_vec.dat @srcdir@/tolerance_testInteractions.dat then echo "Calculating force using 1 vector accuracy test passed" else echo "Calculating force using 1 vector accuracy test failed" exit 1 fi - if python @srcdir@/difffloat.py test_nonsym_force_serial.dat test_nonsym_force_2_vec.dat @srcdir@/tolerance_testInteractions.dat + if python3 @srcdir@/difffloat.py test_nonsym_force_serial.dat test_nonsym_force_2_vec.dat @srcdir@/tolerance_testInteractions.dat then echo "Calculating force using 2 vectors accuracy test passed" else diff --git a/tests/testKernel.c b/tests/testKernel.c index 6b338e93b04c739ff381e12881316c73cc86d1fc..3de191598d898cc6c0df53c38b25daa6bb4bfa76 100644 --- a/tests/testKernel.c +++ b/tests/testKernel.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (C) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * James Willis (james.s.willis@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify @@ -17,11 +17,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "align.h" #include "kernel_hydro.h" #include "vector.h" +/* System includes. */ #include <fenv.h> #include <stdlib.h> #include <strings.h> diff --git a/tests/testKernelGrav.c b/tests/testKernelGrav.c index 36d65ae1d0cc4a7807f60203e8f057e6a9d83cb5..5930c1705023de6c278a58a34ea82201781d7529 100644 --- a/tests/testKernelGrav.c +++ b/tests/testKernelGrav.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (C) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) * James Willis (james.s.willis@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify diff --git a/tests/testKernelLongGrav.c b/tests/testKernelLongGrav.c index b2e0207f7b3e1252cf9a930cbe82905db0ff4362..3b8c3d2711e209f7f0553fead74330b67e433afd 100644 --- a/tests/testKernelLongGrav.c +++ b/tests/testKernelLongGrav.c @@ -17,7 +17,9 @@ * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "exp10.h" #include "swift.h" diff --git a/tests/testLog.c b/tests/testLog.c index 29940db6f592c645909b4359ed22c8a95873bf8b..6dc1c12eb9cc09fd954d9b6819d11ad54272f472 100644 --- a/tests/testLog.c +++ b/tests/testLog.c @@ -17,7 +17,9 @@ * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "exp10.h" #include "log.h" #include "swift.h" diff --git a/tests/testMaths.c b/tests/testMaths.c index 77ad1e35cd76890d177abd0b9566c9c9a74a6174..cc3432ec26761c8d766a0dee3b85664330a68e95 100644 --- a/tests/testMaths.c +++ b/tests/testMaths.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (C) 2016 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 @@ -17,10 +17,13 @@ * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "approx_math.h" #include "vector.h" +/* System includes. */ #include <math.h> #include <stdio.h> diff --git a/tests/testMatrixInversion.c b/tests/testMatrixInversion.c index 8cd0f4e272a6b7e587619117e1aa143409976c51..179b2fa688d5e3c2884bf0b53e8eabfe2fc2ea90 100644 --- a/tests/testMatrixInversion.c +++ b/tests/testMatrixInversion.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdlib.h> @@ -119,9 +119,9 @@ int main(int argc, char* argv[]) { multiply_matrices(A, B, C); if (!is_unit_matrix(C)) { - print_matrix(A, "A"); - print_matrix(B, "B"); - print_matrix(C, "C"); + print_matrix(A, "A - Inverse matrix"); + print_matrix(B, "B - Original matrix"); + print_matrix(C, "C - Multiplication (should be unit matrix)"); error("Inverted matrix is wrong!"); } diff --git a/tests/testNeutrinoCosmology.c b/tests/testNeutrinoCosmology.c index 66609ea2a517482acd2167a760a551d4f0973e3e..88c8b219c3a5727d36c36734355dd621581281f9 100644 --- a/tests/testNeutrinoCosmology.c +++ b/tests/testNeutrinoCosmology.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -103,7 +103,7 @@ int main(int argc, char *argv[]) { const int rows = 51; const int cols = 3; double CLASS_table[rows * cols]; - FILE *stream = fopen("testNeutrinoCosmology.dat", "r"); + FILE *stream = fopen(argv[1], "r"); if (stream == NULL) error("Could not open reference solution file!"); char line[1024]; int row = 0; diff --git a/tests/testNeutrinoCosmology.sh.in b/tests/testNeutrinoCosmology.sh.in new file mode 100644 index 0000000000000000000000000000000000000000..25a0387e39e9f4bb3d7dad922d43eb47c83b1a6a --- /dev/null +++ b/tests/testNeutrinoCosmology.sh.in @@ -0,0 +1,3 @@ +#!/bin/bash + +./testNeutrinoCosmology @srcdir@/testNeutrinoCosmology.dat diff --git a/tests/testNeutrinoFermiDirac.c b/tests/testNeutrinoFermiDirac.c index c7c3dc8c722a8ed4907680ce1bbf6f80855aed96..5cf1ad6070e87e7625e621e57d698f6b56eaf14e 100644 --- a/tests/testNeutrinoFermiDirac.c +++ b/tests/testNeutrinoFermiDirac.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers */ #include <fenv.h> diff --git a/tests/testOutputList.c b/tests/testOutputList.c index 461a46e6def6e4cfabec0653bd263e665ad000fb..3a7300e7bc35c64730e7a406df8fe1938d01e40f 100644 --- a/tests/testOutputList.c +++ b/tests/testOutputList.c @@ -17,7 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Includes. */ #include "swift.h" diff --git a/tests/testPeriodicBC.c b/tests/testPeriodicBC.c index e37f5415e96c01cc7c33ac02a79c19d4826cb49b..ed50bf380a567d79d8b13b248cd36ccd124d788e 100644 --- a/tests/testPeriodicBC.c +++ b/tests/testPeriodicBC.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -132,7 +132,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, h_max = fmax(h_max, part->h); part->id = ++(*partId); -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) part->conserved.mass = density * volume / count; #ifdef SHADOWFAX_SPH @@ -175,6 +175,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, cell->loc[1] = offset[1]; cell->loc[2] = offset[2]; + cell->hydro.super = cell; cell->hydro.ti_old_part = 8; cell->hydro.ti_end_min = 8; cell->nodeID = NODE_ID; @@ -238,7 +239,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, int i, int j, main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1], main_cell->hydro.parts[pid].v[2], hydro_get_comoving_density(&main_cell->hydro.parts[pid]), -#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(SHADOWFAX_SPH) 0.f, #else main_cell->hydro.parts[pid].density.rho_dh, @@ -492,6 +493,10 @@ int main(int argc, char *argv[]) { engine.hydro_properties = &hp; engine.nodeID = NODE_ID; + struct phys_const prog_const; + prog_const.const_vacuum_permeability = 1.0; + engine.physical_constants = &prog_const; + struct runner real_runner; struct runner *runner = &real_runner; runner->e = &engine; @@ -500,6 +505,10 @@ int main(int argc, char *argv[]) { cosmology_init_no_cosmo(&cosmo); engine.cosmology = &cosmo; + struct lightcone_array_props lightcone_array_properties; + lightcone_array_properties.nr_lightcones = 0; + engine.lightcone_array_properties = &lightcone_array_properties; + /* Construct some cells */ struct cell *cells[dim * dim * dim]; static long long partId = 0; @@ -513,7 +522,7 @@ int main(int argc, char *argv[]) { runner_do_drift_part(runner, cells[i * (dim * dim) + j * dim + k], 0); runner_do_hydro_sort(runner, cells[i * (dim * dim) + j * dim + k], - 0x1FFF, 0, 0); + 0x1FFF, 0, 0, 0); } } } diff --git a/tests/testPeriodicBC.sh.in b/tests/testPeriodicBC.sh.in index 075acc0b68686bd2f418cf457140b3d6b93093d5..5d12ba73fb008e6ecadd9d583b7766f40bb075b1 100755 --- a/tests/testPeriodicBC.sh.in +++ b/tests/testPeriodicBC.sh.in @@ -11,7 +11,7 @@ do if [ -e brute_force_periodic_BC_standard.dat ] then - if python @srcdir@/difffloat.py brute_force_periodic_BC_standard.dat swift_periodic_BC_standard.dat @srcdir@/tolerance_periodic_BC_normal.dat + if python3 @srcdir@/difffloat.py brute_force_periodic_BC_standard.dat swift_periodic_BC_standard.dat @srcdir@/tolerance_periodic_BC_normal.dat then echo "Accuracy test passed" else diff --git a/tests/testPeriodicBCPerturbed.sh.in b/tests/testPeriodicBCPerturbed.sh.in index ac190d5a80654154dcd329e69c1c9cc9fe45833a..69fa286c86dd06a17086f276d7d08d38d37e8f5c 100755 --- a/tests/testPeriodicBCPerturbed.sh.in +++ b/tests/testPeriodicBCPerturbed.sh.in @@ -11,7 +11,7 @@ do if [ -e brute_force_periodic_BC_perturbed.dat ] then - if python @srcdir@/difffloat.py brute_force_periodic_BC_perturbed.dat swift_periodic_BC_perturbed.dat @srcdir@/tolerance_periodic_BC_perturbed.dat + if python3 @srcdir@/difffloat.py brute_force_periodic_BC_perturbed.dat swift_periodic_BC_perturbed.dat @srcdir@/tolerance_periodic_BC_perturbed.dat then echo "Accuracy test passed" else diff --git a/tests/testPotentialPair.c b/tests/testPotentialPair.c index 0a8fce8a2a77cef61ea2e405f995f3f8b18a3f58..252c46ec86129503cd16b4302cae74d3f0dbca61 100644 --- a/tests/testPotentialPair.c +++ b/tests/testPotentialPair.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> diff --git a/tests/testPotentialSelf.c b/tests/testPotentialSelf.c index e09fa5d1747fa0e8d19ee29e5e6f909c709e4515..e325d0dfa4df5158984dc308a4bc4adfb9b157f9 100644 --- a/tests/testPotentialSelf.c +++ b/tests/testPotentialSelf.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> diff --git a/tests/testRandom.c b/tests/testRandom.c index d3ab4301e8391eb0c36a2115eda9c3afec51fada..1cc7fb151ac8f35bd2dd49c349f2a086e760fcb2 100644 --- a/tests/testRandom.c +++ b/tests/testRandom.c @@ -19,8 +19,9 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ #include <fenv.h> /* Local headers. */ diff --git a/tests/testRandomCone.c b/tests/testRandomCone.c new file mode 100644 index 0000000000000000000000000000000000000000..2393362bad991a2fa93aecda15a9d326b9ed82de --- /dev/null +++ b/tests/testRandomCone.c @@ -0,0 +1,217 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2022 Filip Husko (filip.husko@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> + +/* Local headers. */ +#include "swift.h" + +/* Number of different maximal opening angles to test between 0 and pi/2. */ +const int N_cos = 20; + +/* Number of random cones to draw for each opening angle. */ +const int N_cos_cone = 30; + +/* Cubical grid size when checking the cone function along axes of the Cartesian + * grid. Should be at least 2. */ +const int N_cube = 5; + +/** + * @brief Test to check whether the function that generates random directions + * within a cone actually generates vectors only within that cone. It also tests + * whether they are uniformly distributed in solid angle. + * + * @param id_bh The ID of a black hole particle around whose spin vector a given + * cone is drawn. + * @param ti_current Current time of the simulation. + * @param type Random number type used. + * @param opening_angle The opening angle of the cone (in radians). + * @param unit_vector The vector that defines where the cone is pointing. + * @param N_test How many random directions to draw within the cone. + * @param N_bins How many bins to distribute these directions into when testing + * the uniformity of the distribution. + * @param tolerance The tolerance of each bin relative to the expected value. + */ +void test_cone(int64_t id_bh, const integertime_t ti_current, + const enum random_number_type type, double opening_angle, + float unit_vector[3], const int N_test, const int N_bins, + const double tolerance) { + + /* Compute cosine that corresponds to the maximum opening angle */ + const double cos_theta_max = cos(opening_angle); + + /* Initialize an array that will hold a random vector every step */ + float rand_vector[3]; + + /* Initialize an array that will hold the binned number of drawn cosines, + i.e. this is the probability density function that we wish to test. */ + double binned_cosines[N_bins]; + for (int j = 0; j < N_bins; ++j) { + binned_cosines[j] = 0.; + } + + for (int k = 0; k < N_test; ++k) { + + /* Generate random ids. */ + const long long id_p = rand() * (1LL << 31) + rand(); + + /* Generate a random unit vector within a cone around unit_vector */ + random_direction_in_cone(id_p, id_bh, ti_current, type, opening_angle, + unit_vector, rand_vector); + + /* Check that this vector is actually within the cone we want */ + const double cos_rand_unit = rand_vector[0] * unit_vector[0] + + rand_vector[1] * unit_vector[1] + + rand_vector[2] * unit_vector[2]; + if (cos_rand_unit < 0.99999 * cos_theta_max) { + printf("Cos_opening_angle is: %f, Random cos is: %f\n", cos_theta_max, + cos_rand_unit); + error("Generated random unit vector is outside cone."); + } + + /* Add the unit vector to the probability density function array. The solid + * angle subtended by some angle theta grows as (1-cos(theta)). Furthermore, + * we are limited to the spherical cap defined by the angles [0, theta_max]. + * Therefore the variable which we expect to be uniformly distributed is (1 + * - cos(theta)) / (1 - cos(theta_max)). */ + double uniform_variable = (1. - cos_rand_unit) / (1 - cos_theta_max); + for (int j = 0; j < N_bins; ++j) { + if ((uniform_variable > (double)j / (double)N_bins) && + (uniform_variable < (double)(j + 1) / (double)N_bins)) { + binned_cosines[j] = binned_cosines[j] + 1. / (double)N_test; + } + } + } + + /* Check whether the binned quantity is really uniformly distributed. If it + * is, the density (value) of each bin should be 1/N_bin. */ + for (int j = 0; j < N_bins; ++j) { + if ((binned_cosines[j] < (1. - tolerance) / (double)N_bins) || + (binned_cosines[j] > (1. + tolerance) / (double)N_bins)) { + error( + "Generated distribution of random unit vectors within a cone exceeds " + "the limit imposed by the tolerance."); + } + } +} + +int main(int argc, char *argv[]) { + + /* Test the random-vector-in-cone function, for different values of opening + * angle from 0 to pi/2 (in radians). For each of these opening angles we draw + * some number of cones, and test whether each of those has a uniform + * distribution of randomly drawn vectors within it.*/ + for (int i = 1; i < N_cos; ++i) { + + /* Opening angle to use */ + const double opening_angle = 0.5 * M_PI * (double)i / (double)N_cos; + + /* Do the test for N_cos_cone cones with this opening angle */ + for (int l = 0; l < N_cos_cone; ++l) { + + /* Generate an id for the bh and a time. We do this for every opening + * angle and every cone. */ + const long long id_bh = rand() * (1LL << 31) + rand(); + const integertime_t ti_current = rand() * (1LL << 31) + rand(); + + /* Generate a random unit vector that defines a cone, along with the + * opening angle. */ + float unit_vector[3]; + const double cos_unit = + random_unit_interval(id_bh, ti_current, random_number_BH_kick); + const double sin_unit = sqrtf(max(0., (1. - cos_unit) * (1. + cos_unit))); + const double phi_unit = random_unit_interval(id_bh * id_bh, ti_current, + random_number_BH_kick); + unit_vector[0] = sin_unit * cos(phi_unit); + unit_vector[1] = sin_unit * sin(phi_unit); + unit_vector[2] = cos_unit; + + /* Do the test. */ + test_cone(id_bh, ti_current, random_number_BH_kick, opening_angle, + unit_vector, 100000, 10, 0.1); + } + } + + /* Repeat the same test but with a larger number of random directions and + * bins, but for just one opening angle and one randomly generated cone */ + const double opening_angle_0 = 0.2; + + /* Generate an id for the bh and a time. We do this for every opening + * angle and every cone. */ + const long long id_bh_0 = rand() * (1LL << 31) + rand(); + const integertime_t ti_current_0 = rand() * (1LL << 31) + rand(); + + /* Generate a random unit vector that defines a cone, along with the + * opening angle. */ + float unit_vector_0[3]; + const double cos_unit = + random_unit_interval(id_bh_0, ti_current_0, random_number_BH_kick); + const double sin_unit = sqrtf(max(0., (1. - cos_unit) * (1. + cos_unit))); + const double phi_unit = random_unit_interval(id_bh_0 * id_bh_0, ti_current_0, + random_number_BH_kick); + unit_vector_0[0] = sin_unit * cos(phi_unit); + unit_vector_0[1] = sin_unit * sin(phi_unit); + unit_vector_0[2] = cos_unit; + + /* Do the test. */ + test_cone(id_bh_0, ti_current_0, random_number_BH_kick, opening_angle_0, + unit_vector_0, 100000000, 100, 0.01); + + /* We now repeat the same process, but we do not generate random unit vectors + * to define the cones. Instead, we sample unit vectors along the grid + * [-N_cube, -N_cube + 1, ..., N_cube -1, N_cube] ^ 3. This can be, e.g. [-2, + * -1, 0, 1, 2] ^ 3 (N_cube should be at least 2). This makes sure that the + * function that generates random unit vectors is well-defined if the unit + * vectors that define the cones point along any of the Cartesian axes, or if + * any of their components are equal. Here we use a fixed opening angle of + * 0.1, since we assume that the earlier test passing means that the function + * correctly does what it should for all opening angles. */ + const double opening_angle = 0.1; + for (int x = -N_cube; x < N_cube + 1; ++x) { + for (int y = -N_cube; y < N_cube + 1; ++y) { + for (int z = -N_cube; z < N_cube + 1; ++z) { + + /* Create our unit vector on this point of the grid */ + float unit_vector[3] = {(float)x, (float)y, (float)z}; + float unit_vector_norm = + sqrtf((float)(x * x) + (float)(y * y) + (float)(z * z)); + + /* Only do the test if the norm is >0, i.e. if we are not at the origin + * of the coordinate frame. */ + if (unit_vector_norm > 0) { + + /* Generate an id for the bh and a time. We do this for every opening + * angle and every cone. */ + const long long id_bh = rand() * (1LL << 31) + rand(); + const integertime_t ti_current = rand() * (1LL << 31) + rand(); + + /* Normalize the unit vector. */ + unit_vector[0] = unit_vector[0] / unit_vector_norm; + unit_vector[1] = unit_vector[1] / unit_vector_norm; + unit_vector[2] = unit_vector[2] / unit_vector_norm; + + /* Do the test. */ + test_cone(id_bh, ti_current, random_number_BH_kick, opening_angle, + unit_vector, 100000, 10, 0.1); + } + } + } + } + + return 0; +} diff --git a/tests/testRandomPoisson.c b/tests/testRandomPoisson.c new file mode 100644 index 0000000000000000000000000000000000000000..afd84304ef543e75991eb7d33b9b6183ea388931 --- /dev/null +++ b/tests/testRandomPoisson.c @@ -0,0 +1,86 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2022 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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include <config.h> + +/* System includes. */ +#include <fenv.h> + +/* Local headers. */ +#include "swift.h" + +const int N = 1000000; + +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); + + /* Log the swift random seed */ + message("SWIFT random seed = %d", SWIFT_RANDOM_SEED_XOR); + + /* Get a random poisson parameter and ID */ + const double lambda = exp10(random_uniform(-1, 3)); + const long long id = random_uniform(0, 1e10); + const int offset = random_uniform(0, 1e6); + const long long type = random_uniform(0, 1e10); + + message("Testing the generator for Lambda=%e", lambda); + + /* Verify that the mean and std. dev. are as expected */ + double mean = 0., mean2 = 0.; + for (int i = 0; i < N; ++i) { + + const double r = + random_poisson(id, lambda, offset + i, (enum random_number_type)type); + + mean += r; + mean2 += r * r; + } + mean /= (double)N; + mean2 /= (double)N; + const double var = mean2 - mean * mean; + + /* Verify that the mean and variance are correct */ + message("Mean = %e expected = %e", mean, lambda); + message("Vari = %e expected = %e", var, lambda); + + if (fabs(mean / lambda - 1.) > 0.01) { + error("Incorrect mean!"); + return 1; + } + if (fabs(var / lambda - 1.) > 0.01) { + error("Incorrect mean!"); + return 1; + } + + return 0; +} diff --git a/tests/testRandomSpacing.c b/tests/testRandomSpacing.c index 59a4f2eb7f2c9bc5a59bd79c1d5dc3ade0811620..cb4dac9a60ef8dec7d5226242555017e96b8ea84 100644 --- a/tests/testRandomSpacing.c +++ b/tests/testRandomSpacing.c @@ -18,8 +18,9 @@ ******************************************************************************/ /* Config parameters. */ -#include "../config.h" +#include <config.h> +/* System includes. */ #include <fenv.h> /* Local headers. */ diff --git a/tests/testReading.c b/tests/testReading.c index 6152503b96d4bc9a1f103cff92da642cfd8c0055..0fa06b790713e14c15605d2cd2597e8213390194 100644 --- a/tests/testReading.c +++ b/tests/testReading.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Some standard headers. */ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdlib.h> diff --git a/tests/testReading.sh.in b/tests/testReading.sh.in index d9dde0ec9160183afcf7c4fe9c8061114aadef48..81150dcf815a053bf3099f89b47de1e9bb4f417a 100755 --- a/tests/testReading.sh.in +++ b/tests/testReading.sh.in @@ -1,5 +1,5 @@ #!/bin/bash -python @srcdir@/makeInput.py +python3 @srcdir@/makeInput.py ./testReading diff --git a/tests/testRiemannExact.c b/tests/testRiemannExact.c index aa630a76f5f82bd87dc15f00d7d90dff2405e749..8892ace011482983b0fc2c5a525f97359b8a55d7 100644 --- a/tests/testRiemannExact.c +++ b/tests/testRiemannExact.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> diff --git a/tests/testRiemannHLLC.c b/tests/testRiemannHLLC.c index 0cce0f9d144f7be9000d368d6ac28e8e49c7d9aa..2683ce77330818790f0e0d1d4d98e5a540a0e7ec 100644 --- a/tests/testRiemannHLLC.c +++ b/tests/testRiemannHLLC.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <fenv.h> @@ -32,7 +32,7 @@ /* Local headers. */ #include "riemann/riemann_hllc.h" -#include "swift.h" +#include "tools.h" const float max_abs_error = 1e-3f; const float max_rel_error = 1e-3f; diff --git a/tests/testRiemannTRRS.c b/tests/testRiemannTRRS.c index e975230c61cd58ad1a077e9b66949044cb7708da..16956bec0fac0b645a577c8e4869450a21eff6d8 100644 --- a/tests/testRiemannTRRS.c +++ b/tests/testRiemannTRRS.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Local headers. */ #include <string.h> diff --git a/tests/testSelectOutput.c b/tests/testSelectOutput.c index 1a537232063d0f0bba38fab4f2b31d4c38b5a205..b6a73c54828be921a2907ab9ebd21488f4f245da 100644 --- a/tests/testSelectOutput.c +++ b/tests/testSelectOutput.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 @@ -18,7 +18,7 @@ ******************************************************************************/ /* Some standard headers. */ -#include "../config.h" +#include <config.h> /* Includes. */ #include "swift.h" diff --git a/tests/testSelectOutput.py b/tests/testSelectOutput.py index 79316d867a35c10214e7138618bfe61943fa2470..7ff629d05614dbdb938f5723e34ef77751406a26 100644 --- a/tests/testSelectOutput.py +++ b/tests/testSelectOutput.py @@ -1,7 +1,7 @@ ############################################################################### # This file is part of SWIFT. # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) -# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 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 diff --git a/tests/testSelectOutput.sh.in b/tests/testSelectOutput.sh.in index 85fd999643f82fd10d96013ad360a75a441a9e1a..5d1ae3a999d59185f9d2bb736ba0281604eb1a27 100644 --- a/tests/testSelectOutput.sh.in +++ b/tests/testSelectOutput.sh.in @@ -1,13 +1,13 @@ #!/bin/bash echo "Creating initial conditions" -python @srcdir@/makeInput.py +python3 @srcdir@/makeInput.py echo "Generating output" ./testSelectOutput 2>&1 | tee select_output.log echo "Checking output" -python @srcdir@/testSelectOutput.py +python3 @srcdir@/testSelectOutput.py rm -f testSelectOutput_0000.hdf5 testSelectOutput.xmf select_output.log diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index b2bdfb77b8797d43a14b059aa5000d5321fd2e17..a1fa8ed7bca97bb91d027fb52fd90920fcd78536 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2016 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,10 +16,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> + +/* Local includes. */ #include "swift.h" #include "timestep_limiter_iact.h" +/* System includes. */ #include <fenv.h> #include <stdio.h> #include <stdlib.h> @@ -46,6 +49,7 @@ 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; + const float mu_0 = 4. * M_PI; const integertime_t ti_current = 1; const double time_base = 1e-5; @@ -192,12 +196,14 @@ void test(void) { /* Call the symmetric version */ runner_iact_density(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_mhd_density(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); runner_iact_chemistry(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_pressure_floor(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_star_formation(r2, dx, pi.h, pj.h, &pi, &pj, a, H); /* Call the non-symmetric version */ runner_iact_nonsym_density(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); + runner_iact_nonsym_mhd_density(r2, dx, pi2.h, pj2.h, &pi2, &pj2, mu_0, a, H); runner_iact_nonsym_chemistry(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); runner_iact_nonsym_pressure_floor(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); runner_iact_nonsym_star_formation(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); @@ -205,6 +211,7 @@ void test(void) { dx[1] = -dx[1]; dx[2] = -dx[2]; runner_iact_nonsym_density(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); + runner_iact_nonsym_mhd_density(r2, dx, pj2.h, pi2.h, &pj2, &pi2, mu_0, a, H); runner_iact_nonsym_chemistry(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); runner_iact_nonsym_pressure_floor(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); runner_iact_nonsym_star_formation(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); @@ -233,13 +240,16 @@ void test(void) { /* Call the symmetric version */ runner_iact_gradient(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_mhd_gradient(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); /* Call the non-symmetric version */ runner_iact_nonsym_gradient(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); + runner_iact_nonsym_mhd_gradient(r2, dx, pi2.h, pj2.h, &pi2, &pj2, mu_0, a, H); dx[0] = -dx[0]; dx[1] = -dx[1]; dx[2] = -dx[2]; runner_iact_nonsym_gradient(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); + runner_iact_nonsym_mhd_gradient(r2, dx, pj2.h, pi2.h, &pj2, &pi2, mu_0, a, H); i_not_ok = memcmp((char *)&pi, (char *)&pi2, sizeof(struct part)); j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); @@ -264,12 +274,14 @@ void test(void) { /* Call the symmetric version */ runner_iact_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_mhd_force(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); runner_iact_diffusion(r2, dx, pi.h, pj.h, &pi, &pj, a, H, time_base, ti_current, NULL, /*with_cosmology=*/0); runner_iact_timebin(r2, dx, pi.h, pj.h, &pi, &pj, a, H); /* Call the non-symmetric version */ runner_iact_nonsym_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); + runner_iact_nonsym_mhd_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H, time_base, ti_current, NULL, /*with_cosmology=*/0); @@ -278,6 +290,7 @@ void test(void) { dx[1] = -dx[1]; dx[2] = -dx[2]; runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); + runner_iact_nonsym_mhd_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H, time_base, ti_current, NULL, /*with_cosmology=*/0); diff --git a/tests/testThreadpool.c b/tests/testThreadpool.c index d06d163ce928e6a16a2f37cf39325e4fc7e0da30..a0e5d8e5129bd3e9ad110c2eafc1635409943a5f 100644 --- a/tests/testThreadpool.c +++ b/tests/testThreadpool.c @@ -18,7 +18,7 @@ * ******************************************************************************/ -#include "../config.h" +#include <config.h> // Standard includes. #include <stdio.h> diff --git a/tests/testTimeIntegration.c b/tests/testTimeIntegration.c index b7f5201356ee52419038c8379dde14c9bab82055..13e38de9d913b4df8e7d0f833e3064b7e904c2b7 100644 --- a/tests/testTimeIntegration.c +++ b/tests/testTimeIntegration.c @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * Copyright (C) 2015 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 diff --git a/tests/testTimeline.c b/tests/testTimeline.c new file mode 100644 index 0000000000000000000000000000000000000000..2f663eadd0fc605187b5425c607ef01e7bb58138 --- /dev/null +++ b/tests/testTimeline.c @@ -0,0 +1,562 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com) + * + * 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> + +/* Local includes. */ +#include "timeline.h" +#include "tools.h" + +/* How many times to repeat randomly drawn tests. */ +#define NREPEAT 1048576 + +/** + * @brief test get_integer_time_end() function. + * For each particle time bin, pick a random valid time_end for + * the dt given by the particle bin; then set a random ti_current + * by subtracting some time interval < dt from the expected time_end + * and see whether the recovered time_end matches up. + * + * @param bin_min lowest bin to start test from + * @param bin_max highest bin to run test with + * @param max_nr_timesteps_test maximal number of timesteps for a sim + */ +void test_get_integer_time_end(timebin_t bin_min, timebin_t bin_max, + integertime_t max_nr_timesteps_test) { + + integertime_t dti, step, max_step, set_time_end, ti_current, displacement; + integertime_t time_end_recovered; + + for (timebin_t bin = bin_min; bin < bin_max; bin++) { + + dti = get_integer_timestep(bin); + + /* Check random state in timeline */ + /* ------------------------------ */ + + for (int r = 0; r < NREPEAT; r++) { + /* First pick a place to set this time_end on the timeline. */ + + /* we can't have more than this many steps of this size */ + max_step = max_nr_timesteps_test / dti; + + if (max_step == 0) + error("max step == 0? bin %d max_nr_steps %lld", bin, + max_nr_timesteps_test); + + /* Set the time_end at any step in between there */ + step = (integertime_t)(random_uniform(1., max_step)); + set_time_end = step * dti; + + /* Do some safety checks */ + if (set_time_end % dti != 0) error("time_end not divisible by dti?"); + if (set_time_end > max_nr_timesteps_test) + error("Time end > max_nr_timesteps?"); + if (set_time_end < (integertime_t)0) error("Time end < 0?"); + + /* Now mimick a "current time" by removing a fraction of dti from + * the step, and see whether we recover the correct time_end */ + displacement = (integertime_t)(random_uniform(0., 1. - 1e-12) * dti); + ti_current = set_time_end - displacement; + + /* Another round of safety checks */ + if (ti_current > set_time_end) + error( + "current>time_end? current=%lld time_end=%lld dti=%lld " + "displacement=%lld bin=%d\n", + ti_current, set_time_end, dti, displacement, bin); + if (ti_current < 0) + error( + "current<0? current=%lld time_end=%lld dti=%lld " + "displacement=%lld bin=%d\n", + ti_current, set_time_end, dti, displacement, bin); + + /* Now the actual check. */ + time_end_recovered = get_integer_time_end(ti_current, bin); + + if (time_end_recovered != set_time_end) { + error( + "time_end incorrect: expect=%lld got=%lld diff=%lld; current=%lld " + "displacement=%lld, dti=%lld, bin=%d", + set_time_end, time_end_recovered, set_time_end - time_end_recovered, + ti_current, displacement, dti, bin); + } + } + + /* Check time_end = 0 */ + /* ------------------ */ + set_time_end = 0; + ti_current = 0; + time_end_recovered = get_integer_time_end(ti_current, bin); + if (time_end_recovered != set_time_end) { + error( + "time_end incorrect: expect=%lld got=%lld diff=%lld; current=%lld " + "bin=%d", + set_time_end, time_end_recovered, set_time_end - time_end_recovered, + ti_current, bin); + } + + /* Check time_end = final_step */ + /* --------------------------- */ + set_time_end = max_nr_timesteps_test; + + if (dti < NREPEAT) { + /* Check all possible time states before the end */ + for (integertime_t delta = 1; delta < dti; delta++) { + ti_current = max_nr_timesteps_test - delta; + time_end_recovered = get_integer_time_end(ti_current, bin); + if (time_end_recovered != set_time_end) { + error( + "time_end incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld bin=%d", + set_time_end, time_end_recovered, + set_time_end - time_end_recovered, ti_current, bin); + } + } + } else { + /* Draw random states again */ + for (int r = 0; r < NREPEAT; r++) { + /* Do some safety checks */ + if (set_time_end % dti != 0) error("time_end not divisible by dti?"); + + /* Now mimick a "current time" by removing a fraction of dti from + * the step, and see whether we recover the correct time_end */ + displacement = (integertime_t)(random_uniform(0., 1. - 1e-12) * dti); + ti_current = set_time_end - displacement; + + /* Another round of safety checks */ + if (ti_current > set_time_end) + error( + "current>time_end? current=%lld time_end=%lld dti=%lld " + "displacement=%lld bin=%d\n", + ti_current, set_time_end, dti, displacement, bin); + if (ti_current < 0) + error( + "current<0? current=%lld time_end=%lld dti=%lld " + "displacement=%lld bin=%d\n", + ti_current, set_time_end, dti, displacement, bin); + + /* Now the actual check. */ + time_end_recovered = get_integer_time_end(ti_current, bin); + + if (time_end_recovered != set_time_end) { + error( + "time_end incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld " + "displacement=%lld, dti=%lld, bin=%d", + set_time_end, time_end_recovered, + set_time_end - time_end_recovered, ti_current, displacement, dti, + bin); + } + } + } + } + + printf("Passed %s\n", __func__); +} + +/** + * @brief test get_time_bin by converting all possible bins + * into timesteps and trying to recover the initial bin + * by calling get_time_bin() + * @param bin_min lowest bin to start test from + * @param bin_max highest bin to run test with + * + **/ +void test_get_time_bin(timebin_t bin_min, timebin_t bin_max) { + + for (timebin_t bin = bin_min; bin < bin_max; bin++) { + integertime_t dti = get_integer_timestep(bin); + timebin_t bin_recovered = get_time_bin(dti); + if (bin != bin_recovered) { + error("Expected bin=%d, got=%d", bin, bin_recovered); + } + } + + printf("Passed %s\n", __func__); +} + +/** + * @brief test get_integer_time_begin() function. + * For each particle time bin, pick a random valid time_begin for + * the dt given by the particle bin; then set a random ti_current + * by adding some time interval < dt to the expected time_begin + * and see whether the recovered time_begin matches up. + * + * @param bin_min lowest bin to start test from + * @param bin_max highest bin to run test with + * @param max_nr_timesteps_test maximal number of timesteps for a sim + */ +void test_get_integer_time_begin(timebin_t bin_min, timebin_t bin_max, + integertime_t max_nr_timesteps_test) { + + integertime_t dti, step, max_step, set_time_begin, ti_current, displacement; + integertime_t time_begin_recovered; + + for (timebin_t bin = bin_min; bin < bin_max; bin++) { + + dti = get_integer_timestep(bin); + + /* Check random state in timeline */ + /* ------------------------------ */ + + for (int r = 0; r < NREPEAT; r++) { + /* First pick a place to set this time_end on the timeline. */ + + /* we can't have more than this many steps of this size */ + max_step = max_nr_timesteps_test / dti; + if (max_step == 0) + error("max step == 0? bin %d max_nr_steps %lld", bin, + max_nr_timesteps_test); + + /* Set the time_end at any step in between there */ + step = (integertime_t)(random_uniform(0., max_step)); + set_time_begin = step * dti; + + /* Do some safety checks */ + if (set_time_begin % dti != 0) + error("set time_begin not divisible by dti?"); + if (set_time_begin >= max_nr_timesteps_test) + error("Time begin %lld >= max_nr_timesteps %lld?", set_time_begin, + max_nr_timesteps); + if (set_time_begin < (integertime_t)0) + error("Time begin < 0? set_time_begin=%lld", set_time_begin); + + /* Now mimick a "current time" by adding a fraction to dti from + * the step, and see whether we recover the correct time_begin */ + displacement = (integertime_t)(random_uniform(0., 1.) * dti); + ti_current = set_time_begin + displacement; + + /* Another round of safety checks */ + if (ti_current < set_time_begin) + printf( + "current<time_begin? current=%lld time_end=%lld dti=%lld " + "displacement=%lld bin=%d\n", + ti_current, set_time_begin, dti, displacement, bin); + + /* Now the actual check. */ + time_begin_recovered = get_integer_time_begin(ti_current, bin); + + if (ti_current == set_time_begin) { + + /* Escape the special case where we randomly drew all zeros. + * This scenario is expliclty tested below */ + if (set_time_begin == (integertime_t)0 && + displacement == (integertime_t)0) + continue; + + /* If the displacement was zero, then the function shall return + * the beginning of the timestep that ends at ti_current */ + if (time_begin_recovered + dti != set_time_begin) + error( + "time_begin incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld " + "displacement=%lld, dti=%lld", + /*expect=*/set_time_begin - dti, + /*got=*/time_begin_recovered, + /*diff=*/set_time_begin - dti - time_begin_recovered, ti_current, + displacement, dti); + } else { + if (time_begin_recovered != set_time_begin) + error( + "time_begin incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld displacement=%lld, dti=%lld", + set_time_begin, time_begin_recovered, + set_time_begin - time_begin_recovered, ti_current, displacement, + dti); + } + } + + /* Check time_begin = 0 */ + /* ------------------ */ + set_time_begin = 0; + ti_current = 0; + time_begin_recovered = get_integer_time_begin(ti_current, bin); + if (time_begin_recovered != set_time_begin) + error( + "time_begin incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld dti=%lld", + set_time_begin, time_begin_recovered, + set_time_begin - time_begin_recovered, ti_current, dti); + + /* Check time_begin = final_step */ + /* ----------------------------- */ + /* This is a tad nonsensial since in this scenario, we're + * at an integer time > max_nr_timesteps */ + set_time_begin = max_nr_timesteps_test; + + if (dti < NREPEAT) { + /* Check all possible time states before the end */ + for (integertime_t delta = 1; delta < dti; delta++) { + ti_current = max_nr_timesteps_test + delta; + time_begin_recovered = get_integer_time_begin(ti_current, bin); + if (time_begin_recovered != set_time_begin) + error( + "time_begin incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld displacement=%lld, dti=%lld", + set_time_begin, time_begin_recovered, + set_time_begin - time_begin_recovered, ti_current, displacement, + dti); + } + } else { + /* Draw random states again */ + for (int r = 0; r < NREPEAT; r++) { + + /* Now mimick a "current time" by removing a fraction of dti from + * the step, and see whether we recover the correct time_end */ + displacement = (integertime_t)(random_uniform(0., 1.) * dti); + ti_current = set_time_begin + displacement; + + /* Another round of safety checks */ + if (ti_current < set_time_begin) + error( + "current>time_begin? current=%lld time_begin=%lld dti=%lld " + "displacement=%lld bin=%d\n", + ti_current, set_time_begin, dti, displacement, bin); + if (ti_current < 0) + error( + "current<0? current=%lld time_begin=%lld dti=%lld " + "displacement=%lld bin=%d\n", + ti_current, set_time_begin, dti, displacement, bin); + + /* Now the actual check. */ + time_begin_recovered = get_integer_time_begin(ti_current, bin); + + if (ti_current == set_time_begin) { + /* If the displacement was zero, then the function shall return + * the beginning of the timestep that ends at ti_current */ + if (time_begin_recovered + dti != set_time_begin) + error( + "time_begin incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld " + "displacement=%lld, dti=%lld", + /*expect=*/set_time_begin - dti, + /*got=*/time_begin_recovered, + /*diff=*/set_time_begin - dti - time_begin_recovered, + ti_current, displacement, dti); + } else { + if (time_begin_recovered != set_time_begin) + error( + "time_begin incorrect: expect=%lld got=%lld diff=%lld; " + "current=%lld displacement=%lld, dti=%lld", + set_time_begin, time_begin_recovered, + set_time_begin - time_begin_recovered, ti_current, displacement, + dti); + } + } + } + } + + printf("Passed %s\n", __func__); +} + +/** + * @brief test get_max_active_bin by randomly choosing current times + * and manually checking whether a higher bin could be active + * + * @param bin_max highest bin to run test with + * @param max_nr_timesteps_test maximal number of timesteps for a sim + **/ +void test_get_max_active_bin(timebin_t bin_max, + integertime_t max_nr_timesteps_test) { + + integertime_t ti_current, dti; + timebin_t max_active_bin, testbin; + + /* Test random ti_currents */ + /* ----------------------- */ + for (int r = 0; r < NREPEAT; r++) { + /* test time 0 later, so use time >= 1 */ + ti_current = (integertime_t)(random_uniform(1., max_nr_timesteps_test)); + + max_active_bin = get_max_active_bin(ti_current); + testbin = 0; + dti = get_integer_timestep(max_active_bin); + for (timebin_t bin = max_active_bin; bin < bin_max && dti <= ti_current; + bin++) { + if (ti_current % dti == 0) testbin = bin; + /* update dti here for exit condition */ + dti = get_integer_timestep(bin + 1); + } + if (testbin > max_active_bin) + error("Found higher active bin: time=%lld max_active_bin=%d found=%d", + ti_current, max_active_bin, testbin); + } + + /* Test first 2^bin_max integertimes */ + /* --------------------------------- */ + for (timebin_t bin = 1; bin < bin_max; bin++) { + ti_current = get_integer_timestep(bin); + max_active_bin = get_max_active_bin(ti_current); + + if (max_active_bin != bin) + error("Got wrong max_active_bin: Expect=%d got=%d time=%lld", bin, + max_active_bin, ti_current); + } + + /* Test final 2^bin_max integertimes */ + /* --------------------------------- */ + for (timebin_t bin = 1; bin < bin_max; bin++) { + dti = get_integer_timestep(bin); + ti_current = max_nr_timesteps_test - dti; + if (ti_current == 0) + error("Testing bin which only fits in once into entire timeline"); + max_active_bin = get_max_active_bin(ti_current); + + if (max_active_bin != bin) { + error("Got wrong max_active_bin: Expect=%d got=%d time=%lld", bin, + max_active_bin, ti_current); + } + } + + /* Make sure everything is active at time zero */ + /* ------------------------------------------- */ + max_active_bin = get_max_active_bin(0); + /* Note: this should be num_time_bins, not bin_max! */ + if (max_active_bin != num_time_bins) + error("Didn't get max_active_bin = num_time_bins at t=0"); + + printf("Passed %s\n", __func__); +} + +/** + * @brief test get_min_active_bin by randomly choosing current times + * for a pair of small and big bins + * + * @param bin_min smallest bin to run test with + * @param bin_max highest bin to run test with + * @param max_nr_timesteps_test maximal number of timesteps for a sim + **/ +void test_get_min_active_bin(timebin_t bin_min, timebin_t bin_max, + integertime_t max_nr_timesteps_test) { + + integertime_t dti_lo, dti_hi; + integertime_t step, max_step; + integertime_t ti_old, ti_current; + timebin_t min_active_bin; + + /* Test random ti_currents */ + /* ----------------------- */ + + for (timebin_t bin_lo = bin_min; bin_lo < bin_max - 1; bin_lo++) { + dti_lo = get_integer_timestep(bin_lo); + + for (timebin_t bin_hi = bin_lo + 1; bin_hi < bin_max; bin_hi++) { + dti_hi = get_integer_timestep(bin_hi); + max_step = (max_nr_timesteps_test / dti_hi); + + if (NREPEAT / bin_max < max_step) { + /* Do random draws */ + for (int r = 0; r < NREPEAT / bin_max; r++) { + + step = (integertime_t)(random_uniform(0., max_step)); + ti_current = step * dti_hi; + ti_old = ti_current - dti_lo; + + if (ti_current % dti_hi != 0) + error("Time current not divisible by dti_hi"); + if (ti_current % dti_lo != 0) + error("Time current not divisible by dti_lo"); + if (ti_current <= ti_old) + error("ti_current=%lld <= ti_old=%lld | bins %d %d; ", ti_current, + ti_old, bin_lo, bin_hi); + + min_active_bin = get_min_active_bin(ti_current, ti_old); + if (min_active_bin != bin_lo) + error("Got wrong min active bin. Expect=%d got=%d", bin_lo, + min_active_bin); + } + } else { + /* systematically check every possibility */ + for (step = 0; step <= max_step; step++) { + + ti_current = step * dti_hi; + ti_old = ti_current - dti_lo; + + if (ti_current % dti_hi != 0) + error("Time current not divisible by dti_hi"); + if (ti_current % dti_lo != 0) + error("Time current not divisible by dti_lo"); + if (ti_current <= ti_old) + error("ti_current=%lld <= ti_old=%lld | bins %d %d; ", ti_current, + ti_old, bin_lo, bin_hi); + + min_active_bin = get_min_active_bin(ti_current, ti_old); + if (min_active_bin != bin_lo) + error("Got wrong min active bin. Expect=%d got=%d", bin_lo, + min_active_bin); + } + } + } + } + + printf("Passed %s\n", __func__); +} + +/** + * @brief Check the timeline functions. + */ +int main(int argc, char* argv[]) { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + + /* Get some randomness going */ + const int seed = time(NULL); + message("Seed = %d", seed); + srand(seed); + + /* run test with the limits we use in SWIFT */ + /* ---------------------------------------- */ + test_get_time_bin(1, num_time_bins); + test_get_integer_time_begin(1, num_time_bins, max_nr_timesteps); + test_get_integer_time_end(1, num_time_bins, max_nr_timesteps); + test_get_max_active_bin(num_time_bins, max_nr_timesteps); + test_get_min_active_bin(1, num_time_bins, max_nr_timesteps); + + /* run test beyond the limits we use in SWIFT */ + /* ------------------------------------------ */ + /* Get maximal bin and number of timesteps based on type size */ + size_t timebin_bits = sizeof(timebin_t) * 8; + timebin_t max_num_time_bins = 0; + for (size_t i = 0; i < timebin_bits - 1; i++) { + max_num_time_bins |= (1 << i); + } + size_t timestep_bits = sizeof(integertime_t) * 8; + + /* timestep_bits -1 because of how << works, + * additional -1 because integertime_t is signed + * additional -1 so we can do any timestep at least twice */ + max_num_time_bins = min((size_t)max_num_time_bins, timestep_bits - 3); + + if (num_time_bins < max_num_time_bins) { + /* Use analogous definition as in timeline.h here */ + integertime_t max_nr_timesteps_test = (1LL << (max_num_time_bins + 1)); + + test_get_time_bin(num_time_bins, max_num_time_bins); + test_get_integer_time_begin(num_time_bins, max_num_time_bins, + max_nr_timesteps_test); + test_get_integer_time_end(num_time_bins, max_num_time_bins, + max_nr_timesteps_test); + test_get_max_active_bin(max_num_time_bins, max_nr_timesteps_test); + test_get_min_active_bin(1, max_num_time_bins, max_nr_timesteps_test); + } + + return 0; +} diff --git a/tests/testVoronoi2D.c b/tests/testVoronoi2D.c index 57d90b64abeef9beae6abe29bb9fb22f39e96aaa..4708f7bdd44aea6b4ce9d022a5f5a0fc2e65f8c2 100644 --- a/tests/testVoronoi2D.c +++ b/tests/testVoronoi2D.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Local headers. */ #include "hydro/Shadowswift/voronoi2d_algorithm.h" diff --git a/tests/testVoronoi3D.c b/tests/testVoronoi3D.c index 5e0288fa9b3e13e0c6a6fb13db202e0f73f29a5b..de3eaf2d50782a20f526229484e2b255ec24dd52 100644 --- a/tests/testVoronoi3D.c +++ b/tests/testVoronoi3D.c @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#include "../config.h" +#include <config.h> /* Some standard headers. */ #include <stdlib.h> diff --git a/theory/Gizmo/gizmo-implementation-details/bert.tex b/theory/Gizmo/gizmo-implementation-details/bert.tex index 99975349d90ef99f736c37dd3a4ec8d2092189cb..455d7236f8ea2a18c3f8c22945240bec8a7f3256 100644 --- a/theory/Gizmo/gizmo-implementation-details/bert.tex +++ b/theory/Gizmo/gizmo-implementation-details/bert.tex @@ -78,7 +78,7 @@ The time derivatives are given by (eqn. (A4) in Hopkins) \vec{w'} & \rho' & 0\\ 0 & \vec{w'} & 1/\rho'\\ 0 & \gamma P' & \vec{w'} - \end{pmatrix} \vec{\nabla}X_k, + \end{pmatrix} \vec{\nabla}X_k, \label{eq:euler_primitive} \end{equation} or, for example~: \begin{equation} @@ -101,4 +101,47 @@ with $\vec{n}_k$ the unit vector along the coordinate axes ($k=x,y,z$) and $e = Finally, we use the fluxes to update the conserved variables (eqn. (23) in Hopkins)~: \begin{equation} \Delta C_k = -\Delta t \sum_j \vec{F_{ij}}.\vec{A_{ij}}. -\end{equation} \ No newline at end of file +\end{equation} + +\subsection{Other equations that are used} + +In order to couple the GIZMO schemes to subgrid physics, we need to provide a way to obtain the time +derivative of the internal energy, which is not a basic quantity in these schemes. This time derivative can be +obtained from the Euler equations, using the gradients. From \eqref{eq:euler_primitive}, we get + +\begin{equation} +\frac{\partial{}P}{\partial{}t} = -\gamma{}P \vec{\nabla{}}.\vec{w} - \vec{w}.\vec{\nabla{}}P, +\end{equation} +while we also have +\begin{equation} +\frac{\partial{}P}{\partial{}t} = \frac{\partial{}}{\partial{}t} \left( + (\gamma{}-1) \rho{} u +\right) = (\gamma{}-1)\rho{}\frac{\partial{}u}{\partial{}t} + (\gamma{}-1)u\frac{\partial{}\rho{}}{\partial{}t} +\end{equation} +or hence +\begin{equation} +\frac{\partial{}u}{\partial{}t} = \frac{1}{(\gamma{}-1)\rho{}} \frac{\partial{}P}{\partial{}t} - +\frac{u}{\rho{}} \left( -\vec{w}.\vec{\nabla{}}\rho{} - \rho{}\vec{\nabla{}}.\vec{w} \right), +\end{equation} +where we again used \eqref{eq:euler_primitive} in the last term. + +Combining these equations, we get +\begin{align} +\frac{\partial{}u}{\partial{}t} &= \frac{1}{(\gamma{}-1)\rho{}} \left( +-\gamma{}P \vec{\nabla{}}.\vec{w} - \vec{w}.\vec{\nabla{}}P +\right) - +\frac{u}{\rho{}} \left( -\vec{w}.\vec{\nabla{}}\rho{} - \rho{}\vec{\nabla{}}.\vec{w} \right) \\ +&= -\gamma{}u \vec{\nabla{}}.\vec{w} - \frac{1}{(\gamma{}-1)\rho{}} \vec{w}.\vec{\nabla{}}P ++ \frac{u}{\rho{}}\vec{w}.\vec{\nabla{}}\rho{} + u \vec{\nabla{}}.\vec{w} \\ +&= -(\gamma{}-1)u \vec{\nabla{}}.\vec{w} - \frac{1}{(\gamma{}-1)\rho{}} \vec{w}.\vec{\nabla{}}P ++ \frac{u}{\rho{}}\vec{w}.\vec{\nabla{}}\rho{}. +\end{align} + +For convenience, we can eliminate $u$ from this equation again: +\begin{equation} +\frac{\partial{}u}{\partial{}t} = -\frac{P}{\rho{}}\vec{\nabla{}}.\vec{w} +- \frac{1}{(\gamma{}-1)\rho{}} \vec{w} . \left( +\vec{\nabla{}}P - \frac{P}{\rho{}} \vec{\nabla{}}\rho{} +\right), +\end{equation} +where we recognise a factor $\vec{\nabla{}}u$ in the second term. diff --git a/theory/Multipoles/plot_derivatives.py b/theory/Multipoles/plot_derivatives.py index 4e3a2c9a66c10a27cf9a124e8e83decb66cae10c..332fc110abc0cd1d09a6dc63648ef9835456da8c 100644 --- a/theory/Multipoles/plot_derivatives.py +++ b/theory/Multipoles/plot_derivatives.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/theory/Multipoles/plot_mesh.py b/theory/Multipoles/plot_mesh.py index 1b09d4b88698c6604c6282603a1164df006dd9a7..304488b138478785a1983e51ca83260c4ea3c439 100644 --- a/theory/Multipoles/plot_mesh.py +++ b/theory/Multipoles/plot_mesh.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/theory/Multipoles/plot_potential.py b/theory/Multipoles/plot_potential.py index 532250b0c46cc76aeb42e7fb43d3841a02778fba..7f7e2448b816ca5a4a96ddb860d428a7a862a44d 100644 --- a/theory/Multipoles/plot_potential.py +++ b/theory/Multipoles/plot_potential.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 diff --git a/theory/Papers/PASC_2016/pasc_paper.tex b/theory/Papers/PASC_2016/pasc_paper.tex index a247192c9446475e86e0d24fff78a7a5fa557717..1abcd5fbed1c0978aeab1a673ed3a0a9c26dbacc 100644 --- a/theory/Papers/PASC_2016/pasc_paper.tex +++ b/theory/Papers/PASC_2016/pasc_paper.tex @@ -56,7 +56,7 @@ strong scaling on more than 100\,000 cores.} \affaddr{Department of Physics}\\ \affaddr{Durham University}\\ \affaddr{Durham DH1 3LE, UK}\\ - \email{\footnotesize \url{matthieu.schaller@durham.ac.uk}} + \email{\footnotesize \url{schaller@strw.leidenuniv.nl}} \alignauthor Pedro~Gonnet\\ \affaddr{School of Engineering and Computing Sciences}\\ diff --git a/theory/RadiativeTransfer/HLLRiemannSolverEigenvalues/get_eigenvalues.f90 b/theory/RadiativeTransfer/HLLRiemannSolverEigenvalues/get_eigenvalues.f90 index 90f5f32d029f9d40b3eca85f02da5448aaed7473..e26e2c1832dc113b144b0d4f16d77f232d73c50c 100644 --- a/theory/RadiativeTransfer/HLLRiemannSolverEigenvalues/get_eigenvalues.f90 +++ b/theory/RadiativeTransfer/HLLRiemannSolverEigenvalues/get_eigenvalues.f90 @@ -375,7 +375,7 @@ subroutine write_eigenvals ! enddo ! enddo - ! Write for python plotting + ! Write for python3 plotting !-------------------------- open(unit=1, file="eigenvals.txt") write(1, "(A2,I5)") "# ", n_points + 1 diff --git a/theory/SPH/EoS/run.sh b/theory/SPH/EoS/run.sh index 72ef573fb26885563e89561d172c15a5a0081564..748e57f7d6938f73d5a8fb4fb1861f57f6345c92 100755 --- a/theory/SPH/EoS/run.sh +++ b/theory/SPH/EoS/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -python kernels.py +python3 kernels.py pdflatex -jobname=eos eos_standalone.tex bibtex eos.aux pdflatex -jobname=eos eos_standalone.tex diff --git a/theory/SPH/Flavours/plotSoundspeed.py b/theory/SPH/Flavours/plotSoundspeed.py index b9f7acec0d4233195ee22657364ad5a9788c423d..2efed14685c1a4d8571bdbbf12b53089425c4e56 100644 --- a/theory/SPH/Flavours/plotSoundspeed.py +++ b/theory/SPH/Flavours/plotSoundspeed.py @@ -34,10 +34,8 @@ if __name__ == "__main__": "figure.subplot.hspace": 0.0, "lines.markersize": 6, "lines.linewidth": 3.0, - "text.latex.unicode": True, } matplotlib.rcParams.update(params) - matplotlib.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) from matplotlib.colors import LogNorm diff --git a/theory/SPH/Flavours/run.sh b/theory/SPH/Flavours/run.sh index 39677c961cfa6557ae0db52d04ff15e39949c80c..de4e9e385d9d553eabd6e3043ce5ce701a0298b1 100755 --- a/theory/SPH/Flavours/run.sh +++ b/theory/SPH/Flavours/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -python plotSoundspeed.py +python3 plotSoundspeed.py pdflatex -jobname=sph_flavours sph_flavours_standalone.tex bibtex sph_flavours.aux diff --git a/theory/SPH/Kernels/kernels.py b/theory/SPH/Kernels/kernels.py index f6f0f46069e5ce41e459182595277b8b48b8bbc8..9c7275d159bb66a9c7ec920031ba3bc38e02b4bc 100644 --- a/theory/SPH/Kernels/kernels.py +++ b/theory/SPH/Kernels/kernels.py @@ -1,6 +1,6 @@ ############################################################################### # This file is part of SWIFT. -# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2016 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 @@ -44,10 +44,8 @@ params = { "figure.subplot.hspace": 0.0, "lines.markersize": 6, "lines.linewidth": 3.0, - "text.latex.unicode": True, } rcParams.update(params) -rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) # params = { @@ -69,7 +67,6 @@ rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) # 'figure.subplot.hspace' : 0. , # 'lines.markersize' : 6, # 'lines.linewidth' : 1.5, -# 'text.latex.unicode': True # } # rcParams.update(params) # rc('font',**{'family':'sans-serif','sans-serif':['Times']}) @@ -459,7 +456,7 @@ legend( # Same but now in log space subplot(212, yscale="log") -plot(xx, Gaussian(xx, h), "k-", linewidth=0.7, label="${\\rm Gaussian}$", lw=1.5) +plot(xx, Gaussian(xx, h), "k-", linewidth=0.7, label="${\\rm Gaussian}$") plot(xx, W_cubic_spline(xx), "b-", label="${\\rm Cubic~spline}$", lw=1.5) plot(xx, W_quartic_spline(xx), "c-", label="${\\rm Quartic~spline}$", lw=1.5) plot(xx, W_quintic_spline(xx), "g-", label="${\\rm Quintic~spline}$", lw=1.5) @@ -751,7 +748,7 @@ figure() subplot(211) plot([0, 2.5 * h], [0.0, 0.0], "k--", linewidth=0.7) -plot(xx, d_Gaussian(xx, h), "k-", linewidth=0.7, label="${\\rm Gaussian}$", lw=1.5) +plot(xx, d_Gaussian(xx, h), "k-", linewidth=0.7, label="${\\rm Gaussian}$") plot(xx, dWdx_cubic_spline(xx), "b-", label="${\\rm Cubic~spline}$", lw=1.5) plot(xx, dWdx_quartic_spline(xx), "c-", label="${\\rm Quartic~spline}$", lw=1.5) plot(xx, dWdx_quintic_spline(xx), "g-", label="${\\rm Quintic~spline}$", lw=1.5) diff --git a/theory/SPH/Kernels/run.sh b/theory/SPH/Kernels/run.sh index 5f2c8569deebad81203ea8cc27c348a0d1607c0a..567e55b205bc1f80fa9dbf9865dea4ece1e9bda9 100755 --- a/theory/SPH/Kernels/run.sh +++ b/theory/SPH/Kernels/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -python kernels.py +python3 kernels.py pdflatex -jobname=kernel_definitions kernel_definitions_standalone.tex bibtex kernel_definitions.aux pdflatex -jobname=kernel_definitions kernel_definitions_standalone.tex diff --git a/theory/SPH/run.sh b/theory/SPH/run.sh index 8d33be12825a31b2906e1259e31185fee2cc74bf..534c4cc0e68ee9eea8d9224c70f8be6d9d300df0 100755 --- a/theory/SPH/run.sh +++ b/theory/SPH/run.sh @@ -1,11 +1,11 @@ #!/bin/bash cd Kernels -python kernels.py +python3 kernels.py cp kernels.pdf .. cp kernel_derivatives.pdf .. cd .. cd Flavours -python plotSoundspeed.py +python3 plotSoundspeed.py cp sedov_blast_soundspeed.pdf .. cd .. pdflatex swift_sph.tex diff --git a/theory/latex/Figures/Gravity/force.py b/theory/latex/Figures/Gravity/force.py index 2fe1199cde1a1e5101d7a90f50b675b650609a10..19454186eabf3e901163ddfeb6cca1f602b0ee79 100644 --- a/theory/latex/Figures/Gravity/force.py +++ b/theory/latex/Figures/Gravity/force.py @@ -21,7 +21,6 @@ params = { "figure.subplot.hspace": 0.2, # the amount of height reserved for white space between subplots "lines.markersize": 4, #'axes.formatter.limits' : (, 0), - "text.latex.unicode": True, } rcParams.update(params) rc("font", family="serif") diff --git a/tools/Makefile.am b/tools/Makefile.am index ff2407fd19831fe78159fbd0cfaec6e3c43758fa..c53f246d03e03c0375c50480cdc7f8390ddc9326 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,6 +1,6 @@ # Scripts to plot task graphs EXTRA_DIST = task_plots/plot_tasks.py task_plots/analyse_tasks.py \ - task_plots/process_plot_tasks_MPI task_plots/process_plot_tasks + task_plots/process_plot_tasks_MPI.py task_plots/process_plot_tasks.py # Scripts to plot threadpool 'task' graphs EXTRA_DIST += task_plots/analyse_threadpool_tasks.py \ @@ -30,7 +30,7 @@ EXTRA_DIST += plot_task_dependencies.py \ plot_task_level.py # Cell hierarchies -EXTRA_DIST += make_cell_hierarchy.sh \ +EXTRA_DIST += make_cell_hierarchy.py \ data/cell_hierarchy.html # Cell dumps diff --git a/tools/analyse_runtime.py b/tools/analyse_runtime.py index 45867684ac6d3f3c116f89c8a769770eac7cbe93..bfb501d814eb32c5a571a59ed0d789f944659633 100755 --- a/tools/analyse_runtime.py +++ b/tools/analyse_runtime.py @@ -2,7 +2,7 @@ ################################################################################ # This file is part of SWIFT. -# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published diff --git a/tools/check_interactions.sh b/tools/check_interactions.sh index d688e69bb36b628905668183989d08204604c631..89a8e6e6dab502d4714a844ab87bf1a0d20963ba 100755 --- a/tools/check_interactions.sh +++ b/tools/check_interactions.sh @@ -18,7 +18,7 @@ make clean; make -j 6 cd examples/SedovBlast_3D/ ./getGlass.sh -python makeIC.py +python3 makeIC.py ../swift --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 @@ -72,7 +72,7 @@ cd examples/SedovBlast_3D/ mv sedov_0000.hdf5 sedov_vec.hdf5 # Compare outputs -if python ../check_ngbs.py sedov_naive.hdf5 sedov_serial.hdf5 +if python3 ../check_ngbs.py sedov_naive.hdf5 sedov_serial.hdf5 then echo "SedovBlast_3D comparison between naive and serial passed" else @@ -80,7 +80,7 @@ else exit 1 fi -if python ../check_ngbs.py sedov_naive.hdf5 sedov_vec.hdf5 +if python3 ../check_ngbs.py sedov_naive.hdf5 sedov_vec.hdf5 then echo "SedovBlast_3D comparison between naive and vectorised passed" else @@ -88,7 +88,7 @@ else exit 1 fi -if python ../check_ngbs.py sedov_serial.hdf5 sedov_vec.hdf5 +if python3 ../check_ngbs.py sedov_serial.hdf5 sedov_vec.hdf5 then echo "SedovBlast_3D comparison between serial and vectorised passed" else @@ -103,7 +103,7 @@ cd ../EAGLE_12/ mv eagle_0000.hdf5 eagle_12_vec.hdf5 # Compare outputs -if python ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_serial.hdf5 +if python3 ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_serial.hdf5 then echo "EAGLE_12 comparison between naive and serial passed" else @@ -111,7 +111,7 @@ else exit 1 fi -if python ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_vec.hdf5 +if python3 ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_vec.hdf5 then echo "EAGLE_12 comparison between naive and vectorised passed" else @@ -119,7 +119,7 @@ else exit 1 fi -if python ../check_ngbs.py eagle_12_serial.hdf5 eagle_12_vec.hdf5 +if python3 ../check_ngbs.py eagle_12_serial.hdf5 eagle_12_vec.hdf5 then echo "EAGLE_12 comparison between serial and vectorised passed" else @@ -194,7 +194,7 @@ mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tole mv sedov_0000.hdf5 sedov_vec.hdf5 # Compare outputs -if python ../check_ngbs.py sedov_naive.hdf5 sedov_serial.hdf5 +if python3 ../check_ngbs.py sedov_naive.hdf5 sedov_serial.hdf5 then echo "SedovBlast_3D comparison between naive and serial passed (MPI)" else @@ -202,7 +202,7 @@ else exit 1 fi -if python ../check_ngbs.py sedov_naive.hdf5 sedov_vec.hdf5 +if python3 ../check_ngbs.py sedov_naive.hdf5 sedov_vec.hdf5 then echo "SedovBlast_3D comparison between naive and vectorised passed (MPI)" else @@ -210,7 +210,7 @@ else exit 1 fi -if python ../check_ngbs.py sedov_serial.hdf5 sedov_vec.hdf5 +if python3 ../check_ngbs.py sedov_serial.hdf5 sedov_vec.hdf5 then echo "SedovBlast_3D comparison between serial and vectorised passed (MPI)" else @@ -225,7 +225,7 @@ mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 eagle_12.yml -P SPH:h_t mv eagle_0000.hdf5 eagle_12_vec.hdf5 # Compare outputs -if python ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_serial.hdf5 +if python3 ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_serial.hdf5 then echo "EAGLE_12 comparison between naive and serial passed (MPI)" else @@ -233,7 +233,7 @@ else exit 1 fi -if python ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_vec.hdf5 +if python3 ../check_ngbs.py eagle_12_naive.hdf5 eagle_12_vec.hdf5 then echo "EAGLE_12 comparison between naive and vectorised passed (MPI)" else @@ -241,7 +241,7 @@ else exit 1 fi -if python ../check_ngbs.py eagle_12_serial.hdf5 eagle_12_vec.hdf5 +if python3 ../check_ngbs.py eagle_12_serial.hdf5 eagle_12_vec.hdf5 then echo "EAGLE_12 comparison between serial and vectorised passed (MPI)" exit 0 diff --git a/tools/combine_ics.py b/tools/combine_ics.py index d96587bcba6f39195ae0137afb9a3cedcd5c1c8c..2497484d9311c85d36448286326e1149f24d7add 100755 --- a/tools/combine_ics.py +++ b/tools/combine_ics.py @@ -16,7 +16,7 @@ to apply to all the fields in the file. Use 0 to cancel all coompression. The default value is `4`. This file is part of SWIFT. -Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +Copyright (C) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) All Rights Reserved. diff --git a/tools/create_virtual_snapshot.py b/tools/create_virtual_snapshot.py new file mode 100644 index 0000000000000000000000000000000000000000..7d9b01e01b7e4af2d081ed47d2ef2fc217ca9feb --- /dev/null +++ b/tools/create_virtual_snapshot.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python + +""" +Usage: + ./create_virtual_snapshot.py SNAPSHOT_NAME + +where SNAPSHOT_NAME is one of multiple snapshots in a multi-file snapshot. +SNAPSHOT_NAME is assumed to be of the format PREFIX.COUNTER.hdf5, where COUNTER +runs from 0 to the number of files minus one. + +This script will create a new file, PREFIX.hdf5, which contains the same metadata +as the original multi-file snapshot, but presents itself as a single snapshot file, +using HDF5's virtual dataset feature. For any other tool reading the file, it will +look as if the new virtual file is a single snapshot, except for the attribute +"Header:Virtual", which will be set to 1. + +This file is part of SWIFT. + +Copyright (C) Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +All Rights Reserved. + +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 numpy as np +import h5py +import argparse +import os +import re + +# pre-compile some regular expressions +filename_re = re.compile("(\S+)\.([0-9]+)\.hdf5\Z") +parttype_re = re.compile("PartType([0-6]+)\Z") +partname_re = re.compile("(\S+)Particles\Z") + +# match PartTypeX to a human friendly group name +partname_dict = { + "PartType0": "GasParticles", + "PartType1": "DMParticles", + "PartType2": "DMBackgroundParticles", + "PartType3": "SinkParticles", + "PartType4": "StarsParticles", + "PartType5": "BHParticles", + "PartType6": "NeutrinoParticles", +} + +# parse the single command-line argument +argparser = argparse.ArgumentParser( + "Create a virtual snapshot for the given multi-file snapshot." +) +argparser.add_argument( + "input", + action="store", + help="File name of one of the files in the multi-file snapshot.", +) +argparser.add_argument( + "--force", + action="store_true", + help="Forcefully overwrite the virtual snapshot if it already exists.", +) +args = argparser.parse_args() + +# parse the input file name +try: + prefix, file_nr = filename_re.match(args.input).groups() +except: + raise RuntimeError( + f"Could not decompose input filename {args.input}." + " Make sure it has the format PREFIX.FILE_NR.hdf5!" + ) +file_nr = int(file_nr) + +# open the input file and get some useful information: +# - the number of files +# - the index of that particular file (sanity check) +# - the (total) number of particles +try: + with h5py.File(args.input, "r") as handle: + nfile = handle["/Header"].attrs["NumFilesPerSnapshot"][0] + ifile = handle["/Header"].attrs["ThisFile"][0] + particle_counts = handle["/Header"].attrs["NumPart_Total_HighWord"][:] + particle_counts = particle_counts.astype(np.int64) + particle_counts <<= 32 + particle_counts += handle["/Header"].attrs["NumPart_Total"][:] +except: + raise RuntimeError(f"Cannot open file {args.input}!") + +# perform a sanity check on the file index +if ifile != file_nr: + raise RuntimeError( + f"File index ({ifile}) does not match file name index ({file_nr})." + " Something is wrong with these files!" + ) + +# check that we are actually dealing with a multi-file snapshot +# (in principle, calling this script on a single file snapshot will fail when parsing the file name) +if nfile == 1: + print( + "You are running this script on a single file snapshot. No virtual file needs to be created!" + ) + exit(0) + +# compose the new virtual file name +output_filename = f"{prefix}.hdf5" + +# check that the file does not exist yet +if os.path.exists(output_filename): + print(f"Output file {output_filename} already exists!") + if args.force: + print("Forcefully overwriting the existing file, as requested.") + else: + print("Not overwriting it. Use --force to overwrite the existing file.") + exit(0) + +# copy all the groups (and datasets) that do not contain particles +# they do not require any changes, or only require minor changes +try: + with h5py.File(args.input, "r") as ifile, h5py.File(output_filename, "w") as ofile: + for group in ifile.keys(): + if parttype_re.match(group) is None and partname_re.match(group) is None: + ifile.copy(group, ofile) +except: + raise RuntimeError( + "Something went wrong while trying to copy over non-particle groups from" + f" {args.input} to {output_filename}!" + ) + +# update the header of the virtual snapshot file +# the virtual file presents itself as an actual single file snapshot containing all the particles +# however, we set Header:Virtual to 1 to distinguish it from such a snapshot +try: + with h5py.File(output_filename, "r+") as handle: + handle["/Header"].attrs["NumFilesPerSnapshot"] = np.array([1], dtype=np.int32) + handle["/Header"].attrs["ThisFile"] = np.array([0], dtype=np.int32) + handle["/Header"].attrs["Virtual"] = np.array([1], dtype=np.int32) + handle["/Header"].attrs["NumPart_ThisFile"] = particle_counts +except: + raise RuntimeError( + f"Could not update header properties of output file {output_filename}!" + ) + +# now process the particle datasets +# first, we loop over all the files to obtain the data type and shape of each dataset +# we also count the number of elements in the dataset belonging to each sub-file +particle_groups = {} +for ifile in range(nfile): + thisfile = f"{prefix}.{ifile}.hdf5" + try: + with h5py.File(thisfile, "r") as handle: + for group in handle.keys(): + if parttype_re.match(group) is not None: + if not group in particle_groups: + particle_groups[group] = {} + for dset in handle[group].keys(): + this_shape = handle[group][dset].shape + if not dset in particle_groups[group]: + if len(this_shape) == 2: + shape = this_shape[1] + else: + shape = 1 + particle_groups[group][dset] = { + "dtype": handle[group][dset].dtype, + "shape": shape, + "sizes": np.zeros(nfile, dtype=np.int64), + } + particle_groups[group][dset]["sizes"][ifile] = this_shape[0] + except: + raise RuntimeError( + f"Something went wrong while retrieving group information from {thisfile}!" + ) + +# now we have all the information to create the new virtual datasets +# these present themselves as if they are a normal dataset containing values for all the particles +# however, the data is provided by a virtual link to the corresponding dataset in the original sub-file +try: + with h5py.File(output_filename, "r+") as handle: + for group in particle_groups: + newgroup = handle.create_group(group) + for dset in particle_groups[group]: + path = f"{group}/{dset}" + dtype = particle_groups[group][dset]["dtype"] + shape = particle_groups[group][dset]["shape"] + sizes = particle_groups[group][dset]["sizes"] + offsets = np.cumsum(sizes) + totsize = offsets[-1] + offsets -= sizes + if shape == 1: + real_shape = (totsize,) + else: + real_shape = (totsize, shape) + layout = h5py.VirtualLayout(shape=real_shape, dtype=dtype) + for ifile in range(nfile): + if shape == 1: + this_shape = (sizes[ifile],) + else: + this_shape = (sizes[ifile], shape) + layout[ + offsets[ifile] : offsets[ifile] + sizes[ifile] + ] = h5py.VirtualSource( + f"{prefix}.{ifile}.hdf5", path, shape=this_shape + ) + newgroup.create_virtual_dataset(dset, layout) + # also create the soft link to the dataset, now that we are processing it anyway + handle[partname_dict[group]] = h5py.SoftLink(group) +except: + raise RuntimeError( + f"Something went wrong while setting up virtual datasets in {output_filename}!" + ) + +# copy over the attributes of the particle groups and datasets +try: + with h5py.File(args.input, "r") as ifile, h5py.File(output_filename, "r+") as ofile: + for group in ifile.keys(): + if parttype_re.match(group) is not None: + for attr in ifile[group].attrs: + ofile[group].attrs[attr] = ifile[group].attrs[attr] + for dset in ifile[group].keys(): + path = f"{group}/{dset}" + for attr in ifile[path].attrs: + ofile[path].attrs[attr] = ifile[path].attrs[attr] +except: + raise RuntimeError( + "Something went wrong while copying dataset attributes from" + f" {args.input} to {output_filename}!" + ) + +# finally: update the cell metadata +# again, the virtual file presents itself as a single file snapshot +# this means that the Cells/Files elements are all 0 +# it also means we have to update the offsets for all cells with the number of particles in cells +# belonging to files with a lower index +# we first gather these offsets (they are the same as the dataset offsets) +particle_offsets = {} +for group in particle_groups: + for dset in particle_groups[group]: + particle_offsets[group] = particle_groups[group][dset]["sizes"] + break + particle_offsets[group] = ( + np.cumsum(particle_offsets[group], dtype=np.int64) - particle_offsets[group] + ) + +# now we actually update the cell metadata +try: + with h5py.File(output_filename, "r+") as handle: + for group in particle_offsets: + files = handle[f"Cells/Files/{group}"][:] + handle[f"Cells/OffsetsInFile/{group}"][:] += particle_offsets[group][files] + handle[f"Cells/Files/{group}"][:] = 0 +except: + raise RuntimeError( + f"Something went wrong while updating cell metadata for {output_filename}!" + ) + +print(f"Finished writing {output_filename}.") diff --git a/tools/make_cell_hierarchy.py b/tools/make_cell_hierarchy.py new file mode 100755 index 0000000000000000000000000000000000000000..d55c736fe5bce04f4d98214824972930c8b6bf63 --- /dev/null +++ b/tools/make_cell_hierarchy.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +""" +Usage: + make_cell_hierarchy.py --input <LIST OF FILES> --output <OUTPUT PREFIX> + [--serve] + +Where <LIST OF FILES> is a list of input files (or file pattern matching that +list) corresponding to the cell hierarchy on different MPI ranks for a single +time step. +<OUTPUT PREFIX> is the name of the output .html and .csv files that will be +created. If the prefix path contains a directory that does not exist, this +directory will be created. +If the optional argument --serve is provided, the script will start a local +server that can run the generated .html page and opens it in a browser. + +Based on a bash script written by Loic Hausammann. + +This file is part of SWIFT. + +Copyright (C) 2021 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +All Rights Reserved. + +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 argparse +import os +import sys + +# Get the script folder, we need to retrieve the html template from it +scriptfolder = os.path.dirname(sys.argv[0]) + +# Handle the command line. +parser = argparse.ArgumentParser(description="Create cell hierarchy web page.") + +parser.add_argument("input", help="Input file(s)", nargs="+") +parser.add_argument("output", help="Output file prefix") +parser.add_argument( + "--serve", + help="Run a local web server and display the generated web page?", + action="store_true", +) +args = parser.parse_args() + +# First check if the output prefix contains a folder and create it to make +# sure it exists +folder, fileprefix = os.path.split(args.output) +if len(folder) > 0: + os.makedirs(folder, exist_ok=True) +else: + folder = None + +# Accumulate all input files into a single file +with open("{0}.csv".format(args.output), "w") as ofile: + for fname in sorted(args.input): + if not os.path.exists(fname): + print('Error: "{0}" does not exist!'.format(fname)) + exit(1) + with open(fname, "r") as ifile: + ofile.write(ifile.read()) + +# Read the html template +with open("{0}/data/cell_hierarchy.html".format(scriptfolder), "r") as ifile: + html = ifile.read() + +# Replace the old csv file with the actual csv file +csvname = "{0}.csv".format(fileprefix) +html = html.replace("cell_hierarchy.csv", csvname) + +# Write out the new html file +with open("{0}.html".format(args.output), "w") as ofile: + ofile.write(html) + +if args.serve: + import http.server + import socketserver + import webbrowser + + class Handler(http.server.SimpleHTTPRequestHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, directory=folder, **kwargs) + + print("Running a local server. Use CTRL+C to stop it.") + PORT = 8000 + found_port = False + while not found_port: + try: + with socketserver.TCPServer(("", PORT), Handler) as httpd: + found_port = True + print("serving at port", PORT) + webbrowser.open( + "http://localhost:{0}/{1}.html".format(PORT, fileprefix) + ) + httpd.serve_forever() + except: + PORT += 1 diff --git a/tools/make_cell_hierarchy.sh b/tools/make_cell_hierarchy.sh deleted file mode 100755 index 9d1d3caf7c4e2f0514c3d6ad5b2db48efa8958d5..0000000000000000000000000000000000000000 --- a/tools/make_cell_hierarchy.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -output=cell_hierarchy.html - -# merge all mpi ranks together -csv_output=cell_hierarchy.csv -if [ -f $csv_output ] -then - rm $csv_output -fi - -for filename in $@; -do - cat $filename >> cell_hierarchy.csv -done - -# copy HTML page to the repository -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -cp $DIR/data/cell_hierarchy.html $output - -echo $output has been generated diff --git a/tools/parallel_replicate_ICs.py b/tools/parallel_replicate_ICs.py index c01251ce90d3f61776c394f62037da1290bed6c1..f2762a1ccd503e0dc866d0b6b8be6a4d6d684113 100755 --- a/tools/parallel_replicate_ICs.py +++ b/tools/parallel_replicate_ICs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ Usage: - python parallel_replicate_ICs.py IC_file.hdf5 rep_fac + python3 parallel_replicate_ICs.py IC_file.hdf5 rep_fac where IC_file.hdf5 is the ICs file that you want to replicate and rep_fac is the replication factor in each dimension @@ -11,7 +11,7 @@ Reads in a ICs file and replicates the particles in each dimension by the replication factor given and write a new IC called IC_file_xrep_fac.hdf5. Example: - python parallel_replicate_ICs.py EAGLE_ICs_50.hdf5 4 + python3 parallel_replicate_ICs.py EAGLE_ICs_50.hdf5 4 Running the above example will produce a tiled 50MPc box in each dimension to give a 200MPc box. @@ -87,7 +87,7 @@ def main(): # Parse command line arguments if len(sys.argv) < 3: print("Error: pass input file and replication factor (integer) as arguments.") - print("python replicate_ICs.py EAGLE_ICs_50.hdf5 4") + print("python3 replicate_ICs.py EAGLE_ICs_50.hdf5 4") sys.exit() else: inputFile = sys.argv[1] diff --git a/tools/plot_ghost_stats.py b/tools/plot_ghost_stats.py new file mode 100644 index 0000000000000000000000000000000000000000..6e88043076aa5b9011ea5e0b7d7c9cf7f0ddfe25 --- /dev/null +++ b/tools/plot_ghost_stats.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python +# +# Usage: +# python plot_ghost_stats.py ghost_stats_file_1 [ghost_stats_file_2...] +# output_image.png +# +# Description: +# Plot the ghost statistics contained in the given input files. For each +# particle type (hydro, stars, black holes), three plots are generated: +# 1. A histogram of the number of active particles as a function of iteration +# number. +# 2. The distribution of h values for active particles as a function of the +# iteration number, compared with the final (converged) values. +# 3. The number of particles that are treated as having no neighbours as a +# function of the iteration number. + +import numpy as np +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as pl +import argparse + +argparser = argparse.ArgumentParser() +argparser.add_argument("input", nargs="+", action="store") +argparser.add_argument("output", action="store") +argparser.add_argument("--label", "-l", action="store", default=None) +args = argparser.parse_args() + +data = None +nbin = None +nval = None +for input in args.input: + # read the header block to figure out how many blocks per particle type + # we have + with open(input, "r") as ifile: + header = ifile.readlines()[:15] + this_nbin = int(header[3].split()[-1]) + this_nval = int(header[4].split()[-1]) + if nbin is None: + nbin = this_nbin + else: + if nbin != this_nbin: + print("Number of bins in files does not match!") + exit(1) + if nval is None: + nval = this_nval + else: + if nval != this_nval: + print("Number of values per bin in files does not match!") + exit(1) + + tdata = np.loadtxt(input, ndmin=2) + if len(tdata) > 0: + if data is None: + data = np.array(tdata) + else: + data = np.append(data, tdata, axis=0) + +# omit meaningless first column +data = data[:, 1:] + +hdata = data[:, : nval * nbin] +sdata = data[:, nval * nbin : 2 * nval * nbin] +bdata = data[:, 2 * nval * nbin :] + + +def get_total_stats(pdata): + counts = pdata[:, ::nval] + ncell = (counts[:, 0] > 0).sum() + if ncell == 0: + return None, None, None, None, None, None, None, 0 + nongb_counts = np.sum(pdata[:, 1::nval], axis=0) + hmin = np.min(pdata[:, 2::nval], axis=0, initial=np.inf, where=counts > 0) + hmax = np.max(pdata[:, 3::nval], axis=0, initial=0.0, where=counts > 0) + hsum = np.sum(pdata[:, 4::nval], axis=0, where=counts > 0) + hsum2 = np.sum(pdata[:, 5::nval], axis=0, where=counts > 0) + counts = counts.sum(axis=0) + filt = counts > 0 + hmean = hsum + hmean[filt] /= counts[filt] + hmean2 = hsum2 + hmean2[filt] /= counts[filt] + hstd = np.sqrt(hmean2 - hmean ** 2) + bins = np.arange(hmean.shape[0]) + return ( + bins[filt], + counts[filt], + nongb_counts[filt], + hmin[filt], + hmax[filt], + hmean[filt], + hstd[filt], + ncell, + ) + + +def no_values(ax): + ax.text(0.5, 0.5, "No values", horizontalalignment="center", transform=ax.transAxes) + return + + +hbins, hcounts, hno_ngb, hhmin, hhmax, hhmean, hhstd, hncell = get_total_stats( + hdata[:, : nval * (nbin - 1)] +) +htbins, htcounts, htno_ngb, hthmin, hthmax, hthmean, hthstd, htncell = get_total_stats( + hdata[:, nval * (nbin - 1) :] +) +sbins, scounts, sno_ngb, shmin, shmax, shmean, shstd, sncell = get_total_stats( + sdata[:, : nval * (nbin - 1)] +) +stbins, stcounts, stno_ngb, sthmin, sthmax, sthmean, sthstd, stncell = get_total_stats( + sdata[:, nval * (nbin - 1) :] +) +bbins, bcounts, bno_ngb, bhmin, bhmax, bhmean, bhstd, bncell = get_total_stats( + bdata[:, : nval * (nbin - 1)] +) +btbins, btcounts, btno_ngb, bthmin, bthmax, bthmean, bthstd, btncell = get_total_stats( + bdata[:, nval * (nbin - 1) :] +) + +fig, ax = pl.subplots(3, 3, sharex=True, figsize=(10, 8)) + +if hncell > 0: + ax[0][0].axhline(y=htcounts[0], linestyle="--", color="k") + ax[1][0].axhline(y=hthmin, linestyle=":", color="k") + ax[1][0].axhline(y=hthmean - hthstd, linestyle="--", color="k") + ax[1][0].axhline(y=hthmean, linestyle="-", color="k") + ax[1][0].axhline(y=hthmean + hthstd, linestyle="--", color="k") + ax[1][0].axhline(y=hthmax, linestyle=":", color="k") + ax[2][0].axhline(y=htno_ngb[0], linestyle="--", color="k") + + ax[0][0].bar(hbins, hcounts, log=True) + + ax[1][0].plot(hbins, hhmin, label="min") + ax[1][0].plot(hbins, hhmax, label="max") + ax[1][0].fill_between(hbins, hhmean - hhstd, hhmean + hhstd, color="C2", alpha=0.5) + ax[1][0].plot(hbins, hhmean, color="C2", label="mean") + ax[1][0].set_yscale("log") + + if hno_ngb.sum() > 0: + ax[2][0].bar(hbins, hno_ngb, log=True) + else: + no_values(ax[2][0]) +else: + no_values(ax[0][0]) + no_values(ax[1][0]) + no_values(ax[2][0]) + +if sncell > 0: + ax[0][1].axhline(y=stcounts[0], linestyle="--", color="k") + ax[1][1].axhline(y=sthmin, linestyle=":", color="k") + ax[1][1].axhline(y=sthmean - sthstd, linestyle="--", color="k") + ax[1][1].axhline(y=sthmean, linestyle="-", color="k") + ax[1][1].axhline(y=sthmean + sthstd, linestyle="--", color="k") + ax[1][1].axhline(y=sthmax, linestyle=":", color="k") + ax[2][1].axhline(y=stno_ngb[0], linestyle="--", color="k") + + ax[0][1].bar(sbins, scounts, log=True) + + ax[1][1].plot(sbins, shmin) + ax[1][1].plot(sbins, shmax) + ax[1][1].fill_between(sbins, shmean - shstd, shmean + shstd, color="C2", alpha=0.5) + ax[1][1].plot(sbins, shmean, color="C2") + ax[1][1].set_yscale("log") + + if sno_ngb.sum() > 0: + ax[2][1].bar(sbins, sno_ngb, log=True) + else: + no_values(ax[2][1]) +else: + no_values(ax[0][1]) + no_values(ax[1][1]) + no_values(ax[2][1]) + +if bncell > 0: + ax[0][2].axhline(y=btcounts[0], linestyle="--", color="k") + ax[1][2].axhline(y=bthmin, linestyle=":", color="k") + ax[1][2].axhline(y=bthmean - bthstd, linestyle="--", color="k") + ax[1][2].axhline(y=bthmean, linestyle="-", color="k") + ax[1][2].axhline(y=bthmean + bthstd, linestyle="--", color="k") + ax[1][2].axhline(y=bthmax, linestyle=":", color="k") + ax[2][2].axhline(y=btno_ngb[0], linestyle="--", color="k") + + ax[0][2].bar(bbins, bcounts, log=True) + + ax[1][2].plot(bbins, bhmin) + ax[1][2].plot(bbins, bhmax) + ax[1][2].fill_between(bbins, bhmean - bhstd, bhmean + bhstd, color="C2", alpha=0.5) + ax[1][2].plot(bbins, bhmean, color="C2") + ax[1][2].set_yscale("log") + + if bno_ngb.sum() > 0: + ax[2][2].bar(bbins, bno_ngb, log=True) + else: + no_values(ax[2][2]) +else: + no_values(ax[0][2]) + no_values(ax[1][2]) + no_values(ax[2][2]) + +ax[1][0].set_xlabel("iteration") +ax[1][1].set_xlabel("iteration") +ax[1][2].set_xlabel("iteration") + +ax[0][0].set_ylabel("count") +ax[1][0].set_ylabel("h") +ax[2][0].set_ylabel("no ngb count") + +ax[1][0].legend(loc="best") +ax[1][1].plot([], [], "k-", label="converged value") +ax[1][1].legend(loc="best") + +ax[0][0].set_xlim(-0.5, nbin - 1.5) +ax[0][0].set_xticks(np.arange(nbin)) +ax[0][0].set_title("hydro") +ax[0][1].set_title("stars") +ax[0][2].set_title("black holes") +ax[1][0].set_title("{0} cells".format(hncell)) +ax[1][1].set_title("{0} cells".format(sncell)) +ax[1][2].set_title("{0} cells".format(bncell)) + +if not args.label is None: + pl.suptitle(args.label) +else: + pl.suptitle(",".join(args.input)) + +pl.tight_layout() +pl.savefig(args.output, dpi=300) diff --git a/tools/plot_gravity_checks.py b/tools/plot_gravity_checks.py index 684805d50115e4258b71b9dc67c47059841426de..9a1e61c46a90eeaf50b247d6c219d59f3c0521de 100755 --- a/tools/plot_gravity_checks.py +++ b/tools/plot_gravity_checks.py @@ -25,7 +25,6 @@ params = { "lines.linewidth": 3.0, } plt.rcParams.update(params) -plt.rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) min_error = 1e-7 max_error = 3e-1 diff --git a/tools/plot_scaling_results.py b/tools/plot_scaling_results.py index 2c29d93f88cbe4bde83f3473e9f738a526480c1c..82670005169a276ca767a455af5ca1455b00adb1 100755 --- a/tools/plot_scaling_results.py +++ b/tools/plot_scaling_results.py @@ -1,13 +1,13 @@ #!/usr/bin/env python # # Usage: -# python plot_scaling_results.py input-file1-ext input-file2-ext ... +# python3 plot_scaling_results.py input-file1-ext input-file2-ext ... # # Description: # Plots speed up, parallel efficiency and time to solution given a "timesteps" output file generated by SWIFT. # # Example: -# python plot_scaling_results.py _hreads_cosma_stdout.txt _threads_knl_stdout.txt +# python3 plot_scaling_results.py _hreads_cosma_stdout.txt _threads_knl_stdout.txt # # The working directory should contain files 1_threads_cosma_stdout.txt - 64_threads_cosma_stdout.txt and 1_threads_knl_stdout.txt - 64_threads_knl_stdout.txt, i.e wall clock time for each run using a given number of threads @@ -35,10 +35,8 @@ params = { "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"]}) version = [] branch = [] diff --git a/tools/plot_scaling_results_breakdown.py b/tools/plot_scaling_results_breakdown.py index 570ec37ee908dbbe51bfc12fa2c3af59d2d8800a..45a0edf7aa9cdb3806e0f89a6ad6a4164792079a 100755 --- a/tools/plot_scaling_results_breakdown.py +++ b/tools/plot_scaling_results_breakdown.py @@ -1,13 +1,13 @@ #!/usr/bin/env python # # Usage: -# python plot_scaling_results.py input-file1-ext input-file2-ext ... +# python3 plot_scaling_results.py input-file1-ext input-file2-ext ... # # Description: # Plots speed up, parallel efficiency and time to solution given a "timesteps" output file generated by SWIFT. # # Example: -# python plot_scaling_results.py _hreads_cosma_stdout.txt _threads_knl_stdout.txt +# python3 plot_scaling_results.py _hreads_cosma_stdout.txt _threads_knl_stdout.txt # # The working directory should contain files 1_threads_cosma_stdout.txt - 64_threads_cosma_stdout.txt and 1_threads_knl_stdout.txt - 64_threads_knl_stdout.txt, i.e wall clock time for each run using a given number of threads @@ -35,10 +35,8 @@ params = { "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"]}) version = [] branch = [] diff --git a/tools/plot_scaling_results_detailed.py b/tools/plot_scaling_results_detailed.py index f32c77760d3686c44709aa7fd9209e257ac9f8f0..36fc4f7efa9f79e96f392e5774c10e0d89271f9d 100644 --- a/tools/plot_scaling_results_detailed.py +++ b/tools/plot_scaling_results_detailed.py @@ -1,14 +1,14 @@ #!/usr/bin/env python # # Usage: -# python plot_scaling_results_detailed.py input-file-1 input-file-2 +# python3 plot_scaling_results_detailed.py input-file-1 input-file-2 # # Description: # Plots speed up, parallel efficiency and time to solution given the output file generated by SWIFT. # You need to run SWIFT with -v 1. # # Example: -# python plot_scaling_results_detailed.py output_1.log output_2.log +# python3 plot_scaling_results_detailed.py output_1.log output_2.log import sys import re diff --git a/tools/plot_task_dependencies.py b/tools/plot_task_dependencies.py index 41b7509de9bc55655a10cb8342fe4c7c195bac3a..0c6ab887fc0d0d2034584d834005b4152a01baf5 100755 --- a/tools/plot_task_dependencies.py +++ b/tools/plot_task_dependencies.py @@ -348,6 +348,7 @@ def write_task( task_is_in_top, task_is_in_hydro_super, task_is_in_grav_super, + cell_has_active_task, with_calls, with_levels, ): @@ -378,19 +379,41 @@ def write_task( task_is_in_grav_super: bool whether task is in grav super cell + cell_has_active_task: bool + if True, the specific cell you are trying to plot + the graph for has an active task of this type. + Otherwise it only unlocks a dependency of some + other cell + with_calls: bool if True, write down the function calls with_levels: bool if True, write down level at which tasks are called """ + + # This feature is used to add tasks to the graph which have + # no dependencies. While such tasks are not expected to exist, + # it might be a helpful debugging feature. + if name == "task_unlocks_nothing": + return + # generate text txt = "\t " + name + "[" - if implicit: - txt += "style=filled,fillcolor=grey90," - if mpi: - txt += "shape=diamond,style=filled,fillcolor=azure," + if not cell_has_active_task: + # give this precedence over implicit tasks and MPI + # If you're this deep in debugging trouble, + # you will most likely know which tasks are + # implicit. + txt += "style=filled,fillcolor=lightpink2," + if mpi: + txt += "shape=diamond," + else: + if implicit: + txt += "style=filled,fillcolor=grey90," + if mpi: + txt += "shape=diamond,style=filled,fillcolor=azure," if with_levels: levelstr = "" if task_is_in_top: @@ -497,9 +520,13 @@ def write_header(f, data, git, opt): data["task_in_is_top"][i] == 1, data["task_in_is_hydro_super"][i] == 1, data["task_in_is_grav_super"][i] == 1, + True, opt.with_calls, opt.with_levels, ) + # Note: In the case where you are plotting a single cell, + # any task only gets an entry in tasks_in if the specific + # cell has an active task of that type. # do task out for i in range(N): @@ -516,6 +543,7 @@ def write_header(f, data, git, opt): data["task_out_is_top"][i] == 1, data["task_out_is_hydro_super"][i] == 1, data["task_out_is_grav_super"][i] == 1, + data["cell_has_active_task"][i] == 1, opt.with_calls, opt.with_levels, ) @@ -612,6 +640,13 @@ def write_dependencies(f, data): # get data ta = l["task_in"] tb = l["task_out"] + + # This feature is used to add tasks to the graph which have + # no dependencies. While such tasks are not expected to exist, + # it might be a helpful debugging feature. + if tb == "task_unlocks_nothing": + continue + number_link = l["number_link"] # check if already done @@ -710,4 +745,4 @@ if __name__ == "__main__": if args.with_calls: print("We recommand to use the python package xdot available on pypi:") - print(" python -m xdot %s" % dot_output) + print(" python3 -m xdot %s" % dot_output) diff --git a/tools/recover_restart_parameters.py b/tools/recover_restart_parameters.py new file mode 100755 index 0000000000000000000000000000000000000000..38a237b9028851008cd4f6e714ff7f732cb0e770 --- /dev/null +++ b/tools/recover_restart_parameters.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +""" +Usage: + recover_restart_parameters.py <STEP> <LIST OF FILES> + +where <STEP> is a simulation time step number, and <LIST OF FILES> are all +the 'used_parameters.yml[.stepno]' files that were produced by the run. + +This script will reconstruct the parameter file as it was used by SWIFT when +it (last) ran time step <STEP>, and will take into account any potential +parameter changes that happened before restarting. + +Copyright (C) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +All Rights Reserved. + +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 yaml +import argparse +import numpy as np +import datetime +import re +import sys + +argparser = argparse.ArgumentParser( + "Recover the parameters used for a specific time step." +) +argparser.add_argument( + "step", type=int, help="Step for which we want to recover the parameters." +) +argparser.add_argument("file", nargs="+", help="used_parameters.yml* file(s) to parse.") +args = argparser.parse_args() + + +def get_step(filename): + """ + Extract the step number from a used_parameters.yml.stepno file. + Return 0 if no step number was attached (corresponding to the first step). + """ + match = re.search("used_parameters.yml.(\d+)", filename) + if match is None: + return 0 + else: + return int(match[1]) + + +# make a data array that contains the timestamp, step number and filename +# of each input file +files = np.zeros( + len(args.file), + dtype=[("timestamp", np.uint64), ("step", np.uint32), ("filename", "U100")], +) +for i, file in enumerate(args.file): + # make sure we are parsing a used_parameters file + if not "used_parameters.yml" in file: + raise ValueError(f'Incompatible filename: "{file}"!') + # store the filename and step number + files[i]["filename"] = file + files[i]["step"] = get_step(file) + # extract the time stamp from the file header + with open(file, "r") as ifile: + for line in ifile.readlines(): + if "current date:" in line: + timestr = " ".join(line.split()[3:]) + date = datetime.datetime.strptime(timestr, "%H:%M:%S %Y-%m-%d %Z") + files[i]["timestamp"] = date.timestamp() + +# sort the files according to timestamp +isort = np.argsort(files["timestamp"]) +files = files[isort] + +# get the index of the first step +ifirst = np.argmax(files["step"] == 0) +# discard any step files that are older +files = files[ifirst:] + +# filter out all the steps that contributed to the step we want +mask = files["step"] <= args.step +files = files[mask] +# only the first (step 0) and last file matter +files = files[[0, -1]] + + +def update_dictionary(d, nd): + """ + Recursively update the contents of dictionary 'd' with that of dictionary + 'nd'. + """ + for key in nd: + if key in d and isinstance(nd[key], dict): + update_dictionary(d[key], nd[key]) + else: + d[key] = nd[key] + + +# now create the final parameter file +params = {} +for file in files["filename"]: + with open(file, "r") as ifile: + this_params = yaml.safe_load(ifile) + # if no parameters changed compared to step 0, this_params is empty + if not this_params is None: + update_dictionary(params, this_params) + +# dump the result to the stdout +# note that pyYAML will format array parameters as lists instead of inline +# arrays. There is not much we can do about that. +yaml.safe_dump(params, stream=sys.stdout, default_flow_style=False) diff --git a/tools/task_plots/analyse_threadpool_tasks.py b/tools/task_plots/analyse_threadpool_tasks.py index 5c25b7673bde02bb950f332d74382e2e8dc2e85b..34f75ea0732470732953fe40b0ce95ef7512c7ef 100755 --- a/tools/task_plots/analyse_threadpool_tasks.py +++ b/tools/task_plots/analyse_threadpool_tasks.py @@ -28,11 +28,6 @@ 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") -import matplotlib.collections as collections -import matplotlib.ticker as plticker import pylab as pl import sys import argparse diff --git a/tools/task_plots/iplot_tasks.py b/tools/task_plots/iplot_tasks.py index a96ad977c2604581fa1c33a234dd1f19d4b0a62b..50e65ebf9b7b285ce6f8c55f20d1006db1c30e67 100755 --- a/tools/task_plots/iplot_tasks.py +++ b/tools/task_plots/iplot_tasks.py @@ -458,6 +458,35 @@ class Container: else: fig.canvas.mpl_connect("button_press_event", self.onclick) + # Space bar to dump all tasks. Use with caution... + fig.canvas.mpl_connect("key_press_event", self.dump) + + def dump(self, event): + # Dump all tasks to the console sorted by tic. + xlow = float(event.inaxes.viewLim.x0) + xhigh = float(event.inaxes.viewLim.x1) + + if event.key == " ": + dumps = {} + for thread in range(nthread): + tics = self.ltics[thread] + tocs = self.ltocs[thread] + labels = self.llabels[thread] + for i in range(len(tics)): + if (tics[i] > xlow and tics[i] < xhigh) or ( + tocs[i] > xlow and tocs[i] < xhigh + ): + tic = "{0:.3f}".format(tics[i]) + toc = "{0:.3f}".format(tocs[i]) + dumps[tics[i]] = ( + labels[i] + ", tic/toc = " + tic + " / " + toc + ) + print("") + print("Tasks in time range: " + str(xlow) + " -> " + str(xhigh)) + for key in sorted(dumps): + print(dumps[key]) + print("") + def onclick(self, event): # Find thread, then scan for bounded task. try: diff --git a/tools/task_plots/iplot_threadpool.py b/tools/task_plots/iplot_threadpool.py new file mode 100755 index 0000000000000000000000000000000000000000..70ca679e0a22a87c45f710d47b4f915a4f3a50ce --- /dev/null +++ b/tools/task_plots/iplot_threadpool.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python3 +""" +Interactive plot of a threadpool dump. + +Usage: + iplot_threadpool.py [options] input.dat + +where input.dat is a threadpool info file for a step. Use the '-Y interval' +flag of the swift or swift_mpi commands to create these (these will need to be +built with the --enable-threadpool-debugging configure option). + +The plot can be scrolled and zoomed using the standard matplotlib +controls, the type of task at a point can be queried by a mouse click +(unless the --motion option is in effect when a continuous readout is +shown) the task type and tic/toc range are reported in the terminal. + +Requires the tkinter module. + +This file is part of SWIFT. + +Copyright (C) 2022 Peter W. Draper (p.w.draper@durham.ac.uk) +All Rights Reserved. + +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("TkAgg") +import numpy as np +import matplotlib.backends.backend_tkagg as tkagg +from matplotlib.figure import Figure +import tkinter as tk +import math +import matplotlib.collections as collections +import matplotlib.ticker as plticker +import pylab as pl +import sys +import argparse + +# Handle the command line. +parser = argparse.ArgumentParser(description="Plot threadpool function graphs") + +parser.add_argument("input", help="Threadpool data file (-Y output)") +parser.add_argument( + "-m", + "--motion", + dest="motion", + help="Track mouse motion, otherwise clicks (def: clicks)", + default=False, + action="store_true", +) +parser.add_argument( + "-l", + "--limit", + dest="limit", + help="Upper time limit in millisecs (def: depends on data)", + default=0, + type=float, +) +parser.add_argument( + "--height", + dest="height", + help="Height of plot in inches (def: 4)", + default=4.0, + type=float, +) +parser.add_argument( + "--width", + dest="width", + help="Width of plot in inches (def: 16)", + default=16.0, + type=float, +) +parser.add_argument( + "-v", + "--verbose", + dest="verbose", + help="Show colour assignments and other details (def: False)", + default=False, + action="store_true", +) + +args = parser.parse_args() +infile = args.input +delta_t = args.limit + +# Basic plot configuration. +PLOT_PARAMS = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 12, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "figure.figsize": (args.width, args.height), + "figure.subplot.left": 0.03, + "figure.subplot.right": 0.995, + "figure.subplot.bottom": 0.09, + "figure.subplot.top": 0.99, + "figure.subplot.wspace": 0.0, + "figure.subplot.hspace": 0.0, + "lines.markersize": 6, + "lines.linewidth": 3.0, +} +pl.rcParams.update(PLOT_PARAMS) + +# A number of colours for the various types. Recycled when there are +# more task types than colours... +colours = [ + "cyan", + "lightgray", + "darkblue", + "yellow", + "tan", + "dodgerblue", + "sienna", + "aquamarine", + "bisque", + "blue", + "green", + "lightgreen", + "brown", + "purple", + "moccasin", + "olivedrab", + "chartreuse", + "olive", + "darkgreen", + "green", + "mediumseagreen", + "mediumaquamarine", + "darkslategrey", + "mediumturquoise", + "black", + "cadetblue", + "skyblue", + "red", + "slategray", + "gold", + "slateblue", + "blueviolet", + "mediumorchid", + "firebrick", + "magenta", + "hotpink", + "pink", + "orange", + "lightgreen", +] +maxcolours = len(colours) + +# Read header. First two lines. +with open(infile) as infid: + head = [next(infid) for x in range(2)] +header = head[1][2:].strip() +header = eval(header) +nthread = int(header["num_threads"]) + 1 +CPU_CLOCK = float(header["cpufreq"]) / 1000.0 +print("Number of threads: ", nthread) +if args.verbose: + print("CPU frequency:", CPU_CLOCK * 1000.0) + +# Read input. +data = pl.genfromtxt(infile, dtype=None, delimiter=" ", encoding=None) + +# Mixed types, so need to separate. +tics = [] +tocs = [] +funcs = [] +threads = [] +chunks = [] +for i in data: + if i[0] != "#": + funcs.append(i[0].replace("_mapper", "")) + if i[1] < 0: + threads.append(nthread - 1) + else: + threads.append(i[1]) + chunks.append(i[2]) + tics.append(i[3]) + tocs.append(i[4]) +tics = pl.array(tics) +tocs = pl.array(tocs) +funcs = pl.array(funcs) +threads = pl.array(threads) +chunks = pl.array(chunks) + +# Recover the start and end time +mintic_step = min(tics) +tic_step = mintic_step +toc_step = max(tocs) +print("# Min tic = ", mintic_step) + +# Calculate the time range, if not given. +delta_t = delta_t * CPU_CLOCK +if delta_t == 0: + dt = toc_step - tic_step + if dt > delta_t: + delta_t = dt + print("Data range: ", delta_t / CPU_CLOCK, "ms") + +# Once more doing the real gather and plots this time. +start_t = float(tic_step) +tics -= tic_step +tocs -= tic_step +end_t = (toc_step - start_t) / CPU_CLOCK + +# Get all "task" names and assign colours. +TASKTYPES = pl.unique(funcs) +print(TASKTYPES) + +# Set colours of tasks. +TASKCOLOURS = {} +ncolours = 0 +for task in TASKTYPES: + TASKCOLOURS[task] = colours[ncolours] + ncolours = (ncolours + 1) % maxcolours + +# For fiddling with colours... +if args.verbose: + print("#Selected colours:") + for task in sorted(TASKCOLOURS.keys()): + print("# " + task + ": " + TASKCOLOURS[task]) + +tasks = {} +tasks[-1] = [] +for i in range(nthread): + tasks[i] = [] + +for i in range(len(threads)): + thread = threads[i] + tasks[thread].append({}) + tasks[thread][-1]["type"] = funcs[i] + tic = tics[i] / CPU_CLOCK + toc = tocs[i] / CPU_CLOCK + tasks[thread][-1]["tic"] = tic + tasks[thread][-1]["toc"] = toc + tasks[thread][-1]["colour"] = TASKCOLOURS[funcs[i]] + +# Do the plotting. +fig = Figure() +ax = fig.add_subplot(1, 1, 1) +ax.set_xlim(-delta_t * 0.01 / CPU_CLOCK, delta_t * 1.01 / CPU_CLOCK) +ax.set_ylim(0.5, nthread + 1.0) + +ltics = [] +ltocs = [] +llabels = [] +for i in range(nthread): + + # Collect ranges and colours into arrays. Also indexed lists for lookup tables. + tictocs = [] + colours = [] + tics = [] + tocs = [] + labels = [] + for task in tasks[i]: + tictocs.append((task["tic"], task["toc"] - task["tic"])) + colours.append(task["colour"]) + + tics.append(task["tic"]) + tocs.append(task["toc"]) + labels.append(task["type"]) + + # Add to look up tables. + ltics.append(tics) + ltocs.append(tocs) + llabels.append(labels) + + # Now plot. + ax.broken_barh(tictocs, [i + 0.55, 0.9], facecolors=colours, linewidth=0) + +# Start and end of time-step +ax.plot([0, 0], [0, nthread + 1], "k--", linewidth=1) +ax.plot([end_t, end_t], [0, nthread + 1], "k--", linewidth=1) + +# Labels. +ax.set_xlabel("Wall clock time [ms]") +ax.set_ylabel("Thread ID") + +loc = plticker.MultipleLocator(base=1) +ax.yaxis.set_major_locator(loc) +ax.grid(True, which="major", axis="y", linestyle="-") + + +class Container: + def __init__(self, window, figure, motion, nthread, ltics, ltocs, llabels): + self.window = window + self.figure = figure + self.motion = motion + self.nthread = nthread + self.ltics = ltics + self.ltocs = ltocs + self.llabels = llabels + + def plot(self): + canvas = tkagg.FigureCanvasTkAgg(self.figure, master=self.window) + wcanvas = canvas.get_tk_widget() + wcanvas.config(width=1000, height=300) + wcanvas.pack(side=tk.TOP, expand=True, fill=tk.BOTH) + + toolbar = tkagg.NavigationToolbar2Tk(canvas, self.window) + toolbar.update() + self.output = tk.StringVar() + label = tk.Label( + self.window, textvariable=self.output, bg="white", fg="red", bd=2 + ) + label.pack(side=tk.RIGHT, expand=True, fill=tk.X) + wcanvas.pack(side=tk.TOP, expand=True, fill=tk.BOTH) + + canvas.draw() + + # Print task type using mouse clicks or motion. + if self.motion: + fig.canvas.mpl_connect("motion_notify_event", self.onclick) + else: + fig.canvas.mpl_connect("button_press_event", self.onclick) + + # Space bar to dump all tasks. Use with caution... + fig.canvas.mpl_connect("key_press_event", self.dump) + + def dump(self, event): + # Dump all tasks to the console sorted by tic. + xlow = float(event.inaxes.viewLim.x0) + xhigh = float(event.inaxes.viewLim.x1) + + if event.key == " ": + dumps = {} + for thread in range(nthread): + tics = self.ltics[thread] + tocs = self.ltocs[thread] + labels = self.llabels[thread] + for i in range(len(tics)): + if (tics[i] > xlow and tics[i] < xhigh) or ( + tocs[i] > xlow and tocs[i] < xhigh + ): + tic = "{0:.3f}".format(tics[i]) + toc = "{0:.3f}".format(tocs[i]) + dumps[tics[i]] = ( + labels[i] + ", tic/toc = " + tic + " / " + toc + ) + print("") + print("Tasks in time range: " + str(xlow) + " -> " + str(xhigh)) + for key in sorted(dumps): + print(dumps[key]) + print("") + + def onclick(self, event): + # Find thread, then scan for bounded task. + try: + thread = int(round(event.ydata)) - 1 + outstr = "none" + if thread >= 0 and thread < self.nthread: + tics = self.ltics[thread] + tocs = self.ltocs[thread] + labels = self.llabels[thread] + for i in range(len(tics)): + if event.xdata > tics[i] and event.xdata < tocs[i]: + tic = "{0:.3f}".format(tics[i]) + toc = "{0:.3f}".format(tocs[i]) + outstr = labels[i] + ", tic/toc = " + tic + " / " + toc + break + self.output.set(outstr) + except TypeError: + # Out of bounds clears field. + self.output.set("") + pass + + def quit(self): + self.window.destroy() + + +window = tk.Tk() +window.protocol("WM_DELETE_WINDOW", window.quit) +container = Container(window, fig, args.motion, nthread, ltics, ltocs, llabels) +container.plot() +window.mainloop() + +sys.exit(0) diff --git a/tools/task_plots/plot_tasks.py b/tools/task_plots/plot_tasks.py index 160451de230fc1bbd333b9d5f919ed4d4fc8ff11..2f6f6733bc88ff62c8120a5887f8a45f354eb6be 100755 --- a/tools/task_plots/plot_tasks.py +++ b/tools/task_plots/plot_tasks.py @@ -22,7 +22,7 @@ This file is part of SWIFT. Copyright (C) 2015 Pedro Gonnet (pedro.gonnet@durham.ac.uk), Bert Vandenbroucke (bert.vandenbroucke@ugent.be) - Matthieu Schaller (matthieu.schaller@durham.ac.uk) + Matthieu Schaller (schaller@strw.leidenuniv.nl) (C) 2017 Peter W. Draper (p.w.draper@durham.ac.uk) All Rights Reserved. diff --git a/tools/task_plots/plot_threadpool.py b/tools/task_plots/plot_threadpool.py index 1d311b5698bbec360312c29e963159020ac73c1c..8ed11a6157b7b5dfd1bad39d980486c21669de2c 100755 --- a/tools/task_plots/plot_threadpool.py +++ b/tools/task_plots/plot_threadpool.py @@ -13,7 +13,7 @@ options can be seen using the --help flag. This file is part of SWIFT. Copyright (c) 2015 Pedro Gonnet (pedro.gonnet@durham.ac.uk), Bert Vandenbroucke (bert.vandenbroucke@ugent.be) - Matthieu Schaller (matthieu.schaller@durham.ac.uk) + Matthieu Schaller (schaller@strw.leidenuniv.nl) (c) 2017 Peter W. Draper (p.w.draper@durham.ac.uk) This program is free software: you can redistribute it and/or modify @@ -33,6 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. import matplotlib matplotlib.use("Agg") +import math import matplotlib.collections as collections import matplotlib.ticker as plticker import pylab as pl @@ -313,12 +314,16 @@ for i in range(nthread): ax.broken_barh(tictocs, [i + 0.05, 0.90], facecolors=colours, linewidth=0) # Legend and room for it. -nrow = len(typesseen) / 5 +nrow = math.ceil(len(typesseen) / 4) if not args.nolegend: - ax.fill_between([0, 0], nthread + 0.5, nthread + nrow + 0.5, facecolor="white") + ax.fill_between([0, 0], nthread, nthread + nrow, facecolor="white") ax.set_ylim(0, nthread + 0.5) ax.legend( - loc=1, shadow=True, bbox_to_anchor=(0.0, 1.05, 1.0, 0.2), mode="expand", ncol=5 + loc="lower left", + shadow=True, + bbox_to_anchor=(0.0, 1.0, 1.0, 0.2), + mode="expand", + ncol=4, ) box = ax.get_position() ax.set_position([box.x0, box.y0, box.width, box.height * 0.8]) @@ -334,14 +339,13 @@ if expand == 1: ax.set_ylabel("Thread ID", labelpad=0) else: ax.set_ylabel("Thread ID * " + str(expand), labelpad=0) -ax.set_yticks(pl.array(list(range(nthread))), True) +ax.set_yticks(pl.array(list(range(nthread))), minor=True) loc = plticker.MultipleLocator(base=expand) ax.yaxis.set_major_locator(loc) ax.grid(True, which="major", axis="y", linestyle="-") -pl.show() -pl.savefig(outpng) +pl.savefig(outpng, bbox_inches="tight", dpi=100) print("Graphics done, output written to", outpng) sys.exit(0) diff --git a/tools/task_plots/process_plot_tasks b/tools/task_plots/process_plot_tasks deleted file mode 100755 index 54bb86fb414cb87ccbe2b7c93b18f010e77be465..0000000000000000000000000000000000000000 --- a/tools/task_plots/process_plot_tasks +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash -# -# Usage: -# process_plot_tasks nprocess time-range-ms -# -# Description: -# Process all the thread info files in the current directory -# creating task graphs for steps and threads. -# -# The input files are created by a run using the "-y interval" flag and -# should be named "thread_info-step<n>.dat" in the current directory. -# All located files will be processed using "nprocess" concurrent -# processes and all plots will have the given time range. An output -# HTML file "index.html" will be created to view all the plots. -# -# -# This file is part of SWIFT: -# -# Copyright (C) 2016 Peter W. Draper (p.w.draper@durham.ac.uk) -# All Rights Reserved. -# -# 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/>. - -# Handle command-line -if test "$2" == ""; then - echo "Usage: $0 nprocess time-range-ms" - exit 1 -fi -NPROCS=$1 -TIMERANGE=$2 - -# Locate script. -SCRIPTHOME=$(dirname "$0") - -# Find all thread info files. Use version sort to get into correct order. -files=$(ls -v thread_info-step*.dat) -if test $? != 0; then - echo "Failed to find any thread info files" - exit 1 -fi - -# Construct list of names, the step no and names for the graphics. -list="" -for f in $files; do - s=$(echo $f| sed 's,thread_info-step\(.*\).dat,\1,') - list="$list $f $s step${s}r" -done - -# And process them, -echo "Processing thread info files..." -echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "${SCRIPTHOME}/plot_tasks.py --expand 1 --limit $TIMERANGE --width 16 --height 4 \$0 \$2 " -echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "${SCRIPTHOME}/analyse_tasks.py --html \$0 > \$2.stats" - -echo "Writing output index.html file" -# Construct document - serial. -cat <<EOF > index.html - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> - <head> - <title>SWIFT task graphs</title> - </head> - <body> - <h1>SWIFT task graphs</h1> -EOF - -echo $list | xargs -n 3 | while read f s g; do - cat <<EOF >> index.html -<h2>Step $s</h2> -EOF - cat <<EOF >> index.html -<a href="step${s}r${i}.html"><img src="step${s}r${i}.png" width=400px/></a> -EOF - cat <<EOF > step${s}r${i}.html - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<body> -<img src="step${s}r${i}.png"> -<pre> -EOF -echo "<nav>Jump to: <a href="#all">all threads</a> <a href="#dead">dead times</a></nav>" >> step${s}r${i}.html -cat step${s}r${i}.stats >> step${s}r${i}.html -cat <<EOF >> step${s}r${i}.html -</body> -</html> -EOF - -done - -cat <<EOF >> index.html - </body> -</html> -EOF - -echo "Finished" - -exit diff --git a/tools/task_plots/process_plot_tasks.py b/tools/task_plots/process_plot_tasks.py new file mode 100755 index 0000000000000000000000000000000000000000..f628393c1e5fbac793f562cee17190e7d8520945 --- /dev/null +++ b/tools/task_plots/process_plot_tasks.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +""" +Usage: + process_plot_tasks.py TIME-RANGE + [--files FILES] [--weights] [--nproc NPROC] + +where NPROC is the number of parallel processes to use to process task info +files and TIME-RANGE is the range (in ms) for the horizontal axis in the task +plots (a value of 0 means the task data is used to determine the range). + +This script will process task info input files named "thread_info-step<n>.dat" +that can be produced by configuring SWIFT with '--enable-task-debugging' and +running SWIFT with the '-y INTERVAL' option. The script produces the following +output: + - task plots ("step<n>r") for each input file + - "index.html": an overview of all task plots + - step pages ("step<n>r.html") with statistical information about the tasks + for a specific step. These can be opened by clicking on a task plot on the + overview page. + +By default, all thread_info*.dat files in the current working directory are +processed. The optional FILES argument allows more fine-grained control over +which files get included. Note that this script acts as a wrapper for +'plot_tasks.py' and 'analyse_tasks.py'; these scripts still expect some files +to be present in the current working directory. + +The optional argument --weights will process the task info files in reverse +order of their size. This significantly improves load-balancing when using +a large number of processes for an inhomogeneous set of input files, but can +also lead to a large memory usage, since NPROC large files will be loaded into +memory simultaneously. + +Note that this script is a Python version of an earlier bash script by Peter +Draper. + +This file is part of SWIFT. + +Copyright (C) 2016 Peter W. Draper (p.w.draper@durham.ac.uk) + (C) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +All Rights Reserved. + +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 subprocess +import argparse +import glob +import re +import sys +import os +import numpy as np + +# location of this script +# used to find other scripts we need to run +script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + +# parse command line arguments +argparser = argparse.ArgumentParser() +argparser.add_argument( + "time_range", type=float, help="Time range to use for the horizontal axis (in ms)." +) +argparser.add_argument( + "--files", + "-f", + default=None, + nargs="+", + help="Files to process (default: all thread_info-step*.dat files in the current directory).", +) +argparser.add_argument( + "--nproc", "-j", default=1, type=int, help="Number of parallel processes to use." +) +argparser.add_argument( + "--weights", + "-w", + action="store_true", + help="Use file sizes as weights to determine task order.", +) +args = argparser.parse_args() + +nproc = args.nproc +trange = args.time_range + +# function used to extract the step counter from a thread_info-step*.dat file +def getcount(filename): + return int(re.findall("\d+", filename)[0]) + + +# sort the files based on the step number +# also do this if a list of files was provided +files = args.files +if files is None: + files = sorted(glob.glob("thread_info-step*.dat"), key=getcount) +else: + files = sorted(files, key=getcount) + +# create a list of step numbers matching the files +nfile = len(files) +steps = np.zeros(nfile, dtype=np.int32) +for ifile in range(nfile): + steps[ifile] = getcount(files[ifile]) + +# create all the analysis commands we want to run without actually running them +cmds = [] +weights = [] +for ifile in range(nfile): + outname = "step{0}r".format(steps[ifile]) + weight = os.path.getsize(files[ifile]) + + cmd = "{0}/plot_tasks.py --expand 1 --limit {1} {2} {3}".format( + script_dir, trange, files[ifile], outname + ) + cmds.append(cmd) + # plot_task commands are more expensive than their analyse_tasks + # counterpart because they also need to write large image files + weights.append(2 * weight) + + outname = "step{0}r.stats".format(steps[ifile]) + cmd = "{0}/analyse_tasks.py --html {1} > {2}".format( + script_dir, files[ifile], outname + ) + cmds.append(cmd) + weights.append(weight) + +if args.weights: + # sort the commands according to their weight + # long/expensive commands will be launched first, to achieve maximum overlap + # with shorter commands + weights = np.array(weights) + order = np.argsort(weights)[::-1] + cmds = np.array(cmds)[order] + +# now run all commands in parallel using the requested number of processes + +print("Done generating analysis commands, running them in parallel...") + +# first, send off 'nproc' processes +icmd = 0 +dfs = [None] * nproc +while icmd < len(cmds) and icmd < nproc: + cmd = cmds[icmd] + print("Starting {0} in slot {1}".format(cmd, icmd)) + dfs[icmd] = subprocess.Popen(cmd, shell=True) + icmd += 1 + +# now keep spawning more processes until all commands have been submitted +while icmd < len(cmds): + # loop over the running processes + for iproc in range(nproc): + # if a process finished, replace it with a new command + if not dfs[iproc].poll() is None: + print("Slot {0} finished".format(iproc)) + cmd = cmds[icmd] + print("Starting {0} in slot {1}".format(cmd, iproc)) + dfs[iproc] = subprocess.Popen(cmd, shell=True) + icmd += 1 + # we are out of commands, exit the for-loop (and the while-loop) + if icmd == len(cmds): + break + +# now wait for the remaining processes +# we don't care that the processes will not finish in list order, since we +# have to wait for the slowest one anyway +for iproc in range(nproc): + if not dfs[iproc] is None: + dfs[iproc].wait() + print("Slot {0} finished".format(iproc)) + +print("Done processing files. Creating web pages...") + +# generate web pages +# note that we don't bother adding newlines, since those are ignored by the +# browser anyway +htmltag = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' + +index_file = open("index.html", "w") +index_file.write(htmltag) +index_file.write("<html><head><title>SWIFT task graphs</title></head><body>") +index_file.write("<h1>SWIFT task graphs</h1>") + +for ifile in range(nfile): + step = steps[ifile] + index_file.write("<h2>Step {0}</h2>".format(step)) + index_file.write('<a href="step{0}r.html">'.format(step)) + index_file.write('<img src="step{0}r.png" width="400px"/></a>'.format(step)) + + step_file = open("step{0}r.html".format(step), "w") + step_file.write(htmltag) + step_file.write("<html><body>") + step_file.write('<img src="step{0}r.png"/>'.format(step)) + step_file.write('<pre><nav>Jump to: <a href="#all">all threads</a> ') + step_file.write('<a href="#dead">dead times</a></nav>\n') + with open("step{0}r.stats".format(step), "r") as stats_file: + step_file.write(stats_file.read()) + step_file.write("</pre></body></html>") + step_file.close() + +index_file.write("</body></html>") +index_file.close() + +print("Done.") diff --git a/tools/task_plots/process_plot_tasks_MPI b/tools/task_plots/process_plot_tasks_MPI deleted file mode 100755 index d1a3c3c6685cebf0e24ecc355f286e4d26d6dd25..0000000000000000000000000000000000000000 --- a/tools/task_plots/process_plot_tasks_MPI +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/bash -# -# Usage: -# process_plot_tasks_MPI nprocess time-range-ms -# -# Description: -# Process all the thread info files in the current directory -# creating task graphs for all MPI ranks and all threads. -# -# The input files are created by a run using the "-y interval" flag and -# should be named "thread_info_MPI-step<n>.dat" in the current directory. -# All located files will be processed using "nprocess" concurrent -# processes and all plots will have the given time range. An output -# HTML file "index.html" will be created to view all the plots. -# -# -# This file is part of SWIFT: -# -# Copyright (C) 2015 Peter W. Draper (p.w.draper@durham.ac.uk) -# All Rights Reserved. -# -# 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/>. - -# Handle command-line -if test "$2" == ""; then - echo "Usage: $0 nprocess time-range-ms" - exit 1 -fi -NPROCS=$1 -TIMERANGE=$2 - -# Locate script. -SCRIPTHOME=$(dirname "$0") - -# Find all thread info files. Use version sort to get into correct order. -files=$(ls -v thread_info_MPI-step*.dat) -if test $? != 0; then - echo "Failed to find any thread info files" - exit 1 -fi - -# Construct list of names, the step no and names for the graphics. -list="" -for f in $files; do - s=$(echo $f| sed 's,thread_info_MPI-step\(.*\).dat,\1,') - list="$list $f $s step${s}r" -done - -# Need number of ranks used. -basefile=$(echo $list | awk '{print $1}') -nrank=$(cat $basefile | awk '{print $1}' | uniq | wc -l) -nrank=$(($nrank-1)) - -# And process them, -echo "Processing thread info files..." -echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "${SCRIPTHOME}/plot_tasks.py --expand 1 --limit $TIMERANGE \$0 \$2 " -for i in $(seq 0 $nrank); do - echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "${SCRIPTHOME}/analyse_tasks.py -r $i --html \$0 > \$2${i}.stats" -done - -echo "Writing output index.html file" -# Construct document - serial. -cat <<EOF > index.html - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> - <head> - <title>SWIFT task graphs</title> - </head> - <body> - <h1>SWIFT task graphs</h1> -EOF - -echo $list | xargs -n 3 | while read f s g; do - cat <<EOF >> index.html -<h2>Step $s</h2> -<ul style="list-style-type:none"> -<li> -EOF - - cat <<EOF3 > step${s}r.html -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<body> -EOF3 - - for i in $(seq 0 $nrank); do - - cat <<EOF >> index.html -<a href="step${s}r.html"><img src="step${s}r${i}.png" width=400px/></a> -EOF - cat <<EOF3 >> step${s}r.html -<a href="step${s}r${i}.html"><img src="step${s}r${i}.png"/></a> -EOF3 - - cat <<EOF2 > step${s}r${i}.html - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<body> -<img src="step${s}r${i}.png"> -<pre> -EOF2 -echo "<nav>Jump to: <a href="#all">all threads</a> <a href="#dead">dead times</a></nav>" >> step${s}r${i}.html -cat step${s}r${i}.stats >> step${s}r${i}.html -cat <<EOF2 >> step${s}r${i}.html -</pre> -</body> -</html> -EOF2 - - done - -cat <<EOF3 >> step${s}r.html -</body> -</html> -EOF3 - -cat <<EOF >> index.html -</li> -</ul> -EOF -done - -cat <<EOF >> index.html - </body> -</html> -EOF - -echo "Finished" - -exit diff --git a/tools/task_plots/process_plot_tasks_MPI.py b/tools/task_plots/process_plot_tasks_MPI.py new file mode 100755 index 0000000000000000000000000000000000000000..bf43d7e2a7a661e9b07225d5172e043d005fe537 --- /dev/null +++ b/tools/task_plots/process_plot_tasks_MPI.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +""" +Usage: + process_plot_tasks_MPI.py TIME-RANGE + [--files FILES] [--weights] [--nproc NPROC] + +where NPROC is the number of parallel processes to use to process task info +files and TIME-RANGE is the range (in ms) for the horizontal axis in the task +plots (a value of 0 means the task data is used to determine the range). + +This script will process task info input files named +"thread_info_MPI-step<n>.dat" that can be produced by configuring SWIFT with +'--enable-task-debugging' and running SWIFT with the '-y INTERVAL' option. The +script produces the following output: + - task plots ("step<n>r<r>.png") for each input file and rank + - "index.html": an overview of all task plots + - step pages ("step<n>r.html") with an overview of the task plots per rank for + a single step. These can be opened by clicking on a task plot on the + overview page. + - step rank pages ("step<n>r<r>.html") with statistical information about the + tasks for a specific step and a specific rank. These can be opened by + clicking on a task plot on the step page for the same step. + +By default, all thread_info_MPI*.dat files in the current working directory are +processed. The optional FILES argument allows more fine-grained control over +which files get included. Note that this script acts as a wrapper for +'plot_tasks.py' and 'analyse_tasks.py'; these scripts still expect some +files to be present in the current working directory. + +The optional argument --weights will process the task info files in reverse +order of their size. This significantly improves load-balancing when using +a large number of processes for an inhomogeneous set of input files, but can +also lead to a large memory usage, since NPROC large files will be loaded into +memory simultaneously. + +Note that this script is a Python version of an earlier bash script by Peter +Draper. + +This file is part of SWIFT. + +Copyright (C) 2015 Peter W. Draper (p.w.draper@durham.ac.uk) + (C) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +All Rights Reserved. + +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 subprocess +import argparse +import glob +import re +import sys +import os +import numpy as np + +# location of this script +# used to find other scripts we need to run +script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + +# parse command line arguments +argparser = argparse.ArgumentParser() +argparser.add_argument( + "time_range", type=float, help="Time range to use for the horizontal axis (in ms)." +) +argparser.add_argument( + "--files", + "-f", + default=None, + nargs="+", + help="Files to process (default: all thread_info_MPI-step*.dat files in the current directory).", +) +argparser.add_argument( + "--weights", + "-w", + action="store_true", + help="Use file sizes as weights to determine task order.", +) +argparser.add_argument( + "--nproc", "-j", default=1, type=int, help="Number of parallel processes to use." +) +args = argparser.parse_args() + +nproc = args.nproc +trange = args.time_range + +# function used to extract the step counter from a thread_info_MPI-step*.dat file +def getcount(filename): + return int(re.findall("\d+", filename)[0]) + + +# function that parses a thread_info_MPI-step*.dat file and gets the number of ranks +def get_nrank(filename): + # read the rank column from the file + data = np.loadtxt(filename, usecols=[0], dtype=np.int32) + # find the maximum + # the number of ranks is this value + 1 + nrank = data.max() + 1 + return nrank + + +# sort the files based on the step number +# also do this if a list of files was provided +files = args.files +if files is None: + files = sorted(glob.glob("thread_info_MPI-step*.dat"), key=getcount) +else: + files = sorted(files, key=getcount) + +# create a list of step numbers matching the files +nfile = len(files) +steps = np.zeros(nfile, dtype=np.int32) +for ifile in range(nfile): + steps[ifile] = getcount(files[ifile]) + +nrank = get_nrank(files[0]) + +# create all the analysis commands we want to run without actually running them +cmds = [] +weights = [] +for ifile in range(nfile): + outname = "step{0}r".format(steps[ifile]) + weight = os.path.getsize(files[ifile]) + + cmd = "{0}/plot_tasks.py --expand 1 --limit {1} {2} {3}".format( + script_dir, trange, files[ifile], outname + ) + cmds.append(cmd) + # plot_task commands are more expensive than their analyse_tasks + # counterpart because they also need to write large image files + weights.append(2 * weight) + + for irank in range(nrank): + outname = "step{0}r{1}.stats".format(steps[ifile], irank) + cmd = "{0}/analyse_tasks.py -r {1} --html {2} > {3}".format( + script_dir, irank, files[ifile], outname + ) + cmds.append(cmd) + weights.append(weight) + +if args.weights: + # sort the commands according to their weight + # long/expensive commands will be launched first, to achieve maximum overlap + # with shorter commands + weights = np.array(weights) + order = np.argsort(weights)[::-1] + cmds = np.array(cmds)[order] + +# now run all commands in parallel using the requested number of processes + +print("Done generating analysis commands, running them in parallel...") + +# first, send off 'nproc' processes +icmd = 0 +dfs = [None] * nproc +while icmd < len(cmds) and icmd < nproc: + cmd = cmds[icmd] + print("Starting {0} in slot {1}".format(cmd, icmd)) + dfs[icmd] = subprocess.Popen(cmd, shell=True) + icmd += 1 + +# now keep spawning more processes until all commands have been submitted +while icmd < len(cmds): + # loop over the running processes + for iproc in range(nproc): + # if a process finished, replace it with a new command + if not dfs[iproc].poll() is None: + print("Slot {0} finished".format(iproc)) + cmd = cmds[icmd] + print("Starting {0} in slot {1}".format(cmd, iproc)) + dfs[iproc] = subprocess.Popen(cmd, shell=True) + icmd += 1 + # we are out of commands, exit the for-loop (and the while-loop) + if icmd == len(cmds): + break + +# now wait for the remaining processes +# we don't care that the processes will not finish in list order, since we +# have to wait for the slowest one anyway +for iproc in range(nproc): + if not dfs[iproc] is None: + dfs[iproc].wait() + print("Slot {0} finished".format(iproc)) + +print("Done processing files. Creating web pages...") + +# generate web pages +# note that we don't bother adding newlines, since those are ignored by the +# browser anyway +htmltag = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' + +index_file = open("index.html", "w") +index_file.write(htmltag) +index_file.write("<html><head><title>SWIFT task graphs</title></head><body>") +index_file.write("<h1>SWIFT task graphs</h1>") + +for ifile in range(nfile): + step = steps[ifile] + index_file.write("<h2>Step {0}</h2>".format(step)) + index_file.write('<ul style="list-style-type:none"><li>') + + step_file = open("step{0}r.html".format(step), "w") + step_file.write(htmltag) + step_file.write("<html><body>") + + for irank in range(nrank): + index_file.write('<a href="step{0}r.html">'.format(step)) + index_file.write( + '<img src="step{0}r{1}.png" width="400px"/></a>'.format(step, irank) + ) + + step_file.write('<a href="step{0}r{1}.html">'.format(step, irank)) + step_file.write('<img src="step{0}r{1}.png"/></a>'.format(step, irank)) + + with open("step{0}r{1}.html".format(step, irank), "w") as rank_file: + rank_file.write(htmltag) + rank_file.write( + '<html></body><img src="step{0}r{1}.png"/>'.format(step, irank) + ) + rank_file.write('<pre><nav>Jump to: <a href="#all">all threads</a> ') + rank_file.write('<a href="#dead">dead times</a></nav>\n') + with open("step{0}r{1}.stats".format(step, irank), "r") as stats_file: + rank_file.write(stats_file.read()) + rank_file.write("</pre></body></html>") + + step_file.write("</body></html>") + + index_file.write("</li></ul>") + +index_file.write("</body></html>") + +print("Done.") diff --git a/tools/task_plots/process_plot_threadpool b/tools/task_plots/process_plot_threadpool index b1238b3668067f17b98099f6287fa63fec5d2395..87b8726924a19122144df4aad3a1b5ac5cf4842c 100755 --- a/tools/task_plots/process_plot_threadpool +++ b/tools/task_plots/process_plot_threadpool @@ -59,12 +59,12 @@ fi list="" for f in $files; do s=$(echo $f| sed 's,threadpool_info-step\(.*\).dat,\1,') - list="$list $f $s poolstep${s}r" + list="$list $f $s threadpool-step${s}" done # And process them, echo "Processing threadpool info files..." -echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "${SCRIPTHOME}/plot_threadpool.py --expand 1 --limit $TIMERANGE --width 16 --height 4 \$0 \$2 " +echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "${SCRIPTHOME}/plot_threadpool.py --expand 1 --limit $TIMERANGE --width 16 --height 8 \$0 \$2 " echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "${SCRIPTHOME}/analyse_threadpool_tasks.py \$0 --html > \$2.stats" echo "Writing output threadpool-index.html file" @@ -84,17 +84,19 @@ echo $list | xargs -n 3 | while read f s g; do <h2>Step $s</h2> EOF cat <<EOF >> threadpool-index.html -<a href="poolstep${s}r${i}.html"><img src="poolstep${s}r${i}.png" width=400px/></a> +<a href="threadpool-step${s}.html"><img src="threadpool-step${s}.png" width=400px/></a> EOF - cat <<EOF > poolstep${s}r${i}.html + cat <<EOF > threadpool-step${s}.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> -<img src="poolstep${s}r${i}.png"> +<img src="threadpool-step${s}.png"> <pre> +<nav>Jump to: <a href="#all">all threads</a> <a href="#dead">dead times</a></nav> EOF -cat poolstep${s}r${i}.stats >> poolstep${s}r${i}.html -cat <<EOF >> poolstep${s}r${i}.html +cat threadpool-step${s}.stats >> threadpool-step${s}.html +cat <<EOF >> threadpool-step${s}.html +</pre> </body> </html> EOF diff --git a/tools/task_plots/process_plot_threadpool_MPI b/tools/task_plots/process_plot_threadpool_MPI new file mode 100755 index 0000000000000000000000000000000000000000..826c60497ba17308aa7ff46cf82344475ba8894e --- /dev/null +++ b/tools/task_plots/process_plot_threadpool_MPI @@ -0,0 +1,116 @@ +#!/bin/bash +# +# Usage: +# process_plot_threadpool_MPI nprocess [time-range-ms] +# +# Description: +# Process all the threadpool info files in the current directory +# creating function graphs for steps and threads. MPI version +# +# The input files are created by a run using the "-Y interval" flag and +# should be named "threadpool_info-rank<m>-step<n>.dat" in the current +# directory. All located files will be processed using "nprocess" concurrent +# processes and all plots will have the same time range if one is given. +# An output HTML file "index.html" will be created to view all the plots. +# +# +# This file is part of SWIFT: +# +# Copyright (C) 2022 Peter W. Draper (p.w.draper@durham.ac.uk) +# All Rights Reserved. +# +# 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/>. + +# Handle command-line +if test "$1" = ""; then + echo "Usage: $0 nprocess [time-range-ms]" + exit 1 +fi +NPROCS=$1 +TIMERANGE=0 +LIMIT="(autoranged)" +if test "$2" != ""; then + TIMERANGE=$2 + LIMIT="" +fi + +# Locate script. +SCRIPTHOME=$(dirname "$0") + +# Find all thread info files. Use sort to get into correct order, step then rank. +files=$(ls -1 threadpool_info-rank*-step*.dat| sort -t- -k3,3) +if test $? != 0; then + echo "Failed to find any threadpool info files" + exit 1 +fi + +# Construct list of names, the step no and names for the graphics. +list="" +for f in $files; do + r=$(echo $f| sed 's,threadpool_info-rank\(.*\)-step.*.dat,\1,') + s=$(echo $f| sed 's,threadpool_info-rank.*-step\(.*\).dat,\1,') + list="$list $f $s $r threadpool-step${s}-rank${r}" +done + +# And process them, +echo "Processing threadpool info files..." +echo $list | xargs -P $NPROCS -n 4 /bin/bash -c "${SCRIPTHOME}/plot_threadpool.py --expand 1 --limit $TIMERANGE --width 16 --height 4 \$0 \$3 " +echo $list | xargs -P $NPROCS -n 4 /bin/bash -c "${SCRIPTHOME}/analyse_threadpool_tasks.py \$0 --html > \$3.stats" + +echo "Writing output threadpool-index.html file" +# Construct document - serial. +cat <<EOF > threadpool-index.html + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> + <head> + <title>SWIFT threadpool tasks $LIMIT</title> + </head> + <body> + <h1>SWIFT threadpool tasks $LIMIT</h1> +EOF + +echo $list | xargs -n 4 | while read f s r g; do + cat <<EOF >> threadpool-index.html +<h2>Step $s Rank $r</h2> +EOF + cat <<EOF >> threadpool-index.html +<a href="threadpool-step${s}-rank${r}.html"><img src="threadpool-step${s}-rank${r}.png" width=400px/></a> +EOF + cat <<EOF > threadpool-step${s}-rank${r}.html + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<body> +<img src="threadpool-step${s}-rank${r}.png"> +<pre> +<nav>Jump to: <a href="#all">all threads</a> <a href="#dead">dead times</a></nav> + +<pre> +EOF +cat threadpool-step${s}-rank${r}.stats >> threadpool-step${s}-rank${r}.html +cat <<EOF >> threadpool-step${s}-rank${r}.html +</pre> +</body> +</html> +EOF + +done + +cat <<EOF >> threadpool-index.html + </body> +</html> +EOF + +echo "Finished" + +exit diff --git a/tools/task_plots/swift_hardcoded_data.py b/tools/task_plots/swift_hardcoded_data.py index 8beb97e07a95c3f1367a62f2f51df01ac45aac72..104c43f28ce048748fe71f0468e1b4488cb95b0e 100644 --- a/tools/task_plots/swift_hardcoded_data.py +++ b/tools/task_plots/swift_hardcoded_data.py @@ -50,6 +50,7 @@ TASKTYPES = [ "star_formation", "star_formation_in", "star_formation_out", + "star_formation_sink", "csds", "stars_in", "stars_out", @@ -63,14 +64,16 @@ TASKTYPES = [ "stars_resort", "bh_in", "bh_out", - "bh_ghost", + "bh_density_ghost", "bh_swallow_ghost1", "bh_swallow_ghost2", "bh_swallow_ghost3", "fof_self", "fof_pair", + "neutrino_weight", "sink_in", - "sink_ghost", + "sink_ghost1", + "sink_ghost2", "sink_out", "rt_in", "rt_out", @@ -113,10 +116,9 @@ SUBTYPES = [ "do_gas_swallow", "do_bh_swallow", "bh_feedback", - "sink_merger", - "rt_inject", - "sink_compute_formation", - "sink_accretion", + "sink_do_sink_swallow", + "sink_swallow", + "sink_do_gas_swallow", "rt_gradient", "rt_transport", # "count", diff --git a/tools/timed_functions.py b/tools/timed_functions.py index d2976c65e487106aea77420f3f4aad20767d7014..d4ad20fae9d95219df6bf573b7154f6b49a985e2 100644 --- a/tools/timed_functions.py +++ b/tools/timed_functions.py @@ -37,6 +37,7 @@ labels = [ ["engine_collect_end_of_step:", 0], ["engine_launch: \(tasks\)", 0], ["engine_launch: \(timesteps\)", 0], + ["engine_launch: \(cycles\)", 0], ["writing particle properties", 0], ["engine_repartition:", 0], ["engine_exchange_cells:", 1], @@ -72,4 +73,6 @@ labels = [ ["csds_log_all_particles:", 0], ["csds_ensure_size:", 0], ["csds_init:", 0], + ["Applying lightcone map updates", 0], + ["Flushing particle buffers", 0], ]